void sgen_workers_join (void) { State old_state; int i; if (!collection_needs_workers ()) return; for (;;) { old_state = workers_state; SGEN_ASSERT (0, old_state.data.state != STATE_NURSERY_COLLECTION, "Can't be in nursery collection when joining"); if (old_state.data.state == STATE_WORKING) { State new_state = old_state; SGEN_ASSERT (0, !old_state.data.post_done, "Why is post_done already set?"); new_state.data.post_done = 1; if (!set_state (old_state, new_state)) continue; MONO_SEM_WAIT (&workers_done_sem); old_state = workers_state; } assert_not_working (old_state); /* * Checking whether there is still work left and, if not, going to sleep, * are two separate actions that are not performed atomically by the * workers. Therefore there's a race condition where work can be added * after they've checked for work, and before they've gone to sleep. */ if (!workers_job_queue_num_entries && sgen_section_gray_queue_is_empty (&workers_distribute_gray_queue)) break; workers_signal_enqueue_work (workers_num, FALSE); } /* At this point all the workers have stopped. */ if (sgen_get_major_collector ()->reset_worker_data) { for (i = 0; i < workers_num; ++i) sgen_get_major_collector ()->reset_worker_data (workers_data [i].major_collector_data); } g_assert (workers_job_queue_num_entries == 0); g_assert (sgen_section_gray_queue_is_empty (&workers_distribute_gray_queue)); for (i = 0; i < workers_num; ++i) g_assert (sgen_gray_object_queue_is_empty (&workers_data [i].private_gray_queue)); }
static void init_distribute_gray_queue (void) { if (workers_distribute_gray_queue_inited) { g_assert (sgen_section_gray_queue_is_empty (&workers_distribute_gray_queue)); g_assert (workers_distribute_gray_queue.locked); return; } sgen_section_gray_queue_init (&workers_distribute_gray_queue, TRUE, sgen_get_major_collector ()->is_concurrent ? concurrent_enqueue_check : NULL); workers_distribute_gray_queue_inited = TRUE; }
void sgen_section_gray_queue_init (SgenSectionGrayQueue *queue, gboolean locked, GrayQueueEnqueueCheckFunc enqueue_check_func) { g_assert (sgen_section_gray_queue_is_empty (queue)); queue->locked = locked; if (locked) { mono_os_mutex_init_recursive (&queue->lock); } #ifdef SGEN_CHECK_GRAY_OBJECT_ENQUEUE queue->enqueue_check_func = enqueue_check_func; #endif }
void sgen_workers_join (void) { int i; sgen_thread_pool_wait_for_all_jobs (); sgen_thread_pool_idle_wait (); SGEN_ASSERT (0, workers_state == STATE_NOT_WORKING, "Can only signal enqueue work when in no work state"); /* At this point all the workers have stopped. */ SGEN_ASSERT (0, sgen_section_gray_queue_is_empty (&workers_distribute_gray_queue), "Why is there still work left to do?"); for (i = 0; i < workers_num; ++i) SGEN_ASSERT (0, sgen_gray_object_queue_is_empty (&workers_data [i].private_gray_queue), "Why is there still work left to do?"); }
/* * Can only be called if the workers are stopped. * If we're stopped, there are also no pending jobs. */ gboolean sgen_workers_have_idle_work (void) { int i; SGEN_ASSERT (0, forced_stop && sgen_workers_all_done (), "Checking for idle work should only happen if the workers are stopped."); if (!sgen_section_gray_queue_is_empty (&workers_distribute_gray_queue)) return TRUE; for (i = 0; i < workers_num; ++i) { if (!sgen_gray_object_queue_is_empty (&workers_data [i].private_gray_queue)) return TRUE; } return FALSE; }
void sgen_workers_join (void) { State old_state, new_state; int i; if (!collection_needs_workers ()) return; do { old_state = new_state = workers_state; g_assert (old_state.data.gc_in_progress); g_assert (!old_state.data.done_posted); new_state.data.gc_in_progress = 0; } while (!set_state (old_state, new_state)); if (new_state.data.num_waiting == workers_num) { /* * All the workers have shut down but haven't posted * the done semaphore yet, or, if we come from below, * haven't done all their work 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. */ reawaken: workers_wake_up_all (); } MONO_SEM_WAIT (&workers_done_sem); old_state = new_state = workers_state; g_assert (old_state.data.num_waiting == workers_num); g_assert (old_state.data.done_posted); if (workers_job_queue_num_entries || !sgen_section_gray_queue_is_empty (&workers_distribute_gray_queue)) { /* * There's a small race condition that we avoid here. * It's possible that a worker thread runs out of * things to do, so it goes to sleep. Right at that * moment a new job is enqueued, but the thread is * still registered as running. Now the threads are * joined, and we wait for the semaphore. Only at * this point does the worker go to sleep, and posts * the semaphore, because workers_gc_in_progress is * already FALSE. The job is still in the queue, * though. * * Clear the done posted flag. */ new_state.data.done_posted = 0; if (!set_state (old_state, new_state)) g_assert_not_reached (); goto reawaken; } /* At this point all the workers have stopped. */ workers_marking = FALSE; if (sgen_get_major_collector ()->reset_worker_data) { for (i = 0; i < workers_num; ++i) sgen_get_major_collector ()->reset_worker_data (workers_data [i].major_collector_data); } g_assert (workers_job_queue_num_entries == 0); g_assert (sgen_section_gray_queue_is_empty (&workers_distribute_gray_queue)); for (i = 0; i < workers_num; ++i) { g_assert (!workers_data [i].stealable_stack_fill); g_assert (sgen_gray_object_queue_is_empty (&workers_data [i].private_gray_queue)); } }