[sgen] Use parallel minors for pause mode
[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 (current_collection_generation, 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, job_split_count;
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 (worker_data);
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, job_data->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, job_data->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, job_data->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, job_data->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, job_data->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, job_data->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 (GENERATION_OLD);
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                 psj->job_split_count = split_count;
1532                 sgen_workers_enqueue_job (GENERATION_OLD, &psj->scan_job.job, TRUE);
1533         }
1534
1535         for (i = 0; i < split_count; i++) {
1536                 psj = (ParallelScanJob*)sgen_thread_pool_job_alloc ("preclean los mod union cardtable", job_los_mod_union_preclean, sizeof (ParallelScanJob));
1537                 psj->scan_job.gc_thread_gray_queue = NULL;
1538                 psj->job_index = i;
1539                 psj->job_split_count = split_count;
1540                 sgen_workers_enqueue_job (GENERATION_OLD, &psj->scan_job.job, TRUE);
1541         }
1542
1543         sj = (ScanJob*)sgen_thread_pool_job_alloc ("scan last pinned", job_scan_last_pinned, sizeof (ScanJob));
1544         sj->gc_thread_gray_queue = NULL;
1545         sgen_workers_enqueue_job (GENERATION_OLD, &sj->job, TRUE);
1546 }
1547
1548 static void
1549 init_gray_queue (SgenGrayQueue *gc_thread_gray_queue)
1550 {
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 (GENERATION_NURSERY);
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 (GENERATION_NURSERY, &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                 psj->job_split_count = split_count;
1573                 sgen_workers_enqueue_job (GENERATION_NURSERY, &psj->scan_job.job, enqueue);
1574
1575                 psj = (ParallelScanJob*)sgen_thread_pool_job_alloc ("scan LOS remsets", job_scan_los_card_table, sizeof (ParallelScanJob));
1576                 psj->scan_job.ops = ops;
1577                 psj->scan_job.gc_thread_gray_queue = gc_thread_gray_queue;
1578                 psj->job_index = i;
1579                 psj->job_split_count = split_count;
1580                 sgen_workers_enqueue_job (GENERATION_NURSERY, &psj->scan_job.job, enqueue);
1581         }
1582 }
1583
1584 static void
1585 enqueue_scan_from_roots_jobs (SgenGrayQueue *gc_thread_gray_queue, char *heap_start, char *heap_end, SgenObjectOperations *ops, gboolean enqueue)
1586 {
1587         ScanFromRegisteredRootsJob *scrrj;
1588         ScanThreadDataJob *stdj;
1589         ScanFinalizerEntriesJob *sfej;
1590
1591         /* registered roots, this includes static fields */
1592
1593         scrrj = (ScanFromRegisteredRootsJob*)sgen_thread_pool_job_alloc ("scan from registered roots normal", job_scan_from_registered_roots, sizeof (ScanFromRegisteredRootsJob));
1594         scrrj->scan_job.ops = ops;
1595         scrrj->scan_job.gc_thread_gray_queue = gc_thread_gray_queue;
1596         scrrj->heap_start = heap_start;
1597         scrrj->heap_end = heap_end;
1598         scrrj->root_type = ROOT_TYPE_NORMAL;
1599         sgen_workers_enqueue_job (current_collection_generation, &scrrj->scan_job.job, enqueue);
1600
1601         if (current_collection_generation == GENERATION_OLD) {
1602                 /* During minors we scan the cardtable for these roots instead */
1603                 scrrj = (ScanFromRegisteredRootsJob*)sgen_thread_pool_job_alloc ("scan from registered roots wbarrier", job_scan_from_registered_roots, sizeof (ScanFromRegisteredRootsJob));
1604                 scrrj->scan_job.ops = ops;
1605                 scrrj->scan_job.gc_thread_gray_queue = gc_thread_gray_queue;
1606                 scrrj->heap_start = heap_start;
1607                 scrrj->heap_end = heap_end;
1608                 scrrj->root_type = ROOT_TYPE_WBARRIER;
1609                 sgen_workers_enqueue_job (current_collection_generation, &scrrj->scan_job.job, enqueue);
1610         }
1611
1612         /* Threads */
1613
1614         stdj = (ScanThreadDataJob*)sgen_thread_pool_job_alloc ("scan thread data", job_scan_thread_data, sizeof (ScanThreadDataJob));
1615         stdj->scan_job.ops = ops;
1616         stdj->scan_job.gc_thread_gray_queue = gc_thread_gray_queue;
1617         stdj->heap_start = heap_start;
1618         stdj->heap_end = heap_end;
1619         sgen_workers_enqueue_job (current_collection_generation, &stdj->scan_job.job, enqueue);
1620
1621         /* Scan the list of objects ready for finalization. */
1622
1623         sfej = (ScanFinalizerEntriesJob*)sgen_thread_pool_job_alloc ("scan finalizer entries", job_scan_finalizer_entries, sizeof (ScanFinalizerEntriesJob));
1624         sfej->scan_job.ops = ops;
1625         sfej->scan_job.gc_thread_gray_queue = gc_thread_gray_queue;
1626         sfej->queue = &fin_ready_queue;
1627         sgen_workers_enqueue_job (current_collection_generation, &sfej->scan_job.job, enqueue);
1628
1629         sfej = (ScanFinalizerEntriesJob*)sgen_thread_pool_job_alloc ("scan critical finalizer entries", job_scan_finalizer_entries, sizeof (ScanFinalizerEntriesJob));
1630         sfej->scan_job.ops = ops;
1631         sfej->scan_job.gc_thread_gray_queue = gc_thread_gray_queue;
1632         sfej->queue = &critical_fin_queue;
1633         sgen_workers_enqueue_job (current_collection_generation, &sfej->scan_job.job, enqueue);
1634 }
1635
1636 /*
1637  * Perform a nursery collection.
1638  *
1639  * Return whether any objects were late-pinned due to being out of memory.
1640  */
1641 static gboolean
1642 collect_nursery (const char *reason, gboolean is_overflow, SgenGrayQueue *unpin_queue)
1643 {
1644         gboolean needs_major, is_parallel = FALSE;
1645         mword fragment_total;
1646         SgenGrayQueue gc_thread_gray_queue;
1647         SgenObjectOperations *object_ops_nopar, *object_ops_par = NULL;
1648         ScanCopyContext ctx;
1649         TV_DECLARE (atv);
1650         TV_DECLARE (btv);
1651         SGEN_TV_DECLARE (last_minor_collection_start_tv);
1652         SGEN_TV_DECLARE (last_minor_collection_end_tv);
1653
1654         if (disable_minor_collections)
1655                 return TRUE;
1656
1657         TV_GETTIME (last_minor_collection_start_tv);
1658         atv = last_minor_collection_start_tv;
1659
1660         binary_protocol_collection_begin (gc_stats.minor_gc_count, GENERATION_NURSERY);
1661
1662         object_ops_nopar = sgen_concurrent_collection_in_progress ()
1663                                 ? &sgen_minor_collector.serial_ops_with_concurrent_major
1664                                 : &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_concurrent_collection_in_progress ()
1667                                         ? &sgen_minor_collector.parallel_ops_with_concurrent_major
1668                                         : &sgen_minor_collector.parallel_ops;
1669                 is_parallel = TRUE;
1670         }
1671
1672         if (do_verify_nursery || do_dump_nursery_content)
1673                 sgen_debug_verify_nursery (do_dump_nursery_content);
1674
1675         current_collection_generation = GENERATION_NURSERY;
1676
1677         SGEN_ASSERT (0, !sgen_collection_is_concurrent (), "Why is the nursery collection concurrent?");
1678
1679         reset_pinned_from_failed_allocation ();
1680
1681         check_scan_starts ();
1682
1683         sgen_nursery_alloc_prepare_for_minor ();
1684
1685         degraded_mode = 0;
1686         objects_pinned = 0;
1687
1688         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));
1689
1690         /* world must be stopped already */
1691         TV_GETTIME (btv);
1692         time_minor_pre_collection_fragment_clear += TV_ELAPSED (atv, btv);
1693
1694         sgen_client_pre_collection_checks ();
1695
1696         major_collector.start_nursery_collection ();
1697
1698         sgen_memgov_minor_collection_start ();
1699
1700         init_gray_queue (&gc_thread_gray_queue);
1701         ctx = CONTEXT_FROM_OBJECT_OPERATIONS (object_ops_nopar, &gc_thread_gray_queue);
1702
1703         gc_stats.minor_gc_count ++;
1704
1705         sgen_process_fin_stage_entries ();
1706
1707         /* pin from pinned handles */
1708         sgen_init_pinning ();
1709         if (concurrent_collection_in_progress)
1710                 sgen_init_pinning_for_conc ();
1711         sgen_client_binary_protocol_mark_start (GENERATION_NURSERY);
1712         pin_from_roots (nursery_section->data, nursery_section->end_data, ctx);
1713         /* pin cemented objects */
1714         sgen_pin_cemented_objects ();
1715         /* identify pinned objects */
1716         sgen_optimize_pin_queue ();
1717         sgen_pinning_setup_section (nursery_section);
1718
1719         pin_objects_in_nursery (FALSE, ctx);
1720         sgen_pinning_trim_queue_to_section (nursery_section);
1721         if (concurrent_collection_in_progress)
1722                 sgen_finish_pinning_for_conc ();
1723
1724         if (remset_consistency_checks)
1725                 sgen_check_remset_consistency ();
1726
1727         if (whole_heap_check_before_collection) {
1728                 sgen_clear_nursery_fragments ();
1729                 sgen_check_whole_heap (FALSE);
1730         }
1731
1732         TV_GETTIME (atv);
1733         time_minor_pinning += TV_ELAPSED (btv, atv);
1734         SGEN_LOG (2, "Finding pinned pointers: %zd in %lld usecs", sgen_get_pinned_count (), (long long)TV_ELAPSED (btv, atv));
1735         SGEN_LOG (4, "Start scan with %zd pinned objects", sgen_get_pinned_count ());
1736
1737         remset.start_scan_remsets ();
1738
1739         enqueue_scan_remembered_set_jobs (&gc_thread_gray_queue, is_parallel ? NULL : object_ops_nopar, is_parallel);
1740
1741         /* we don't have complete write barrier yet, so we scan all the old generation sections */
1742         TV_GETTIME (btv);
1743         time_minor_scan_remsets += TV_ELAPSED (atv, btv);
1744         SGEN_LOG (2, "Old generation scan: %lld usecs", (long long)TV_ELAPSED (atv, btv));
1745
1746         sgen_pin_stats_report ();
1747
1748         /* FIXME: Why do we do this at this specific, seemingly random, point? */
1749         sgen_client_collecting_minor (&fin_ready_queue, &critical_fin_queue);
1750
1751         TV_GETTIME (atv);
1752         time_minor_scan_pinned += TV_ELAPSED (btv, atv);
1753
1754         enqueue_scan_from_roots_jobs (&gc_thread_gray_queue, nursery_section->data, nursery_section->end_data, is_parallel ? NULL : object_ops_nopar, is_parallel);
1755
1756         if (is_parallel) {
1757                 gray_queue_redirect (&gc_thread_gray_queue);
1758                 sgen_workers_start_all_workers (GENERATION_NURSERY, object_ops_nopar, object_ops_par, NULL);
1759                 sgen_workers_join (GENERATION_NURSERY);
1760         }
1761
1762         TV_GETTIME (btv);
1763         time_minor_scan_roots += TV_ELAPSED (atv, btv);
1764
1765         finish_gray_stack (GENERATION_NURSERY, ctx);
1766
1767         TV_GETTIME (atv);
1768         time_minor_finish_gray_stack += TV_ELAPSED (btv, atv);
1769         sgen_client_binary_protocol_mark_end (GENERATION_NURSERY);
1770
1771         if (objects_pinned) {
1772                 sgen_optimize_pin_queue ();
1773                 sgen_pinning_setup_section (nursery_section);
1774         }
1775
1776         /*
1777          * This is the latest point at which we can do this check, because
1778          * sgen_build_nursery_fragments() unpins nursery objects again.
1779          */
1780         if (remset_consistency_checks)
1781                 sgen_check_remset_consistency ();
1782
1783
1784         if (sgen_max_pause_time) {
1785                 int duration;
1786
1787                 TV_GETTIME (btv);
1788                 duration = (int)(TV_ELAPSED (last_minor_collection_start_tv, btv) / 10000);
1789                 if (duration > (sgen_max_pause_time * sgen_max_pause_margin))
1790                         sgen_resize_nursery (TRUE);
1791                 else
1792                         sgen_resize_nursery (FALSE);
1793         } else {
1794                         sgen_resize_nursery (FALSE);
1795         }
1796
1797         /* walk the pin_queue, build up the fragment list of free memory, unmark
1798          * pinned objects as we go, memzero() the empty fragments so they are ready for the
1799          * next allocations.
1800          */
1801         sgen_client_binary_protocol_reclaim_start (GENERATION_NURSERY);
1802         fragment_total = sgen_build_nursery_fragments (nursery_section, unpin_queue);
1803         if (!fragment_total)
1804                 degraded_mode = 1;
1805
1806         /* Clear TLABs for all threads */
1807         sgen_clear_tlabs ();
1808
1809         sgen_client_binary_protocol_reclaim_end (GENERATION_NURSERY);
1810         TV_GETTIME (btv);
1811         time_minor_fragment_creation += TV_ELAPSED (atv, btv);
1812         SGEN_LOG (2, "Fragment creation: %lld usecs, %lu bytes available", (long long)TV_ELAPSED (atv, btv), (unsigned long)fragment_total);
1813
1814         if (remset_consistency_checks)
1815                 sgen_check_major_refs ();
1816
1817         major_collector.finish_nursery_collection ();
1818
1819         TV_GETTIME (last_minor_collection_end_tv);
1820         gc_stats.minor_gc_time += TV_ELAPSED (last_minor_collection_start_tv, last_minor_collection_end_tv);
1821
1822         sgen_debug_dump_heap ("minor", gc_stats.minor_gc_count - 1, NULL);
1823
1824         /* prepare the pin queue for the next collection */
1825         sgen_finish_pinning ();
1826         if (sgen_have_pending_finalizers ()) {
1827                 SGEN_LOG (4, "Finalizer-thread wakeup");
1828                 sgen_client_finalize_notify ();
1829         }
1830         sgen_pin_stats_reset ();
1831         /* clear cemented hash */
1832         sgen_cement_clear_below_threshold ();
1833
1834         sgen_gray_object_queue_dispose (&gc_thread_gray_queue);
1835
1836         check_scan_starts ();
1837
1838         binary_protocol_flush_buffers (FALSE);
1839
1840         sgen_memgov_minor_collection_end (reason, is_overflow);
1841
1842         /*objects are late pinned because of lack of memory, so a major is a good call*/
1843         needs_major = objects_pinned > 0;
1844         current_collection_generation = -1;
1845         objects_pinned = 0;
1846
1847         binary_protocol_collection_end (gc_stats.minor_gc_count - 1, GENERATION_NURSERY, 0, 0);
1848
1849         if (check_nursery_objects_pinned && !sgen_minor_collector.is_split)
1850                 sgen_check_nursery_objects_pinned (unpin_queue != NULL);
1851
1852         return needs_major;
1853 }
1854
1855 typedef enum {
1856         COPY_OR_MARK_FROM_ROOTS_SERIAL,
1857         COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT,
1858         COPY_OR_MARK_FROM_ROOTS_FINISH_CONCURRENT
1859 } CopyOrMarkFromRootsMode;
1860
1861 static void
1862 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)
1863 {
1864         LOSObject *bigobj;
1865         TV_DECLARE (atv);
1866         TV_DECLARE (btv);
1867         /* FIXME: only use these values for the precise scan
1868          * note that to_space pointers should be excluded anyway...
1869          */
1870         char *heap_start = NULL;
1871         char *heap_end = (char*)-1;
1872         ScanCopyContext ctx = CONTEXT_FROM_OBJECT_OPERATIONS (object_ops_nopar, gc_thread_gray_queue);
1873         gboolean concurrent = mode != COPY_OR_MARK_FROM_ROOTS_SERIAL;
1874
1875         SGEN_ASSERT (0, !!concurrent == !!concurrent_collection_in_progress, "We've been called with the wrong mode.");
1876
1877         if (mode == COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT) {
1878                 /*This cleans up unused fragments */
1879                 sgen_nursery_allocator_prepare_for_pinning ();
1880
1881                 if (do_concurrent_checks)
1882                         sgen_debug_check_nursery_is_clean ();
1883         } else {
1884                 /* The concurrent collector doesn't touch the nursery. */
1885                 sgen_nursery_alloc_prepare_for_major ();
1886         }
1887
1888         TV_GETTIME (atv);
1889
1890         /* Pinning depends on this */
1891         sgen_clear_nursery_fragments ();
1892
1893         if (whole_heap_check_before_collection)
1894                 sgen_check_whole_heap (TRUE);
1895
1896         TV_GETTIME (btv);
1897         time_major_pre_collection_fragment_clear += TV_ELAPSED (atv, btv);
1898
1899         objects_pinned = 0;
1900
1901         sgen_client_pre_collection_checks ();
1902
1903         if (mode != COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT) {
1904                 /* Remsets are not useful for a major collection */
1905                 remset.clear_cards ();
1906         }
1907
1908         sgen_process_fin_stage_entries ();
1909
1910         TV_GETTIME (atv);
1911         sgen_init_pinning ();
1912         if (mode == COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT)
1913                 sgen_init_pinning_for_conc ();
1914         SGEN_LOG (6, "Collecting pinned addresses");
1915         pin_from_roots ((void*)lowest_heap_address, (void*)highest_heap_address, ctx);
1916         if (mode == COPY_OR_MARK_FROM_ROOTS_FINISH_CONCURRENT) {
1917                 /* Pin cemented objects that were forced */
1918                 sgen_pin_cemented_objects ();
1919         }
1920         sgen_optimize_pin_queue ();
1921         if (mode == COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT) {
1922                 /*
1923                  * Cemented objects that are in the pinned list will be marked. When
1924                  * marking concurrently we won't mark mod-union cards for these objects.
1925                  * Instead they will remain cemented until the next major collection,
1926                  * when we will recheck if they are still pinned in the roots.
1927                  */
1928                 sgen_cement_force_pinned ();
1929         }
1930
1931         sgen_client_collecting_major_1 ();
1932
1933         /*
1934          * pin_queue now contains all candidate pointers, sorted and
1935          * uniqued.  We must do two passes now to figure out which
1936          * objects are pinned.
1937          *
1938          * The first is to find within the pin_queue the area for each
1939          * section.  This requires that the pin_queue be sorted.  We
1940          * also process the LOS objects and pinned chunks here.
1941          *
1942          * The second, destructive, pass is to reduce the section
1943          * areas to pointers to the actually pinned objects.
1944          */
1945         SGEN_LOG (6, "Pinning from sections");
1946         /* first pass for the sections */
1947         sgen_find_section_pin_queue_start_end (nursery_section);
1948         /* identify possible pointers to the insize of large objects */
1949         SGEN_LOG (6, "Pinning from large objects");
1950         for (bigobj = los_object_list; bigobj; bigobj = bigobj->next) {
1951                 size_t dummy;
1952                 if (sgen_find_optimized_pin_queue_area ((char*)bigobj->data, (char*)bigobj->data + sgen_los_object_size (bigobj), &dummy, &dummy)) {
1953                         binary_protocol_pin (bigobj->data, (gpointer)LOAD_VTABLE (bigobj->data), safe_object_get_size (bigobj->data));
1954
1955                         if (sgen_los_object_is_pinned (bigobj->data)) {
1956                                 SGEN_ASSERT (0, mode == COPY_OR_MARK_FROM_ROOTS_FINISH_CONCURRENT, "LOS objects can only be pinned here after concurrent marking.");
1957                                 continue;
1958                         }
1959                         sgen_los_pin_object (bigobj->data);
1960                         if (SGEN_OBJECT_HAS_REFERENCES (bigobj->data))
1961                                 GRAY_OBJECT_ENQUEUE_SERIAL (gc_thread_gray_queue, bigobj->data, sgen_obj_get_descriptor ((GCObject*)bigobj->data));
1962                         sgen_pin_stats_register_object (bigobj->data, GENERATION_OLD);
1963                         SGEN_LOG (6, "Marked large object %p (%s) size: %lu from roots", bigobj->data,
1964                                         sgen_client_vtable_get_name (SGEN_LOAD_VTABLE (bigobj->data)),
1965                                         (unsigned long)sgen_los_object_size (bigobj));
1966
1967                         sgen_client_pinned_los_object (bigobj->data);
1968                 }
1969         }
1970
1971         pin_objects_in_nursery (mode == COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT, ctx);
1972         if (check_nursery_objects_pinned && !sgen_minor_collector.is_split)
1973                 sgen_check_nursery_objects_pinned (mode != COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT);
1974
1975         major_collector.pin_objects (gc_thread_gray_queue);
1976         if (old_next_pin_slot)
1977                 *old_next_pin_slot = sgen_get_pinned_count ();
1978
1979         TV_GETTIME (btv);
1980         time_major_pinning += TV_ELAPSED (atv, btv);
1981         SGEN_LOG (2, "Finding pinned pointers: %zd in %lld usecs", sgen_get_pinned_count (), (long long)TV_ELAPSED (atv, btv));
1982         SGEN_LOG (4, "Start scan with %zd pinned objects", sgen_get_pinned_count ());
1983
1984         if (mode == COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT)
1985                 sgen_finish_pinning_for_conc ();
1986
1987         major_collector.init_to_space ();
1988
1989         SGEN_ASSERT (0, sgen_workers_all_done (), "Why are the workers not done when we start or finish a major collection?");
1990         if (mode == COPY_OR_MARK_FROM_ROOTS_FINISH_CONCURRENT) {
1991                 if (object_ops_par != NULL)
1992                         sgen_workers_set_num_active_workers (GENERATION_OLD, 0);
1993                 if (sgen_workers_have_idle_work (GENERATION_OLD)) {
1994                         /*
1995                          * We force the finish of the worker with the new object ops context
1996                          * which can also do copying. We need to have finished pinning.
1997                          */
1998                         sgen_workers_start_all_workers (GENERATION_OLD, object_ops_nopar, object_ops_par, NULL);
1999
2000                         sgen_workers_join (GENERATION_OLD);
2001                 }
2002         }
2003
2004 #ifdef SGEN_DEBUG_INTERNAL_ALLOC
2005         main_gc_thread = mono_native_thread_self ();
2006 #endif
2007
2008         sgen_client_collecting_major_2 ();
2009
2010         TV_GETTIME (atv);
2011         time_major_scan_pinned += TV_ELAPSED (btv, atv);
2012
2013         sgen_client_collecting_major_3 (&fin_ready_queue, &critical_fin_queue);
2014
2015         enqueue_scan_from_roots_jobs (gc_thread_gray_queue, heap_start, heap_end, object_ops_nopar, FALSE);
2016
2017         TV_GETTIME (btv);
2018         time_major_scan_roots += TV_ELAPSED (atv, btv);
2019
2020         /*
2021          * We start the concurrent worker after pinning and after we scanned the roots
2022          * in order to make sure that the worker does not finish before handling all
2023          * the roots.
2024          */
2025         if (mode == COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT) {
2026                 sgen_workers_set_num_active_workers (GENERATION_OLD, 1);
2027                 gray_queue_redirect (gc_thread_gray_queue);
2028                 if (precleaning_enabled) {
2029                         sgen_workers_start_all_workers (GENERATION_OLD, object_ops_nopar, object_ops_par, workers_finish_callback);
2030                 } else {
2031                         sgen_workers_start_all_workers (GENERATION_OLD, object_ops_nopar, object_ops_par, NULL);
2032                 }
2033         }
2034
2035         if (mode == COPY_OR_MARK_FROM_ROOTS_FINISH_CONCURRENT) {
2036                 int i, split_count = sgen_workers_get_job_split_count (GENERATION_OLD);
2037                 gboolean parallel = object_ops_par != NULL;
2038
2039                 /* If we're not parallel we finish the collection on the gc thread */
2040                 if (parallel)
2041                         gray_queue_redirect (gc_thread_gray_queue);
2042
2043                 /* Mod union card table */
2044                 for (i = 0; i < split_count; i++) {
2045                         ParallelScanJob *psj;
2046
2047                         psj = (ParallelScanJob*)sgen_thread_pool_job_alloc ("scan mod union cardtable", job_scan_major_mod_union_card_table, sizeof (ParallelScanJob));
2048                         psj->scan_job.ops = parallel ? NULL : object_ops_nopar;
2049                         psj->scan_job.gc_thread_gray_queue = gc_thread_gray_queue;
2050                         psj->job_index = i;
2051                         psj->job_split_count = split_count;
2052                         sgen_workers_enqueue_job (GENERATION_OLD, &psj->scan_job.job, parallel);
2053
2054                         psj = (ParallelScanJob*)sgen_thread_pool_job_alloc ("scan LOS mod union cardtable", job_scan_los_mod_union_card_table, sizeof (ParallelScanJob));
2055                         psj->scan_job.ops = parallel ? NULL : object_ops_nopar;
2056                         psj->scan_job.gc_thread_gray_queue = gc_thread_gray_queue;
2057                         psj->job_index = i;
2058                         psj->job_split_count = split_count;
2059                         sgen_workers_enqueue_job (GENERATION_OLD, &psj->scan_job.job, parallel);
2060                 }
2061
2062                 if (parallel) {
2063                         /*
2064                          * If we enqueue a job while workers are running we need to sgen_workers_ensure_awake
2065                          * in order to make sure that we are running the idle func and draining all worker
2066                          * gray queues. The operation of starting workers implies this, so we start them after
2067                          * in order to avoid doing this operation twice. The workers will drain the main gray
2068                          * stack that contained roots and pinned objects and also scan the mod union card
2069                          * table.
2070                          */
2071                         sgen_workers_start_all_workers (GENERATION_OLD, object_ops_nopar, object_ops_par, NULL);
2072                         sgen_workers_join (GENERATION_OLD);
2073                 }
2074         }
2075
2076         sgen_pin_stats_report ();
2077
2078         if (mode == COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT) {
2079                 sgen_finish_pinning ();
2080
2081                 sgen_pin_stats_reset ();
2082
2083                 if (do_concurrent_checks)
2084                         sgen_debug_check_nursery_is_clean ();
2085         }
2086 }
2087
2088 static void
2089 major_start_collection (SgenGrayQueue *gc_thread_gray_queue, const char *reason, gboolean concurrent, size_t *old_next_pin_slot)
2090 {
2091         SgenObjectOperations *object_ops_nopar, *object_ops_par = NULL;
2092
2093         binary_protocol_collection_begin (gc_stats.major_gc_count, GENERATION_OLD);
2094
2095         current_collection_generation = GENERATION_OLD;
2096
2097         sgen_workers_assert_gray_queue_is_empty (GENERATION_OLD);
2098
2099         if (!concurrent)
2100                 sgen_cement_reset ();
2101
2102         if (concurrent) {
2103                 g_assert (major_collector.is_concurrent);
2104                 concurrent_collection_in_progress = TRUE;
2105
2106                 object_ops_nopar = &major_collector.major_ops_concurrent_start;
2107                 if (major_collector.is_parallel)
2108                         object_ops_par = &major_collector.major_ops_conc_par_start;
2109
2110         } else {
2111                 object_ops_nopar = &major_collector.major_ops_serial;
2112         }
2113
2114         reset_pinned_from_failed_allocation ();
2115
2116         sgen_memgov_major_collection_start (concurrent, reason);
2117
2118         //count_ref_nonref_objs ();
2119         //consistency_check ();
2120
2121         check_scan_starts ();
2122
2123         degraded_mode = 0;
2124         SGEN_LOG (1, "Start major collection %d", gc_stats.major_gc_count);
2125         gc_stats.major_gc_count ++;
2126
2127         if (major_collector.start_major_collection)
2128                 major_collector.start_major_collection ();
2129
2130         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);
2131 }
2132
2133 static void
2134 major_finish_collection (SgenGrayQueue *gc_thread_gray_queue, const char *reason, gboolean is_overflow, size_t old_next_pin_slot, gboolean forced)
2135 {
2136         ScannedObjectCounts counts;
2137         SgenObjectOperations *object_ops_nopar;
2138         mword fragment_total;
2139         TV_DECLARE (atv);
2140         TV_DECLARE (btv);
2141
2142         TV_GETTIME (btv);
2143
2144         if (concurrent_collection_in_progress) {
2145                 SgenObjectOperations *object_ops_par = NULL;
2146
2147                 object_ops_nopar = &major_collector.major_ops_concurrent_finish;
2148                 if (major_collector.is_parallel)
2149                         object_ops_par = &major_collector.major_ops_conc_par_finish;
2150
2151                 major_copy_or_mark_from_roots (gc_thread_gray_queue, NULL, COPY_OR_MARK_FROM_ROOTS_FINISH_CONCURRENT, object_ops_nopar, object_ops_par);
2152
2153 #ifdef SGEN_DEBUG_INTERNAL_ALLOC
2154                 main_gc_thread = NULL;
2155 #endif
2156         } else {
2157                 object_ops_nopar = &major_collector.major_ops_serial;
2158         }
2159
2160         sgen_workers_assert_gray_queue_is_empty (GENERATION_OLD);
2161
2162         finish_gray_stack (GENERATION_OLD, CONTEXT_FROM_OBJECT_OPERATIONS (object_ops_nopar, gc_thread_gray_queue));
2163         TV_GETTIME (atv);
2164         time_major_finish_gray_stack += TV_ELAPSED (btv, atv);
2165
2166         SGEN_ASSERT (0, sgen_workers_all_done (), "Can't have workers working after joining");
2167
2168         if (objects_pinned) {
2169                 g_assert (!concurrent_collection_in_progress);
2170
2171                 /*
2172                  * This is slow, but we just OOM'd.
2173                  *
2174                  * See comment at `sgen_pin_queue_clear_discarded_entries` for how the pin
2175                  * queue is laid out at this point.
2176                  */
2177                 sgen_pin_queue_clear_discarded_entries (nursery_section, old_next_pin_slot);
2178                 /*
2179                  * We need to reestablish all pinned nursery objects in the pin queue
2180                  * because they're needed for fragment creation.  Unpinning happens by
2181                  * walking the whole queue, so it's not necessary to reestablish where major
2182                  * heap block pins are - all we care is that they're still in there
2183                  * somewhere.
2184                  */
2185                 sgen_optimize_pin_queue ();
2186                 sgen_find_section_pin_queue_start_end (nursery_section);
2187                 objects_pinned = 0;
2188         }
2189
2190         reset_heap_boundaries ();
2191         sgen_update_heap_boundaries ((mword)sgen_get_nursery_start (), (mword)sgen_get_nursery_end ());
2192
2193         /* walk the pin_queue, build up the fragment list of free memory, unmark
2194          * pinned objects as we go, memzero() the empty fragments so they are ready for the
2195          * next allocations.
2196          */
2197         fragment_total = sgen_build_nursery_fragments (nursery_section, NULL);
2198         if (!fragment_total)
2199                 degraded_mode = 1;
2200         SGEN_LOG (4, "Free space in nursery after major %ld", (long)fragment_total);
2201
2202         if (do_concurrent_checks && concurrent_collection_in_progress)
2203                 sgen_debug_check_nursery_is_clean ();
2204
2205         /* prepare the pin queue for the next collection */
2206         sgen_finish_pinning ();
2207
2208         /* Clear TLABs for all threads */
2209         sgen_clear_tlabs ();
2210
2211         sgen_pin_stats_reset ();
2212
2213         sgen_cement_clear_below_threshold ();
2214
2215         if (check_mark_bits_after_major_collection)
2216                 sgen_check_heap_marked (concurrent_collection_in_progress);
2217
2218         TV_GETTIME (btv);
2219         time_major_fragment_creation += TV_ELAPSED (atv, btv);
2220
2221         binary_protocol_sweep_begin (GENERATION_OLD, !major_collector.sweeps_lazily);
2222         sgen_memgov_major_pre_sweep ();
2223
2224         TV_GETTIME (atv);
2225         time_major_free_bigobjs += TV_ELAPSED (btv, atv);
2226
2227         sgen_los_sweep ();
2228
2229         TV_GETTIME (btv);
2230         time_major_los_sweep += TV_ELAPSED (atv, btv);
2231
2232         major_collector.sweep ();
2233
2234         binary_protocol_sweep_end (GENERATION_OLD, !major_collector.sweeps_lazily);
2235
2236         TV_GETTIME (atv);
2237         time_major_sweep += TV_ELAPSED (btv, atv);
2238
2239         sgen_debug_dump_heap ("major", gc_stats.major_gc_count - 1, reason);
2240
2241         if (sgen_have_pending_finalizers ()) {
2242                 SGEN_LOG (4, "Finalizer-thread wakeup");
2243                 sgen_client_finalize_notify ();
2244         }
2245
2246         sgen_memgov_major_collection_end (forced, concurrent_collection_in_progress, reason, is_overflow);
2247         current_collection_generation = -1;
2248
2249         memset (&counts, 0, sizeof (ScannedObjectCounts));
2250         major_collector.finish_major_collection (&counts);
2251
2252         sgen_workers_assert_gray_queue_is_empty (GENERATION_OLD);
2253
2254         SGEN_ASSERT (0, sgen_workers_all_done (), "Can't have workers working after major collection has finished");
2255         if (concurrent_collection_in_progress)
2256                 concurrent_collection_in_progress = FALSE;
2257
2258         check_scan_starts ();
2259
2260         binary_protocol_flush_buffers (FALSE);
2261
2262         //consistency_check ();
2263
2264         binary_protocol_collection_end (gc_stats.major_gc_count - 1, GENERATION_OLD, counts.num_scanned_objects, counts.num_unique_scanned_objects);
2265 }
2266
2267 static gboolean
2268 major_do_collection (const char *reason, gboolean is_overflow, gboolean forced)
2269 {
2270         TV_DECLARE (time_start);
2271         TV_DECLARE (time_end);
2272         size_t old_next_pin_slot;
2273         SgenGrayQueue gc_thread_gray_queue;
2274
2275         if (disable_major_collections)
2276                 return FALSE;
2277
2278         if (major_collector.get_and_reset_num_major_objects_marked) {
2279                 long long num_marked = major_collector.get_and_reset_num_major_objects_marked ();
2280                 g_assert (!num_marked);
2281         }
2282
2283         /* world must be stopped already */
2284         TV_GETTIME (time_start);
2285
2286         init_gray_queue (&gc_thread_gray_queue);
2287         major_start_collection (&gc_thread_gray_queue, reason, FALSE, &old_next_pin_slot);
2288         major_finish_collection (&gc_thread_gray_queue, reason, is_overflow, old_next_pin_slot, forced);
2289         sgen_gray_object_queue_dispose (&gc_thread_gray_queue);
2290
2291         TV_GETTIME (time_end);
2292         gc_stats.major_gc_time += TV_ELAPSED (time_start, time_end);
2293
2294         /* FIXME: also report this to the user, preferably in gc-end. */
2295         if (major_collector.get_and_reset_num_major_objects_marked)
2296                 major_collector.get_and_reset_num_major_objects_marked ();
2297
2298         return bytes_pinned_from_failed_allocation > 0;
2299 }
2300
2301 static void
2302 major_start_concurrent_collection (const char *reason)
2303 {
2304         TV_DECLARE (time_start);
2305         TV_DECLARE (time_end);
2306         long long num_objects_marked;
2307         SgenGrayQueue gc_thread_gray_queue;
2308
2309         if (disable_major_collections)
2310                 return;
2311
2312         TV_GETTIME (time_start);
2313         SGEN_TV_GETTIME (time_major_conc_collection_start);
2314
2315         num_objects_marked = major_collector.get_and_reset_num_major_objects_marked ();
2316         g_assert (num_objects_marked == 0);
2317
2318         binary_protocol_concurrent_start ();
2319
2320         init_gray_queue (&gc_thread_gray_queue);
2321         // FIXME: store reason and pass it when finishing
2322         major_start_collection (&gc_thread_gray_queue, reason, TRUE, NULL);
2323         sgen_gray_object_queue_dispose (&gc_thread_gray_queue);
2324
2325         num_objects_marked = major_collector.get_and_reset_num_major_objects_marked ();
2326
2327         TV_GETTIME (time_end);
2328         gc_stats.major_gc_time += TV_ELAPSED (time_start, time_end);
2329
2330         current_collection_generation = -1;
2331 }
2332
2333 /*
2334  * Returns whether the major collection has finished.
2335  */
2336 static gboolean
2337 major_should_finish_concurrent_collection (void)
2338 {
2339         return sgen_workers_all_done ();
2340 }
2341
2342 static void
2343 major_update_concurrent_collection (void)
2344 {
2345         TV_DECLARE (total_start);
2346         TV_DECLARE (total_end);
2347
2348         TV_GETTIME (total_start);
2349
2350         binary_protocol_concurrent_update ();
2351
2352         major_collector.update_cardtable_mod_union ();
2353         sgen_los_update_cardtable_mod_union ();
2354
2355         TV_GETTIME (total_end);
2356         gc_stats.major_gc_time += TV_ELAPSED (total_start, total_end);
2357 }
2358
2359 static void
2360 major_finish_concurrent_collection (gboolean forced)
2361 {
2362         SgenGrayQueue gc_thread_gray_queue;
2363         TV_DECLARE (total_start);
2364         TV_DECLARE (total_end);
2365
2366         TV_GETTIME (total_start);
2367
2368         binary_protocol_concurrent_finish ();
2369
2370         /*
2371          * We need to stop all workers since we're updating the cardtable below.
2372          * The workers will be resumed with a finishing pause context to avoid
2373          * additional cardtable and object scanning.
2374          */
2375         sgen_workers_stop_all_workers (GENERATION_OLD);
2376
2377         SGEN_TV_GETTIME (time_major_conc_collection_end);
2378         gc_stats.major_gc_time_concurrent += SGEN_TV_ELAPSED (time_major_conc_collection_start, time_major_conc_collection_end);
2379
2380         major_collector.update_cardtable_mod_union ();
2381         sgen_los_update_cardtable_mod_union ();
2382
2383         if (mod_union_consistency_check)
2384                 sgen_check_mod_union_consistency ();
2385
2386         current_collection_generation = GENERATION_OLD;
2387         sgen_cement_reset ();
2388         init_gray_queue (&gc_thread_gray_queue);
2389         major_finish_collection (&gc_thread_gray_queue, "finishing", FALSE, -1, forced);
2390         sgen_gray_object_queue_dispose (&gc_thread_gray_queue);
2391
2392         TV_GETTIME (total_end);
2393         gc_stats.major_gc_time += TV_ELAPSED (total_start, total_end);
2394
2395         current_collection_generation = -1;
2396 }
2397
2398 /*
2399  * Ensure an allocation request for @size will succeed by freeing enough memory.
2400  *
2401  * LOCKING: The GC lock MUST be held.
2402  */
2403 void
2404 sgen_ensure_free_space (size_t size, int generation)
2405 {
2406         int generation_to_collect = -1;
2407         const char *reason = NULL;
2408
2409         if (generation == GENERATION_OLD) {
2410                 if (sgen_need_major_collection (size)) {
2411                         reason = "LOS overflow";
2412                         generation_to_collect = GENERATION_OLD;
2413                 }
2414         } else {
2415                 if (degraded_mode) {
2416                         if (sgen_need_major_collection (size)) {
2417                                 reason = "Degraded mode overflow";
2418                                 generation_to_collect = GENERATION_OLD;
2419                         }
2420                 } else if (sgen_need_major_collection (size)) {
2421                         reason = concurrent_collection_in_progress ? "Forced finish concurrent collection" : "Minor allowance";
2422                         generation_to_collect = GENERATION_OLD;
2423                 } else {
2424                         generation_to_collect = GENERATION_NURSERY;
2425                         reason = "Nursery full";                        
2426                 }
2427         }
2428
2429         if (generation_to_collect == -1) {
2430                 if (concurrent_collection_in_progress && sgen_workers_all_done ()) {
2431                         generation_to_collect = GENERATION_OLD;
2432                         reason = "Finish concurrent collection";
2433                 }
2434         }
2435
2436         if (generation_to_collect == -1)
2437                 return;
2438         sgen_perform_collection (size, generation_to_collect, reason, FALSE, TRUE);
2439 }
2440
2441 /*
2442  * LOCKING: Assumes the GC lock is held.
2443  */
2444 void
2445 sgen_perform_collection (size_t requested_size, int generation_to_collect, const char *reason, gboolean wait_to_finish, gboolean stw)
2446 {
2447         TV_DECLARE (gc_total_start);
2448         TV_DECLARE (gc_total_end);
2449         int overflow_generation_to_collect = -1;
2450         int oldest_generation_collected = generation_to_collect;
2451         const char *overflow_reason = NULL;
2452         gboolean finish_concurrent = concurrent_collection_in_progress && (major_should_finish_concurrent_collection () || generation_to_collect == GENERATION_OLD);
2453
2454         binary_protocol_collection_requested (generation_to_collect, requested_size, wait_to_finish ? 1 : 0);
2455
2456         SGEN_ASSERT (0, generation_to_collect == GENERATION_NURSERY || generation_to_collect == GENERATION_OLD, "What generation is this?");
2457
2458         if (stw)
2459                 sgen_stop_world (generation_to_collect);
2460         else
2461                 SGEN_ASSERT (0, sgen_is_world_stopped (), "We can only collect if the world is stopped");
2462                 
2463
2464         TV_GETTIME (gc_total_start);
2465
2466         // FIXME: extract overflow reason
2467         // FIXME: minor overflow for concurrent case
2468         if (generation_to_collect == GENERATION_NURSERY && !finish_concurrent) {
2469                 if (concurrent_collection_in_progress)
2470                         major_update_concurrent_collection ();
2471
2472                 if (collect_nursery (reason, FALSE, NULL) && !concurrent_collection_in_progress) {
2473                         overflow_generation_to_collect = GENERATION_OLD;
2474                         overflow_reason = "Minor overflow";
2475                 }
2476         } else if (finish_concurrent) {
2477                 major_finish_concurrent_collection (wait_to_finish);
2478                 oldest_generation_collected = GENERATION_OLD;
2479         } else {
2480                 SGEN_ASSERT (0, generation_to_collect == GENERATION_OLD, "We should have handled nursery collections above");
2481                 if (major_collector.is_concurrent && !wait_to_finish) {
2482                         collect_nursery ("Concurrent start", FALSE, NULL);
2483                         major_start_concurrent_collection (reason);
2484                         oldest_generation_collected = GENERATION_NURSERY;
2485                 } else if (major_do_collection (reason, FALSE, wait_to_finish)) {
2486                         overflow_generation_to_collect = GENERATION_NURSERY;
2487                         overflow_reason = "Excessive pinning";
2488                 }
2489         }
2490
2491         if (overflow_generation_to_collect != -1) {
2492                 SGEN_ASSERT (0, !concurrent_collection_in_progress, "We don't yet support overflow collections with the concurrent collector");
2493
2494                 /*
2495                  * We need to do an overflow collection, either because we ran out of memory
2496                  * or the nursery is fully pinned.
2497                  */
2498
2499                 if (overflow_generation_to_collect == GENERATION_NURSERY)
2500                         collect_nursery (overflow_reason, TRUE, NULL);
2501                 else
2502                         major_do_collection (overflow_reason, TRUE, wait_to_finish);
2503
2504                 oldest_generation_collected = MAX (oldest_generation_collected, overflow_generation_to_collect);
2505         }
2506
2507         SGEN_LOG (2, "Heap size: %lu, LOS size: %lu", (unsigned long)sgen_gc_get_total_heap_allocation (), (unsigned long)los_memory_usage);
2508
2509         /* this also sets the proper pointers for the next allocation */
2510         if (generation_to_collect == GENERATION_NURSERY && !sgen_can_alloc_size (requested_size)) {
2511                 /* TypeBuilder and MonoMethod are killing mcs with fragmentation */
2512                 SGEN_LOG (1, "nursery collection didn't find enough room for %zd alloc (%zd pinned)", requested_size, sgen_get_pinned_count ());
2513                 sgen_dump_pin_queue ();
2514                 degraded_mode = 1;
2515         }
2516
2517         TV_GETTIME (gc_total_end);
2518         time_max = MAX (time_max, TV_ELAPSED (gc_total_start, gc_total_end));
2519
2520         if (stw)
2521                 sgen_restart_world (oldest_generation_collected);
2522 }
2523
2524 /*
2525  * ######################################################################
2526  * ########  Memory allocation from the OS
2527  * ######################################################################
2528  * This section of code deals with getting memory from the OS and
2529  * allocating memory for GC-internal data structures.
2530  * Internal memory can be handled with a freelist for small objects.
2531  */
2532
2533 /*
2534  * Debug reporting.
2535  */
2536 G_GNUC_UNUSED static void
2537 report_internal_mem_usage (void)
2538 {
2539         printf ("Internal memory usage:\n");
2540         sgen_report_internal_mem_usage ();
2541         printf ("Pinned memory usage:\n");
2542         major_collector.report_pinned_memory_usage ();
2543 }
2544
2545 /*
2546  * ######################################################################
2547  * ########  Finalization support
2548  * ######################################################################
2549  */
2550
2551 /*
2552  * If the object has been forwarded it means it's still referenced from a root. 
2553  * If it is pinned it's still alive as well.
2554  * A LOS object is only alive if we have pinned it.
2555  * Return TRUE if @obj is ready to be finalized.
2556  */
2557 static inline gboolean
2558 sgen_is_object_alive (GCObject *object)
2559 {
2560         if (ptr_in_nursery (object))
2561                 return sgen_nursery_is_object_alive (object);
2562
2563         return sgen_major_is_object_alive (object);
2564 }
2565
2566 /*
2567  * This function returns true if @object is either alive and belongs to the
2568  * current collection - major collections are full heap, so old gen objects
2569  * are never alive during a minor collection.
2570  */
2571 static inline int
2572 sgen_is_object_alive_and_on_current_collection (GCObject *object)
2573 {
2574         if (ptr_in_nursery (object))
2575                 return sgen_nursery_is_object_alive (object);
2576
2577         if (current_collection_generation == GENERATION_NURSERY)
2578                 return FALSE;
2579
2580         return sgen_major_is_object_alive (object);
2581 }
2582
2583
2584 gboolean
2585 sgen_gc_is_object_ready_for_finalization (GCObject *object)
2586 {
2587         return !sgen_is_object_alive (object);
2588 }
2589
2590 void
2591 sgen_queue_finalization_entry (GCObject *obj)
2592 {
2593         gboolean critical = sgen_client_object_has_critical_finalizer (obj);
2594
2595         sgen_pointer_queue_add (critical ? &critical_fin_queue : &fin_ready_queue, obj);
2596
2597         sgen_client_object_queued_for_finalization (obj);
2598 }
2599
2600 gboolean
2601 sgen_object_is_live (GCObject *obj)
2602 {
2603         return sgen_is_object_alive_and_on_current_collection (obj);
2604 }
2605
2606 /*
2607  * `System.GC.WaitForPendingFinalizers` first checks `sgen_have_pending_finalizers()` to
2608  * determine whether it can exit quickly.  The latter must therefore only return FALSE if
2609  * all finalizers have really finished running.
2610  *
2611  * `sgen_gc_invoke_finalizers()` first dequeues a finalizable object, and then finalizes it.
2612  * This means that just checking whether the queues are empty leaves the possibility that an
2613  * object might have been dequeued but not yet finalized.  That's why we need the additional
2614  * flag `pending_unqueued_finalizer`.
2615  */
2616
2617 static volatile gboolean pending_unqueued_finalizer = FALSE;
2618 volatile gboolean sgen_suspend_finalizers = FALSE;
2619
2620 void
2621 sgen_set_suspend_finalizers (void)
2622 {
2623         sgen_suspend_finalizers = TRUE;
2624 }
2625
2626 int
2627 sgen_gc_invoke_finalizers (void)
2628 {
2629         int count = 0;
2630
2631         g_assert (!pending_unqueued_finalizer);
2632
2633         /* FIXME: batch to reduce lock contention */
2634         while (sgen_have_pending_finalizers ()) {
2635                 GCObject *obj;
2636
2637                 LOCK_GC;
2638
2639                 /*
2640                  * We need to set `pending_unqueued_finalizer` before dequeing the
2641                  * finalizable object.
2642                  */
2643                 if (!sgen_pointer_queue_is_empty (&fin_ready_queue)) {
2644                         pending_unqueued_finalizer = TRUE;
2645                         mono_memory_write_barrier ();
2646                         obj = (GCObject *)sgen_pointer_queue_pop (&fin_ready_queue);
2647                 } else if (!sgen_pointer_queue_is_empty (&critical_fin_queue)) {
2648                         pending_unqueued_finalizer = TRUE;
2649                         mono_memory_write_barrier ();
2650                         obj = (GCObject *)sgen_pointer_queue_pop (&critical_fin_queue);
2651                 } else {
2652                         obj = NULL;
2653                 }
2654
2655                 if (obj)
2656                         SGEN_LOG (7, "Finalizing object %p (%s)", obj, sgen_client_vtable_get_name (SGEN_LOAD_VTABLE (obj)));
2657
2658                 UNLOCK_GC;
2659
2660                 if (!obj)
2661                         break;
2662
2663                 count++;
2664                 /* the object is on the stack so it is pinned */
2665                 /*g_print ("Calling finalizer for object: %p (%s)\n", obj, sgen_client_object_safe_name (obj));*/
2666                 sgen_client_run_finalize (obj);
2667         }
2668
2669         if (pending_unqueued_finalizer) {
2670                 mono_memory_write_barrier ();
2671                 pending_unqueued_finalizer = FALSE;
2672         }
2673
2674         return count;
2675 }
2676
2677 gboolean
2678 sgen_have_pending_finalizers (void)
2679 {
2680         if (sgen_suspend_finalizers)
2681                 return FALSE;
2682         return pending_unqueued_finalizer || !sgen_pointer_queue_is_empty (&fin_ready_queue) || !sgen_pointer_queue_is_empty (&critical_fin_queue);
2683 }
2684
2685 /*
2686  * ######################################################################
2687  * ########  registered roots support
2688  * ######################################################################
2689  */
2690
2691 /*
2692  * We do not coalesce roots.
2693  */
2694 int
2695 sgen_register_root (char *start, size_t size, SgenDescriptor descr, int root_type, int source, const char *msg)
2696 {
2697         RootRecord new_root;
2698         int i;
2699         LOCK_GC;
2700         for (i = 0; i < ROOT_TYPE_NUM; ++i) {
2701                 RootRecord *root = (RootRecord *)sgen_hash_table_lookup (&roots_hash [i], start);
2702                 /* we allow changing the size and the descriptor (for thread statics etc) */
2703                 if (root) {
2704                         size_t old_size = root->end_root - start;
2705                         root->end_root = start + size;
2706                         SGEN_ASSERT (0, !!root->root_desc == !!descr, "Can't change whether a root is precise or conservative.");
2707                         SGEN_ASSERT (0, root->source == source, "Can't change a root's source identifier.");
2708                         SGEN_ASSERT (0, !!root->msg == !!msg, "Can't change a root's message.");
2709                         root->root_desc = descr;
2710                         roots_size += size;
2711                         roots_size -= old_size;
2712                         UNLOCK_GC;
2713                         return TRUE;
2714                 }
2715         }
2716
2717         new_root.end_root = start + size;
2718         new_root.root_desc = descr;
2719         new_root.source = source;
2720         new_root.msg = msg;
2721
2722         sgen_hash_table_replace (&roots_hash [root_type], start, &new_root, NULL);
2723         roots_size += size;
2724
2725         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);
2726
2727         UNLOCK_GC;
2728         return TRUE;
2729 }
2730
2731 void
2732 sgen_deregister_root (char* addr)
2733 {
2734         int root_type;
2735         RootRecord root;
2736
2737         LOCK_GC;
2738         for (root_type = 0; root_type < ROOT_TYPE_NUM; ++root_type) {
2739                 if (sgen_hash_table_remove (&roots_hash [root_type], addr, &root))
2740                         roots_size -= (root.end_root - addr);
2741         }
2742         UNLOCK_GC;
2743 }
2744
2745 void
2746 sgen_wbroots_iterate_live_block_ranges (sgen_cardtable_block_callback cb)
2747 {
2748         void **start_root;
2749         RootRecord *root;
2750         SGEN_HASH_TABLE_FOREACH (&roots_hash [ROOT_TYPE_WBARRIER], void **, start_root, RootRecord *, root) {
2751                 cb ((mword)start_root, (mword)root->end_root - (mword)start_root);
2752         } SGEN_HASH_TABLE_FOREACH_END;
2753 }
2754
2755 /* Root equivalent of sgen_client_cardtable_scan_object */
2756 static void
2757 sgen_wbroot_scan_card_table (void** start_root, mword size,  ScanCopyContext ctx)
2758 {
2759         ScanPtrFieldFunc scan_field_func = ctx.ops->scan_ptr_field;
2760         guint8 *card_data = sgen_card_table_get_card_scan_address ((mword)start_root);
2761         guint8 *card_base = card_data;
2762         mword card_count = sgen_card_table_number_of_cards_in_range ((mword)start_root, size);
2763         guint8 *card_data_end = card_data + card_count;
2764         mword extra_idx = 0;
2765         char *obj_start = sgen_card_table_align_pointer (start_root);
2766         char *obj_end = (char*)start_root + size;
2767 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
2768         guint8 *overflow_scan_end = NULL;
2769 #endif
2770
2771 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
2772         /*Check for overflow and if so, setup to scan in two steps*/
2773         if (card_data_end >= SGEN_SHADOW_CARDTABLE_END) {
2774                 overflow_scan_end = sgen_shadow_cardtable + (card_data_end - SGEN_SHADOW_CARDTABLE_END);
2775                 card_data_end = SGEN_SHADOW_CARDTABLE_END;
2776         }
2777
2778 LOOP_HEAD:
2779 #endif
2780
2781         card_data = sgen_find_next_card (card_data, card_data_end);
2782
2783         for (; card_data < card_data_end; card_data = sgen_find_next_card (card_data + 1, card_data_end)) {
2784                 size_t idx = (card_data - card_base) + extra_idx;
2785                 char *start = (char*)(obj_start + idx * CARD_SIZE_IN_BYTES);
2786                 char *card_end = start + CARD_SIZE_IN_BYTES;
2787                 char *elem = start, *first_elem = start;
2788
2789                 /*
2790                  * Don't clean first and last card on 32bit systems since they
2791                  * may also be part from other roots.
2792                  */
2793                 if (card_data != card_base && card_data != (card_data_end - 1))
2794                         sgen_card_table_prepare_card_for_scanning (card_data);
2795
2796                 card_end = MIN (card_end, obj_end);
2797
2798                 if (elem < (char*)start_root)
2799                         first_elem = elem = (char*)start_root;
2800
2801                 for (; elem < card_end; elem += SIZEOF_VOID_P) {
2802                         if (*(GCObject**)elem)
2803                                 scan_field_func (NULL, (GCObject**)elem, ctx.queue);
2804                 }
2805
2806                 binary_protocol_card_scan (first_elem, elem - first_elem);
2807         }
2808
2809 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
2810         if (overflow_scan_end) {
2811                 extra_idx = card_data - card_base;
2812                 card_base = card_data = sgen_shadow_cardtable;
2813                 card_data_end = overflow_scan_end;
2814                 overflow_scan_end = NULL;
2815                 goto LOOP_HEAD;
2816         }
2817 #endif
2818 }
2819
2820 void
2821 sgen_wbroots_scan_card_table (ScanCopyContext ctx)
2822 {
2823         void **start_root;
2824         RootRecord *root;
2825
2826         SGEN_HASH_TABLE_FOREACH (&roots_hash [ROOT_TYPE_WBARRIER], void **, start_root, RootRecord *, root) {
2827                 SGEN_ASSERT (0, (root->root_desc & ROOT_DESC_TYPE_MASK) == ROOT_DESC_VECTOR, "Unsupported root type");
2828
2829                 sgen_wbroot_scan_card_table (start_root, (mword)root->end_root - (mword)start_root, ctx);
2830         } SGEN_HASH_TABLE_FOREACH_END;
2831 }
2832
2833 /*
2834  * ######################################################################
2835  * ########  Thread handling (stop/start code)
2836  * ######################################################################
2837  */
2838
2839 int
2840 sgen_get_current_collection_generation (void)
2841 {
2842         return current_collection_generation;
2843 }
2844
2845 void*
2846 sgen_thread_attach (SgenThreadInfo* info)
2847 {
2848         info->tlab_start = info->tlab_next = info->tlab_temp_end = info->tlab_real_end = NULL;
2849
2850         sgen_client_thread_attach (info);
2851
2852         return info;
2853 }
2854
2855 void
2856 sgen_thread_detach_with_lock (SgenThreadInfo *p)
2857 {
2858         sgen_client_thread_detach_with_lock (p);
2859 }
2860
2861 /*
2862  * ######################################################################
2863  * ########  Write barriers
2864  * ######################################################################
2865  */
2866
2867 /*
2868  * Note: the write barriers first do the needed GC work and then do the actual store:
2869  * this way the value is visible to the conservative GC scan after the write barrier
2870  * itself. If a GC interrupts the barrier in the middle, value will be kept alive by
2871  * the conservative scan, otherwise by the remembered set scan.
2872  */
2873
2874 /**
2875  * mono_gc_wbarrier_arrayref_copy:
2876  */
2877 void
2878 mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
2879 {
2880         HEAVY_STAT (++stat_wbarrier_arrayref_copy);
2881         /*This check can be done without taking a lock since dest_ptr array is pinned*/
2882         if (ptr_in_nursery (dest_ptr) || count <= 0) {
2883                 mono_gc_memmove_aligned (dest_ptr, src_ptr, count * sizeof (gpointer));
2884                 return;
2885         }
2886
2887 #ifdef SGEN_HEAVY_BINARY_PROTOCOL
2888         if (binary_protocol_is_heavy_enabled ()) {
2889                 int i;
2890                 for (i = 0; i < count; ++i) {
2891                         gpointer dest = (gpointer*)dest_ptr + i;
2892                         gpointer obj = *((gpointer*)src_ptr + i);
2893                         if (obj)
2894                                 binary_protocol_wbarrier (dest, obj, (gpointer)LOAD_VTABLE (obj));
2895                 }
2896         }
2897 #endif
2898
2899         remset.wbarrier_arrayref_copy (dest_ptr, src_ptr, count);
2900 }
2901
2902 /**
2903  * mono_gc_wbarrier_generic_nostore:
2904  */
2905 void
2906 mono_gc_wbarrier_generic_nostore (gpointer ptr)
2907 {
2908         gpointer obj;
2909
2910         HEAVY_STAT (++stat_wbarrier_generic_store);
2911
2912         sgen_client_wbarrier_generic_nostore_check (ptr);
2913
2914         obj = *(gpointer*)ptr;
2915         if (obj)
2916                 binary_protocol_wbarrier (ptr, obj, (gpointer)LOAD_VTABLE (obj));
2917
2918         /*
2919          * We need to record old->old pointer locations for the
2920          * concurrent collector.
2921          */
2922         if (!ptr_in_nursery (obj) && !concurrent_collection_in_progress) {
2923                 SGEN_LOG (8, "Skipping remset at %p", ptr);
2924                 return;
2925         }
2926
2927         SGEN_LOG (8, "Adding remset at %p", ptr);
2928
2929         remset.wbarrier_generic_nostore (ptr);
2930 }
2931
2932 /**
2933  * mono_gc_wbarrier_generic_store:
2934  */
2935 void
2936 mono_gc_wbarrier_generic_store (gpointer ptr, GCObject* value)
2937 {
2938         SGEN_LOG (8, "Wbarrier store at %p to %p (%s)", ptr, value, value ? sgen_client_vtable_get_name (SGEN_LOAD_VTABLE (value)) : "null");
2939         SGEN_UPDATE_REFERENCE_ALLOW_NULL (ptr, value);
2940         if (ptr_in_nursery (value) || concurrent_collection_in_progress)
2941                 mono_gc_wbarrier_generic_nostore (ptr);
2942         sgen_dummy_use (value);
2943 }
2944
2945 /**
2946  * mono_gc_wbarrier_generic_store_atomic:
2947  * Same as \c mono_gc_wbarrier_generic_store but performs the store
2948  * as an atomic operation with release semantics.
2949  */
2950 void
2951 mono_gc_wbarrier_generic_store_atomic (gpointer ptr, GCObject *value)
2952 {
2953         HEAVY_STAT (++stat_wbarrier_generic_store_atomic);
2954
2955         SGEN_LOG (8, "Wbarrier atomic store at %p to %p (%s)", ptr, value, value ? sgen_client_vtable_get_name (SGEN_LOAD_VTABLE (value)) : "null");
2956
2957         InterlockedWritePointer ((volatile gpointer *)ptr, value);
2958
2959         if (ptr_in_nursery (value) || concurrent_collection_in_progress)
2960                 mono_gc_wbarrier_generic_nostore (ptr);
2961
2962         sgen_dummy_use (value);
2963 }
2964
2965 void
2966 sgen_wbarrier_range_copy (gpointer _dest, gpointer _src, int size)
2967 {
2968         remset.wbarrier_range_copy (_dest,_src, size);
2969 }
2970
2971 /*
2972  * ######################################################################
2973  * ########  Other mono public interface functions.
2974  * ######################################################################
2975  */
2976
2977 void
2978 sgen_gc_collect (int generation)
2979 {
2980         LOCK_GC;
2981         if (generation > 1)
2982                 generation = 1;
2983         sgen_perform_collection (0, generation, "user request", TRUE, TRUE);
2984         UNLOCK_GC;
2985 }
2986
2987 int
2988 sgen_gc_collection_count (int generation)
2989 {
2990         if (generation == 0)
2991                 return gc_stats.minor_gc_count;
2992         return gc_stats.major_gc_count;
2993 }
2994
2995 size_t
2996 sgen_gc_get_used_size (void)
2997 {
2998         gint64 tot = 0;
2999         LOCK_GC;
3000         tot = los_memory_usage;
3001         tot += nursery_section->end_data - nursery_section->data;
3002         tot += major_collector.get_used_size ();
3003         /* FIXME: account for pinned objects */
3004         UNLOCK_GC;
3005         return tot;
3006 }
3007
3008 void
3009 sgen_env_var_error (const char *env_var, const char *fallback, const char *description_format, ...)
3010 {
3011         va_list ap;
3012
3013         va_start (ap, description_format);
3014
3015         fprintf (stderr, "Warning: In environment variable `%s': ", env_var);
3016         vfprintf (stderr, description_format, ap);
3017         if (fallback)
3018                 fprintf (stderr, " - %s", fallback);
3019         fprintf (stderr, "\n");
3020
3021         va_end (ap);
3022 }
3023
3024 static gboolean
3025 parse_double_in_interval (const char *env_var, const char *opt_name, const char *opt, double min, double max, double *result)
3026 {
3027         char *endptr;
3028         double val = strtod (opt, &endptr);
3029         if (endptr == opt) {
3030                 sgen_env_var_error (env_var, "Using default value.", "`%s` must be a number.", opt_name);
3031                 return FALSE;
3032         }
3033         else if (val < min || val > max) {
3034                 sgen_env_var_error (env_var, "Using default value.", "`%s` must be between %.2f - %.2f.", opt_name, min, max);
3035                 return FALSE;
3036         }
3037         *result = val;
3038         return TRUE;
3039 }
3040
3041 static SgenMinor
3042 parse_sgen_minor (const char *opt)
3043 {
3044         if (!opt)
3045                 return SGEN_MINOR_DEFAULT;
3046
3047         if (!strcmp (opt, "simple")) {
3048                 return SGEN_MINOR_SIMPLE;
3049         } else if (!strcmp (opt, "simple-par")) {
3050                 return SGEN_MINOR_SIMPLE_PARALLEL;
3051         } else if (!strcmp (opt, "split")) {
3052                 return SGEN_MINOR_SPLIT;
3053         } else {
3054                 sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using default instead.", "Unknown minor collector `%s'.", opt);
3055                 return SGEN_MINOR_DEFAULT;
3056         }
3057 }
3058
3059 static SgenMajor
3060 parse_sgen_major (const char *opt)
3061 {
3062         if (!opt)
3063                 return SGEN_MAJOR_DEFAULT;
3064
3065         if (!strcmp (opt, "marksweep")) {
3066                 return SGEN_MAJOR_SERIAL;
3067         } else if (!strcmp (opt, "marksweep-conc")) {
3068                 return SGEN_MAJOR_CONCURRENT;
3069         } else if (!strcmp (opt, "marksweep-conc-par")) {
3070                 return SGEN_MAJOR_CONCURRENT_PARALLEL;
3071         } else {
3072                 sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using default instead.", "Unknown major collector `%s'.", opt);
3073                 return SGEN_MAJOR_DEFAULT;
3074         }
3075
3076 }
3077
3078 static SgenMode
3079 parse_sgen_mode (const char *opt)
3080 {
3081         if (!opt)
3082                 return SGEN_MODE_NONE;
3083
3084         if (!strcmp (opt, "balanced")) {
3085                 return SGEN_MODE_BALANCED;
3086         } else if (!strcmp (opt, "throughput")) {
3087                 return SGEN_MODE_THROUGHPUT;
3088         } else if (!strcmp (opt, "pause") || g_str_has_prefix (opt, "pause:")) {
3089                 return SGEN_MODE_PAUSE;
3090         } else {
3091                 sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using default configurations.", "Unknown mode `%s'.", opt);
3092                 return SGEN_MODE_NONE;
3093         }
3094 }
3095
3096 static void
3097 init_sgen_minor (SgenMinor minor)
3098 {
3099         switch (minor) {
3100         case SGEN_MINOR_DEFAULT:
3101         case SGEN_MINOR_SIMPLE:
3102                 sgen_simple_nursery_init (&sgen_minor_collector, FALSE);
3103                 break;
3104         case SGEN_MINOR_SIMPLE_PARALLEL:
3105                 sgen_simple_nursery_init (&sgen_minor_collector, TRUE);
3106                 break;
3107         case SGEN_MINOR_SPLIT:
3108                 sgen_split_nursery_init (&sgen_minor_collector);
3109                 break;
3110         default:
3111                 g_assert_not_reached ();
3112         }
3113 }
3114
3115 static void
3116 init_sgen_major (SgenMajor major)
3117 {
3118         if (major == SGEN_MAJOR_DEFAULT)
3119                 major = DEFAULT_MAJOR;
3120
3121         switch (major) {
3122         case SGEN_MAJOR_SERIAL:
3123                 sgen_marksweep_init (&major_collector);
3124                 break;
3125         case SGEN_MAJOR_CONCURRENT:
3126                 sgen_marksweep_conc_init (&major_collector);
3127                 break;
3128         case SGEN_MAJOR_CONCURRENT_PARALLEL:
3129                 sgen_marksweep_conc_par_init (&major_collector);
3130                 break;
3131         default:
3132                 g_assert_not_reached ();
3133         }
3134 }
3135
3136 /*
3137  * If sgen mode is set, major/minor configuration is fixed. The other gc_params
3138  * are parsed and processed after major/minor initialization, so it can potentially
3139  * override some knobs set by the sgen mode. We can consider locking out additional
3140  * configurations when gc_modes are used.
3141  */
3142 static void
3143 init_sgen_mode (SgenMode mode)
3144 {
3145         SgenMinor minor = SGEN_MINOR_DEFAULT;
3146         SgenMajor major = SGEN_MAJOR_DEFAULT;
3147
3148         switch (mode) {
3149         case SGEN_MODE_BALANCED:
3150                 /*
3151                  * Use a dynamic parallel nursery with a major concurrent collector.
3152                  * This uses the default values for max pause time and nursery size.
3153                  */
3154                 minor = SGEN_MINOR_SIMPLE;
3155                 major = SGEN_MAJOR_CONCURRENT;
3156                 dynamic_nursery = TRUE;
3157                 break;
3158         case SGEN_MODE_THROUGHPUT:
3159                 /*
3160                  * Use concurrent major to let the mutator do more work. Use a larger
3161                  * nursery, without pause time constraints, in order to collect more
3162                  * objects in parallel and avoid repetitive collection tasks (pinning,
3163                  * root scanning etc)
3164                  */
3165                 minor = SGEN_MINOR_SIMPLE_PARALLEL;
3166                 major = SGEN_MAJOR_CONCURRENT;
3167                 dynamic_nursery = TRUE;
3168                 sgen_max_pause_time = 0;
3169                 break;
3170         case SGEN_MODE_PAUSE:
3171                 /*
3172                  * Use concurrent major and dynamic nursery with a more
3173                  * aggressive shrinking relative to pause times.
3174                  */
3175                 minor = SGEN_MINOR_SIMPLE_PARALLEL;
3176                 major = SGEN_MAJOR_CONCURRENT;
3177                 dynamic_nursery = TRUE;
3178                 sgen_max_pause_margin = SGEN_PAUSE_MODE_MAX_PAUSE_MARGIN;
3179                 break;
3180         default:
3181                 g_assert_not_reached ();
3182         }
3183
3184         init_sgen_minor (minor);
3185         init_sgen_major (major);
3186 }
3187
3188 void
3189 sgen_gc_init (void)
3190 {
3191         char *env;
3192         char **opts, **ptr;
3193         SgenMajor sgen_major = SGEN_MAJOR_DEFAULT;
3194         SgenMinor sgen_minor = SGEN_MINOR_DEFAULT;
3195         SgenMode sgen_mode = SGEN_MODE_NONE;
3196         char *params_opts = NULL;
3197         char *debug_opts = NULL;
3198         size_t max_heap = 0;
3199         size_t soft_limit = 0;
3200         int result;
3201         gboolean debug_print_allowance = FALSE;
3202         double allowance_ratio = 0, save_target = 0;
3203         gboolean cement_enabled = TRUE;
3204
3205         do {
3206                 result = InterlockedCompareExchange (&gc_initialized, -1, 0);
3207                 switch (result) {
3208                 case 1:
3209                         /* already inited */
3210                         return;
3211                 case -1:
3212                         /* being inited by another thread */
3213                         mono_thread_info_usleep (1000);
3214                         break;
3215                 case 0:
3216                         /* we will init it */
3217                         break;
3218                 default:
3219                         g_assert_not_reached ();
3220                 }
3221         } while (result != 0);
3222
3223         SGEN_TV_GETTIME (sgen_init_timestamp);
3224
3225 #ifdef SGEN_WITHOUT_MONO
3226         mono_thread_smr_init ();
3227 #endif
3228
3229         mono_coop_mutex_init (&gc_mutex);
3230
3231         gc_debug_file = stderr;
3232
3233         mono_coop_mutex_init (&sgen_interruption_mutex);
3234
3235         if ((env = g_getenv (MONO_GC_PARAMS_NAME)) || gc_params_options) {
3236                 params_opts = g_strdup_printf ("%s,%s", gc_params_options ? gc_params_options : "", env ? env : "");
3237                 g_free (env);
3238         }
3239
3240         if (params_opts) {
3241                 opts = g_strsplit (params_opts, ",", -1);
3242                 for (ptr = opts; *ptr; ++ptr) {
3243                         char *opt = *ptr;
3244                         if (g_str_has_prefix (opt, "major=")) {
3245                                 opt = strchr (opt, '=') + 1;
3246                                 sgen_major = parse_sgen_major (opt);
3247                         } else if (g_str_has_prefix (opt, "minor=")) {
3248                                 opt = strchr (opt, '=') + 1;
3249                                 sgen_minor = parse_sgen_minor (opt);
3250                         } else if (g_str_has_prefix (opt, "mode=")) {
3251                                 opt = strchr (opt, '=') + 1;
3252                                 sgen_mode = parse_sgen_mode (opt);
3253                         }
3254                 }
3255         } else {
3256                 opts = NULL;
3257         }
3258
3259         init_stats ();
3260         sgen_init_internal_allocator ();
3261         sgen_init_nursery_allocator ();
3262         sgen_init_fin_weak_hash ();
3263         sgen_init_hash_table ();
3264         sgen_init_descriptors ();
3265         sgen_init_gray_queues ();
3266         sgen_init_allocator ();
3267         sgen_init_gchandles ();
3268
3269         sgen_register_fixed_internal_mem_type (INTERNAL_MEM_SECTION, SGEN_SIZEOF_GC_MEM_SECTION);
3270         sgen_register_fixed_internal_mem_type (INTERNAL_MEM_GRAY_QUEUE, sizeof (GrayQueueSection));
3271
3272         sgen_client_init ();
3273
3274         if (sgen_mode != SGEN_MODE_NONE) {
3275                 if (sgen_minor != SGEN_MINOR_DEFAULT || sgen_major != SGEN_MAJOR_DEFAULT)
3276                         sgen_env_var_error (MONO_GC_PARAMS_NAME, "Ignoring major/minor configuration", "Major/minor configurations cannot be used with sgen modes");
3277                 init_sgen_mode (sgen_mode);
3278         } else {
3279                 init_sgen_minor (sgen_minor);
3280                 init_sgen_major (sgen_major);
3281         }
3282
3283         if (opts) {
3284                 gboolean usage_printed = FALSE;
3285
3286                 for (ptr = opts; *ptr; ++ptr) {
3287                         char *opt = *ptr;
3288                         if (!strcmp (opt, ""))
3289                                 continue;
3290                         if (g_str_has_prefix (opt, "major="))
3291                                 continue;
3292                         if (g_str_has_prefix (opt, "minor="))
3293                                 continue;
3294                         if (g_str_has_prefix (opt, "mode=")) {
3295                                 if (g_str_has_prefix (opt, "mode=pause:")) {
3296                                         char *str_pause = strchr (opt, ':') + 1;
3297                                         int pause = atoi (str_pause);
3298                                         if (pause)
3299                                                 sgen_max_pause_time = pause;
3300                                         else
3301                                                 sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using default", "Invalid maximum pause time for `pause` sgen mode");
3302                                 }
3303                                 continue;
3304                         }
3305                         if (g_str_has_prefix (opt, "max-heap-size=")) {
3306                                 size_t page_size = mono_pagesize ();
3307                                 size_t max_heap_candidate = 0;
3308                                 opt = strchr (opt, '=') + 1;
3309                                 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &max_heap_candidate)) {
3310                                         max_heap = (max_heap_candidate + page_size - 1) & ~(size_t)(page_size - 1);
3311                                         if (max_heap != max_heap_candidate)
3312                                                 sgen_env_var_error (MONO_GC_PARAMS_NAME, "Rounding up.", "`max-heap-size` size must be a multiple of %d.", page_size);
3313                                 } else {
3314                                         sgen_env_var_error (MONO_GC_PARAMS_NAME, NULL, "`max-heap-size` must be an integer.");
3315                                 }
3316                                 continue;
3317                         }
3318                         if (g_str_has_prefix (opt, "soft-heap-limit=")) {
3319                                 opt = strchr (opt, '=') + 1;
3320                                 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &soft_limit)) {
3321                                         if (soft_limit <= 0) {
3322                                                 sgen_env_var_error (MONO_GC_PARAMS_NAME, NULL, "`soft-heap-limit` must be positive.");
3323                                                 soft_limit = 0;
3324                                         }
3325                                 } else {
3326                                         sgen_env_var_error (MONO_GC_PARAMS_NAME, NULL, "`soft-heap-limit` must be an integer.");
3327                                 }
3328                                 continue;
3329                         }
3330                         if (g_str_has_prefix (opt, "nursery-size=")) {
3331                                 size_t val;
3332                                 opt = strchr (opt, '=') + 1;
3333                                 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &val)) {
3334                                         if ((val & (val - 1))) {
3335                                                 sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using default value.", "`nursery-size` must be a power of two.");
3336                                                 continue;
3337                                         }
3338
3339                                         if (val < SGEN_MAX_NURSERY_WASTE) {
3340                                                 sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using default value.",
3341                                                                 "`nursery-size` must be at least %d bytes.", SGEN_MAX_NURSERY_WASTE);
3342                                                 continue;
3343                                         }
3344
3345                                         min_nursery_size = max_nursery_size = val;
3346                                         dynamic_nursery = FALSE;
3347                                 } else {
3348                                         sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using default value.", "`nursery-size` must be an integer.");
3349                                         continue;
3350                                 }
3351                                 continue;
3352                         }
3353                         if (g_str_has_prefix (opt, "save-target-ratio=")) {
3354                                 double val;
3355                                 opt = strchr (opt, '=') + 1;
3356                                 if (parse_double_in_interval (MONO_GC_PARAMS_NAME, "save-target-ratio", opt,
3357                                                 SGEN_MIN_SAVE_TARGET_RATIO, SGEN_MAX_SAVE_TARGET_RATIO, &val)) {
3358                                         save_target = val;
3359                                 }
3360                                 continue;
3361                         }
3362                         if (g_str_has_prefix (opt, "default-allowance-ratio=")) {
3363                                 double val;
3364                                 opt = strchr (opt, '=') + 1;
3365                                 if (parse_double_in_interval (MONO_GC_PARAMS_NAME, "default-allowance-ratio", opt,
3366                                                 SGEN_MIN_ALLOWANCE_NURSERY_SIZE_RATIO, SGEN_MAX_ALLOWANCE_NURSERY_SIZE_RATIO, &val)) {
3367                                         allowance_ratio = val;
3368                                 }
3369                                 continue;
3370                         }
3371
3372                         if (!strcmp (opt, "cementing")) {
3373                                 cement_enabled = TRUE;
3374                                 continue;
3375                         }
3376                         if (!strcmp (opt, "no-cementing")) {
3377                                 cement_enabled = FALSE;
3378                                 continue;
3379                         }
3380
3381                         if (!strcmp (opt, "precleaning")) {
3382                                 precleaning_enabled = TRUE;
3383                                 continue;
3384                         }
3385                         if (!strcmp (opt, "no-precleaning")) {
3386                                 precleaning_enabled = FALSE;
3387                                 continue;
3388                         }
3389
3390                         if (!strcmp (opt, "dynamic-nursery")) {
3391                                 if (sgen_minor_collector.is_split)
3392                                         sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using default value.",
3393                                                         "dynamic-nursery not supported with split-nursery.");
3394                                 else
3395                                         dynamic_nursery = TRUE;
3396                                 continue;
3397                         }
3398                         if (!strcmp (opt, "no-dynamic-nursery")) {
3399                                 dynamic_nursery = FALSE;
3400                                 continue;
3401                         }
3402
3403                         if (major_collector.handle_gc_param && major_collector.handle_gc_param (opt))
3404                                 continue;
3405
3406                         if (sgen_minor_collector.handle_gc_param && sgen_minor_collector.handle_gc_param (opt))
3407                                 continue;
3408
3409                         if (sgen_client_handle_gc_param (opt))
3410                                 continue;
3411
3412                         sgen_env_var_error (MONO_GC_PARAMS_NAME, "Ignoring.", "Unknown option `%s`.", opt);
3413
3414                         if (usage_printed)
3415                                 continue;
3416
3417                         fprintf (stderr, "\n%s must be a comma-delimited list of one or more of the following:\n", MONO_GC_PARAMS_NAME);
3418                         fprintf (stderr, "  max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
3419                         fprintf (stderr, "  soft-heap-limit=n (where N is an integer, possibly with a k, m or a g suffix)\n");
3420                         fprintf (stderr, "  mode=MODE (where MODE is 'balanced', 'throughput' or 'pause[:N]' and N is maximum pause in milliseconds)\n");
3421                         fprintf (stderr, "  nursery-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
3422                         fprintf (stderr, "  major=COLLECTOR (where COLLECTOR is `marksweep', `marksweep-conc', `marksweep-par')\n");
3423                         fprintf (stderr, "  minor=COLLECTOR (where COLLECTOR is `simple' or `split')\n");
3424                         fprintf (stderr, "  wbarrier=WBARRIER (where WBARRIER is `remset' or `cardtable')\n");
3425                         fprintf (stderr, "  [no-]cementing\n");
3426                         fprintf (stderr, "  [no-]dynamic-nursery\n");
3427                         if (major_collector.print_gc_param_usage)
3428                                 major_collector.print_gc_param_usage ();
3429                         if (sgen_minor_collector.print_gc_param_usage)
3430                                 sgen_minor_collector.print_gc_param_usage ();
3431                         sgen_client_print_gc_params_usage ();
3432                         fprintf (stderr, " Experimental options:\n");
3433                         fprintf (stderr, "  save-target-ratio=R (where R must be between %.2f - %.2f).\n", SGEN_MIN_SAVE_TARGET_RATIO, SGEN_MAX_SAVE_TARGET_RATIO);
3434                         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);
3435                         fprintf (stderr, "\n");
3436
3437                         usage_printed = TRUE;
3438                 }
3439                 g_strfreev (opts);
3440         }
3441
3442         if (params_opts)
3443                 g_free (params_opts);
3444
3445         alloc_nursery (dynamic_nursery, min_nursery_size, max_nursery_size);
3446
3447         sgen_pinning_init ();
3448         sgen_cement_init (cement_enabled);
3449
3450         if ((env = g_getenv (MONO_GC_DEBUG_NAME)) || gc_debug_options) {
3451                 debug_opts = g_strdup_printf ("%s,%s", gc_debug_options ? gc_debug_options  : "", env ? env : "");
3452                 g_free (env);
3453         }
3454
3455         if (debug_opts) {
3456                 gboolean usage_printed = FALSE;
3457
3458                 opts = g_strsplit (debug_opts, ",", -1);
3459                 for (ptr = opts; ptr && *ptr; ptr ++) {
3460                         char *opt = *ptr;
3461                         if (!strcmp (opt, ""))
3462                                 continue;
3463                         if (opt [0] >= '0' && opt [0] <= '9') {
3464                                 gc_debug_level = atoi (opt);
3465                                 opt++;
3466                                 if (opt [0] == ':')
3467                                         opt++;
3468                                 if (opt [0]) {
3469                                         char *rf = g_strdup_printf ("%s.%d", opt, mono_process_current_pid ());
3470                                         gc_debug_file = fopen (rf, "wb");
3471                                         if (!gc_debug_file)
3472                                                 gc_debug_file = stderr;
3473                                         g_free (rf);
3474                                 }
3475                         } else if (!strcmp (opt, "print-allowance")) {
3476                                 debug_print_allowance = TRUE;
3477                         } else if (!strcmp (opt, "print-pinning")) {
3478                                 sgen_pin_stats_enable ();
3479                         } else if (!strcmp (opt, "verify-before-allocs")) {
3480                                 verify_before_allocs = 1;
3481                                 has_per_allocation_action = TRUE;
3482                         } else if (g_str_has_prefix (opt, "max-valloc-size=")) {
3483                                 size_t max_valloc_size;
3484                                 char *arg = strchr (opt, '=') + 1;
3485                                 if (*opt && mono_gc_parse_environment_string_extract_number (arg, &max_valloc_size)) {
3486                                         mono_valloc_set_limit (max_valloc_size);
3487                                 } else {
3488                                         sgen_env_var_error (MONO_GC_DEBUG_NAME, NULL, "`max-valloc-size` must be an integer.");
3489                                 }
3490                                 continue;
3491                         } else if (g_str_has_prefix (opt, "verify-before-allocs=")) {
3492                                 char *arg = strchr (opt, '=') + 1;
3493                                 verify_before_allocs = atoi (arg);
3494                                 has_per_allocation_action = TRUE;
3495                         } else if (!strcmp (opt, "collect-before-allocs")) {
3496                                 collect_before_allocs = 1;
3497                                 has_per_allocation_action = TRUE;
3498                         } else if (g_str_has_prefix (opt, "collect-before-allocs=")) {
3499                                 char *arg = strchr (opt, '=') + 1;
3500                                 has_per_allocation_action = TRUE;
3501                                 collect_before_allocs = atoi (arg);
3502                         } else if (!strcmp (opt, "verify-before-collections")) {
3503                                 whole_heap_check_before_collection = TRUE;
3504                         } else if (!strcmp (opt, "check-remset-consistency")) {
3505                                 remset_consistency_checks = TRUE;
3506                                 nursery_clear_policy = CLEAR_AT_GC;
3507                         } else if (!strcmp (opt, "mod-union-consistency-check")) {
3508                                 if (!major_collector.is_concurrent) {
3509                                         sgen_env_var_error (MONO_GC_DEBUG_NAME, "Ignoring.", "`mod-union-consistency-check` only works with concurrent major collector.");
3510                                         continue;
3511                                 }
3512                                 mod_union_consistency_check = TRUE;
3513                         } else if (!strcmp (opt, "check-mark-bits")) {
3514                                 check_mark_bits_after_major_collection = TRUE;
3515                         } else if (!strcmp (opt, "check-nursery-pinned")) {
3516                                 check_nursery_objects_pinned = TRUE;
3517                         } else if (!strcmp (opt, "clear-at-gc")) {
3518                                 nursery_clear_policy = CLEAR_AT_GC;
3519                         } else if (!strcmp (opt, "clear-nursery-at-gc")) {
3520                                 nursery_clear_policy = CLEAR_AT_GC;
3521                         } else if (!strcmp (opt, "clear-at-tlab-creation")) {
3522                                 nursery_clear_policy = CLEAR_AT_TLAB_CREATION;
3523                         } else if (!strcmp (opt, "debug-clear-at-tlab-creation")) {
3524                                 nursery_clear_policy = CLEAR_AT_TLAB_CREATION_DEBUG;
3525                         } else if (!strcmp (opt, "check-scan-starts")) {
3526                                 do_scan_starts_check = TRUE;
3527                         } else if (!strcmp (opt, "verify-nursery-at-minor-gc")) {
3528                                 do_verify_nursery = TRUE;
3529                         } else if (!strcmp (opt, "check-concurrent")) {
3530                                 if (!major_collector.is_concurrent) {
3531                                         sgen_env_var_error (MONO_GC_DEBUG_NAME, "Ignoring.", "`check-concurrent` only works with concurrent major collectors.");
3532                                         continue;
3533                                 }
3534                                 nursery_clear_policy = CLEAR_AT_GC;
3535                                 do_concurrent_checks = TRUE;
3536                         } else if (!strcmp (opt, "dump-nursery-at-minor-gc")) {
3537                                 do_dump_nursery_content = TRUE;
3538                         } else if (!strcmp (opt, "disable-minor")) {
3539                                 disable_minor_collections = TRUE;
3540                         } else if (!strcmp (opt, "disable-major")) {
3541                                 disable_major_collections = TRUE;
3542                         } else if (g_str_has_prefix (opt, "heap-dump=")) {
3543                                 char *filename = strchr (opt, '=') + 1;
3544                                 nursery_clear_policy = CLEAR_AT_GC;
3545                                 sgen_debug_enable_heap_dump (filename);
3546                         } else if (g_str_has_prefix (opt, "binary-protocol=")) {
3547                                 char *filename = strchr (opt, '=') + 1;
3548                                 char *colon = strrchr (filename, ':');
3549                                 size_t limit = 0;
3550                                 if (colon) {
3551                                         if (!mono_gc_parse_environment_string_extract_number (colon + 1, &limit)) {
3552                                                 sgen_env_var_error (MONO_GC_DEBUG_NAME, "Ignoring limit.", "Binary protocol file size limit must be an integer.");
3553                                                 limit = -1;
3554                                         }
3555                                         *colon = '\0';
3556                                 }
3557                                 binary_protocol_init (filename, (long long)limit);
3558                         } else if (!strcmp (opt, "nursery-canaries")) {
3559                                 do_verify_nursery = TRUE;
3560                                 enable_nursery_canaries = TRUE;
3561                         } else if (!sgen_client_handle_gc_debug (opt)) {
3562                                 sgen_env_var_error (MONO_GC_DEBUG_NAME, "Ignoring.", "Unknown option `%s`.", opt);
3563
3564                                 if (usage_printed)
3565                                         continue;
3566
3567                                 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);
3568                                 fprintf (stderr, "Valid <option>s are:\n");
3569                                 fprintf (stderr, "  collect-before-allocs[=<n>]\n");
3570                                 fprintf (stderr, "  verify-before-allocs[=<n>]\n");
3571                                 fprintf (stderr, "  max-valloc-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
3572                                 fprintf (stderr, "  check-remset-consistency\n");
3573                                 fprintf (stderr, "  check-mark-bits\n");
3574                                 fprintf (stderr, "  check-nursery-pinned\n");
3575                                 fprintf (stderr, "  verify-before-collections\n");
3576                                 fprintf (stderr, "  verify-nursery-at-minor-gc\n");
3577                                 fprintf (stderr, "  dump-nursery-at-minor-gc\n");
3578                                 fprintf (stderr, "  disable-minor\n");
3579                                 fprintf (stderr, "  disable-major\n");
3580                                 fprintf (stderr, "  check-concurrent\n");
3581                                 fprintf (stderr, "  clear-[nursery-]at-gc\n");
3582                                 fprintf (stderr, "  clear-at-tlab-creation\n");
3583                                 fprintf (stderr, "  debug-clear-at-tlab-creation\n");
3584                                 fprintf (stderr, "  check-scan-starts\n");
3585                                 fprintf (stderr, "  print-allowance\n");
3586                                 fprintf (stderr, "  print-pinning\n");
3587                                 fprintf (stderr, "  heap-dump=<filename>\n");
3588                                 fprintf (stderr, "  binary-protocol=<filename>[:<file-size-limit>]\n");
3589                                 fprintf (stderr, "  nursery-canaries\n");
3590                                 sgen_client_print_gc_debug_usage ();
3591                                 fprintf (stderr, "\n");
3592
3593                                 usage_printed = TRUE;
3594                         }
3595                 }
3596                 g_strfreev (opts);
3597         }
3598
3599         if (debug_opts)
3600                 g_free (debug_opts);
3601
3602         if (check_mark_bits_after_major_collection)
3603                 nursery_clear_policy = CLEAR_AT_GC;
3604
3605         if (major_collector.post_param_init)
3606                 major_collector.post_param_init (&major_collector);
3607
3608         sgen_thread_pool_start ();
3609
3610         sgen_memgov_init (max_heap, soft_limit, debug_print_allowance, allowance_ratio, save_target);
3611
3612         memset (&remset, 0, sizeof (remset));
3613
3614         sgen_card_table_init (&remset);
3615
3616         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");
3617
3618         gc_initialized = 1;
3619
3620         sgen_init_bridge ();
3621 }
3622
3623 gboolean
3624 sgen_gc_initialized ()
3625 {
3626         return gc_initialized > 0;
3627 }
3628
3629 NurseryClearPolicy
3630 sgen_get_nursery_clear_policy (void)
3631 {
3632         return nursery_clear_policy;
3633 }
3634
3635 void
3636 sgen_gc_lock (void)
3637 {
3638         mono_coop_mutex_lock (&gc_mutex);
3639 }
3640
3641 void
3642 sgen_gc_unlock (void)
3643 {
3644         mono_coop_mutex_unlock (&gc_mutex);
3645 }
3646
3647 void
3648 sgen_major_collector_iterate_live_block_ranges (sgen_cardtable_block_callback callback)
3649 {
3650         major_collector.iterate_live_block_ranges (callback);
3651 }
3652
3653 void
3654 sgen_major_collector_iterate_block_ranges (sgen_cardtable_block_callback callback)
3655 {
3656         major_collector.iterate_block_ranges (callback);
3657 }
3658
3659 SgenMajorCollector*
3660 sgen_get_major_collector (void)
3661 {
3662         return &major_collector;
3663 }
3664
3665 SgenMinorCollector*
3666 sgen_get_minor_collector (void)
3667 {
3668         return &sgen_minor_collector;
3669 }
3670
3671 SgenRememberedSet*
3672 sgen_get_remset (void)
3673 {
3674         return &remset;
3675 }
3676
3677 static void
3678 count_cards (long long *major_total, long long *major_marked, long long *los_total, long long *los_marked)
3679 {
3680         sgen_get_major_collector ()->count_cards (major_total, major_marked);
3681         sgen_los_count_cards (los_total, los_marked);
3682 }
3683
3684 static gboolean world_is_stopped = FALSE;
3685
3686 /* LOCKING: assumes the GC lock is held */
3687 void
3688 sgen_stop_world (int generation)
3689 {
3690         long long major_total = -1, major_marked = -1, los_total = -1, los_marked = -1;
3691
3692         SGEN_ASSERT (0, !world_is_stopped, "Why are we stopping a stopped world?");
3693
3694         binary_protocol_world_stopping (generation, sgen_timestamp (), (gpointer) (gsize) mono_native_thread_id_get ());
3695
3696         sgen_client_stop_world (generation);
3697
3698         world_is_stopped = TRUE;
3699
3700         if (binary_protocol_is_heavy_enabled ())
3701                 count_cards (&major_total, &major_marked, &los_total, &los_marked);
3702         binary_protocol_world_stopped (generation, sgen_timestamp (), major_total, major_marked, los_total, los_marked);
3703 }
3704
3705 /* LOCKING: assumes the GC lock is held */
3706 void
3707 sgen_restart_world (int generation)
3708 {
3709         long long major_total = -1, major_marked = -1, los_total = -1, los_marked = -1;
3710         gint64 stw_time;
3711
3712         SGEN_ASSERT (0, world_is_stopped, "Why are we restarting a running world?");
3713
3714         if (binary_protocol_is_heavy_enabled ())
3715                 count_cards (&major_total, &major_marked, &los_total, &los_marked);
3716         binary_protocol_world_restarting (generation, sgen_timestamp (), major_total, major_marked, los_total, los_marked);
3717
3718         world_is_stopped = FALSE;
3719
3720         sgen_client_restart_world (generation, &stw_time);
3721
3722         binary_protocol_world_restarted (generation, sgen_timestamp ());
3723
3724         if (sgen_client_bridge_need_processing ())
3725                 sgen_client_bridge_processing_finish (generation);
3726
3727         sgen_memgov_collection_end (generation, stw_time);
3728 }
3729
3730 gboolean
3731 sgen_is_world_stopped (void)
3732 {
3733         return world_is_stopped;
3734 }
3735
3736 void
3737 sgen_check_whole_heap_stw (void)
3738 {
3739         sgen_stop_world (0);
3740         sgen_clear_nursery_fragments ();
3741         sgen_check_whole_heap (TRUE);
3742         sgen_restart_world (0);
3743 }
3744
3745 gint64
3746 sgen_timestamp (void)
3747 {
3748         SGEN_TV_DECLARE (timestamp);
3749         SGEN_TV_GETTIME (timestamp);
3750         return SGEN_TV_ELAPSED (sgen_init_timestamp, timestamp);
3751 }
3752
3753 #endif /* HAVE_SGEN_GC */