void sgen_workers_enqueue_job (JobFunc func, void *data) { int num_entries; JobQueueEntry *entry; if (!collection_needs_workers ()) { func (NULL, data); return; } g_assert (workers_state.data.gc_in_progress); entry = sgen_alloc_internal (INTERNAL_MEM_JOB_QUEUE_ENTRY); entry->func = func; entry->data = data; mono_mutex_lock (&workers_job_queue_mutex); entry->next = workers_job_queue; workers_job_queue = entry; num_entries = ++workers_job_queue_num_entries; ++workers_num_jobs_enqueued; mono_mutex_unlock (&workers_job_queue_mutex); workers_wake_up (num_entries); }
void sgen_workers_start_all_workers (void) { State old_state, new_state; int i; gboolean result; if (!collection_needs_workers ()) return; if (sgen_get_major_collector ()->init_worker_thread) sgen_get_major_collector ()->init_worker_thread (workers_gc_thread_major_collector_data); old_state = new_state = workers_state; assert_not_working (old_state); g_assert (workers_job_queue_num_entries == 0); workers_num_jobs_enqueued = 0; workers_num_jobs_finished = 0; if (workers_started) { workers_signal_enqueue_work (workers_num, FALSE); return; } new_state.data.state = STATE_WORKING; new_state.data.num_awake = workers_num; result = set_state (old_state, new_state); SGEN_ASSERT (0, result, "Nobody else should have modified the state - workers have not been started yet"); for (i = 0; i < workers_num; ++i) workers_start_worker (i); workers_started = TRUE; }
void sgen_workers_enqueue_job (const char *name, JobFunc func, void *data) { int num_entries; JobQueueEntry *entry; if (!collection_needs_workers ()) { func (NULL, data); return; } entry = sgen_alloc_internal (INTERNAL_MEM_JOB_QUEUE_ENTRY); entry->name = name; entry->func = func; entry->data = data; mono_mutex_lock (&workers_job_queue_mutex); entry->next = workers_job_queue; workers_job_queue = entry; num_entries = ++workers_job_queue_num_entries; ++workers_num_jobs_enqueued; mono_mutex_unlock (&workers_job_queue_mutex); if (workers_state.data.state != STATE_NURSERY_COLLECTION) workers_signal_enqueue_work_if_necessary (num_entries < workers_num ? num_entries : workers_num); }
void sgen_workers_init_distribute_gray_queue (void) { if (!collection_needs_workers ()) return; init_distribute_gray_queue (sgen_get_major_collector ()->is_concurrent); }
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)); }
void sgen_workers_start_marking (void) { if (!collection_needs_workers ()) return; g_assert (workers_started && workers_state.data.gc_in_progress); g_assert (!workers_marking); workers_marking = TRUE; workers_wake_up_all (); }
void sgen_workers_start_all_workers (void) { State old_state, new_state; int i; if (!collection_needs_workers ()) return; if (sgen_get_major_collector ()->init_worker_thread) sgen_get_major_collector ()->init_worker_thread (workers_gc_thread_major_collector_data); old_state = new_state = workers_state; g_assert (!old_state.data.gc_in_progress); new_state.data.gc_in_progress = TRUE; workers_marking = FALSE; g_assert (workers_job_queue_num_entries == 0); workers_num_jobs_enqueued = 0; workers_num_jobs_finished = 0; if (workers_started) { g_assert (old_state.data.done_posted); if (old_state.data.num_waiting != workers_num) { g_error ("Expecting all %d sgen workers to be parked, but only %d are", workers_num, old_state.data.num_waiting); } /* Clear the done posted flag */ new_state.data.done_posted = 0; if (!set_state (old_state, new_state)) g_assert_not_reached (); workers_wake_up_all (); return; } g_assert (!old_state.data.done_posted); if (!set_state (old_state, new_state)) g_assert_not_reached (); for (i = 0; i < workers_num; ++i) workers_start_worker (i); workers_started = TRUE; }
static gboolean workers_dequeue_and_do_job (WorkerData *data) { JobQueueEntry *entry; int num_finished; /* * At this point the GC might not be running anymore. We * could have been woken up by a job that was then taken by * another thread, after which the collection finished, so we * first have to successfully dequeue a job before doing * anything assuming that the collection is still ongoing. */ if (!workers_job_queue_num_entries) return FALSE; mono_mutex_lock (&workers_job_queue_mutex); entry = (JobQueueEntry*)workers_job_queue; if (entry) { workers_job_queue = entry->next; --workers_job_queue_num_entries; } mono_mutex_unlock (&workers_job_queue_mutex); if (!entry) return FALSE; g_assert (collection_needs_workers ()); entry->func (data, entry->data); sgen_free_internal (entry, INTERNAL_MEM_JOB_QUEUE_ENTRY); SGEN_ATOMIC_ADD (workers_num_jobs_finished, 1); return TRUE; }
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)); } }