fc00b61fb633a61afd5694be4bfe1ab1809fdbe2
[mono.git] / mono / metadata / sgen-gc.c
1 /*
2  * sgen-gc.c: Simple generational GC.
3  *
4  * Author:
5  *      Paolo Molaro (lupus@ximian.com)
6  *  Rodrigo Kumpera (kumpera@gmail.com)
7  *
8  * Copyright 2005-2011 Novell, Inc (http://www.novell.com)
9  * Copyright 2011 Xamarin Inc (http://www.xamarin.com)
10  *
11  * Thread start/stop adapted from Boehm's GC:
12  * Copyright (c) 1994 by Xerox Corporation.  All rights reserved.
13  * Copyright (c) 1996 by Silicon Graphics.  All rights reserved.
14  * Copyright (c) 1998 by Fergus Henderson.  All rights reserved.
15  * Copyright (c) 2000-2004 by Hewlett-Packard Company.  All rights reserved.
16  * Copyright 2001-2003 Ximian, Inc
17  * Copyright 2003-2010 Novell, Inc.
18  * Copyright 2011 Xamarin, Inc.
19  * Copyright (C) 2012 Xamarin Inc
20  *
21  * This library is free software; you can redistribute it and/or
22  * modify it under the terms of the GNU Library General Public
23  * License 2.0 as published by the Free Software Foundation;
24  *
25  * This library is distributed in the hope that it will be useful,
26  * but WITHOUT ANY WARRANTY; without even the implied warranty of
27  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
28  * Library General Public License for more details.
29  *
30  * You should have received a copy of the GNU Library General Public
31  * License 2.0 along with this library; if not, write to the Free
32  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
33  *
34  * Important: allocation provides always zeroed memory, having to do
35  * a memset after allocation is deadly for performance.
36  * Memory usage at startup is currently as follows:
37  * 64 KB pinned space
38  * 64 KB internal space
39  * size of nursery
40  * We should provide a small memory config with half the sizes
41  *
42  * We currently try to make as few mono assumptions as possible:
43  * 1) 2-word header with no GC pointers in it (first vtable, second to store the
44  *    forwarding ptr)
45  * 2) gc descriptor is the second word in the vtable (first word in the class)
46  * 3) 8 byte alignment is the minimum and enough (not true for special structures (SIMD), FIXME)
47  * 4) there is a function to get an object's size and the number of
48  *    elements in an array.
49  * 5) we know the special way bounds are allocated for complex arrays
50  * 6) we know about proxies and how to treat them when domains are unloaded
51  *
52  * Always try to keep stack usage to a minimum: no recursive behaviour
53  * and no large stack allocs.
54  *
55  * General description.
56  * Objects are initially allocated in a nursery using a fast bump-pointer technique.
57  * When the nursery is full we start a nursery collection: this is performed with a
58  * copying GC.
59  * When the old generation is full we start a copying GC of the old generation as well:
60  * this will be changed to mark&sweep with copying when fragmentation becomes to severe
61  * in the future.  Maybe we'll even do both during the same collection like IMMIX.
62  *
63  * The things that complicate this description are:
64  * *) pinned objects: we can't move them so we need to keep track of them
65  * *) no precise info of the thread stacks and registers: we need to be able to
66  *    quickly find the objects that may be referenced conservatively and pin them
67  *    (this makes the first issues more important)
68  * *) large objects are too expensive to be dealt with using copying GC: we handle them
69  *    with mark/sweep during major collections
70  * *) some objects need to not move even if they are small (interned strings, Type handles):
71  *    we use mark/sweep for them, too: they are not allocated in the nursery, but inside
72  *    PinnedChunks regions
73  */
74
75 /*
76  * TODO:
77
78  *) we could have a function pointer in MonoClass to implement
79   customized write barriers for value types
80
81  *) investigate the stuff needed to advance a thread to a GC-safe
82   point (single-stepping, read from unmapped memory etc) and implement it.
83   This would enable us to inline allocations and write barriers, for example,
84   or at least parts of them, like the write barrier checks.
85   We may need this also for handling precise info on stacks, even simple things
86   as having uninitialized data on the stack and having to wait for the prolog
87   to zero it. Not an issue for the last frame that we scan conservatively.
88   We could always not trust the value in the slots anyway.
89
90  *) modify the jit to save info about references in stack locations:
91   this can be done just for locals as a start, so that at least
92   part of the stack is handled precisely.
93
94  *) test/fix endianess issues
95
96  *) Implement a card table as the write barrier instead of remembered
97     sets?  Card tables are not easy to implement with our current
98     memory layout.  We have several different kinds of major heap
99     objects: Small objects in regular blocks, small objects in pinned
100     chunks and LOS objects.  If we just have a pointer we have no way
101     to tell which kind of object it points into, therefore we cannot
102     know where its card table is.  The least we have to do to make
103     this happen is to get rid of write barriers for indirect stores.
104     (See next item)
105
106  *) Get rid of write barriers for indirect stores.  We can do this by
107     telling the GC to wbarrier-register an object once we do an ldloca
108     or ldelema on it, and to unregister it once it's not used anymore
109     (it can only travel downwards on the stack).  The problem with
110     unregistering is that it needs to happen eventually no matter
111     what, even if exceptions are thrown, the thread aborts, etc.
112     Rodrigo suggested that we could do only the registering part and
113     let the collector find out (pessimistically) when it's safe to
114     unregister, namely when the stack pointer of the thread that
115     registered the object is higher than it was when the registering
116     happened.  This might make for a good first implementation to get
117     some data on performance.
118
119  *) Some sort of blacklist support?  Blacklists is a concept from the
120     Boehm GC: if during a conservative scan we find pointers to an
121     area which we might use as heap, we mark that area as unusable, so
122     pointer retention by random pinning pointers is reduced.
123
124  *) experiment with max small object size (very small right now - 2kb,
125     because it's tied to the max freelist size)
126
127   *) add an option to mmap the whole heap in one chunk: it makes for many
128      simplifications in the checks (put the nursery at the top and just use a single
129      check for inclusion/exclusion): the issue this has is that on 32 bit systems it's
130      not flexible (too much of the address space may be used by default or we can't
131      increase the heap as needed) and we'd need a race-free mechanism to return memory
132      back to the system (mprotect(PROT_NONE) will still keep the memory allocated if it
133      was written to, munmap is needed, but the following mmap may not find the same segment
134      free...)
135
136  *) memzero the major fragments after restarting the world and optionally a smaller
137     chunk at a time
138
139  *) investigate having fragment zeroing threads
140
141  *) separate locks for finalization and other minor stuff to reduce
142     lock contention
143
144  *) try a different copying order to improve memory locality
145
146  *) a thread abort after a store but before the write barrier will
147     prevent the write barrier from executing
148
149  *) specialized dynamically generated markers/copiers
150
151  *) Dynamically adjust TLAB size to the number of threads.  If we have
152     too many threads that do allocation, we might need smaller TLABs,
153     and we might get better performance with larger TLABs if we only
154     have a handful of threads.  We could sum up the space left in all
155     assigned TLABs and if that's more than some percentage of the
156     nursery size, reduce the TLAB size.
157
158  *) Explore placing unreachable objects on unused nursery memory.
159         Instead of memset'ng a region to zero, place an int[] covering it.
160         A good place to start is add_nursery_frag. The tricky thing here is
161         placing those objects atomically outside of a collection.
162
163  *) Allocation should use asymmetric Dekker synchronization:
164         http://blogs.oracle.com/dave/resource/Asymmetric-Dekker-Synchronization.txt
165         This should help weak consistency archs.
166  */
167 #include "config.h"
168 #ifdef HAVE_SGEN_GC
169
170 #ifdef __MACH__
171 #undef _XOPEN_SOURCE
172 #define _XOPEN_SOURCE
173 #define _DARWIN_C_SOURCE
174 #endif
175
176 #ifdef HAVE_UNISTD_H
177 #include <unistd.h>
178 #endif
179 #ifdef HAVE_PTHREAD_H
180 #include <pthread.h>
181 #endif
182 #ifdef HAVE_PTHREAD_NP_H
183 #include <pthread_np.h>
184 #endif
185 #ifdef HAVE_SEMAPHORE_H
186 #include <semaphore.h>
187 #endif
188 #include <stdio.h>
189 #include <string.h>
190 #include <errno.h>
191 #include <assert.h>
192
193 #include "metadata/sgen-gc.h"
194 #include "metadata/metadata-internals.h"
195 #include "metadata/class-internals.h"
196 #include "metadata/gc-internal.h"
197 #include "metadata/object-internals.h"
198 #include "metadata/threads.h"
199 #include "metadata/sgen-cardtable.h"
200 #include "metadata/sgen-protocol.h"
201 #include "metadata/sgen-archdep.h"
202 #include "metadata/sgen-bridge.h"
203 #include "metadata/sgen-memory-governor.h"
204 #include "metadata/sgen-hash-table.h"
205 #include "metadata/mono-gc.h"
206 #include "metadata/method-builder.h"
207 #include "metadata/profiler-private.h"
208 #include "metadata/mempool-internals.h"
209 #include "metadata/marshal.h"
210 #include "metadata/runtime.h"
211 #include "metadata/sgen-cardtable.h"
212 #include "metadata/sgen-pinning.h"
213 #include "metadata/sgen-workers.h"
214 #include "metadata/sgen-client.h"
215 #include "utils/mono-mmap.h"
216 #include "utils/mono-time.h"
217 #include "utils/mono-semaphore.h"
218 #include "utils/mono-counters.h"
219 #include "utils/mono-proclib.h"
220 #include "utils/mono-memory-model.h"
221 #include "utils/mono-logger-internal.h"
222 #include "utils/dtrace.h"
223
224 #include <mono/utils/mono-logger-internal.h>
225 #include <mono/utils/memcheck.h>
226
227 #if defined(__MACH__)
228 #include "utils/mach-support.h"
229 #endif
230
231 #define OPDEF(a,b,c,d,e,f,g,h,i,j) \
232         a = i,
233
234 enum {
235 #include "mono/cil/opcode.def"
236         CEE_LAST
237 };
238
239 #undef OPDEF
240
241 #undef pthread_create
242 #undef pthread_join
243 #undef pthread_detach
244
245 /*
246  * ######################################################################
247  * ########  Types and constants used by the GC.
248  * ######################################################################
249  */
250
251 /* 0 means not initialized, 1 is initialized, -1 means in progress */
252 static int gc_initialized = 0;
253 /* If set, check if we need to do something every X allocations */
254 gboolean has_per_allocation_action;
255 /* If set, do a heap check every X allocation */
256 guint32 verify_before_allocs = 0;
257 /* If set, do a minor collection before every X allocation */
258 guint32 collect_before_allocs = 0;
259 /* If set, do a whole heap check before each collection */
260 static gboolean whole_heap_check_before_collection = FALSE;
261 /* If set, do a heap consistency check before each minor collection */
262 static gboolean consistency_check_at_minor_collection = FALSE;
263 /* If set, do a mod union consistency check before each finishing collection pause */
264 static gboolean mod_union_consistency_check = FALSE;
265 /* If set, check whether mark bits are consistent after major collections */
266 static gboolean check_mark_bits_after_major_collection = FALSE;
267 /* If set, check that all nursery objects are pinned/not pinned, depending on context */
268 static gboolean check_nursery_objects_pinned = FALSE;
269 /* If set, do a few checks when the concurrent collector is used */
270 static gboolean do_concurrent_checks = FALSE;
271 /* If set, mark stacks conservatively, even if precise marking is possible */
272 static gboolean conservative_stack_mark = FALSE;
273 /* If set, do a plausibility check on the scan_starts before and after
274    each collection */
275 static gboolean do_scan_starts_check = FALSE;
276
277 /*
278  * If the major collector is concurrent and this is FALSE, we will
279  * never initiate a synchronous major collection, unless requested via
280  * GC.Collect().
281  */
282 static gboolean allow_synchronous_major = TRUE;
283 static gboolean disable_minor_collections = FALSE;
284 static gboolean disable_major_collections = FALSE;
285 static gboolean do_verify_nursery = FALSE;
286 static gboolean do_dump_nursery_content = FALSE;
287 static gboolean enable_nursery_canaries = FALSE;
288
289 #ifdef HEAVY_STATISTICS
290 guint64 stat_objects_alloced_degraded = 0;
291 guint64 stat_bytes_alloced_degraded = 0;
292
293 guint64 stat_copy_object_called_nursery = 0;
294 guint64 stat_objects_copied_nursery = 0;
295 guint64 stat_copy_object_called_major = 0;
296 guint64 stat_objects_copied_major = 0;
297
298 guint64 stat_scan_object_called_nursery = 0;
299 guint64 stat_scan_object_called_major = 0;
300
301 guint64 stat_slots_allocated_in_vain;
302
303 guint64 stat_nursery_copy_object_failed_from_space = 0;
304 guint64 stat_nursery_copy_object_failed_forwarded = 0;
305 guint64 stat_nursery_copy_object_failed_pinned = 0;
306 guint64 stat_nursery_copy_object_failed_to_space = 0;
307
308 static int stat_wbarrier_add_to_global_remset = 0;
309 static int stat_wbarrier_set_field = 0;
310 static int stat_wbarrier_set_arrayref = 0;
311 static int stat_wbarrier_arrayref_copy = 0;
312 static int stat_wbarrier_generic_store = 0;
313 static int stat_wbarrier_generic_store_atomic = 0;
314 static int stat_wbarrier_set_root = 0;
315 static int stat_wbarrier_value_copy = 0;
316 static int stat_wbarrier_object_copy = 0;
317 #endif
318
319 static guint64 stat_pinned_objects = 0;
320
321 static guint64 time_minor_pre_collection_fragment_clear = 0;
322 static guint64 time_minor_pinning = 0;
323 static guint64 time_minor_scan_remsets = 0;
324 static guint64 time_minor_scan_pinned = 0;
325 static guint64 time_minor_scan_roots = 0;
326 static guint64 time_minor_finish_gray_stack = 0;
327 static guint64 time_minor_fragment_creation = 0;
328
329 static guint64 time_major_pre_collection_fragment_clear = 0;
330 static guint64 time_major_pinning = 0;
331 static guint64 time_major_scan_pinned = 0;
332 static guint64 time_major_scan_roots = 0;
333 static guint64 time_major_scan_mod_union = 0;
334 static guint64 time_major_finish_gray_stack = 0;
335 static guint64 time_major_free_bigobjs = 0;
336 static guint64 time_major_los_sweep = 0;
337 static guint64 time_major_sweep = 0;
338 static guint64 time_major_fragment_creation = 0;
339
340 static guint64 time_max = 0;
341
342 static SGEN_TV_DECLARE (time_major_conc_collection_start);
343 static SGEN_TV_DECLARE (time_major_conc_collection_end);
344
345 static SGEN_TV_DECLARE (last_minor_collection_start_tv);
346 static SGEN_TV_DECLARE (last_minor_collection_end_tv);
347
348 int gc_debug_level = 0;
349 FILE* gc_debug_file;
350
351 /*
352 void
353 mono_gc_flush_info (void)
354 {
355         fflush (gc_debug_file);
356 }
357 */
358
359 #define TV_DECLARE SGEN_TV_DECLARE
360 #define TV_GETTIME SGEN_TV_GETTIME
361 #define TV_ELAPSED SGEN_TV_ELAPSED
362
363 SGEN_TV_DECLARE (sgen_init_timestamp);
364
365 NurseryClearPolicy nursery_clear_policy = CLEAR_AT_TLAB_CREATION;
366
367 #define object_is_forwarded     SGEN_OBJECT_IS_FORWARDED
368 #define object_is_pinned        SGEN_OBJECT_IS_PINNED
369 #define pin_object              SGEN_PIN_OBJECT
370
371 #define ptr_in_nursery sgen_ptr_in_nursery
372
373 #define LOAD_VTABLE     SGEN_LOAD_VTABLE
374
375 static const char*
376 safe_name (void* obj)
377 {
378         MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (obj);
379         return vt->klass->name;
380 }
381
382 gboolean
383 nursery_canaries_enabled (void)
384 {
385         return enable_nursery_canaries;
386 }
387
388 #define safe_object_get_size    sgen_safe_object_get_size
389
390 const char*
391 sgen_safe_name (void* obj)
392 {
393         return safe_name (obj);
394 }
395
396 /*
397  * ######################################################################
398  * ########  Global data.
399  * ######################################################################
400  */
401 LOCK_DECLARE (gc_mutex);
402 gboolean sgen_try_free_some_memory;
403
404 #define SCAN_START_SIZE SGEN_SCAN_START_SIZE
405
406 static mword pagesize = 4096;
407 size_t degraded_mode = 0;
408
409 static mword bytes_pinned_from_failed_allocation = 0;
410
411 GCMemSection *nursery_section = NULL;
412 static volatile mword lowest_heap_address = ~(mword)0;
413 static volatile mword highest_heap_address = 0;
414
415 LOCK_DECLARE (sgen_interruption_mutex);
416
417 typedef struct _FinalizeReadyEntry FinalizeReadyEntry;
418 struct _FinalizeReadyEntry {
419         FinalizeReadyEntry *next;
420         void *object;
421 };
422
423 int current_collection_generation = -1;
424 volatile gboolean concurrent_collection_in_progress = FALSE;
425
426 /* objects that are ready to be finalized */
427 static FinalizeReadyEntry *fin_ready_list = NULL;
428 static FinalizeReadyEntry *critical_fin_list = NULL;
429
430 /* registered roots: the key to the hash is the root start address */
431 /* 
432  * Different kinds of roots are kept separate to speed up pin_from_roots () for example.
433  */
434 SgenHashTable roots_hash [ROOT_TYPE_NUM] = {
435         SGEN_HASH_TABLE_INIT (INTERNAL_MEM_ROOTS_TABLE, INTERNAL_MEM_ROOT_RECORD, sizeof (RootRecord), mono_aligned_addr_hash, NULL),
436         SGEN_HASH_TABLE_INIT (INTERNAL_MEM_ROOTS_TABLE, INTERNAL_MEM_ROOT_RECORD, sizeof (RootRecord), mono_aligned_addr_hash, NULL),
437         SGEN_HASH_TABLE_INIT (INTERNAL_MEM_ROOTS_TABLE, INTERNAL_MEM_ROOT_RECORD, sizeof (RootRecord), mono_aligned_addr_hash, NULL)
438 };
439 static mword roots_size = 0; /* amount of memory in the root set */
440
441 #define GC_ROOT_NUM 32
442 typedef struct {
443         int count;              /* must be the first field */
444         void *objects [GC_ROOT_NUM];
445         int root_types [GC_ROOT_NUM];
446         uintptr_t extra_info [GC_ROOT_NUM];
447 } GCRootReport;
448
449 static void
450 notify_gc_roots (GCRootReport *report)
451 {
452         if (!report->count)
453                 return;
454         mono_profiler_gc_roots (report->count, report->objects, report->root_types, report->extra_info);
455         report->count = 0;
456 }
457
458 static void
459 add_profile_gc_root (GCRootReport *report, void *object, int rtype, uintptr_t extra_info)
460 {
461         if (report->count == GC_ROOT_NUM)
462                 notify_gc_roots (report);
463         report->objects [report->count] = object;
464         report->root_types [report->count] = rtype;
465         report->extra_info [report->count++] = (uintptr_t)((MonoVTable*)LOAD_VTABLE (object))->klass;
466 }
467
468 MonoNativeTlsKey thread_info_key;
469
470 #ifdef HAVE_KW_THREAD
471 __thread SgenThreadInfo *sgen_thread_info;
472 __thread char *stack_end;
473 #endif
474
475 /* The size of a TLAB */
476 /* The bigger the value, the less often we have to go to the slow path to allocate a new 
477  * one, but the more space is wasted by threads not allocating much memory.
478  * FIXME: Tune this.
479  * FIXME: Make this self-tuning for each thread.
480  */
481 guint32 tlab_size = (1024 * 4);
482
483 #define MAX_SMALL_OBJ_SIZE      SGEN_MAX_SMALL_OBJ_SIZE
484
485 /* Functions supplied by the runtime to be called by the GC */
486 static MonoGCCallbacks gc_callbacks;
487
488 #define ALLOC_ALIGN             SGEN_ALLOC_ALIGN
489
490 #define ALIGN_UP                SGEN_ALIGN_UP
491
492 #define MOVED_OBJECTS_NUM 64
493 static void *moved_objects [MOVED_OBJECTS_NUM];
494 static int moved_objects_idx = 0;
495
496 #ifdef SGEN_DEBUG_INTERNAL_ALLOC
497 MonoNativeThreadId main_gc_thread = NULL;
498 #endif
499
500 /*Object was pinned during the current collection*/
501 static mword objects_pinned;
502
503 /*
504  * ######################################################################
505  * ########  Macros and function declarations.
506  * ######################################################################
507  */
508
509 typedef SgenGrayQueue GrayQueue;
510
511 /* forward declarations */
512 static void scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise, ScanCopyContext ctx);
513 static void scan_from_registered_roots (char *addr_start, char *addr_end, int root_type, ScanCopyContext ctx);
514 static void scan_finalizer_entries (FinalizeReadyEntry *list, ScanCopyContext ctx);
515 static void report_finalizer_roots (void);
516 static void report_registered_roots (void);
517
518 static void pin_from_roots (void *start_nursery, void *end_nursery, ScanCopyContext ctx);
519 static void finish_gray_stack (int generation, ScanCopyContext ctx);
520
521 void mono_gc_scan_for_specific_ref (MonoObject *key, gboolean precise);
522
523
524 static void init_stats (void);
525
526 SgenMajorCollector major_collector;
527 SgenMinorCollector sgen_minor_collector;
528 /* FIXME: get rid of this */
529 static GrayQueue gray_queue;
530
531 static SgenRememberedSet remset;
532
533 /* The gray queue to use from the main collection thread. */
534 #define WORKERS_DISTRIBUTE_GRAY_QUEUE   (&gray_queue)
535
536 /*
537  * The gray queue a worker job must use.  If we're not parallel or
538  * concurrent, we use the main gray queue.
539  */
540 static SgenGrayQueue*
541 sgen_workers_get_job_gray_queue (WorkerData *worker_data)
542 {
543         return worker_data ? &worker_data->private_gray_queue : WORKERS_DISTRIBUTE_GRAY_QUEUE;
544 }
545
546 static void
547 gray_queue_redirect (SgenGrayQueue *queue)
548 {
549         gboolean wake = FALSE;
550
551         for (;;) {
552                 GrayQueueSection *section = sgen_gray_object_dequeue_section (queue);
553                 if (!section)
554                         break;
555                 sgen_section_gray_queue_enqueue (queue->alloc_prepare_data, section);
556                 wake = TRUE;
557         }
558
559         if (wake) {
560                 g_assert (concurrent_collection_in_progress);
561                 sgen_workers_ensure_awake ();
562         }
563 }
564
565 static void
566 gray_queue_enable_redirect (SgenGrayQueue *queue)
567 {
568         if (!concurrent_collection_in_progress)
569                 return;
570
571         sgen_gray_queue_set_alloc_prepare (queue, gray_queue_redirect, sgen_workers_get_distribute_section_gray_queue ());
572         gray_queue_redirect (queue);
573 }
574
575 void
576 sgen_scan_area_with_callback (char *start, char *end, IterateObjectCallbackFunc callback, void *data, gboolean allow_flags)
577 {
578         MonoVTable *array_fill_vtable = sgen_client_get_array_fill_vtable ();
579
580         while (start < end) {
581                 size_t size;
582                 char *obj;
583
584                 if (!*(void**)start) {
585                         start += sizeof (void*); /* should be ALLOC_ALIGN, really */
586                         continue;
587                 }
588
589                 if (allow_flags) {
590                         if (!(obj = SGEN_OBJECT_IS_FORWARDED (start)))
591                                 obj = start;
592                 } else {
593                         obj = start;
594                 }
595
596                 if ((MonoVTable*)SGEN_LOAD_VTABLE (obj) != array_fill_vtable) {
597                         CHECK_CANARY_FOR_OBJECT (obj);
598                         size = ALIGN_UP (safe_object_get_size ((MonoObject*)obj));
599                         callback (obj, size, data);
600                         CANARIFY_SIZE (size);
601                 } else {
602                         size = ALIGN_UP (safe_object_get_size ((MonoObject*)obj));
603                 }
604
605                 start += size;
606         }
607 }
608
609 /*
610  * sgen_add_to_global_remset:
611  *
612  *   The global remset contains locations which point into newspace after
613  * a minor collection. This can happen if the objects they point to are pinned.
614  *
615  * LOCKING: If called from a parallel collector, the global remset
616  * lock must be held.  For serial collectors that is not necessary.
617  */
618 void
619 sgen_add_to_global_remset (gpointer ptr, gpointer obj)
620 {
621         SGEN_ASSERT (5, sgen_ptr_in_nursery (obj), "Target pointer of global remset must be in the nursery");
622
623         HEAVY_STAT (++stat_wbarrier_add_to_global_remset);
624
625         if (!major_collector.is_concurrent) {
626                 SGEN_ASSERT (5, current_collection_generation != -1, "Global remsets can only be added during collections");
627         } else {
628                 if (current_collection_generation == -1)
629                         SGEN_ASSERT (5, sgen_concurrent_collection_in_progress (), "Global remsets outside of collection pauses can only be added by the concurrent collector");
630         }
631
632         if (!object_is_pinned (obj))
633                 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");
634         else if (sgen_cement_lookup_or_register (obj))
635                 return;
636
637         remset.record_pointer (ptr);
638
639         sgen_pin_stats_register_global_remset (obj);
640
641         SGEN_LOG (8, "Adding global remset for %p", ptr);
642         binary_protocol_global_remset (ptr, obj, (gpointer)SGEN_LOAD_VTABLE (obj));
643
644
645 #ifdef ENABLE_DTRACE
646         if (G_UNLIKELY (MONO_GC_GLOBAL_REMSET_ADD_ENABLED ())) {
647                 MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (obj);
648                 MONO_GC_GLOBAL_REMSET_ADD ((mword)ptr, (mword)obj, sgen_safe_object_get_size (obj),
649                                 vt->klass->name_space, vt->klass->name);
650         }
651 #endif
652 }
653
654 /*
655  * sgen_drain_gray_stack:
656  *
657  *   Scan objects in the gray stack until the stack is empty. This should be called
658  * frequently after each object is copied, to achieve better locality and cache
659  * usage.
660  *
661  * max_objs is the maximum number of objects to scan, or -1 to scan until the stack is
662  * empty.
663  */
664 gboolean
665 sgen_drain_gray_stack (int max_objs, ScanCopyContext ctx)
666 {
667         ScanObjectFunc scan_func = ctx.ops->scan_object;
668         GrayQueue *queue = ctx.queue;
669
670         if (current_collection_generation == GENERATION_OLD && major_collector.drain_gray_stack)
671                 return major_collector.drain_gray_stack (ctx);
672
673         do {
674                 int i;
675                 for (i = 0; i != max_objs; ++i) {
676                         char *obj;
677                         mword desc;
678                         GRAY_OBJECT_DEQUEUE (queue, &obj, &desc);
679                         if (!obj)
680                                 return TRUE;
681                         SGEN_LOG (9, "Precise gray object scan %p (%s)", obj, safe_name (obj));
682                         scan_func (obj, desc, queue);
683                 }
684         } while (max_objs < 0);
685         return FALSE;
686 }
687
688 /*
689  * Addresses in the pin queue are already sorted. This function finds
690  * the object header for each address and pins the object. The
691  * addresses must be inside the nursery section.  The (start of the)
692  * address array is overwritten with the addresses of the actually
693  * pinned objects.  Return the number of pinned objects.
694  */
695 static int
696 pin_objects_from_nursery_pin_queue (gboolean do_scan_objects, ScanCopyContext ctx)
697 {
698         GCMemSection *section = nursery_section;
699         void **start =  sgen_pinning_get_entry (section->pin_queue_first_entry);
700         void **end = sgen_pinning_get_entry (section->pin_queue_last_entry);
701         void *start_nursery = section->data;
702         void *end_nursery = section->next_data;
703         void *last = NULL;
704         int count = 0;
705         void *search_start;
706         void *addr;
707         void *pinning_front = start_nursery;
708         size_t idx;
709         void **definitely_pinned = start;
710         ScanObjectFunc scan_func = ctx.ops->scan_object;
711         SgenGrayQueue *queue = ctx.queue;
712
713         sgen_nursery_allocator_prepare_for_pinning ();
714
715         while (start < end) {
716                 void *obj_to_pin = NULL;
717                 size_t obj_to_pin_size = 0;
718                 mword desc;
719
720                 addr = *start;
721
722                 SGEN_ASSERT (0, addr >= start_nursery && addr < end_nursery, "Potential pinning address out of range");
723                 SGEN_ASSERT (0, addr >= last, "Pin queue not sorted");
724
725                 if (addr == last) {
726                         ++start;
727                         continue;
728                 }
729
730                 SGEN_LOG (5, "Considering pinning addr %p", addr);
731                 /* We've already processed everything up to pinning_front. */
732                 if (addr < pinning_front) {
733                         start++;
734                         continue;
735                 }
736
737                 /*
738                  * Find the closest scan start <= addr.  We might search backward in the
739                  * scan_starts array because entries might be NULL.  In the worst case we
740                  * start at start_nursery.
741                  */
742                 idx = ((char*)addr - (char*)section->data) / SCAN_START_SIZE;
743                 SGEN_ASSERT (0, idx < section->num_scan_start, "Scan start index out of range");
744                 search_start = (void*)section->scan_starts [idx];
745                 if (!search_start || search_start > addr) {
746                         while (idx) {
747                                 --idx;
748                                 search_start = section->scan_starts [idx];
749                                 if (search_start && search_start <= addr)
750                                         break;
751                         }
752                         if (!search_start || search_start > addr)
753                                 search_start = start_nursery;
754                 }
755
756                 /*
757                  * If the pinning front is closer than the scan start we found, start
758                  * searching at the front.
759                  */
760                 if (search_start < pinning_front)
761                         search_start = pinning_front;
762
763                 /*
764                  * Now addr should be in an object a short distance from search_start.
765                  *
766                  * search_start must point to zeroed mem or point to an object.
767                  */
768                 do {
769                         size_t obj_size, canarified_obj_size;
770
771                         /* Skip zeros. */
772                         if (!*(void**)search_start) {
773                                 search_start = (void*)ALIGN_UP ((mword)search_start + sizeof (gpointer));
774                                 /* The loop condition makes sure we don't overrun addr. */
775                                 continue;
776                         }
777
778                         canarified_obj_size = obj_size = ALIGN_UP (safe_object_get_size ((MonoObject*)search_start));
779
780                         /*
781                          * Filler arrays are marked by an invalid sync word.  We don't
782                          * consider them for pinning.  They are not delimited by canaries,
783                          * either.
784                          */
785                         if (((MonoObject*)search_start)->synchronisation != GINT_TO_POINTER (-1)) {
786                                 CHECK_CANARY_FOR_OBJECT (search_start);
787                                 CANARIFY_SIZE (canarified_obj_size);
788
789                                 if (addr >= search_start && (char*)addr < (char*)search_start + obj_size) {
790                                         /* This is the object we're looking for. */
791                                         obj_to_pin = search_start;
792                                         obj_to_pin_size = canarified_obj_size;
793                                         break;
794                                 }
795                         }
796
797                         /* Skip to the next object */
798                         search_start = (void*)((char*)search_start + canarified_obj_size);
799                 } while (search_start <= addr);
800
801                 /* We've searched past the address we were looking for. */
802                 if (!obj_to_pin) {
803                         pinning_front = search_start;
804                         goto next_pin_queue_entry;
805                 }
806
807                 /*
808                  * We've found an object to pin.  It might still be a dummy array, but we
809                  * can advance the pinning front in any case.
810                  */
811                 pinning_front = (char*)obj_to_pin + obj_to_pin_size;
812
813                 /*
814                  * If this is a dummy array marking the beginning of a nursery
815                  * fragment, we don't pin it.
816                  */
817                 if (((MonoObject*)obj_to_pin)->synchronisation == GINT_TO_POINTER (-1))
818                         goto next_pin_queue_entry;
819
820                 /*
821                  * Finally - pin the object!
822                  */
823                 desc = sgen_obj_get_descriptor_safe (obj_to_pin);
824                 if (do_scan_objects) {
825                         scan_func (obj_to_pin, desc, queue);
826                 } else {
827                         SGEN_LOG (4, "Pinned object %p, vtable %p (%s), count %d\n",
828                                         obj_to_pin, *(void**)obj_to_pin, safe_name (obj_to_pin), count);
829                         binary_protocol_pin (obj_to_pin,
830                                         (gpointer)LOAD_VTABLE (obj_to_pin),
831                                         safe_object_get_size (obj_to_pin));
832
833 #ifdef ENABLE_DTRACE
834                         if (G_UNLIKELY (MONO_GC_OBJ_PINNED_ENABLED ())) {
835                                 int gen = sgen_ptr_in_nursery (obj_to_pin) ? GENERATION_NURSERY : GENERATION_OLD;
836                                 MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (obj_to_pin);
837                                 MONO_GC_OBJ_PINNED ((mword)obj_to_pin,
838                                                 sgen_safe_object_get_size (obj_to_pin),
839                                                 vt->klass->name_space, vt->klass->name, gen);
840                         }
841 #endif
842
843                         pin_object (obj_to_pin);
844                         GRAY_OBJECT_ENQUEUE (queue, obj_to_pin, desc);
845                         sgen_pin_stats_register_object (obj_to_pin, obj_to_pin_size);
846                         definitely_pinned [count] = obj_to_pin;
847                         count++;
848                 }
849
850         next_pin_queue_entry:
851                 last = addr;
852                 ++start;
853         }
854         //printf ("effective pinned: %d (at the end: %d)\n", count, (char*)end_nursery - (char*)last);
855         if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS) {
856                 GCRootReport report;
857                 report.count = 0;
858                 for (idx = 0; idx < count; ++idx)
859                         add_profile_gc_root (&report, definitely_pinned [idx], MONO_PROFILE_GC_ROOT_PINNING | MONO_PROFILE_GC_ROOT_MISC, 0);
860                 notify_gc_roots (&report);
861         }
862         stat_pinned_objects += count;
863         return count;
864 }
865
866 static void
867 pin_objects_in_nursery (gboolean do_scan_objects, ScanCopyContext ctx)
868 {
869         size_t reduced_to;
870
871         if (nursery_section->pin_queue_first_entry == nursery_section->pin_queue_last_entry)
872                 return;
873
874         reduced_to = pin_objects_from_nursery_pin_queue (do_scan_objects, ctx);
875         nursery_section->pin_queue_last_entry = nursery_section->pin_queue_first_entry + reduced_to;
876 }
877
878 /*
879  * This function is only ever called (via `collector_pin_object()` in `sgen-copy-object.h`)
880  * when we can't promote an object because we're out of memory.
881  */
882 void
883 sgen_pin_object (void *object, GrayQueue *queue)
884 {
885         /*
886          * All pinned objects are assumed to have been staged, so we need to stage as well.
887          * Also, the count of staged objects shows that "late pinning" happened.
888          */
889         sgen_pin_stage_ptr (object);
890
891         SGEN_PIN_OBJECT (object);
892         binary_protocol_pin (object, (gpointer)LOAD_VTABLE (object), safe_object_get_size (object));
893
894         ++objects_pinned;
895         sgen_pin_stats_register_object (object, safe_object_get_size (object));
896
897         GRAY_OBJECT_ENQUEUE (queue, object, sgen_obj_get_descriptor_safe (object));
898
899 #ifdef ENABLE_DTRACE
900         if (G_UNLIKELY (MONO_GC_OBJ_PINNED_ENABLED ())) {
901                 int gen = sgen_ptr_in_nursery (object) ? GENERATION_NURSERY : GENERATION_OLD;
902                 MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (object);
903                 MONO_GC_OBJ_PINNED ((mword)object, sgen_safe_object_get_size (object), vt->klass->name_space, vt->klass->name, gen);
904         }
905 #endif
906 }
907
908 /* Sort the addresses in array in increasing order.
909  * Done using a by-the book heap sort. Which has decent and stable performance, is pretty cache efficient.
910  */
911 void
912 sgen_sort_addresses (void **array, size_t size)
913 {
914         size_t i;
915         void *tmp;
916
917         for (i = 1; i < size; ++i) {
918                 size_t child = i;
919                 while (child > 0) {
920                         size_t parent = (child - 1) / 2;
921
922                         if (array [parent] >= array [child])
923                                 break;
924
925                         tmp = array [parent];
926                         array [parent] = array [child];
927                         array [child] = tmp;
928
929                         child = parent;
930                 }
931         }
932
933         for (i = size - 1; i > 0; --i) {
934                 size_t end, root;
935                 tmp = array [i];
936                 array [i] = array [0];
937                 array [0] = tmp;
938
939                 end = i - 1;
940                 root = 0;
941
942                 while (root * 2 + 1 <= end) {
943                         size_t child = root * 2 + 1;
944
945                         if (child < end && array [child] < array [child + 1])
946                                 ++child;
947                         if (array [root] >= array [child])
948                                 break;
949
950                         tmp = array [root];
951                         array [root] = array [child];
952                         array [child] = tmp;
953
954                         root = child;
955                 }
956         }
957 }
958
959 /* 
960  * Scan the memory between start and end and queue values which could be pointers
961  * to the area between start_nursery and end_nursery for later consideration.
962  * Typically used for thread stacks.
963  */
964 static void
965 conservatively_pin_objects_from (void **start, void **end, void *start_nursery, void *end_nursery, int pin_type)
966 {
967         int count = 0;
968
969 #ifdef VALGRIND_MAKE_MEM_DEFINED_IF_ADDRESSABLE
970         VALGRIND_MAKE_MEM_DEFINED_IF_ADDRESSABLE (start, (char*)end - (char*)start);
971 #endif
972
973         while (start < end) {
974                 if (*start >= start_nursery && *start < end_nursery) {
975                         /*
976                          * *start can point to the middle of an object
977                          * note: should we handle pointing at the end of an object?
978                          * pinning in C# code disallows pointing at the end of an object
979                          * but there is some small chance that an optimizing C compiler
980                          * may keep the only reference to an object by pointing
981                          * at the end of it. We ignore this small chance for now.
982                          * Pointers to the end of an object are indistinguishable
983                          * from pointers to the start of the next object in memory
984                          * so if we allow that we'd need to pin two objects...
985                          * We queue the pointer in an array, the
986                          * array will then be sorted and uniqued. This way
987                          * we can coalesce several pinning pointers and it should
988                          * be faster since we'd do a memory scan with increasing
989                          * addresses. Note: we can align the address to the allocation
990                          * alignment, so the unique process is more effective.
991                          */
992                         mword addr = (mword)*start;
993                         addr &= ~(ALLOC_ALIGN - 1);
994                         if (addr >= (mword)start_nursery && addr < (mword)end_nursery) {
995                                 SGEN_LOG (6, "Pinning address %p from %p", (void*)addr, start);
996                                 sgen_pin_stage_ptr ((void*)addr);
997                                 binary_protocol_pin_stage (start, (void*)addr);
998                                 count++;
999                         }
1000
1001                         /*
1002                          * FIXME: It seems we're registering objects from all over the heap
1003                          * (at least from the nursery and the LOS), but we're only
1004                          * registering pinned addresses in the nursery.  What's up with
1005                          * that?
1006                          *
1007                          * Also, why wouldn't we register addresses once the pinning queue
1008                          * is sorted and uniqued?
1009                          */
1010                         if (ptr_in_nursery ((void*)addr))
1011                                 sgen_pin_stats_register_address ((char*)addr, pin_type);
1012                 }
1013                 start++;
1014         }
1015         if (count)
1016                 SGEN_LOG (7, "found %d potential pinned heap pointers", count);
1017 }
1018
1019 /*
1020  * The first thing we do in a collection is to identify pinned objects.
1021  * This function considers all the areas of memory that need to be
1022  * conservatively scanned.
1023  */
1024 static void
1025 pin_from_roots (void *start_nursery, void *end_nursery, ScanCopyContext ctx)
1026 {
1027         void **start_root;
1028         RootRecord *root;
1029         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);
1030         /* objects pinned from the API are inside these roots */
1031         SGEN_HASH_TABLE_FOREACH (&roots_hash [ROOT_TYPE_PINNED], start_root, root) {
1032                 SGEN_LOG (6, "Pinned roots %p-%p", start_root, root->end_root);
1033                 conservatively_pin_objects_from (start_root, (void**)root->end_root, start_nursery, end_nursery, PIN_TYPE_OTHER);
1034         } SGEN_HASH_TABLE_FOREACH_END;
1035         /* now deal with the thread stacks
1036          * in the future we should be able to conservatively scan only:
1037          * *) the cpu registers
1038          * *) the unmanaged stack frames
1039          * *) the _last_ managed stack frame
1040          * *) pointers slots in managed frames
1041          */
1042         scan_thread_data (start_nursery, end_nursery, FALSE, ctx);
1043 }
1044
1045 static void
1046 unpin_objects_from_queue (SgenGrayQueue *queue)
1047 {
1048         for (;;) {
1049                 char *addr;
1050                 mword desc;
1051                 GRAY_OBJECT_DEQUEUE (queue, &addr, &desc);
1052                 if (!addr)
1053                         break;
1054                 g_assert (SGEN_OBJECT_IS_PINNED (addr));
1055                 SGEN_UNPIN_OBJECT (addr);
1056         }
1057 }
1058
1059 static void
1060 single_arg_user_copy_or_mark (void **obj, void *gc_data)
1061 {
1062         ScanCopyContext *ctx = gc_data;
1063         ctx->ops->copy_or_mark_object (obj, ctx->queue);
1064 }
1065
1066 /*
1067  * The memory area from start_root to end_root contains pointers to objects.
1068  * Their position is precisely described by @desc (this means that the pointer
1069  * can be either NULL or the pointer to the start of an object).
1070  * This functions copies them to to_space updates them.
1071  *
1072  * This function is not thread-safe!
1073  */
1074 static void
1075 precisely_scan_objects_from (void** start_root, void** end_root, char* n_start, char *n_end, mword desc, ScanCopyContext ctx)
1076 {
1077         CopyOrMarkObjectFunc copy_func = ctx.ops->copy_or_mark_object;
1078         SgenGrayQueue *queue = ctx.queue;
1079
1080         switch (desc & ROOT_DESC_TYPE_MASK) {
1081         case ROOT_DESC_BITMAP:
1082                 desc >>= ROOT_DESC_TYPE_SHIFT;
1083                 while (desc) {
1084                         if ((desc & 1) && *start_root) {
1085                                 copy_func (start_root, queue);
1086                                 SGEN_LOG (9, "Overwrote root at %p with %p", start_root, *start_root);
1087                         }
1088                         desc >>= 1;
1089                         start_root++;
1090                 }
1091                 return;
1092         case ROOT_DESC_COMPLEX: {
1093                 gsize *bitmap_data = sgen_get_complex_descriptor_bitmap (desc);
1094                 gsize bwords = (*bitmap_data) - 1;
1095                 void **start_run = start_root;
1096                 bitmap_data++;
1097                 while (bwords-- > 0) {
1098                         gsize bmap = *bitmap_data++;
1099                         void **objptr = start_run;
1100                         while (bmap) {
1101                                 if ((bmap & 1) && *objptr) {
1102                                         copy_func (objptr, queue);
1103                                         SGEN_LOG (9, "Overwrote root at %p with %p", objptr, *objptr);
1104                                 }
1105                                 bmap >>= 1;
1106                                 ++objptr;
1107                         }
1108                         start_run += GC_BITS_PER_WORD;
1109                 }
1110                 break;
1111         }
1112         case ROOT_DESC_USER: {
1113                 MonoGCRootMarkFunc marker = sgen_get_user_descriptor_func (desc);
1114                 marker (start_root, single_arg_user_copy_or_mark, &ctx);
1115                 break;
1116         }
1117         case ROOT_DESC_RUN_LEN:
1118                 g_assert_not_reached ();
1119         default:
1120                 g_assert_not_reached ();
1121         }
1122 }
1123
1124 static void
1125 reset_heap_boundaries (void)
1126 {
1127         lowest_heap_address = ~(mword)0;
1128         highest_heap_address = 0;
1129 }
1130
1131 void
1132 sgen_update_heap_boundaries (mword low, mword high)
1133 {
1134         mword old;
1135
1136         do {
1137                 old = lowest_heap_address;
1138                 if (low >= old)
1139                         break;
1140         } while (SGEN_CAS_PTR ((gpointer*)&lowest_heap_address, (gpointer)low, (gpointer)old) != (gpointer)old);
1141
1142         do {
1143                 old = highest_heap_address;
1144                 if (high <= old)
1145                         break;
1146         } while (SGEN_CAS_PTR ((gpointer*)&highest_heap_address, (gpointer)high, (gpointer)old) != (gpointer)old);
1147 }
1148
1149 /*
1150  * Allocate and setup the data structures needed to be able to allocate objects
1151  * in the nursery. The nursery is stored in nursery_section.
1152  */
1153 static void
1154 alloc_nursery (void)
1155 {
1156         GCMemSection *section;
1157         char *data;
1158         size_t scan_starts;
1159         size_t alloc_size;
1160
1161         if (nursery_section)
1162                 return;
1163         SGEN_LOG (2, "Allocating nursery size: %zu", (size_t)sgen_nursery_size);
1164         /* later we will alloc a larger area for the nursery but only activate
1165          * what we need. The rest will be used as expansion if we have too many pinned
1166          * objects in the existing nursery.
1167          */
1168         /* FIXME: handle OOM */
1169         section = sgen_alloc_internal (INTERNAL_MEM_SECTION);
1170
1171         alloc_size = sgen_nursery_size;
1172
1173         /* If there isn't enough space even for the nursery we should simply abort. */
1174         g_assert (sgen_memgov_try_alloc_space (alloc_size, SPACE_NURSERY));
1175
1176         data = major_collector.alloc_heap (alloc_size, alloc_size, DEFAULT_NURSERY_BITS);
1177         sgen_update_heap_boundaries ((mword)data, (mword)(data + sgen_nursery_size));
1178         SGEN_LOG (4, "Expanding nursery size (%p-%p): %lu, total: %lu", data, data + alloc_size, (unsigned long)sgen_nursery_size, (unsigned long)mono_gc_get_heap_size ());
1179         section->data = section->next_data = data;
1180         section->size = alloc_size;
1181         section->end_data = data + sgen_nursery_size;
1182         scan_starts = (alloc_size + SCAN_START_SIZE - 1) / SCAN_START_SIZE;
1183         section->scan_starts = sgen_alloc_internal_dynamic (sizeof (char*) * scan_starts, INTERNAL_MEM_SCAN_STARTS, TRUE);
1184         section->num_scan_start = scan_starts;
1185
1186         nursery_section = section;
1187
1188         sgen_nursery_allocator_set_nursery_bounds (data, data + sgen_nursery_size);
1189 }
1190
1191 void*
1192 mono_gc_get_nursery (int *shift_bits, size_t *size)
1193 {
1194         *size = sgen_nursery_size;
1195         *shift_bits = DEFAULT_NURSERY_BITS;
1196         return sgen_get_nursery_start ();
1197 }
1198
1199 void
1200 mono_gc_set_current_thread_appdomain (MonoDomain *domain)
1201 {
1202         SgenThreadInfo *info = mono_thread_info_current ();
1203
1204         /* Could be called from sgen_thread_unregister () with a NULL info */
1205         if (domain) {
1206                 g_assert (info);
1207                 info->stopped_domain = domain;
1208         }
1209 }
1210
1211 gboolean
1212 mono_gc_precise_stack_mark_enabled (void)
1213 {
1214         return !conservative_stack_mark;
1215 }
1216
1217 FILE *
1218 mono_gc_get_logfile (void)
1219 {
1220         return gc_debug_file;
1221 }
1222
1223 static void
1224 report_finalizer_roots_list (FinalizeReadyEntry *list)
1225 {
1226         GCRootReport report;
1227         FinalizeReadyEntry *fin;
1228
1229         report.count = 0;
1230         for (fin = list; fin; fin = fin->next) {
1231                 if (!fin->object)
1232                         continue;
1233                 add_profile_gc_root (&report, fin->object, MONO_PROFILE_GC_ROOT_FINALIZER, 0);
1234         }
1235         notify_gc_roots (&report);
1236 }
1237
1238 static void
1239 report_finalizer_roots (void)
1240 {
1241         report_finalizer_roots_list (fin_ready_list);
1242         report_finalizer_roots_list (critical_fin_list);
1243 }
1244
1245 static GCRootReport *root_report;
1246
1247 static void
1248 single_arg_report_root (void **obj, void *gc_data)
1249 {
1250         if (*obj)
1251                 add_profile_gc_root (root_report, *obj, MONO_PROFILE_GC_ROOT_OTHER, 0);
1252 }
1253
1254 static void
1255 precisely_report_roots_from (GCRootReport *report, void** start_root, void** end_root, mword desc)
1256 {
1257         switch (desc & ROOT_DESC_TYPE_MASK) {
1258         case ROOT_DESC_BITMAP:
1259                 desc >>= ROOT_DESC_TYPE_SHIFT;
1260                 while (desc) {
1261                         if ((desc & 1) && *start_root) {
1262                                 add_profile_gc_root (report, *start_root, MONO_PROFILE_GC_ROOT_OTHER, 0);
1263                         }
1264                         desc >>= 1;
1265                         start_root++;
1266                 }
1267                 return;
1268         case ROOT_DESC_COMPLEX: {
1269                 gsize *bitmap_data = sgen_get_complex_descriptor_bitmap (desc);
1270                 gsize bwords = (*bitmap_data) - 1;
1271                 void **start_run = start_root;
1272                 bitmap_data++;
1273                 while (bwords-- > 0) {
1274                         gsize bmap = *bitmap_data++;
1275                         void **objptr = start_run;
1276                         while (bmap) {
1277                                 if ((bmap & 1) && *objptr) {
1278                                         add_profile_gc_root (report, *objptr, MONO_PROFILE_GC_ROOT_OTHER, 0);
1279                                 }
1280                                 bmap >>= 1;
1281                                 ++objptr;
1282                         }
1283                         start_run += GC_BITS_PER_WORD;
1284                 }
1285                 break;
1286         }
1287         case ROOT_DESC_USER: {
1288                 MonoGCRootMarkFunc marker = sgen_get_user_descriptor_func (desc);
1289                 root_report = report;
1290                 marker (start_root, single_arg_report_root, NULL);
1291                 break;
1292         }
1293         case ROOT_DESC_RUN_LEN:
1294                 g_assert_not_reached ();
1295         default:
1296                 g_assert_not_reached ();
1297         }
1298 }
1299
1300 static void
1301 report_registered_roots_by_type (int root_type)
1302 {
1303         GCRootReport report;
1304         void **start_root;
1305         RootRecord *root;
1306         report.count = 0;
1307         SGEN_HASH_TABLE_FOREACH (&roots_hash [root_type], start_root, root) {
1308                 SGEN_LOG (6, "Precise root scan %p-%p (desc: %p)", start_root, root->end_root, (void*)root->root_desc);
1309                 precisely_report_roots_from (&report, start_root, (void**)root->end_root, root->root_desc);
1310         } SGEN_HASH_TABLE_FOREACH_END;
1311         notify_gc_roots (&report);
1312 }
1313
1314 static void
1315 report_registered_roots (void)
1316 {
1317         report_registered_roots_by_type (ROOT_TYPE_NORMAL);
1318         report_registered_roots_by_type (ROOT_TYPE_WBARRIER);
1319 }
1320
1321 static void
1322 scan_finalizer_entries (FinalizeReadyEntry *list, ScanCopyContext ctx)
1323 {
1324         CopyOrMarkObjectFunc copy_func = ctx.ops->copy_or_mark_object;
1325         SgenGrayQueue *queue = ctx.queue;
1326         FinalizeReadyEntry *fin;
1327
1328         for (fin = list; fin; fin = fin->next) {
1329                 if (!fin->object)
1330                         continue;
1331                 SGEN_LOG (5, "Scan of fin ready object: %p (%s)\n", fin->object, safe_name (fin->object));
1332                 copy_func (&fin->object, queue);
1333         }
1334 }
1335
1336 static const char*
1337 generation_name (int generation)
1338 {
1339         switch (generation) {
1340         case GENERATION_NURSERY: return "nursery";
1341         case GENERATION_OLD: return "old";
1342         default: g_assert_not_reached ();
1343         }
1344 }
1345
1346 const char*
1347 sgen_generation_name (int generation)
1348 {
1349         return generation_name (generation);
1350 }
1351
1352 static void
1353 finish_gray_stack (int generation, ScanCopyContext ctx)
1354 {
1355         TV_DECLARE (atv);
1356         TV_DECLARE (btv);
1357         int done_with_ephemerons, ephemeron_rounds = 0;
1358         char *start_addr = generation == GENERATION_NURSERY ? sgen_get_nursery_start () : NULL;
1359         char *end_addr = generation == GENERATION_NURSERY ? sgen_get_nursery_end () : (char*)-1;
1360         SgenGrayQueue *queue = ctx.queue;
1361
1362         /*
1363          * We copied all the reachable objects. Now it's the time to copy
1364          * the objects that were not referenced by the roots, but by the copied objects.
1365          * we built a stack of objects pointed to by gray_start: they are
1366          * additional roots and we may add more items as we go.
1367          * We loop until gray_start == gray_objects which means no more objects have
1368          * been added. Note this is iterative: no recursion is involved.
1369          * We need to walk the LO list as well in search of marked big objects
1370          * (use a flag since this is needed only on major collections). We need to loop
1371          * here as well, so keep a counter of marked LO (increasing it in copy_object).
1372          *   To achieve better cache locality and cache usage, we drain the gray stack 
1373          * frequently, after each object is copied, and just finish the work here.
1374          */
1375         sgen_drain_gray_stack (-1, ctx);
1376         TV_GETTIME (atv);
1377         SGEN_LOG (2, "%s generation done", generation_name (generation));
1378
1379         /*
1380         Reset bridge data, we might have lingering data from a previous collection if this is a major
1381         collection trigged by minor overflow.
1382
1383         We must reset the gathered bridges since their original block might be evacuated due to major
1384         fragmentation in the meanwhile and the bridge code should not have to deal with that.
1385         */
1386         if (sgen_need_bridge_processing ())
1387                 sgen_bridge_reset_data ();
1388
1389         /*
1390          * Walk the ephemeron tables marking all values with reachable keys. This must be completely done
1391          * before processing finalizable objects and non-tracking weak links to avoid finalizing/clearing
1392          * objects that are in fact reachable.
1393          */
1394         done_with_ephemerons = 0;
1395         do {
1396                 done_with_ephemerons = sgen_client_mark_ephemerons (ctx);
1397                 sgen_drain_gray_stack (-1, ctx);
1398                 ++ephemeron_rounds;
1399         } while (!done_with_ephemerons);
1400
1401         sgen_mark_togglerefs (start_addr, end_addr, ctx);
1402
1403         if (sgen_need_bridge_processing ()) {
1404                 /*Make sure the gray stack is empty before we process bridge objects so we get liveness right*/
1405                 sgen_drain_gray_stack (-1, ctx);
1406                 sgen_collect_bridge_objects (generation, ctx);
1407                 if (generation == GENERATION_OLD)
1408                         sgen_collect_bridge_objects (GENERATION_NURSERY, ctx);
1409
1410                 /*
1411                 Do the first bridge step here, as the collector liveness state will become useless after that.
1412
1413                 An important optimization is to only proccess the possibly dead part of the object graph and skip
1414                 over all live objects as we transitively know everything they point must be alive too.
1415
1416                 The above invariant is completely wrong if we let the gray queue be drained and mark/copy everything.
1417
1418                 This has the unfortunate side effect of making overflow collections perform the first step twice, but
1419                 given we now have heuristics that perform major GC in anticipation of minor overflows this should not
1420                 be a big deal.
1421                 */
1422                 sgen_bridge_processing_stw_step ();
1423         }
1424
1425         /*
1426         Make sure we drain the gray stack before processing disappearing links and finalizers.
1427         If we don't make sure it is empty we might wrongly see a live object as dead.
1428         */
1429         sgen_drain_gray_stack (-1, ctx);
1430
1431         /*
1432         We must clear weak links that don't track resurrection before processing object ready for
1433         finalization so they can be cleared before that.
1434         */
1435         sgen_null_link_in_range (generation, TRUE, ctx);
1436         if (generation == GENERATION_OLD)
1437                 sgen_null_link_in_range (GENERATION_NURSERY, TRUE, ctx);
1438
1439
1440         /* walk the finalization queue and move also the objects that need to be
1441          * finalized: use the finalized objects as new roots so the objects they depend
1442          * on are also not reclaimed. As with the roots above, only objects in the nursery
1443          * are marked/copied.
1444          */
1445         sgen_finalize_in_range (generation, ctx);
1446         if (generation == GENERATION_OLD)
1447                 sgen_finalize_in_range (GENERATION_NURSERY, ctx);
1448         /* drain the new stack that might have been created */
1449         SGEN_LOG (6, "Precise scan of gray area post fin");
1450         sgen_drain_gray_stack (-1, ctx);
1451
1452         /*
1453          * This must be done again after processing finalizable objects since CWL slots are cleared only after the key is finalized.
1454          */
1455         done_with_ephemerons = 0;
1456         do {
1457                 done_with_ephemerons = sgen_client_mark_ephemerons (ctx);
1458                 sgen_drain_gray_stack (-1, ctx);
1459                 ++ephemeron_rounds;
1460         } while (!done_with_ephemerons);
1461
1462         sgen_client_clear_unreachable_ephemerons (ctx);
1463
1464         /*
1465          * We clear togglerefs only after all possible chances of revival are done. 
1466          * This is semantically more inline with what users expect and it allows for
1467          * user finalizers to correctly interact with TR objects.
1468         */
1469         sgen_clear_togglerefs (start_addr, end_addr, ctx);
1470
1471         TV_GETTIME (btv);
1472         SGEN_LOG (2, "Finalize queue handling scan for %s generation: %d usecs %d ephemeron rounds", generation_name (generation), TV_ELAPSED (atv, btv), ephemeron_rounds);
1473
1474         /*
1475          * handle disappearing links
1476          * Note we do this after checking the finalization queue because if an object
1477          * survives (at least long enough to be finalized) we don't clear the link.
1478          * This also deals with a possible issue with the monitor reclamation: with the Boehm
1479          * GC a finalized object my lose the monitor because it is cleared before the finalizer is
1480          * called.
1481          */
1482         g_assert (sgen_gray_object_queue_is_empty (queue));
1483         for (;;) {
1484                 sgen_null_link_in_range (generation, FALSE, ctx);
1485                 if (generation == GENERATION_OLD)
1486                         sgen_null_link_in_range (GENERATION_NURSERY, FALSE, ctx);
1487                 if (sgen_gray_object_queue_is_empty (queue))
1488                         break;
1489                 sgen_drain_gray_stack (-1, ctx);
1490         }
1491
1492         g_assert (sgen_gray_object_queue_is_empty (queue));
1493
1494         sgen_gray_object_queue_trim_free_list (queue);
1495 }
1496
1497 void
1498 sgen_check_section_scan_starts (GCMemSection *section)
1499 {
1500         size_t i;
1501         for (i = 0; i < section->num_scan_start; ++i) {
1502                 if (section->scan_starts [i]) {
1503                         mword size = safe_object_get_size ((MonoObject*) section->scan_starts [i]);
1504                         g_assert (size >= sizeof (MonoObject) && size <= MAX_SMALL_OBJ_SIZE);
1505                 }
1506         }
1507 }
1508
1509 static void
1510 check_scan_starts (void)
1511 {
1512         if (!do_scan_starts_check)
1513                 return;
1514         sgen_check_section_scan_starts (nursery_section);
1515         major_collector.check_scan_starts ();
1516 }
1517
1518 static void
1519 scan_from_registered_roots (char *addr_start, char *addr_end, int root_type, ScanCopyContext ctx)
1520 {
1521         void **start_root;
1522         RootRecord *root;
1523         SGEN_HASH_TABLE_FOREACH (&roots_hash [root_type], start_root, root) {
1524                 SGEN_LOG (6, "Precise root scan %p-%p (desc: %p)", start_root, root->end_root, (void*)root->root_desc);
1525                 precisely_scan_objects_from (start_root, (void**)root->end_root, addr_start, addr_end, root->root_desc, ctx);
1526         } SGEN_HASH_TABLE_FOREACH_END;
1527 }
1528
1529 void
1530 sgen_register_moved_object (void *obj, void *destination)
1531 {
1532         g_assert (mono_profiler_events & MONO_PROFILE_GC_MOVES);
1533
1534         if (moved_objects_idx == MOVED_OBJECTS_NUM) {
1535                 mono_profiler_gc_moves (moved_objects, moved_objects_idx);
1536                 moved_objects_idx = 0;
1537         }
1538         moved_objects [moved_objects_idx++] = obj;
1539         moved_objects [moved_objects_idx++] = destination;
1540 }
1541
1542 static void
1543 init_stats (void)
1544 {
1545         static gboolean inited = FALSE;
1546
1547         if (inited)
1548                 return;
1549
1550         mono_counters_register ("Collection max time",  MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME | MONO_COUNTER_MONOTONIC, &time_max);
1551
1552         mono_counters_register ("Minor fragment clear", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_minor_pre_collection_fragment_clear);
1553         mono_counters_register ("Minor pinning", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_minor_pinning);
1554         mono_counters_register ("Minor scan remembered set", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_minor_scan_remsets);
1555         mono_counters_register ("Minor scan pinned", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_minor_scan_pinned);
1556         mono_counters_register ("Minor scan roots", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_minor_scan_roots);
1557         mono_counters_register ("Minor fragment creation", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_minor_fragment_creation);
1558
1559         mono_counters_register ("Major fragment clear", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_major_pre_collection_fragment_clear);
1560         mono_counters_register ("Major pinning", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_major_pinning);
1561         mono_counters_register ("Major scan pinned", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_major_scan_pinned);
1562         mono_counters_register ("Major scan roots", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_major_scan_roots);
1563         mono_counters_register ("Major scan mod union", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_major_scan_mod_union);
1564         mono_counters_register ("Major finish gray stack", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_major_finish_gray_stack);
1565         mono_counters_register ("Major free big objects", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_major_free_bigobjs);
1566         mono_counters_register ("Major LOS sweep", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_major_los_sweep);
1567         mono_counters_register ("Major sweep", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_major_sweep);
1568         mono_counters_register ("Major fragment creation", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_major_fragment_creation);
1569
1570         mono_counters_register ("Number of pinned objects", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_pinned_objects);
1571
1572 #ifdef HEAVY_STATISTICS
1573         mono_counters_register ("WBarrier remember pointer", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_add_to_global_remset);
1574         mono_counters_register ("WBarrier set field", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_field);
1575         mono_counters_register ("WBarrier set arrayref", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_arrayref);
1576         mono_counters_register ("WBarrier arrayref copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_arrayref_copy);
1577         mono_counters_register ("WBarrier generic store called", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_generic_store);
1578         mono_counters_register ("WBarrier generic atomic store called", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_generic_store_atomic);
1579         mono_counters_register ("WBarrier set root", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_root);
1580         mono_counters_register ("WBarrier value copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_value_copy);
1581         mono_counters_register ("WBarrier object copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_object_copy);
1582
1583         mono_counters_register ("# objects allocated degraded", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_objects_alloced_degraded);
1584         mono_counters_register ("bytes allocated degraded", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_bytes_alloced_degraded);
1585
1586         mono_counters_register ("# copy_object() called (nursery)", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_copy_object_called_nursery);
1587         mono_counters_register ("# objects copied (nursery)", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_objects_copied_nursery);
1588         mono_counters_register ("# copy_object() called (major)", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_copy_object_called_major);
1589         mono_counters_register ("# objects copied (major)", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_objects_copied_major);
1590
1591         mono_counters_register ("# scan_object() called (nursery)", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_scan_object_called_nursery);
1592         mono_counters_register ("# scan_object() called (major)", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_scan_object_called_major);
1593
1594         mono_counters_register ("Slots allocated in vain", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_slots_allocated_in_vain);
1595
1596         mono_counters_register ("# nursery copy_object() failed from space", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_nursery_copy_object_failed_from_space);
1597         mono_counters_register ("# nursery copy_object() failed forwarded", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_nursery_copy_object_failed_forwarded);
1598         mono_counters_register ("# nursery copy_object() failed pinned", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_nursery_copy_object_failed_pinned);
1599         mono_counters_register ("# nursery copy_object() failed to space", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_nursery_copy_object_failed_to_space);
1600
1601         sgen_nursery_allocator_init_heavy_stats ();
1602 #endif
1603
1604         inited = TRUE;
1605 }
1606
1607
1608 static void
1609 reset_pinned_from_failed_allocation (void)
1610 {
1611         bytes_pinned_from_failed_allocation = 0;
1612 }
1613
1614 void
1615 sgen_set_pinned_from_failed_allocation (mword objsize)
1616 {
1617         bytes_pinned_from_failed_allocation += objsize;
1618 }
1619
1620 gboolean
1621 sgen_collection_is_concurrent (void)
1622 {
1623         switch (current_collection_generation) {
1624         case GENERATION_NURSERY:
1625                 return FALSE;
1626         case GENERATION_OLD:
1627                 return concurrent_collection_in_progress;
1628         default:
1629                 g_error ("Invalid current generation %d", current_collection_generation);
1630         }
1631 }
1632
1633 gboolean
1634 sgen_concurrent_collection_in_progress (void)
1635 {
1636         return concurrent_collection_in_progress;
1637 }
1638
1639 typedef struct {
1640         SgenThreadPoolJob job;
1641         SgenObjectOperations *ops;
1642 } ScanJob;
1643
1644 static void
1645 job_remembered_set_scan (void *worker_data_untyped, SgenThreadPoolJob *job)
1646 {
1647         WorkerData *worker_data = worker_data_untyped;
1648         ScanJob *job_data = (ScanJob*)job;
1649         ScanCopyContext ctx = CONTEXT_FROM_OBJECT_OPERATIONS (job_data->ops, sgen_workers_get_job_gray_queue (worker_data));
1650         remset.scan_remsets (ctx);
1651 }
1652
1653 typedef struct {
1654         SgenThreadPoolJob job;
1655         SgenObjectOperations *ops;
1656         char *heap_start;
1657         char *heap_end;
1658         int root_type;
1659 } ScanFromRegisteredRootsJob;
1660
1661 static void
1662 job_scan_from_registered_roots (void *worker_data_untyped, SgenThreadPoolJob *job)
1663 {
1664         WorkerData *worker_data = worker_data_untyped;
1665         ScanFromRegisteredRootsJob *job_data = (ScanFromRegisteredRootsJob*)job;
1666         ScanCopyContext ctx = CONTEXT_FROM_OBJECT_OPERATIONS (job_data->ops, sgen_workers_get_job_gray_queue (worker_data));
1667
1668         scan_from_registered_roots (job_data->heap_start, job_data->heap_end, job_data->root_type, ctx);
1669 }
1670
1671 typedef struct {
1672         SgenThreadPoolJob job;
1673         SgenObjectOperations *ops;
1674         char *heap_start;
1675         char *heap_end;
1676 } ScanThreadDataJob;
1677
1678 static void
1679 job_scan_thread_data (void *worker_data_untyped, SgenThreadPoolJob *job)
1680 {
1681         WorkerData *worker_data = worker_data_untyped;
1682         ScanThreadDataJob *job_data = (ScanThreadDataJob*)job;
1683         ScanCopyContext ctx = CONTEXT_FROM_OBJECT_OPERATIONS (job_data->ops, sgen_workers_get_job_gray_queue (worker_data));
1684
1685         scan_thread_data (job_data->heap_start, job_data->heap_end, TRUE, ctx);
1686 }
1687
1688 typedef struct {
1689         SgenThreadPoolJob job;
1690         SgenObjectOperations *ops;
1691         FinalizeReadyEntry *list;
1692 } ScanFinalizerEntriesJob;
1693
1694 static void
1695 job_scan_finalizer_entries (void *worker_data_untyped, SgenThreadPoolJob *job)
1696 {
1697         WorkerData *worker_data = worker_data_untyped;
1698         ScanFinalizerEntriesJob *job_data = (ScanFinalizerEntriesJob*)job;
1699         ScanCopyContext ctx = CONTEXT_FROM_OBJECT_OPERATIONS (job_data->ops, sgen_workers_get_job_gray_queue (worker_data));
1700
1701         scan_finalizer_entries (job_data->list, ctx);
1702 }
1703
1704 static void
1705 job_scan_major_mod_union_card_table (void *worker_data_untyped, SgenThreadPoolJob *job)
1706 {
1707         WorkerData *worker_data = worker_data_untyped;
1708         ScanJob *job_data = (ScanJob*)job;
1709         ScanCopyContext ctx = CONTEXT_FROM_OBJECT_OPERATIONS (job_data->ops, sgen_workers_get_job_gray_queue (worker_data));
1710
1711         g_assert (concurrent_collection_in_progress);
1712         major_collector.scan_card_table (TRUE, ctx);
1713 }
1714
1715 static void
1716 job_scan_los_mod_union_card_table (void *worker_data_untyped, SgenThreadPoolJob *job)
1717 {
1718         WorkerData *worker_data = worker_data_untyped;
1719         ScanJob *job_data = (ScanJob*)job;
1720         ScanCopyContext ctx = CONTEXT_FROM_OBJECT_OPERATIONS (job_data->ops, sgen_workers_get_job_gray_queue (worker_data));
1721
1722         g_assert (concurrent_collection_in_progress);
1723         sgen_los_scan_card_table (TRUE, ctx);
1724 }
1725
1726 static void
1727 verify_scan_starts (char *start, char *end)
1728 {
1729         size_t i;
1730
1731         for (i = 0; i < nursery_section->num_scan_start; ++i) {
1732                 char *addr = nursery_section->scan_starts [i];
1733                 if (addr > start && addr < end)
1734                         SGEN_LOG (1, "NFC-BAD SCAN START [%zu] %p for obj [%p %p]", i, addr, start, end);
1735         }
1736 }
1737
1738 static void
1739 verify_nursery (void)
1740 {
1741         char *start, *end, *cur, *hole_start;
1742
1743         if (!do_verify_nursery)
1744                 return;
1745                 
1746         if (nursery_canaries_enabled ())
1747                 SGEN_LOG (1, "Checking nursery canaries...");
1748
1749         /*This cleans up unused fragments */
1750         sgen_nursery_allocator_prepare_for_pinning ();
1751
1752         hole_start = start = cur = sgen_get_nursery_start ();
1753         end = sgen_get_nursery_end ();
1754
1755         while (cur < end) {
1756                 size_t ss, size;
1757
1758                 if (!*(void**)cur) {
1759                         cur += sizeof (void*);
1760                         continue;
1761                 }
1762
1763                 if (object_is_forwarded (cur))
1764                         SGEN_LOG (1, "FORWARDED OBJ %p", cur);
1765                 else if (object_is_pinned (cur))
1766                         SGEN_LOG (1, "PINNED OBJ %p", cur);
1767
1768                 ss = safe_object_get_size ((MonoObject*)cur);
1769                 size = ALIGN_UP (safe_object_get_size ((MonoObject*)cur));
1770                 verify_scan_starts (cur, cur + size);
1771                 if (do_dump_nursery_content) {
1772                         if (cur > hole_start)
1773                                 SGEN_LOG (1, "HOLE [%p %p %d]", hole_start, cur, (int)(cur - hole_start));
1774                         SGEN_LOG (1, "OBJ  [%p %p %d %d %s %d]", cur, cur + size, (int)size, (int)ss, sgen_safe_name ((MonoObject*)cur), (gpointer)LOAD_VTABLE (cur) == sgen_client_get_array_fill_vtable ());
1775                 }
1776                 if (nursery_canaries_enabled () && (MonoVTable*)SGEN_LOAD_VTABLE (cur) != sgen_client_get_array_fill_vtable ()) {
1777                         CHECK_CANARY_FOR_OBJECT (cur);
1778                         CANARIFY_SIZE (size);
1779                 }
1780                 cur += size;
1781                 hole_start = cur;
1782         }
1783 }
1784
1785 /*
1786  * Checks that no objects in the nursery are fowarded or pinned.  This
1787  * is a precondition to restarting the mutator while doing a
1788  * concurrent collection.  Note that we don't clear fragments because
1789  * we depend on that having happened earlier.
1790  */
1791 static void
1792 check_nursery_is_clean (void)
1793 {
1794         char *end, *cur;
1795
1796         cur = sgen_get_nursery_start ();
1797         end = sgen_get_nursery_end ();
1798
1799         while (cur < end) {
1800                 size_t size;
1801
1802                 if (!*(void**)cur) {
1803                         cur += sizeof (void*);
1804                         continue;
1805                 }
1806
1807                 g_assert (!object_is_forwarded (cur));
1808                 g_assert (!object_is_pinned (cur));
1809
1810                 size = ALIGN_UP (safe_object_get_size ((MonoObject*)cur));
1811                 verify_scan_starts (cur, cur + size);
1812
1813                 cur += size;
1814         }
1815 }
1816
1817 static void
1818 init_gray_queue (void)
1819 {
1820         if (sgen_collection_is_concurrent ())
1821                 sgen_workers_init_distribute_gray_queue ();
1822         sgen_gray_object_queue_init (&gray_queue, NULL);
1823 }
1824
1825 static void
1826 enqueue_scan_from_roots_jobs (char *heap_start, char *heap_end, SgenObjectOperations *ops)
1827 {
1828         ScanFromRegisteredRootsJob *scrrj;
1829         ScanThreadDataJob *stdj;
1830         ScanFinalizerEntriesJob *sfej;
1831
1832         /* registered roots, this includes static fields */
1833
1834         scrrj = (ScanFromRegisteredRootsJob*)sgen_thread_pool_job_alloc ("scan from registered roots normal", job_scan_from_registered_roots, sizeof (ScanFromRegisteredRootsJob));
1835         scrrj->ops = ops;
1836         scrrj->heap_start = heap_start;
1837         scrrj->heap_end = heap_end;
1838         scrrj->root_type = ROOT_TYPE_NORMAL;
1839         sgen_workers_enqueue_job (&scrrj->job);
1840
1841         scrrj = (ScanFromRegisteredRootsJob*)sgen_thread_pool_job_alloc ("scan from registered roots wbarrier", job_scan_from_registered_roots, sizeof (ScanFromRegisteredRootsJob));
1842         scrrj->ops = ops;
1843         scrrj->heap_start = heap_start;
1844         scrrj->heap_end = heap_end;
1845         scrrj->root_type = ROOT_TYPE_WBARRIER;
1846         sgen_workers_enqueue_job (&scrrj->job);
1847
1848         /* Threads */
1849
1850         stdj = (ScanThreadDataJob*)sgen_thread_pool_job_alloc ("scan thread data", job_scan_thread_data, sizeof (ScanThreadDataJob));
1851         stdj->heap_start = heap_start;
1852         stdj->heap_end = heap_end;
1853         sgen_workers_enqueue_job (&stdj->job);
1854
1855         /* Scan the list of objects ready for finalization. */
1856
1857         sfej = (ScanFinalizerEntriesJob*)sgen_thread_pool_job_alloc ("scan finalizer entries", job_scan_finalizer_entries, sizeof (ScanFinalizerEntriesJob));
1858         sfej->list = fin_ready_list;
1859         sfej->ops = ops;
1860         sgen_workers_enqueue_job (&sfej->job);
1861
1862         sfej = (ScanFinalizerEntriesJob*)sgen_thread_pool_job_alloc ("scan critical finalizer entries", job_scan_finalizer_entries, sizeof (ScanFinalizerEntriesJob));
1863         sfej->list = critical_fin_list;
1864         sfej->ops = ops;
1865         sgen_workers_enqueue_job (&sfej->job);
1866 }
1867
1868 /*
1869  * Perform a nursery collection.
1870  *
1871  * Return whether any objects were late-pinned due to being out of memory.
1872  */
1873 static gboolean
1874 collect_nursery (SgenGrayQueue *unpin_queue, gboolean finish_up_concurrent_mark)
1875 {
1876         gboolean needs_major;
1877         size_t max_garbage_amount;
1878         char *nursery_next;
1879         mword fragment_total;
1880         ScanJob *sj;
1881         SgenObjectOperations *object_ops = &sgen_minor_collector.serial_ops;
1882         ScanCopyContext ctx = CONTEXT_FROM_OBJECT_OPERATIONS (object_ops, &gray_queue);
1883         TV_DECLARE (atv);
1884         TV_DECLARE (btv);
1885
1886         if (disable_minor_collections)
1887                 return TRUE;
1888
1889         TV_GETTIME (last_minor_collection_start_tv);
1890         atv = last_minor_collection_start_tv;
1891
1892         MONO_GC_BEGIN (GENERATION_NURSERY);
1893         binary_protocol_collection_begin (gc_stats.minor_gc_count, GENERATION_NURSERY);
1894
1895         verify_nursery ();
1896
1897 #ifndef DISABLE_PERFCOUNTERS
1898         mono_perfcounters->gc_collections0++;
1899 #endif
1900
1901         current_collection_generation = GENERATION_NURSERY;
1902
1903         SGEN_ASSERT (0, !sgen_collection_is_concurrent (), "Why is the nursery collection concurrent?");
1904
1905         reset_pinned_from_failed_allocation ();
1906
1907         check_scan_starts ();
1908
1909         sgen_nursery_alloc_prepare_for_minor ();
1910
1911         degraded_mode = 0;
1912         objects_pinned = 0;
1913         nursery_next = sgen_nursery_alloc_get_upper_alloc_bound ();
1914         /* FIXME: optimize later to use the higher address where an object can be present */
1915         nursery_next = MAX (nursery_next, sgen_get_nursery_end ());
1916
1917         SGEN_LOG (1, "Start nursery collection %d %p-%p, size: %d", gc_stats.minor_gc_count, sgen_get_nursery_start (), nursery_next, (int)(nursery_next - sgen_get_nursery_start ()));
1918         max_garbage_amount = nursery_next - sgen_get_nursery_start ();
1919         g_assert (nursery_section->size >= max_garbage_amount);
1920
1921         /* world must be stopped already */
1922         TV_GETTIME (btv);
1923         time_minor_pre_collection_fragment_clear += TV_ELAPSED (atv, btv);
1924
1925         sgen_client_pre_collection_checks ();
1926
1927         nursery_section->next_data = nursery_next;
1928
1929         major_collector.start_nursery_collection ();
1930
1931         sgen_memgov_minor_collection_start ();
1932
1933         init_gray_queue ();
1934
1935         gc_stats.minor_gc_count ++;
1936
1937         if (whole_heap_check_before_collection) {
1938                 sgen_clear_nursery_fragments ();
1939                 sgen_check_whole_heap (finish_up_concurrent_mark);
1940         }
1941         if (consistency_check_at_minor_collection)
1942                 sgen_check_consistency ();
1943
1944         MONO_GC_CHECKPOINT_1 (GENERATION_NURSERY);
1945
1946         sgen_process_fin_stage_entries ();
1947         sgen_process_dislink_stage_entries ();
1948
1949         MONO_GC_CHECKPOINT_2 (GENERATION_NURSERY);
1950
1951         /* pin from pinned handles */
1952         sgen_init_pinning ();
1953         mono_profiler_gc_event (MONO_GC_EVENT_MARK_START, 0);
1954         pin_from_roots (sgen_get_nursery_start (), nursery_next, ctx);
1955         /* pin cemented objects */
1956         sgen_pin_cemented_objects ();
1957         /* identify pinned objects */
1958         sgen_optimize_pin_queue ();
1959         sgen_pinning_setup_section (nursery_section);
1960
1961         pin_objects_in_nursery (FALSE, ctx);
1962         sgen_pinning_trim_queue_to_section (nursery_section);
1963
1964         TV_GETTIME (atv);
1965         time_minor_pinning += TV_ELAPSED (btv, atv);
1966         SGEN_LOG (2, "Finding pinned pointers: %zd in %d usecs", sgen_get_pinned_count (), TV_ELAPSED (btv, atv));
1967         SGEN_LOG (4, "Start scan with %zd pinned objects", sgen_get_pinned_count ());
1968
1969         MONO_GC_CHECKPOINT_3 (GENERATION_NURSERY);
1970
1971         /*
1972          * FIXME: When we finish a concurrent collection we do a nursery collection first,
1973          * as part of which we scan the card table.  Then, later, we scan the mod union
1974          * cardtable.  We should only have to do one.
1975          */
1976         sj = (ScanJob*)sgen_thread_pool_job_alloc ("scan remset", job_remembered_set_scan, sizeof (ScanJob));
1977         sj->ops = object_ops;
1978         sgen_workers_enqueue_job (&sj->job);
1979
1980         /* we don't have complete write barrier yet, so we scan all the old generation sections */
1981         TV_GETTIME (btv);
1982         time_minor_scan_remsets += TV_ELAPSED (atv, btv);
1983         SGEN_LOG (2, "Old generation scan: %d usecs", TV_ELAPSED (atv, btv));
1984
1985         MONO_GC_CHECKPOINT_4 (GENERATION_NURSERY);
1986
1987         sgen_drain_gray_stack (-1, ctx);
1988
1989         if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
1990                 report_registered_roots ();
1991         if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
1992                 report_finalizer_roots ();
1993         TV_GETTIME (atv);
1994         time_minor_scan_pinned += TV_ELAPSED (btv, atv);
1995
1996         MONO_GC_CHECKPOINT_5 (GENERATION_NURSERY);
1997
1998         enqueue_scan_from_roots_jobs (sgen_get_nursery_start (), nursery_next, object_ops);
1999
2000         TV_GETTIME (btv);
2001         time_minor_scan_roots += TV_ELAPSED (atv, btv);
2002
2003         MONO_GC_CHECKPOINT_6 (GENERATION_NURSERY);
2004         MONO_GC_CHECKPOINT_7 (GENERATION_NURSERY);
2005         MONO_GC_CHECKPOINT_8 (GENERATION_NURSERY);
2006
2007         finish_gray_stack (GENERATION_NURSERY, ctx);
2008         TV_GETTIME (atv);
2009         time_minor_finish_gray_stack += TV_ELAPSED (btv, atv);
2010         mono_profiler_gc_event (MONO_GC_EVENT_MARK_END, 0);
2011
2012         MONO_GC_CHECKPOINT_9 (GENERATION_NURSERY);
2013
2014         if (objects_pinned) {
2015                 sgen_optimize_pin_queue ();
2016                 sgen_pinning_setup_section (nursery_section);
2017         }
2018
2019         /* walk the pin_queue, build up the fragment list of free memory, unmark
2020          * pinned objects as we go, memzero() the empty fragments so they are ready for the
2021          * next allocations.
2022          */
2023         mono_profiler_gc_event (MONO_GC_EVENT_RECLAIM_START, 0);
2024         fragment_total = sgen_build_nursery_fragments (nursery_section, unpin_queue);
2025         if (!fragment_total)
2026                 degraded_mode = 1;
2027
2028         /* Clear TLABs for all threads */
2029         sgen_clear_tlabs ();
2030
2031         mono_profiler_gc_event (MONO_GC_EVENT_RECLAIM_END, 0);
2032         TV_GETTIME (btv);
2033         time_minor_fragment_creation += TV_ELAPSED (atv, btv);
2034         SGEN_LOG (2, "Fragment creation: %d usecs, %lu bytes available", TV_ELAPSED (atv, btv), (unsigned long)fragment_total);
2035
2036         if (consistency_check_at_minor_collection)
2037                 sgen_check_major_refs ();
2038
2039         major_collector.finish_nursery_collection ();
2040
2041         TV_GETTIME (last_minor_collection_end_tv);
2042         gc_stats.minor_gc_time += TV_ELAPSED (last_minor_collection_start_tv, last_minor_collection_end_tv);
2043
2044         sgen_debug_dump_heap ("minor", gc_stats.minor_gc_count - 1, NULL);
2045
2046         /* prepare the pin queue for the next collection */
2047         sgen_finish_pinning ();
2048         if (fin_ready_list || critical_fin_list) {
2049                 SGEN_LOG (4, "Finalizer-thread wakeup: ready %d", num_ready_finalizers);
2050                 mono_gc_finalize_notify ();
2051         }
2052         sgen_pin_stats_reset ();
2053         /* clear cemented hash */
2054         sgen_cement_clear_below_threshold ();
2055
2056         g_assert (sgen_gray_object_queue_is_empty (&gray_queue));
2057
2058         remset.finish_minor_collection ();
2059
2060         check_scan_starts ();
2061
2062         binary_protocol_flush_buffers (FALSE);
2063
2064         sgen_memgov_minor_collection_end ();
2065
2066         /*objects are late pinned because of lack of memory, so a major is a good call*/
2067         needs_major = objects_pinned > 0;
2068         current_collection_generation = -1;
2069         objects_pinned = 0;
2070
2071         MONO_GC_END (GENERATION_NURSERY);
2072         binary_protocol_collection_end (gc_stats.minor_gc_count - 1, GENERATION_NURSERY, 0, 0);
2073
2074         if (check_nursery_objects_pinned && !sgen_minor_collector.is_split)
2075                 sgen_check_nursery_objects_pinned (unpin_queue != NULL);
2076
2077         return needs_major;
2078 }
2079
2080 static void
2081 scan_nursery_objects_callback (char *obj, size_t size, ScanCopyContext *ctx)
2082 {
2083         /*
2084          * This is called on all objects in the nursery, including pinned ones, so we need
2085          * to use sgen_obj_get_descriptor_safe(), which masks out the vtable tag bits.
2086          */
2087         ctx->ops->scan_object (obj, sgen_obj_get_descriptor_safe (obj), ctx->queue);
2088 }
2089
2090 static void
2091 scan_nursery_objects (ScanCopyContext ctx)
2092 {
2093         sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
2094                         (IterateObjectCallbackFunc)scan_nursery_objects_callback, (void*)&ctx, FALSE);
2095 }
2096
2097 typedef enum {
2098         COPY_OR_MARK_FROM_ROOTS_SERIAL,
2099         COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT,
2100         COPY_OR_MARK_FROM_ROOTS_FINISH_CONCURRENT
2101 } CopyOrMarkFromRootsMode;
2102
2103 static void
2104 major_copy_or_mark_from_roots (size_t *old_next_pin_slot, CopyOrMarkFromRootsMode mode, gboolean scan_whole_nursery, SgenObjectOperations *object_ops)
2105 {
2106         LOSObject *bigobj;
2107         TV_DECLARE (atv);
2108         TV_DECLARE (btv);
2109         /* FIXME: only use these values for the precise scan
2110          * note that to_space pointers should be excluded anyway...
2111          */
2112         char *heap_start = NULL;
2113         char *heap_end = (char*)-1;
2114         gboolean profile_roots = mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS;
2115         GCRootReport root_report = { 0 };
2116         ScanCopyContext ctx = CONTEXT_FROM_OBJECT_OPERATIONS (object_ops, WORKERS_DISTRIBUTE_GRAY_QUEUE);
2117         gboolean concurrent = mode != COPY_OR_MARK_FROM_ROOTS_SERIAL;
2118
2119         SGEN_ASSERT (0, !!concurrent == !!concurrent_collection_in_progress, "We've been called with the wrong mode.");
2120
2121         if (scan_whole_nursery)
2122                 SGEN_ASSERT (0, mode == COPY_OR_MARK_FROM_ROOTS_FINISH_CONCURRENT, "Scanning whole nursery only makes sense when we're finishing a concurrent collection.");
2123
2124         if (concurrent) {
2125                 /*This cleans up unused fragments */
2126                 sgen_nursery_allocator_prepare_for_pinning ();
2127
2128                 if (do_concurrent_checks)
2129                         check_nursery_is_clean ();
2130         } else {
2131                 /* The concurrent collector doesn't touch the nursery. */
2132                 sgen_nursery_alloc_prepare_for_major ();
2133         }
2134
2135         init_gray_queue ();
2136
2137         TV_GETTIME (atv);
2138
2139         /* Pinning depends on this */
2140         sgen_clear_nursery_fragments ();
2141
2142         if (whole_heap_check_before_collection)
2143                 sgen_check_whole_heap (mode == COPY_OR_MARK_FROM_ROOTS_FINISH_CONCURRENT);
2144
2145         TV_GETTIME (btv);
2146         time_major_pre_collection_fragment_clear += TV_ELAPSED (atv, btv);
2147
2148         if (!sgen_collection_is_concurrent ())
2149                 nursery_section->next_data = sgen_get_nursery_end ();
2150         /* we should also coalesce scanning from sections close to each other
2151          * and deal with pointers outside of the sections later.
2152          */
2153
2154         objects_pinned = 0;
2155
2156         sgen_client_pre_collection_checks ();
2157
2158         if (!concurrent) {
2159                 /* Remsets are not useful for a major collection */
2160                 remset.clear_cards ();
2161         }
2162
2163         sgen_process_fin_stage_entries ();
2164         sgen_process_dislink_stage_entries ();
2165
2166         TV_GETTIME (atv);
2167         sgen_init_pinning ();
2168         SGEN_LOG (6, "Collecting pinned addresses");
2169         pin_from_roots ((void*)lowest_heap_address, (void*)highest_heap_address, ctx);
2170
2171         if (mode != COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT) {
2172                 if (major_collector.is_concurrent) {
2173                         /*
2174                          * The concurrent major collector cannot evict
2175                          * yet, so we need to pin cemented objects to
2176                          * not break some asserts.
2177                          *
2178                          * FIXME: We could evict now!
2179                          */
2180                         sgen_pin_cemented_objects ();
2181                 }
2182         }
2183
2184         sgen_optimize_pin_queue ();
2185
2186         /*
2187          * pin_queue now contains all candidate pointers, sorted and
2188          * uniqued.  We must do two passes now to figure out which
2189          * objects are pinned.
2190          *
2191          * The first is to find within the pin_queue the area for each
2192          * section.  This requires that the pin_queue be sorted.  We
2193          * also process the LOS objects and pinned chunks here.
2194          *
2195          * The second, destructive, pass is to reduce the section
2196          * areas to pointers to the actually pinned objects.
2197          */
2198         SGEN_LOG (6, "Pinning from sections");
2199         /* first pass for the sections */
2200         sgen_find_section_pin_queue_start_end (nursery_section);
2201         /* identify possible pointers to the insize of large objects */
2202         SGEN_LOG (6, "Pinning from large objects");
2203         for (bigobj = los_object_list; bigobj; bigobj = bigobj->next) {
2204                 size_t dummy;
2205                 if (sgen_find_optimized_pin_queue_area (bigobj->data, (char*)bigobj->data + sgen_los_object_size (bigobj), &dummy, &dummy)) {
2206                         binary_protocol_pin (bigobj->data, (gpointer)LOAD_VTABLE (bigobj->data), safe_object_get_size (((MonoObject*)(bigobj->data))));
2207
2208 #ifdef ENABLE_DTRACE
2209                         if (G_UNLIKELY (MONO_GC_OBJ_PINNED_ENABLED ())) {
2210                                 MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (bigobj->data);
2211                                 MONO_GC_OBJ_PINNED ((mword)bigobj->data, sgen_safe_object_get_size ((MonoObject*)bigobj->data), vt->klass->name_space, vt->klass->name, GENERATION_OLD);
2212                         }
2213 #endif
2214
2215                         if (sgen_los_object_is_pinned (bigobj->data)) {
2216                                 SGEN_ASSERT (0, mode == COPY_OR_MARK_FROM_ROOTS_FINISH_CONCURRENT, "LOS objects can only be pinned here after concurrent marking.");
2217                                 continue;
2218                         }
2219                         sgen_los_pin_object (bigobj->data);
2220                         if (SGEN_OBJECT_HAS_REFERENCES (bigobj->data))
2221                                 GRAY_OBJECT_ENQUEUE (WORKERS_DISTRIBUTE_GRAY_QUEUE, bigobj->data, sgen_obj_get_descriptor (bigobj->data));
2222                         sgen_pin_stats_register_object ((char*) bigobj->data, safe_object_get_size ((MonoObject*) bigobj->data));
2223                         SGEN_LOG (6, "Marked large object %p (%s) size: %lu from roots", bigobj->data, safe_name (bigobj->data), (unsigned long)sgen_los_object_size (bigobj));
2224
2225                         if (profile_roots)
2226                                 add_profile_gc_root (&root_report, bigobj->data, MONO_PROFILE_GC_ROOT_PINNING | MONO_PROFILE_GC_ROOT_MISC, 0);
2227                 }
2228         }
2229         if (profile_roots)
2230                 notify_gc_roots (&root_report);
2231         /* second pass for the sections */
2232
2233         /*
2234          * Concurrent mark never follows references into the nursery.  In the start and
2235          * finish pauses we must scan live nursery objects, though.
2236          *
2237          * In the finish pause we do this conservatively by scanning all nursery objects.
2238          * Previously we would only scan pinned objects here.  We assumed that all objects
2239          * that were pinned during the nursery collection immediately preceding this finish
2240          * mark would be pinned again here.  Due to the way we get the stack end for the GC
2241          * thread, however, that's not necessarily the case: we scan part of the stack used
2242          * by the GC itself, which changes constantly, so pinning isn't entirely
2243          * deterministic.
2244          *
2245          * The split nursery also complicates things because non-pinned objects can survive
2246          * in the nursery.  That's why we need to do a full scan of the nursery for it, too.
2247          *
2248          * In the future we shouldn't do a preceding nursery collection at all and instead
2249          * do the finish pause with promotion from the nursery.
2250          *
2251          * A further complication arises when we have late-pinned objects from the preceding
2252          * nursery collection.  Those are the result of being out of memory when trying to
2253          * evacuate objects.  They won't be found from the roots, so we just scan the whole
2254          * nursery.
2255          *
2256          * Non-concurrent mark evacuates from the nursery, so it's
2257          * sufficient to just scan pinned nursery objects.
2258          */
2259         if (scan_whole_nursery || mode == COPY_OR_MARK_FROM_ROOTS_FINISH_CONCURRENT || (concurrent && sgen_minor_collector.is_split)) {
2260                 scan_nursery_objects (ctx);
2261         } else {
2262                 pin_objects_in_nursery (concurrent, ctx);
2263                 if (check_nursery_objects_pinned && !sgen_minor_collector.is_split)
2264                         sgen_check_nursery_objects_pinned (mode != COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT);
2265         }
2266
2267         major_collector.pin_objects (WORKERS_DISTRIBUTE_GRAY_QUEUE);
2268         if (old_next_pin_slot)
2269                 *old_next_pin_slot = sgen_get_pinned_count ();
2270
2271         TV_GETTIME (btv);
2272         time_major_pinning += TV_ELAPSED (atv, btv);
2273         SGEN_LOG (2, "Finding pinned pointers: %zd in %d usecs", sgen_get_pinned_count (), TV_ELAPSED (atv, btv));
2274         SGEN_LOG (4, "Start scan with %zd pinned objects", sgen_get_pinned_count ());
2275
2276         major_collector.init_to_space ();
2277
2278         /*
2279          * The concurrent collector doesn't move objects, neither on
2280          * the major heap nor in the nursery, so we can mark even
2281          * before pinning has finished.  For the non-concurrent
2282          * collector we start the workers after pinning.
2283          */
2284         if (mode != COPY_OR_MARK_FROM_ROOTS_SERIAL) {
2285                 SGEN_ASSERT (0, sgen_workers_all_done (), "Why are the workers not done when we start or finish a major collection?");
2286                 sgen_workers_start_all_workers (object_ops);
2287                 gray_queue_enable_redirect (WORKERS_DISTRIBUTE_GRAY_QUEUE);
2288         }
2289
2290 #ifdef SGEN_DEBUG_INTERNAL_ALLOC
2291         main_gc_thread = mono_native_thread_self ();
2292 #endif
2293
2294         if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
2295                 report_registered_roots ();
2296         TV_GETTIME (atv);
2297         time_major_scan_pinned += TV_ELAPSED (btv, atv);
2298
2299         if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
2300                 report_finalizer_roots ();
2301
2302         /*
2303          * FIXME: is this the right context?  It doesn't seem to contain a copy function
2304          * unless we're concurrent.
2305          */
2306         enqueue_scan_from_roots_jobs (heap_start, heap_end, object_ops);
2307
2308         TV_GETTIME (btv);
2309         time_major_scan_roots += TV_ELAPSED (atv, btv);
2310
2311         if (mode == COPY_OR_MARK_FROM_ROOTS_FINISH_CONCURRENT) {
2312                 ScanJob *sj;
2313
2314                 /* Mod union card table */
2315                 sj = (ScanJob*)sgen_thread_pool_job_alloc ("scan mod union cardtable", job_scan_major_mod_union_card_table, sizeof (ScanJob));
2316                 sj->ops = object_ops;
2317                 sgen_workers_enqueue_job (&sj->job);
2318
2319                 sj = (ScanJob*)sgen_thread_pool_job_alloc ("scan LOS mod union cardtable", job_scan_los_mod_union_card_table, sizeof (ScanJob));
2320                 sj->ops = object_ops;
2321                 sgen_workers_enqueue_job (&sj->job);
2322
2323                 TV_GETTIME (atv);
2324                 time_major_scan_mod_union += TV_ELAPSED (btv, atv);
2325         }
2326 }
2327
2328 static void
2329 major_finish_copy_or_mark (void)
2330 {
2331         if (!concurrent_collection_in_progress)
2332                 return;
2333
2334         /*
2335          * Prepare the pin queue for the next collection.  Since pinning runs on the worker
2336          * threads we must wait for the jobs to finish before we can reset it.
2337          */
2338         sgen_workers_wait_for_jobs_finished ();
2339         sgen_finish_pinning ();
2340
2341         sgen_pin_stats_reset ();
2342
2343         if (do_concurrent_checks)
2344                 check_nursery_is_clean ();
2345 }
2346
2347 static void
2348 major_start_collection (gboolean concurrent, size_t *old_next_pin_slot)
2349 {
2350         SgenObjectOperations *object_ops;
2351
2352         MONO_GC_BEGIN (GENERATION_OLD);
2353         binary_protocol_collection_begin (gc_stats.major_gc_count, GENERATION_OLD);
2354
2355         current_collection_generation = GENERATION_OLD;
2356 #ifndef DISABLE_PERFCOUNTERS
2357         mono_perfcounters->gc_collections1++;
2358 #endif
2359
2360         g_assert (sgen_section_gray_queue_is_empty (sgen_workers_get_distribute_section_gray_queue ()));
2361
2362         sgen_cement_reset ();
2363
2364         if (concurrent) {
2365                 g_assert (major_collector.is_concurrent);
2366                 concurrent_collection_in_progress = TRUE;
2367
2368                 object_ops = &major_collector.major_ops_concurrent_start;
2369         } else {
2370                 object_ops = &major_collector.major_ops_serial;
2371         }
2372
2373         reset_pinned_from_failed_allocation ();
2374
2375         sgen_memgov_major_collection_start ();
2376
2377         //count_ref_nonref_objs ();
2378         //consistency_check ();
2379
2380         check_scan_starts ();
2381
2382         degraded_mode = 0;
2383         SGEN_LOG (1, "Start major collection %d", gc_stats.major_gc_count);
2384         gc_stats.major_gc_count ++;
2385
2386         if (major_collector.start_major_collection)
2387                 major_collector.start_major_collection ();
2388
2389         major_copy_or_mark_from_roots (old_next_pin_slot, concurrent ? COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT : COPY_OR_MARK_FROM_ROOTS_SERIAL, FALSE, object_ops);
2390         major_finish_copy_or_mark ();
2391 }
2392
2393 static void
2394 major_finish_collection (const char *reason, size_t old_next_pin_slot, gboolean forced, gboolean scan_whole_nursery)
2395 {
2396         ScannedObjectCounts counts;
2397         SgenObjectOperations *object_ops;
2398         TV_DECLARE (atv);
2399         TV_DECLARE (btv);
2400
2401         TV_GETTIME (btv);
2402
2403         if (concurrent_collection_in_progress) {
2404                 object_ops = &major_collector.major_ops_concurrent_finish;
2405
2406                 major_copy_or_mark_from_roots (NULL, COPY_OR_MARK_FROM_ROOTS_FINISH_CONCURRENT, scan_whole_nursery, object_ops);
2407
2408                 major_finish_copy_or_mark ();
2409
2410                 sgen_workers_join ();
2411
2412                 SGEN_ASSERT (0, sgen_gray_object_queue_is_empty (&gray_queue), "Why is the gray queue not empty after workers have finished working?");
2413
2414 #ifdef SGEN_DEBUG_INTERNAL_ALLOC
2415                 main_gc_thread = NULL;
2416 #endif
2417
2418                 if (do_concurrent_checks)
2419                         check_nursery_is_clean ();
2420         } else {
2421                 SGEN_ASSERT (0, !scan_whole_nursery, "scan_whole_nursery only applies to concurrent collections");
2422                 object_ops = &major_collector.major_ops_serial;
2423         }
2424
2425         /*
2426          * The workers have stopped so we need to finish gray queue
2427          * work that might result from finalization in the main GC
2428          * thread.  Redirection must therefore be turned off.
2429          */
2430         sgen_gray_object_queue_disable_alloc_prepare (&gray_queue);
2431         g_assert (sgen_section_gray_queue_is_empty (sgen_workers_get_distribute_section_gray_queue ()));
2432
2433         /* all the objects in the heap */
2434         finish_gray_stack (GENERATION_OLD, CONTEXT_FROM_OBJECT_OPERATIONS (object_ops, &gray_queue));
2435         TV_GETTIME (atv);
2436         time_major_finish_gray_stack += TV_ELAPSED (btv, atv);
2437
2438         SGEN_ASSERT (0, sgen_workers_all_done (), "Can't have workers working after joining");
2439
2440         if (objects_pinned) {
2441                 g_assert (!concurrent_collection_in_progress);
2442
2443                 /*
2444                  * This is slow, but we just OOM'd.
2445                  *
2446                  * See comment at `sgen_pin_queue_clear_discarded_entries` for how the pin
2447                  * queue is laid out at this point.
2448                  */
2449                 sgen_pin_queue_clear_discarded_entries (nursery_section, old_next_pin_slot);
2450                 /*
2451                  * We need to reestablish all pinned nursery objects in the pin queue
2452                  * because they're needed for fragment creation.  Unpinning happens by
2453                  * walking the whole queue, so it's not necessary to reestablish where major
2454                  * heap block pins are - all we care is that they're still in there
2455                  * somewhere.
2456                  */
2457                 sgen_optimize_pin_queue ();
2458                 sgen_find_section_pin_queue_start_end (nursery_section);
2459                 objects_pinned = 0;
2460         }
2461
2462         reset_heap_boundaries ();
2463         sgen_update_heap_boundaries ((mword)sgen_get_nursery_start (), (mword)sgen_get_nursery_end ());
2464
2465         if (!concurrent_collection_in_progress) {
2466                 /* walk the pin_queue, build up the fragment list of free memory, unmark
2467                  * pinned objects as we go, memzero() the empty fragments so they are ready for the
2468                  * next allocations.
2469                  */
2470                 if (!sgen_build_nursery_fragments (nursery_section, NULL))
2471                         degraded_mode = 1;
2472
2473                 /* prepare the pin queue for the next collection */
2474                 sgen_finish_pinning ();
2475
2476                 /* Clear TLABs for all threads */
2477                 sgen_clear_tlabs ();
2478
2479                 sgen_pin_stats_reset ();
2480         }
2481
2482         sgen_cement_clear_below_threshold ();
2483
2484         if (check_mark_bits_after_major_collection)
2485                 sgen_check_heap_marked (concurrent_collection_in_progress);
2486
2487         TV_GETTIME (btv);
2488         time_major_fragment_creation += TV_ELAPSED (atv, btv);
2489
2490         MONO_GC_SWEEP_BEGIN (GENERATION_OLD, !major_collector.sweeps_lazily);
2491
2492         TV_GETTIME (atv);
2493         time_major_free_bigobjs += TV_ELAPSED (btv, atv);
2494
2495         sgen_los_sweep ();
2496
2497         TV_GETTIME (btv);
2498         time_major_los_sweep += TV_ELAPSED (atv, btv);
2499
2500         major_collector.sweep ();
2501
2502         MONO_GC_SWEEP_END (GENERATION_OLD, !major_collector.sweeps_lazily);
2503
2504         TV_GETTIME (atv);
2505         time_major_sweep += TV_ELAPSED (btv, atv);
2506
2507         sgen_debug_dump_heap ("major", gc_stats.major_gc_count - 1, reason);
2508
2509         if (fin_ready_list || critical_fin_list) {
2510                 SGEN_LOG (4, "Finalizer-thread wakeup: ready %d", num_ready_finalizers);
2511                 mono_gc_finalize_notify ();
2512         }
2513
2514         g_assert (sgen_gray_object_queue_is_empty (&gray_queue));
2515
2516         sgen_memgov_major_collection_end (forced);
2517         current_collection_generation = -1;
2518
2519         memset (&counts, 0, sizeof (ScannedObjectCounts));
2520         major_collector.finish_major_collection (&counts);
2521
2522         g_assert (sgen_section_gray_queue_is_empty (sgen_workers_get_distribute_section_gray_queue ()));
2523
2524         SGEN_ASSERT (0, sgen_workers_all_done (), "Can't have workers working after major collection has finished");
2525         if (concurrent_collection_in_progress)
2526                 concurrent_collection_in_progress = FALSE;
2527
2528         check_scan_starts ();
2529
2530         binary_protocol_flush_buffers (FALSE);
2531
2532         //consistency_check ();
2533
2534         MONO_GC_END (GENERATION_OLD);
2535         binary_protocol_collection_end (gc_stats.major_gc_count - 1, GENERATION_OLD, counts.num_scanned_objects, counts.num_unique_scanned_objects);
2536 }
2537
2538 static gboolean
2539 major_do_collection (const char *reason, gboolean forced)
2540 {
2541         TV_DECLARE (time_start);
2542         TV_DECLARE (time_end);
2543         size_t old_next_pin_slot;
2544
2545         if (disable_major_collections)
2546                 return FALSE;
2547
2548         if (major_collector.get_and_reset_num_major_objects_marked) {
2549                 long long num_marked = major_collector.get_and_reset_num_major_objects_marked ();
2550                 g_assert (!num_marked);
2551         }
2552
2553         /* world must be stopped already */
2554         TV_GETTIME (time_start);
2555
2556         major_start_collection (FALSE, &old_next_pin_slot);
2557         major_finish_collection (reason, old_next_pin_slot, forced, FALSE);
2558
2559         TV_GETTIME (time_end);
2560         gc_stats.major_gc_time += TV_ELAPSED (time_start, time_end);
2561
2562         /* FIXME: also report this to the user, preferably in gc-end. */
2563         if (major_collector.get_and_reset_num_major_objects_marked)
2564                 major_collector.get_and_reset_num_major_objects_marked ();
2565
2566         return bytes_pinned_from_failed_allocation > 0;
2567 }
2568
2569 static void
2570 major_start_concurrent_collection (const char *reason)
2571 {
2572         TV_DECLARE (time_start);
2573         TV_DECLARE (time_end);
2574         long long num_objects_marked;
2575
2576         if (disable_major_collections)
2577                 return;
2578
2579         TV_GETTIME (time_start);
2580         SGEN_TV_GETTIME (time_major_conc_collection_start);
2581
2582         num_objects_marked = major_collector.get_and_reset_num_major_objects_marked ();
2583         g_assert (num_objects_marked == 0);
2584
2585         MONO_GC_CONCURRENT_START_BEGIN (GENERATION_OLD);
2586         binary_protocol_concurrent_start ();
2587
2588         // FIXME: store reason and pass it when finishing
2589         major_start_collection (TRUE, NULL);
2590
2591         gray_queue_redirect (&gray_queue);
2592
2593         num_objects_marked = major_collector.get_and_reset_num_major_objects_marked ();
2594         MONO_GC_CONCURRENT_START_END (GENERATION_OLD, num_objects_marked);
2595
2596         TV_GETTIME (time_end);
2597         gc_stats.major_gc_time += TV_ELAPSED (time_start, time_end);
2598
2599         current_collection_generation = -1;
2600 }
2601
2602 /*
2603  * Returns whether the major collection has finished.
2604  */
2605 static gboolean
2606 major_should_finish_concurrent_collection (void)
2607 {
2608         SGEN_ASSERT (0, sgen_gray_object_queue_is_empty (&gray_queue), "Why is the gray queue not empty before we have started doing anything?");
2609         return sgen_workers_all_done ();
2610 }
2611
2612 static void
2613 major_update_concurrent_collection (void)
2614 {
2615         TV_DECLARE (total_start);
2616         TV_DECLARE (total_end);
2617
2618         TV_GETTIME (total_start);
2619
2620         MONO_GC_CONCURRENT_UPDATE_FINISH_BEGIN (GENERATION_OLD, major_collector.get_and_reset_num_major_objects_marked ());
2621         binary_protocol_concurrent_update ();
2622
2623         major_collector.update_cardtable_mod_union ();
2624         sgen_los_update_cardtable_mod_union ();
2625
2626         MONO_GC_CONCURRENT_UPDATE_END (GENERATION_OLD, major_collector.get_and_reset_num_major_objects_marked ());
2627
2628         TV_GETTIME (total_end);
2629         gc_stats.major_gc_time += TV_ELAPSED (total_start, total_end);
2630 }
2631
2632 static void
2633 major_finish_concurrent_collection (gboolean forced)
2634 {
2635         TV_DECLARE (total_start);
2636         TV_DECLARE (total_end);
2637         gboolean late_pinned;
2638         SgenGrayQueue unpin_queue;
2639         memset (&unpin_queue, 0, sizeof (unpin_queue));
2640
2641         TV_GETTIME (total_start);
2642
2643         MONO_GC_CONCURRENT_UPDATE_FINISH_BEGIN (GENERATION_OLD, major_collector.get_and_reset_num_major_objects_marked ());
2644         binary_protocol_concurrent_finish ();
2645
2646         /*
2647          * The major collector can add global remsets which are processed in the finishing
2648          * nursery collection, below.  That implies that the workers must have finished
2649          * marking before the nursery collection is allowed to run, otherwise we might miss
2650          * some remsets.
2651          */
2652         sgen_workers_wait ();
2653
2654         SGEN_TV_GETTIME (time_major_conc_collection_end);
2655         gc_stats.major_gc_time_concurrent += SGEN_TV_ELAPSED (time_major_conc_collection_start, time_major_conc_collection_end);
2656
2657         major_collector.update_cardtable_mod_union ();
2658         sgen_los_update_cardtable_mod_union ();
2659
2660         late_pinned = collect_nursery (&unpin_queue, TRUE);
2661
2662         if (mod_union_consistency_check)
2663                 sgen_check_mod_union_consistency ();
2664
2665         current_collection_generation = GENERATION_OLD;
2666         major_finish_collection ("finishing", -1, forced, late_pinned);
2667
2668         if (whole_heap_check_before_collection)
2669                 sgen_check_whole_heap (FALSE);
2670
2671         unpin_objects_from_queue (&unpin_queue);
2672         sgen_gray_object_queue_deinit (&unpin_queue);
2673
2674         MONO_GC_CONCURRENT_FINISH_END (GENERATION_OLD, major_collector.get_and_reset_num_major_objects_marked ());
2675
2676         TV_GETTIME (total_end);
2677         gc_stats.major_gc_time += TV_ELAPSED (total_start, total_end) - TV_ELAPSED (last_minor_collection_start_tv, last_minor_collection_end_tv);
2678
2679         current_collection_generation = -1;
2680 }
2681
2682 /*
2683  * Ensure an allocation request for @size will succeed by freeing enough memory.
2684  *
2685  * LOCKING: The GC lock MUST be held.
2686  */
2687 void
2688 sgen_ensure_free_space (size_t size)
2689 {
2690         int generation_to_collect = -1;
2691         const char *reason = NULL;
2692
2693         if (size > SGEN_MAX_SMALL_OBJ_SIZE) {
2694                 if (sgen_need_major_collection (size)) {
2695                         reason = "LOS overflow";
2696                         generation_to_collect = GENERATION_OLD;
2697                 }
2698         } else {
2699                 if (degraded_mode) {
2700                         if (sgen_need_major_collection (size)) {
2701                                 reason = "Degraded mode overflow";
2702                                 generation_to_collect = GENERATION_OLD;
2703                         }
2704                 } else if (sgen_need_major_collection (size)) {
2705                         reason = "Minor allowance";
2706                         generation_to_collect = GENERATION_OLD;
2707                 } else {
2708                         generation_to_collect = GENERATION_NURSERY;
2709                         reason = "Nursery full";                        
2710                 }
2711         }
2712
2713         if (generation_to_collect == -1) {
2714                 if (concurrent_collection_in_progress && sgen_workers_all_done ()) {
2715                         generation_to_collect = GENERATION_OLD;
2716                         reason = "Finish concurrent collection";
2717                 }
2718         }
2719
2720         if (generation_to_collect == -1)
2721                 return;
2722         sgen_perform_collection (size, generation_to_collect, reason, FALSE);
2723 }
2724
2725 /*
2726  * LOCKING: Assumes the GC lock is held.
2727  */
2728 void
2729 sgen_perform_collection (size_t requested_size, int generation_to_collect, const char *reason, gboolean wait_to_finish)
2730 {
2731         TV_DECLARE (gc_start);
2732         TV_DECLARE (gc_end);
2733         TV_DECLARE (gc_total_start);
2734         TV_DECLARE (gc_total_end);
2735         GGTimingInfo infos [2];
2736         int overflow_generation_to_collect = -1;
2737         int oldest_generation_collected = generation_to_collect;
2738         const char *overflow_reason = NULL;
2739
2740         MONO_GC_REQUESTED (generation_to_collect, requested_size, wait_to_finish ? 1 : 0);
2741         if (wait_to_finish)
2742                 binary_protocol_collection_force (generation_to_collect);
2743
2744         SGEN_ASSERT (0, generation_to_collect == GENERATION_NURSERY || generation_to_collect == GENERATION_OLD, "What generation is this?");
2745
2746         mono_profiler_gc_event (MONO_GC_EVENT_START, generation_to_collect);
2747
2748         TV_GETTIME (gc_start);
2749
2750         sgen_stop_world (generation_to_collect);
2751
2752         TV_GETTIME (gc_total_start);
2753
2754         if (concurrent_collection_in_progress) {
2755                 /*
2756                  * We update the concurrent collection.  If it finished, we're done.  If
2757                  * not, and we've been asked to do a nursery collection, we do that.
2758                  */
2759                 gboolean finish = major_should_finish_concurrent_collection () || (wait_to_finish && generation_to_collect == GENERATION_OLD);
2760
2761                 if (finish) {
2762                         major_finish_concurrent_collection (wait_to_finish);
2763                         oldest_generation_collected = GENERATION_OLD;
2764                 } else {
2765                         sgen_workers_signal_start_nursery_collection_and_wait ();
2766
2767                         major_update_concurrent_collection ();
2768                         if (generation_to_collect == GENERATION_NURSERY)
2769                                 collect_nursery (NULL, FALSE);
2770
2771                         sgen_workers_signal_finish_nursery_collection ();
2772                 }
2773
2774                 goto done;
2775         }
2776
2777         /*
2778          * If we've been asked to do a major collection, and the major collector wants to
2779          * run synchronously (to evacuate), we set the flag to do that.
2780          */
2781         if (generation_to_collect == GENERATION_OLD &&
2782                         allow_synchronous_major &&
2783                         major_collector.want_synchronous_collection &&
2784                         *major_collector.want_synchronous_collection) {
2785                 wait_to_finish = TRUE;
2786         }
2787
2788         SGEN_ASSERT (0, !concurrent_collection_in_progress, "Why did this not get handled above?");
2789
2790         /*
2791          * There's no concurrent collection in progress.  Collect the generation we're asked
2792          * to collect.  If the major collector is concurrent and we're not forced to wait,
2793          * start a concurrent collection.
2794          */
2795         // FIXME: extract overflow reason
2796         if (generation_to_collect == GENERATION_NURSERY) {
2797                 if (collect_nursery (NULL, FALSE)) {
2798                         overflow_generation_to_collect = GENERATION_OLD;
2799                         overflow_reason = "Minor overflow";
2800                 }
2801         } else {
2802                 if (major_collector.is_concurrent && !wait_to_finish) {
2803                         collect_nursery (NULL, FALSE);
2804                         major_start_concurrent_collection (reason);
2805                         // FIXME: set infos[0] properly
2806                         goto done;
2807                 }
2808
2809                 if (major_do_collection (reason, wait_to_finish)) {
2810                         overflow_generation_to_collect = GENERATION_NURSERY;
2811                         overflow_reason = "Excessive pinning";
2812                 }
2813         }
2814
2815         TV_GETTIME (gc_end);
2816
2817         memset (infos, 0, sizeof (infos));
2818         infos [0].generation = generation_to_collect;
2819         infos [0].reason = reason;
2820         infos [0].is_overflow = FALSE;
2821         infos [1].generation = -1;
2822         infos [0].total_time = SGEN_TV_ELAPSED (gc_start, gc_end);
2823
2824         SGEN_ASSERT (0, !concurrent_collection_in_progress, "Why did this not get handled above?");
2825
2826         if (overflow_generation_to_collect != -1) {
2827                 /*
2828                  * We need to do an overflow collection, either because we ran out of memory
2829                  * or the nursery is fully pinned.
2830                  */
2831
2832                 mono_profiler_gc_event (MONO_GC_EVENT_START, overflow_generation_to_collect);
2833                 infos [1].generation = overflow_generation_to_collect;
2834                 infos [1].reason = overflow_reason;
2835                 infos [1].is_overflow = TRUE;
2836                 infos [1].total_time = gc_end;
2837
2838                 if (overflow_generation_to_collect == GENERATION_NURSERY)
2839                         collect_nursery (NULL, FALSE);
2840                 else
2841                         major_do_collection (overflow_reason, wait_to_finish);
2842
2843                 TV_GETTIME (gc_end);
2844                 infos [1].total_time = SGEN_TV_ELAPSED (infos [1].total_time, gc_end);
2845
2846                 /* keep events symmetric */
2847                 mono_profiler_gc_event (MONO_GC_EVENT_END, overflow_generation_to_collect);
2848
2849                 oldest_generation_collected = MAX (oldest_generation_collected, overflow_generation_to_collect);
2850         }
2851
2852         SGEN_LOG (2, "Heap size: %lu, LOS size: %lu", (unsigned long)mono_gc_get_heap_size (), (unsigned long)los_memory_usage);
2853
2854         /* this also sets the proper pointers for the next allocation */
2855         if (generation_to_collect == GENERATION_NURSERY && !sgen_can_alloc_size (requested_size)) {
2856                 /* TypeBuilder and MonoMethod are killing mcs with fragmentation */
2857                 SGEN_LOG (1, "nursery collection didn't find enough room for %zd alloc (%zd pinned)", requested_size, sgen_get_pinned_count ());
2858                 sgen_dump_pin_queue ();
2859                 degraded_mode = 1;
2860         }
2861
2862  done:
2863         g_assert (sgen_gray_object_queue_is_empty (&gray_queue));
2864
2865         TV_GETTIME (gc_total_end);
2866         time_max = MAX (time_max, TV_ELAPSED (gc_total_start, gc_total_end));
2867
2868         sgen_restart_world (oldest_generation_collected, infos);
2869
2870         mono_profiler_gc_event (MONO_GC_EVENT_END, generation_to_collect);
2871 }
2872
2873 /*
2874  * ######################################################################
2875  * ########  Memory allocation from the OS
2876  * ######################################################################
2877  * This section of code deals with getting memory from the OS and
2878  * allocating memory for GC-internal data structures.
2879  * Internal memory can be handled with a freelist for small objects.
2880  */
2881
2882 /*
2883  * Debug reporting.
2884  */
2885 G_GNUC_UNUSED static void
2886 report_internal_mem_usage (void)
2887 {
2888         printf ("Internal memory usage:\n");
2889         sgen_report_internal_mem_usage ();
2890         printf ("Pinned memory usage:\n");
2891         major_collector.report_pinned_memory_usage ();
2892 }
2893
2894 /*
2895  * ######################################################################
2896  * ########  Finalization support
2897  * ######################################################################
2898  */
2899
2900 /*
2901  * If the object has been forwarded it means it's still referenced from a root. 
2902  * If it is pinned it's still alive as well.
2903  * A LOS object is only alive if we have pinned it.
2904  * Return TRUE if @obj is ready to be finalized.
2905  */
2906 static inline gboolean
2907 sgen_is_object_alive (void *object)
2908 {
2909         if (ptr_in_nursery (object))
2910                 return sgen_nursery_is_object_alive (object);
2911
2912         return sgen_major_is_object_alive (object);
2913 }
2914
2915 /*
2916  * This function returns true if @object is either alive and belongs to the
2917  * current collection - major collections are full heap, so old gen objects
2918  * are never alive during a minor collection.
2919  */
2920 static inline int
2921 sgen_is_object_alive_and_on_current_collection (char *object)
2922 {
2923         if (ptr_in_nursery (object))
2924                 return sgen_nursery_is_object_alive (object);
2925
2926         if (current_collection_generation == GENERATION_NURSERY)
2927                 return FALSE;
2928
2929         return sgen_major_is_object_alive (object);
2930 }
2931
2932
2933 gboolean
2934 sgen_gc_is_object_ready_for_finalization (void *object)
2935 {
2936         return !sgen_is_object_alive (object);
2937 }
2938
2939 void
2940 sgen_queue_finalization_entry (MonoObject *obj)
2941 {
2942         FinalizeReadyEntry *entry = sgen_alloc_internal (INTERNAL_MEM_FINALIZE_READY_ENTRY);
2943         gboolean critical = sgen_client_object_has_critical_finalizer (obj);
2944         entry->object = obj;
2945         if (critical) {
2946                 entry->next = critical_fin_list;
2947                 critical_fin_list = entry;
2948         } else {
2949                 entry->next = fin_ready_list;
2950                 fin_ready_list = entry;
2951         }
2952
2953         sgen_client_object_queued_for_finalization (obj);
2954
2955 #ifdef ENABLE_DTRACE
2956         if (G_UNLIKELY (MONO_GC_FINALIZE_ENQUEUE_ENABLED ())) {
2957                 int gen = sgen_ptr_in_nursery (obj) ? GENERATION_NURSERY : GENERATION_OLD;
2958                 MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (obj);
2959                 MONO_GC_FINALIZE_ENQUEUE ((mword)obj, sgen_safe_object_get_size (obj),
2960                                 vt->klass->name_space, vt->klass->name, gen, critical);
2961         }
2962 #endif
2963 }
2964
2965 gboolean
2966 sgen_object_is_live (void *obj)
2967 {
2968         return sgen_is_object_alive_and_on_current_collection (obj);
2969 }
2970
2971 int
2972 mono_gc_invoke_finalizers (void)
2973 {
2974         FinalizeReadyEntry *entry = NULL;
2975         gboolean entry_is_critical = FALSE;
2976         int count = 0;
2977         void *obj;
2978         /* FIXME: batch to reduce lock contention */
2979         while (fin_ready_list || critical_fin_list) {
2980                 LOCK_GC;
2981
2982                 if (entry) {
2983                         FinalizeReadyEntry **list = entry_is_critical ? &critical_fin_list : &fin_ready_list;
2984
2985                         /* We have finalized entry in the last
2986                            interation, now we need to remove it from
2987                            the list. */
2988                         if (*list == entry)
2989                                 *list = entry->next;
2990                         else {
2991                                 FinalizeReadyEntry *e = *list;
2992                                 while (e->next != entry)
2993                                         e = e->next;
2994                                 e->next = entry->next;
2995                         }
2996                         sgen_free_internal (entry, INTERNAL_MEM_FINALIZE_READY_ENTRY);
2997                         entry = NULL;
2998                 }
2999
3000                 /* Now look for the first non-null entry. */
3001                 for (entry = fin_ready_list; entry && !entry->object; entry = entry->next)
3002                         ;
3003                 if (entry) {
3004                         entry_is_critical = FALSE;
3005                 } else {
3006                         entry_is_critical = TRUE;
3007                         for (entry = critical_fin_list; entry && !entry->object; entry = entry->next)
3008                                 ;
3009                 }
3010
3011                 if (entry) {
3012                         g_assert (entry->object);
3013                         num_ready_finalizers--;
3014                         obj = entry->object;
3015                         entry->object = NULL;
3016                         SGEN_LOG (7, "Finalizing object %p (%s)", obj, safe_name (obj));
3017                 }
3018
3019                 UNLOCK_GC;
3020
3021                 if (!entry)
3022                         break;
3023
3024                 g_assert (entry->object == NULL);
3025                 count++;
3026                 /* the object is on the stack so it is pinned */
3027                 /*g_print ("Calling finalizer for object: %p (%s)\n", entry->object, safe_name (entry->object));*/
3028                 mono_gc_run_finalize (obj, NULL);
3029         }
3030         g_assert (!entry);
3031         return count;
3032 }
3033
3034 gboolean
3035 mono_gc_pending_finalizers (void)
3036 {
3037         return fin_ready_list || critical_fin_list;
3038 }
3039
3040 /*
3041  * ######################################################################
3042  * ########  registered roots support
3043  * ######################################################################
3044  */
3045
3046 /*
3047  * We do not coalesce roots.
3048  */
3049 static int
3050 mono_gc_register_root_inner (char *start, size_t size, void *descr, int root_type)
3051 {
3052         RootRecord new_root;
3053         int i;
3054         LOCK_GC;
3055         for (i = 0; i < ROOT_TYPE_NUM; ++i) {
3056                 RootRecord *root = sgen_hash_table_lookup (&roots_hash [i], start);
3057                 /* we allow changing the size and the descriptor (for thread statics etc) */
3058                 if (root) {
3059                         size_t old_size = root->end_root - start;
3060                         root->end_root = start + size;
3061                         g_assert (((root->root_desc != 0) && (descr != NULL)) ||
3062                                           ((root->root_desc == 0) && (descr == NULL)));
3063                         root->root_desc = (mword)descr;
3064                         roots_size += size;
3065                         roots_size -= old_size;
3066                         UNLOCK_GC;
3067                         return TRUE;
3068                 }
3069         }
3070
3071         new_root.end_root = start + size;
3072         new_root.root_desc = (mword)descr;
3073
3074         sgen_hash_table_replace (&roots_hash [root_type], start, &new_root, NULL);
3075         roots_size += size;
3076
3077         SGEN_LOG (3, "Added root for range: %p-%p, descr: %p  (%d/%d bytes)", start, new_root.end_root, descr, (int)size, (int)roots_size);
3078
3079         UNLOCK_GC;
3080         return TRUE;
3081 }
3082
3083 int
3084 mono_gc_register_root (char *start, size_t size, void *descr)
3085 {
3086         return mono_gc_register_root_inner (start, size, descr, descr ? ROOT_TYPE_NORMAL : ROOT_TYPE_PINNED);
3087 }
3088
3089 int
3090 mono_gc_register_root_wbarrier (char *start, size_t size, void *descr)
3091 {
3092         return mono_gc_register_root_inner (start, size, descr, ROOT_TYPE_WBARRIER);
3093 }
3094
3095 void
3096 mono_gc_deregister_root (char* addr)
3097 {
3098         int root_type;
3099         RootRecord root;
3100
3101         LOCK_GC;
3102         for (root_type = 0; root_type < ROOT_TYPE_NUM; ++root_type) {
3103                 if (sgen_hash_table_remove (&roots_hash [root_type], addr, &root))
3104                         roots_size -= (root.end_root - addr);
3105         }
3106         UNLOCK_GC;
3107 }
3108
3109 /*
3110  * ######################################################################
3111  * ########  Thread handling (stop/start code)
3112  * ######################################################################
3113  */
3114
3115 unsigned int sgen_global_stop_count = 0;
3116
3117 int
3118 sgen_get_current_collection_generation (void)
3119 {
3120         return current_collection_generation;
3121 }
3122
3123 void
3124 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
3125 {
3126         gc_callbacks = *callbacks;
3127 }
3128
3129 MonoGCCallbacks *
3130 mono_gc_get_gc_callbacks ()
3131 {
3132         return &gc_callbacks;
3133 }
3134
3135 /* Variables holding start/end nursery so it won't have to be passed at every call */
3136 static void *scan_area_arg_start, *scan_area_arg_end;
3137
3138 void
3139 mono_gc_conservatively_scan_area (void *start, void *end)
3140 {
3141         conservatively_pin_objects_from (start, end, scan_area_arg_start, scan_area_arg_end, PIN_TYPE_STACK);
3142 }
3143
3144 void*
3145 mono_gc_scan_object (void *obj, void *gc_data)
3146 {
3147         ScanCopyContext *ctx = gc_data;
3148         ctx->ops->copy_or_mark_object (&obj, ctx->queue);
3149         return obj;
3150 }
3151
3152 /*
3153  * Mark from thread stacks and registers.
3154  */
3155 static void
3156 scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise, ScanCopyContext ctx)
3157 {
3158         SgenThreadInfo *info;
3159
3160         scan_area_arg_start = start_nursery;
3161         scan_area_arg_end = end_nursery;
3162
3163         FOREACH_THREAD (info) {
3164                 if (info->skip) {
3165                         SGEN_LOG (3, "Skipping dead thread %p, range: %p-%p, size: %td", info, info->stack_start, info->stack_end, (char*)info->stack_end - (char*)info->stack_start);
3166                         continue;
3167                 }
3168                 if (info->gc_disabled) {
3169                         SGEN_LOG (3, "GC disabled for thread %p, range: %p-%p, size: %td", info, info->stack_start, info->stack_end, (char*)info->stack_end - (char*)info->stack_start);
3170                         continue;
3171                 }
3172                 if (!mono_thread_info_is_live (info)) {
3173                         SGEN_LOG (3, "Skipping non-running thread %p, range: %p-%p, size: %td (state %x)", info, info->stack_start, info->stack_end, (char*)info->stack_end - (char*)info->stack_start, info->info.thread_state);
3174                         continue;
3175                 }
3176                 g_assert (info->suspend_done);
3177                 SGEN_LOG (3, "Scanning thread %p, range: %p-%p, size: %td, pinned=%zd", info, info->stack_start, info->stack_end, (char*)info->stack_end - (char*)info->stack_start, sgen_get_pinned_count ());
3178                 if (gc_callbacks.thread_mark_func && !conservative_stack_mark) {
3179                         gc_callbacks.thread_mark_func (info->runtime_data, info->stack_start, info->stack_end, precise, &ctx);
3180                 } else if (!precise) {
3181                         if (!conservative_stack_mark) {
3182                                 fprintf (stderr, "Precise stack mark not supported - disabling.\n");
3183                                 conservative_stack_mark = TRUE;
3184                         }
3185                         conservatively_pin_objects_from (info->stack_start, info->stack_end, start_nursery, end_nursery, PIN_TYPE_STACK);
3186                 }
3187
3188                 if (!precise) {
3189 #ifdef USE_MONO_CTX
3190                         conservatively_pin_objects_from ((void**)&info->ctx, (void**)&info->ctx + ARCH_NUM_REGS,
3191                                 start_nursery, end_nursery, PIN_TYPE_STACK);
3192 #else
3193                         conservatively_pin_objects_from ((void**)&info->regs, (void**)&info->regs + ARCH_NUM_REGS,
3194                                         start_nursery, end_nursery, PIN_TYPE_STACK);
3195 #endif
3196                 }
3197         } END_FOREACH_THREAD
3198 }
3199
3200 static void*
3201 sgen_thread_register (SgenThreadInfo* info, void *addr)
3202 {
3203         size_t stsize = 0;
3204         guint8 *staddr = NULL;
3205
3206 #ifndef HAVE_KW_THREAD
3207         info->tlab_start = info->tlab_next = info->tlab_temp_end = info->tlab_real_end = NULL;
3208
3209         g_assert (!mono_native_tls_get_value (thread_info_key));
3210         mono_native_tls_set_value (thread_info_key, info);
3211 #else
3212         sgen_thread_info = info;
3213 #endif
3214
3215 #ifdef SGEN_POSIX_STW
3216         info->stop_count = -1;
3217         info->signal = 0;
3218 #endif
3219         info->skip = 0;
3220         info->stack_start = NULL;
3221         info->stopped_ip = NULL;
3222         info->stopped_domain = NULL;
3223 #ifdef USE_MONO_CTX
3224         memset (&info->ctx, 0, sizeof (MonoContext));
3225 #else
3226         memset (&info->regs, 0, sizeof (info->regs));
3227 #endif
3228
3229         sgen_init_tlab_info (info);
3230
3231         binary_protocol_thread_register ((gpointer)mono_thread_info_get_tid (info));
3232
3233         /* On win32, stack_start_limit should be 0, since the stack can grow dynamically */
3234         mono_thread_info_get_stack_bounds (&staddr, &stsize);
3235         if (staddr) {
3236 #ifndef HOST_WIN32
3237                 info->stack_start_limit = staddr;
3238 #endif
3239                 info->stack_end = staddr + stsize;
3240         } else {
3241                 gsize stack_bottom = (gsize)addr;
3242                 stack_bottom += 4095;
3243                 stack_bottom &= ~4095;
3244                 info->stack_end = (char*)stack_bottom;
3245         }
3246
3247 #ifdef HAVE_KW_THREAD
3248         stack_end = info->stack_end;
3249 #endif
3250
3251         SGEN_LOG (3, "registered thread %p (%p) stack end %p", info, (gpointer)mono_thread_info_get_tid (info), info->stack_end);
3252
3253         if (gc_callbacks.thread_attach_func)
3254                 info->runtime_data = gc_callbacks.thread_attach_func ();
3255         return info;
3256 }
3257
3258 static void
3259 sgen_thread_detach (SgenThreadInfo *p)
3260 {
3261         /* If a delegate is passed to native code and invoked on a thread we dont
3262          * know about, the jit will register it with mono_jit_thread_attach, but
3263          * we have no way of knowing when that thread goes away.  SGen has a TSD
3264          * so we assume that if the domain is still registered, we can detach
3265          * the thread
3266          */
3267         if (mono_domain_get ())
3268                 mono_thread_detach_internal (mono_thread_internal_current ());
3269 }
3270
3271 static void
3272 sgen_thread_unregister (SgenThreadInfo *p)
3273 {
3274         MonoNativeThreadId tid;
3275
3276         tid = mono_thread_info_get_tid (p);
3277         binary_protocol_thread_unregister ((gpointer)tid);
3278         SGEN_LOG (3, "unregister thread %p (%p)", p, (gpointer)tid);
3279
3280 #ifndef HAVE_KW_THREAD
3281         mono_native_tls_set_value (thread_info_key, NULL);
3282 #else
3283         sgen_thread_info = NULL;
3284 #endif
3285
3286         if (p->info.runtime_thread)
3287                 mono_threads_add_joinable_thread ((gpointer)tid);
3288
3289         if (gc_callbacks.thread_detach_func) {
3290                 gc_callbacks.thread_detach_func (p->runtime_data);
3291                 p->runtime_data = NULL;
3292         }
3293 }
3294
3295
3296 static void
3297 sgen_thread_attach (SgenThreadInfo *info)
3298 {
3299         LOCK_GC;
3300         /*this is odd, can we get attached before the gc is inited?*/
3301         init_stats ();
3302         UNLOCK_GC;
3303         
3304         if (gc_callbacks.thread_attach_func && !info->runtime_data)
3305                 info->runtime_data = gc_callbacks.thread_attach_func ();
3306 }
3307 gboolean
3308 mono_gc_register_thread (void *baseptr)
3309 {
3310         return mono_thread_info_attach (baseptr) != NULL;
3311 }
3312
3313 /*
3314  * mono_gc_set_stack_end:
3315  *
3316  *   Set the end of the current threads stack to STACK_END. The stack space between 
3317  * STACK_END and the real end of the threads stack will not be scanned during collections.
3318  */
3319 void
3320 mono_gc_set_stack_end (void *stack_end)
3321 {
3322         SgenThreadInfo *info;
3323
3324         LOCK_GC;
3325         info = mono_thread_info_current ();
3326         if (info) {
3327                 g_assert (stack_end < info->stack_end);
3328                 info->stack_end = stack_end;
3329         }
3330         UNLOCK_GC;
3331 }
3332
3333 #if USE_PTHREAD_INTERCEPT
3334
3335
3336 int
3337 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
3338 {
3339         return pthread_create (new_thread, attr, start_routine, arg);
3340 }
3341
3342 int
3343 mono_gc_pthread_join (pthread_t thread, void **retval)
3344 {
3345         return pthread_join (thread, retval);
3346 }
3347
3348 int
3349 mono_gc_pthread_detach (pthread_t thread)
3350 {
3351         return pthread_detach (thread);
3352 }
3353
3354 void
3355 mono_gc_pthread_exit (void *retval) 
3356 {
3357         mono_thread_info_detach ();
3358         pthread_exit (retval);
3359         g_assert_not_reached ();
3360 }
3361
3362 #endif /* USE_PTHREAD_INTERCEPT */
3363
3364 /*
3365  * ######################################################################
3366  * ########  Write barriers
3367  * ######################################################################
3368  */
3369
3370 /*
3371  * Note: the write barriers first do the needed GC work and then do the actual store:
3372  * this way the value is visible to the conservative GC scan after the write barrier
3373  * itself. If a GC interrupts the barrier in the middle, value will be kept alive by
3374  * the conservative scan, otherwise by the remembered set scan.
3375  */
3376 void
3377 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
3378 {
3379         HEAVY_STAT (++stat_wbarrier_set_field);
3380         if (ptr_in_nursery (field_ptr)) {
3381                 *(void**)field_ptr = value;
3382                 return;
3383         }
3384         SGEN_LOG (8, "Adding remset at %p", field_ptr);
3385         if (value)
3386                 binary_protocol_wbarrier (field_ptr, value, value->vtable);
3387
3388         remset.wbarrier_set_field (obj, field_ptr, value);
3389 }
3390
3391 void
3392 mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
3393 {
3394         HEAVY_STAT (++stat_wbarrier_arrayref_copy);
3395         /*This check can be done without taking a lock since dest_ptr array is pinned*/
3396         if (ptr_in_nursery (dest_ptr) || count <= 0) {
3397                 mono_gc_memmove_aligned (dest_ptr, src_ptr, count * sizeof (gpointer));
3398                 return;
3399         }
3400
3401 #ifdef SGEN_HEAVY_BINARY_PROTOCOL
3402         if (binary_protocol_is_heavy_enabled ()) {
3403                 int i;
3404                 for (i = 0; i < count; ++i) {
3405                         gpointer dest = (gpointer*)dest_ptr + i;
3406                         gpointer obj = *((gpointer*)src_ptr + i);
3407                         if (obj)
3408                                 binary_protocol_wbarrier (dest, obj, (gpointer)LOAD_VTABLE (obj));
3409                 }
3410         }
3411 #endif
3412
3413         remset.wbarrier_arrayref_copy (dest_ptr, src_ptr, count);
3414 }
3415
3416 void
3417 mono_gc_wbarrier_generic_nostore (gpointer ptr)
3418 {
3419         gpointer obj;
3420
3421         HEAVY_STAT (++stat_wbarrier_generic_store);
3422
3423         sgen_client_wbarrier_generic_nostore_check (ptr);
3424
3425         obj = *(gpointer*)ptr;
3426         if (obj)
3427                 binary_protocol_wbarrier (ptr, obj, (gpointer)LOAD_VTABLE (obj));
3428
3429         /*
3430          * We need to record old->old pointer locations for the
3431          * concurrent collector.
3432          */
3433         if (!ptr_in_nursery (obj) && !concurrent_collection_in_progress) {
3434                 SGEN_LOG (8, "Skipping remset at %p", ptr);
3435                 return;
3436         }
3437
3438         SGEN_LOG (8, "Adding remset at %p", ptr);
3439
3440         remset.wbarrier_generic_nostore (ptr);
3441 }
3442
3443 void
3444 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
3445 {
3446         SGEN_LOG (8, "Wbarrier store at %p to %p (%s)", ptr, value, value ? safe_name (value) : "null");
3447         SGEN_UPDATE_REFERENCE_ALLOW_NULL (ptr, value);
3448         if (ptr_in_nursery (value))
3449                 mono_gc_wbarrier_generic_nostore (ptr);
3450         sgen_dummy_use (value);
3451 }
3452
3453 /* Same as mono_gc_wbarrier_generic_store () but performs the store
3454  * as an atomic operation with release semantics.
3455  */
3456 void
3457 mono_gc_wbarrier_generic_store_atomic (gpointer ptr, MonoObject *value)
3458 {
3459         HEAVY_STAT (++stat_wbarrier_generic_store_atomic);
3460
3461         SGEN_LOG (8, "Wbarrier atomic store at %p to %p (%s)", ptr, value, value ? safe_name (value) : "null");
3462
3463         InterlockedWritePointer (ptr, value);
3464
3465         if (ptr_in_nursery (value))
3466                 mono_gc_wbarrier_generic_nostore (ptr);
3467
3468         sgen_dummy_use (value);
3469 }
3470
3471 void mono_gc_wbarrier_value_copy_bitmap (gpointer _dest, gpointer _src, int size, unsigned bitmap)
3472 {
3473         mword *dest = _dest;
3474         mword *src = _src;
3475
3476         while (size) {
3477                 if (bitmap & 0x1)
3478                         mono_gc_wbarrier_generic_store (dest, (MonoObject*)*src);
3479                 else
3480                         SGEN_UPDATE_REFERENCE_ALLOW_NULL (dest, *src);
3481                 ++src;
3482                 ++dest;
3483                 size -= SIZEOF_VOID_P;
3484                 bitmap >>= 1;
3485         }
3486 }
3487
3488 /*
3489  * ######################################################################
3490  * ########  Other mono public interface functions.
3491  * ######################################################################
3492  */
3493
3494 #define REFS_SIZE 128
3495 typedef struct {
3496         void *data;
3497         MonoGCReferences callback;
3498         int flags;
3499         int count;
3500         int called;
3501         MonoObject *refs [REFS_SIZE];
3502         uintptr_t offsets [REFS_SIZE];
3503 } HeapWalkInfo;
3504
3505 #undef HANDLE_PTR
3506 #define HANDLE_PTR(ptr,obj)     do {    \
3507                 if (*(ptr)) {   \
3508                         if (hwi->count == REFS_SIZE) {  \
3509                                 hwi->callback ((MonoObject*)start, mono_object_class (start), hwi->called? 0: size, hwi->count, hwi->refs, hwi->offsets, hwi->data);    \
3510                                 hwi->count = 0; \
3511                                 hwi->called = 1;        \
3512                         }       \
3513                         hwi->offsets [hwi->count] = (char*)(ptr)-(char*)start;  \
3514                         hwi->refs [hwi->count++] = *(ptr);      \
3515                 }       \
3516         } while (0)
3517
3518 static void
3519 collect_references (HeapWalkInfo *hwi, char *start, size_t size)
3520 {
3521         mword desc = sgen_obj_get_descriptor (start);
3522
3523 #include "sgen-scan-object.h"
3524 }
3525
3526 static void
3527 walk_references (char *start, size_t size, void *data)
3528 {
3529         HeapWalkInfo *hwi = data;
3530         hwi->called = 0;
3531         hwi->count = 0;
3532         collect_references (hwi, start, size);
3533         if (hwi->count || !hwi->called)
3534                 hwi->callback ((MonoObject*)start, mono_object_class (start), hwi->called? 0: size, hwi->count, hwi->refs, hwi->offsets, hwi->data);
3535 }
3536
3537 /**
3538  * mono_gc_walk_heap:
3539  * @flags: flags for future use
3540  * @callback: a function pointer called for each object in the heap
3541  * @data: a user data pointer that is passed to callback
3542  *
3543  * This function can be used to iterate over all the live objects in the heap:
3544  * for each object, @callback is invoked, providing info about the object's
3545  * location in memory, its class, its size and the objects it references.
3546  * For each referenced object it's offset from the object address is
3547  * reported in the offsets array.
3548  * The object references may be buffered, so the callback may be invoked
3549  * multiple times for the same object: in all but the first call, the size
3550  * argument will be zero.
3551  * Note that this function can be only called in the #MONO_GC_EVENT_PRE_START_WORLD
3552  * profiler event handler.
3553  *
3554  * Returns: a non-zero value if the GC doesn't support heap walking
3555  */
3556 int
3557 mono_gc_walk_heap (int flags, MonoGCReferences callback, void *data)
3558 {
3559         HeapWalkInfo hwi;
3560
3561         hwi.flags = flags;
3562         hwi.callback = callback;
3563         hwi.data = data;
3564
3565         sgen_clear_nursery_fragments ();
3566         sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data, walk_references, &hwi, FALSE);
3567
3568         major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, walk_references, &hwi);
3569         sgen_los_iterate_objects (walk_references, &hwi);
3570
3571         return 0;
3572 }
3573
3574 void
3575 mono_gc_collect (int generation)
3576 {
3577         LOCK_GC;
3578         if (generation > 1)
3579                 generation = 1;
3580         sgen_perform_collection (0, generation, "user request", TRUE);
3581         UNLOCK_GC;
3582 }
3583
3584 int
3585 mono_gc_max_generation (void)
3586 {
3587         return 1;
3588 }
3589
3590 int
3591 mono_gc_collection_count (int generation)
3592 {
3593         if (generation == 0)
3594                 return gc_stats.minor_gc_count;
3595         return gc_stats.major_gc_count;
3596 }
3597
3598 int64_t
3599 mono_gc_get_used_size (void)
3600 {
3601         gint64 tot = 0;
3602         LOCK_GC;
3603         tot = los_memory_usage;
3604         tot += nursery_section->next_data - nursery_section->data;
3605         tot += major_collector.get_used_size ();
3606         /* FIXME: account for pinned objects */
3607         UNLOCK_GC;
3608         return tot;
3609 }
3610
3611 int
3612 mono_gc_get_los_limit (void)
3613 {
3614         return MAX_SMALL_OBJ_SIZE;
3615 }
3616
3617 gboolean
3618 mono_gc_user_markers_supported (void)
3619 {
3620         return TRUE;
3621 }
3622
3623 gboolean
3624 mono_object_is_alive (MonoObject* o)
3625 {
3626         return TRUE;
3627 }
3628
3629 int
3630 mono_gc_get_generation (MonoObject *obj)
3631 {
3632         if (ptr_in_nursery (obj))
3633                 return 0;
3634         return 1;
3635 }
3636
3637 void
3638 mono_gc_enable_events (void)
3639 {
3640 }
3641
3642 void
3643 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
3644 {
3645         sgen_register_disappearing_link (obj, link_addr, track, FALSE);
3646 }
3647
3648 void
3649 mono_gc_weak_link_remove (void **link_addr, gboolean track)
3650 {
3651         sgen_register_disappearing_link (NULL, link_addr, track, FALSE);
3652 }
3653
3654 MonoObject*
3655 mono_gc_weak_link_get (void **link_addr)
3656 {
3657         void * volatile *link_addr_volatile;
3658         void *ptr;
3659         MonoObject *obj;
3660  retry:
3661         link_addr_volatile = link_addr;
3662         ptr = (void*)*link_addr_volatile;
3663         /*
3664          * At this point we have a hidden pointer.  If the GC runs
3665          * here, it will not recognize the hidden pointer as a
3666          * reference, and if the object behind it is not referenced
3667          * elsewhere, it will be freed.  Once the world is restarted
3668          * we reveal the pointer, giving us a pointer to a freed
3669          * object.  To make sure we don't return it, we load the
3670          * hidden pointer again.  If it's still the same, we can be
3671          * sure the object reference is valid.
3672          */
3673         if (ptr)
3674                 obj = (MonoObject*) REVEAL_POINTER (ptr);
3675         else
3676                 return NULL;
3677
3678         mono_memory_barrier ();
3679
3680         /*
3681          * During the second bridge processing step the world is
3682          * running again.  That step processes all weak links once
3683          * more to null those that refer to dead objects.  Before that
3684          * is completed, those links must not be followed, so we
3685          * conservatively wait for bridge processing when any weak
3686          * link is dereferenced.
3687          */
3688         if (G_UNLIKELY (bridge_processing_in_progress))
3689                 mono_gc_wait_for_bridge_processing ();
3690
3691         if ((void*)*link_addr_volatile != ptr)
3692                 goto retry;
3693
3694         return obj;
3695 }
3696
3697 gboolean
3698 mono_gc_set_allow_synchronous_major (gboolean flag)
3699 {
3700         if (!major_collector.is_concurrent)
3701                 return flag;
3702
3703         allow_synchronous_major = flag;
3704         return TRUE;
3705 }
3706
3707 void*
3708 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
3709 {
3710         void *result;
3711         LOCK_INTERRUPTION;
3712         result = func (data);
3713         UNLOCK_INTERRUPTION;
3714         return result;
3715 }
3716
3717 gboolean
3718 mono_gc_is_gc_thread (void)
3719 {
3720         gboolean result;
3721         LOCK_GC;
3722         result = mono_thread_info_current () != NULL;
3723         UNLOCK_GC;
3724         return result;
3725 }
3726
3727 static gboolean
3728 is_critical_method (MonoMethod *method)
3729 {
3730         return mono_runtime_is_critical_method (method) || sgen_is_critical_method (method);
3731 }
3732
3733 void
3734 sgen_env_var_error (const char *env_var, const char *fallback, const char *description_format, ...)
3735 {
3736         va_list ap;
3737
3738         va_start (ap, description_format);
3739
3740         fprintf (stderr, "Warning: In environment variable `%s': ", env_var);
3741         vfprintf (stderr, description_format, ap);
3742         if (fallback)
3743                 fprintf (stderr, " - %s", fallback);
3744         fprintf (stderr, "\n");
3745
3746         va_end (ap);
3747 }
3748
3749 static gboolean
3750 parse_double_in_interval (const char *env_var, const char *opt_name, const char *opt, double min, double max, double *result)
3751 {
3752         char *endptr;
3753         double val = strtod (opt, &endptr);
3754         if (endptr == opt) {
3755                 sgen_env_var_error (env_var, "Using default value.", "`%s` must be a number.", opt_name);
3756                 return FALSE;
3757         }
3758         else if (val < min || val > max) {
3759                 sgen_env_var_error (env_var, "Using default value.", "`%s` must be between %.2f - %.2f.", opt_name, min, max);
3760                 return FALSE;
3761         }
3762         *result = val;
3763         return TRUE;
3764 }
3765
3766 static gboolean
3767 thread_in_critical_region (SgenThreadInfo *info)
3768 {
3769         return info->in_critical_region;
3770 }
3771
3772 void
3773 mono_gc_base_init (void)
3774 {
3775         MonoThreadInfoCallbacks cb;
3776         const char *env;
3777         char **opts, **ptr;
3778         char *major_collector_opt = NULL;
3779         char *minor_collector_opt = NULL;
3780         size_t max_heap = 0;
3781         size_t soft_limit = 0;
3782         int result;
3783         int dummy;
3784         gboolean debug_print_allowance = FALSE;
3785         double allowance_ratio = 0, save_target = 0;
3786         gboolean cement_enabled = TRUE;
3787
3788         mono_counters_init ();
3789
3790         do {
3791                 result = InterlockedCompareExchange (&gc_initialized, -1, 0);
3792                 switch (result) {
3793                 case 1:
3794                         /* already inited */
3795                         return;
3796                 case -1:
3797                         /* being inited by another thread */
3798                         g_usleep (1000);
3799                         break;
3800                 case 0:
3801                         /* we will init it */
3802                         break;
3803                 default:
3804                         g_assert_not_reached ();
3805                 }
3806         } while (result != 0);
3807
3808         SGEN_TV_GETTIME (sgen_init_timestamp);
3809
3810         LOCK_INIT (gc_mutex);
3811
3812         pagesize = mono_pagesize ();
3813         gc_debug_file = stderr;
3814
3815         cb.thread_register = sgen_thread_register;
3816         cb.thread_detach = sgen_thread_detach;
3817         cb.thread_unregister = sgen_thread_unregister;
3818         cb.thread_attach = sgen_thread_attach;
3819         cb.mono_method_is_critical = (gpointer)is_critical_method;
3820         cb.mono_thread_in_critical_region = thread_in_critical_region;
3821 #ifndef HOST_WIN32
3822         cb.thread_exit = mono_gc_pthread_exit;
3823         cb.mono_gc_pthread_create = (gpointer)mono_gc_pthread_create;
3824 #endif
3825
3826         mono_threads_init (&cb, sizeof (SgenThreadInfo));
3827
3828         LOCK_INIT (sgen_interruption_mutex);
3829
3830         if ((env = g_getenv (MONO_GC_PARAMS_NAME))) {
3831                 opts = g_strsplit (env, ",", -1);
3832                 for (ptr = opts; *ptr; ++ptr) {
3833                         char *opt = *ptr;
3834                         if (g_str_has_prefix (opt, "major=")) {
3835                                 opt = strchr (opt, '=') + 1;
3836                                 major_collector_opt = g_strdup (opt);
3837                         } else if (g_str_has_prefix (opt, "minor=")) {
3838                                 opt = strchr (opt, '=') + 1;
3839                                 minor_collector_opt = g_strdup (opt);
3840                         }
3841                 }
3842         } else {
3843                 opts = NULL;
3844         }
3845
3846         init_stats ();
3847         sgen_init_internal_allocator ();
3848         sgen_init_nursery_allocator ();
3849         sgen_init_fin_weak_hash ();
3850         sgen_init_stw ();
3851         sgen_init_hash_table ();
3852         sgen_init_descriptors ();
3853         sgen_init_gray_queues ();
3854         sgen_init_allocator ();
3855
3856         sgen_register_fixed_internal_mem_type (INTERNAL_MEM_SECTION, SGEN_SIZEOF_GC_MEM_SECTION);
3857         sgen_register_fixed_internal_mem_type (INTERNAL_MEM_FINALIZE_READY_ENTRY, sizeof (FinalizeReadyEntry));
3858         sgen_register_fixed_internal_mem_type (INTERNAL_MEM_GRAY_QUEUE, sizeof (GrayQueueSection));
3859
3860         sgen_client_init ();
3861
3862 #ifndef HAVE_KW_THREAD
3863         mono_native_tls_alloc (&thread_info_key, NULL);
3864 #if defined(__APPLE__) || defined (HOST_WIN32)
3865         /* 
3866          * CEE_MONO_TLS requires the tls offset, not the key, so the code below only works on darwin,
3867          * where the two are the same.
3868          */
3869         mono_tls_key_set_offset (TLS_KEY_SGEN_THREAD_INFO, thread_info_key);
3870 #endif
3871 #else
3872         {
3873                 int tls_offset = -1;
3874                 MONO_THREAD_VAR_OFFSET (sgen_thread_info, tls_offset);
3875                 mono_tls_key_set_offset (TLS_KEY_SGEN_THREAD_INFO, tls_offset);
3876         }
3877 #endif
3878
3879         /*
3880          * This needs to happen before any internal allocations because
3881          * it inits the small id which is required for hazard pointer
3882          * operations.
3883          */
3884         sgen_os_init ();
3885
3886         mono_thread_info_attach (&dummy);
3887
3888         if (!minor_collector_opt) {
3889                 sgen_simple_nursery_init (&sgen_minor_collector);
3890         } else {
3891                 if (!strcmp (minor_collector_opt, "simple")) {
3892                 use_simple_nursery:
3893                         sgen_simple_nursery_init (&sgen_minor_collector);
3894                 } else if (!strcmp (minor_collector_opt, "split")) {
3895                         sgen_split_nursery_init (&sgen_minor_collector);
3896                 } else {
3897                         sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using `simple` instead.", "Unknown minor collector `%s'.", minor_collector_opt);
3898                         goto use_simple_nursery;
3899                 }
3900         }
3901
3902         if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep")) {
3903         use_marksweep_major:
3904                 sgen_marksweep_init (&major_collector);
3905         } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-conc")) {
3906                 sgen_marksweep_conc_init (&major_collector);
3907         } else {
3908                 sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using `marksweep` instead.", "Unknown major collector `%s'.", major_collector_opt);
3909                 goto use_marksweep_major;
3910         }
3911
3912         ///* Keep this the default for now */
3913         /* Precise marking is broken on all supported targets. Disable until fixed. */
3914         conservative_stack_mark = TRUE;
3915
3916         sgen_nursery_size = DEFAULT_NURSERY_SIZE;
3917
3918         if (major_collector.is_concurrent)
3919                 cement_enabled = FALSE;
3920
3921         if (opts) {
3922                 gboolean usage_printed = FALSE;
3923
3924                 for (ptr = opts; *ptr; ++ptr) {
3925                         char *opt = *ptr;
3926                         if (!strcmp (opt, ""))
3927                                 continue;
3928                         if (g_str_has_prefix (opt, "major="))
3929                                 continue;
3930                         if (g_str_has_prefix (opt, "minor="))
3931                                 continue;
3932                         if (g_str_has_prefix (opt, "max-heap-size=")) {
3933                                 size_t max_heap_candidate = 0;
3934                                 opt = strchr (opt, '=') + 1;
3935                                 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &max_heap_candidate)) {
3936                                         max_heap = (max_heap_candidate + mono_pagesize () - 1) & ~(size_t)(mono_pagesize () - 1);
3937                                         if (max_heap != max_heap_candidate)
3938                                                 sgen_env_var_error (MONO_GC_PARAMS_NAME, "Rounding up.", "`max-heap-size` size must be a multiple of %d.", mono_pagesize ());
3939                                 } else {
3940                                         sgen_env_var_error (MONO_GC_PARAMS_NAME, NULL, "`max-heap-size` must be an integer.");
3941                                 }
3942                                 continue;
3943                         }
3944                         if (g_str_has_prefix (opt, "soft-heap-limit=")) {
3945                                 opt = strchr (opt, '=') + 1;
3946                                 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &soft_limit)) {
3947                                         if (soft_limit <= 0) {
3948                                                 sgen_env_var_error (MONO_GC_PARAMS_NAME, NULL, "`soft-heap-limit` must be positive.");
3949                                                 soft_limit = 0;
3950                                         }
3951                                 } else {
3952                                         sgen_env_var_error (MONO_GC_PARAMS_NAME, NULL, "`soft-heap-limit` must be an integer.");
3953                                 }
3954                                 continue;
3955                         }
3956                         if (g_str_has_prefix (opt, "stack-mark=")) {
3957                                 opt = strchr (opt, '=') + 1;
3958                                 if (!strcmp (opt, "precise")) {
3959                                         conservative_stack_mark = FALSE;
3960                                 } else if (!strcmp (opt, "conservative")) {
3961                                         conservative_stack_mark = TRUE;
3962                                 } else {
3963                                         sgen_env_var_error (MONO_GC_PARAMS_NAME, conservative_stack_mark ? "Using `conservative`." : "Using `precise`.",
3964                                                         "Invalid value `%s` for `stack-mark` option, possible values are: `precise`, `conservative`.", opt);
3965                                 }
3966                                 continue;
3967                         }
3968                         if (g_str_has_prefix (opt, "bridge-implementation=")) {
3969                                 opt = strchr (opt, '=') + 1;
3970                                 sgen_set_bridge_implementation (opt);
3971                                 continue;
3972                         }
3973                         if (g_str_has_prefix (opt, "toggleref-test")) {
3974                                 sgen_register_test_toggleref_callback ();
3975                                 continue;
3976                         }
3977
3978 #ifdef USER_CONFIG
3979                         if (g_str_has_prefix (opt, "nursery-size=")) {
3980                                 size_t val;
3981                                 opt = strchr (opt, '=') + 1;
3982                                 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &val)) {
3983                                         if ((val & (val - 1))) {
3984                                                 sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using default value.", "`nursery-size` must be a power of two.");
3985                                                 continue;
3986                                         }
3987
3988                                         if (val < SGEN_MAX_NURSERY_WASTE) {
3989                                                 sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using default value.",
3990                                                                 "`nursery-size` must be at least %d bytes.", SGEN_MAX_NURSERY_WASTE);
3991                                                 continue;
3992                                         }
3993
3994                                         sgen_nursery_size = val;
3995                                         sgen_nursery_bits = 0;
3996                                         while (ONE_P << (++ sgen_nursery_bits) != sgen_nursery_size)
3997                                                 ;
3998                                 } else {
3999                                         sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using default value.", "`nursery-size` must be an integer.");
4000                                         continue;
4001                                 }
4002                                 continue;
4003                         }
4004 #endif
4005                         if (g_str_has_prefix (opt, "save-target-ratio=")) {
4006                                 double val;
4007                                 opt = strchr (opt, '=') + 1;
4008                                 if (parse_double_in_interval (MONO_GC_PARAMS_NAME, "save-target-ratio", opt,
4009                                                 SGEN_MIN_SAVE_TARGET_RATIO, SGEN_MAX_SAVE_TARGET_RATIO, &val)) {
4010                                         save_target = val;
4011                                 }
4012                                 continue;
4013                         }
4014                         if (g_str_has_prefix (opt, "default-allowance-ratio=")) {
4015                                 double val;
4016                                 opt = strchr (opt, '=') + 1;
4017                                 if (parse_double_in_interval (MONO_GC_PARAMS_NAME, "default-allowance-ratio", opt,
4018                                                 SGEN_MIN_ALLOWANCE_NURSERY_SIZE_RATIO, SGEN_MIN_ALLOWANCE_NURSERY_SIZE_RATIO, &val)) {
4019                                         allowance_ratio = val;
4020                                 }
4021                                 continue;
4022                         }
4023                         if (g_str_has_prefix (opt, "allow-synchronous-major=")) {
4024                                 if (!major_collector.is_concurrent) {
4025                                         sgen_env_var_error (MONO_GC_PARAMS_NAME, "Ignoring.", "`allow-synchronous-major` is only valid for the concurrent major collector.");
4026                                         continue;
4027                                 }
4028
4029                                 opt = strchr (opt, '=') + 1;
4030
4031                                 if (!strcmp (opt, "yes")) {
4032                                         allow_synchronous_major = TRUE;
4033                                 } else if (!strcmp (opt, "no")) {
4034                                         allow_synchronous_major = FALSE;
4035                                 } else {
4036                                         sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using default value.", "`allow-synchronous-major` must be either `yes' or `no'.");
4037                                         continue;
4038                                 }
4039                         }
4040
4041                         if (!strcmp (opt, "cementing")) {
4042                                 cement_enabled = TRUE;
4043                                 continue;
4044                         }
4045                         if (!strcmp (opt, "no-cementing")) {
4046                                 cement_enabled = FALSE;
4047                                 continue;
4048                         }
4049
4050                         if (major_collector.handle_gc_param && major_collector.handle_gc_param (opt))
4051                                 continue;
4052
4053                         if (sgen_minor_collector.handle_gc_param && sgen_minor_collector.handle_gc_param (opt))
4054                                 continue;
4055
4056                         sgen_env_var_error (MONO_GC_PARAMS_NAME, "Ignoring.", "Unknown option `%s`.", opt);
4057
4058                         if (usage_printed)
4059                                 continue;
4060
4061                         fprintf (stderr, "\n%s must be a comma-delimited list of one or more of the following:\n", MONO_GC_PARAMS_NAME);
4062                         fprintf (stderr, "  max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
4063                         fprintf (stderr, "  soft-heap-limit=n (where N is an integer, possibly with a k, m or a g suffix)\n");
4064                         fprintf (stderr, "  nursery-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
4065                         fprintf (stderr, "  major=COLLECTOR (where COLLECTOR is `marksweep', `marksweep-conc', `marksweep-par')\n");
4066                         fprintf (stderr, "  minor=COLLECTOR (where COLLECTOR is `simple' or `split')\n");
4067                         fprintf (stderr, "  wbarrier=WBARRIER (where WBARRIER is `remset' or `cardtable')\n");
4068                         fprintf (stderr, "  stack-mark=MARK-METHOD (where MARK-METHOD is 'precise' or 'conservative')\n");
4069                         fprintf (stderr, "  [no-]cementing\n");
4070                         if (major_collector.is_concurrent)
4071                                 fprintf (stderr, "  allow-synchronous-major=FLAG (where FLAG is `yes' or `no')\n");
4072                         if (major_collector.print_gc_param_usage)
4073                                 major_collector.print_gc_param_usage ();
4074                         if (sgen_minor_collector.print_gc_param_usage)
4075                                 sgen_minor_collector.print_gc_param_usage ();
4076                         fprintf (stderr, " Experimental options:\n");
4077                         fprintf (stderr, "  save-target-ratio=R (where R must be between %.2f - %.2f).\n", SGEN_MIN_SAVE_TARGET_RATIO, SGEN_MAX_SAVE_TARGET_RATIO);
4078                         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);
4079                         fprintf (stderr, "\n");
4080
4081                         usage_printed = TRUE;
4082                 }
4083                 g_strfreev (opts);
4084         }
4085
4086         if (major_collector_opt)
4087                 g_free (major_collector_opt);
4088
4089         if (minor_collector_opt)
4090                 g_free (minor_collector_opt);
4091
4092         alloc_nursery ();
4093
4094         if (major_collector.is_concurrent && cement_enabled) {
4095                 sgen_env_var_error (MONO_GC_PARAMS_NAME, "Ignoring.", "`cementing` is not supported on concurrent major collectors.");
4096                 cement_enabled = FALSE;
4097         }
4098
4099         sgen_cement_init (cement_enabled);
4100
4101         if ((env = g_getenv (MONO_GC_DEBUG_NAME))) {
4102                 gboolean usage_printed = FALSE;
4103
4104                 opts = g_strsplit (env, ",", -1);
4105                 for (ptr = opts; ptr && *ptr; ptr ++) {
4106                         char *opt = *ptr;
4107                         if (!strcmp (opt, ""))
4108                                 continue;
4109                         if (opt [0] >= '0' && opt [0] <= '9') {
4110                                 gc_debug_level = atoi (opt);
4111                                 opt++;
4112                                 if (opt [0] == ':')
4113                                         opt++;
4114                                 if (opt [0]) {
4115                                         char *rf = g_strdup_printf ("%s.%d", opt, mono_process_current_pid ());
4116                                         gc_debug_file = fopen (rf, "wb");
4117                                         if (!gc_debug_file)
4118                                                 gc_debug_file = stderr;
4119                                         g_free (rf);
4120                                 }
4121                         } else if (!strcmp (opt, "print-allowance")) {
4122                                 debug_print_allowance = TRUE;
4123                         } else if (!strcmp (opt, "print-pinning")) {
4124                                 sgen_pin_stats_enable ();
4125                         } else if (!strcmp (opt, "verify-before-allocs")) {
4126                                 verify_before_allocs = 1;
4127                                 has_per_allocation_action = TRUE;
4128                         } else if (g_str_has_prefix (opt, "verify-before-allocs=")) {
4129                                 char *arg = strchr (opt, '=') + 1;
4130                                 verify_before_allocs = atoi (arg);
4131                                 has_per_allocation_action = TRUE;
4132                         } else if (!strcmp (opt, "collect-before-allocs")) {
4133                                 collect_before_allocs = 1;
4134                                 has_per_allocation_action = TRUE;
4135                         } else if (g_str_has_prefix (opt, "collect-before-allocs=")) {
4136                                 char *arg = strchr (opt, '=') + 1;
4137                                 has_per_allocation_action = TRUE;
4138                                 collect_before_allocs = atoi (arg);
4139                         } else if (!strcmp (opt, "verify-before-collections")) {
4140                                 whole_heap_check_before_collection = TRUE;
4141                         } else if (!strcmp (opt, "check-at-minor-collections")) {
4142                                 consistency_check_at_minor_collection = TRUE;
4143                                 nursery_clear_policy = CLEAR_AT_GC;
4144                         } else if (!strcmp (opt, "mod-union-consistency-check")) {
4145                                 if (!major_collector.is_concurrent) {
4146                                         sgen_env_var_error (MONO_GC_DEBUG_NAME, "Ignoring.", "`mod-union-consistency-check` only works with concurrent major collector.");
4147                                         continue;
4148                                 }
4149                                 mod_union_consistency_check = TRUE;
4150                         } else if (!strcmp (opt, "check-mark-bits")) {
4151                                 check_mark_bits_after_major_collection = TRUE;
4152                         } else if (!strcmp (opt, "check-nursery-pinned")) {
4153                                 check_nursery_objects_pinned = TRUE;
4154                         } else if (!strcmp (opt, "clear-at-gc")) {
4155                                 nursery_clear_policy = CLEAR_AT_GC;
4156                         } else if (!strcmp (opt, "clear-nursery-at-gc")) {
4157                                 nursery_clear_policy = CLEAR_AT_GC;
4158                         } else if (!strcmp (opt, "clear-at-tlab-creation")) {
4159                                 nursery_clear_policy = CLEAR_AT_TLAB_CREATION;
4160                         } else if (!strcmp (opt, "debug-clear-at-tlab-creation")) {
4161                                 nursery_clear_policy = CLEAR_AT_TLAB_CREATION_DEBUG;
4162                         } else if (!strcmp (opt, "check-scan-starts")) {
4163                                 do_scan_starts_check = TRUE;
4164                         } else if (!strcmp (opt, "verify-nursery-at-minor-gc")) {
4165                                 do_verify_nursery = TRUE;
4166                         } else if (!strcmp (opt, "check-concurrent")) {
4167                                 if (!major_collector.is_concurrent) {
4168                                         sgen_env_var_error (MONO_GC_DEBUG_NAME, "Ignoring.", "`check-concurrent` only works with concurrent major collectors.");
4169                                         continue;
4170                                 }
4171                                 do_concurrent_checks = TRUE;
4172                         } else if (!strcmp (opt, "dump-nursery-at-minor-gc")) {
4173                                 do_dump_nursery_content = TRUE;
4174                         } else if (!strcmp (opt, "no-managed-allocator")) {
4175                                 sgen_set_use_managed_allocator (FALSE);
4176                         } else if (!strcmp (opt, "disable-minor")) {
4177                                 disable_minor_collections = TRUE;
4178                         } else if (!strcmp (opt, "disable-major")) {
4179                                 disable_major_collections = TRUE;
4180                         } else if (g_str_has_prefix (opt, "heap-dump=")) {
4181                                 char *filename = strchr (opt, '=') + 1;
4182                                 nursery_clear_policy = CLEAR_AT_GC;
4183                                 sgen_debug_enable_heap_dump (filename);
4184                         } else if (g_str_has_prefix (opt, "binary-protocol=")) {
4185                                 char *filename = strchr (opt, '=') + 1;
4186                                 char *colon = strrchr (filename, ':');
4187                                 size_t limit = -1;
4188                                 if (colon) {
4189                                         if (!mono_gc_parse_environment_string_extract_number (colon + 1, &limit)) {
4190                                                 sgen_env_var_error (MONO_GC_DEBUG_NAME, "Ignoring limit.", "Binary protocol file size limit must be an integer.");
4191                                                 limit = -1;
4192                                         }
4193                                         *colon = '\0';
4194                                 }
4195                                 binary_protocol_init (filename, (long long)limit);
4196                         } else if (!strcmp (opt, "nursery-canaries")) {
4197                                 do_verify_nursery = TRUE;
4198                                 sgen_set_use_managed_allocator (FALSE);
4199                                 enable_nursery_canaries = TRUE;
4200                         } else if (!strcmp (opt, "do-not-finalize")) {
4201                                 do_not_finalize = TRUE;
4202                         } else if (!strcmp (opt, "log-finalizers")) {
4203                                 log_finalizers = TRUE;
4204                         } else if (!sgen_client_handle_gc_debug (opt) && !sgen_bridge_handle_gc_debug (opt)) {
4205                                 sgen_env_var_error (MONO_GC_DEBUG_NAME, "Ignoring.", "Unknown option `%s`.", opt);
4206
4207                                 if (usage_printed)
4208                                         continue;
4209
4210                                 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);
4211                                 fprintf (stderr, "Valid <option>s are:\n");
4212                                 fprintf (stderr, "  collect-before-allocs[=<n>]\n");
4213                                 fprintf (stderr, "  verify-before-allocs[=<n>]\n");
4214                                 fprintf (stderr, "  check-at-minor-collections\n");
4215                                 fprintf (stderr, "  check-mark-bits\n");
4216                                 fprintf (stderr, "  check-nursery-pinned\n");
4217                                 fprintf (stderr, "  verify-before-collections\n");
4218                                 fprintf (stderr, "  verify-nursery-at-minor-gc\n");
4219                                 fprintf (stderr, "  dump-nursery-at-minor-gc\n");
4220                                 fprintf (stderr, "  disable-minor\n");
4221                                 fprintf (stderr, "  disable-major\n");
4222                                 fprintf (stderr, "  check-concurrent\n");
4223                                 fprintf (stderr, "  clear-[nursery-]at-gc\n");
4224                                 fprintf (stderr, "  clear-at-tlab-creation\n");
4225                                 fprintf (stderr, "  debug-clear-at-tlab-creation\n");
4226                                 fprintf (stderr, "  check-scan-starts\n");
4227                                 fprintf (stderr, "  no-managed-allocator\n");
4228                                 fprintf (stderr, "  print-allowance\n");
4229                                 fprintf (stderr, "  print-pinning\n");
4230                                 fprintf (stderr, "  heap-dump=<filename>\n");
4231                                 fprintf (stderr, "  binary-protocol=<filename>[:<file-size-limit>]\n");
4232                                 fprintf (stderr, "  nursery-canaries\n");
4233                                 fprintf (stderr, "  do-not-finalize\n");
4234                                 fprintf (stderr, "  log-finalizers\n");
4235                                 sgen_client_print_gc_debug_usage ();
4236                                 sgen_bridge_print_gc_debug_usage ();
4237                                 fprintf (stderr, "\n");
4238
4239                                 usage_printed = TRUE;
4240                         }
4241                 }
4242                 g_strfreev (opts);
4243         }
4244
4245         if (check_mark_bits_after_major_collection)
4246                 nursery_clear_policy = CLEAR_AT_GC;
4247
4248         if (major_collector.post_param_init)
4249                 major_collector.post_param_init (&major_collector);
4250
4251         if (major_collector.needs_thread_pool)
4252                 sgen_workers_init (1);
4253
4254         sgen_memgov_init (max_heap, soft_limit, debug_print_allowance, allowance_ratio, save_target);
4255
4256         memset (&remset, 0, sizeof (remset));
4257
4258         sgen_card_table_init (&remset);
4259
4260         gc_initialized = 1;
4261 }
4262
4263 const char *
4264 mono_gc_get_gc_name (void)
4265 {
4266         return "sgen";
4267 }
4268
4269 static MonoMethod *write_barrier_conc_method;
4270 static MonoMethod *write_barrier_noconc_method;
4271
4272 gboolean
4273 sgen_is_critical_method (MonoMethod *method)
4274 {
4275         return (method == write_barrier_conc_method || method == write_barrier_noconc_method || sgen_is_managed_allocator (method));
4276 }
4277
4278 gboolean
4279 sgen_has_critical_method (void)
4280 {
4281         return write_barrier_conc_method || write_barrier_noconc_method || sgen_has_managed_allocator ();
4282 }
4283
4284 #ifndef DISABLE_JIT
4285
4286 static void
4287 emit_nursery_check (MonoMethodBuilder *mb, int *nursery_check_return_labels, gboolean is_concurrent)
4288 {
4289         int shifted_nursery_start = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
4290
4291         memset (nursery_check_return_labels, 0, sizeof (int) * 2);
4292         // if (ptr_in_nursery (ptr)) return;
4293         /*
4294          * Masking out the bits might be faster, but we would have to use 64 bit
4295          * immediates, which might be slower.
4296          */
4297         mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
4298         mono_mb_emit_byte (mb, CEE_MONO_LDPTR_NURSERY_START);
4299         mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
4300         mono_mb_emit_byte (mb, CEE_SHR_UN);
4301         mono_mb_emit_stloc (mb, shifted_nursery_start);
4302
4303         mono_mb_emit_ldarg (mb, 0);
4304         mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
4305         mono_mb_emit_byte (mb, CEE_SHR_UN);
4306         mono_mb_emit_ldloc (mb, shifted_nursery_start);
4307         nursery_check_return_labels [0] = mono_mb_emit_branch (mb, CEE_BEQ);
4308
4309         if (!is_concurrent) {
4310                 // if (!ptr_in_nursery (*ptr)) return;
4311                 mono_mb_emit_ldarg (mb, 0);
4312                 mono_mb_emit_byte (mb, CEE_LDIND_I);
4313                 mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
4314                 mono_mb_emit_byte (mb, CEE_SHR_UN);
4315                 mono_mb_emit_ldloc (mb, shifted_nursery_start);
4316                 nursery_check_return_labels [1] = mono_mb_emit_branch (mb, CEE_BNE_UN);
4317         }
4318 }
4319 #endif
4320
4321 MonoMethod*
4322 mono_gc_get_specific_write_barrier (gboolean is_concurrent)
4323 {
4324         MonoMethod *res;
4325         MonoMethodBuilder *mb;
4326         MonoMethodSignature *sig;
4327         MonoMethod **write_barrier_method_addr;
4328 #ifdef MANAGED_WBARRIER
4329         int i, nursery_check_labels [2];
4330
4331 #ifdef HAVE_KW_THREAD
4332         int stack_end_offset = -1;
4333
4334         MONO_THREAD_VAR_OFFSET (stack_end, stack_end_offset);
4335         g_assert (stack_end_offset != -1);
4336 #endif
4337 #endif
4338
4339         // FIXME: Maybe create a separate version for ctors (the branch would be
4340         // correctly predicted more times)
4341         if (is_concurrent)
4342                 write_barrier_method_addr = &write_barrier_conc_method;
4343         else
4344                 write_barrier_method_addr = &write_barrier_noconc_method;
4345
4346         if (*write_barrier_method_addr)
4347                 return *write_barrier_method_addr;
4348
4349         /* Create the IL version of mono_gc_barrier_generic_store () */
4350         sig = mono_metadata_signature_alloc (mono_defaults.corlib, 1);
4351         sig->ret = &mono_defaults.void_class->byval_arg;
4352         sig->params [0] = &mono_defaults.int_class->byval_arg;
4353
4354         if (is_concurrent)
4355                 mb = mono_mb_new (mono_defaults.object_class, "wbarrier_conc", MONO_WRAPPER_WRITE_BARRIER);
4356         else
4357                 mb = mono_mb_new (mono_defaults.object_class, "wbarrier_noconc", MONO_WRAPPER_WRITE_BARRIER);
4358
4359 #ifndef DISABLE_JIT
4360 #ifdef MANAGED_WBARRIER
4361         emit_nursery_check (mb, nursery_check_labels, is_concurrent);
4362         /*
4363         addr = sgen_cardtable + ((address >> CARD_BITS) & CARD_MASK)
4364         *addr = 1;
4365
4366         sgen_cardtable:
4367                 LDC_PTR sgen_cardtable
4368
4369         address >> CARD_BITS
4370                 LDARG_0
4371                 LDC_I4 CARD_BITS
4372                 SHR_UN
4373         if (SGEN_HAVE_OVERLAPPING_CARDS) {
4374                 LDC_PTR card_table_mask
4375                 AND
4376         }
4377         AND
4378         ldc_i4_1
4379         stind_i1
4380         */
4381         mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
4382         mono_mb_emit_byte (mb, CEE_MONO_LDPTR_CARD_TABLE);
4383         mono_mb_emit_ldarg (mb, 0);
4384         mono_mb_emit_icon (mb, CARD_BITS);
4385         mono_mb_emit_byte (mb, CEE_SHR_UN);
4386         mono_mb_emit_byte (mb, CEE_CONV_I);
4387 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
4388 #if SIZEOF_VOID_P == 8
4389         mono_mb_emit_icon8 (mb, CARD_MASK);
4390 #else
4391         mono_mb_emit_icon (mb, CARD_MASK);
4392 #endif
4393         mono_mb_emit_byte (mb, CEE_CONV_I);
4394         mono_mb_emit_byte (mb, CEE_AND);
4395 #endif
4396         mono_mb_emit_byte (mb, CEE_ADD);
4397         mono_mb_emit_icon (mb, 1);
4398         mono_mb_emit_byte (mb, CEE_STIND_I1);
4399
4400         // return;
4401         for (i = 0; i < 2; ++i) {
4402                 if (nursery_check_labels [i])
4403                         mono_mb_patch_branch (mb, nursery_check_labels [i]);
4404         }
4405         mono_mb_emit_byte (mb, CEE_RET);
4406 #else
4407         mono_mb_emit_ldarg (mb, 0);
4408         mono_mb_emit_icall (mb, mono_gc_wbarrier_generic_nostore);
4409         mono_mb_emit_byte (mb, CEE_RET);
4410 #endif
4411 #endif
4412         res = mono_mb_create_method (mb, sig, 16);
4413         mono_mb_free (mb);
4414
4415         LOCK_GC;
4416         if (*write_barrier_method_addr) {
4417                 /* Already created */
4418                 mono_free_method (res);
4419         } else {
4420                 /* double-checked locking */
4421                 mono_memory_barrier ();
4422                 *write_barrier_method_addr = res;
4423         }
4424         UNLOCK_GC;
4425
4426         return *write_barrier_method_addr;
4427 }
4428
4429 MonoMethod*
4430 mono_gc_get_write_barrier (void)
4431 {
4432         return mono_gc_get_specific_write_barrier (major_collector.is_concurrent);
4433 }
4434
4435 char*
4436 mono_gc_get_description (void)
4437 {
4438         return g_strdup ("sgen");
4439 }
4440
4441 void
4442 mono_gc_set_desktop_mode (void)
4443 {
4444 }
4445
4446 gboolean
4447 mono_gc_is_moving (void)
4448 {
4449         return TRUE;
4450 }
4451
4452 gboolean
4453 mono_gc_is_disabled (void)
4454 {
4455         return FALSE;
4456 }
4457
4458 #ifdef HOST_WIN32
4459 BOOL APIENTRY mono_gc_dllmain (HMODULE module_handle, DWORD reason, LPVOID reserved)
4460 {
4461         return TRUE;
4462 }
4463 #endif
4464
4465 NurseryClearPolicy
4466 sgen_get_nursery_clear_policy (void)
4467 {
4468         return nursery_clear_policy;
4469 }
4470
4471 void
4472 sgen_gc_lock (void)
4473 {
4474         LOCK_GC;
4475 }
4476
4477 void
4478 sgen_gc_unlock (void)
4479 {
4480         gboolean try_free = sgen_try_free_some_memory;
4481         sgen_try_free_some_memory = FALSE;
4482         mono_mutex_unlock (&gc_mutex);
4483         MONO_GC_UNLOCKED ();
4484         if (try_free)
4485                 mono_thread_hazardous_try_free_some ();
4486 }
4487
4488 void
4489 sgen_major_collector_iterate_live_block_ranges (sgen_cardtable_block_callback callback)
4490 {
4491         major_collector.iterate_live_block_ranges (callback);
4492 }
4493
4494 SgenMajorCollector*
4495 sgen_get_major_collector (void)
4496 {
4497         return &major_collector;
4498 }
4499
4500 void mono_gc_set_skip_thread (gboolean skip)
4501 {
4502         SgenThreadInfo *info = mono_thread_info_current ();
4503
4504         LOCK_GC;
4505         info->gc_disabled = skip;
4506         UNLOCK_GC;
4507 }
4508
4509 SgenRememberedSet*
4510 sgen_get_remset (void)
4511 {
4512         return &remset;
4513 }
4514
4515 void
4516 mono_gc_register_altstack (gpointer stack, gint32 stack_size, gpointer altstack, gint32 altstack_size)
4517 {
4518         // FIXME:
4519 }
4520
4521
4522 void
4523 sgen_check_whole_heap_stw (void)
4524 {
4525         sgen_stop_world (0);
4526         sgen_clear_nursery_fragments ();
4527         sgen_check_whole_heap (FALSE);
4528         sgen_restart_world (0, NULL);
4529 }
4530
4531 void
4532 sgen_gc_event_moves (void)
4533 {
4534         if (moved_objects_idx) {
4535                 mono_profiler_gc_moves (moved_objects, moved_objects_idx);
4536                 moved_objects_idx = 0;
4537         }
4538 }
4539
4540 gint64
4541 sgen_timestamp (void)
4542 {
4543         SGEN_TV_DECLARE (timestamp);
4544         SGEN_TV_GETTIME (timestamp);
4545         return SGEN_TV_ELAPSED (sgen_init_timestamp, timestamp);
4546 }
4547
4548 #endif /* HAVE_SGEN_GC */