static gboolean workers_get_work (WorkerData *data) { int i; g_assert (gray_object_queue_is_empty (&data->private_gray_queue)); /* Try to steal from our own stack. */ if (workers_steal (data, data, TRUE)) return TRUE; /* Then from the GC thread's stack. */ if (workers_steal (data, &workers_gc_thread_data, TRUE)) return TRUE; /* Finally, from another worker. */ for (i = 0; i < workers_num; ++i) { WorkerData *victim_data = &workers_data [i]; if (data == victim_data) continue; if (workers_steal (data, victim_data, TRUE)) return TRUE; } /* Nobody to steal from */ g_assert (gray_object_queue_is_empty (&data->private_gray_queue)); return FALSE; }
static void workers_join (void) { int i; if (!collection_is_parallel ()) return; g_assert (gray_object_queue_is_empty (&workers_gc_thread_data.private_gray_queue)); g_assert (gray_object_queue_is_empty (&workers_distribute_gray_queue)); g_assert (workers_gc_in_progress); workers_gc_in_progress = FALSE; if (workers_num_waiting == workers_num) { /* * All the workers might have shut down at this point * and posted the done semaphore but we don't know it * yet. It's not a big deal to wake them up again - * they'll just do one iteration of their loop trying to * find something to do and then go back to waiting * again. */ workers_wake_up_all (); } MONO_SEM_WAIT (&workers_done_sem); workers_marking = FALSE; if (major_collector.reset_worker_data) { for (i = 0; i < workers_num; ++i) major_collector.reset_worker_data (workers_data [i].major_collector_data); } g_assert (workers_done_posted); g_assert (!workers_gc_thread_data.stealable_stack_fill); g_assert (gray_object_queue_is_empty (&workers_gc_thread_data.private_gray_queue)); for (i = 0; i < workers_num; ++i) { g_assert (!workers_data [i].stealable_stack_fill); g_assert (gray_object_queue_is_empty (&workers_data [i].private_gray_queue)); } }
static void* workers_thread_func (void *data_untyped) { WorkerData *data = data_untyped; mono_thread_info_register_small_id (); if (major_collector.init_worker_thread) major_collector.init_worker_thread (data->major_collector_data); gray_object_queue_init_with_alloc_prepare (&data->private_gray_queue, workers_gray_queue_share_redirect, data); for (;;) { gboolean did_work = FALSE; while (workers_dequeue_and_do_job (data)) { did_work = TRUE; /* FIXME: maybe distribute the gray queue here? */ } if (workers_marking && (!gray_object_queue_is_empty (&data->private_gray_queue) || workers_get_work (data))) { g_assert (!gray_object_queue_is_empty (&data->private_gray_queue)); while (!drain_gray_stack (&data->private_gray_queue, 32)) workers_gray_queue_share_redirect (&data->private_gray_queue); g_assert (gray_object_queue_is_empty (&data->private_gray_queue)); gray_object_queue_init (&data->private_gray_queue); did_work = TRUE; } if (!did_work) workers_wait (); } /* dummy return to make compilers happy */ return NULL; }
static void workers_gray_queue_share_redirect (GrayQueue *queue) { GrayQueueSection *section; WorkerData *data = queue->alloc_prepare_data; if (data->stealable_stack_fill) { /* * There are still objects in the stealable stack, so * wake up any workers that might be sleeping */ if (workers_gc_in_progress) workers_wake_up_all (); return; } /* The stealable stack is empty, so fill it. */ pthread_mutex_lock (&data->stealable_stack_mutex); while (data->stealable_stack_fill < STEALABLE_STACK_SIZE && (section = gray_object_dequeue_section (queue))) { int num = MIN (section->end, STEALABLE_STACK_SIZE - data->stealable_stack_fill); memcpy (data->stealable_stack + data->stealable_stack_fill, section->objects + section->end - num, sizeof (char*) * num); section->end -= num; data->stealable_stack_fill += num; if (section->end) gray_object_enqueue_section (queue, section); else gray_object_free_queue_section (section); } if (data != &workers_gc_thread_data && gray_object_queue_is_empty (queue)) workers_steal (data, data, FALSE); pthread_mutex_unlock (&data->stealable_stack_mutex); if (workers_gc_in_progress) workers_wake_up_all (); }
static void gray_object_queue_init (GrayQueue *queue) { GrayQueueSection *section, *next; int i; g_assert (gray_object_queue_is_empty (queue)); DEBUG (9, g_assert (queue->balance == 0)); /* Free the extra sections allocated during the last collection */ i = 0; for (section = queue->free_list; section && i < GRAY_QUEUE_LENGTH_LIMIT - 1; section = section->next) i ++; if (!section) return; while (section->next) { next = section->next; section->next = next->next; gray_object_free_queue_section (next); } }
static inline char* gray_object_dequeue (GrayQueue *queue) { char *obj; if (gray_object_queue_is_empty (queue)) return NULL; DEBUG (9, g_assert (queue->first->end)); obj = queue->first->objects [--queue->first->end]; if (G_UNLIKELY (queue->first->end == 0)) { GrayQueueSection *section = queue->first; queue->first = section->next; section->next = queue->free_list; queue->free_list = section; } DEBUG (9, --queue->balance); return obj; }