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