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