/* Search @list for element with key @key. The nodes next, cur and prev are returned in @hp. Returns true if a node with key @key was found. This function cannot be called from a signal nor within interrupt context*. XXX A variant that works within interrupted is possible if needed. * interrupt context is when the current thread is reposible for another thread been suspended at an arbritary point. This is a limitation of our SMR implementation. */ gboolean mono_lls_find (MonoLinkedListSet *list, MonoThreadHazardPointers *hp, uintptr_t key) { 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 = get_hazardous_pointer_with_mask ((gpointer*)prev, hp, 1); while (1) { if (cur == NULL) return FALSE; next = 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 = 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_free_or_queue (cur, list->free_node_func); } else goto try_again; } cur = mono_lls_pointer_unmask (next); mono_hazard_pointer_set (hp, 1, cur); } }
/* Can be called with hp==NULL, in which case it acts as an ordinary pointer fetch. It's used that way indirectly from mono_jit_info_table_add(), which doesn't have to care about hazards because it holds the respective domain lock. */ gpointer mono_get_hazardous_pointer (gpointer volatile *pp, MonoThreadHazardPointers *hp, int hazard_index) { gpointer p; for (;;) { /* Get the pointer */ p = *pp; /* If we don't have hazard pointers just return the pointer. */ if (!hp) return p; /* Make it hazardous */ mono_hazard_pointer_set (hp, hazard_index, p); /* Check that it's still the same. If not, try again. */ if (*pp != p) { mono_hazard_pointer_clear (hp, hazard_index); continue; } break; } return p; }
void mono_thread_info_safe_suspend_and_run (MonoNativeThreadId id, gboolean interrupt_kernel, MonoSuspendThreadCallback callback, gpointer user_data) { int result; MonoThreadInfo *info = NULL; MonoThreadHazardPointers *hp = mono_hazard_pointer_get (); THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p\n", (void*)id); /*FIXME: unify this with self-suspend*/ g_assert (id != mono_native_thread_id_get ()); mono_thread_info_suspend_lock (); mono_threads_begin_global_suspend (); info = suspend_sync_nolock (id, interrupt_kernel); if (!info) goto done; switch (result = callback (info, user_data)) { case MonoResumeThread: mono_hazard_pointer_set (hp, 1, info); mono_thread_info_core_resume (info); mono_threads_wait_pending_operations (); break; case KeepSuspended: break; default: g_error ("Invalid suspend_and_run callback return value %d", result); } done: mono_hazard_pointer_clear (hp, 1); mono_threads_end_global_suspend (); mono_thread_info_suspend_unlock (); }
void mono_thread_info_finish_suspend_and_resume (MonoThreadInfo *info) { MonoThreadHazardPointers *hp = mono_hazard_pointer_get (); /*Resume can access info after the target has resumed, so we must ensure it won't touch freed memory. */ mono_hazard_pointer_set (hp, 1, info); mono_thread_info_core_resume (info); mono_hazard_pointer_clear (hp, 1); mono_atomic_store_release (&mono_thread_info_current ()->inside_critical_region, FALSE); }
/* 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; } }