/* Search @list for element with key @key. The nodes next, cur and prev are returned in @hp Returns true if @value was removed by this call. This function cannot be called from a signal nor with the world stopped. */ gboolean mono_lls_remove (MonoLinkedListSet *list, MonoThreadHazardPointers *hp, MonoLinkedListSetNode *value) { MonoLinkedListSetNode *cur, **prev, *next; while (1) { if (!mono_lls_find (list, hp, value->key)) return FALSE; next = mono_hazard_pointer_get_val (hp, 0); cur = mono_hazard_pointer_get_val (hp, 1); prev = 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, 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_free_or_queue (value, list->free_node_func); } else mono_lls_find (list, hp, value->key); return TRUE; } }
static void* worker (void *arg) { thread_data_t *thread_data = (thread_data_t *)arg; MonoThreadHazardPointers *hp; int skip = thread_data->skip; int i, j; gboolean result; mono_thread_info_register_small_id (); hp = mono_hazard_pointer_get (); i = 0; for (j = 0; j < NUM_ITERS; ++j) { switch (nodes [i].state) { case STATE_BUSY: mono_thread_hazardous_try_free_some (); break; case STATE_OUT: if (InterlockedCompareExchange (&nodes [i].state, STATE_BUSY, STATE_OUT) == STATE_OUT) { result = mono_lls_find (&lls, hp, i, HAZARD_FREE_SAFE_CTX); assert (!result); mono_hazard_pointer_clear_all (hp, -1); result = mono_lls_insert (&lls, hp, &nodes [i].node, HAZARD_FREE_SAFE_CTX); mono_hazard_pointer_clear_all (hp, -1); assert (nodes [i].state == STATE_BUSY); nodes [i].state = STATE_IN; ++thread_data->num_adds; } break; case STATE_IN: if (InterlockedCompareExchange (&nodes [i].state, STATE_BUSY, STATE_IN) == STATE_IN) { result = mono_lls_find (&lls, hp, i, HAZARD_FREE_SAFE_CTX); assert (result); assert (mono_hazard_pointer_get_val (hp, 1) == &nodes [i].node); mono_hazard_pointer_clear_all (hp, -1); result = mono_lls_remove (&lls, hp, &nodes [i].node, HAZARD_FREE_SAFE_CTX); mono_hazard_pointer_clear_all (hp, -1); ++thread_data->num_removes; } break; default: assert (FALSE); } i += skip; if (i >= N) i -= N; } return NULL; }
/* If return non null Hazard Pointer 1 holds the return value. */ MonoThreadInfo* mono_thread_info_lookup (MonoNativeThreadId id) { MonoThreadHazardPointers *hp = mono_hazard_pointer_get (); if (!mono_lls_find (&thread_list, hp, (uintptr_t)id)) { mono_hazard_pointer_clear_all (hp, -1); return NULL; } mono_hazard_pointer_clear_all (hp, 1); return mono_hazard_pointer_get_val (hp, 1); }
/* Insert @value into @list. The nodes value, cur and prev are returned in @hp. Return true if @value was inserted by this call. If it returns FALSE, it's the caller resposibility to release memory. This function cannot be called from a signal nor with the world stopped. */ gboolean mono_lls_insert (MonoLinkedListSet *list, MonoThreadHazardPointers *hp, MonoLinkedListSetNode *value) { MonoLinkedListSetNode *cur, **prev; /*We must do a store barrier before inserting to make sure all values in @node are globally visible.*/ mono_memory_barrier (); while (1) { if (mono_lls_find (list, hp, value->key)) return FALSE; cur = mono_hazard_pointer_get_val (hp, 1); prev = mono_hazard_pointer_get_val (hp, 2); value->next = cur; mono_hazard_pointer_set (hp, 0, value); /* The CAS must happen after setting the hazard pointer. */ mono_memory_write_barrier (); if (InterlockedCompareExchangePointer ((volatile gpointer*)prev, value, cur) == cur) return TRUE; } }