/* Search @list for element with key @key and remove it. @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 @value was removed by this call. */ gboolean mono_lls_remove (MonoLinkedListSet *list, MonoThreadHazardPointers *hp, MonoLinkedListSetNode *value, HazardFreeContext context) { MonoLinkedListSetNode *cur, **prev, *next; while (1) { if (!mono_lls_find (list, hp, value->key, context)) return FALSE; next = (MonoLinkedListSetNode *) mono_hazard_pointer_get_val (hp, 0); cur = (MonoLinkedListSetNode *) mono_hazard_pointer_get_val (hp, 1); prev = (MonoLinkedListSetNode **) mono_hazard_pointer_get_val (hp, 2); g_assert (cur == value); if (InterlockedCompareExchangePointer ((volatile gpointer*)&cur->next, mask (next, 1), next) != next) continue; /* The second CAS must happen before the first. */ mono_memory_write_barrier (); if (InterlockedCompareExchangePointer ((volatile gpointer*)prev, mono_lls_pointer_unmask (next), cur) == cur) { /* The CAS must happen before the hazard pointer clear. */ mono_memory_write_barrier (); mono_hazard_pointer_clear (hp, 1); if (list->free_node_func) mono_thread_hazardous_try_free (value, list->free_node_func); } else mono_lls_find (list, hp, value->key, context); return TRUE; } }
static void desc_retire (Descriptor *desc) { g_assert (desc->anchor.data.state == STATE_EMPTY); g_assert (desc->in_use); desc->in_use = FALSE; free_sb (desc->sb, desc->block_size, desc->heap->account_type); mono_thread_hazardous_try_free (desc, desc_enqueue_avail); }
static void list_remove_empty_desc (MonoLockFreeAllocSizeClass *sc) { int num_non_empty = 0; for (;;) { Descriptor *desc = (Descriptor*) mono_lock_free_queue_dequeue (&sc->partial); if (!desc) return; /* * We don't need to read atomically because we're the * only thread that references this descriptor. */ if (desc->anchor.data.state == STATE_EMPTY) { desc_retire (desc); } else { g_assert (desc->heap->sc == sc); mono_thread_hazardous_try_free (desc, desc_put_partial); if (++num_non_empty >= 2) return; } } }
static void conc_table_lf_free (conc_table *table) { mono_thread_hazardous_try_free (table, conc_table_free); }
static void unregister_thread (void *arg) { gpointer gc_unsafe_stackdata; MonoThreadInfo *info; int small_id; info = (MonoThreadInfo *) arg; g_assert (info); g_assert (mono_thread_info_is_current (info)); g_assert (mono_thread_info_is_live (info)); small_id = info->small_id; /* We only enter the GC unsafe region, as when exiting this function, the thread * will be detached, and the current MonoThreadInfo* will be destroyed. */ mono_threads_enter_gc_unsafe_region_unbalanced_with_info (info, &gc_unsafe_stackdata); THREADS_DEBUG ("unregistering info %p\n", info); mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1)); mono_threads_platform_unregister (info); /* * TLS destruction order is not reliable so small_id might be cleaned up * before us. */ #ifndef HAVE_KW_THREAD mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (info->small_id + 1)); #endif /* First perform the callback that requires no locks. This callback has the potential of taking other locks, so we do it before. After it completes, the thread remains functional. */ if (threads_callbacks.thread_detach) threads_callbacks.thread_detach (info); mono_thread_info_suspend_lock_with_info (info); /* Now perform the callback that must be done under locks. This will render the thread useless and non-suspendable, so it must be done while holding the suspend lock to give no other thread chance to suspend it. */ if (threads_callbacks.thread_unregister) threads_callbacks.thread_unregister (info); mono_threads_unregister_current_thread (info); mono_threads_transition_detach (info); mono_thread_info_suspend_unlock (); g_byte_array_free (info->stackdata, /*free_segment=*/TRUE); /*now it's safe to free the thread info.*/ mono_thread_hazardous_try_free (info, free_thread_info); /* Pump the HP queue */ mono_thread_hazardous_try_free_some (); mono_thread_small_id_free (small_id); }
static void list_put_partial (Descriptor *desc) { g_assert (desc->anchor.data.state != STATE_FULL); mono_thread_hazardous_try_free (desc, desc_put_partial); }