2 * sgen-workers.c: Worker threads for parallel and concurrent GC.
4 * Copyright 2001-2003 Ximian, Inc
5 * Copyright 2003-2010 Novell, Inc.
6 * Copyright (C) 2012 Xamarin Inc
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License 2.0 as published by the Free Software Foundation;
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public
18 * License 2.0 along with this library; if not, write to the Free
19 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 #include "metadata/sgen-gc.h"
26 #include "metadata/sgen-workers.h"
27 #include "metadata/sgen-thread-pool.h"
28 #include "utils/mono-counters.h"
30 static int workers_num;
31 static WorkerData *workers_data;
33 static SgenSectionGrayQueue workers_distribute_gray_queue;
34 static gboolean workers_distribute_gray_queue_inited;
39 STATE_NURSERY_COLLECTION
44 static volatile State workers_state;
46 static guint64 stat_workers_num_finished;
49 set_state (State old_state, State new_state)
51 if (old_state == STATE_NURSERY_COLLECTION)
52 SGEN_ASSERT (0, new_state != STATE_NOT_WORKING, "Can't go from nursery collection to not working");
54 return InterlockedCompareExchange (&workers_state, new_state, old_state) == old_state;
58 assert_not_working (State state)
60 SGEN_ASSERT (0, state == STATE_NOT_WORKING, "Can only signal enqueue work when in no work state");
64 assert_working (State state)
66 SGEN_ASSERT (0, state == STATE_WORKING, "A worker can't wait without being in working state");
70 assert_nursery_collection (State state)
72 SGEN_ASSERT (0, state == STATE_NURSERY_COLLECTION, "Must be in the nursery collection state");
76 assert_working_or_nursery_collection (State state)
78 if (state != STATE_WORKING)
79 assert_nursery_collection (state);
83 workers_signal_enqueue_work (gboolean from_nursery_collection)
85 State old_state = workers_state;
86 gboolean did_set_state;
88 if (from_nursery_collection)
89 assert_nursery_collection (old_state);
91 assert_not_working (old_state);
93 did_set_state = set_state (old_state, STATE_WORKING);
94 SGEN_ASSERT (0, did_set_state, "Nobody else should be mutating the state");
96 sgen_thread_pool_idle_signal ();
100 workers_signal_enqueue_work_if_necessary (void)
102 if (workers_state == STATE_NOT_WORKING)
103 workers_signal_enqueue_work (FALSE);
107 sgen_workers_ensure_awake (void)
109 SGEN_ASSERT (0, workers_state != STATE_NURSERY_COLLECTION, "Can't wake workers during nursery collection");
110 workers_signal_enqueue_work_if_necessary ();
118 ++stat_workers_num_finished;
121 old_state = workers_state;
123 assert_working_or_nursery_collection (old_state);
124 if (old_state == STATE_NURSERY_COLLECTION)
127 /* We are the last thread to go to sleep. */
128 } while (!set_state (old_state, STATE_NOT_WORKING));
132 collection_needs_workers (void)
134 return sgen_collection_is_concurrent ();
138 sgen_workers_enqueue_job (SgenThreadPoolJob *job)
140 if (!collection_needs_workers ()) {
141 job->func (NULL, job);
142 sgen_thread_pool_job_free (job);
146 sgen_thread_pool_job_enqueue (job);
150 sgen_workers_wait_for_jobs_finished (void)
152 sgen_thread_pool_wait_for_all_jobs ();
156 sgen_workers_signal_start_nursery_collection_and_wait (void)
161 old_state = workers_state;
163 if (old_state != STATE_NOT_WORKING)
164 assert_working (old_state);
165 } while (!set_state (old_state, STATE_NURSERY_COLLECTION));
167 sgen_thread_pool_idle_wait ();
169 assert_nursery_collection (workers_state);
173 sgen_workers_signal_finish_nursery_collection (void)
175 assert_nursery_collection (workers_state);
176 workers_signal_enqueue_work (TRUE);
180 workers_get_work (WorkerData *data)
182 SgenMajorCollector *major;
184 g_assert (sgen_gray_object_queue_is_empty (&data->private_gray_queue));
186 /* If we're concurrent, steal from the workers distribute gray queue. */
187 major = sgen_get_major_collector ();
188 if (major->is_concurrent) {
189 GrayQueueSection *section = sgen_section_gray_queue_dequeue (&workers_distribute_gray_queue);
191 sgen_gray_object_enqueue_section (&data->private_gray_queue, section);
196 /* Nobody to steal from */
197 g_assert (sgen_gray_object_queue_is_empty (&data->private_gray_queue));
202 concurrent_enqueue_check (char *obj)
204 g_assert (sgen_concurrent_collection_in_progress ());
205 g_assert (!sgen_ptr_in_nursery (obj));
206 g_assert (SGEN_LOAD_VTABLE (obj));
210 init_private_gray_queue (WorkerData *data)
212 sgen_gray_object_queue_init (&data->private_gray_queue,
213 sgen_get_major_collector ()->is_concurrent ? concurrent_enqueue_check : NULL);
217 thread_pool_init_func (void *data_untyped)
219 WorkerData *data = data_untyped;
220 SgenMajorCollector *major = sgen_get_major_collector ();
222 mono_thread_info_register_small_id ();
224 if (!major->is_concurrent)
227 init_private_gray_queue (data);
231 marker_idle_func (void *data_untyped)
233 WorkerData *data = data_untyped;
234 SgenMajorCollector *major = sgen_get_major_collector ();
236 if (workers_state != STATE_WORKING)
239 SGEN_ASSERT (0, sgen_get_current_collection_generation () != GENERATION_NURSERY, "Why are we doing work while there's a nursery collection happening?");
241 if (!sgen_gray_object_queue_is_empty (&data->private_gray_queue) || workers_get_work (data)) {
242 SgenObjectOperations *ops = sgen_concurrent_collection_in_progress ()
243 ? &major->major_concurrent_ops
245 ScanCopyContext ctx = { ops->scan_object, NULL, &data->private_gray_queue };
247 SGEN_ASSERT (0, !sgen_gray_object_queue_is_empty (&data->private_gray_queue), "How is our gray queue empty if we just got work?");
249 sgen_drain_gray_stack (32, ctx);
260 init_distribute_gray_queue (void)
262 if (workers_distribute_gray_queue_inited) {
263 g_assert (sgen_section_gray_queue_is_empty (&workers_distribute_gray_queue));
264 g_assert (workers_distribute_gray_queue.locked);
268 sgen_section_gray_queue_init (&workers_distribute_gray_queue, TRUE,
269 sgen_get_major_collector ()->is_concurrent ? concurrent_enqueue_check : NULL);
270 workers_distribute_gray_queue_inited = TRUE;
274 sgen_workers_init_distribute_gray_queue (void)
276 SGEN_ASSERT (0, sgen_get_major_collector ()->is_concurrent && collection_needs_workers (),
277 "Why should we init the distribute gray queue if we don't need it?");
278 init_distribute_gray_queue ();
282 sgen_workers_init (int num_workers)
285 void *workers_data_ptrs [num_workers];
287 if (!sgen_get_major_collector ()->is_concurrent) {
288 sgen_thread_pool_init (num_workers, thread_pool_init_func, NULL, NULL);
292 //g_print ("initing %d workers\n", num_workers);
294 workers_num = num_workers;
296 workers_data = sgen_alloc_internal_dynamic (sizeof (WorkerData) * num_workers, INTERNAL_MEM_WORKER_DATA, TRUE);
297 memset (workers_data, 0, sizeof (WorkerData) * num_workers);
299 init_distribute_gray_queue ();
301 for (i = 0; i < workers_num; ++i)
302 workers_data_ptrs [i] = &workers_data [i];
304 sgen_thread_pool_init (num_workers, thread_pool_init_func, marker_idle_func, workers_data_ptrs);
306 mono_counters_register ("# workers finished", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_workers_num_finished);
310 sgen_workers_start_all_workers (void)
312 if (!collection_needs_workers ())
315 workers_signal_enqueue_work (FALSE);
319 sgen_workers_join (void)
323 if (!collection_needs_workers ())
326 sgen_thread_pool_wait_for_all_jobs ();
329 SGEN_ASSERT (0, workers_state != STATE_NURSERY_COLLECTION, "Can't be in nursery collection when joining");
330 sgen_thread_pool_idle_wait ();
331 assert_not_working (workers_state);
334 * Checking whether there is still work left and, if not, going to sleep,
335 * are two separate actions that are not performed atomically by the
336 * workers. Therefore there's a race condition where work can be added
337 * after they've checked for work, and before they've gone to sleep.
339 if (sgen_section_gray_queue_is_empty (&workers_distribute_gray_queue))
342 workers_signal_enqueue_work (FALSE);
345 /* At this point all the workers have stopped. */
347 g_assert (sgen_section_gray_queue_is_empty (&workers_distribute_gray_queue));
348 for (i = 0; i < workers_num; ++i)
349 g_assert (sgen_gray_object_queue_is_empty (&workers_data [i].private_gray_queue));
353 sgen_workers_all_done (void)
355 return workers_state == STATE_NOT_WORKING;
359 sgen_workers_are_working (void)
361 return workers_state == STATE_WORKING;
365 sgen_workers_wait (void)
367 sgen_thread_pool_idle_wait ();
368 SGEN_ASSERT (0, sgen_workers_all_done (), "Why are the workers not done after we wait for them?");
371 SgenSectionGrayQueue*
372 sgen_workers_get_distribute_section_gray_queue (void)
374 return &workers_distribute_gray_queue;