*table_ptr = new_table;
mono_memory_barrier ();
domain->num_jit_info_tables++;
- mono_thread_hazardous_free_or_queue (table, (MonoHazardousFreeFunc)mono_jit_info_table_free, TRUE, FALSE);
+ mono_thread_hazardous_free_or_queue (table, (MonoHazardousFreeFunc)mono_jit_info_table_free, HAZARD_FREE_MAY_LOCK, HAZARD_FREE_SAFE_CTX);
table = new_table;
goto restart;
if (domain->num_jit_info_tables <= 1) {
/* Can it actually happen that we only have one table
but ji is still hazardous? */
- mono_thread_hazardous_free_or_queue (ji, g_free, TRUE, FALSE);
+ mono_thread_hazardous_free_or_queue (ji, g_free, HAZARD_FREE_MAY_LOCK, HAZARD_FREE_SAFE_CTX);
} else {
domain->jit_info_free_queue = g_slist_prepend (domain->jit_info_free_queue, ji);
}
void
sgen_client_scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise, ScanCopyContext ctx)
{
- SgenThreadInfo *info;
-
scan_area_arg_start = start_nursery;
scan_area_arg_end = end_nursery;
}
}
}
- } END_FOREACH_THREAD
+ } FOREACH_THREAD_END
}
/*
{
SgenThreadInfo *cur_thread = mono_thread_info_current ();
kern_return_t ret;
- SgenThreadInfo *info;
int count = 0;
cur_thread->client_info.suspend_done = TRUE;
- FOREACH_THREAD_SAFE (info) {
+ FOREACH_THREAD (info) {
if (info == cur_thread || sgen_thread_pool_is_thread_pool_thread (mono_thread_info_get_tid (info)))
continue;
continue;
}
count ++;
- } END_FOREACH_THREAD_SAFE
+ } FOREACH_THREAD_END
return count;
}
sgen_thread_handshake (BOOL suspend)
{
int count, result;
- SgenThreadInfo *info;
int signum = suspend ? suspend_signal_num : restart_signal_num;
MonoNativeThreadId me = mono_native_thread_id_get ();
count = 0;
mono_thread_info_current ()->client_info.suspend_done = TRUE;
- FOREACH_THREAD_SAFE (info) {
+ FOREACH_THREAD (info) {
if (mono_native_thread_id_equals (mono_thread_info_get_tid (info), me)) {
continue;
}
} else {
info->client_info.skip = 1;
}
- } END_FOREACH_THREAD_SAFE
+ } FOREACH_THREAD_END
sgen_wait_for_suspend_ack (count);
int
sgen_thread_handshake (BOOL suspend)
{
- SgenThreadInfo *info;
SgenThreadInfo *current = mono_thread_info_current ();
int count = 0;
current->client_info.suspend_done = TRUE;
- FOREACH_THREAD_SAFE (info) {
+ FOREACH_THREAD (info) {
if (info == current)
continue;
info->client_info.suspend_done = FALSE;
continue;
}
++count;
- } END_FOREACH_THREAD_SAFE
+ } FOREACH_THREAD_END
return count;
}
static int
restart_threads_until_none_in_managed_allocator (void)
{
- SgenThreadInfo *info;
int num_threads_died = 0;
int sleep_duration = -1;
int restart_count = 0, restarted_count = 0;
/* restart all threads that stopped in the
allocator */
- FOREACH_THREAD_SAFE (info) {
+ FOREACH_THREAD (info) {
gboolean result;
if (info->client_info.skip || info->client_info.gc_disabled || info->client_info.suspend_done)
continue;
info->client_info.stopped_domain = NULL;
info->client_info.suspend_done = TRUE;
}
- } END_FOREACH_THREAD_SAFE
+ } FOREACH_THREAD_END
/* if no threads were restarted, we're done */
if (restart_count == 0)
break;
} else {
info->client_info.skip = 1;
}
- } END_FOREACH_THREAD
+ } FOREACH_THREAD_END
/* some threads might have died */
num_threads_died += restart_count - restarted_count;
/* wait for the threads to signal their suspension
void
sgen_client_restart_world (int generation, GGTimingInfo *timing)
{
- SgenThreadInfo *info;
TV_DECLARE (end_sw);
TV_DECLARE (start_handshake);
TV_DECLARE (end_bridge);
#else
memset (&info->client_info.regs, 0, sizeof (info->client_info.regs));
#endif
- } END_FOREACH_THREAD
+ } FOREACH_THREAD_END
TV_GETTIME (start_handshake);
sgen_unified_suspend_stop_world (void)
{
int restart_counter;
- SgenThreadInfo *info;
int sleep_duration = -1;
mono_threads_begin_global_suspend ();
THREADS_STW_DEBUG ("[GC-STW-BEGIN] *** BEGIN SUSPEND *** \n");
- FOREACH_THREAD_SAFE (info) {
+ FOREACH_THREAD (info) {
info->client_info.skip = FALSE;
info->client_info.suspend_done = FALSE;
if (sgen_is_thread_in_current_stw (info)) {
} else {
THREADS_STW_DEBUG ("[GC-STW-BEGIN-SUSPEND] IGNORE thread %p skip %d\n", mono_thread_info_get_tid (info), info->client_info.skip);
}
- } END_FOREACH_THREAD_SAFE
+ } FOREACH_THREAD_END
mono_thread_info_current ()->client_info.suspend_done = TRUE;
mono_threads_wait_pending_operations ();
for (;;) {
restart_counter = 0;
- FOREACH_THREAD_SAFE (info) {
+ FOREACH_THREAD (info) {
if (info->client_info.suspend_done || !sgen_is_thread_in_current_stw (info)) {
THREADS_STW_DEBUG ("[GC-STW-RESTART] IGNORE thread %p not been processed done %d current %d\n", mono_thread_info_get_tid (info), info->client_info.suspend_done, !sgen_is_thread_in_current_stw (info));
continue;
g_assert (!info->client_info.in_critical_region);
info->client_info.suspend_done = TRUE;
}
- } END_FOREACH_THREAD_SAFE
+ } FOREACH_THREAD_END
if (restart_counter == 0)
break;
sleep_duration += 10;
}
- FOREACH_THREAD_SAFE (info) {
+ FOREACH_THREAD (info) {
if (sgen_is_thread_in_current_stw (info) && mono_thread_info_is_running (info)) {
gboolean res = mono_thread_info_begin_suspend (info);
THREADS_STW_DEBUG ("[GC-STW-RESTART] SUSPEND thread %p skip %d\n", mono_thread_info_get_tid (info), res);
if (!res)
info->client_info.skip = TRUE;
}
- } END_FOREACH_THREAD_SAFE
+ } FOREACH_THREAD_END
mono_threads_wait_pending_operations ();
}
- FOREACH_THREAD_SAFE (info) {
+ FOREACH_THREAD (info) {
if (sgen_is_thread_in_current_stw (info)) {
THREADS_STW_DEBUG ("[GC-STW-SUSPEND-END] thread %p is suspended\n", mono_thread_info_get_tid (info));
g_assert (info->client_info.suspend_done);
} else {
g_assert (!info->client_info.suspend_done || info == mono_thread_info_current ());
}
- } END_FOREACH_THREAD_SAFE
+ } FOREACH_THREAD_END
}
static void
sgen_unified_suspend_restart_world (void)
{
- SgenThreadInfo *info;
-
THREADS_STW_DEBUG ("[GC-STW-END] *** BEGIN RESUME ***\n");
- FOREACH_THREAD_SAFE (info) {
+ FOREACH_THREAD (info) {
if (sgen_is_thread_in_current_stw (info)) {
g_assert (mono_thread_info_begin_resume (info));
THREADS_STW_DEBUG ("[GC-STW-RESUME-WORLD] RESUME thread %p\n", mono_thread_info_get_tid (info));
} else {
THREADS_STW_DEBUG ("[GC-STW-RESUME-WORLD] IGNORE thread %p\n", mono_thread_info_get_tid (info));
}
- } END_FOREACH_THREAD_SAFE
+ } FOREACH_THREAD_END
mono_threads_wait_pending_operations ();
mono_threads_end_global_suspend ();
MONO_SIG_HANDLER_FUNC (static, sigprof_signal_handler)
{
- MonoThreadInfo *info;
int old_errno = errno;
int hp_save_index;
MONO_SIG_HANDLER_GET_CONTEXT;
mono_threads_add_async_job (info, MONO_SERVICE_REQUEST_SAMPLE);
mono_threads_pthread_kill (info, profiling_signal_in_use);
- } END_FOREACH_THREAD_SAFE;
+ } FOREACH_THREAD_SAFE_END
}
mono_thread_info_set_is_async_context (TRUE);
void
sgen_clear_tlabs (void)
{
- SgenThreadInfo *info;
-
FOREACH_THREAD (info) {
/* A new TLAB will be allocated when the thread does its first allocation */
*info->tlab_start_addr = NULL;
*info->tlab_next_addr = NULL;
*info->tlab_temp_end_addr = NULL;
*info->tlab_real_end_addr = NULL;
- } END_FOREACH_THREAD
+ } FOREACH_THREAD_END
}
void
{
#ifndef SGEN_WITHOUT_MONO
int j;
- SgenThreadInfo *info;
char *endobj = obj + size;
FOREACH_THREAD (info) {
if (w >= (mword)obj && w < (mword)obj + size)
SGEN_LOG (0, "Object %p referenced in saved reg %d of thread %p (id %p)", obj, j, info, (gpointer)mono_thread_info_get_tid (info));
- } END_FOREACH_THREAD
- }
+ }
+ } FOREACH_THREAD_END
#endif
}
break;
case STATE_OUT:
if (InterlockedCompareExchange (&nodes [i].state, STATE_BUSY, STATE_OUT) == STATE_OUT) {
- result = mono_lls_find (&lls, hp, i);
+ 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);
+ 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);
break;
case STATE_IN:
if (InterlockedCompareExchange (&nodes [i].state, STATE_BUSY, STATE_IN) == STATE_IN) {
- result = mono_lls_find (&lls, hp, i);
+ 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);
+ result = mono_lls_remove (&lls, hp, &nodes [i].node, HAZARD_FREE_SAFE_CTX);
mono_hazard_pointer_clear_all (hp, -1);
++thread_data->num_removes;
mono_threads_init (&thread_callbacks, 0);
- mono_lls_init (&lls, free_node);
+ mono_lls_init (&lls, free_node, HAZARD_FREE_NO_LOCK);
for (i = 0; i < N; ++i) {
nodes [i].node.key = i;
typedef struct {
gpointer p;
MonoHazardousFreeFunc free_func;
- gboolean might_lock;
+ HazardFreeLocking locking;
} DelayedFreeItem;
/* The hazard table */
}
static gboolean
-try_free_delayed_free_item (gboolean lock_free_context)
+try_free_delayed_free_item (HazardFreeContext context)
{
DelayedFreeItem item;
gboolean popped = mono_lock_free_array_queue_pop (&delayed_free_queue, &item);
if (!popped)
return FALSE;
- if ((lock_free_context && item.might_lock) || (is_pointer_hazardous (item.p))) {
+ if ((context == HAZARD_FREE_ASYNC_CTX && item.locking == HAZARD_FREE_MAY_LOCK) ||
+ (is_pointer_hazardous (item.p))) {
mono_lock_free_array_queue_push (&delayed_free_queue, &item);
return FALSE;
}
void
mono_thread_hazardous_free_or_queue (gpointer p, MonoHazardousFreeFunc free_func,
- gboolean free_func_might_lock, gboolean lock_free_context)
+ HazardFreeLocking locking, HazardFreeContext context)
{
int i;
- if (lock_free_context)
- g_assert (!free_func_might_lock);
- if (free_func_might_lock)
- g_assert (!lock_free_context);
-
/* First try to free a few entries in the delayed free
table. */
for (i = 0; i < 3; ++i)
- try_free_delayed_free_item (lock_free_context);
+ try_free_delayed_free_item (context);
/* Now see if the pointer we're freeing is hazardous. If it
isn't, free it. Otherwise put it in the delay list. */
- if (is_pointer_hazardous (p)) {
- DelayedFreeItem item = { p, free_func, free_func_might_lock };
+ if ((context == HAZARD_FREE_ASYNC_CTX && locking == HAZARD_FREE_MAY_LOCK) ||
+ is_pointer_hazardous (p)) {
+ DelayedFreeItem item = { p, free_func, locking };
++hazardous_pointer_count;
void
mono_thread_hazardous_try_free_all (void)
{
- while (try_free_delayed_free_item (FALSE))
+ while (try_free_delayed_free_item (HAZARD_FREE_SAFE_CTX))
;
}
{
int i;
for (i = 0; i < 10; ++i)
- try_free_delayed_free_item (FALSE);
+ try_free_delayed_free_item (HAZARD_FREE_SAFE_CTX);
}
void
typedef void (*MonoHazardousFreeFunc) (gpointer p);
+typedef enum {
+ HAZARD_FREE_MAY_LOCK,
+ HAZARD_FREE_NO_LOCK,
+} HazardFreeLocking;
+
+typedef enum {
+ HAZARD_FREE_SAFE_CTX,
+ HAZARD_FREE_ASYNC_CTX,
+} HazardFreeContext;
+
void mono_thread_hazardous_free_or_queue (gpointer p, MonoHazardousFreeFunc free_func,
- gboolean free_func_might_lock, gboolean lock_free_context);
+ HazardFreeLocking locking, HazardFreeContext context);
void mono_thread_hazardous_try_free_all (void);
void mono_thread_hazardous_try_free_some (void);
MonoThreadHazardPointers* mono_hazard_pointer_get (void);
g_assert (desc->in_use);
desc->in_use = FALSE;
free_sb (desc->sb, desc->block_size);
- mono_thread_hazardous_free_or_queue (desc, desc_enqueue_avail, FALSE, TRUE);
+ mono_thread_hazardous_free_or_queue (desc, desc_enqueue_avail, HAZARD_FREE_NO_LOCK, HAZARD_FREE_ASYNC_CTX);
}
#else
MonoLockFreeQueue available_descs;
list_put_partial (Descriptor *desc)
{
g_assert (desc->anchor.data.state != STATE_FULL);
- mono_thread_hazardous_free_or_queue (desc, desc_put_partial, FALSE, TRUE);
+ mono_thread_hazardous_free_or_queue (desc, desc_put_partial, HAZARD_FREE_NO_LOCK, HAZARD_FREE_ASYNC_CTX);
}
static void
desc_retire (desc);
} else {
g_assert (desc->heap->sc == sc);
- mono_thread_hazardous_free_or_queue (desc, desc_put_partial, FALSE, TRUE);
+ mono_thread_hazardous_free_or_queue (desc, desc_put_partial, HAZARD_FREE_NO_LOCK, HAZARD_FREE_ASYNC_CTX);
if (++num_non_empty >= 2)
return;
}
g_assert (q->has_dummy);
q->has_dummy = 0;
mono_memory_write_barrier ();
- mono_thread_hazardous_free_or_queue (head, free_dummy, FALSE, TRUE);
+ mono_thread_hazardous_free_or_queue (head, free_dummy, HAZARD_FREE_NO_LOCK, HAZARD_FREE_ASYNC_CTX);
if (try_reenqueue_dummy (q))
goto retry;
return NULL;
static void
conc_table_lf_free (conc_table *table)
{
- mono_thread_hazardous_free_or_queue (table, conc_table_free, TRUE, FALSE);
+ mono_thread_hazardous_free_or_queue (table, conc_table_free, HAZARD_FREE_MAY_LOCK, HAZARD_FREE_SAFE_CTX);
}
/*
Initialize @list and will use @free_node_func to release memory.
If @free_node_func is null the caller is responsible for releasing node memory.
-@free_node_func must be lock-free. That implies that it cannot use malloc/free.
+If @free_node_func may lock, @free_node_func_locking must be
+HAZARD_FREE_MAY_LOCK; otherwise, HAZARD_FREE_NO_LOCK. It is ignored if
+@free_node_func is null.
*/
void
-mono_lls_init (MonoLinkedListSet *list, void (*free_node_func)(void *))
+mono_lls_init (MonoLinkedListSet *list, void (*free_node_func)(void *), HazardFreeLocking free_node_func_locking)
{
list->head = NULL;
list->free_node_func = free_node_func;
+ list->locking = free_node_func_locking;
}
/*
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.
-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)
+mono_lls_find (MonoLinkedListSet *list, MonoThreadHazardPointers *hp, uintptr_t key, HazardFreeContext context)
{
MonoLinkedListSetNode *cur, *next;
MonoLinkedListSetNode **prev;
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, FALSE, TRUE);
+ mono_thread_hazardous_free_or_queue (cur, list->free_node_func, list->locking, context);
} else
goto try_again;
}
/*
Insert @value into @list.
+@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 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)
+mono_lls_insert (MonoLinkedListSet *list, MonoThreadHazardPointers *hp, MonoLinkedListSetNode *value, HazardFreeContext context)
{
MonoLinkedListSetNode *cur, **prev;
/*We must do a store barrier before inserting
mono_memory_barrier ();
while (1) {
- if (mono_lls_find (list, hp, value->key))
+ if (mono_lls_find (list, hp, value->key, context))
return FALSE;
cur = (MonoLinkedListSetNode *) mono_hazard_pointer_get_val (hp, 1);
prev = (MonoLinkedListSetNode **) mono_hazard_pointer_get_val (hp, 2);
}
/*
-Search @list for element with key @key.
+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.
-This function cannot be called from a signal nor with the world stopped.
*/
gboolean
-mono_lls_remove (MonoLinkedListSet *list, MonoThreadHazardPointers *hp, MonoLinkedListSetNode *value)
+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))
+ if (!mono_lls_find (list, hp, value->key, context))
return FALSE;
next = (MonoLinkedListSetNode *) mono_hazard_pointer_get_val (hp, 0);
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, FALSE, TRUE);
+ mono_thread_hazardous_free_or_queue (value, list->free_node_func, list->locking, context);
} else
- mono_lls_find (list, hp, value->key);
+ mono_lls_find (list, hp, value->key, context);
return TRUE;
}
}
typedef struct {
MonoLinkedListSetNode *head;
void (*free_node_func)(void *);
+ HazardFreeLocking locking;
} MonoLinkedListSet;
*/
void
-mono_lls_init (MonoLinkedListSet *list, void (*free_node_func)(void *));
+mono_lls_init (MonoLinkedListSet *list, void (*free_node_func)(void *), HazardFreeLocking free_node_func_locking);
gboolean
-mono_lls_find (MonoLinkedListSet *list, MonoThreadHazardPointers *hp, uintptr_t key);
+mono_lls_find (MonoLinkedListSet *list, MonoThreadHazardPointers *hp, uintptr_t key, HazardFreeContext context);
gboolean
-mono_lls_insert (MonoLinkedListSet *list, MonoThreadHazardPointers *hp, MonoLinkedListSetNode *value);
+mono_lls_insert (MonoLinkedListSet *list, MonoThreadHazardPointers *hp, MonoLinkedListSetNode *value, HazardFreeContext context);
gboolean
-mono_lls_remove (MonoLinkedListSet *list, MonoThreadHazardPointers *hp, MonoLinkedListSetNode *value);
+mono_lls_remove (MonoLinkedListSet *list, MonoThreadHazardPointers *hp, MonoLinkedListSetNode *value, HazardFreeContext context);
gpointer
get_hazardous_pointer_with_mask (gpointer volatile *pp, MonoThreadHazardPointers *hp, int hazard_index);
-/*
-Requires the world to be stoped
-*/
-#define MONO_LLS_FOREACH(list, element, type) {\
- MonoLinkedListSetNode *__cur; \
- for (__cur = (list)->head; __cur; __cur = mono_lls_pointer_unmask (__cur->next)) \
- if (!mono_lls_pointer_get_mark (__cur->next)) { \
- (element) = (type)__cur;
-
-
-#define MONO_LLS_FOREACH_FILTERED(list, element, filter_func, type) {\
- MonoLinkedListSetNode *__cur; \
- for (__cur = (list)->head; __cur; __cur = (MonoLinkedListSetNode *)mono_lls_pointer_unmask (__cur->next)) \
- if (!mono_lls_pointer_get_mark (__cur->next)) { \
- (element) = (type)__cur; \
- if (!filter_func (element)) continue;
-
-#define MONO_LLS_END_FOREACH }}
-
-static inline MonoLinkedListSetNode*
-mono_lls_info_step (MonoLinkedListSetNode *val, MonoThreadHazardPointers *hp)
+static inline gboolean
+mono_lls_filter_accept_all (gpointer elem)
{
- val = (MonoLinkedListSetNode *) mono_lls_pointer_unmask (val);
- mono_hazard_pointer_set (hp, 1, val);
- return val;
+ return TRUE;
}
/*
-Provides snapshot iteration
-*/
-#define MONO_LLS_FOREACH_SAFE(list, element, type) {\
- MonoThreadHazardPointers *__hp = mono_hazard_pointer_get (); \
- MonoLinkedListSetNode *__cur, *__next; \
- for (__cur = (MonoLinkedListSetNode *) mono_lls_pointer_unmask (get_hazardous_pointer ((gpointer volatile*)&(list)->head, __hp, 1)); \
- __cur; \
- __cur = (MonoLinkedListSetNode *) mono_lls_info_step (__next, __hp)) { \
- __next = (MonoLinkedListSetNode *) get_hazardous_pointer_with_mask ((gpointer volatile*)&__cur->next, __hp, 0); \
- if (!mono_lls_pointer_get_mark (__next)) { \
- (element) = (type)__cur;
-
-#define MONO_LLS_FOREACH_FILTERED_SAFE(list, element, filter_func, type) {\
- MonoThreadHazardPointers *__hp = mono_hazard_pointer_get (); \
- MonoLinkedListSetNode *__cur, *__next; \
- for (__cur = (MonoLinkedListSetNode *) mono_lls_pointer_unmask (get_hazardous_pointer ((gpointer volatile*)&(list)->head, __hp, 1)); \
- __cur; \
- __cur = (MonoLinkedListSetNode *) mono_lls_info_step (__next, __hp)) { \
- __next = (MonoLinkedListSetNode *) get_hazardous_pointer_with_mask ((gpointer volatile*)&__cur->next, __hp, 0); \
- if (!mono_lls_pointer_get_mark (__next)) { \
- (element) = (type)__cur; \
- if (!filter_func (element)) continue;
-
-
-#define MONO_LLS_END_FOREACH_SAFE \
+ * These macros assume that no other threads are actively modifying the list.
+ */
+
+#define MONO_LLS_FOREACH_FILTERED(list, type, elem, filter) \
+ do { \
+ MonoLinkedListSet *list__ = (list); \
+ for (MonoLinkedListSetNode *cur__ = list__->head; cur__; cur__ = (MonoLinkedListSetNode *) mono_lls_pointer_unmask (cur__->next)) { \
+ if (!mono_lls_pointer_get_mark (cur__->next)) { \
+ type *elem = (type *) cur__; \
+ if (filter (elem)) {
+
+#define MONO_LLS_FOREACH_END \
+ } \
+ } \
} \
- } \
- mono_hazard_pointer_clear (__hp, 0); \
- mono_hazard_pointer_clear (__hp, 1); \
-}
+ } while (0);
+
+#define MONO_LLS_FOREACH(list, type, elem) \
+ MONO_LLS_FOREACH_FILTERED ((list), type, elem, mono_lls_filter_accept_all)
+
+/*
+ * These macros can be used while other threads are potentially modifying the
+ * list, but they only provide a snapshot of the list as a result.
+ *
+ * NOTE: Do NOT break out of the loop through any other means than a break
+ * statement, as other ways of breaking the loop will skip past important
+ * cleanup work.
+ */
+
+#define MONO_LLS_FOREACH_FILTERED_SAFE(list, type, elem, filter) \
+ do { \
+ /* NOTE: Keep this macro's code in sync with the mono_lls_find () logic. */ \
+ MonoLinkedListSet *list__ = (list); \
+ MonoThreadHazardPointers *hp__ = mono_hazard_pointer_get (); \
+ gboolean progress__ = FALSE; \
+ uintptr_t hkey__; \
+ gboolean restart__; \
+ do { \
+ restart__ = FALSE; \
+ MonoLinkedListSetNode **prev__ = &list__->head; \
+ mono_hazard_pointer_set (hp__, 2, prev__); \
+ MonoLinkedListSetNode *cur__ = (MonoLinkedListSetNode *) get_hazardous_pointer_with_mask ((gpointer *) prev__, hp__, 1); \
+ while (1) { \
+ if (!cur__) { \
+ break; \
+ } \
+ MonoLinkedListSetNode *next__ = (MonoLinkedListSetNode *) get_hazardous_pointer_with_mask ((gpointer *) &cur__->next, hp__, 0); \
+ uintptr_t ckey__ = cur__->key; \
+ mono_memory_read_barrier (); \
+ if (*prev__ != cur__) { \
+ restart__ = TRUE; \
+ break; \
+ } \
+ if (!mono_lls_pointer_get_mark (next__)) { \
+ if (!progress__ || ckey__ > hkey__) { \
+ progress__ = TRUE; \
+ hkey__ = ckey__; \
+ type *elem = (type *) cur__; \
+ if (filter (elem)) { \
+ gboolean broke__ = TRUE; \
+ gboolean done__ = FALSE; \
+ do { \
+ if (done__) { \
+ broke__ = FALSE; \
+ break; \
+ } \
+ done__ = TRUE;
+
+#define MONO_LLS_FOREACH_SAFE_END \
+ broke__ = FALSE; \
+ break; \
+ } while (1); \
+ if (broke__) { \
+ break; \
+ } \
+ } \
+ } \
+ 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__) { \
+ 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, list__->locking, HAZARD_FREE_ASYNC_CTX); \
+ } \
+ } else { \
+ restart__ = TRUE; \
+ break; \
+ } \
+ } \
+ cur__ = (MonoLinkedListSetNode *) mono_lls_pointer_unmask (next__); \
+ mono_hazard_pointer_set (hp__, 1, cur__); \
+ } \
+ } while (restart__); \
+ mono_hazard_pointer_clear (hp__, 0); \
+ mono_hazard_pointer_clear (hp__, 1); \
+ mono_hazard_pointer_clear (hp__, 2); \
+ } while (0);
+
+#define MONO_LLS_FOREACH_SAFE(list, type, elem) \
+ MONO_LLS_FOREACH_FILTERED_SAFE ((list), type, elem, mono_lls_filter_accept_all)
#endif /* __MONO_SPLIT_ORDERED_LIST_H__ */
static void
dump_threads (void)
{
- MonoThreadInfo *info;
MonoThreadInfo *cur = mono_thread_info_current ();
MOSTLY_ASYNC_SAFE_PRINTF ("STATE CUE CARD: (? means a positive number, usually 1 or 2, * means any number)\n");
#else
MOSTLY_ASYNC_SAFE_PRINTF ("--thread %p id %p [%p] state %x %s\n", info, (void *) mono_thread_info_get_tid (info), (void*)(size_t)info->native_handle, info->thread_state, info == cur ? "GC INITIATOR" : "" );
#endif
-
- } END_FOREACH_THREAD_SAFE
+ } FOREACH_THREAD_SAFE_END
}
gboolean
{
MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
- if (!mono_lls_find (&thread_list, hp, (uintptr_t)id)) {
+ if (!mono_lls_find (&thread_list, hp, (uintptr_t)id, HAZARD_FREE_ASYNC_CTX)) {
mono_hazard_pointer_clear_all (hp, -1);
return NULL;
}
{
MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
- if (!mono_lls_insert (&thread_list, hp, (MonoLinkedListSetNode*)info)) {
+ if (!mono_lls_insert (&thread_list, hp, (MonoLinkedListSetNode*)info, HAZARD_FREE_SAFE_CTX)) {
mono_hazard_pointer_clear_all (hp, -1);
return FALSE;
}
gboolean res;
THREADS_DEBUG ("removing info %p\n", info);
- res = mono_lls_remove (&thread_list, hp, (MonoLinkedListSetNode*)info);
+ res = mono_lls_remove (&thread_list, hp, (MonoLinkedListSetNode*)info, HAZARD_FREE_SAFE_CTX);
mono_hazard_pointer_clear_all (hp, -1);
return res;
}
g_byte_array_free (info->stackdata, /*free_segment=*/TRUE);
/*now it's safe to free the thread info.*/
- mono_thread_hazardous_free_or_queue (info, free_thread_info, TRUE, FALSE);
+ mono_thread_hazardous_free_or_queue (info, free_thread_info, HAZARD_FREE_MAY_LOCK, HAZARD_FREE_SAFE_CTX);
mono_thread_small_id_free (small_id);
}
mono_coop_sem_init (&global_suspend_semaphore, 1);
mono_os_sem_init (&suspend_semaphore, 0);
- mono_lls_init (&thread_list, NULL);
+ mono_lls_init (&thread_list, NULL, HAZARD_FREE_NO_LOCK);
mono_thread_smr_init ();
mono_threads_init_platform ();
mono_threads_init_coop ();
/*
Requires the world to be stoped
*/
-#define FOREACH_THREAD(thread) MONO_LLS_FOREACH_FILTERED (mono_thread_info_list_head (), thread, mono_threads_filter_tools_threads, THREAD_INFO_TYPE*)
-#define END_FOREACH_THREAD MONO_LLS_END_FOREACH
+#define FOREACH_THREAD(thread) \
+ MONO_LLS_FOREACH_FILTERED (mono_thread_info_list_head (), THREAD_INFO_TYPE, thread, mono_threads_filter_tools_threads)
+
+#define FOREACH_THREAD_END \
+ MONO_LLS_FOREACH_END
/*
Snapshot iteration.
*/
-#define FOREACH_THREAD_SAFE(thread) MONO_LLS_FOREACH_FILTERED_SAFE (mono_thread_info_list_head (), thread, mono_threads_filter_tools_threads, THREAD_INFO_TYPE*)
-#define END_FOREACH_THREAD_SAFE MONO_LLS_END_FOREACH_SAFE
+#define FOREACH_THREAD_SAFE(thread) \
+ MONO_LLS_FOREACH_FILTERED_SAFE (mono_thread_info_list_head (), THREAD_INFO_TYPE, thread, mono_threads_filter_tools_threads)
+
+#define FOREACH_THREAD_SAFE_END \
+ MONO_LLS_FOREACH_SAFE_END
static inline MonoNativeThreadId
mono_thread_info_get_tid (THREAD_INFO_TYPE *info)