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