1 /**
2  * \file
3  * Simple generational GC.
4  *
5  * Author:
6  * 	Paolo Molaro (lupus@ximian.com)
7  *  Rodrigo Kumpera (kumpera@gmail.com)
8  *
9  * Copyright 2005-2011 Novell, Inc (http://www.novell.com)
10  * Copyright 2011 Xamarin Inc (http://www.xamarin.com)
11  *
12  * Thread start/stop adapted from Boehm's GC:
13  * Copyright (c) 1994 by Xerox Corporation.  All rights reserved.
14  * Copyright (c) 1996 by Silicon Graphics.  All rights reserved.
15  * Copyright (c) 1998 by Fergus Henderson.  All rights reserved.
16  * Copyright (c) 2000-2004 by Hewlett-Packard Company.  All rights reserved.
17  * Copyright 2001-2003 Ximian, Inc
18  * Copyright 2003-2010 Novell, Inc.
19  * Copyright 2011 Xamarin, Inc.
20  * Copyright (C) 2012 Xamarin Inc
21  *
22  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
23  *
24  * Important: allocation provides always zeroed memory, having to do
25  * a memset after allocation is deadly for performance.
26  * Memory usage at startup is currently as follows:
27  * 64 KB pinned space
28  * 64 KB internal space
29  * size of nursery
30  * We should provide a small memory config with half the sizes
31  *
32  * We currently try to make as few mono assumptions as possible:
33  * 1) 2-word header with no GC pointers in it (first vtable, second to store the
34  *    forwarding ptr)
35  * 2) gc descriptor is the second word in the vtable (first word in the class)
36  * 3) 8 byte alignment is the minimum and enough (not true for special structures (SIMD), FIXME)
37  * 4) there is a function to get an object's size and the number of
38  *    elements in an array.
39  * 5) we know the special way bounds are allocated for complex arrays
40  * 6) we know about proxies and how to treat them when domains are unloaded
41  *
42  * Always try to keep stack usage to a minimum: no recursive behaviour
43  * and no large stack allocs.
44  *
45  * General description.
46  * Objects are initially allocated in a nursery using a fast bump-pointer technique.
47  * When the nursery is full we start a nursery collection: this is performed with a
48  * copying GC.
49  * When the old generation is full we start a copying GC of the old generation as well:
50  * this will be changed to mark&sweep with copying when fragmentation becomes to severe
51  * in the future.  Maybe we'll even do both during the same collection like IMMIX.
52  *
53  * The things that complicate this description are:
54  * *) pinned objects: we can't move them so we need to keep track of them
55  * *) no precise info of the thread stacks and registers: we need to be able to
56  *    quickly find the objects that may be referenced conservatively and pin them
57  *    (this makes the first issues more important)
58  * *) large objects are too expensive to be dealt with using copying GC: we handle them
59  *    with mark/sweep during major collections
60  * *) some objects need to not move even if they are small (interned strings, Type handles):
61  *    we use mark/sweep for them, too: they are not allocated in the nursery, but inside
62  *    PinnedChunks regions
63  */
64 
65 /*
66  * TODO:
67 
68  *) we could have a function pointer in MonoClass to implement
69   customized write barriers for value types
70 
71  *) investigate the stuff needed to advance a thread to a GC-safe
72   point (single-stepping, read from unmapped memory etc) and implement it.
73   This would enable us to inline allocations and write barriers, for example,
74   or at least parts of them, like the write barrier checks.
75   We may need this also for handling precise info on stacks, even simple things
76   as having uninitialized data on the stack and having to wait for the prolog
77   to zero it. Not an issue for the last frame that we scan conservatively.
78   We could always not trust the value in the slots anyway.
79 
80  *) modify the jit to save info about references in stack locations:
81   this can be done just for locals as a start, so that at least
82   part of the stack is handled precisely.
83 
84  *) test/fix endianess issues
85 
86  *) Implement a card table as the write barrier instead of remembered
87     sets?  Card tables are not easy to implement with our current
88     memory layout.  We have several different kinds of major heap
89     objects: Small objects in regular blocks, small objects in pinned
90     chunks and LOS objects.  If we just have a pointer we have no way
91     to tell which kind of object it points into, therefore we cannot
92     know where its card table is.  The least we have to do to make
93     this happen is to get rid of write barriers for indirect stores.
94     (See next item)
95 
96  *) Get rid of write barriers for indirect stores.  We can do this by
97     telling the GC to wbarrier-register an object once we do an ldloca
98     or ldelema on it, and to unregister it once it's not used anymore
99     (it can only travel downwards on the stack).  The problem with
100     unregistering is that it needs to happen eventually no matter
101     what, even if exceptions are thrown, the thread aborts, etc.
102     Rodrigo suggested that we could do only the registering part and
103     let the collector find out (pessimistically) when it's safe to
104     unregister, namely when the stack pointer of the thread that
105     registered the object is higher than it was when the registering
106     happened.  This might make for a good first implementation to get
107     some data on performance.
108 
109  *) Some sort of blacklist support?  Blacklists is a concept from the
110     Boehm GC: if during a conservative scan we find pointers to an
111     area which we might use as heap, we mark that area as unusable, so
112     pointer retention by random pinning pointers is reduced.
113 
114  *) experiment with max small object size (very small right now - 2kb,
115     because it's tied to the max freelist size)
116 
117   *) add an option to mmap the whole heap in one chunk: it makes for many
118      simplifications in the checks (put the nursery at the top and just use a single
119      check for inclusion/exclusion): the issue this has is that on 32 bit systems it's
120      not flexible (too much of the address space may be used by default or we can't
121      increase the heap as needed) and we'd need a race-free mechanism to return memory
122      back to the system (mprotect(PROT_NONE) will still keep the memory allocated if it
123      was written to, munmap is needed, but the following mmap may not find the same segment
124      free...)
125 
126  *) memzero the major fragments after restarting the world and optionally a smaller
127     chunk at a time
128 
129  *) investigate having fragment zeroing threads
130 
131  *) separate locks for finalization and other minor stuff to reduce
132     lock contention
133 
134  *) try a different copying order to improve memory locality
135 
136  *) a thread abort after a store but before the write barrier will
137     prevent the write barrier from executing
138 
139  *) specialized dynamically generated markers/copiers
140 
141  *) Dynamically adjust TLAB size to the number of threads.  If we have
142     too many threads that do allocation, we might need smaller TLABs,
143     and we might get better performance with larger TLABs if we only
144     have a handful of threads.  We could sum up the space left in all
145     assigned TLABs and if that's more than some percentage of the
146     nursery size, reduce the TLAB size.
147 
148  *) Explore placing unreachable objects on unused nursery memory.
149 	Instead of memset'ng a region to zero, place an int[] covering it.
150 	A good place to start is add_nursery_frag. The tricky thing here is
151 	placing those objects atomically outside of a collection.
152 
153  *) Allocation should use asymmetric Dekker synchronization:
154  	http://blogs.oracle.com/dave/resource/Asymmetric-Dekker-Synchronization.txt
155 	This should help weak consistency archs.
156  */
157 #include "config.h"
158 #ifdef HAVE_SGEN_GC
159 
160 #ifdef __MACH__
161 #undef _XOPEN_SOURCE
162 #define _XOPEN_SOURCE
163 #define _DARWIN_C_SOURCE
164 #endif
165 
166 #ifdef HAVE_UNISTD_H
167 #include <unistd.h>
168 #endif
169 #ifdef HAVE_PTHREAD_H
170 #include <pthread.h>
171 #endif
172 #ifdef HAVE_PTHREAD_NP_H
173 #include <pthread_np.h>
174 #endif
175 #include <stdio.h>
176 #include <string.h>
177 #include <errno.h>
178 #include <assert.h>
179 #include <stdlib.h>
180 #include <glib.h>
181 
182 #include "mono/sgen/sgen-gc.h"
183 #include "mono/sgen/sgen-cardtable.h"
184 #include "mono/sgen/sgen-protocol.h"
185 #include "mono/sgen/sgen-memory-governor.h"
186 #include "mono/sgen/sgen-hash-table.h"
187 #include "mono/sgen/sgen-pinning.h"
188 #include "mono/sgen/sgen-workers.h"
189 #include "mono/sgen/sgen-client.h"
190 #include "mono/sgen/sgen-pointer-queue.h"
191 #include "mono/sgen/gc-internal-agnostic.h"
192 #include "mono/utils/mono-proclib.h"
193 #include "mono/utils/mono-memory-model.h"
194 #include "mono/utils/hazard-pointer.h"
195 
196 #include <mono/utils/memcheck.h>
197 #include <mono/utils/mono-mmap-internals.h>
198 #include <mono/utils/unlocked.h>
199 
200 #undef pthread_create
201 #undef pthread_join
202 #undef pthread_detach
203 
204 /*
205  * ######################################################################
206  * ########  Types and constants used by the GC.
207  * ######################################################################
208  */
209 
210 /* 0 means not initialized, 1 is initialized, -1 means in progress */
211 static int gc_initialized = 0;
212 /* If set, check if we need to do something every X allocations */
213 gboolean has_per_allocation_action;
214 /* If set, do a heap check every X allocation */
215 guint32 verify_before_allocs = 0;
216 /* If set, do a minor collection before every X allocation */
217 guint32 collect_before_allocs = 0;
218 /* If set, do a whole heap check before each collection */
219 static gboolean whole_heap_check_before_collection = FALSE;
220 /* If set, do a remset consistency check at various opportunities */
221 static gboolean remset_consistency_checks = FALSE;
222 /* If set, do a mod union consistency check before each finishing collection pause */
223 static gboolean mod_union_consistency_check = FALSE;
224 /* If set, check whether mark bits are consistent after major collections */
225 static gboolean check_mark_bits_after_major_collection = FALSE;
226 /* If set, check that all nursery objects are pinned/not pinned, depending on context */
227 static gboolean check_nursery_objects_pinned = FALSE;
228 /* If set, do a few checks when the concurrent collector is used */
229 static gboolean do_concurrent_checks = FALSE;
230 /* If set, do a plausibility check on the scan_starts before and after
231    each collection */
232 static gboolean do_scan_starts_check = FALSE;
233 
234 static gboolean disable_minor_collections = FALSE;
235 static gboolean disable_major_collections = FALSE;
236 static gboolean do_verify_nursery = FALSE;
237 static gboolean do_dump_nursery_content = FALSE;
238 static gboolean enable_nursery_canaries = FALSE;
239 
240 static gboolean precleaning_enabled = TRUE;
241 static gboolean dynamic_nursery = FALSE;
242 static size_t min_nursery_size = 0;
243 static size_t max_nursery_size = 0;
244 
245 #ifdef HEAVY_STATISTICS
246 guint64 stat_objects_alloced_degraded = 0;
247 guint64 stat_bytes_alloced_degraded = 0;
248 
249 guint64 stat_copy_object_called_nursery = 0;
250 guint64 stat_objects_copied_nursery = 0;
251 guint64 stat_copy_object_called_major = 0;
252 guint64 stat_objects_copied_major = 0;
253 
254 guint64 stat_scan_object_called_nursery = 0;
255 guint64 stat_scan_object_called_major = 0;
256 
257 guint64 stat_slots_allocated_in_vain;
258 
259 guint64 stat_nursery_copy_object_failed_from_space = 0;
260 guint64 stat_nursery_copy_object_failed_forwarded = 0;
261 guint64 stat_nursery_copy_object_failed_pinned = 0;
262 guint64 stat_nursery_copy_object_failed_to_space = 0;
263 
264 static guint64 stat_wbarrier_add_to_global_remset = 0;
265 static guint64 stat_wbarrier_arrayref_copy = 0;
266 static guint64 stat_wbarrier_generic_store = 0;
267 static guint64 stat_wbarrier_generic_store_atomic = 0;
268 static guint64 stat_wbarrier_set_root = 0;
269 #endif
270 
271 static guint64 stat_pinned_objects = 0;
272 
273 static guint64 time_minor_pre_collection_fragment_clear = 0;
274 static guint64 time_minor_pinning = 0;
275 static guint64 time_minor_scan_remsets = 0;
276 static guint64 time_minor_scan_major_blocks = 0;
277 static guint64 time_minor_scan_los = 0;
278 static guint64 time_minor_scan_pinned = 0;
279 static guint64 time_minor_scan_roots = 0;
280 static guint64 time_minor_finish_gray_stack = 0;
281 static guint64 time_minor_fragment_creation = 0;
282 
283 static guint64 time_major_pre_collection_fragment_clear = 0;
284 static guint64 time_major_pinning = 0;
285 static guint64 time_major_scan_pinned = 0;
286 static guint64 time_major_scan_roots = 0;
287 static guint64 time_major_scan_mod_union_blocks = 0;
288 static guint64 time_major_scan_mod_union_los = 0;
289 static guint64 time_major_finish_gray_stack = 0;
290 static guint64 time_major_free_bigobjs = 0;
291 static guint64 time_major_los_sweep = 0;
292 static guint64 time_major_sweep = 0;
293 static guint64 time_major_fragment_creation = 0;
294 
295 static guint64 time_max = 0;
296 
297 static int sgen_max_pause_time = SGEN_DEFAULT_MAX_PAUSE_TIME;
298 static float sgen_max_pause_margin = SGEN_DEFAULT_MAX_PAUSE_MARGIN;
299 
300 static SGEN_TV_DECLARE (time_major_conc_collection_start);
301 static SGEN_TV_DECLARE (time_major_conc_collection_end);
302 
303 int gc_debug_level = 0;
304 FILE* gc_debug_file;
305 static char* gc_params_options;
306 static char* gc_debug_options;
307 
308 /*
309 void
310 mono_gc_flush_info (void)
311 {
312 	fflush (gc_debug_file);
313 }
314 */
315 
316 #define TV_DECLARE SGEN_TV_DECLARE
317 #define TV_GETTIME SGEN_TV_GETTIME
318 #define TV_ELAPSED SGEN_TV_ELAPSED
319 
320 static SGEN_TV_DECLARE (sgen_init_timestamp);
321 
322 NurseryClearPolicy nursery_clear_policy = CLEAR_AT_TLAB_CREATION;
323 
324 #define object_is_forwarded	SGEN_OBJECT_IS_FORWARDED
325 #define object_is_pinned	SGEN_OBJECT_IS_PINNED
326 #define pin_object		SGEN_PIN_OBJECT
327 
328 #define ptr_in_nursery sgen_ptr_in_nursery
329 
330 #define LOAD_VTABLE	SGEN_LOAD_VTABLE
331 
332 gboolean
nursery_canaries_enabled(void)333 nursery_canaries_enabled (void)
334 {
335 	return enable_nursery_canaries;
336 }
337 
338 #define safe_object_get_size	sgen_safe_object_get_size
339 
340 typedef enum {
341 	SGEN_MAJOR_DEFAULT,
342 	SGEN_MAJOR_SERIAL,
343 	SGEN_MAJOR_CONCURRENT,
344 	SGEN_MAJOR_CONCURRENT_PARALLEL
345 } SgenMajor;
346 
347 typedef enum {
348 	SGEN_MINOR_DEFAULT,
349 	SGEN_MINOR_SIMPLE,
350 	SGEN_MINOR_SIMPLE_PARALLEL,
351 	SGEN_MINOR_SPLIT
352 } SgenMinor;
353 
354 typedef enum {
355 	SGEN_MODE_NONE,
356 	SGEN_MODE_BALANCED,
357 	SGEN_MODE_THROUGHPUT,
358 	SGEN_MODE_PAUSE
359 } SgenMode;
360 
361 /*
362  * ######################################################################
363  * ########  Global data.
364  * ######################################################################
365  */
366 MonoCoopMutex gc_mutex;
367 
368 #define SCAN_START_SIZE	SGEN_SCAN_START_SIZE
369 
370 size_t degraded_mode = 0;
371 
372 static mword bytes_pinned_from_failed_allocation = 0;
373 
374 GCMemSection *nursery_section = NULL;
375 static volatile mword lowest_heap_address = ~(mword)0;
376 static volatile mword highest_heap_address = 0;
377 
378 MonoCoopMutex sgen_interruption_mutex;
379 
380 int current_collection_generation = -1;
381 volatile gboolean concurrent_collection_in_progress = FALSE;
382 
383 /* objects that are ready to be finalized */
384 static SgenPointerQueue fin_ready_queue = SGEN_POINTER_QUEUE_INIT (INTERNAL_MEM_FINALIZE_READY);
385 static SgenPointerQueue critical_fin_queue = SGEN_POINTER_QUEUE_INIT (INTERNAL_MEM_FINALIZE_READY);
386 
387 /* registered roots: the key to the hash is the root start address */
388 /*
389  * Different kinds of roots are kept separate to speed up pin_from_roots () for example.
390  */
391 SgenHashTable roots_hash [ROOT_TYPE_NUM] = {
392 	SGEN_HASH_TABLE_INIT (INTERNAL_MEM_ROOTS_TABLE, INTERNAL_MEM_ROOT_RECORD, sizeof (RootRecord), sgen_aligned_addr_hash, NULL),
393 	SGEN_HASH_TABLE_INIT (INTERNAL_MEM_ROOTS_TABLE, INTERNAL_MEM_ROOT_RECORD, sizeof (RootRecord), sgen_aligned_addr_hash, NULL),
394 	SGEN_HASH_TABLE_INIT (INTERNAL_MEM_ROOTS_TABLE, INTERNAL_MEM_ROOT_RECORD, sizeof (RootRecord), sgen_aligned_addr_hash, NULL)
395 };
396 static mword roots_size = 0; /* amount of memory in the root set */
397 
398 /* The size of a TLAB */
399 /* The bigger the value, the less often we have to go to the slow path to allocate a new
400  * one, but the more space is wasted by threads not allocating much memory.
401  * FIXME: Tune this.
402  * FIXME: Make this self-tuning for each thread.
403  */
404 guint32 tlab_size = (1024 * 4);
405 
406 #define MAX_SMALL_OBJ_SIZE	SGEN_MAX_SMALL_OBJ_SIZE
407 
408 #define ALLOC_ALIGN		SGEN_ALLOC_ALIGN
409 
410 #define ALIGN_UP		SGEN_ALIGN_UP
411 
412 #ifdef SGEN_DEBUG_INTERNAL_ALLOC
413 MonoNativeThreadId main_gc_thread = NULL;
414 #endif
415 
416 /*Object was pinned during the current collection*/
417 static mword objects_pinned;
418 
419 /*
420  * ######################################################################
421  * ########  Macros and function declarations.
422  * ######################################################################
423  */
424 
425 /* forward declarations */
426 static void scan_from_registered_roots (char *addr_start, char *addr_end, int root_type, ScanCopyContext ctx);
427 
428 static void pin_from_roots (void *start_nursery, void *end_nursery, ScanCopyContext ctx);
429 static void finish_gray_stack (int generation, ScanCopyContext ctx);
430 
431 
432 SgenMajorCollector major_collector;
433 SgenMinorCollector sgen_minor_collector;
434 
435 static SgenRememberedSet remset;
436 
437 /*
438  * The gray queue a worker job must use.  If we're not parallel or
439  * concurrent, we use the main gray queue.
440  */
441 static SgenGrayQueue*
sgen_workers_get_job_gray_queue(WorkerData * worker_data,SgenGrayQueue * default_gray_queue)442 sgen_workers_get_job_gray_queue (WorkerData *worker_data, SgenGrayQueue *default_gray_queue)
443 {
444 	if (worker_data)
445 		return &worker_data->private_gray_queue;
446 	SGEN_ASSERT (0, default_gray_queue, "Why don't we have a default gray queue when we're not running in a worker thread?");
447 	return default_gray_queue;
448 }
449 
450 static void
gray_queue_redirect(SgenGrayQueue * queue)451 gray_queue_redirect (SgenGrayQueue *queue)
452 {
453 	sgen_workers_take_from_queue (current_collection_generation, queue);
454 }
455 
456 void
sgen_scan_area_with_callback(char * start,char * end,IterateObjectCallbackFunc callback,void * data,gboolean allow_flags,gboolean fail_on_canaries)457 sgen_scan_area_with_callback (char *start, char *end, IterateObjectCallbackFunc callback, void *data, gboolean allow_flags, gboolean fail_on_canaries)
458 {
459 	while (start < end) {
460 		size_t size;
461 		char *obj;
462 
463 		if (!*(void**)start) {
464 			start += sizeof (void*); /* should be ALLOC_ALIGN, really */
465 			continue;
466 		}
467 
468 		if (allow_flags) {
469 			if (!(obj = (char *)SGEN_OBJECT_IS_FORWARDED (start)))
470 				obj = start;
471 		} else {
472 			obj = start;
473 		}
474 
475 		if (!sgen_client_object_is_array_fill ((GCObject*)obj)) {
476 			CHECK_CANARY_FOR_OBJECT ((GCObject*)obj, fail_on_canaries);
477 			size = ALIGN_UP (safe_object_get_size ((GCObject*)obj));
478 			callback ((GCObject*)obj, size, data);
479 			CANARIFY_SIZE (size);
480 		} else {
481 			size = ALIGN_UP (safe_object_get_size ((GCObject*)obj));
482 		}
483 
484 		start += size;
485 	}
486 }
487 
488 /*
489  * sgen_add_to_global_remset:
490  *
491  *   The global remset contains locations which point into newspace after
492  * a minor collection. This can happen if the objects they point to are pinned.
493  *
494  * LOCKING: If called from a parallel collector, the global remset
495  * lock must be held.  For serial collectors that is not necessary.
496  */
497 void
sgen_add_to_global_remset(gpointer ptr,GCObject * obj)498 sgen_add_to_global_remset (gpointer ptr, GCObject *obj)
499 {
500 	SGEN_ASSERT (5, sgen_ptr_in_nursery (obj), "Target pointer of global remset must be in the nursery");
501 
502 	HEAVY_STAT (++stat_wbarrier_add_to_global_remset);
503 
504 	if (!major_collector.is_concurrent) {
505 		SGEN_ASSERT (5, current_collection_generation != -1, "Global remsets can only be added during collections");
506 	} else {
507 		if (current_collection_generation == -1)
508 			SGEN_ASSERT (5, sgen_concurrent_collection_in_progress (), "Global remsets outside of collection pauses can only be added by the concurrent collector");
509 	}
510 
511 	if (!object_is_pinned (obj))
512 		SGEN_ASSERT (5, sgen_minor_collector.is_split || sgen_concurrent_collection_in_progress (), "Non-pinned objects can only remain in nursery if it is a split nursery");
513 	else if (sgen_cement_lookup_or_register (obj))
514 		return;
515 
516 	remset.record_pointer (ptr);
517 
518 	sgen_pin_stats_register_global_remset (obj);
519 
520 	SGEN_LOG (8, "Adding global remset for %p", ptr);
521 	binary_protocol_global_remset (ptr, obj, (gpointer)SGEN_LOAD_VTABLE (obj));
522 }
523 
524 /*
525  * sgen_drain_gray_stack:
526  *
527  *   Scan objects in the gray stack until the stack is empty. This should be called
528  * frequently after each object is copied, to achieve better locality and cache
529  * usage.
530  *
531  */
532 gboolean
sgen_drain_gray_stack(ScanCopyContext ctx)533 sgen_drain_gray_stack (ScanCopyContext ctx)
534 {
535 	SGEN_ASSERT (0, ctx.ops->drain_gray_stack, "Why do we have a scan/copy context with a missing drain gray stack function?");
536 
537 	return ctx.ops->drain_gray_stack (ctx.queue);
538 }
539 
540 /*
541  * Addresses in the pin queue are already sorted. This function finds
542  * the object header for each address and pins the object. The
543  * addresses must be inside the nursery section.  The (start of the)
544  * address array is overwritten with the addresses of the actually
545  * pinned objects.  Return the number of pinned objects.
546  */
547 static int
pin_objects_from_nursery_pin_queue(gboolean do_scan_objects,ScanCopyContext ctx)548 pin_objects_from_nursery_pin_queue (gboolean do_scan_objects, ScanCopyContext ctx)
549 {
550 	GCMemSection *section = nursery_section;
551 	void **start =  sgen_pinning_get_entry (section->pin_queue_first_entry);
552 	void **end = sgen_pinning_get_entry (section->pin_queue_last_entry);
553 	void *start_nursery = section->data;
554 	void *end_nursery = section->end_data;
555 	void *last = NULL;
556 	int count = 0;
557 	void *search_start;
558 	void *addr;
559 	void *pinning_front = start_nursery;
560 	size_t idx;
561 	void **definitely_pinned = start;
562 	ScanObjectFunc scan_func = ctx.ops->scan_object;
563 	SgenGrayQueue *queue = ctx.queue;
564 
565 	sgen_nursery_allocator_prepare_for_pinning ();
566 
567 	while (start < end) {
568 		GCObject *obj_to_pin = NULL;
569 		size_t obj_to_pin_size = 0;
570 		SgenDescriptor desc;
571 
572 		addr = *start;
573 
574 		SGEN_ASSERT (0, addr >= start_nursery && addr < end_nursery, "Potential pinning address out of range");
575 		SGEN_ASSERT (0, addr >= last, "Pin queue not sorted");
576 
577 		if (addr == last) {
578 			++start;
579 			continue;
580 		}
581 
582 		SGEN_LOG (5, "Considering pinning addr %p", addr);
583 		/* We've already processed everything up to pinning_front. */
584 		if (addr < pinning_front) {
585 			start++;
586 			continue;
587 		}
588 
589 		/*
590 		 * Find the closest scan start <= addr.  We might search backward in the
591 		 * scan_starts array because entries might be NULL.  In the worst case we
592 		 * start at start_nursery.
593 		 */
594 		idx = ((char*)addr - (char*)section->data) / SCAN_START_SIZE;
595 		SGEN_ASSERT (0, idx < section->num_scan_start, "Scan start index out of range");
596 		search_start = (void*)section->scan_starts [idx];
597 		if (!search_start || search_start > addr) {
598 			while (idx) {
599 				--idx;
600 				search_start = section->scan_starts [idx];
601 				if (search_start && search_start <= addr)
602 					break;
603 			}
604 			if (!search_start || search_start > addr)
605 				search_start = start_nursery;
606 		}
607 
608 		/*
609 		 * If the pinning front is closer than the scan start we found, start
610 		 * searching at the front.
611 		 */
612 		if (search_start < pinning_front)
613 			search_start = pinning_front;
614 
615 		/*
616 		 * Now addr should be in an object a short distance from search_start.
617 		 *
618 		 * search_start must point to zeroed mem or point to an object.
619 		 */
620 		do {
621 			size_t obj_size, canarified_obj_size;
622 
623 			/* Skip zeros. */
624 			if (!*(void**)search_start) {
625 				search_start = (void*)ALIGN_UP ((mword)search_start + sizeof (gpointer));
626 				/* The loop condition makes sure we don't overrun addr. */
627 				continue;
628 			}
629 
630 			canarified_obj_size = obj_size = ALIGN_UP (safe_object_get_size ((GCObject*)search_start));
631 
632 			/*
633 			 * Filler arrays are marked by an invalid sync word.  We don't
634 			 * consider them for pinning.  They are not delimited by canaries,
635 			 * either.
636 			 */
637 			if (!sgen_client_object_is_array_fill ((GCObject*)search_start)) {
638 				CHECK_CANARY_FOR_OBJECT (search_start, TRUE);
639 				CANARIFY_SIZE (canarified_obj_size);
640 
641 				if (addr >= search_start && (char*)addr < (char*)search_start + obj_size) {
642 					/* This is the object we're looking for. */
643 					obj_to_pin = (GCObject*)search_start;
644 					obj_to_pin_size = canarified_obj_size;
645 					break;
646 				}
647 			}
648 
649 			/* Skip to the next object */
650 			search_start = (void*)((char*)search_start + canarified_obj_size);
651 		} while (search_start <= addr);
652 
653 		/* We've searched past the address we were looking for. */
654 		if (!obj_to_pin) {
655 			pinning_front = search_start;
656 			goto next_pin_queue_entry;
657 		}
658 
659 		/*
660 		 * We've found an object to pin.  It might still be a dummy array, but we
661 		 * can advance the pinning front in any case.
662 		 */
663 		pinning_front = (char*)obj_to_pin + obj_to_pin_size;
664 
665 		/*
666 		 * If this is a dummy array marking the beginning of a nursery
667 		 * fragment, we don't pin it.
668 		 */
669 		if (sgen_client_object_is_array_fill (obj_to_pin))
670 			goto next_pin_queue_entry;
671 
672 		/*
673 		 * Finally - pin the object!
674 		 */
675 		desc = sgen_obj_get_descriptor_safe (obj_to_pin);
676 		if (do_scan_objects) {
677 			scan_func (obj_to_pin, desc, queue);
678 		} else {
679 			SGEN_LOG (4, "Pinned object %p, vtable %p (%s), count %d\n",
680 					obj_to_pin, *(void**)obj_to_pin, sgen_client_vtable_get_name (SGEN_LOAD_VTABLE (obj_to_pin)), count);
681 			binary_protocol_pin (obj_to_pin,
682 					(gpointer)LOAD_VTABLE (obj_to_pin),
683 					safe_object_get_size (obj_to_pin));
684 
685 			pin_object (obj_to_pin);
686 			GRAY_OBJECT_ENQUEUE_SERIAL (queue, obj_to_pin, desc);
687 			sgen_pin_stats_register_object (obj_to_pin, GENERATION_NURSERY);
688 			definitely_pinned [count] = obj_to_pin;
689 			count++;
690 		}
691 		if (concurrent_collection_in_progress)
692 			sgen_pinning_register_pinned_in_nursery (obj_to_pin);
693 
694 	next_pin_queue_entry:
695 		last = addr;
696 		++start;
697 	}
698 	sgen_client_nursery_objects_pinned (definitely_pinned, count);
699 	stat_pinned_objects += count;
700 	return count;
701 }
702 
703 static void
pin_objects_in_nursery(gboolean do_scan_objects,ScanCopyContext ctx)704 pin_objects_in_nursery (gboolean do_scan_objects, ScanCopyContext ctx)
705 {
706 	size_t reduced_to;
707 
708 	if (nursery_section->pin_queue_first_entry == nursery_section->pin_queue_last_entry)
709 		return;
710 
711 	reduced_to = pin_objects_from_nursery_pin_queue (do_scan_objects, ctx);
712 	nursery_section->pin_queue_last_entry = nursery_section->pin_queue_first_entry + reduced_to;
713 }
714 
715 /*
716  * This function is only ever called (via `collector_pin_object()` in `sgen-copy-object.h`)
717  * when we can't promote an object because we're out of memory.
718  */
719 void
sgen_pin_object(GCObject * object,SgenGrayQueue * queue)720 sgen_pin_object (GCObject *object, SgenGrayQueue *queue)
721 {
722 	SGEN_ASSERT (0, sgen_ptr_in_nursery (object), "We're only supposed to use this for pinning nursery objects when out of memory.");
723 
724 	/*
725 	 * All pinned objects are assumed to have been staged, so we need to stage as well.
726 	 * Also, the count of staged objects shows that "late pinning" happened.
727 	 */
728 	sgen_pin_stage_ptr (object);
729 
730 	SGEN_PIN_OBJECT (object);
731 	binary_protocol_pin (object, (gpointer)LOAD_VTABLE (object), safe_object_get_size (object));
732 
733 	++objects_pinned;
734 	sgen_pin_stats_register_object (object, GENERATION_NURSERY);
735 
736 	GRAY_OBJECT_ENQUEUE_SERIAL (queue, object, sgen_obj_get_descriptor_safe (object));
737 }
738 
739 /* Sort the addresses in array in increasing order.
740  * Done using a by-the book heap sort. Which has decent and stable performance, is pretty cache efficient.
741  */
742 void
sgen_sort_addresses(void ** array,size_t size)743 sgen_sort_addresses (void **array, size_t size)
744 {
745 	size_t i;
746 	void *tmp;
747 
748 	for (i = 1; i < size; ++i) {
749 		size_t child = i;
750 		while (child > 0) {
751 			size_t parent = (child - 1) / 2;
752 
753 			if (array [parent] >= array [child])
754 				break;
755 
756 			tmp = array [parent];
757 			array [parent] = array [child];
758 			array [child] = tmp;
759 
760 			child = parent;
761 		}
762 	}
763 
764 	for (i = size - 1; i > 0; --i) {
765 		size_t end, root;
766 		tmp = array [i];
767 		array [i] = array [0];
768 		array [0] = tmp;
769 
770 		end = i - 1;
771 		root = 0;
772 
773 		while (root * 2 + 1 <= end) {
774 			size_t child = root * 2 + 1;
775 
776 			if (child < end && array [child] < array [child + 1])
777 				++child;
778 			if (array [root] >= array [child])
779 				break;
780 
781 			tmp = array [root];
782 			array [root] = array [child];
783 			array [child] = tmp;
784 
785 			root = child;
786 		}
787 	}
788 }
789 
790 /*
791  * Scan the memory between start and end and queue values which could be pointers
792  * to the area between start_nursery and end_nursery for later consideration.
793  * Typically used for thread stacks.
794  */
795 MONO_NO_SANITIZE_ADDRESS
796 void
sgen_conservatively_pin_objects_from(void ** start,void ** end,void * start_nursery,void * end_nursery,int pin_type)797 sgen_conservatively_pin_objects_from (void **start, void **end, void *start_nursery, void *end_nursery, int pin_type)
798 {
799 	int count = 0;
800 
801 	SGEN_ASSERT (0, ((mword)start & (SIZEOF_VOID_P - 1)) == 0, "Why are we scanning for references in unaligned memory ?");
802 
803 #if defined(VALGRIND_MAKE_MEM_DEFINED_IF_ADDRESSABLE) && !defined(_WIN64)
804 	VALGRIND_MAKE_MEM_DEFINED_IF_ADDRESSABLE (start, (char*)end - (char*)start);
805 #endif
806 
807 	while (start < end) {
808 		/*
809 		 * *start can point to the middle of an object
810 		 * note: should we handle pointing at the end of an object?
811 		 * pinning in C# code disallows pointing at the end of an object
812 		 * but there is some small chance that an optimizing C compiler
813 		 * may keep the only reference to an object by pointing
814 		 * at the end of it. We ignore this small chance for now.
815 		 * Pointers to the end of an object are indistinguishable
816 		 * from pointers to the start of the next object in memory
817 		 * so if we allow that we'd need to pin two objects...
818 		 * We queue the pointer in an array, the
819 		 * array will then be sorted and uniqued. This way
820 		 * we can coalesce several pinning pointers and it should
821 		 * be faster since we'd do a memory scan with increasing
822 		 * addresses. Note: we can align the address to the allocation
823 		 * alignment, so the unique process is more effective.
824 		 */
825 		mword addr = (mword)*start;
826 		addr &= ~(ALLOC_ALIGN - 1);
827 		if (addr >= (mword)start_nursery && addr < (mword)end_nursery) {
828 			SGEN_LOG (6, "Pinning address %p from %p", (void*)addr, start);
829 			sgen_pin_stage_ptr ((void*)addr);
830 			binary_protocol_pin_stage (start, (void*)addr);
831 			sgen_pin_stats_register_address ((char*)addr, pin_type);
832 			count++;
833 		}
834 		start++;
835 	}
836 	if (count)
837 		SGEN_LOG (7, "found %d potential pinned heap pointers", count);
838 }
839 
840 /*
841  * The first thing we do in a collection is to identify pinned objects.
842  * This function considers all the areas of memory that need to be
843  * conservatively scanned.
844  */
845 static void
pin_from_roots(void * start_nursery,void * end_nursery,ScanCopyContext ctx)846 pin_from_roots (void *start_nursery, void *end_nursery, ScanCopyContext ctx)
847 {
848 	void **start_root;
849 	RootRecord *root;
850 	SGEN_LOG (2, "Scanning pinned roots (%d bytes, %d/%d entries)", (int)roots_size, roots_hash [ROOT_TYPE_NORMAL].num_entries, roots_hash [ROOT_TYPE_PINNED].num_entries);
851 	/* objects pinned from the API are inside these roots */
852 	SGEN_HASH_TABLE_FOREACH (&roots_hash [ROOT_TYPE_PINNED], void **, start_root, RootRecord *, root) {
853 		SGEN_LOG (6, "Pinned roots %p-%p", start_root, root->end_root);
854 		sgen_conservatively_pin_objects_from (start_root, (void**)root->end_root, start_nursery, end_nursery, PIN_TYPE_OTHER);
855 	} SGEN_HASH_TABLE_FOREACH_END;
856 	/* now deal with the thread stacks
857 	 * in the future we should be able to conservatively scan only:
858 	 * *) the cpu registers
859 	 * *) the unmanaged stack frames
860 	 * *) the _last_ managed stack frame
861 	 * *) pointers slots in managed frames
862 	 */
863 	sgen_client_scan_thread_data (start_nursery, end_nursery, FALSE, ctx);
864 }
865 
866 static void
single_arg_user_copy_or_mark(GCObject ** obj,void * gc_data)867 single_arg_user_copy_or_mark (GCObject **obj, void *gc_data)
868 {
869 	ScanCopyContext *ctx = (ScanCopyContext *)gc_data;
870 	ctx->ops->copy_or_mark_object (obj, ctx->queue);
871 }
872 
873 /*
874  * The memory area from start_root to end_root contains pointers to objects.
875  * Their position is precisely described by @desc (this means that the pointer
876  * can be either NULL or the pointer to the start of an object).
877  * This functions copies them to to_space updates them.
878  *
879  * This function is not thread-safe!
880  */
881 static void
precisely_scan_objects_from(void ** start_root,void ** end_root,char * n_start,char * n_end,SgenDescriptor desc,ScanCopyContext ctx)882 precisely_scan_objects_from (void** start_root, void** end_root, char* n_start, char *n_end, SgenDescriptor desc, ScanCopyContext ctx)
883 {
884 	CopyOrMarkObjectFunc copy_func = ctx.ops->copy_or_mark_object;
885 	ScanPtrFieldFunc scan_field_func = ctx.ops->scan_ptr_field;
886 	SgenGrayQueue *queue = ctx.queue;
887 
888 	switch (desc & ROOT_DESC_TYPE_MASK) {
889 	case ROOT_DESC_BITMAP:
890 		desc >>= ROOT_DESC_TYPE_SHIFT;
891 		while (desc) {
892 			if ((desc & 1) && *start_root) {
893 				copy_func ((GCObject**)start_root, queue);
894 				SGEN_LOG (9, "Overwrote root at %p with %p", start_root, *start_root);
895 			}
896 			desc >>= 1;
897 			start_root++;
898 		}
899 		return;
900 	case ROOT_DESC_COMPLEX: {
901 		gsize *bitmap_data = (gsize *)sgen_get_complex_descriptor_bitmap (desc);
902 		gsize bwords = (*bitmap_data) - 1;
903 		void **start_run = start_root;
904 		bitmap_data++;
905 		while (bwords-- > 0) {
906 			gsize bmap = *bitmap_data++;
907 			void **objptr = start_run;
908 			while (bmap) {
909 				if ((bmap & 1) && *objptr) {
910 					copy_func ((GCObject**)objptr, queue);
911 					SGEN_LOG (9, "Overwrote root at %p with %p", objptr, *objptr);
912 				}
913 				bmap >>= 1;
914 				++objptr;
915 			}
916 			start_run += GC_BITS_PER_WORD;
917 		}
918 		break;
919 	}
920 	case ROOT_DESC_VECTOR: {
921 		void **p;
922 
923 		for (p = start_root; p < end_root; p++) {
924 			if (*p)
925 				scan_field_func (NULL, (GCObject**)p, queue);
926 		}
927 		break;
928 	}
929 	case ROOT_DESC_USER: {
930 		SgenUserRootMarkFunc marker = sgen_get_user_descriptor_func (desc);
931 		marker (start_root, single_arg_user_copy_or_mark, &ctx);
932 		break;
933 	}
934 	case ROOT_DESC_RUN_LEN:
935 		g_assert_not_reached ();
936 	default:
937 		g_assert_not_reached ();
938 	}
939 }
940 
941 static void
reset_heap_boundaries(void)942 reset_heap_boundaries (void)
943 {
944 	lowest_heap_address = ~(mword)0;
945 	highest_heap_address = 0;
946 }
947 
948 void
sgen_update_heap_boundaries(mword low,mword high)949 sgen_update_heap_boundaries (mword low, mword high)
950 {
951 	mword old;
952 
953 	do {
954 		old = lowest_heap_address;
955 		if (low >= old)
956 			break;
957 	} while (SGEN_CAS_PTR ((gpointer*)&lowest_heap_address, (gpointer)low, (gpointer)old) != (gpointer)old);
958 
959 	do {
960 		old = highest_heap_address;
961 		if (high <= old)
962 			break;
963 	} while (SGEN_CAS_PTR ((gpointer*)&highest_heap_address, (gpointer)high, (gpointer)old) != (gpointer)old);
964 }
965 
966 /*
967  * Allocate and setup the data structures needed to be able to allocate objects
968  * in the nursery. The nursery is stored in nursery_section.
969  */
970 static void
alloc_nursery(gboolean dynamic,size_t min_size,size_t max_size)971 alloc_nursery (gboolean dynamic, size_t min_size, size_t max_size)
972 {
973 	char *data;
974 	size_t scan_starts;
975 
976 	if (dynamic) {
977 		if (!min_size)
978 			min_size = SGEN_DEFAULT_NURSERY_MIN_SIZE;
979 		if (!max_size)
980 			max_size = SGEN_DEFAULT_NURSERY_MAX_SIZE;
981 	} else {
982 		SGEN_ASSERT (0, min_size == max_size, "We can't have nursery ranges for static configuration.");
983 		if (!min_size)
984 			min_size = max_size = SGEN_DEFAULT_NURSERY_SIZE;
985 	}
986 
987 	SGEN_ASSERT (0, !nursery_section, "Why are we allocating the nursery twice?");
988 	SGEN_LOG (2, "Allocating nursery size: %zu, initial %zu", max_size, min_size);
989 
990 	/* FIXME: handle OOM */
991 	nursery_section = (GCMemSection *)sgen_alloc_internal (INTERNAL_MEM_SECTION);
992 
993 	/* If there isn't enough space even for the nursery we should simply abort. */
994 	g_assert (sgen_memgov_try_alloc_space (max_size, SPACE_NURSERY));
995 
996 	/*
997 	 * The nursery section range represents the memory section where objects
998 	 * can be found. This is used when iterating for objects in the nursery,
999 	 * pinning etc. sgen_nursery_max_size represents the total allocated space
1000 	 * for the nursery. sgen_nursery_size represents the current size of the
1001 	 * nursery and it is used for allocation limits, heuristics etc. The
1002 	 * nursery section is not always identical to the current nursery size
1003 	 * because it can contain pinned objects from when the nursery was larger.
1004 	 *
1005 	 * sgen_nursery_size <= nursery_section size <= sgen_nursery_max_size
1006 	 */
1007 	data = (char *)major_collector.alloc_heap (max_size, max_size);
1008 	sgen_update_heap_boundaries ((mword)data, (mword)(data + max_size));
1009 	nursery_section->data = data;
1010 	nursery_section->end_data = data + min_size;
1011 	scan_starts = (max_size + SCAN_START_SIZE - 1) / SCAN_START_SIZE;
1012 	nursery_section->scan_starts = (char **)sgen_alloc_internal_dynamic (sizeof (char*) * scan_starts, INTERNAL_MEM_SCAN_STARTS, TRUE);
1013 	nursery_section->num_scan_start = scan_starts;
1014 
1015 	sgen_nursery_allocator_set_nursery_bounds (data, min_size, max_size);
1016 }
1017 
1018 FILE *
mono_gc_get_logfile(void)1019 mono_gc_get_logfile (void)
1020 {
1021 	return gc_debug_file;
1022 }
1023 
1024 void
mono_gc_params_set(const char * options)1025 mono_gc_params_set (const char* options)
1026 {
1027 	if (gc_params_options)
1028 		g_free (gc_params_options);
1029 
1030 	gc_params_options = g_strdup (options);
1031 }
1032 
1033 void
mono_gc_debug_set(const char * options)1034 mono_gc_debug_set (const char* options)
1035 {
1036 	if (gc_debug_options)
1037 		g_free (gc_debug_options);
1038 
1039 	gc_debug_options = g_strdup (options);
1040 }
1041 
1042 static void
scan_finalizer_entries(SgenPointerQueue * fin_queue,ScanCopyContext ctx)1043 scan_finalizer_entries (SgenPointerQueue *fin_queue, ScanCopyContext ctx)
1044 {
1045 	CopyOrMarkObjectFunc copy_func = ctx.ops->copy_or_mark_object;
1046 	SgenGrayQueue *queue = ctx.queue;
1047 	size_t i;
1048 
1049 	for (i = 0; i < fin_queue->next_slot; ++i) {
1050 		GCObject *obj = (GCObject *)fin_queue->data [i];
1051 		if (!obj)
1052 			continue;
1053 		SGEN_LOG (5, "Scan of fin ready object: %p (%s)\n", obj, sgen_client_vtable_get_name (SGEN_LOAD_VTABLE (obj)));
1054 		copy_func ((GCObject**)&fin_queue->data [i], queue);
1055 	}
1056 }
1057 
1058 static const char*
generation_name(int generation)1059 generation_name (int generation)
1060 {
1061 	switch (generation) {
1062 	case GENERATION_NURSERY: return "nursery";
1063 	case GENERATION_OLD: return "old";
1064 	default: g_assert_not_reached ();
1065 	}
1066 }
1067 
1068 const char*
sgen_generation_name(int generation)1069 sgen_generation_name (int generation)
1070 {
1071 	return generation_name (generation);
1072 }
1073 
1074 static void
finish_gray_stack(int generation,ScanCopyContext ctx)1075 finish_gray_stack (int generation, ScanCopyContext ctx)
1076 {
1077 	TV_DECLARE (atv);
1078 	TV_DECLARE (btv);
1079 	int done_with_ephemerons, ephemeron_rounds = 0;
1080 	char *start_addr = generation == GENERATION_NURSERY ? sgen_get_nursery_start () : NULL;
1081 	char *end_addr = generation == GENERATION_NURSERY ? sgen_get_nursery_end () : (char*)-1;
1082 	SgenGrayQueue *queue = ctx.queue;
1083 
1084 	binary_protocol_finish_gray_stack_start (sgen_timestamp (), generation);
1085 	/*
1086 	 * We copied all the reachable objects. Now it's the time to copy
1087 	 * the objects that were not referenced by the roots, but by the copied objects.
1088 	 * we built a stack of objects pointed to by gray_start: they are
1089 	 * additional roots and we may add more items as we go.
1090 	 * We loop until gray_start == gray_objects which means no more objects have
1091 	 * been added. Note this is iterative: no recursion is involved.
1092 	 * We need to walk the LO list as well in search of marked big objects
1093 	 * (use a flag since this is needed only on major collections). We need to loop
1094 	 * here as well, so keep a counter of marked LO (increasing it in copy_object).
1095 	 *   To achieve better cache locality and cache usage, we drain the gray stack
1096 	 * frequently, after each object is copied, and just finish the work here.
1097 	 */
1098 	sgen_drain_gray_stack (ctx);
1099 	TV_GETTIME (atv);
1100 	SGEN_LOG (2, "%s generation done", generation_name (generation));
1101 
1102 	/*
1103 	Reset bridge data, we might have lingering data from a previous collection if this is a major
1104 	collection trigged by minor overflow.
1105 
1106 	We must reset the gathered bridges since their original block might be evacuated due to major
1107 	fragmentation in the meanwhile and the bridge code should not have to deal with that.
1108 	*/
1109 	if (sgen_client_bridge_need_processing ())
1110 		sgen_client_bridge_reset_data ();
1111 
1112 	/*
1113 	 * Mark all strong toggleref objects. This must be done before we walk ephemerons or finalizers
1114 	 * to ensure they see the full set of live objects.
1115 	 */
1116 	sgen_client_mark_togglerefs (start_addr, end_addr, ctx);
1117 
1118 	/*
1119 	 * Walk the ephemeron tables marking all values with reachable keys. This must be completely done
1120 	 * before processing finalizable objects and non-tracking weak links to avoid finalizing/clearing
1121 	 * objects that are in fact reachable.
1122 	 */
1123 	done_with_ephemerons = 0;
1124 	do {
1125 		done_with_ephemerons = sgen_client_mark_ephemerons (ctx);
1126 		sgen_drain_gray_stack (ctx);
1127 		++ephemeron_rounds;
1128 	} while (!done_with_ephemerons);
1129 
1130 	if (sgen_client_bridge_need_processing ()) {
1131 		/*Make sure the gray stack is empty before we process bridge objects so we get liveness right*/
1132 		sgen_drain_gray_stack (ctx);
1133 		sgen_collect_bridge_objects (generation, ctx);
1134 		if (generation == GENERATION_OLD)
1135 			sgen_collect_bridge_objects (GENERATION_NURSERY, ctx);
1136 
1137 		/*
1138 		Do the first bridge step here, as the collector liveness state will become useless after that.
1139 
1140 		An important optimization is to only proccess the possibly dead part of the object graph and skip
1141 		over all live objects as we transitively know everything they point must be alive too.
1142 
1143 		The above invariant is completely wrong if we let the gray queue be drained and mark/copy everything.
1144 
1145 		This has the unfortunate side effect of making overflow collections perform the first step twice, but
1146 		given we now have heuristics that perform major GC in anticipation of minor overflows this should not
1147 		be a big deal.
1148 		*/
1149 		sgen_client_bridge_processing_stw_step ();
1150 	}
1151 
1152 	/*
1153 	Make sure we drain the gray stack before processing disappearing links and finalizers.
1154 	If we don't make sure it is empty we might wrongly see a live object as dead.
1155 	*/
1156 	sgen_drain_gray_stack (ctx);
1157 
1158 	/*
1159 	We must clear weak links that don't track resurrection before processing object ready for
1160 	finalization so they can be cleared before that.
1161 	*/
1162 	sgen_null_link_in_range (generation, ctx, FALSE);
1163 	if (generation == GENERATION_OLD)
1164 		sgen_null_link_in_range (GENERATION_NURSERY, ctx, FALSE);
1165 
1166 
1167 	/* walk the finalization queue and move also the objects that need to be
1168 	 * finalized: use the finalized objects as new roots so the objects they depend
1169 	 * on are also not reclaimed. As with the roots above, only objects in the nursery
1170 	 * are marked/copied.
1171 	 */
1172 	sgen_finalize_in_range (generation, ctx);
1173 	if (generation == GENERATION_OLD)
1174 		sgen_finalize_in_range (GENERATION_NURSERY, ctx);
1175 	/* drain the new stack that might have been created */
1176 	SGEN_LOG (6, "Precise scan of gray area post fin");
1177 	sgen_drain_gray_stack (ctx);
1178 
1179 	/*
1180 	 * This must be done again after processing finalizable objects since CWL slots are cleared only after the key is finalized.
1181 	 */
1182 	done_with_ephemerons = 0;
1183 	do {
1184 		done_with_ephemerons = sgen_client_mark_ephemerons (ctx);
1185 		sgen_drain_gray_stack (ctx);
1186 		++ephemeron_rounds;
1187 	} while (!done_with_ephemerons);
1188 
1189 	sgen_client_clear_unreachable_ephemerons (ctx);
1190 
1191 	/*
1192 	 * We clear togglerefs only after all possible chances of revival are done.
1193 	 * This is semantically more inline with what users expect and it allows for
1194 	 * user finalizers to correctly interact with TR objects.
1195 	*/
1196 	sgen_client_clear_togglerefs (start_addr, end_addr, ctx);
1197 
1198 	TV_GETTIME (btv);
1199 	SGEN_LOG (2, "Finalize queue handling scan for %s generation: %lld usecs %d ephemeron rounds", generation_name (generation), (long long)TV_ELAPSED (atv, btv), ephemeron_rounds);
1200 
1201 	/*
1202 	 * handle disappearing links
1203 	 * Note we do this after checking the finalization queue because if an object
1204 	 * survives (at least long enough to be finalized) we don't clear the link.
1205 	 * This also deals with a possible issue with the monitor reclamation: with the Boehm
1206 	 * GC a finalized object my lose the monitor because it is cleared before the finalizer is
1207 	 * called.
1208 	 */
1209 	g_assert (sgen_gray_object_queue_is_empty (queue));
1210 	for (;;) {
1211 		sgen_null_link_in_range (generation, ctx, TRUE);
1212 		if (generation == GENERATION_OLD)
1213 			sgen_null_link_in_range (GENERATION_NURSERY, ctx, TRUE);
1214 		if (sgen_gray_object_queue_is_empty (queue))
1215 			break;
1216 		sgen_drain_gray_stack (ctx);
1217 	}
1218 
1219 	g_assert (sgen_gray_object_queue_is_empty (queue));
1220 
1221 	binary_protocol_finish_gray_stack_end (sgen_timestamp (), generation);
1222 }
1223 
1224 void
sgen_check_section_scan_starts(GCMemSection * section)1225 sgen_check_section_scan_starts (GCMemSection *section)
1226 {
1227 	size_t i;
1228 	for (i = 0; i < section->num_scan_start; ++i) {
1229 		if (section->scan_starts [i]) {
1230 			mword size = safe_object_get_size ((GCObject*) section->scan_starts [i]);
1231 			SGEN_ASSERT (0, size >= SGEN_CLIENT_MINIMUM_OBJECT_SIZE && size <= MAX_SMALL_OBJ_SIZE, "Weird object size at scan starts.");
1232 		}
1233 	}
1234 }
1235 
1236 static void
check_scan_starts(void)1237 check_scan_starts (void)
1238 {
1239 	if (!do_scan_starts_check)
1240 		return;
1241 	sgen_check_section_scan_starts (nursery_section);
1242 	major_collector.check_scan_starts ();
1243 }
1244 
1245 static void
scan_from_registered_roots(char * addr_start,char * addr_end,int root_type,ScanCopyContext ctx)1246 scan_from_registered_roots (char *addr_start, char *addr_end, int root_type, ScanCopyContext ctx)
1247 {
1248 	void **start_root;
1249 	RootRecord *root;
1250 	SGEN_HASH_TABLE_FOREACH (&roots_hash [root_type], void **, start_root, RootRecord *, root) {
1251 		SGEN_LOG (6, "Precise root scan %p-%p (desc: %p)", start_root, root->end_root, (void*)root->root_desc);
1252 		precisely_scan_objects_from (start_root, (void**)root->end_root, addr_start, addr_end, root->root_desc, ctx);
1253 	} SGEN_HASH_TABLE_FOREACH_END;
1254 }
1255 
1256 static void
init_stats(void)1257 init_stats (void)
1258 {
1259 	static gboolean inited = FALSE;
1260 
1261 	if (inited)
1262 		return;
1263 
1264 	mono_counters_register ("Collection max time",  MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME | MONO_COUNTER_MONOTONIC, &time_max);
1265 
1266 	mono_counters_register ("Minor fragment clear", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_minor_pre_collection_fragment_clear);
1267 	mono_counters_register ("Minor pinning", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_minor_pinning);
1268 	mono_counters_register ("Minor scan remembered set", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_minor_scan_remsets);
1269 	mono_counters_register ("Minor scan major blocks", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_minor_scan_major_blocks);
1270 	mono_counters_register ("Minor scan los", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_minor_scan_los);
1271 	mono_counters_register ("Minor scan pinned", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_minor_scan_pinned);
1272 	mono_counters_register ("Minor scan roots", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_minor_scan_roots);
1273 	mono_counters_register ("Minor fragment creation", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_minor_fragment_creation);
1274 
1275 	mono_counters_register ("Major fragment clear", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_major_pre_collection_fragment_clear);
1276 	mono_counters_register ("Major pinning", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_major_pinning);
1277 	mono_counters_register ("Major scan pinned", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_major_scan_pinned);
1278 	mono_counters_register ("Major scan roots", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_major_scan_roots);
1279 	mono_counters_register ("Major scan mod union blocks", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_major_scan_mod_union_blocks);
1280 	mono_counters_register ("Major scan mod union los", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_major_scan_mod_union_los);
1281 	mono_counters_register ("Major finish gray stack", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_major_finish_gray_stack);
1282 	mono_counters_register ("Major free big objects", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_major_free_bigobjs);
1283 	mono_counters_register ("Major LOS sweep", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_major_los_sweep);
1284 	mono_counters_register ("Major sweep", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_major_sweep);
1285 	mono_counters_register ("Major fragment creation", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_major_fragment_creation);
1286 
1287 	mono_counters_register ("Number of pinned objects", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_pinned_objects);
1288 
1289 #ifdef HEAVY_STATISTICS
1290 	mono_counters_register ("WBarrier remember pointer", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_wbarrier_add_to_global_remset);
1291 	mono_counters_register ("WBarrier arrayref copy", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_wbarrier_arrayref_copy);
1292 	mono_counters_register ("WBarrier generic store called", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_wbarrier_generic_store);
1293 	mono_counters_register ("WBarrier generic atomic store called", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_wbarrier_generic_store_atomic);
1294 	mono_counters_register ("WBarrier set root", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_wbarrier_set_root);
1295 
1296 	mono_counters_register ("# objects allocated degraded", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_objects_alloced_degraded);
1297 	mono_counters_register ("bytes allocated degraded", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_bytes_alloced_degraded);
1298 
1299 	mono_counters_register ("# copy_object() called (nursery)", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_copy_object_called_nursery);
1300 	mono_counters_register ("# objects copied (nursery)", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_objects_copied_nursery);
1301 	mono_counters_register ("# copy_object() called (major)", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_copy_object_called_major);
1302 	mono_counters_register ("# objects copied (major)", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_objects_copied_major);
1303 
1304 	mono_counters_register ("# scan_object() called (nursery)", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_scan_object_called_nursery);
1305 	mono_counters_register ("# scan_object() called (major)", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_scan_object_called_major);
1306 
1307 	mono_counters_register ("Slots allocated in vain", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_slots_allocated_in_vain);
1308 
1309 	mono_counters_register ("# nursery copy_object() failed from space", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_nursery_copy_object_failed_from_space);
1310 	mono_counters_register ("# nursery copy_object() failed forwarded", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_nursery_copy_object_failed_forwarded);
1311 	mono_counters_register ("# nursery copy_object() failed pinned", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_nursery_copy_object_failed_pinned);
1312 	mono_counters_register ("# nursery copy_object() failed to space", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_nursery_copy_object_failed_to_space);
1313 
1314 	sgen_nursery_allocator_init_heavy_stats ();
1315 #endif
1316 
1317 	inited = TRUE;
1318 }
1319 
1320 
1321 static void
reset_pinned_from_failed_allocation(void)1322 reset_pinned_from_failed_allocation (void)
1323 {
1324 	bytes_pinned_from_failed_allocation = 0;
1325 }
1326 
1327 void
sgen_set_pinned_from_failed_allocation(mword objsize)1328 sgen_set_pinned_from_failed_allocation (mword objsize)
1329 {
1330 	bytes_pinned_from_failed_allocation += objsize;
1331 }
1332 
1333 gboolean
sgen_collection_is_concurrent(void)1334 sgen_collection_is_concurrent (void)
1335 {
1336 	switch (current_collection_generation) {
1337 	case GENERATION_NURSERY:
1338 		return FALSE;
1339 	case GENERATION_OLD:
1340 		return concurrent_collection_in_progress;
1341 	default:
1342 		g_error ("Invalid current generation %d", current_collection_generation);
1343 	}
1344 	return FALSE;
1345 }
1346 
1347 gboolean
sgen_concurrent_collection_in_progress(void)1348 sgen_concurrent_collection_in_progress (void)
1349 {
1350 	return concurrent_collection_in_progress;
1351 }
1352 
1353 typedef struct {
1354 	SgenThreadPoolJob job;
1355 	SgenObjectOperations *ops;
1356 	SgenGrayQueue *gc_thread_gray_queue;
1357 } ScanJob;
1358 
1359 typedef struct {
1360 	ScanJob scan_job;
1361 	int job_index, job_split_count;
1362 	int data;
1363 } ParallelScanJob;
1364 
1365 static ScanCopyContext
scan_copy_context_for_scan_job(void * worker_data_untyped,ScanJob * job)1366 scan_copy_context_for_scan_job (void *worker_data_untyped, ScanJob *job)
1367 {
1368 	WorkerData *worker_data = (WorkerData *)worker_data_untyped;
1369 
1370 	if (!job->ops) {
1371 		/*
1372 		 * For jobs enqueued on workers we set the ops at job runtime in order
1373 		 * to be able to profit from on the fly optimized object ops or other
1374 		 * object ops changes, like forced concurrent finish.
1375 		 */
1376 		SGEN_ASSERT (0, sgen_workers_is_worker_thread (mono_native_thread_id_get ()), "We need a context for the scan job");
1377 		job->ops = sgen_workers_get_idle_func_object_ops (worker_data);
1378 	}
1379 
1380 	return CONTEXT_FROM_OBJECT_OPERATIONS (job->ops, sgen_workers_get_job_gray_queue (worker_data, job->gc_thread_gray_queue));
1381 }
1382 
1383 typedef struct {
1384 	ScanJob scan_job;
1385 	char *heap_start;
1386 	char *heap_end;
1387 	int root_type;
1388 } ScanFromRegisteredRootsJob;
1389 
1390 static void
job_scan_from_registered_roots(void * worker_data_untyped,SgenThreadPoolJob * job)1391 job_scan_from_registered_roots (void *worker_data_untyped, SgenThreadPoolJob *job)
1392 {
1393 	ScanFromRegisteredRootsJob *job_data = (ScanFromRegisteredRootsJob*)job;
1394 	ScanCopyContext ctx = scan_copy_context_for_scan_job (worker_data_untyped, &job_data->scan_job);
1395 
1396 	scan_from_registered_roots (job_data->heap_start, job_data->heap_end, job_data->root_type, ctx);
1397 }
1398 
1399 typedef struct {
1400 	ScanJob scan_job;
1401 	char *heap_start;
1402 	char *heap_end;
1403 } ScanThreadDataJob;
1404 
1405 static void
job_scan_thread_data(void * worker_data_untyped,SgenThreadPoolJob * job)1406 job_scan_thread_data (void *worker_data_untyped, SgenThreadPoolJob *job)
1407 {
1408 	ScanThreadDataJob *job_data = (ScanThreadDataJob*)job;
1409 	ScanCopyContext ctx = scan_copy_context_for_scan_job (worker_data_untyped, &job_data->scan_job);
1410 
1411 	sgen_client_scan_thread_data (job_data->heap_start, job_data->heap_end, TRUE, ctx);
1412 }
1413 
1414 typedef struct {
1415 	ScanJob scan_job;
1416 	SgenPointerQueue *queue;
1417 } ScanFinalizerEntriesJob;
1418 
1419 static void
job_scan_finalizer_entries(void * worker_data_untyped,SgenThreadPoolJob * job)1420 job_scan_finalizer_entries (void *worker_data_untyped, SgenThreadPoolJob *job)
1421 {
1422 	ScanFinalizerEntriesJob *job_data = (ScanFinalizerEntriesJob*)job;
1423 	ScanCopyContext ctx = scan_copy_context_for_scan_job (worker_data_untyped, &job_data->scan_job);
1424 
1425 	scan_finalizer_entries (job_data->queue, ctx);
1426 }
1427 
1428 static void
job_scan_wbroots(void * worker_data_untyped,SgenThreadPoolJob * job)1429 job_scan_wbroots (void *worker_data_untyped, SgenThreadPoolJob *job)
1430 {
1431 	ScanJob *job_data = (ScanJob*)job;
1432 	ScanCopyContext ctx = scan_copy_context_for_scan_job (worker_data_untyped, job_data);
1433 
1434 	sgen_wbroots_scan_card_table (ctx);
1435 }
1436 
1437 static void
job_scan_major_card_table(void * worker_data_untyped,SgenThreadPoolJob * job)1438 job_scan_major_card_table (void *worker_data_untyped, SgenThreadPoolJob *job)
1439 {
1440 	SGEN_TV_DECLARE (atv);
1441 	SGEN_TV_DECLARE (btv);
1442 	ParallelScanJob *job_data = (ParallelScanJob*)job;
1443 	ScanCopyContext ctx = scan_copy_context_for_scan_job (worker_data_untyped, (ScanJob*)job_data);
1444 
1445 	SGEN_TV_GETTIME (atv);
1446 	major_collector.scan_card_table (CARDTABLE_SCAN_GLOBAL, ctx, job_data->job_index, job_data->job_split_count, job_data->data);
1447 	SGEN_TV_GETTIME (btv);
1448 	time_minor_scan_major_blocks += SGEN_TV_ELAPSED (atv, btv);
1449 
1450 	if (worker_data_untyped)
1451 		((WorkerData*)worker_data_untyped)->major_scan_time += SGEN_TV_ELAPSED (atv, btv);
1452 }
1453 
1454 static void
job_scan_los_card_table(void * worker_data_untyped,SgenThreadPoolJob * job)1455 job_scan_los_card_table (void *worker_data_untyped, SgenThreadPoolJob *job)
1456 {
1457 	SGEN_TV_DECLARE (atv);
1458 	SGEN_TV_DECLARE (btv);
1459 	ParallelScanJob *job_data = (ParallelScanJob*)job;
1460 	ScanCopyContext ctx = scan_copy_context_for_scan_job (worker_data_untyped, (ScanJob*)job_data);
1461 
1462 	SGEN_TV_GETTIME (atv);
1463 	sgen_los_scan_card_table (CARDTABLE_SCAN_GLOBAL, ctx, job_data->job_index, job_data->job_split_count);
1464 	SGEN_TV_GETTIME (btv);
1465 	time_minor_scan_los += SGEN_TV_ELAPSED (atv, btv);
1466 
1467 	if (worker_data_untyped)
1468 		((WorkerData*)worker_data_untyped)->los_scan_time += SGEN_TV_ELAPSED (atv, btv);
1469 }
1470 
1471 static void
job_scan_major_mod_union_card_table(void * worker_data_untyped,SgenThreadPoolJob * job)1472 job_scan_major_mod_union_card_table (void *worker_data_untyped, SgenThreadPoolJob *job)
1473 {
1474 	SGEN_TV_DECLARE (atv);
1475 	SGEN_TV_DECLARE (btv);
1476 	ParallelScanJob *job_data = (ParallelScanJob*)job;
1477 	ScanCopyContext ctx = scan_copy_context_for_scan_job (worker_data_untyped, (ScanJob*)job_data);
1478 
1479 	g_assert (concurrent_collection_in_progress);
1480 	SGEN_TV_GETTIME (atv);
1481 	major_collector.scan_card_table (CARDTABLE_SCAN_MOD_UNION, ctx, job_data->job_index, job_data->job_split_count, job_data->data);
1482 	SGEN_TV_GETTIME (btv);
1483 	time_major_scan_mod_union_blocks += SGEN_TV_ELAPSED (atv, btv);
1484 
1485 	if (worker_data_untyped)
1486 		((WorkerData*)worker_data_untyped)->major_scan_time += SGEN_TV_ELAPSED (atv, btv);
1487 }
1488 
1489 static void
job_scan_los_mod_union_card_table(void * worker_data_untyped,SgenThreadPoolJob * job)1490 job_scan_los_mod_union_card_table (void *worker_data_untyped, SgenThreadPoolJob *job)
1491 {
1492 	SGEN_TV_DECLARE (atv);
1493 	SGEN_TV_DECLARE (btv);
1494 	ParallelScanJob *job_data = (ParallelScanJob*)job;
1495 	ScanCopyContext ctx = scan_copy_context_for_scan_job (worker_data_untyped, (ScanJob*)job_data);
1496 
1497 	g_assert (concurrent_collection_in_progress);
1498 	SGEN_TV_GETTIME (atv);
1499 	sgen_los_scan_card_table (CARDTABLE_SCAN_MOD_UNION, ctx, job_data->job_index, job_data->job_split_count);
1500 	SGEN_TV_GETTIME (btv);
1501 	time_major_scan_mod_union_los += SGEN_TV_ELAPSED (atv, btv);
1502 
1503 	if (worker_data_untyped)
1504 		((WorkerData*)worker_data_untyped)->los_scan_time += SGEN_TV_ELAPSED (atv, btv);
1505 }
1506 
1507 static void
job_major_mod_union_preclean(void * worker_data_untyped,SgenThreadPoolJob * job)1508 job_major_mod_union_preclean (void *worker_data_untyped, SgenThreadPoolJob *job)
1509 {
1510 	SGEN_TV_DECLARE (atv);
1511 	SGEN_TV_DECLARE (btv);
1512 	ParallelScanJob *job_data = (ParallelScanJob*)job;
1513 	ScanCopyContext ctx = scan_copy_context_for_scan_job (worker_data_untyped, (ScanJob*)job_data);
1514 
1515 	g_assert (concurrent_collection_in_progress);
1516 	SGEN_TV_GETTIME (atv);
1517 	major_collector.scan_card_table (CARDTABLE_SCAN_MOD_UNION_PRECLEAN, ctx, job_data->job_index, job_data->job_split_count, job_data->data);
1518 	SGEN_TV_GETTIME (btv);
1519 
1520 	g_assert (worker_data_untyped);
1521 	((WorkerData*)worker_data_untyped)->major_scan_time += SGEN_TV_ELAPSED (atv, btv);
1522 }
1523 
1524 static void
job_los_mod_union_preclean(void * worker_data_untyped,SgenThreadPoolJob * job)1525 job_los_mod_union_preclean (void *worker_data_untyped, SgenThreadPoolJob *job)
1526 {
1527 	SGEN_TV_DECLARE (atv);
1528 	SGEN_TV_DECLARE (btv);
1529 	ParallelScanJob *job_data = (ParallelScanJob*)job;
1530 	ScanCopyContext ctx = scan_copy_context_for_scan_job (worker_data_untyped, (ScanJob*)job_data);
1531 
1532 	g_assert (concurrent_collection_in_progress);
1533 	SGEN_TV_GETTIME (atv);
1534 	sgen_los_scan_card_table (CARDTABLE_SCAN_MOD_UNION_PRECLEAN, ctx, job_data->job_index, job_data->job_split_count);
1535 	SGEN_TV_GETTIME (btv);
1536 
1537 	g_assert (worker_data_untyped);
1538 	((WorkerData*)worker_data_untyped)->los_scan_time += SGEN_TV_ELAPSED (atv, btv);
1539 }
1540 
1541 static void
job_scan_last_pinned(void * worker_data_untyped,SgenThreadPoolJob * job)1542 job_scan_last_pinned (void *worker_data_untyped, SgenThreadPoolJob *job)
1543 {
1544 	ScanJob *job_data = (ScanJob*)job;
1545 	ScanCopyContext ctx = scan_copy_context_for_scan_job (worker_data_untyped, job_data);
1546 
1547 	g_assert (concurrent_collection_in_progress);
1548 
1549 	sgen_scan_pin_queue_objects (ctx);
1550 }
1551 
1552 static void
workers_finish_callback(void)1553 workers_finish_callback (void)
1554 {
1555 	ParallelScanJob *psj;
1556 	ScanJob *sj;
1557 	size_t num_major_sections = major_collector.get_num_major_sections ();
1558 	int split_count = sgen_workers_get_job_split_count (GENERATION_OLD);
1559 	int i;
1560 	/* Mod union preclean jobs */
1561 	for (i = 0; i < split_count; i++) {
1562 		psj = (ParallelScanJob*)sgen_thread_pool_job_alloc ("preclean major mod union cardtable", job_major_mod_union_preclean, sizeof (ParallelScanJob));
1563 		psj->scan_job.gc_thread_gray_queue = NULL;
1564 		psj->job_index = i;
1565 		psj->job_split_count = split_count;
1566 		psj->data = num_major_sections / split_count;
1567 		sgen_workers_enqueue_job (GENERATION_OLD, &psj->scan_job.job, TRUE);
1568 	}
1569 
1570 	for (i = 0; i < split_count; i++) {
1571 		psj = (ParallelScanJob*)sgen_thread_pool_job_alloc ("preclean los mod union cardtable", job_los_mod_union_preclean, sizeof (ParallelScanJob));
1572 		psj->scan_job.gc_thread_gray_queue = NULL;
1573 		psj->job_index = i;
1574 		psj->job_split_count = split_count;
1575 		sgen_workers_enqueue_job (GENERATION_OLD, &psj->scan_job.job, TRUE);
1576 	}
1577 
1578 	sj = (ScanJob*)sgen_thread_pool_job_alloc ("scan last pinned", job_scan_last_pinned, sizeof (ScanJob));
1579 	sj->gc_thread_gray_queue = NULL;
1580 	sgen_workers_enqueue_job (GENERATION_OLD, &sj->job, TRUE);
1581 }
1582 
1583 static void
init_gray_queue(SgenGrayQueue * gc_thread_gray_queue)1584 init_gray_queue (SgenGrayQueue *gc_thread_gray_queue)
1585 {
1586 	sgen_gray_object_queue_init (gc_thread_gray_queue, NULL, TRUE);
1587 }
1588 
1589 static void
enqueue_scan_remembered_set_jobs(SgenGrayQueue * gc_thread_gray_queue,SgenObjectOperations * ops,gboolean enqueue)1590 enqueue_scan_remembered_set_jobs (SgenGrayQueue *gc_thread_gray_queue, SgenObjectOperations *ops, gboolean enqueue)
1591 {
1592 	int i, split_count = sgen_workers_get_job_split_count (GENERATION_NURSERY);
1593 	size_t num_major_sections = major_collector.get_num_major_sections ();
1594 	ScanJob *sj;
1595 
1596 	sj = (ScanJob*)sgen_thread_pool_job_alloc ("scan wbroots", job_scan_wbroots, sizeof (ScanJob));
1597 	sj->ops = ops;
1598 	sj->gc_thread_gray_queue = gc_thread_gray_queue;
1599 	sgen_workers_enqueue_job (GENERATION_NURSERY, &sj->job, enqueue);
1600 
1601 	for (i = 0; i < split_count; i++) {
1602 		ParallelScanJob *psj;
1603 
1604 		psj = (ParallelScanJob*)sgen_thread_pool_job_alloc ("scan major remsets", job_scan_major_card_table, sizeof (ParallelScanJob));
1605 		psj->scan_job.ops = ops;
1606 		psj->scan_job.gc_thread_gray_queue = gc_thread_gray_queue;
1607 		psj->job_index = i;
1608 		psj->job_split_count = split_count;
1609 		psj->data = num_major_sections / split_count;
1610 		sgen_workers_enqueue_job (GENERATION_NURSERY, &psj->scan_job.job, enqueue);
1611 
1612 		psj = (ParallelScanJob*)sgen_thread_pool_job_alloc ("scan LOS remsets", job_scan_los_card_table, sizeof (ParallelScanJob));
1613 		psj->scan_job.ops = ops;
1614 		psj->scan_job.gc_thread_gray_queue = gc_thread_gray_queue;
1615 		psj->job_index = i;
1616 		psj->job_split_count = split_count;
1617 		sgen_workers_enqueue_job (GENERATION_NURSERY, &psj->scan_job.job, enqueue);
1618 	}
1619 }
1620 
1621 static void
enqueue_scan_from_roots_jobs(SgenGrayQueue * gc_thread_gray_queue,char * heap_start,char * heap_end,SgenObjectOperations * ops,gboolean enqueue)1622 enqueue_scan_from_roots_jobs (SgenGrayQueue *gc_thread_gray_queue, char *heap_start, char *heap_end, SgenObjectOperations *ops, gboolean enqueue)
1623 {
1624 	ScanFromRegisteredRootsJob *scrrj;
1625 	ScanThreadDataJob *stdj;
1626 	ScanFinalizerEntriesJob *sfej;
1627 
1628 	/* registered roots, this includes static fields */
1629 
1630 	scrrj = (ScanFromRegisteredRootsJob*)sgen_thread_pool_job_alloc ("scan from registered roots normal", job_scan_from_registered_roots, sizeof (ScanFromRegisteredRootsJob));
1631 	scrrj->scan_job.ops = ops;
1632 	scrrj->scan_job.gc_thread_gray_queue = gc_thread_gray_queue;
1633 	scrrj->heap_start = heap_start;
1634 	scrrj->heap_end = heap_end;
1635 	scrrj->root_type = ROOT_TYPE_NORMAL;
1636 	sgen_workers_enqueue_job (current_collection_generation, &scrrj->scan_job.job, enqueue);
1637 
1638 	if (current_collection_generation == GENERATION_OLD) {
1639 		/* During minors we scan the cardtable for these roots instead */
1640 		scrrj = (ScanFromRegisteredRootsJob*)sgen_thread_pool_job_alloc ("scan from registered roots wbarrier", job_scan_from_registered_roots, sizeof (ScanFromRegisteredRootsJob));
1641 		scrrj->scan_job.ops = ops;
1642 		scrrj->scan_job.gc_thread_gray_queue = gc_thread_gray_queue;
1643 		scrrj->heap_start = heap_start;
1644 		scrrj->heap_end = heap_end;
1645 		scrrj->root_type = ROOT_TYPE_WBARRIER;
1646 		sgen_workers_enqueue_job (current_collection_generation, &scrrj->scan_job.job, enqueue);
1647 	}
1648 
1649 	/* Threads */
1650 
1651 	stdj = (ScanThreadDataJob*)sgen_thread_pool_job_alloc ("scan thread data", job_scan_thread_data, sizeof (ScanThreadDataJob));
1652 	stdj->scan_job.ops = ops;
1653 	stdj->scan_job.gc_thread_gray_queue = gc_thread_gray_queue;
1654 	stdj->heap_start = heap_start;
1655 	stdj->heap_end = heap_end;
1656 	sgen_workers_enqueue_job (current_collection_generation, &stdj->scan_job.job, enqueue);
1657 
1658 	/* Scan the list of objects ready for finalization. */
1659 
1660 	sfej = (ScanFinalizerEntriesJob*)sgen_thread_pool_job_alloc ("scan finalizer entries", job_scan_finalizer_entries, sizeof (ScanFinalizerEntriesJob));
1661 	sfej->scan_job.ops = ops;
1662 	sfej->scan_job.gc_thread_gray_queue = gc_thread_gray_queue;
1663 	sfej->queue = &fin_ready_queue;
1664 	sgen_workers_enqueue_job (current_collection_generation, &sfej->scan_job.job, enqueue);
1665 
1666 	sfej = (ScanFinalizerEntriesJob*)sgen_thread_pool_job_alloc ("scan critical finalizer entries", job_scan_finalizer_entries, sizeof (ScanFinalizerEntriesJob));
1667 	sfej->scan_job.ops = ops;
1668 	sfej->scan_job.gc_thread_gray_queue = gc_thread_gray_queue;
1669 	sfej->queue = &critical_fin_queue;
1670 	sgen_workers_enqueue_job (current_collection_generation, &sfej->scan_job.job, enqueue);
1671 }
1672 
1673 /*
1674  * Perform a nursery collection.
1675  *
1676  * Return whether any objects were late-pinned due to being out of memory.
1677  */
1678 static gboolean
collect_nursery(const char * reason,gboolean is_overflow,SgenGrayQueue * unpin_queue)1679 collect_nursery (const char *reason, gboolean is_overflow, SgenGrayQueue *unpin_queue)
1680 {
1681 	gboolean needs_major, is_parallel = FALSE;
1682 	mword fragment_total;
1683 	SgenGrayQueue gc_thread_gray_queue;
1684 	SgenObjectOperations *object_ops_nopar, *object_ops_par = NULL;
1685 	ScanCopyContext ctx;
1686 	TV_DECLARE (atv);
1687 	TV_DECLARE (btv);
1688 	SGEN_TV_DECLARE (last_minor_collection_start_tv);
1689 	SGEN_TV_DECLARE (last_minor_collection_end_tv);
1690 	guint64 major_scan_start = time_minor_scan_major_blocks;
1691 	guint64 los_scan_start = time_minor_scan_los;
1692 	guint64 finish_gray_start = time_minor_finish_gray_stack;
1693 
1694 	if (disable_minor_collections)
1695 		return TRUE;
1696 
1697 	TV_GETTIME (last_minor_collection_start_tv);
1698 	atv = last_minor_collection_start_tv;
1699 
1700 	binary_protocol_collection_begin (mono_atomic_load_i32 (&gc_stats.minor_gc_count), GENERATION_NURSERY);
1701 
1702 	object_ops_nopar = sgen_concurrent_collection_in_progress ()
1703 				? &sgen_minor_collector.serial_ops_with_concurrent_major
1704 				: &sgen_minor_collector.serial_ops;
1705 	if (sgen_minor_collector.is_parallel && sgen_nursery_size >= SGEN_PARALLEL_MINOR_MIN_NURSERY_SIZE) {
1706 		object_ops_par = sgen_concurrent_collection_in_progress ()
1707 					? &sgen_minor_collector.parallel_ops_with_concurrent_major
1708 					: &sgen_minor_collector.parallel_ops;
1709 		is_parallel = TRUE;
1710 	}
1711 
1712 	if (do_verify_nursery || do_dump_nursery_content)
1713 		sgen_debug_verify_nursery (do_dump_nursery_content);
1714 
1715 	current_collection_generation = GENERATION_NURSERY;
1716 
1717 	SGEN_ASSERT (0, !sgen_collection_is_concurrent (), "Why is the nursery collection concurrent?");
1718 
1719 	reset_pinned_from_failed_allocation ();
1720 
1721 	check_scan_starts ();
1722 
1723 	sgen_nursery_alloc_prepare_for_minor ();
1724 
1725 	degraded_mode = 0;
1726 	objects_pinned = 0;
1727 
1728 	SGEN_LOG (1, "Start nursery collection %" G_GINT32_FORMAT " %p-%p, size: %d", mono_atomic_load_i32 (&gc_stats.minor_gc_count), nursery_section->data, nursery_section->end_data, (int)(nursery_section->end_data - nursery_section->data));
1729 
1730 	/* world must be stopped already */
1731 	TV_GETTIME (btv);
1732 	time_minor_pre_collection_fragment_clear += TV_ELAPSED (atv, btv);
1733 
1734 	sgen_client_pre_collection_checks ();
1735 
1736 	major_collector.start_nursery_collection ();
1737 
1738 	sgen_memgov_minor_collection_start ();
1739 
1740 	init_gray_queue (&gc_thread_gray_queue);
1741 	ctx = CONTEXT_FROM_OBJECT_OPERATIONS (object_ops_nopar, &gc_thread_gray_queue);
1742 
1743 	mono_atomic_inc_i32 (&gc_stats.minor_gc_count);
1744 
1745 	sgen_process_fin_stage_entries ();
1746 
1747 	/* pin from pinned handles */
1748 	sgen_init_pinning ();
1749 	if (concurrent_collection_in_progress)
1750 		sgen_init_pinning_for_conc ();
1751 	sgen_client_binary_protocol_mark_start (GENERATION_NURSERY);
1752 	pin_from_roots (nursery_section->data, nursery_section->end_data, ctx);
1753 	/* pin cemented objects */
1754 	sgen_pin_cemented_objects ();
1755 	/* identify pinned objects */
1756 	sgen_optimize_pin_queue ();
1757 	sgen_pinning_setup_section (nursery_section);
1758 
1759 	pin_objects_in_nursery (FALSE, ctx);
1760 	sgen_pinning_trim_queue_to_section (nursery_section);
1761 	if (concurrent_collection_in_progress)
1762 		sgen_finish_pinning_for_conc ();
1763 
1764 	if (remset_consistency_checks)
1765 		sgen_check_remset_consistency ();
1766 
1767 	if (whole_heap_check_before_collection) {
1768 		sgen_clear_nursery_fragments ();
1769 		sgen_check_whole_heap (FALSE);
1770 	}
1771 
1772 	TV_GETTIME (atv);
1773 	time_minor_pinning += TV_ELAPSED (btv, atv);
1774 	SGEN_LOG (2, "Finding pinned pointers: %zd in %lld usecs", sgen_get_pinned_count (), (long long)TV_ELAPSED (btv, atv));
1775 	SGEN_LOG (4, "Start scan with %zd pinned objects", sgen_get_pinned_count ());
1776 
1777 	remset.start_scan_remsets ();
1778 
1779 	enqueue_scan_remembered_set_jobs (&gc_thread_gray_queue, is_parallel ? NULL : object_ops_nopar, is_parallel);
1780 
1781 	/* we don't have complete write barrier yet, so we scan all the old generation sections */
1782 	TV_GETTIME (btv);
1783 	time_minor_scan_remsets += TV_ELAPSED (atv, btv);
1784 	SGEN_LOG (2, "Old generation scan: %lld usecs", (long long)TV_ELAPSED (atv, btv));
1785 
1786 	sgen_pin_stats_report ();
1787 
1788 	/* FIXME: Why do we do this at this specific, seemingly random, point? */
1789 	sgen_client_collecting_minor (&fin_ready_queue, &critical_fin_queue);
1790 
1791 	TV_GETTIME (atv);
1792 	time_minor_scan_pinned += TV_ELAPSED (btv, atv);
1793 
1794 	enqueue_scan_from_roots_jobs (&gc_thread_gray_queue, nursery_section->data, nursery_section->end_data, is_parallel ? NULL : object_ops_nopar, is_parallel);
1795 
1796 	if (is_parallel) {
1797 		gray_queue_redirect (&gc_thread_gray_queue);
1798 		sgen_workers_start_all_workers (GENERATION_NURSERY, object_ops_nopar, object_ops_par, NULL);
1799 		sgen_workers_join (GENERATION_NURSERY);
1800 	}
1801 
1802 	TV_GETTIME (btv);
1803 	time_minor_scan_roots += TV_ELAPSED (atv, btv);
1804 
1805 	finish_gray_stack (GENERATION_NURSERY, ctx);
1806 
1807 	TV_GETTIME (atv);
1808 	time_minor_finish_gray_stack += TV_ELAPSED (btv, atv);
1809 	sgen_client_binary_protocol_mark_end (GENERATION_NURSERY);
1810 
1811 	if (objects_pinned) {
1812 		sgen_optimize_pin_queue ();
1813 		sgen_pinning_setup_section (nursery_section);
1814 	}
1815 
1816 	/*
1817 	 * This is the latest point at which we can do this check, because
1818 	 * sgen_build_nursery_fragments() unpins nursery objects again.
1819 	 */
1820 	if (remset_consistency_checks)
1821 		sgen_check_remset_consistency ();
1822 
1823 
1824 	if (sgen_max_pause_time) {
1825 		int duration;
1826 
1827 		TV_GETTIME (btv);
1828 		duration = (int)(TV_ELAPSED (last_minor_collection_start_tv, btv) / 10000);
1829 		if (duration > (sgen_max_pause_time * sgen_max_pause_margin))
1830 			sgen_resize_nursery (TRUE);
1831 		else
1832 			sgen_resize_nursery (FALSE);
1833 	} else {
1834 			sgen_resize_nursery (FALSE);
1835 	}
1836 
1837 	/* walk the pin_queue, build up the fragment list of free memory, unmark
1838 	 * pinned objects as we go, memzero() the empty fragments so they are ready for the
1839 	 * next allocations.
1840 	 */
1841 	sgen_client_binary_protocol_reclaim_start (GENERATION_NURSERY);
1842 	fragment_total = sgen_build_nursery_fragments (nursery_section, unpin_queue);
1843 	if (!fragment_total)
1844 		degraded_mode = 1;
1845 
1846 	/* Clear TLABs for all threads */
1847 	sgen_clear_tlabs ();
1848 
1849 	sgen_client_binary_protocol_reclaim_end (GENERATION_NURSERY);
1850 	TV_GETTIME (btv);
1851 	time_minor_fragment_creation += TV_ELAPSED (atv, btv);
1852 	SGEN_LOG (2, "Fragment creation: %lld usecs, %lu bytes available", (long long)TV_ELAPSED (atv, btv), (unsigned long)fragment_total);
1853 
1854 	if (remset_consistency_checks)
1855 		sgen_check_major_refs ();
1856 
1857 	major_collector.finish_nursery_collection ();
1858 
1859 	TV_GETTIME (last_minor_collection_end_tv);
1860 	UnlockedAdd64 (&gc_stats.minor_gc_time, TV_ELAPSED (last_minor_collection_start_tv, last_minor_collection_end_tv));
1861 
1862 	sgen_debug_dump_heap ("minor", mono_atomic_load_i32 (&gc_stats.minor_gc_count) - 1, NULL);
1863 
1864 	/* prepare the pin queue for the next collection */
1865 	sgen_finish_pinning ();
1866 	if (sgen_have_pending_finalizers ()) {
1867 		SGEN_LOG (4, "Finalizer-thread wakeup");
1868 		sgen_client_finalize_notify ();
1869 	}
1870 	sgen_pin_stats_reset ();
1871 	/* clear cemented hash */
1872 	sgen_cement_clear_below_threshold ();
1873 
1874 	sgen_gray_object_queue_dispose (&gc_thread_gray_queue);
1875 
1876 	check_scan_starts ();
1877 
1878 	binary_protocol_flush_buffers (FALSE);
1879 
1880 	sgen_memgov_minor_collection_end (reason, is_overflow);
1881 
1882 	/*objects are late pinned because of lack of memory, so a major is a good call*/
1883 	needs_major = objects_pinned > 0;
1884 	current_collection_generation = -1;
1885 	objects_pinned = 0;
1886 
1887 	if (is_parallel)
1888 		binary_protocol_collection_end_stats (0, 0, time_minor_finish_gray_stack - finish_gray_start);
1889 	else
1890 		binary_protocol_collection_end_stats (
1891 			time_minor_scan_major_blocks - major_scan_start,
1892 			time_minor_scan_los - los_scan_start,
1893 			time_minor_finish_gray_stack - finish_gray_start);
1894 
1895 	binary_protocol_collection_end (mono_atomic_load_i32 (&gc_stats.minor_gc_count) - 1, GENERATION_NURSERY, 0, 0);
1896 
1897 	if (check_nursery_objects_pinned && !sgen_minor_collector.is_split)
1898 		sgen_check_nursery_objects_pinned (unpin_queue != NULL);
1899 
1900 	return needs_major;
1901 }
1902 
1903 typedef enum {
1904 	COPY_OR_MARK_FROM_ROOTS_SERIAL,
1905 	COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT,
1906 	COPY_OR_MARK_FROM_ROOTS_FINISH_CONCURRENT
1907 } CopyOrMarkFromRootsMode;
1908 
1909 static void
major_copy_or_mark_from_roots(SgenGrayQueue * gc_thread_gray_queue,size_t * old_next_pin_slot,CopyOrMarkFromRootsMode mode,SgenObjectOperations * object_ops_nopar,SgenObjectOperations * object_ops_par)1910 major_copy_or_mark_from_roots (SgenGrayQueue *gc_thread_gray_queue, size_t *old_next_pin_slot, CopyOrMarkFromRootsMode mode, SgenObjectOperations *object_ops_nopar, SgenObjectOperations *object_ops_par)
1911 {
1912 	LOSObject *bigobj;
1913 	TV_DECLARE (atv);
1914 	TV_DECLARE (btv);
1915 	/* FIXME: only use these values for the precise scan
1916 	 * note that to_space pointers should be excluded anyway...
1917 	 */
1918 	char *heap_start = NULL;
1919 	char *heap_end = (char*)-1;
1920 	ScanCopyContext ctx = CONTEXT_FROM_OBJECT_OPERATIONS (object_ops_nopar, gc_thread_gray_queue);
1921 	gboolean concurrent = mode != COPY_OR_MARK_FROM_ROOTS_SERIAL;
1922 
1923 	SGEN_ASSERT (0, !!concurrent == !!concurrent_collection_in_progress, "We've been called with the wrong mode.");
1924 
1925 	if (mode == COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT) {
1926 		/*This cleans up unused fragments */
1927 		sgen_nursery_allocator_prepare_for_pinning ();
1928 
1929 		if (do_concurrent_checks)
1930 			sgen_debug_check_nursery_is_clean ();
1931 	} else {
1932 		/* The concurrent collector doesn't touch the nursery. */
1933 		sgen_nursery_alloc_prepare_for_major ();
1934 	}
1935 
1936 	TV_GETTIME (atv);
1937 
1938 	/* Pinning depends on this */
1939 	sgen_clear_nursery_fragments ();
1940 
1941 	if (whole_heap_check_before_collection)
1942 		sgen_check_whole_heap (TRUE);
1943 
1944 	TV_GETTIME (btv);
1945 	time_major_pre_collection_fragment_clear += TV_ELAPSED (atv, btv);
1946 
1947 	objects_pinned = 0;
1948 
1949 	sgen_client_pre_collection_checks ();
1950 
1951 	if (mode != COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT) {
1952 		/* Remsets are not useful for a major collection */
1953 		remset.clear_cards ();
1954 	}
1955 
1956 	sgen_process_fin_stage_entries ();
1957 
1958 	TV_GETTIME (atv);
1959 	sgen_init_pinning ();
1960 	if (mode == COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT)
1961 		sgen_init_pinning_for_conc ();
1962 	SGEN_LOG (6, "Collecting pinned addresses");
1963 	pin_from_roots ((void*)lowest_heap_address, (void*)highest_heap_address, ctx);
1964 	if (mode == COPY_OR_MARK_FROM_ROOTS_FINISH_CONCURRENT) {
1965 		/* Pin cemented objects that were forced */
1966 		sgen_pin_cemented_objects ();
1967 	}
1968 	sgen_optimize_pin_queue ();
1969 	if (mode == COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT) {
1970 		/*
1971 		 * Cemented objects that are in the pinned list will be marked. When
1972 		 * marking concurrently we won't mark mod-union cards for these objects.
1973 		 * Instead they will remain cemented until the next major collection,
1974 		 * when we will recheck if they are still pinned in the roots.
1975 		 */
1976 		sgen_cement_force_pinned ();
1977 	}
1978 
1979 	sgen_client_collecting_major_1 ();
1980 
1981 	/*
1982 	 * pin_queue now contains all candidate pointers, sorted and
1983 	 * uniqued.  We must do two passes now to figure out which
1984 	 * objects are pinned.
1985 	 *
1986 	 * The first is to find within the pin_queue the area for each
1987 	 * section.  This requires that the pin_queue be sorted.  We
1988 	 * also process the LOS objects and pinned chunks here.
1989 	 *
1990 	 * The second, destructive, pass is to reduce the section
1991 	 * areas to pointers to the actually pinned objects.
1992 	 */
1993 	SGEN_LOG (6, "Pinning from sections");
1994 	/* first pass for the sections */
1995 	sgen_find_section_pin_queue_start_end (nursery_section);
1996 	/* identify possible pointers to the insize of large objects */
1997 	SGEN_LOG (6, "Pinning from large objects");
1998 	for (bigobj = los_object_list; bigobj; bigobj = bigobj->next) {
1999 		size_t dummy;
2000 		if (sgen_find_optimized_pin_queue_area ((char*)bigobj->data, (char*)bigobj->data + sgen_los_object_size (bigobj), &dummy, &dummy)) {
2001 			binary_protocol_pin (bigobj->data, (gpointer)LOAD_VTABLE (bigobj->data), safe_object_get_size (bigobj->data));
2002 
2003 			if (sgen_los_object_is_pinned (bigobj->data)) {
2004 				SGEN_ASSERT (0, mode == COPY_OR_MARK_FROM_ROOTS_FINISH_CONCURRENT, "LOS objects can only be pinned here after concurrent marking.");
2005 				continue;
2006 			}
2007 			sgen_los_pin_object (bigobj->data);
2008 			if (SGEN_OBJECT_HAS_REFERENCES (bigobj->data))
2009 				GRAY_OBJECT_ENQUEUE_SERIAL (gc_thread_gray_queue, bigobj->data, sgen_obj_get_descriptor ((GCObject*)bigobj->data));
2010 			sgen_pin_stats_register_object (bigobj->data, GENERATION_OLD);
2011 			SGEN_LOG (6, "Marked large object %p (%s) size: %lu from roots", bigobj->data,
2012 					sgen_client_vtable_get_name (SGEN_LOAD_VTABLE (bigobj->data)),
2013 					(unsigned long)sgen_los_object_size (bigobj));
2014 
2015 			sgen_client_pinned_los_object (bigobj->data);
2016 		}
2017 	}
2018 
2019 	pin_objects_in_nursery (mode == COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT, ctx);
2020 	if (check_nursery_objects_pinned && !sgen_minor_collector.is_split)
2021 		sgen_check_nursery_objects_pinned (mode != COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT);
2022 
2023 	major_collector.pin_objects (gc_thread_gray_queue);
2024 	if (old_next_pin_slot)
2025 		*old_next_pin_slot = sgen_get_pinned_count ();
2026 
2027 	TV_GETTIME (btv);
2028 	time_major_pinning += TV_ELAPSED (atv, btv);
2029 	SGEN_LOG (2, "Finding pinned pointers: %zd in %lld usecs", sgen_get_pinned_count (), (long long)TV_ELAPSED (atv, btv));
2030 	SGEN_LOG (4, "Start scan with %zd pinned objects", sgen_get_pinned_count ());
2031 
2032 	if (mode == COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT)
2033 		sgen_finish_pinning_for_conc ();
2034 
2035 	major_collector.init_to_space ();
2036 
2037 	SGEN_ASSERT (0, sgen_workers_all_done (), "Why are the workers not done when we start or finish a major collection?");
2038 	if (mode == COPY_OR_MARK_FROM_ROOTS_FINISH_CONCURRENT) {
2039 		if (object_ops_par != NULL)
2040 			sgen_workers_set_num_active_workers (GENERATION_OLD, 0);
2041 		if (object_ops_par == NULL && sgen_workers_have_idle_work (GENERATION_OLD)) {
2042 			/*
2043 			 * We force the finish of the worker with the new object ops context
2044 			 * which can also do copying. We need to have finished pinning. On the
2045 			 * parallel collector, there is no need to drain the private queues
2046 			 * here, since we can do it as part of the finishing work, achieving
2047 			 * better work distribution.
2048 			 */
2049 			sgen_workers_start_all_workers (GENERATION_OLD, object_ops_nopar, object_ops_par, NULL);
2050 
2051 			sgen_workers_join (GENERATION_OLD);
2052 		}
2053 	}
2054 
2055 #ifdef SGEN_DEBUG_INTERNAL_ALLOC
2056 	main_gc_thread = mono_native_thread_self ();
2057 #endif
2058 
2059 	sgen_client_collecting_major_2 ();
2060 
2061 	TV_GETTIME (atv);
2062 	time_major_scan_pinned += TV_ELAPSED (btv, atv);
2063 
2064 	sgen_client_collecting_major_3 (&fin_ready_queue, &critical_fin_queue);
2065 
2066 	enqueue_scan_from_roots_jobs (gc_thread_gray_queue, heap_start, heap_end, object_ops_nopar, FALSE);
2067 
2068 	TV_GETTIME (btv);
2069 	time_major_scan_roots += TV_ELAPSED (atv, btv);
2070 
2071 	/*
2072 	 * We start the concurrent worker after pinning and after we scanned the roots
2073 	 * in order to make sure that the worker does not finish before handling all
2074 	 * the roots.
2075 	 */
2076 	if (mode == COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT) {
2077 		sgen_workers_set_num_active_workers (GENERATION_OLD, 1);
2078 		gray_queue_redirect (gc_thread_gray_queue);
2079 		if (precleaning_enabled) {
2080 			sgen_workers_start_all_workers (GENERATION_OLD, object_ops_nopar, object_ops_par, workers_finish_callback);
2081 		} else {
2082 			sgen_workers_start_all_workers (GENERATION_OLD, object_ops_nopar, object_ops_par, NULL);
2083 		}
2084 	}
2085 
2086 	if (mode == COPY_OR_MARK_FROM_ROOTS_FINISH_CONCURRENT) {
2087 		int i, split_count = sgen_workers_get_job_split_count (GENERATION_OLD);
2088 		size_t num_major_sections = major_collector.get_num_major_sections ();
2089 		gboolean parallel = object_ops_par != NULL;
2090 
2091 		/* If we're not parallel we finish the collection on the gc thread */
2092 		if (parallel)
2093 			gray_queue_redirect (gc_thread_gray_queue);
2094 
2095 		/* Mod union card table */
2096 		for (i = 0; i < split_count; i++) {
2097 			ParallelScanJob *psj;
2098 
2099 			psj = (ParallelScanJob*)sgen_thread_pool_job_alloc ("scan mod union cardtable", job_scan_major_mod_union_card_table, sizeof (ParallelScanJob));
2100 			psj->scan_job.ops = parallel ? NULL : object_ops_nopar;
2101 			psj->scan_job.gc_thread_gray_queue = gc_thread_gray_queue;
2102 			psj->job_index = i;
2103 			psj->job_split_count = split_count;
2104 			psj->data = num_major_sections / split_count;
2105 			sgen_workers_enqueue_job (GENERATION_OLD, &psj->scan_job.job, parallel);
2106 
2107 			psj = (ParallelScanJob*)sgen_thread_pool_job_alloc ("scan LOS mod union cardtable", job_scan_los_mod_union_card_table, sizeof (ParallelScanJob));
2108 			psj->scan_job.ops = parallel ? NULL : object_ops_nopar;
2109 			psj->scan_job.gc_thread_gray_queue = gc_thread_gray_queue;
2110 			psj->job_index = i;
2111 			psj->job_split_count = split_count;
2112 			sgen_workers_enqueue_job (GENERATION_OLD, &psj->scan_job.job, parallel);
2113 		}
2114 
2115 		if (parallel) {
2116 			/*
2117 			 * If we enqueue a job while workers are running we need to sgen_workers_ensure_awake
2118 			 * in order to make sure that we are running the idle func and draining all worker
2119 			 * gray queues. The operation of starting workers implies this, so we start them after
2120 			 * in order to avoid doing this operation twice. The workers will drain the main gray
2121 			 * stack that contained roots and pinned objects and also scan the mod union card
2122 			 * table.
2123 			 */
2124 			sgen_workers_start_all_workers (GENERATION_OLD, object_ops_nopar, object_ops_par, NULL);
2125 			sgen_workers_join (GENERATION_OLD);
2126 		}
2127 	}
2128 
2129 	sgen_pin_stats_report ();
2130 
2131 	if (mode == COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT) {
2132 		sgen_finish_pinning ();
2133 
2134 		sgen_pin_stats_reset ();
2135 
2136 		if (do_concurrent_checks)
2137 			sgen_debug_check_nursery_is_clean ();
2138 	}
2139 }
2140 
2141 static void
major_start_collection(SgenGrayQueue * gc_thread_gray_queue,const char * reason,gboolean concurrent,size_t * old_next_pin_slot)2142 major_start_collection (SgenGrayQueue *gc_thread_gray_queue, const char *reason, gboolean concurrent, size_t *old_next_pin_slot)
2143 {
2144 	SgenObjectOperations *object_ops_nopar, *object_ops_par = NULL;
2145 
2146 	binary_protocol_collection_begin (mono_atomic_load_i32 (&gc_stats.major_gc_count), GENERATION_OLD);
2147 
2148 	current_collection_generation = GENERATION_OLD;
2149 
2150 	sgen_workers_assert_gray_queue_is_empty (GENERATION_OLD);
2151 
2152 	if (!concurrent)
2153 		sgen_cement_reset ();
2154 
2155 	if (concurrent) {
2156 		g_assert (major_collector.is_concurrent);
2157 		concurrent_collection_in_progress = TRUE;
2158 
2159 		object_ops_nopar = &major_collector.major_ops_concurrent_start;
2160 		if (major_collector.is_parallel)
2161 			object_ops_par = &major_collector.major_ops_conc_par_start;
2162 
2163 	} else {
2164 		object_ops_nopar = &major_collector.major_ops_serial;
2165 	}
2166 
2167 	reset_pinned_from_failed_allocation ();
2168 
2169 	sgen_memgov_major_collection_start (concurrent, reason);
2170 
2171 	//count_ref_nonref_objs ();
2172 	//consistency_check ();
2173 
2174 	check_scan_starts ();
2175 
2176 	degraded_mode = 0;
2177 	SGEN_LOG (1, "Start major collection %" G_GINT32_FORMAT, mono_atomic_load_i32 (&gc_stats.major_gc_count));
2178 	mono_atomic_inc_i32 (&gc_stats.major_gc_count);
2179 
2180 	if (major_collector.start_major_collection)
2181 		major_collector.start_major_collection ();
2182 
2183 	major_copy_or_mark_from_roots (gc_thread_gray_queue, old_next_pin_slot, concurrent ? COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT : COPY_OR_MARK_FROM_ROOTS_SERIAL, object_ops_nopar, object_ops_par);
2184 }
2185 
2186 static void
major_finish_collection(SgenGrayQueue * gc_thread_gray_queue,const char * reason,gboolean is_overflow,size_t old_next_pin_slot,gboolean forced)2187 major_finish_collection (SgenGrayQueue *gc_thread_gray_queue, const char *reason, gboolean is_overflow, size_t old_next_pin_slot, gboolean forced)
2188 {
2189 	ScannedObjectCounts counts;
2190 	SgenObjectOperations *object_ops_nopar;
2191 	mword fragment_total;
2192 	TV_DECLARE (atv);
2193 	TV_DECLARE (btv);
2194 	guint64 major_scan_start = time_major_scan_mod_union_blocks;
2195 	guint64 los_scan_start = time_major_scan_mod_union_los;
2196 	guint64 finish_gray_start = time_major_finish_gray_stack;
2197 
2198 	if (concurrent_collection_in_progress) {
2199 		SgenObjectOperations *object_ops_par = NULL;
2200 
2201 		object_ops_nopar = &major_collector.major_ops_concurrent_finish;
2202 		if (major_collector.is_parallel)
2203 			object_ops_par = &major_collector.major_ops_conc_par_finish;
2204 
2205 		major_copy_or_mark_from_roots (gc_thread_gray_queue, NULL, COPY_OR_MARK_FROM_ROOTS_FINISH_CONCURRENT, object_ops_nopar, object_ops_par);
2206 
2207 #ifdef SGEN_DEBUG_INTERNAL_ALLOC
2208 		main_gc_thread = NULL;
2209 #endif
2210 	} else {
2211 		object_ops_nopar = &major_collector.major_ops_serial;
2212 	}
2213 
2214 	sgen_workers_assert_gray_queue_is_empty (GENERATION_OLD);
2215 
2216 	TV_GETTIME (btv);
2217 	finish_gray_stack (GENERATION_OLD, CONTEXT_FROM_OBJECT_OPERATIONS (object_ops_nopar, gc_thread_gray_queue));
2218 	TV_GETTIME (atv);
2219 	time_major_finish_gray_stack += TV_ELAPSED (btv, atv);
2220 
2221 	SGEN_ASSERT (0, sgen_workers_all_done (), "Can't have workers working after joining");
2222 
2223 	if (objects_pinned) {
2224 		g_assert (!concurrent_collection_in_progress);
2225 
2226 		/*
2227 		 * This is slow, but we just OOM'd.
2228 		 *
2229 		 * See comment at `sgen_pin_queue_clear_discarded_entries` for how the pin
2230 		 * queue is laid out at this point.
2231 		 */
2232 		sgen_pin_queue_clear_discarded_entries (nursery_section, old_next_pin_slot);
2233 		/*
2234 		 * We need to reestablish all pinned nursery objects in the pin queue
2235 		 * because they're needed for fragment creation.  Unpinning happens by
2236 		 * walking the whole queue, so it's not necessary to reestablish where major
2237 		 * heap block pins are - all we care is that they're still in there
2238 		 * somewhere.
2239 		 */
2240 		sgen_optimize_pin_queue ();
2241 		sgen_find_section_pin_queue_start_end (nursery_section);
2242 		objects_pinned = 0;
2243 	}
2244 
2245 	reset_heap_boundaries ();
2246 	sgen_update_heap_boundaries ((mword)sgen_get_nursery_start (), (mword)sgen_get_nursery_end ());
2247 
2248 	/* walk the pin_queue, build up the fragment list of free memory, unmark
2249 	 * pinned objects as we go, memzero() the empty fragments so they are ready for the
2250 	 * next allocations.
2251 	 */
2252 	fragment_total = sgen_build_nursery_fragments (nursery_section, NULL);
2253 	if (!fragment_total)
2254 		degraded_mode = 1;
2255 	SGEN_LOG (4, "Free space in nursery after major %ld", (long)fragment_total);
2256 
2257 	if (do_concurrent_checks && concurrent_collection_in_progress)
2258 		sgen_debug_check_nursery_is_clean ();
2259 
2260 	/* prepare the pin queue for the next collection */
2261 	sgen_finish_pinning ();
2262 
2263 	/* Clear TLABs for all threads */
2264 	sgen_clear_tlabs ();
2265 
2266 	sgen_pin_stats_reset ();
2267 
2268 	sgen_cement_clear_below_threshold ();
2269 
2270 	if (check_mark_bits_after_major_collection)
2271 		sgen_check_heap_marked (concurrent_collection_in_progress);
2272 
2273 	TV_GETTIME (btv);
2274 	time_major_fragment_creation += TV_ELAPSED (atv, btv);
2275 
2276 	binary_protocol_sweep_begin (GENERATION_OLD, !major_collector.sweeps_lazily);
2277 	sgen_memgov_major_pre_sweep ();
2278 
2279 	TV_GETTIME (atv);
2280 	time_major_free_bigobjs += TV_ELAPSED (btv, atv);
2281 
2282 	sgen_los_sweep ();
2283 
2284 	TV_GETTIME (btv);
2285 	time_major_los_sweep += TV_ELAPSED (atv, btv);
2286 
2287 	major_collector.sweep ();
2288 
2289 	binary_protocol_sweep_end (GENERATION_OLD, !major_collector.sweeps_lazily);
2290 
2291 	TV_GETTIME (atv);
2292 	time_major_sweep += TV_ELAPSED (btv, atv);
2293 
2294 	sgen_debug_dump_heap ("major", mono_atomic_load_i32 (&gc_stats.major_gc_count) - 1, reason);
2295 
2296 	if (sgen_have_pending_finalizers ()) {
2297 		SGEN_LOG (4, "Finalizer-thread wakeup");
2298 		sgen_client_finalize_notify ();
2299 	}
2300 
2301 	sgen_memgov_major_collection_end (forced, concurrent_collection_in_progress, reason, is_overflow);
2302 	current_collection_generation = -1;
2303 
2304 	memset (&counts, 0, sizeof (ScannedObjectCounts));
2305 	major_collector.finish_major_collection (&counts);
2306 
2307 	sgen_workers_assert_gray_queue_is_empty (GENERATION_OLD);
2308 
2309 	SGEN_ASSERT (0, sgen_workers_all_done (), "Can't have workers working after major collection has finished");
2310 	if (concurrent_collection_in_progress)
2311 		concurrent_collection_in_progress = FALSE;
2312 
2313 	check_scan_starts ();
2314 
2315 	binary_protocol_flush_buffers (FALSE);
2316 
2317 	//consistency_check ();
2318 	if (major_collector.is_parallel)
2319                 binary_protocol_collection_end_stats (0, 0, time_major_finish_gray_stack - finish_gray_start);
2320         else
2321                 binary_protocol_collection_end_stats (
2322                         time_major_scan_mod_union_blocks - major_scan_start,
2323                         time_major_scan_mod_union_los - los_scan_start,
2324                         time_major_finish_gray_stack - finish_gray_start);
2325 
2326 	binary_protocol_collection_end (mono_atomic_load_i32 (&gc_stats.major_gc_count) - 1, GENERATION_OLD, counts.num_scanned_objects, counts.num_unique_scanned_objects);
2327 }
2328 
2329 static gboolean
major_do_collection(const char * reason,gboolean is_overflow,gboolean forced)2330 major_do_collection (const char *reason, gboolean is_overflow, gboolean forced)
2331 {
2332 	TV_DECLARE (time_start);
2333 	TV_DECLARE (time_end);
2334 	size_t old_next_pin_slot;
2335 	SgenGrayQueue gc_thread_gray_queue;
2336 
2337 	if (disable_major_collections)
2338 		return FALSE;
2339 
2340 	if (major_collector.get_and_reset_num_major_objects_marked) {
2341 		long long num_marked = major_collector.get_and_reset_num_major_objects_marked ();
2342 		g_assert (!num_marked);
2343 	}
2344 
2345 	/* world must be stopped already */
2346 	TV_GETTIME (time_start);
2347 
2348 	init_gray_queue (&gc_thread_gray_queue);
2349 	major_start_collection (&gc_thread_gray_queue, reason, FALSE, &old_next_pin_slot);
2350 	major_finish_collection (&gc_thread_gray_queue, reason, is_overflow, old_next_pin_slot, forced);
2351 	sgen_gray_object_queue_dispose (&gc_thread_gray_queue);
2352 
2353 	TV_GETTIME (time_end);
2354 	UnlockedAdd64 (&gc_stats.major_gc_time, TV_ELAPSED (time_start, time_end));
2355 
2356 	/* FIXME: also report this to the user, preferably in gc-end. */
2357 	if (major_collector.get_and_reset_num_major_objects_marked)
2358 		major_collector.get_and_reset_num_major_objects_marked ();
2359 
2360 	return bytes_pinned_from_failed_allocation > 0;
2361 }
2362 
2363 static void
major_start_concurrent_collection(const char * reason)2364 major_start_concurrent_collection (const char *reason)
2365 {
2366 	TV_DECLARE (time_start);
2367 	TV_DECLARE (time_end);
2368 	long long num_objects_marked;
2369 	SgenGrayQueue gc_thread_gray_queue;
2370 
2371 	if (disable_major_collections)
2372 		return;
2373 
2374 	TV_GETTIME (time_start);
2375 	SGEN_TV_GETTIME (time_major_conc_collection_start);
2376 
2377 	num_objects_marked = major_collector.get_and_reset_num_major_objects_marked ();
2378 	g_assert (num_objects_marked == 0);
2379 
2380 	binary_protocol_concurrent_start ();
2381 
2382 	init_gray_queue (&gc_thread_gray_queue);
2383 	// FIXME: store reason and pass it when finishing
2384 	major_start_collection (&gc_thread_gray_queue, reason, TRUE, NULL);
2385 	sgen_gray_object_queue_dispose (&gc_thread_gray_queue);
2386 
2387 	num_objects_marked = major_collector.get_and_reset_num_major_objects_marked ();
2388 
2389 	TV_GETTIME (time_end);
2390 	UnlockedAdd64 (&gc_stats.major_gc_time, TV_ELAPSED (time_start, time_end));
2391 
2392 	current_collection_generation = -1;
2393 }
2394 
2395 /*
2396  * Returns whether the major collection has finished.
2397  */
2398 static gboolean
major_should_finish_concurrent_collection(void)2399 major_should_finish_concurrent_collection (void)
2400 {
2401 	return sgen_workers_all_done ();
2402 }
2403 
2404 static void
major_update_concurrent_collection(void)2405 major_update_concurrent_collection (void)
2406 {
2407 	TV_DECLARE (total_start);
2408 	TV_DECLARE (total_end);
2409 
2410 	TV_GETTIME (total_start);
2411 
2412 	binary_protocol_concurrent_update ();
2413 
2414 	major_collector.update_cardtable_mod_union ();
2415 	sgen_los_update_cardtable_mod_union ();
2416 
2417 	TV_GETTIME (total_end);
2418 	UnlockedAdd64 (&gc_stats.major_gc_time, TV_ELAPSED (total_start, total_end));
2419 }
2420 
2421 static void
major_finish_concurrent_collection(gboolean forced)2422 major_finish_concurrent_collection (gboolean forced)
2423 {
2424 	SgenGrayQueue gc_thread_gray_queue;
2425 	TV_DECLARE (total_start);
2426 	TV_DECLARE (total_end);
2427 
2428 	TV_GETTIME (total_start);
2429 
2430 	binary_protocol_concurrent_finish ();
2431 
2432 	/*
2433 	 * We need to stop all workers since we're updating the cardtable below.
2434 	 * The workers will be resumed with a finishing pause context to avoid
2435 	 * additional cardtable and object scanning.
2436 	 */
2437 	sgen_workers_stop_all_workers (GENERATION_OLD);
2438 
2439 	SGEN_TV_GETTIME (time_major_conc_collection_end);
2440 	UnlockedAdd64 (&gc_stats.major_gc_time_concurrent, SGEN_TV_ELAPSED (time_major_conc_collection_start, time_major_conc_collection_end));
2441 
2442 	major_collector.update_cardtable_mod_union ();
2443 	sgen_los_update_cardtable_mod_union ();
2444 
2445 	if (mod_union_consistency_check)
2446 		sgen_check_mod_union_consistency ();
2447 
2448 	current_collection_generation = GENERATION_OLD;
2449 	sgen_cement_reset ();
2450 	init_gray_queue (&gc_thread_gray_queue);
2451 	major_finish_collection (&gc_thread_gray_queue, "finishing", FALSE, -1, forced);
2452 	sgen_gray_object_queue_dispose (&gc_thread_gray_queue);
2453 
2454 	TV_GETTIME (total_end);
2455 	UnlockedAdd64 (&gc_stats.major_gc_time, TV_ELAPSED (total_start, total_end));
2456 
2457 	current_collection_generation = -1;
2458 }
2459 
2460 /*
2461  * Ensure an allocation request for @size will succeed by freeing enough memory.
2462  *
2463  * LOCKING: The GC lock MUST be held.
2464  */
2465 void
sgen_ensure_free_space(size_t size,int generation)2466 sgen_ensure_free_space (size_t size, int generation)
2467 {
2468 	int generation_to_collect = -1;
2469 	const char *reason = NULL;
2470 
2471 	if (generation == GENERATION_OLD) {
2472 		if (sgen_need_major_collection (size)) {
2473 			reason = "LOS overflow";
2474 			generation_to_collect = GENERATION_OLD;
2475 		}
2476 	} else {
2477 		if (degraded_mode) {
2478 			if (sgen_need_major_collection (size)) {
2479 				reason = "Degraded mode overflow";
2480 				generation_to_collect = GENERATION_OLD;
2481 			}
2482 		} else if (sgen_need_major_collection (size)) {
2483 			reason = concurrent_collection_in_progress ? "Forced finish concurrent collection" : "Minor allowance";
2484 			generation_to_collect = GENERATION_OLD;
2485 		} else {
2486 			generation_to_collect = GENERATION_NURSERY;
2487 			reason = "Nursery full";
2488 		}
2489 	}
2490 
2491 	if (generation_to_collect == -1) {
2492 		if (concurrent_collection_in_progress && sgen_workers_all_done ()) {
2493 			generation_to_collect = GENERATION_OLD;
2494 			reason = "Finish concurrent collection";
2495 		}
2496 	}
2497 
2498 	if (generation_to_collect == -1)
2499 		return;
2500 	sgen_perform_collection (size, generation_to_collect, reason, FALSE, TRUE);
2501 }
2502 
2503 /*
2504  * LOCKING: Assumes the GC lock is held.
2505  */
2506 static void
sgen_perform_collection_inner(size_t requested_size,int generation_to_collect,const char * reason,gboolean wait_to_finish,gboolean stw)2507 sgen_perform_collection_inner (size_t requested_size, int generation_to_collect, const char *reason, gboolean wait_to_finish, gboolean stw)
2508 {
2509 	TV_DECLARE (gc_total_start);
2510 	TV_DECLARE (gc_total_end);
2511 	int overflow_generation_to_collect = -1;
2512 	int oldest_generation_collected = generation_to_collect;
2513 	const char *overflow_reason = NULL;
2514 	gboolean finish_concurrent = concurrent_collection_in_progress && (major_should_finish_concurrent_collection () || generation_to_collect == GENERATION_OLD);
2515 
2516 	binary_protocol_collection_requested (generation_to_collect, requested_size, wait_to_finish ? 1 : 0);
2517 
2518 	SGEN_ASSERT (0, generation_to_collect == GENERATION_NURSERY || generation_to_collect == GENERATION_OLD, "What generation is this?");
2519 
2520 	if (stw)
2521 		sgen_stop_world (generation_to_collect);
2522 	else
2523 		SGEN_ASSERT (0, sgen_is_world_stopped (), "We can only collect if the world is stopped");
2524 
2525 
2526 	TV_GETTIME (gc_total_start);
2527 
2528 	// FIXME: extract overflow reason
2529 	// FIXME: minor overflow for concurrent case
2530 	if (generation_to_collect == GENERATION_NURSERY && !finish_concurrent) {
2531 		if (concurrent_collection_in_progress)
2532 			major_update_concurrent_collection ();
2533 
2534 		if (collect_nursery (reason, FALSE, NULL) && !concurrent_collection_in_progress) {
2535 			overflow_generation_to_collect = GENERATION_OLD;
2536 			overflow_reason = "Minor overflow";
2537 		}
2538 	} else if (finish_concurrent) {
2539 		major_finish_concurrent_collection (wait_to_finish);
2540 		oldest_generation_collected = GENERATION_OLD;
2541 	} else {
2542 		SGEN_ASSERT (0, generation_to_collect == GENERATION_OLD, "We should have handled nursery collections above");
2543 		if (major_collector.is_concurrent && !wait_to_finish) {
2544 			collect_nursery ("Concurrent start", FALSE, NULL);
2545 			major_start_concurrent_collection (reason);
2546 			oldest_generation_collected = GENERATION_NURSERY;
2547 		} else if (major_do_collection (reason, FALSE, wait_to_finish)) {
2548 			overflow_generation_to_collect = GENERATION_NURSERY;
2549 			overflow_reason = "Excessive pinning";
2550 		}
2551 	}
2552 
2553 	if (overflow_generation_to_collect != -1) {
2554 		SGEN_ASSERT (0, !concurrent_collection_in_progress, "We don't yet support overflow collections with the concurrent collector");
2555 
2556 		/*
2557 		 * We need to do an overflow collection, either because we ran out of memory
2558 		 * or the nursery is fully pinned.
2559 		 */
2560 
2561 		if (overflow_generation_to_collect == GENERATION_NURSERY)
2562 			collect_nursery (overflow_reason, TRUE, NULL);
2563 		else
2564 			major_do_collection (overflow_reason, TRUE, wait_to_finish);
2565 
2566 		oldest_generation_collected = MAX (oldest_generation_collected, overflow_generation_to_collect);
2567 	}
2568 
2569 	SGEN_LOG (2, "Heap size: %lu, LOS size: %lu", (unsigned long)sgen_gc_get_total_heap_allocation (), (unsigned long)los_memory_usage);
2570 
2571 	/* this also sets the proper pointers for the next allocation */
2572 	if (generation_to_collect == GENERATION_NURSERY && !sgen_can_alloc_size (requested_size)) {
2573 		/* TypeBuilder and MonoMethod are killing mcs with fragmentation */
2574 		SGEN_LOG (1, "nursery collection didn't find enough room for %zd alloc (%zd pinned)", requested_size, sgen_get_pinned_count ());
2575 		sgen_dump_pin_queue ();
2576 		degraded_mode = 1;
2577 	}
2578 
2579 	TV_GETTIME (gc_total_end);
2580 	time_max = MAX (time_max, TV_ELAPSED (gc_total_start, gc_total_end));
2581 
2582 	if (stw)
2583 		sgen_restart_world (oldest_generation_collected);
2584 }
2585 
2586 #ifdef HOST_WASM
2587 
2588 typedef struct {
2589 	size_t requested_size;
2590 	int generation_to_collect;
2591 	const char *reason;
2592 } SgenGcRequest;
2593 
2594 static SgenGcRequest gc_request;
2595 static gboolean pending_request;
2596 
2597 extern void request_gc_cycle (void);
2598 
2599 #include <emscripten.h>
2600 
2601 EMSCRIPTEN_KEEPALIVE void
mono_gc_pump_callback(void)2602 mono_gc_pump_callback (void)
2603 {
2604 	if (!pending_request)
2605 		return;
2606 	pending_request = FALSE;
2607 	sgen_perform_collection_inner (gc_request.requested_size, gc_request.generation_to_collect, gc_request.reason, TRUE, TRUE);
2608 }
2609 #endif
2610 
2611 void
sgen_perform_collection(size_t requested_size,int generation_to_collect,const char * reason,gboolean wait_to_finish,gboolean stw)2612 sgen_perform_collection (size_t requested_size, int generation_to_collect, const char *reason, gboolean wait_to_finish, gboolean stw)
2613 {
2614 #ifdef HOST_WASM
2615 	g_assert (stw); //can't handle non-stw mode (IE, domain unload)
2616 	//we ignore wait_to_finish
2617 	if (!pending_request || gc_request.generation_to_collect <= generation_to_collect) { //no active request or request was for a smaller part of the heap
2618 		gc_request.requested_size = requested_size;
2619 		gc_request.generation_to_collect = generation_to_collect;
2620 		gc_request.reason = reason;
2621 		if (!pending_request) {
2622 			request_gc_cycle ();
2623 			pending_request = TRUE;
2624 		}
2625 	}
2626 
2627 	degraded_mode = 1; //enable degraded mode so allocation can continue
2628 #else
2629 	sgen_perform_collection_inner (requested_size, generation_to_collect, reason, wait_to_finish, stw);
2630 #endif
2631 }
2632 /*
2633  * ######################################################################
2634  * ########  Memory allocation from the OS
2635  * ######################################################################
2636  * This section of code deals with getting memory from the OS and
2637  * allocating memory for GC-internal data structures.
2638  * Internal memory can be handled with a freelist for small objects.
2639  */
2640 
2641 /*
2642  * Debug reporting.
2643  */
2644 G_GNUC_UNUSED static void
report_internal_mem_usage(void)2645 report_internal_mem_usage (void)
2646 {
2647 	printf ("Internal memory usage:\n");
2648 	sgen_report_internal_mem_usage ();
2649 	printf ("Pinned memory usage:\n");
2650 	major_collector.report_pinned_memory_usage ();
2651 }
2652 
2653 /*
2654  * ######################################################################
2655  * ########  Finalization support
2656  * ######################################################################
2657  */
2658 
2659 /*
2660  * This function returns true if @object is either alive and belongs to the
2661  * current collection - major collections are full heap, so old gen objects
2662  * are never alive during a minor collection.
2663  */
2664 static inline int
sgen_is_object_alive_and_on_current_collection(GCObject * object)2665 sgen_is_object_alive_and_on_current_collection (GCObject *object)
2666 {
2667 	if (ptr_in_nursery (object))
2668 		return sgen_nursery_is_object_alive (object);
2669 
2670 	if (current_collection_generation == GENERATION_NURSERY)
2671 		return FALSE;
2672 
2673 	return sgen_major_is_object_alive (object);
2674 }
2675 
2676 
2677 gboolean
sgen_gc_is_object_ready_for_finalization(GCObject * object)2678 sgen_gc_is_object_ready_for_finalization (GCObject *object)
2679 {
2680 	return !sgen_is_object_alive (object);
2681 }
2682 
2683 void
sgen_queue_finalization_entry(GCObject * obj)2684 sgen_queue_finalization_entry (GCObject *obj)
2685 {
2686 	gboolean critical = sgen_client_object_has_critical_finalizer (obj);
2687 
2688 	sgen_pointer_queue_add (critical ? &critical_fin_queue : &fin_ready_queue, obj);
2689 
2690 	sgen_client_object_queued_for_finalization (obj);
2691 }
2692 
2693 gboolean
sgen_object_is_live(GCObject * obj)2694 sgen_object_is_live (GCObject *obj)
2695 {
2696 	return sgen_is_object_alive_and_on_current_collection (obj);
2697 }
2698 
2699 /*
2700  * `System.GC.WaitForPendingFinalizers` first checks `sgen_have_pending_finalizers()` to
2701  * determine whether it can exit quickly.  The latter must therefore only return FALSE if
2702  * all finalizers have really finished running.
2703  *
2704  * `sgen_gc_invoke_finalizers()` first dequeues a finalizable object, and then finalizes it.
2705  * This means that just checking whether the queues are empty leaves the possibility that an
2706  * object might have been dequeued but not yet finalized.  That's why we need the additional
2707  * flag `pending_unqueued_finalizer`.
2708  */
2709 
2710 static volatile gboolean pending_unqueued_finalizer = FALSE;
2711 volatile gboolean sgen_suspend_finalizers = FALSE;
2712 
2713 void
sgen_set_suspend_finalizers(void)2714 sgen_set_suspend_finalizers (void)
2715 {
2716 	sgen_suspend_finalizers = TRUE;
2717 }
2718 
2719 int
sgen_gc_invoke_finalizers(void)2720 sgen_gc_invoke_finalizers (void)
2721 {
2722 	int count = 0;
2723 
2724 	g_assert (!pending_unqueued_finalizer);
2725 
2726 	/* FIXME: batch to reduce lock contention */
2727 	while (sgen_have_pending_finalizers ()) {
2728 		GCObject *obj;
2729 
2730 		LOCK_GC;
2731 
2732 		/*
2733 		 * We need to set `pending_unqueued_finalizer` before dequeing the
2734 		 * finalizable object.
2735 		 */
2736 		if (!sgen_pointer_queue_is_empty (&fin_ready_queue)) {
2737 			pending_unqueued_finalizer = TRUE;
2738 			mono_memory_write_barrier ();
2739 			obj = (GCObject *)sgen_pointer_queue_pop (&fin_ready_queue);
2740 		} else if (!sgen_pointer_queue_is_empty (&critical_fin_queue)) {
2741 			pending_unqueued_finalizer = TRUE;
2742 			mono_memory_write_barrier ();
2743 			obj = (GCObject *)sgen_pointer_queue_pop (&critical_fin_queue);
2744 		} else {
2745 			obj = NULL;
2746 		}
2747 
2748 		if (obj)
2749 			SGEN_LOG (7, "Finalizing object %p (%s)", obj, sgen_client_vtable_get_name (SGEN_LOAD_VTABLE (obj)));
2750 
2751 		UNLOCK_GC;
2752 
2753 		if (!obj)
2754 			break;
2755 
2756 		count++;
2757 		/* the object is on the stack so it is pinned */
2758 		/*g_print ("Calling finalizer for object: %p (%s)\n", obj, sgen_client_object_safe_name (obj));*/
2759 		sgen_client_run_finalize (obj);
2760 	}
2761 
2762 	if (pending_unqueued_finalizer) {
2763 		mono_memory_write_barrier ();
2764 		pending_unqueued_finalizer = FALSE;
2765 	}
2766 
2767 	return count;
2768 }
2769 
2770 gboolean
sgen_have_pending_finalizers(void)2771 sgen_have_pending_finalizers (void)
2772 {
2773 	if (sgen_suspend_finalizers)
2774 		return FALSE;
2775 	return pending_unqueued_finalizer || !sgen_pointer_queue_is_empty (&fin_ready_queue) || !sgen_pointer_queue_is_empty (&critical_fin_queue);
2776 }
2777 
2778 /*
2779  * ######################################################################
2780  * ########  registered roots support
2781  * ######################################################################
2782  */
2783 
2784 /*
2785  * We do not coalesce roots.
2786  */
2787 int
sgen_register_root(char * start,size_t size,SgenDescriptor descr,int root_type,int source,const char * msg)2788 sgen_register_root (char *start, size_t size, SgenDescriptor descr, int root_type, int source, const char *msg)
2789 {
2790 	RootRecord new_root;
2791 	int i;
2792 	LOCK_GC;
2793 	for (i = 0; i < ROOT_TYPE_NUM; ++i) {
2794 		RootRecord *root = (RootRecord *)sgen_hash_table_lookup (&roots_hash [i], start);
2795 		/* we allow changing the size and the descriptor (for thread statics etc) */
2796 		if (root) {
2797 			size_t old_size = root->end_root - start;
2798 			root->end_root = start + size;
2799 			SGEN_ASSERT (0, !!root->root_desc == !!descr, "Can't change whether a root is precise or conservative.");
2800 			SGEN_ASSERT (0, root->source == source, "Can't change a root's source identifier.");
2801 			SGEN_ASSERT (0, !!root->msg == !!msg, "Can't change a root's message.");
2802 			root->root_desc = descr;
2803 			roots_size += size;
2804 			roots_size -= old_size;
2805 			UNLOCK_GC;
2806 			return TRUE;
2807 		}
2808 	}
2809 
2810 	new_root.end_root = start + size;
2811 	new_root.root_desc = descr;
2812 	new_root.source = source;
2813 	new_root.msg = msg;
2814 
2815 	sgen_hash_table_replace (&roots_hash [root_type], start, &new_root, NULL);
2816 	roots_size += size;
2817 
2818 	SGEN_LOG (3, "Added root for range: %p-%p, descr: %llx  (%d/%d bytes)", start, new_root.end_root, (long long)descr, (int)size, (int)roots_size);
2819 
2820 	UNLOCK_GC;
2821 	return TRUE;
2822 }
2823 
2824 void
sgen_deregister_root(char * addr)2825 sgen_deregister_root (char* addr)
2826 {
2827 	int root_type;
2828 	RootRecord root;
2829 
2830 	LOCK_GC;
2831 	for (root_type = 0; root_type < ROOT_TYPE_NUM; ++root_type) {
2832 		if (sgen_hash_table_remove (&roots_hash [root_type], addr, &root))
2833 			roots_size -= (root.end_root - addr);
2834 	}
2835 	UNLOCK_GC;
2836 }
2837 
2838 void
sgen_wbroots_iterate_live_block_ranges(sgen_cardtable_block_callback cb)2839 sgen_wbroots_iterate_live_block_ranges (sgen_cardtable_block_callback cb)
2840 {
2841 	void **start_root;
2842 	RootRecord *root;
2843 	SGEN_HASH_TABLE_FOREACH (&roots_hash [ROOT_TYPE_WBARRIER], void **, start_root, RootRecord *, root) {
2844 		cb ((mword)start_root, (mword)root->end_root - (mword)start_root);
2845 	} SGEN_HASH_TABLE_FOREACH_END;
2846 }
2847 
2848 /* Root equivalent of sgen_client_cardtable_scan_object */
2849 static void
sgen_wbroot_scan_card_table(void ** start_root,mword size,ScanCopyContext ctx)2850 sgen_wbroot_scan_card_table (void** start_root, mword size,  ScanCopyContext ctx)
2851 {
2852 	ScanPtrFieldFunc scan_field_func = ctx.ops->scan_ptr_field;
2853 	guint8 *card_data = sgen_card_table_get_card_scan_address ((mword)start_root);
2854 	guint8 *card_base = card_data;
2855 	mword card_count = sgen_card_table_number_of_cards_in_range ((mword)start_root, size);
2856 	guint8 *card_data_end = card_data + card_count;
2857 	mword extra_idx = 0;
2858 	char *obj_start = sgen_card_table_align_pointer (start_root);
2859 	char *obj_end = (char*)start_root + size;
2860 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
2861 	guint8 *overflow_scan_end = NULL;
2862 #endif
2863 
2864 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
2865 	/*Check for overflow and if so, setup to scan in two steps*/
2866 	if (card_data_end >= SGEN_SHADOW_CARDTABLE_END) {
2867 		overflow_scan_end = sgen_shadow_cardtable + (card_data_end - SGEN_SHADOW_CARDTABLE_END);
2868 		card_data_end = SGEN_SHADOW_CARDTABLE_END;
2869 	}
2870 
2871 LOOP_HEAD:
2872 #endif
2873 
2874 	card_data = sgen_find_next_card (card_data, card_data_end);
2875 
2876 	for (; card_data < card_data_end; card_data = sgen_find_next_card (card_data + 1, card_data_end)) {
2877 		size_t idx = (card_data - card_base) + extra_idx;
2878 		char *start = (char*)(obj_start + idx * CARD_SIZE_IN_BYTES);
2879 		char *card_end = start + CARD_SIZE_IN_BYTES;
2880 		char *elem = start, *first_elem = start;
2881 
2882 		/*
2883 		 * Don't clean first and last card on 32bit systems since they
2884 		 * may also be part from other roots.
2885 		 */
2886 		if (card_data != card_base && card_data != (card_data_end - 1))
2887 			sgen_card_table_prepare_card_for_scanning (card_data);
2888 
2889 		card_end = MIN (card_end, obj_end);
2890 
2891 		if (elem < (char*)start_root)
2892 			first_elem = elem = (char*)start_root;
2893 
2894 		for (; elem < card_end; elem += SIZEOF_VOID_P) {
2895 			if (*(GCObject**)elem)
2896 				scan_field_func (NULL, (GCObject**)elem, ctx.queue);
2897 		}
2898 
2899 		binary_protocol_card_scan (first_elem, elem - first_elem);
2900 	}
2901 
2902 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
2903 	if (overflow_scan_end) {
2904 		extra_idx = card_data - card_base;
2905 		card_base = card_data = sgen_shadow_cardtable;
2906 		card_data_end = overflow_scan_end;
2907 		overflow_scan_end = NULL;
2908 		goto LOOP_HEAD;
2909 	}
2910 #endif
2911 }
2912 
2913 void
sgen_wbroots_scan_card_table(ScanCopyContext ctx)2914 sgen_wbroots_scan_card_table (ScanCopyContext ctx)
2915 {
2916 	void **start_root;
2917 	RootRecord *root;
2918 
2919 	SGEN_HASH_TABLE_FOREACH (&roots_hash [ROOT_TYPE_WBARRIER], void **, start_root, RootRecord *, root) {
2920 		SGEN_ASSERT (0, (root->root_desc & ROOT_DESC_TYPE_MASK) == ROOT_DESC_VECTOR, "Unsupported root type");
2921 
2922 		sgen_wbroot_scan_card_table (start_root, (mword)root->end_root - (mword)start_root, ctx);
2923 	} SGEN_HASH_TABLE_FOREACH_END;
2924 }
2925 
2926 /*
2927  * ######################################################################
2928  * ########  Thread handling (stop/start code)
2929  * ######################################################################
2930  */
2931 
2932 int
sgen_get_current_collection_generation(void)2933 sgen_get_current_collection_generation (void)
2934 {
2935 	return current_collection_generation;
2936 }
2937 
2938 void*
sgen_thread_attach(SgenThreadInfo * info)2939 sgen_thread_attach (SgenThreadInfo* info)
2940 {
2941 	info->tlab_start = info->tlab_next = info->tlab_temp_end = info->tlab_real_end = NULL;
2942 
2943 	sgen_client_thread_attach (info);
2944 
2945 	return info;
2946 }
2947 
2948 void
sgen_thread_detach_with_lock(SgenThreadInfo * p)2949 sgen_thread_detach_with_lock (SgenThreadInfo *p)
2950 {
2951 	sgen_client_thread_detach_with_lock (p);
2952 }
2953 
2954 /*
2955  * ######################################################################
2956  * ########  Write barriers
2957  * ######################################################################
2958  */
2959 
2960 /*
2961  * Note: the write barriers first do the needed GC work and then do the actual store:
2962  * this way the value is visible to the conservative GC scan after the write barrier
2963  * itself. If a GC interrupts the barrier in the middle, value will be kept alive by
2964  * the conservative scan, otherwise by the remembered set scan.
2965  */
2966 
2967 /**
2968  * mono_gc_wbarrier_arrayref_copy:
2969  */
2970 void
mono_gc_wbarrier_arrayref_copy(gpointer dest_ptr,gpointer src_ptr,int count)2971 mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
2972 {
2973 	HEAVY_STAT (++stat_wbarrier_arrayref_copy);
2974 	/*This check can be done without taking a lock since dest_ptr array is pinned*/
2975 	if (ptr_in_nursery (dest_ptr) || count <= 0) {
2976 		mono_gc_memmove_aligned (dest_ptr, src_ptr, count * sizeof (gpointer));
2977 		return;
2978 	}
2979 
2980 #ifdef SGEN_HEAVY_BINARY_PROTOCOL
2981 	if (binary_protocol_is_heavy_enabled ()) {
2982 		int i;
2983 		for (i = 0; i < count; ++i) {
2984 			gpointer dest = (gpointer*)dest_ptr + i;
2985 			gpointer obj = *((gpointer*)src_ptr + i);
2986 			if (obj)
2987 				binary_protocol_wbarrier (dest, obj, (gpointer)LOAD_VTABLE (obj));
2988 		}
2989 	}
2990 #endif
2991 
2992 	remset.wbarrier_arrayref_copy (dest_ptr, src_ptr, count);
2993 }
2994 
2995 /**
2996  * mono_gc_wbarrier_generic_nostore:
2997  */
2998 void
mono_gc_wbarrier_generic_nostore(gpointer ptr)2999 mono_gc_wbarrier_generic_nostore (gpointer ptr)
3000 {
3001 	gpointer obj;
3002 
3003 	HEAVY_STAT (++stat_wbarrier_generic_store);
3004 
3005 	sgen_client_wbarrier_generic_nostore_check (ptr);
3006 
3007 	obj = *(gpointer*)ptr;
3008 	if (obj)
3009 		binary_protocol_wbarrier (ptr, obj, (gpointer)LOAD_VTABLE (obj));
3010 
3011 	/*
3012 	 * We need to record old->old pointer locations for the
3013 	 * concurrent collector.
3014 	 */
3015 	if (!ptr_in_nursery (obj) && !concurrent_collection_in_progress) {
3016 		SGEN_LOG (8, "Skipping remset at %p", ptr);
3017 		return;
3018 	}
3019 
3020 	SGEN_LOG (8, "Adding remset at %p", ptr);
3021 
3022 	remset.wbarrier_generic_nostore (ptr);
3023 }
3024 
3025 /**
3026  * mono_gc_wbarrier_generic_store:
3027  */
3028 void
mono_gc_wbarrier_generic_store(gpointer ptr,GCObject * value)3029 mono_gc_wbarrier_generic_store (gpointer ptr, GCObject* value)
3030 {
3031 	SGEN_LOG (8, "Wbarrier store at %p to %p (%s)", ptr, value, value ? sgen_client_vtable_get_name (SGEN_LOAD_VTABLE (value)) : "null");
3032 	SGEN_UPDATE_REFERENCE_ALLOW_NULL (ptr, value);
3033 	if (ptr_in_nursery (value) || concurrent_collection_in_progress)
3034 		mono_gc_wbarrier_generic_nostore (ptr);
3035 	sgen_dummy_use (value);
3036 }
3037 
3038 /**
3039  * mono_gc_wbarrier_generic_store_atomic:
3040  * Same as \c mono_gc_wbarrier_generic_store but performs the store
3041  * as an atomic operation with release semantics.
3042  */
3043 void
mono_gc_wbarrier_generic_store_atomic(gpointer ptr,GCObject * value)3044 mono_gc_wbarrier_generic_store_atomic (gpointer ptr, GCObject *value)
3045 {
3046 	HEAVY_STAT (++stat_wbarrier_generic_store_atomic);
3047 
3048 	SGEN_LOG (8, "Wbarrier atomic store at %p to %p (%s)", ptr, value, value ? sgen_client_vtable_get_name (SGEN_LOAD_VTABLE (value)) : "null");
3049 
3050 	mono_atomic_store_ptr ((volatile gpointer *)ptr, value);
3051 
3052 	if (ptr_in_nursery (value) || concurrent_collection_in_progress)
3053 		mono_gc_wbarrier_generic_nostore (ptr);
3054 
3055 	sgen_dummy_use (value);
3056 }
3057 
3058 void
sgen_wbarrier_range_copy(gpointer _dest,gpointer _src,int size)3059 sgen_wbarrier_range_copy (gpointer _dest, gpointer _src, int size)
3060 {
3061 	remset.wbarrier_range_copy (_dest,_src, size);
3062 }
3063 
3064 /*
3065  * ######################################################################
3066  * ########  Other mono public interface functions.
3067  * ######################################################################
3068  */
3069 
3070 void
sgen_gc_collect(int generation)3071 sgen_gc_collect (int generation)
3072 {
3073 	LOCK_GC;
3074 	if (generation > 1)
3075 		generation = 1;
3076 	sgen_perform_collection (0, generation, "user request", TRUE, TRUE);
3077 	/* Make sure we don't exceed heap size allowance by promoting */
3078 	if (generation == GENERATION_NURSERY && sgen_need_major_collection (0))
3079 		sgen_perform_collection (0, GENERATION_OLD, "Minor allowance", FALSE, TRUE);
3080 	UNLOCK_GC;
3081 }
3082 
3083 int
sgen_gc_collection_count(int generation)3084 sgen_gc_collection_count (int generation)
3085 {
3086 	return mono_atomic_load_i32 (generation == GENERATION_NURSERY ? &gc_stats.minor_gc_count : &gc_stats.major_gc_count);
3087 }
3088 
3089 size_t
sgen_gc_get_used_size(void)3090 sgen_gc_get_used_size (void)
3091 {
3092 	gint64 tot = 0;
3093 	LOCK_GC;
3094 	tot = los_memory_usage;
3095 	tot += nursery_section->end_data - nursery_section->data;
3096 	tot += major_collector.get_used_size ();
3097 	/* FIXME: account for pinned objects */
3098 	UNLOCK_GC;
3099 	return tot;
3100 }
3101 
3102 void
sgen_env_var_error(const char * env_var,const char * fallback,const char * description_format,...)3103 sgen_env_var_error (const char *env_var, const char *fallback, const char *description_format, ...)
3104 {
3105 	va_list ap;
3106 
3107 	va_start (ap, description_format);
3108 
3109 	fprintf (stderr, "Warning: In environment variable `%s': ", env_var);
3110 	vfprintf (stderr, description_format, ap);
3111 	if (fallback)
3112 		fprintf (stderr, " - %s", fallback);
3113 	fprintf (stderr, "\n");
3114 
3115 	va_end (ap);
3116 }
3117 
3118 static gboolean
parse_double_in_interval(const char * env_var,const char * opt_name,const char * opt,double min,double max,double * result)3119 parse_double_in_interval (const char *env_var, const char *opt_name, const char *opt, double min, double max, double *result)
3120 {
3121 	char *endptr;
3122 	double val = strtod (opt, &endptr);
3123 	if (endptr == opt) {
3124 		sgen_env_var_error (env_var, "Using default value.", "`%s` must be a number.", opt_name);
3125 		return FALSE;
3126 	}
3127 	else if (val < min || val > max) {
3128 		sgen_env_var_error (env_var, "Using default value.", "`%s` must be between %.2f - %.2f.", opt_name, min, max);
3129 		return FALSE;
3130 	}
3131 	*result = val;
3132 	return TRUE;
3133 }
3134 
3135 static SgenMinor
parse_sgen_minor(const char * opt)3136 parse_sgen_minor (const char *opt)
3137 {
3138 	if (!opt)
3139 		return SGEN_MINOR_DEFAULT;
3140 
3141 	if (!strcmp (opt, "simple")) {
3142 		return SGEN_MINOR_SIMPLE;
3143 	} else if (!strcmp (opt, "simple-par")) {
3144 		return SGEN_MINOR_SIMPLE_PARALLEL;
3145 	} else if (!strcmp (opt, "split")) {
3146 		return SGEN_MINOR_SPLIT;
3147 	} else {
3148 		sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using default instead.", "Unknown minor collector `%s'.", opt);
3149 		return SGEN_MINOR_DEFAULT;
3150 	}
3151 }
3152 
3153 static SgenMajor
parse_sgen_major(const char * opt)3154 parse_sgen_major (const char *opt)
3155 {
3156 	if (!opt)
3157 		return SGEN_MAJOR_DEFAULT;
3158 
3159 	if (!strcmp (opt, "marksweep")) {
3160 		return SGEN_MAJOR_SERIAL;
3161 	} else if (!strcmp (opt, "marksweep-conc")) {
3162 		return SGEN_MAJOR_CONCURRENT;
3163 	} else if (!strcmp (opt, "marksweep-conc-par")) {
3164 		return SGEN_MAJOR_CONCURRENT_PARALLEL;
3165 	} else {
3166 		sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using default instead.", "Unknown major collector `%s'.", opt);
3167 		return SGEN_MAJOR_DEFAULT;
3168 	}
3169 
3170 }
3171 
3172 static SgenMode
parse_sgen_mode(const char * opt)3173 parse_sgen_mode (const char *opt)
3174 {
3175 	if (!opt)
3176 		return SGEN_MODE_NONE;
3177 
3178 	if (!strcmp (opt, "balanced")) {
3179 		return SGEN_MODE_BALANCED;
3180 	} else if (!strcmp (opt, "throughput")) {
3181 		return SGEN_MODE_THROUGHPUT;
3182 	} else if (!strcmp (opt, "pause") || g_str_has_prefix (opt, "pause:")) {
3183 		return SGEN_MODE_PAUSE;
3184 	} else {
3185 		sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using default configurations.", "Unknown mode `%s'.", opt);
3186 		return SGEN_MODE_NONE;
3187 	}
3188 }
3189 
3190 static void
init_sgen_minor(SgenMinor minor)3191 init_sgen_minor (SgenMinor minor)
3192 {
3193 	switch (minor) {
3194 	case SGEN_MINOR_DEFAULT:
3195 	case SGEN_MINOR_SIMPLE:
3196 		sgen_simple_nursery_init (&sgen_minor_collector, FALSE);
3197 		break;
3198 	case SGEN_MINOR_SIMPLE_PARALLEL:
3199 		sgen_simple_nursery_init (&sgen_minor_collector, TRUE);
3200 		break;
3201 	case SGEN_MINOR_SPLIT:
3202 		sgen_split_nursery_init (&sgen_minor_collector);
3203 		break;
3204 	default:
3205 		g_assert_not_reached ();
3206 	}
3207 }
3208 
3209 static void
init_sgen_major(SgenMajor major)3210 init_sgen_major (SgenMajor major)
3211 {
3212 	if (major == SGEN_MAJOR_DEFAULT)
3213 		major = DEFAULT_MAJOR;
3214 
3215 	switch (major) {
3216 	case SGEN_MAJOR_SERIAL:
3217 		sgen_marksweep_init (&major_collector);
3218 		break;
3219 	case SGEN_MAJOR_CONCURRENT:
3220 		sgen_marksweep_conc_init (&major_collector);
3221 		break;
3222 	case SGEN_MAJOR_CONCURRENT_PARALLEL:
3223 		sgen_marksweep_conc_par_init (&major_collector);
3224 		break;
3225 	default:
3226 		g_assert_not_reached ();
3227 	}
3228 }
3229 
3230 /*
3231  * If sgen mode is set, major/minor configuration is fixed. The other gc_params
3232  * are parsed and processed after major/minor initialization, so it can potentially
3233  * override some knobs set by the sgen mode. We can consider locking out additional
3234  * configurations when gc_modes are used.
3235  */
3236 static void
init_sgen_mode(SgenMode mode)3237 init_sgen_mode (SgenMode mode)
3238 {
3239 	SgenMinor minor = SGEN_MINOR_DEFAULT;
3240 	SgenMajor major = SGEN_MAJOR_DEFAULT;
3241 
3242 	switch (mode) {
3243 	case SGEN_MODE_BALANCED:
3244 		/*
3245 		 * Use a dynamic parallel nursery with a major concurrent collector.
3246 		 * This uses the default values for max pause time and nursery size.
3247 		 */
3248 		minor = SGEN_MINOR_SIMPLE;
3249 		major = SGEN_MAJOR_CONCURRENT;
3250 		dynamic_nursery = TRUE;
3251 		break;
3252 	case SGEN_MODE_THROUGHPUT:
3253 		/*
3254 		 * Use concurrent major to let the mutator do more work. Use a larger
3255 		 * nursery, without pause time constraints, in order to collect more
3256 		 * objects in parallel and avoid repetitive collection tasks (pinning,
3257 		 * root scanning etc)
3258 		 */
3259 		minor = SGEN_MINOR_SIMPLE_PARALLEL;
3260 		major = SGEN_MAJOR_CONCURRENT;
3261 		dynamic_nursery = TRUE;
3262 		sgen_max_pause_time = 0;
3263 		break;
3264 	case SGEN_MODE_PAUSE:
3265 		/*
3266 		 * Use concurrent major and dynamic nursery with a more
3267 		 * aggressive shrinking relative to pause times.
3268 		 */
3269 		minor = SGEN_MINOR_SIMPLE_PARALLEL;
3270 		major = SGEN_MAJOR_CONCURRENT;
3271 		dynamic_nursery = TRUE;
3272 		sgen_max_pause_margin = SGEN_PAUSE_MODE_MAX_PAUSE_MARGIN;
3273 		break;
3274 	default:
3275 		g_assert_not_reached ();
3276 	}
3277 
3278 	init_sgen_minor (minor);
3279 	init_sgen_major (major);
3280 }
3281 
3282 void
sgen_gc_init(void)3283 sgen_gc_init (void)
3284 {
3285 	char *env;
3286 	char **opts, **ptr;
3287 	SgenMajor sgen_major = SGEN_MAJOR_DEFAULT;
3288 	SgenMinor sgen_minor = SGEN_MINOR_DEFAULT;
3289 	SgenMode sgen_mode = SGEN_MODE_NONE;
3290 	char *params_opts = NULL;
3291 	char *debug_opts = NULL;
3292 	size_t max_heap = 0;
3293 	size_t soft_limit = 0;
3294 	int result;
3295 	gboolean debug_print_allowance = FALSE;
3296 	double allowance_ratio = 0, save_target = 0;
3297 	gboolean cement_enabled = TRUE;
3298 
3299 	do {
3300 		result = mono_atomic_cas_i32 (&gc_initialized, -1, 0);
3301 		switch (result) {
3302 		case 1:
3303 			/* already inited */
3304 			return;
3305 		case -1:
3306 			/* being inited by another thread */
3307 			mono_thread_info_usleep (1000);
3308 			break;
3309 		case 0:
3310 			/* we will init it */
3311 			break;
3312 		default:
3313 			g_assert_not_reached ();
3314 		}
3315 	} while (result != 0);
3316 
3317 	SGEN_TV_GETTIME (sgen_init_timestamp);
3318 
3319 #ifdef SGEN_WITHOUT_MONO
3320 	mono_thread_smr_init ();
3321 #endif
3322 
3323 	mono_coop_mutex_init (&gc_mutex);
3324 
3325 	gc_debug_file = stderr;
3326 
3327 	mono_coop_mutex_init (&sgen_interruption_mutex);
3328 
3329 	if ((env = g_getenv (MONO_GC_PARAMS_NAME)) || gc_params_options) {
3330 		params_opts = g_strdup_printf ("%s,%s", gc_params_options ? gc_params_options : "", env ? env : "");
3331 		g_free (env);
3332 	}
3333 
3334 	if (params_opts) {
3335 		opts = g_strsplit (params_opts, ",", -1);
3336 		for (ptr = opts; *ptr; ++ptr) {
3337 			char *opt = *ptr;
3338 			if (g_str_has_prefix (opt, "major=")) {
3339 				opt = strchr (opt, '=') + 1;
3340 				sgen_major = parse_sgen_major (opt);
3341 			} else if (g_str_has_prefix (opt, "minor=")) {
3342 				opt = strchr (opt, '=') + 1;
3343 				sgen_minor = parse_sgen_minor (opt);
3344 			} else if (g_str_has_prefix (opt, "mode=")) {
3345 				opt = strchr (opt, '=') + 1;
3346 				sgen_mode = parse_sgen_mode (opt);
3347 			}
3348 		}
3349 	} else {
3350 		opts = NULL;
3351 	}
3352 
3353 	init_stats ();
3354 	sgen_init_internal_allocator ();
3355 	sgen_init_nursery_allocator ();
3356 	sgen_init_fin_weak_hash ();
3357 	sgen_init_hash_table ();
3358 	sgen_init_descriptors ();
3359 	sgen_init_gray_queues ();
3360 	sgen_init_allocator ();
3361 	sgen_init_gchandles ();
3362 
3363 	sgen_register_fixed_internal_mem_type (INTERNAL_MEM_SECTION, SGEN_SIZEOF_GC_MEM_SECTION);
3364 	sgen_register_fixed_internal_mem_type (INTERNAL_MEM_GRAY_QUEUE, sizeof (GrayQueueSection));
3365 
3366 	sgen_client_init ();
3367 
3368 	if (sgen_mode != SGEN_MODE_NONE) {
3369 		if (sgen_minor != SGEN_MINOR_DEFAULT || sgen_major != SGEN_MAJOR_DEFAULT)
3370 			sgen_env_var_error (MONO_GC_PARAMS_NAME, "Ignoring major/minor configuration", "Major/minor configurations cannot be used with sgen modes");
3371 		init_sgen_mode (sgen_mode);
3372 	} else {
3373 		init_sgen_minor (sgen_minor);
3374 		init_sgen_major (sgen_major);
3375 	}
3376 
3377 	if (opts) {
3378 		gboolean usage_printed = FALSE;
3379 
3380 		for (ptr = opts; *ptr; ++ptr) {
3381 			char *opt = *ptr;
3382 			if (!strcmp (opt, ""))
3383 				continue;
3384 			if (g_str_has_prefix (opt, "major="))
3385 				continue;
3386 			if (g_str_has_prefix (opt, "minor="))
3387 				continue;
3388 			if (g_str_has_prefix (opt, "mode=")) {
3389 				if (g_str_has_prefix (opt, "mode=pause:")) {
3390 					char *str_pause = strchr (opt, ':') + 1;
3391 					int pause = atoi (str_pause);
3392 					if (pause)
3393 						sgen_max_pause_time = pause;
3394 					else
3395 						sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using default", "Invalid maximum pause time for `pause` sgen mode");
3396 				}
3397 				continue;
3398 			}
3399 			if (g_str_has_prefix (opt, "max-heap-size=")) {
3400 				size_t page_size = mono_pagesize ();
3401 				size_t max_heap_candidate = 0;
3402 				opt = strchr (opt, '=') + 1;
3403 				if (*opt && mono_gc_parse_environment_string_extract_number (opt, &max_heap_candidate)) {
3404 					max_heap = (max_heap_candidate + page_size - 1) & ~(size_t)(page_size - 1);
3405 					if (max_heap != max_heap_candidate)
3406 						sgen_env_var_error (MONO_GC_PARAMS_NAME, "Rounding up.", "`max-heap-size` size must be a multiple of %d.", page_size);
3407 				} else {
3408 					sgen_env_var_error (MONO_GC_PARAMS_NAME, NULL, "`max-heap-size` must be an integer.");
3409 				}
3410 				continue;
3411 			}
3412 			if (g_str_has_prefix (opt, "soft-heap-limit=")) {
3413 				opt = strchr (opt, '=') + 1;
3414 				if (*opt && mono_gc_parse_environment_string_extract_number (opt, &soft_limit)) {
3415 					if (soft_limit <= 0) {
3416 						sgen_env_var_error (MONO_GC_PARAMS_NAME, NULL, "`soft-heap-limit` must be positive.");
3417 						soft_limit = 0;
3418 					}
3419 				} else {
3420 					sgen_env_var_error (MONO_GC_PARAMS_NAME, NULL, "`soft-heap-limit` must be an integer.");
3421 				}
3422 				continue;
3423 			}
3424 			if (g_str_has_prefix (opt, "nursery-size=")) {
3425 				size_t val;
3426 				opt = strchr (opt, '=') + 1;
3427 				if (*opt && mono_gc_parse_environment_string_extract_number (opt, &val)) {
3428 					if ((val & (val - 1))) {
3429 						sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using default value.", "`nursery-size` must be a power of two.");
3430 						continue;
3431 					}
3432 
3433 					if (val < SGEN_MAX_NURSERY_WASTE) {
3434 						sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using default value.",
3435 								"`nursery-size` must be at least %d bytes.", SGEN_MAX_NURSERY_WASTE);
3436 						continue;
3437 					}
3438 
3439 					min_nursery_size = max_nursery_size = val;
3440 					dynamic_nursery = FALSE;
3441 				} else {
3442 					sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using default value.", "`nursery-size` must be an integer.");
3443 					continue;
3444 				}
3445 				continue;
3446 			}
3447 			if (g_str_has_prefix (opt, "save-target-ratio=")) {
3448 				double val;
3449 				opt = strchr (opt, '=') + 1;
3450 				if (parse_double_in_interval (MONO_GC_PARAMS_NAME, "save-target-ratio", opt,
3451 						SGEN_MIN_SAVE_TARGET_RATIO, SGEN_MAX_SAVE_TARGET_RATIO, &val)) {
3452 					save_target = val;
3453 				}
3454 				continue;
3455 			}
3456 			if (g_str_has_prefix (opt, "default-allowance-ratio=")) {
3457 				double val;
3458 				opt = strchr (opt, '=') + 1;
3459 				if (parse_double_in_interval (MONO_GC_PARAMS_NAME, "default-allowance-ratio", opt,
3460 						SGEN_MIN_ALLOWANCE_NURSERY_SIZE_RATIO, SGEN_MAX_ALLOWANCE_NURSERY_SIZE_RATIO, &val)) {
3461 					allowance_ratio = val;
3462 				}
3463 				continue;
3464 			}
3465 
3466 			if (!strcmp (opt, "cementing")) {
3467 				cement_enabled = TRUE;
3468 				continue;
3469 			}
3470 			if (!strcmp (opt, "no-cementing")) {
3471 				cement_enabled = FALSE;
3472 				continue;
3473 			}
3474 
3475 			if (!strcmp (opt, "precleaning")) {
3476 				precleaning_enabled = TRUE;
3477 				continue;
3478 			}
3479 			if (!strcmp (opt, "no-precleaning")) {
3480 				precleaning_enabled = FALSE;
3481 				continue;
3482 			}
3483 
3484 			if (!strcmp (opt, "dynamic-nursery")) {
3485 				if (sgen_minor_collector.is_split)
3486 					sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using default value.",
3487 							"dynamic-nursery not supported with split-nursery.");
3488 				else
3489 					dynamic_nursery = TRUE;
3490 				continue;
3491 			}
3492 			if (!strcmp (opt, "no-dynamic-nursery")) {
3493 				dynamic_nursery = FALSE;
3494 				continue;
3495 			}
3496 
3497 			if (major_collector.handle_gc_param && major_collector.handle_gc_param (opt))
3498 				continue;
3499 
3500 			if (sgen_minor_collector.handle_gc_param && sgen_minor_collector.handle_gc_param (opt))
3501 				continue;
3502 
3503 			if (sgen_client_handle_gc_param (opt))
3504 				continue;
3505 
3506 			sgen_env_var_error (MONO_GC_PARAMS_NAME, "Ignoring.", "Unknown option `%s`.", opt);
3507 
3508 			if (usage_printed)
3509 				continue;
3510 
3511 			fprintf (stderr, "\n%s must be a comma-delimited list of one or more of the following:\n", MONO_GC_PARAMS_NAME);
3512 			fprintf (stderr, "  max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
3513 			fprintf (stderr, "  soft-heap-limit=n (where N is an integer, possibly with a k, m or a g suffix)\n");
3514 			fprintf (stderr, "  mode=MODE (where MODE is 'balanced', 'throughput' or 'pause[:N]' and N is maximum pause in milliseconds)\n");
3515 			fprintf (stderr, "  nursery-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
3516 			fprintf (stderr, "  major=COLLECTOR (where COLLECTOR is `marksweep', `marksweep-conc', `marksweep-par')\n");
3517 			fprintf (stderr, "  minor=COLLECTOR (where COLLECTOR is `simple' or `split')\n");
3518 			fprintf (stderr, "  wbarrier=WBARRIER (where WBARRIER is `remset' or `cardtable')\n");
3519 			fprintf (stderr, "  [no-]cementing\n");
3520 			fprintf (stderr, "  [no-]dynamic-nursery\n");
3521 			if (major_collector.print_gc_param_usage)
3522 				major_collector.print_gc_param_usage ();
3523 			if (sgen_minor_collector.print_gc_param_usage)
3524 				sgen_minor_collector.print_gc_param_usage ();
3525 			sgen_client_print_gc_params_usage ();
3526 			fprintf (stderr, " Experimental options:\n");
3527 			fprintf (stderr, "  save-target-ratio=R (where R must be between %.2f - %.2f).\n", SGEN_MIN_SAVE_TARGET_RATIO, SGEN_MAX_SAVE_TARGET_RATIO);
3528 			fprintf (stderr, "  default-allowance-ratio=R (where R must be between %.2f - %.2f).\n", SGEN_MIN_ALLOWANCE_NURSERY_SIZE_RATIO, SGEN_MAX_ALLOWANCE_NURSERY_SIZE_RATIO);
3529 			fprintf (stderr, "\n");
3530 
3531 			usage_printed = TRUE;
3532 		}
3533 		g_strfreev (opts);
3534 	}
3535 
3536 	if (params_opts)
3537 		g_free (params_opts);
3538 
3539 	alloc_nursery (dynamic_nursery, min_nursery_size, max_nursery_size);
3540 
3541 	sgen_pinning_init ();
3542 	sgen_cement_init (cement_enabled);
3543 
3544 	if ((env = g_getenv (MONO_GC_DEBUG_NAME)) || gc_debug_options) {
3545 		debug_opts = g_strdup_printf ("%s,%s", gc_debug_options ? gc_debug_options  : "", env ? env : "");
3546 		g_free (env);
3547 	}
3548 
3549 	if (debug_opts) {
3550 		gboolean usage_printed = FALSE;
3551 
3552 		opts = g_strsplit (debug_opts, ",", -1);
3553 		for (ptr = opts; ptr && *ptr; ptr ++) {
3554 			char *opt = *ptr;
3555 			if (!strcmp (opt, ""))
3556 				continue;
3557 			if (opt [0] >= '0' && opt [0] <= '9') {
3558 				gc_debug_level = atoi (opt);
3559 				opt++;
3560 				if (opt [0] == ':')
3561 					opt++;
3562 				if (opt [0]) {
3563 					char *rf = g_strdup_printf ("%s.%d", opt, mono_process_current_pid ());
3564 					gc_debug_file = fopen (rf, "wb");
3565 					if (!gc_debug_file)
3566 						gc_debug_file = stderr;
3567 					g_free (rf);
3568 				}
3569 			} else if (!strcmp (opt, "print-allowance")) {
3570 				debug_print_allowance = TRUE;
3571 			} else if (!strcmp (opt, "print-pinning")) {
3572 				sgen_pin_stats_enable ();
3573 			} else if (!strcmp (opt, "verify-before-allocs")) {
3574 				verify_before_allocs = 1;
3575 				has_per_allocation_action = TRUE;
3576 			} else if (g_str_has_prefix (opt, "max-valloc-size=")) {
3577 				size_t max_valloc_size;
3578 				char *arg = strchr (opt, '=') + 1;
3579 				if (*opt && mono_gc_parse_environment_string_extract_number (arg, &max_valloc_size)) {
3580 					mono_valloc_set_limit (max_valloc_size);
3581 				} else {
3582 					sgen_env_var_error (MONO_GC_DEBUG_NAME, NULL, "`max-valloc-size` must be an integer.");
3583 				}
3584 				continue;
3585 			} else if (g_str_has_prefix (opt, "verify-before-allocs=")) {
3586 				char *arg = strchr (opt, '=') + 1;
3587 				verify_before_allocs = atoi (arg);
3588 				has_per_allocation_action = TRUE;
3589 			} else if (!strcmp (opt, "collect-before-allocs")) {
3590 				collect_before_allocs = 1;
3591 				has_per_allocation_action = TRUE;
3592 			} else if (g_str_has_prefix (opt, "collect-before-allocs=")) {
3593 				char *arg = strchr (opt, '=') + 1;
3594 				has_per_allocation_action = TRUE;
3595 				collect_before_allocs = atoi (arg);
3596 			} else if (!strcmp (opt, "verify-before-collections")) {
3597 				whole_heap_check_before_collection = TRUE;
3598 			} else if (!strcmp (opt, "check-remset-consistency")) {
3599 				remset_consistency_checks = TRUE;
3600 				nursery_clear_policy = CLEAR_AT_GC;
3601 			} else if (!strcmp (opt, "mod-union-consistency-check")) {
3602 				if (!major_collector.is_concurrent) {
3603 					sgen_env_var_error (MONO_GC_DEBUG_NAME, "Ignoring.", "`mod-union-consistency-check` only works with concurrent major collector.");
3604 					continue;
3605 				}
3606 				mod_union_consistency_check = TRUE;
3607 			} else if (!strcmp (opt, "check-mark-bits")) {
3608 				check_mark_bits_after_major_collection = TRUE;
3609 			} else if (!strcmp (opt, "check-nursery-pinned")) {
3610 				check_nursery_objects_pinned = TRUE;
3611 			} else if (!strcmp (opt, "clear-at-gc")) {
3612 				nursery_clear_policy = CLEAR_AT_GC;
3613 			} else if (!strcmp (opt, "clear-nursery-at-gc")) {
3614 				nursery_clear_policy = CLEAR_AT_GC;
3615 			} else if (!strcmp (opt, "clear-at-tlab-creation")) {
3616 				nursery_clear_policy = CLEAR_AT_TLAB_CREATION;
3617 			} else if (!strcmp (opt, "debug-clear-at-tlab-creation")) {
3618 				nursery_clear_policy = CLEAR_AT_TLAB_CREATION_DEBUG;
3619 			} else if (!strcmp (opt, "check-scan-starts")) {
3620 				do_scan_starts_check = TRUE;
3621 			} else if (!strcmp (opt, "verify-nursery-at-minor-gc")) {
3622 				do_verify_nursery = TRUE;
3623 			} else if (!strcmp (opt, "check-concurrent")) {
3624 				if (!major_collector.is_concurrent) {
3625 					sgen_env_var_error (MONO_GC_DEBUG_NAME, "Ignoring.", "`check-concurrent` only works with concurrent major collectors.");
3626 					continue;
3627 				}
3628 				nursery_clear_policy = CLEAR_AT_GC;
3629 				do_concurrent_checks = TRUE;
3630 			} else if (!strcmp (opt, "dump-nursery-at-minor-gc")) {
3631 				do_dump_nursery_content = TRUE;
3632 			} else if (!strcmp (opt, "disable-minor")) {
3633 				disable_minor_collections = TRUE;
3634 			} else if (!strcmp (opt, "disable-major")) {
3635 				disable_major_collections = TRUE;
3636 			} else if (g_str_has_prefix (opt, "heap-dump=")) {
3637 				char *filename = strchr (opt, '=') + 1;
3638 				nursery_clear_policy = CLEAR_AT_GC;
3639 				sgen_debug_enable_heap_dump (filename);
3640 			} else if (g_str_has_prefix (opt, "binary-protocol=")) {
3641 				char *filename = strchr (opt, '=') + 1;
3642 				char *colon = strrchr (filename, ':');
3643 				size_t limit = 0;
3644 				if (colon) {
3645 					if (!mono_gc_parse_environment_string_extract_number (colon + 1, &limit)) {
3646 						sgen_env_var_error (MONO_GC_DEBUG_NAME, "Ignoring limit.", "Binary protocol file size limit must be an integer.");
3647 						limit = -1;
3648 					}
3649 					*colon = '\0';
3650 				}
3651 				binary_protocol_init (filename, (long long)limit);
3652 			} else if (!strcmp (opt, "nursery-canaries")) {
3653 				do_verify_nursery = TRUE;
3654 				enable_nursery_canaries = TRUE;
3655 			} else if (!sgen_client_handle_gc_debug (opt)) {
3656 				sgen_env_var_error (MONO_GC_DEBUG_NAME, "Ignoring.", "Unknown option `%s`.", opt);
3657 
3658 				if (usage_printed)
3659 					continue;
3660 
3661 				fprintf (stderr, "\n%s must be of the format [<l>[:<filename>]|<option>]+ where <l> is a debug level 0-9.\n", MONO_GC_DEBUG_NAME);
3662 				fprintf (stderr, "Valid <option>s are:\n");
3663 				fprintf (stderr, "  collect-before-allocs[=<n>]\n");
3664 				fprintf (stderr, "  verify-before-allocs[=<n>]\n");
3665 				fprintf (stderr, "  max-valloc-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
3666 				fprintf (stderr, "  check-remset-consistency\n");
3667 				fprintf (stderr, "  check-mark-bits\n");
3668 				fprintf (stderr, "  check-nursery-pinned\n");
3669 				fprintf (stderr, "  verify-before-collections\n");
3670 				fprintf (stderr, "  verify-nursery-at-minor-gc\n");
3671 				fprintf (stderr, "  dump-nursery-at-minor-gc\n");
3672 				fprintf (stderr, "  disable-minor\n");
3673 				fprintf (stderr, "  disable-major\n");
3674 				fprintf (stderr, "  check-concurrent\n");
3675 				fprintf (stderr, "  clear-[nursery-]at-gc\n");
3676 				fprintf (stderr, "  clear-at-tlab-creation\n");
3677 				fprintf (stderr, "  debug-clear-at-tlab-creation\n");
3678 				fprintf (stderr, "  check-scan-starts\n");
3679 				fprintf (stderr, "  print-allowance\n");
3680 				fprintf (stderr, "  print-pinning\n");
3681 				fprintf (stderr, "  heap-dump=<filename>\n");
3682 				fprintf (stderr, "  binary-protocol=<filename>[:<file-size-limit>]\n");
3683 				fprintf (stderr, "  nursery-canaries\n");
3684 				sgen_client_print_gc_debug_usage ();
3685 				fprintf (stderr, "\n");
3686 
3687 				usage_printed = TRUE;
3688 			}
3689 		}
3690 		g_strfreev (opts);
3691 	}
3692 
3693 	if (debug_opts)
3694 		g_free (debug_opts);
3695 
3696 	if (check_mark_bits_after_major_collection)
3697 		nursery_clear_policy = CLEAR_AT_GC;
3698 
3699 	if (major_collector.post_param_init)
3700 		major_collector.post_param_init (&major_collector);
3701 
3702 	sgen_thread_pool_start ();
3703 
3704 	sgen_memgov_init (max_heap, soft_limit, debug_print_allowance, allowance_ratio, save_target);
3705 
3706 	memset (&remset, 0, sizeof (remset));
3707 
3708 	sgen_card_table_init (&remset);
3709 
3710 	sgen_register_root (NULL, 0, sgen_make_user_root_descriptor (sgen_mark_normal_gc_handles), ROOT_TYPE_NORMAL, MONO_ROOT_SOURCE_GC_HANDLE, "normal gc handles");
3711 
3712 	gc_initialized = 1;
3713 
3714 	sgen_init_bridge ();
3715 }
3716 
3717 gboolean
sgen_gc_initialized()3718 sgen_gc_initialized ()
3719 {
3720 	return gc_initialized > 0;
3721 }
3722 
3723 NurseryClearPolicy
sgen_get_nursery_clear_policy(void)3724 sgen_get_nursery_clear_policy (void)
3725 {
3726 	return nursery_clear_policy;
3727 }
3728 
3729 void
sgen_gc_lock(void)3730 sgen_gc_lock (void)
3731 {
3732 	mono_coop_mutex_lock (&gc_mutex);
3733 }
3734 
3735 void
sgen_gc_unlock(void)3736 sgen_gc_unlock (void)
3737 {
3738 	mono_coop_mutex_unlock (&gc_mutex);
3739 }
3740 
3741 void
sgen_major_collector_iterate_live_block_ranges(sgen_cardtable_block_callback callback)3742 sgen_major_collector_iterate_live_block_ranges (sgen_cardtable_block_callback callback)
3743 {
3744 	major_collector.iterate_live_block_ranges (callback);
3745 }
3746 
3747 void
sgen_major_collector_iterate_block_ranges(sgen_cardtable_block_callback callback)3748 sgen_major_collector_iterate_block_ranges (sgen_cardtable_block_callback callback)
3749 {
3750 	major_collector.iterate_block_ranges (callback);
3751 }
3752 
3753 SgenMajorCollector*
sgen_get_major_collector(void)3754 sgen_get_major_collector (void)
3755 {
3756 	return &major_collector;
3757 }
3758 
3759 SgenMinorCollector*
sgen_get_minor_collector(void)3760 sgen_get_minor_collector (void)
3761 {
3762 	return &sgen_minor_collector;
3763 }
3764 
3765 SgenRememberedSet*
sgen_get_remset(void)3766 sgen_get_remset (void)
3767 {
3768 	return &remset;
3769 }
3770 
3771 static void
count_cards(long long * major_total,long long * major_marked,long long * los_total,long long * los_marked)3772 count_cards (long long *major_total, long long *major_marked, long long *los_total, long long *los_marked)
3773 {
3774 	sgen_get_major_collector ()->count_cards (major_total, major_marked);
3775 	sgen_los_count_cards (los_total, los_marked);
3776 }
3777 
3778 static gboolean world_is_stopped = FALSE;
3779 
3780 /* LOCKING: assumes the GC lock is held */
3781 void
sgen_stop_world(int generation)3782 sgen_stop_world (int generation)
3783 {
3784 	long long major_total = -1, major_marked = -1, los_total = -1, los_marked = -1;
3785 
3786 	SGEN_ASSERT (0, !world_is_stopped, "Why are we stopping a stopped world?");
3787 
3788 	binary_protocol_world_stopping (generation, sgen_timestamp (), (gpointer) (gsize) mono_native_thread_id_get ());
3789 
3790 	sgen_client_stop_world (generation);
3791 
3792 	world_is_stopped = TRUE;
3793 
3794 	if (binary_protocol_is_heavy_enabled ())
3795 		count_cards (&major_total, &major_marked, &los_total, &los_marked);
3796 	binary_protocol_world_stopped (generation, sgen_timestamp (), major_total, major_marked, los_total, los_marked);
3797 }
3798 
3799 /* LOCKING: assumes the GC lock is held */
3800 void
sgen_restart_world(int generation)3801 sgen_restart_world (int generation)
3802 {
3803 	long long major_total = -1, major_marked = -1, los_total = -1, los_marked = -1;
3804 	gint64 stw_time;
3805 
3806 	SGEN_ASSERT (0, world_is_stopped, "Why are we restarting a running world?");
3807 
3808 	if (binary_protocol_is_heavy_enabled ())
3809 		count_cards (&major_total, &major_marked, &los_total, &los_marked);
3810 	binary_protocol_world_restarting (generation, sgen_timestamp (), major_total, major_marked, los_total, los_marked);
3811 
3812 	world_is_stopped = FALSE;
3813 
3814 	sgen_client_restart_world (generation, &stw_time);
3815 
3816 	binary_protocol_world_restarted (generation, sgen_timestamp ());
3817 
3818 	if (sgen_client_bridge_need_processing ())
3819 		sgen_client_bridge_processing_finish (generation);
3820 
3821 	sgen_memgov_collection_end (generation, stw_time);
3822 }
3823 
3824 gboolean
sgen_is_world_stopped(void)3825 sgen_is_world_stopped (void)
3826 {
3827 	return world_is_stopped;
3828 }
3829 
3830 void
sgen_check_whole_heap_stw(void)3831 sgen_check_whole_heap_stw (void)
3832 {
3833 	sgen_stop_world (0);
3834 	sgen_clear_nursery_fragments ();
3835 	sgen_check_whole_heap (TRUE);
3836 	sgen_restart_world (0);
3837 }
3838 
3839 gint64
sgen_timestamp(void)3840 sgen_timestamp (void)
3841 {
3842 	SGEN_TV_DECLARE (timestamp);
3843 	SGEN_TV_GETTIME (timestamp);
3844 	return SGEN_TV_ELAPSED (sgen_init_timestamp, timestamp);
3845 }
3846 
3847 #endif /* HAVE_SGEN_GC */
3848