/* Search @list for element with key @key. @context specifies whether the function is being called from a lock-free (i.e. signal handler or world stopped) context. It is only relevant if a node free function was given. The nodes next, cur and prev are returned in @hp. Returns true if a node with key @key was found. */ gboolean mono_lls_find (MonoLinkedListSet *list, MonoThreadHazardPointers *hp, uintptr_t key, HazardFreeContext context) { MonoLinkedListSetNode *cur, *next; MonoLinkedListSetNode **prev; uintptr_t cur_key; try_again: prev = &list->head; /* * prev is not really a hazardous pointer, but we return prev * in hazard pointer 2, so we set it here. Note also that * prev is not a pointer to a node. We use here the fact that * the first element in a node is the next pointer, so it * works, but it's not pretty. */ mono_hazard_pointer_set (hp, 2, prev); cur = (MonoLinkedListSetNode *) get_hazardous_pointer_with_mask ((gpointer*)prev, hp, 1); while (1) { if (cur == NULL) return FALSE; next = (MonoLinkedListSetNode *) get_hazardous_pointer_with_mask ((gpointer*)&cur->next, hp, 0); cur_key = cur->key; /* * We need to make sure that we dereference prev below * after reading cur->next above, so we need a read * barrier. */ mono_memory_read_barrier (); if (*prev != cur) goto try_again; if (!mono_lls_pointer_get_mark (next)) { if (cur_key >= key) return cur_key == key; prev = &cur->next; mono_hazard_pointer_set (hp, 2, cur); } else { next = (MonoLinkedListSetNode *) mono_lls_pointer_unmask (next); if (InterlockedCompareExchangePointer ((volatile gpointer*)prev, next, cur) == cur) { /* The hazard pointer must be cleared after the CAS. */ mono_memory_write_barrier (); mono_hazard_pointer_clear (hp, 1); if (list->free_node_func) mono_thread_hazardous_queue_free (cur, list->free_node_func); } else goto try_again; } cur = (MonoLinkedListSetNode *) mono_lls_pointer_unmask (next); mono_hazard_pointer_set (hp, 1, cur); } }
/** * mono_thread_hazardous_try_free: * \param p the pointer to free * \param free_func the function that can free the pointer * * If \p p is not a hazardous pointer it will be immediately freed by calling \p free_func. * Otherwise it will be queued for later. * * Use this function if \p free_func can ALWAYS be called in the context where this function is being called. * * This function doesn't pump the free queue so try to accommodate a call at an appropriate time. * See mono_thread_hazardous_try_free_some for when it's appropriate. * * \returns TRUE if \p p was free or FALSE if it was queued. */ gboolean mono_thread_hazardous_try_free (gpointer p, MonoHazardousFreeFunc free_func) { if (!is_pointer_hazardous (p)) { free_func (p); return TRUE; } else { mono_thread_hazardous_queue_free (p, free_func); return FALSE; } }