fc442d4f5ad4da284c2ad881c297657f7dd03a86
[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/monitor.h"
209 #include "metadata/mempool-internals.h"
210 #include "metadata/marshal.h"
211 #include "metadata/runtime.h"
212 #include "metadata/sgen-cardtable.h"
213 #include "metadata/sgen-pinning.h"
214 #include "metadata/sgen-workers.h"
215 #include "metadata/sgen-layout-stats.h"
216 #include "utils/mono-mmap.h"
217 #include "utils/mono-time.h"
218 #include "utils/mono-semaphore.h"
219 #include "utils/mono-counters.h"
220 #include "utils/mono-proclib.h"
221 #include "utils/mono-memory-model.h"
222 #include "utils/mono-logger-internal.h"
223 #include "utils/dtrace.h"
224
225 #include <mono/utils/mono-logger-internal.h>
226 #include <mono/utils/memcheck.h>
227
228 #if defined(__MACH__)
229 #include "utils/mach-support.h"
230 #endif
231
232 #define OPDEF(a,b,c,d,e,f,g,h,i,j) \
233         a = i,
234
235 enum {
236 #include "mono/cil/opcode.def"
237         CEE_LAST
238 };
239
240 #undef OPDEF
241
242 #undef pthread_create
243 #undef pthread_join
244 #undef pthread_detach
245
246 /*
247  * ######################################################################
248  * ########  Types and constants used by the GC.
249  * ######################################################################
250  */
251
252 /* 0 means not initialized, 1 is initialized, -1 means in progress */
253 static int gc_initialized = 0;
254 /* If set, check if we need to do something every X allocations */
255 gboolean has_per_allocation_action;
256 /* If set, do a heap check every X allocation */
257 guint32 verify_before_allocs = 0;
258 /* If set, do a minor collection before every X allocation */
259 guint32 collect_before_allocs = 0;
260 /* If set, do a whole heap check before each collection */
261 static gboolean whole_heap_check_before_collection = FALSE;
262 /* If set, do a heap consistency check before each minor collection */
263 static gboolean consistency_check_at_minor_collection = FALSE;
264 /* If set, do a mod union consistency check before each finishing collection pause */
265 static gboolean mod_union_consistency_check = FALSE;
266 /* If set, check whether mark bits are consistent after major collections */
267 static gboolean check_mark_bits_after_major_collection = FALSE;
268 /* If set, check that all nursery objects are pinned/not pinned, depending on context */
269 static gboolean check_nursery_objects_pinned = FALSE;
270 /* If set, do a few checks when the concurrent collector is used */
271 static gboolean do_concurrent_checks = FALSE;
272 /* If set, check that there are no references to the domain left at domain unload */
273 static gboolean xdomain_checks = FALSE;
274 /* If not null, dump the heap after each collection into this file */
275 static FILE *heap_dump_file = NULL;
276 /* If set, mark stacks conservatively, even if precise marking is possible */
277 static gboolean conservative_stack_mark = FALSE;
278 /* If set, do a plausibility check on the scan_starts before and after
279    each collection */
280 static gboolean do_scan_starts_check = FALSE;
281
282 /*
283  * If the major collector is concurrent and this is FALSE, we will
284  * never initiate a synchronous major collection, unless requested via
285  * GC.Collect().
286  */
287 static gboolean allow_synchronous_major = TRUE;
288 static gboolean disable_minor_collections = FALSE;
289 static gboolean disable_major_collections = FALSE;
290 gboolean do_pin_stats = FALSE;
291 static gboolean do_verify_nursery = FALSE;
292 static gboolean do_dump_nursery_content = FALSE;
293 static gboolean enable_nursery_canaries = FALSE;
294
295 #ifdef HEAVY_STATISTICS
296 guint64 stat_objects_alloced_degraded = 0;
297 guint64 stat_bytes_alloced_degraded = 0;
298
299 guint64 stat_copy_object_called_nursery = 0;
300 guint64 stat_objects_copied_nursery = 0;
301 guint64 stat_copy_object_called_major = 0;
302 guint64 stat_objects_copied_major = 0;
303
304 guint64 stat_scan_object_called_nursery = 0;
305 guint64 stat_scan_object_called_major = 0;
306
307 guint64 stat_slots_allocated_in_vain;
308
309 guint64 stat_nursery_copy_object_failed_from_space = 0;
310 guint64 stat_nursery_copy_object_failed_forwarded = 0;
311 guint64 stat_nursery_copy_object_failed_pinned = 0;
312 guint64 stat_nursery_copy_object_failed_to_space = 0;
313
314 static int stat_wbarrier_add_to_global_remset = 0;
315 static int stat_wbarrier_set_field = 0;
316 static int stat_wbarrier_set_arrayref = 0;
317 static int stat_wbarrier_arrayref_copy = 0;
318 static int stat_wbarrier_generic_store = 0;
319 static int stat_wbarrier_generic_store_atomic = 0;
320 static int stat_wbarrier_set_root = 0;
321 static int stat_wbarrier_value_copy = 0;
322 static int stat_wbarrier_object_copy = 0;
323 #endif
324
325 static guint64 stat_pinned_objects = 0;
326
327 static guint64 time_minor_pre_collection_fragment_clear = 0;
328 static guint64 time_minor_pinning = 0;
329 static guint64 time_minor_scan_remsets = 0;
330 static guint64 time_minor_scan_pinned = 0;
331 static guint64 time_minor_scan_registered_roots = 0;
332 static guint64 time_minor_scan_thread_data = 0;
333 static guint64 time_minor_finish_gray_stack = 0;
334 static guint64 time_minor_fragment_creation = 0;
335
336 static guint64 time_major_pre_collection_fragment_clear = 0;
337 static guint64 time_major_pinning = 0;
338 static guint64 time_major_scan_pinned = 0;
339 static guint64 time_major_scan_registered_roots = 0;
340 static guint64 time_major_scan_thread_data = 0;
341 static guint64 time_major_scan_alloc_pinned = 0;
342 static guint64 time_major_scan_finalized = 0;
343 static guint64 time_major_scan_big_objects = 0;
344 static guint64 time_major_finish_gray_stack = 0;
345 static guint64 time_major_free_bigobjs = 0;
346 static guint64 time_major_los_sweep = 0;
347 static guint64 time_major_sweep = 0;
348 static guint64 time_major_fragment_creation = 0;
349
350 static guint64 time_max = 0;
351
352 static SGEN_TV_DECLARE (time_major_conc_collection_start);
353 static SGEN_TV_DECLARE (time_major_conc_collection_end);
354
355 static SGEN_TV_DECLARE (last_minor_collection_start_tv);
356 static SGEN_TV_DECLARE (last_minor_collection_end_tv);
357
358 int gc_debug_level = 0;
359 FILE* gc_debug_file;
360
361 static MonoGCFinalizerCallbacks fin_callbacks;
362
363 /*
364 void
365 mono_gc_flush_info (void)
366 {
367         fflush (gc_debug_file);
368 }
369 */
370
371 #define TV_DECLARE SGEN_TV_DECLARE
372 #define TV_GETTIME SGEN_TV_GETTIME
373 #define TV_ELAPSED SGEN_TV_ELAPSED
374
375 SGEN_TV_DECLARE (sgen_init_timestamp);
376
377 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
378
379 NurseryClearPolicy nursery_clear_policy = CLEAR_AT_TLAB_CREATION;
380
381 #define object_is_forwarded     SGEN_OBJECT_IS_FORWARDED
382 #define object_is_pinned        SGEN_OBJECT_IS_PINNED
383 #define pin_object              SGEN_PIN_OBJECT
384
385 #define ptr_in_nursery sgen_ptr_in_nursery
386
387 #define LOAD_VTABLE     SGEN_LOAD_VTABLE
388
389 static const char*
390 safe_name (void* obj)
391 {
392         MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (obj);
393         return vt->klass->name;
394 }
395
396 gboolean
397 nursery_canaries_enabled (void)
398 {
399         return enable_nursery_canaries;
400 }
401
402 #define safe_object_get_size    sgen_safe_object_get_size
403
404 const char*
405 sgen_safe_name (void* obj)
406 {
407         return safe_name (obj);
408 }
409
410 /*
411  * ######################################################################
412  * ########  Global data.
413  * ######################################################################
414  */
415 LOCK_DECLARE (gc_mutex);
416 gboolean sgen_try_free_some_memory;
417
418 #define SCAN_START_SIZE SGEN_SCAN_START_SIZE
419
420 static mword pagesize = 4096;
421 size_t degraded_mode = 0;
422
423 static mword bytes_pinned_from_failed_allocation = 0;
424
425 GCMemSection *nursery_section = NULL;
426 static volatile mword lowest_heap_address = ~(mword)0;
427 static volatile mword highest_heap_address = 0;
428
429 LOCK_DECLARE (sgen_interruption_mutex);
430
431 typedef struct _FinalizeReadyEntry FinalizeReadyEntry;
432 struct _FinalizeReadyEntry {
433         FinalizeReadyEntry *next;
434         void *object;
435 };
436
437 typedef struct _EphemeronLinkNode EphemeronLinkNode;
438
439 struct _EphemeronLinkNode {
440         EphemeronLinkNode *next;
441         char *array;
442 };
443
444 typedef struct {
445        void *key;
446        void *value;
447 } Ephemeron;
448
449 int current_collection_generation = -1;
450 volatile gboolean concurrent_collection_in_progress = FALSE;
451
452 /* objects that are ready to be finalized */
453 static FinalizeReadyEntry *fin_ready_list = NULL;
454 static FinalizeReadyEntry *critical_fin_list = NULL;
455
456 static EphemeronLinkNode *ephemeron_list;
457
458 /* registered roots: the key to the hash is the root start address */
459 /* 
460  * Different kinds of roots are kept separate to speed up pin_from_roots () for example.
461  */
462 SgenHashTable roots_hash [ROOT_TYPE_NUM] = {
463         SGEN_HASH_TABLE_INIT (INTERNAL_MEM_ROOTS_TABLE, INTERNAL_MEM_ROOT_RECORD, sizeof (RootRecord), mono_aligned_addr_hash, NULL),
464         SGEN_HASH_TABLE_INIT (INTERNAL_MEM_ROOTS_TABLE, INTERNAL_MEM_ROOT_RECORD, sizeof (RootRecord), mono_aligned_addr_hash, NULL),
465         SGEN_HASH_TABLE_INIT (INTERNAL_MEM_ROOTS_TABLE, INTERNAL_MEM_ROOT_RECORD, sizeof (RootRecord), mono_aligned_addr_hash, NULL)
466 };
467 static mword roots_size = 0; /* amount of memory in the root set */
468
469 #define GC_ROOT_NUM 32
470 typedef struct {
471         int count;              /* must be the first field */
472         void *objects [GC_ROOT_NUM];
473         int root_types [GC_ROOT_NUM];
474         uintptr_t extra_info [GC_ROOT_NUM];
475 } GCRootReport;
476
477 static void
478 notify_gc_roots (GCRootReport *report)
479 {
480         if (!report->count)
481                 return;
482         mono_profiler_gc_roots (report->count, report->objects, report->root_types, report->extra_info);
483         report->count = 0;
484 }
485
486 static void
487 add_profile_gc_root (GCRootReport *report, void *object, int rtype, uintptr_t extra_info)
488 {
489         if (report->count == GC_ROOT_NUM)
490                 notify_gc_roots (report);
491         report->objects [report->count] = object;
492         report->root_types [report->count] = rtype;
493         report->extra_info [report->count++] = (uintptr_t)((MonoVTable*)LOAD_VTABLE (object))->klass;
494 }
495
496 MonoNativeTlsKey thread_info_key;
497
498 #ifdef HAVE_KW_THREAD
499 __thread SgenThreadInfo *sgen_thread_info;
500 __thread char *stack_end;
501 #endif
502
503 /* The size of a TLAB */
504 /* The bigger the value, the less often we have to go to the slow path to allocate a new 
505  * one, but the more space is wasted by threads not allocating much memory.
506  * FIXME: Tune this.
507  * FIXME: Make this self-tuning for each thread.
508  */
509 guint32 tlab_size = (1024 * 4);
510
511 #define MAX_SMALL_OBJ_SIZE      SGEN_MAX_SMALL_OBJ_SIZE
512
513 /* Functions supplied by the runtime to be called by the GC */
514 static MonoGCCallbacks gc_callbacks;
515
516 #define ALLOC_ALIGN             SGEN_ALLOC_ALIGN
517
518 #define ALIGN_UP                SGEN_ALIGN_UP
519
520 #define MOVED_OBJECTS_NUM 64
521 static void *moved_objects [MOVED_OBJECTS_NUM];
522 static int moved_objects_idx = 0;
523
524 /* Vtable of the objects used to fill out nursery fragments before a collection */
525 static MonoVTable *array_fill_vtable;
526
527 #ifdef SGEN_DEBUG_INTERNAL_ALLOC
528 MonoNativeThreadId main_gc_thread = NULL;
529 #endif
530
531 /*Object was pinned during the current collection*/
532 static mword objects_pinned;
533
534 /*
535  * ######################################################################
536  * ########  Macros and function declarations.
537  * ######################################################################
538  */
539
540 typedef SgenGrayQueue GrayQueue;
541
542 /* forward declarations */
543 static void scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise, GrayQueue *queue);
544 static void scan_from_registered_roots (char *addr_start, char *addr_end, int root_type, ScanCopyContext ctx);
545 static void scan_finalizer_entries (FinalizeReadyEntry *list, ScanCopyContext ctx);
546 static void report_finalizer_roots (void);
547 static void report_registered_roots (void);
548
549 static void pin_from_roots (void *start_nursery, void *end_nursery, GrayQueue *queue);
550 static void finish_gray_stack (int generation, GrayQueue *queue);
551
552 void mono_gc_scan_for_specific_ref (MonoObject *key, gboolean precise);
553
554
555 static void init_stats (void);
556
557 static int mark_ephemerons_in_range (ScanCopyContext ctx);
558 static void clear_unreachable_ephemerons (ScanCopyContext ctx);
559 static void null_ephemerons_for_domain (MonoDomain *domain);
560
561 SgenObjectOperations current_object_ops;
562 SgenMajorCollector major_collector;
563 SgenMinorCollector sgen_minor_collector;
564 static GrayQueue gray_queue;
565
566 static SgenRememberedSet remset;
567
568 /* The gray queue to use from the main collection thread. */
569 #define WORKERS_DISTRIBUTE_GRAY_QUEUE   (&gray_queue)
570
571 /*
572  * The gray queue a worker job must use.  If we're not parallel or
573  * concurrent, we use the main gray queue.
574  */
575 static SgenGrayQueue*
576 sgen_workers_get_job_gray_queue (WorkerData *worker_data)
577 {
578         return worker_data ? &worker_data->private_gray_queue : WORKERS_DISTRIBUTE_GRAY_QUEUE;
579 }
580
581 static void
582 gray_queue_redirect (SgenGrayQueue *queue)
583 {
584         gboolean wake = FALSE;
585
586         for (;;) {
587                 GrayQueueSection *section = sgen_gray_object_dequeue_section (queue);
588                 if (!section)
589                         break;
590                 sgen_section_gray_queue_enqueue (queue->alloc_prepare_data, section);
591                 wake = TRUE;
592         }
593
594         if (wake) {
595                 g_assert (concurrent_collection_in_progress);
596                 if (sgen_workers_have_started ()) {
597                         sgen_workers_ensure_awake ();
598                 } else {
599                         if (concurrent_collection_in_progress)
600                                 g_assert (current_collection_generation == -1);
601                 }
602         }
603 }
604
605 static void
606 gray_queue_enable_redirect (SgenGrayQueue *queue)
607 {
608         if (!concurrent_collection_in_progress)
609                 return;
610
611         sgen_gray_queue_set_alloc_prepare (queue, gray_queue_redirect, sgen_workers_get_distribute_section_gray_queue ());
612         gray_queue_redirect (queue);
613 }
614
615 void
616 sgen_scan_area_with_callback (char *start, char *end, IterateObjectCallbackFunc callback, void *data, gboolean allow_flags)
617 {
618         while (start < end) {
619                 size_t size;
620                 char *obj;
621
622                 if (!*(void**)start) {
623                         start += sizeof (void*); /* should be ALLOC_ALIGN, really */
624                         continue;
625                 }
626
627                 if (allow_flags) {
628                         if (!(obj = SGEN_OBJECT_IS_FORWARDED (start)))
629                                 obj = start;
630                 } else {
631                         obj = start;
632                 }
633
634                 if ((MonoVTable*)SGEN_LOAD_VTABLE (obj) != array_fill_vtable) {
635                         CHECK_CANARY_FOR_OBJECT (obj);
636                         size = ALIGN_UP (safe_object_get_size ((MonoObject*)obj));
637                         callback (obj, size, data);
638                         CANARIFY_SIZE (size);
639                 } else {
640                         size = ALIGN_UP (safe_object_get_size ((MonoObject*)obj));
641                 }
642
643                 start += size;
644         }
645 }
646
647 static gboolean
648 need_remove_object_for_domain (char *start, MonoDomain *domain)
649 {
650         if (mono_object_domain (start) == domain) {
651                 SGEN_LOG (4, "Need to cleanup object %p", start);
652                 binary_protocol_cleanup (start, (gpointer)LOAD_VTABLE (start), safe_object_get_size ((MonoObject*)start));
653                 return TRUE;
654         }
655         return FALSE;
656 }
657
658 static void
659 process_object_for_domain_clearing (char *start, MonoDomain *domain)
660 {
661         GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
662         if (vt->klass == mono_defaults.internal_thread_class)
663                 g_assert (mono_object_domain (start) == mono_get_root_domain ());
664         /* The object could be a proxy for an object in the domain
665            we're deleting. */
666 #ifndef DISABLE_REMOTING
667         if (mono_defaults.real_proxy_class->supertypes && mono_class_has_parent_fast (vt->klass, mono_defaults.real_proxy_class)) {
668                 MonoObject *server = ((MonoRealProxy*)start)->unwrapped_server;
669
670                 /* The server could already have been zeroed out, so
671                    we need to check for that, too. */
672                 if (server && (!LOAD_VTABLE (server) || mono_object_domain (server) == domain)) {
673                         SGEN_LOG (4, "Cleaning up remote pointer in %p to object %p", start, server);
674                         ((MonoRealProxy*)start)->unwrapped_server = NULL;
675                 }
676         }
677 #endif
678 }
679
680 static gboolean
681 clear_domain_process_object (char *obj, MonoDomain *domain)
682 {
683         gboolean remove;
684
685         process_object_for_domain_clearing (obj, domain);
686         remove = need_remove_object_for_domain (obj, domain);
687
688         if (remove && ((MonoObject*)obj)->synchronisation) {
689                 void **dislink = mono_monitor_get_object_monitor_weak_link ((MonoObject*)obj);
690                 if (dislink)
691                         sgen_register_disappearing_link (NULL, dislink, FALSE, TRUE);
692         }
693
694         return remove;
695 }
696
697 static void
698 clear_domain_process_minor_object_callback (char *obj, size_t size, MonoDomain *domain)
699 {
700         if (clear_domain_process_object (obj, domain)) {
701                 CANARIFY_SIZE (size);
702                 memset (obj, 0, size);
703         }
704 }
705
706 static void
707 clear_domain_process_major_object_callback (char *obj, size_t size, MonoDomain *domain)
708 {
709         clear_domain_process_object (obj, domain);
710 }
711
712 static void
713 clear_domain_free_major_non_pinned_object_callback (char *obj, size_t size, MonoDomain *domain)
714 {
715         if (need_remove_object_for_domain (obj, domain))
716                 major_collector.free_non_pinned_object (obj, size);
717 }
718
719 static void
720 clear_domain_free_major_pinned_object_callback (char *obj, size_t size, MonoDomain *domain)
721 {
722         if (need_remove_object_for_domain (obj, domain))
723                 major_collector.free_pinned_object (obj, size);
724 }
725
726 /*
727  * When appdomains are unloaded we can easily remove objects that have finalizers,
728  * but all the others could still be present in random places on the heap.
729  * We need a sweep to get rid of them even though it's going to be costly
730  * with big heaps.
731  * The reason we need to remove them is because we access the vtable and class
732  * structures to know the object size and the reference bitmap: once the domain is
733  * unloaded the point to random memory.
734  */
735 void
736 mono_gc_clear_domain (MonoDomain * domain)
737 {
738         LOSObject *bigobj, *prev;
739         int i;
740
741         LOCK_GC;
742
743         binary_protocol_domain_unload_begin (domain);
744
745         sgen_stop_world (0);
746
747         if (concurrent_collection_in_progress)
748                 sgen_perform_collection (0, GENERATION_OLD, "clear domain", TRUE);
749         g_assert (!concurrent_collection_in_progress);
750
751         sgen_process_fin_stage_entries ();
752         sgen_process_dislink_stage_entries ();
753
754         sgen_clear_nursery_fragments ();
755
756         if (xdomain_checks && domain != mono_get_root_domain ()) {
757                 sgen_scan_for_registered_roots_in_domain (domain, ROOT_TYPE_NORMAL);
758                 sgen_scan_for_registered_roots_in_domain (domain, ROOT_TYPE_WBARRIER);
759                 sgen_check_for_xdomain_refs ();
760         }
761
762         /*Ephemerons and dislinks must be processed before LOS since they might end up pointing
763         to memory returned to the OS.*/
764         null_ephemerons_for_domain (domain);
765
766         for (i = GENERATION_NURSERY; i < GENERATION_MAX; ++i)
767                 sgen_null_links_for_domain (domain, i);
768
769         for (i = GENERATION_NURSERY; i < GENERATION_MAX; ++i)
770                 sgen_remove_finalizers_for_domain (domain, i);
771
772         sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
773                         (IterateObjectCallbackFunc)clear_domain_process_minor_object_callback, domain, FALSE);
774
775         /* We need two passes over major and large objects because
776            freeing such objects might give their memory back to the OS
777            (in the case of large objects) or obliterate its vtable
778            (pinned objects with major-copying or pinned and non-pinned
779            objects with major-mark&sweep), but we might need to
780            dereference a pointer from an object to another object if
781            the first object is a proxy. */
782         major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, (IterateObjectCallbackFunc)clear_domain_process_major_object_callback, domain);
783         for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
784                 clear_domain_process_object (bigobj->data, domain);
785
786         prev = NULL;
787         for (bigobj = los_object_list; bigobj;) {
788                 if (need_remove_object_for_domain (bigobj->data, domain)) {
789                         LOSObject *to_free = bigobj;
790                         if (prev)
791                                 prev->next = bigobj->next;
792                         else
793                                 los_object_list = bigobj->next;
794                         bigobj = bigobj->next;
795                         SGEN_LOG (4, "Freeing large object %p", bigobj->data);
796                         sgen_los_free_object (to_free);
797                         continue;
798                 }
799                 prev = bigobj;
800                 bigobj = bigobj->next;
801         }
802         major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_NON_PINNED, (IterateObjectCallbackFunc)clear_domain_free_major_non_pinned_object_callback, domain);
803         major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_PINNED, (IterateObjectCallbackFunc)clear_domain_free_major_pinned_object_callback, domain);
804
805         if (domain == mono_get_root_domain ()) {
806                 if (G_UNLIKELY (do_pin_stats))
807                         sgen_pin_stats_print_class_stats ();
808                 sgen_object_layout_dump (stdout);
809         }
810
811         sgen_restart_world (0, NULL);
812
813         binary_protocol_domain_unload_end (domain);
814         binary_protocol_flush_buffers (FALSE);
815
816         UNLOCK_GC;
817 }
818
819 /*
820  * sgen_add_to_global_remset:
821  *
822  *   The global remset contains locations which point into newspace after
823  * a minor collection. This can happen if the objects they point to are pinned.
824  *
825  * LOCKING: If called from a parallel collector, the global remset
826  * lock must be held.  For serial collectors that is not necessary.
827  */
828 void
829 sgen_add_to_global_remset (gpointer ptr, gpointer obj)
830 {
831         SGEN_ASSERT (5, sgen_ptr_in_nursery (obj), "Target pointer of global remset must be in the nursery");
832
833         HEAVY_STAT (++stat_wbarrier_add_to_global_remset);
834
835         if (!major_collector.is_concurrent) {
836                 SGEN_ASSERT (5, current_collection_generation != -1, "Global remsets can only be added during collections");
837         } else {
838                 if (current_collection_generation == -1)
839                         SGEN_ASSERT (5, sgen_concurrent_collection_in_progress (), "Global remsets outside of collection pauses can only be added by the concurrent collector");
840         }
841
842         if (!object_is_pinned (obj))
843                 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");
844         else if (sgen_cement_lookup_or_register (obj))
845                 return;
846
847         remset.record_pointer (ptr);
848
849         if (G_UNLIKELY (do_pin_stats))
850                 sgen_pin_stats_register_global_remset (obj);
851
852         SGEN_LOG (8, "Adding global remset for %p", ptr);
853         binary_protocol_global_remset (ptr, obj, (gpointer)SGEN_LOAD_VTABLE (obj));
854
855
856 #ifdef ENABLE_DTRACE
857         if (G_UNLIKELY (MONO_GC_GLOBAL_REMSET_ADD_ENABLED ())) {
858                 MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (obj);
859                 MONO_GC_GLOBAL_REMSET_ADD ((mword)ptr, (mword)obj, sgen_safe_object_get_size (obj),
860                                 vt->klass->name_space, vt->klass->name);
861         }
862 #endif
863 }
864
865 /*
866  * sgen_drain_gray_stack:
867  *
868  *   Scan objects in the gray stack until the stack is empty. This should be called
869  * frequently after each object is copied, to achieve better locality and cache
870  * usage.
871  *
872  * max_objs is the maximum number of objects to scan, or -1 to scan until the stack is
873  * empty.
874  */
875 gboolean
876 sgen_drain_gray_stack (int max_objs, ScanCopyContext ctx)
877 {
878         ScanObjectFunc scan_func = ctx.scan_func;
879         GrayQueue *queue = ctx.queue;
880
881         if (current_collection_generation == GENERATION_OLD && major_collector.drain_gray_stack)
882                 return major_collector.drain_gray_stack (ctx);
883
884         do {
885                 int i;
886                 for (i = 0; i != max_objs; ++i) {
887                         char *obj;
888                         mword desc;
889                         GRAY_OBJECT_DEQUEUE (queue, &obj, &desc);
890                         if (!obj)
891                                 return TRUE;
892                         SGEN_LOG (9, "Precise gray object scan %p (%s)", obj, safe_name (obj));
893                         scan_func (obj, desc, queue);
894                 }
895         } while (max_objs < 0);
896         return FALSE;
897 }
898
899 /*
900  * Addresses in the pin queue are already sorted. This function finds
901  * the object header for each address and pins the object. The
902  * addresses must be inside the nursery section.  The (start of the)
903  * address array is overwritten with the addresses of the actually
904  * pinned objects.  Return the number of pinned objects.
905  */
906 static int
907 pin_objects_from_nursery_pin_queue (ScanCopyContext ctx)
908 {
909         GCMemSection *section = nursery_section;
910         void **start =  sgen_pinning_get_entry (section->pin_queue_first_entry);
911         void **end = sgen_pinning_get_entry (section->pin_queue_last_entry);
912         void *start_nursery = section->data;
913         void *end_nursery = section->next_data;
914         void *last = NULL;
915         int count = 0;
916         void *search_start;
917         void *addr;
918         void *pinning_front = start_nursery;
919         size_t idx;
920         void **definitely_pinned = start;
921         ScanObjectFunc scan_func = ctx.scan_func;
922         SgenGrayQueue *queue = ctx.queue;
923
924         sgen_nursery_allocator_prepare_for_pinning ();
925
926         while (start < end) {
927                 void *obj_to_pin = NULL;
928                 size_t obj_to_pin_size = 0;
929                 mword desc;
930
931                 addr = *start;
932
933                 SGEN_ASSERT (0, addr >= start_nursery && addr < end_nursery, "Potential pinning address out of range");
934                 SGEN_ASSERT (0, addr >= last, "Pin queue not sorted");
935
936                 if (addr == last) {
937                         ++start;
938                         continue;
939                 }
940
941                 SGEN_LOG (5, "Considering pinning addr %p", addr);
942                 /* We've already processed everything up to pinning_front. */
943                 if (addr < pinning_front) {
944                         start++;
945                         continue;
946                 }
947
948                 /*
949                  * Find the closest scan start <= addr.  We might search backward in the
950                  * scan_starts array because entries might be NULL.  In the worst case we
951                  * start at start_nursery.
952                  */
953                 idx = ((char*)addr - (char*)section->data) / SCAN_START_SIZE;
954                 SGEN_ASSERT (0, idx < section->num_scan_start, "Scan start index out of range");
955                 search_start = (void*)section->scan_starts [idx];
956                 if (!search_start || search_start > addr) {
957                         while (idx) {
958                                 --idx;
959                                 search_start = section->scan_starts [idx];
960                                 if (search_start && search_start <= addr)
961                                         break;
962                         }
963                         if (!search_start || search_start > addr)
964                                 search_start = start_nursery;
965                 }
966
967                 /*
968                  * If the pinning front is closer than the scan start we found, start
969                  * searching at the front.
970                  */
971                 if (search_start < pinning_front)
972                         search_start = pinning_front;
973
974                 /*
975                  * Now addr should be in an object a short distance from search_start.
976                  *
977                  * search_start must point to zeroed mem or point to an object.
978                  */
979                 do {
980                         size_t obj_size, canarified_obj_size;
981
982                         /* Skip zeros. */
983                         if (!*(void**)search_start) {
984                                 search_start = (void*)ALIGN_UP ((mword)search_start + sizeof (gpointer));
985                                 /* The loop condition makes sure we don't overrun addr. */
986                                 continue;
987                         }
988
989                         canarified_obj_size = obj_size = ALIGN_UP (safe_object_get_size ((MonoObject*)search_start));
990
991                         /*
992                          * Filler arrays are marked by an invalid sync word.  We don't
993                          * consider them for pinning.  They are not delimited by canaries,
994                          * either.
995                          */
996                         if (((MonoObject*)search_start)->synchronisation != GINT_TO_POINTER (-1)) {
997                                 CHECK_CANARY_FOR_OBJECT (search_start);
998                                 CANARIFY_SIZE (canarified_obj_size);
999
1000                                 if (addr >= search_start && (char*)addr < (char*)search_start + obj_size) {
1001                                         /* This is the object we're looking for. */
1002                                         obj_to_pin = search_start;
1003                                         obj_to_pin_size = canarified_obj_size;
1004                                         break;
1005                                 }
1006                         }
1007
1008                         /* Skip to the next object */
1009                         search_start = (void*)((char*)search_start + canarified_obj_size);
1010                 } while (search_start <= addr);
1011
1012                 /* We've searched past the address we were looking for. */
1013                 if (!obj_to_pin) {
1014                         pinning_front = search_start;
1015                         goto next_pin_queue_entry;
1016                 }
1017
1018                 /*
1019                  * We've found an object to pin.  It might still be a dummy array, but we
1020                  * can advance the pinning front in any case.
1021                  */
1022                 pinning_front = (char*)obj_to_pin + obj_to_pin_size;
1023
1024                 /*
1025                  * If this is a dummy array marking the beginning of a nursery
1026                  * fragment, we don't pin it.
1027                  */
1028                 if (((MonoObject*)obj_to_pin)->synchronisation == GINT_TO_POINTER (-1))
1029                         goto next_pin_queue_entry;
1030
1031                 /*
1032                  * Finally - pin the object!
1033                  */
1034                 desc = sgen_obj_get_descriptor_safe (obj_to_pin);
1035                 if (scan_func) {
1036                         scan_func (obj_to_pin, desc, queue);
1037                 } else {
1038                         SGEN_LOG (4, "Pinned object %p, vtable %p (%s), count %d\n",
1039                                         obj_to_pin, *(void**)obj_to_pin, safe_name (obj_to_pin), count);
1040                         binary_protocol_pin (obj_to_pin,
1041                                         (gpointer)LOAD_VTABLE (obj_to_pin),
1042                                         safe_object_get_size (obj_to_pin));
1043
1044 #ifdef ENABLE_DTRACE
1045                         if (G_UNLIKELY (MONO_GC_OBJ_PINNED_ENABLED ())) {
1046                                 int gen = sgen_ptr_in_nursery (obj_to_pin) ? GENERATION_NURSERY : GENERATION_OLD;
1047                                 MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (obj_to_pin);
1048                                 MONO_GC_OBJ_PINNED ((mword)obj_to_pin,
1049                                                 sgen_safe_object_get_size (obj_to_pin),
1050                                                 vt->klass->name_space, vt->klass->name, gen);
1051                         }
1052 #endif
1053
1054                         pin_object (obj_to_pin);
1055                         GRAY_OBJECT_ENQUEUE (queue, obj_to_pin, desc);
1056                         if (G_UNLIKELY (do_pin_stats))
1057                                 sgen_pin_stats_register_object (obj_to_pin, obj_to_pin_size);
1058                         definitely_pinned [count] = obj_to_pin;
1059                         count++;
1060                 }
1061
1062         next_pin_queue_entry:
1063                 last = addr;
1064                 ++start;
1065         }
1066         //printf ("effective pinned: %d (at the end: %d)\n", count, (char*)end_nursery - (char*)last);
1067         if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS) {
1068                 GCRootReport report;
1069                 report.count = 0;
1070                 for (idx = 0; idx < count; ++idx)
1071                         add_profile_gc_root (&report, definitely_pinned [idx], MONO_PROFILE_GC_ROOT_PINNING | MONO_PROFILE_GC_ROOT_MISC, 0);
1072                 notify_gc_roots (&report);
1073         }
1074         stat_pinned_objects += count;
1075         return count;
1076 }
1077
1078 static void
1079 pin_objects_in_nursery (ScanCopyContext ctx)
1080 {
1081         size_t reduced_to;
1082
1083         if (nursery_section->pin_queue_first_entry == nursery_section->pin_queue_last_entry)
1084                 return;
1085
1086         reduced_to = pin_objects_from_nursery_pin_queue (ctx);
1087         nursery_section->pin_queue_last_entry = nursery_section->pin_queue_first_entry + reduced_to;
1088 }
1089
1090
1091 void
1092 sgen_pin_object (void *object, GrayQueue *queue)
1093 {
1094         SGEN_PIN_OBJECT (object);
1095         sgen_pin_stage_ptr (object);
1096         ++objects_pinned;
1097         if (G_UNLIKELY (do_pin_stats))
1098                 sgen_pin_stats_register_object (object, safe_object_get_size (object));
1099
1100         GRAY_OBJECT_ENQUEUE (queue, object, sgen_obj_get_descriptor_safe (object));
1101         binary_protocol_pin (object, (gpointer)LOAD_VTABLE (object), safe_object_get_size (object));
1102
1103 #ifdef ENABLE_DTRACE
1104         if (G_UNLIKELY (MONO_GC_OBJ_PINNED_ENABLED ())) {
1105                 int gen = sgen_ptr_in_nursery (object) ? GENERATION_NURSERY : GENERATION_OLD;
1106                 MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (object);
1107                 MONO_GC_OBJ_PINNED ((mword)object, sgen_safe_object_get_size (object), vt->klass->name_space, vt->klass->name, gen);
1108         }
1109 #endif
1110 }
1111
1112 void
1113 sgen_parallel_pin_or_update (void **ptr, void *obj, MonoVTable *vt, SgenGrayQueue *queue)
1114 {
1115         for (;;) {
1116                 mword vtable_word;
1117                 gboolean major_pinned = FALSE;
1118
1119                 if (sgen_ptr_in_nursery (obj)) {
1120                         if (SGEN_CAS_PTR (obj, SGEN_POINTER_TAG_PINNED (vt), vt) == vt) {
1121                                 sgen_pin_object (obj, queue);
1122                                 break;
1123                         }
1124                 } else {
1125                         major_collector.pin_major_object (obj, queue);
1126                         major_pinned = TRUE;
1127                 }
1128
1129                 vtable_word = *(mword*)obj;
1130                 /*someone else forwarded it, update the pointer and bail out*/
1131                 if (SGEN_POINTER_IS_TAGGED_FORWARDED (vtable_word)) {
1132                         *ptr = SGEN_POINTER_UNTAG_VTABLE (vtable_word);
1133                         break;
1134                 }
1135
1136                 /*someone pinned it, nothing to do.*/
1137                 if (SGEN_POINTER_IS_TAGGED_PINNED (vtable_word) || major_pinned)
1138                         break;
1139         }
1140 }
1141
1142 /* Sort the addresses in array in increasing order.
1143  * Done using a by-the book heap sort. Which has decent and stable performance, is pretty cache efficient.
1144  */
1145 void
1146 sgen_sort_addresses (void **array, size_t size)
1147 {
1148         size_t i;
1149         void *tmp;
1150
1151         for (i = 1; i < size; ++i) {
1152                 size_t child = i;
1153                 while (child > 0) {
1154                         size_t parent = (child - 1) / 2;
1155
1156                         if (array [parent] >= array [child])
1157                                 break;
1158
1159                         tmp = array [parent];
1160                         array [parent] = array [child];
1161                         array [child] = tmp;
1162
1163                         child = parent;
1164                 }
1165         }
1166
1167         for (i = size - 1; i > 0; --i) {
1168                 size_t end, root;
1169                 tmp = array [i];
1170                 array [i] = array [0];
1171                 array [0] = tmp;
1172
1173                 end = i - 1;
1174                 root = 0;
1175
1176                 while (root * 2 + 1 <= end) {
1177                         size_t child = root * 2 + 1;
1178
1179                         if (child < end && array [child] < array [child + 1])
1180                                 ++child;
1181                         if (array [root] >= array [child])
1182                                 break;
1183
1184                         tmp = array [root];
1185                         array [root] = array [child];
1186                         array [child] = tmp;
1187
1188                         root = child;
1189                 }
1190         }
1191 }
1192
1193 /* 
1194  * Scan the memory between start and end and queue values which could be pointers
1195  * to the area between start_nursery and end_nursery for later consideration.
1196  * Typically used for thread stacks.
1197  */
1198 static void
1199 conservatively_pin_objects_from (void **start, void **end, void *start_nursery, void *end_nursery, int pin_type)
1200 {
1201         int count = 0;
1202
1203 #ifdef VALGRIND_MAKE_MEM_DEFINED_IF_ADDRESSABLE
1204         VALGRIND_MAKE_MEM_DEFINED_IF_ADDRESSABLE (start, (char*)end - (char*)start);
1205 #endif
1206
1207         while (start < end) {
1208                 if (*start >= start_nursery && *start < end_nursery) {
1209                         /*
1210                          * *start can point to the middle of an object
1211                          * note: should we handle pointing at the end of an object?
1212                          * pinning in C# code disallows pointing at the end of an object
1213                          * but there is some small chance that an optimizing C compiler
1214                          * may keep the only reference to an object by pointing
1215                          * at the end of it. We ignore this small chance for now.
1216                          * Pointers to the end of an object are indistinguishable
1217                          * from pointers to the start of the next object in memory
1218                          * so if we allow that we'd need to pin two objects...
1219                          * We queue the pointer in an array, the
1220                          * array will then be sorted and uniqued. This way
1221                          * we can coalesce several pinning pointers and it should
1222                          * be faster since we'd do a memory scan with increasing
1223                          * addresses. Note: we can align the address to the allocation
1224                          * alignment, so the unique process is more effective.
1225                          */
1226                         mword addr = (mword)*start;
1227                         addr &= ~(ALLOC_ALIGN - 1);
1228                         if (addr >= (mword)start_nursery && addr < (mword)end_nursery) {
1229                                 SGEN_LOG (6, "Pinning address %p from %p", (void*)addr, start);
1230                                 sgen_pin_stage_ptr ((void*)addr);
1231                                 binary_protocol_pin_stage (start, (void*)addr);
1232                                 count++;
1233                         }
1234                         if (G_UNLIKELY (do_pin_stats)) { 
1235                                 if (ptr_in_nursery ((void*)addr))
1236                                         sgen_pin_stats_register_address ((char*)addr, pin_type);
1237                         }
1238                 }
1239                 start++;
1240         }
1241         if (count)
1242                 SGEN_LOG (7, "found %d potential pinned heap pointers", count);
1243 }
1244
1245 /*
1246  * The first thing we do in a collection is to identify pinned objects.
1247  * This function considers all the areas of memory that need to be
1248  * conservatively scanned.
1249  */
1250 static void
1251 pin_from_roots (void *start_nursery, void *end_nursery, GrayQueue *queue)
1252 {
1253         void **start_root;
1254         RootRecord *root;
1255         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);
1256         /* objects pinned from the API are inside these roots */
1257         SGEN_HASH_TABLE_FOREACH (&roots_hash [ROOT_TYPE_PINNED], start_root, root) {
1258                 SGEN_LOG (6, "Pinned roots %p-%p", start_root, root->end_root);
1259                 conservatively_pin_objects_from (start_root, (void**)root->end_root, start_nursery, end_nursery, PIN_TYPE_OTHER);
1260         } SGEN_HASH_TABLE_FOREACH_END;
1261         /* now deal with the thread stacks
1262          * in the future we should be able to conservatively scan only:
1263          * *) the cpu registers
1264          * *) the unmanaged stack frames
1265          * *) the _last_ managed stack frame
1266          * *) pointers slots in managed frames
1267          */
1268         scan_thread_data (start_nursery, end_nursery, FALSE, queue);
1269 }
1270
1271 static void
1272 unpin_objects_from_queue (SgenGrayQueue *queue)
1273 {
1274         for (;;) {
1275                 char *addr;
1276                 mword desc;
1277                 GRAY_OBJECT_DEQUEUE (queue, &addr, &desc);
1278                 if (!addr)
1279                         break;
1280                 g_assert (SGEN_OBJECT_IS_PINNED (addr));
1281                 SGEN_UNPIN_OBJECT (addr);
1282         }
1283 }
1284
1285 typedef struct {
1286         CopyOrMarkObjectFunc func;
1287         GrayQueue *queue;
1288 } UserCopyOrMarkData;
1289
1290 static void
1291 single_arg_user_copy_or_mark (void **obj, void *gc_data)
1292 {
1293         UserCopyOrMarkData *data = gc_data;
1294
1295         data->func (obj, data->queue);
1296 }
1297
1298 /*
1299  * The memory area from start_root to end_root contains pointers to objects.
1300  * Their position is precisely described by @desc (this means that the pointer
1301  * can be either NULL or the pointer to the start of an object).
1302  * This functions copies them to to_space updates them.
1303  *
1304  * This function is not thread-safe!
1305  */
1306 static void
1307 precisely_scan_objects_from (void** start_root, void** end_root, char* n_start, char *n_end, mword desc, ScanCopyContext ctx)
1308 {
1309         CopyOrMarkObjectFunc copy_func = ctx.copy_func;
1310         SgenGrayQueue *queue = ctx.queue;
1311
1312         switch (desc & ROOT_DESC_TYPE_MASK) {
1313         case ROOT_DESC_BITMAP:
1314                 desc >>= ROOT_DESC_TYPE_SHIFT;
1315                 while (desc) {
1316                         if ((desc & 1) && *start_root) {
1317                                 copy_func (start_root, queue);
1318                                 SGEN_LOG (9, "Overwrote root at %p with %p", start_root, *start_root);
1319                         }
1320                         desc >>= 1;
1321                         start_root++;
1322                 }
1323                 return;
1324         case ROOT_DESC_COMPLEX: {
1325                 gsize *bitmap_data = sgen_get_complex_descriptor_bitmap (desc);
1326                 gsize bwords = (*bitmap_data) - 1;
1327                 void **start_run = start_root;
1328                 bitmap_data++;
1329                 while (bwords-- > 0) {
1330                         gsize bmap = *bitmap_data++;
1331                         void **objptr = start_run;
1332                         while (bmap) {
1333                                 if ((bmap & 1) && *objptr) {
1334                                         copy_func (objptr, queue);
1335                                         SGEN_LOG (9, "Overwrote root at %p with %p", objptr, *objptr);
1336                                 }
1337                                 bmap >>= 1;
1338                                 ++objptr;
1339                         }
1340                         start_run += GC_BITS_PER_WORD;
1341                 }
1342                 break;
1343         }
1344         case ROOT_DESC_USER: {
1345                 UserCopyOrMarkData data = { copy_func, queue };
1346                 MonoGCRootMarkFunc marker = sgen_get_user_descriptor_func (desc);
1347                 marker (start_root, single_arg_user_copy_or_mark, &data);
1348                 break;
1349         }
1350         case ROOT_DESC_RUN_LEN:
1351                 g_assert_not_reached ();
1352         default:
1353                 g_assert_not_reached ();
1354         }
1355 }
1356
1357 static void
1358 reset_heap_boundaries (void)
1359 {
1360         lowest_heap_address = ~(mword)0;
1361         highest_heap_address = 0;
1362 }
1363
1364 void
1365 sgen_update_heap_boundaries (mword low, mword high)
1366 {
1367         mword old;
1368
1369         do {
1370                 old = lowest_heap_address;
1371                 if (low >= old)
1372                         break;
1373         } while (SGEN_CAS_PTR ((gpointer*)&lowest_heap_address, (gpointer)low, (gpointer)old) != (gpointer)old);
1374
1375         do {
1376                 old = highest_heap_address;
1377                 if (high <= old)
1378                         break;
1379         } while (SGEN_CAS_PTR ((gpointer*)&highest_heap_address, (gpointer)high, (gpointer)old) != (gpointer)old);
1380 }
1381
1382 /*
1383  * Allocate and setup the data structures needed to be able to allocate objects
1384  * in the nursery. The nursery is stored in nursery_section.
1385  */
1386 static void
1387 alloc_nursery (void)
1388 {
1389         GCMemSection *section;
1390         char *data;
1391         size_t scan_starts;
1392         size_t alloc_size;
1393
1394         if (nursery_section)
1395                 return;
1396         SGEN_LOG (2, "Allocating nursery size: %zu", (size_t)sgen_nursery_size);
1397         /* later we will alloc a larger area for the nursery but only activate
1398          * what we need. The rest will be used as expansion if we have too many pinned
1399          * objects in the existing nursery.
1400          */
1401         /* FIXME: handle OOM */
1402         section = sgen_alloc_internal (INTERNAL_MEM_SECTION);
1403
1404         alloc_size = sgen_nursery_size;
1405
1406         /* If there isn't enough space even for the nursery we should simply abort. */
1407         g_assert (sgen_memgov_try_alloc_space (alloc_size, SPACE_NURSERY));
1408
1409 #ifdef SGEN_ALIGN_NURSERY
1410         data = major_collector.alloc_heap (alloc_size, alloc_size, DEFAULT_NURSERY_BITS);
1411 #else
1412         data = major_collector.alloc_heap (alloc_size, 0, DEFAULT_NURSERY_BITS);
1413 #endif
1414         sgen_update_heap_boundaries ((mword)data, (mword)(data + sgen_nursery_size));
1415         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 ());
1416         section->data = section->next_data = data;
1417         section->size = alloc_size;
1418         section->end_data = data + sgen_nursery_size;
1419         scan_starts = (alloc_size + SCAN_START_SIZE - 1) / SCAN_START_SIZE;
1420         section->scan_starts = sgen_alloc_internal_dynamic (sizeof (char*) * scan_starts, INTERNAL_MEM_SCAN_STARTS, TRUE);
1421         section->num_scan_start = scan_starts;
1422
1423         nursery_section = section;
1424
1425         sgen_nursery_allocator_set_nursery_bounds (data, data + sgen_nursery_size);
1426 }
1427
1428 void*
1429 mono_gc_get_nursery (int *shift_bits, size_t *size)
1430 {
1431         *size = sgen_nursery_size;
1432 #ifdef SGEN_ALIGN_NURSERY
1433         *shift_bits = DEFAULT_NURSERY_BITS;
1434 #else
1435         *shift_bits = -1;
1436 #endif
1437         return sgen_get_nursery_start ();
1438 }
1439
1440 void
1441 mono_gc_set_current_thread_appdomain (MonoDomain *domain)
1442 {
1443         SgenThreadInfo *info = mono_thread_info_current ();
1444
1445         /* Could be called from sgen_thread_unregister () with a NULL info */
1446         if (domain) {
1447                 g_assert (info);
1448                 info->stopped_domain = domain;
1449         }
1450 }
1451
1452 gboolean
1453 mono_gc_precise_stack_mark_enabled (void)
1454 {
1455         return !conservative_stack_mark;
1456 }
1457
1458 FILE *
1459 mono_gc_get_logfile (void)
1460 {
1461         return gc_debug_file;
1462 }
1463
1464 static void
1465 report_finalizer_roots_list (FinalizeReadyEntry *list)
1466 {
1467         GCRootReport report;
1468         FinalizeReadyEntry *fin;
1469
1470         report.count = 0;
1471         for (fin = list; fin; fin = fin->next) {
1472                 if (!fin->object)
1473                         continue;
1474                 add_profile_gc_root (&report, fin->object, MONO_PROFILE_GC_ROOT_FINALIZER, 0);
1475         }
1476         notify_gc_roots (&report);
1477 }
1478
1479 static void
1480 report_finalizer_roots (void)
1481 {
1482         report_finalizer_roots_list (fin_ready_list);
1483         report_finalizer_roots_list (critical_fin_list);
1484 }
1485
1486 static GCRootReport *root_report;
1487
1488 static void
1489 single_arg_report_root (void **obj, void *gc_data)
1490 {
1491         if (*obj)
1492                 add_profile_gc_root (root_report, *obj, MONO_PROFILE_GC_ROOT_OTHER, 0);
1493 }
1494
1495 static void
1496 precisely_report_roots_from (GCRootReport *report, void** start_root, void** end_root, mword desc)
1497 {
1498         switch (desc & ROOT_DESC_TYPE_MASK) {
1499         case ROOT_DESC_BITMAP:
1500                 desc >>= ROOT_DESC_TYPE_SHIFT;
1501                 while (desc) {
1502                         if ((desc & 1) && *start_root) {
1503                                 add_profile_gc_root (report, *start_root, MONO_PROFILE_GC_ROOT_OTHER, 0);
1504                         }
1505                         desc >>= 1;
1506                         start_root++;
1507                 }
1508                 return;
1509         case ROOT_DESC_COMPLEX: {
1510                 gsize *bitmap_data = sgen_get_complex_descriptor_bitmap (desc);
1511                 gsize bwords = (*bitmap_data) - 1;
1512                 void **start_run = start_root;
1513                 bitmap_data++;
1514                 while (bwords-- > 0) {
1515                         gsize bmap = *bitmap_data++;
1516                         void **objptr = start_run;
1517                         while (bmap) {
1518                                 if ((bmap & 1) && *objptr) {
1519                                         add_profile_gc_root (report, *objptr, MONO_PROFILE_GC_ROOT_OTHER, 0);
1520                                 }
1521                                 bmap >>= 1;
1522                                 ++objptr;
1523                         }
1524                         start_run += GC_BITS_PER_WORD;
1525                 }
1526                 break;
1527         }
1528         case ROOT_DESC_USER: {
1529                 MonoGCRootMarkFunc marker = sgen_get_user_descriptor_func (desc);
1530                 root_report = report;
1531                 marker (start_root, single_arg_report_root, NULL);
1532                 break;
1533         }
1534         case ROOT_DESC_RUN_LEN:
1535                 g_assert_not_reached ();
1536         default:
1537                 g_assert_not_reached ();
1538         }
1539 }
1540
1541 static void
1542 report_registered_roots_by_type (int root_type)
1543 {
1544         GCRootReport report;
1545         void **start_root;
1546         RootRecord *root;
1547         report.count = 0;
1548         SGEN_HASH_TABLE_FOREACH (&roots_hash [root_type], start_root, root) {
1549                 SGEN_LOG (6, "Precise root scan %p-%p (desc: %p)", start_root, root->end_root, (void*)root->root_desc);
1550                 precisely_report_roots_from (&report, start_root, (void**)root->end_root, root->root_desc);
1551         } SGEN_HASH_TABLE_FOREACH_END;
1552         notify_gc_roots (&report);
1553 }
1554
1555 static void
1556 report_registered_roots (void)
1557 {
1558         report_registered_roots_by_type (ROOT_TYPE_NORMAL);
1559         report_registered_roots_by_type (ROOT_TYPE_WBARRIER);
1560 }
1561
1562 static void
1563 scan_finalizer_entries (FinalizeReadyEntry *list, ScanCopyContext ctx)
1564 {
1565         CopyOrMarkObjectFunc copy_func = ctx.copy_func;
1566         SgenGrayQueue *queue = ctx.queue;
1567         FinalizeReadyEntry *fin;
1568
1569         for (fin = list; fin; fin = fin->next) {
1570                 if (!fin->object)
1571                         continue;
1572                 SGEN_LOG (5, "Scan of fin ready object: %p (%s)\n", fin->object, safe_name (fin->object));
1573                 copy_func (&fin->object, queue);
1574         }
1575 }
1576
1577 static const char*
1578 generation_name (int generation)
1579 {
1580         switch (generation) {
1581         case GENERATION_NURSERY: return "nursery";
1582         case GENERATION_OLD: return "old";
1583         default: g_assert_not_reached ();
1584         }
1585 }
1586
1587 const char*
1588 sgen_generation_name (int generation)
1589 {
1590         return generation_name (generation);
1591 }
1592
1593 SgenObjectOperations *
1594 sgen_get_current_object_ops (void){
1595         return &current_object_ops;
1596 }
1597
1598
1599 static void
1600 finish_gray_stack (int generation, GrayQueue *queue)
1601 {
1602         TV_DECLARE (atv);
1603         TV_DECLARE (btv);
1604         int done_with_ephemerons, ephemeron_rounds = 0;
1605         CopyOrMarkObjectFunc copy_func = current_object_ops.copy_or_mark_object;
1606         ScanObjectFunc scan_func = current_object_ops.scan_object;
1607         ScanCopyContext ctx = { scan_func, copy_func, queue };
1608         char *start_addr = generation == GENERATION_NURSERY ? sgen_get_nursery_start () : NULL;
1609         char *end_addr = generation == GENERATION_NURSERY ? sgen_get_nursery_end () : (char*)-1;
1610
1611         /*
1612          * We copied all the reachable objects. Now it's the time to copy
1613          * the objects that were not referenced by the roots, but by the copied objects.
1614          * we built a stack of objects pointed to by gray_start: they are
1615          * additional roots and we may add more items as we go.
1616          * We loop until gray_start == gray_objects which means no more objects have
1617          * been added. Note this is iterative: no recursion is involved.
1618          * We need to walk the LO list as well in search of marked big objects
1619          * (use a flag since this is needed only on major collections). We need to loop
1620          * here as well, so keep a counter of marked LO (increasing it in copy_object).
1621          *   To achieve better cache locality and cache usage, we drain the gray stack 
1622          * frequently, after each object is copied, and just finish the work here.
1623          */
1624         sgen_drain_gray_stack (-1, ctx);
1625         TV_GETTIME (atv);
1626         SGEN_LOG (2, "%s generation done", generation_name (generation));
1627
1628         /*
1629         Reset bridge data, we might have lingering data from a previous collection if this is a major
1630         collection trigged by minor overflow.
1631
1632         We must reset the gathered bridges since their original block might be evacuated due to major
1633         fragmentation in the meanwhile and the bridge code should not have to deal with that.
1634         */
1635         if (sgen_need_bridge_processing ())
1636                 sgen_bridge_reset_data ();
1637
1638         /*
1639          * Walk the ephemeron tables marking all values with reachable keys. This must be completely done
1640          * before processing finalizable objects and non-tracking weak links to avoid finalizing/clearing
1641          * objects that are in fact reachable.
1642          */
1643         done_with_ephemerons = 0;
1644         do {
1645                 done_with_ephemerons = mark_ephemerons_in_range (ctx);
1646                 sgen_drain_gray_stack (-1, ctx);
1647                 ++ephemeron_rounds;
1648         } while (!done_with_ephemerons);
1649
1650         sgen_mark_togglerefs (start_addr, end_addr, ctx);
1651
1652         if (sgen_need_bridge_processing ()) {
1653                 /*Make sure the gray stack is empty before we process bridge objects so we get liveness right*/
1654                 sgen_drain_gray_stack (-1, ctx);
1655                 sgen_collect_bridge_objects (generation, ctx);
1656                 if (generation == GENERATION_OLD)
1657                         sgen_collect_bridge_objects (GENERATION_NURSERY, ctx);
1658
1659                 /*
1660                 Do the first bridge step here, as the collector liveness state will become useless after that.
1661
1662                 An important optimization is to only proccess the possibly dead part of the object graph and skip
1663                 over all live objects as we transitively know everything they point must be alive too.
1664
1665                 The above invariant is completely wrong if we let the gray queue be drained and mark/copy everything.
1666
1667                 This has the unfortunate side effect of making overflow collections perform the first step twice, but
1668                 given we now have heuristics that perform major GC in anticipation of minor overflows this should not
1669                 be a big deal.
1670                 */
1671                 sgen_bridge_processing_stw_step ();
1672         }
1673
1674         /*
1675         Make sure we drain the gray stack before processing disappearing links and finalizers.
1676         If we don't make sure it is empty we might wrongly see a live object as dead.
1677         */
1678         sgen_drain_gray_stack (-1, ctx);
1679
1680         /*
1681         We must clear weak links that don't track resurrection before processing object ready for
1682         finalization so they can be cleared before that.
1683         */
1684         sgen_null_link_in_range (generation, TRUE, ctx);
1685         if (generation == GENERATION_OLD)
1686                 sgen_null_link_in_range (GENERATION_NURSERY, TRUE, ctx);
1687
1688
1689         /* walk the finalization queue and move also the objects that need to be
1690          * finalized: use the finalized objects as new roots so the objects they depend
1691          * on are also not reclaimed. As with the roots above, only objects in the nursery
1692          * are marked/copied.
1693          */
1694         sgen_finalize_in_range (generation, ctx);
1695         if (generation == GENERATION_OLD)
1696                 sgen_finalize_in_range (GENERATION_NURSERY, ctx);
1697         /* drain the new stack that might have been created */
1698         SGEN_LOG (6, "Precise scan of gray area post fin");
1699         sgen_drain_gray_stack (-1, ctx);
1700
1701         /*
1702          * This must be done again after processing finalizable objects since CWL slots are cleared only after the key is finalized.
1703          */
1704         done_with_ephemerons = 0;
1705         do {
1706                 done_with_ephemerons = mark_ephemerons_in_range (ctx);
1707                 sgen_drain_gray_stack (-1, ctx);
1708                 ++ephemeron_rounds;
1709         } while (!done_with_ephemerons);
1710
1711         /*
1712          * Clear ephemeron pairs with unreachable keys.
1713          * We pass the copy func so we can figure out if an array was promoted or not.
1714          */
1715         clear_unreachable_ephemerons (ctx);
1716
1717         /*
1718          * We clear togglerefs only after all possible chances of revival are done. 
1719          * This is semantically more inline with what users expect and it allows for
1720          * user finalizers to correctly interact with TR objects.
1721         */
1722         sgen_clear_togglerefs (start_addr, end_addr, ctx);
1723
1724         TV_GETTIME (btv);
1725         SGEN_LOG (2, "Finalize queue handling scan for %s generation: %d usecs %d ephemeron rounds", generation_name (generation), TV_ELAPSED (atv, btv), ephemeron_rounds);
1726
1727         /*
1728          * handle disappearing links
1729          * Note we do this after checking the finalization queue because if an object
1730          * survives (at least long enough to be finalized) we don't clear the link.
1731          * This also deals with a possible issue with the monitor reclamation: with the Boehm
1732          * GC a finalized object my lose the monitor because it is cleared before the finalizer is
1733          * called.
1734          */
1735         g_assert (sgen_gray_object_queue_is_empty (queue));
1736         for (;;) {
1737                 sgen_null_link_in_range (generation, FALSE, ctx);
1738                 if (generation == GENERATION_OLD)
1739                         sgen_null_link_in_range (GENERATION_NURSERY, FALSE, ctx);
1740                 if (sgen_gray_object_queue_is_empty (queue))
1741                         break;
1742                 sgen_drain_gray_stack (-1, ctx);
1743         }
1744
1745         g_assert (sgen_gray_object_queue_is_empty (queue));
1746
1747         sgen_gray_object_queue_trim_free_list (queue);
1748 }
1749
1750 void
1751 sgen_check_section_scan_starts (GCMemSection *section)
1752 {
1753         size_t i;
1754         for (i = 0; i < section->num_scan_start; ++i) {
1755                 if (section->scan_starts [i]) {
1756                         mword size = safe_object_get_size ((MonoObject*) section->scan_starts [i]);
1757                         g_assert (size >= sizeof (MonoObject) && size <= MAX_SMALL_OBJ_SIZE);
1758                 }
1759         }
1760 }
1761
1762 static void
1763 check_scan_starts (void)
1764 {
1765         if (!do_scan_starts_check)
1766                 return;
1767         sgen_check_section_scan_starts (nursery_section);
1768         major_collector.check_scan_starts ();
1769 }
1770
1771 static void
1772 scan_from_registered_roots (char *addr_start, char *addr_end, int root_type, ScanCopyContext ctx)
1773 {
1774         void **start_root;
1775         RootRecord *root;
1776         SGEN_HASH_TABLE_FOREACH (&roots_hash [root_type], start_root, root) {
1777                 SGEN_LOG (6, "Precise root scan %p-%p (desc: %p)", start_root, root->end_root, (void*)root->root_desc);
1778                 precisely_scan_objects_from (start_root, (void**)root->end_root, addr_start, addr_end, root->root_desc, ctx);
1779         } SGEN_HASH_TABLE_FOREACH_END;
1780 }
1781
1782 void
1783 sgen_dump_occupied (char *start, char *end, char *section_start)
1784 {
1785         fprintf (heap_dump_file, "<occupied offset=\"%td\" size=\"%td\"/>\n", start - section_start, end - start);
1786 }
1787
1788 void
1789 sgen_dump_section (GCMemSection *section, const char *type)
1790 {
1791         char *start = section->data;
1792         char *end = section->data + section->size;
1793         char *occ_start = NULL;
1794         GCVTable *vt;
1795         char *old_start G_GNUC_UNUSED = NULL; /* just for debugging */
1796
1797         fprintf (heap_dump_file, "<section type=\"%s\" size=\"%lu\">\n", type, (unsigned long)section->size);
1798
1799         while (start < end) {
1800                 guint size;
1801                 MonoClass *class G_GNUC_UNUSED;
1802
1803                 if (!*(void**)start) {
1804                         if (occ_start) {
1805                                 sgen_dump_occupied (occ_start, start, section->data);
1806                                 occ_start = NULL;
1807                         }
1808                         start += sizeof (void*); /* should be ALLOC_ALIGN, really */
1809                         continue;
1810                 }
1811                 g_assert (start < section->next_data);
1812
1813                 if (!occ_start)
1814                         occ_start = start;
1815
1816                 vt = (GCVTable*)LOAD_VTABLE (start);
1817                 class = vt->klass;
1818
1819                 size = ALIGN_UP (safe_object_get_size ((MonoObject*) start));
1820
1821                 /*
1822                 fprintf (heap_dump_file, "<object offset=\"%d\" class=\"%s.%s\" size=\"%d\"/>\n",
1823                                 start - section->data,
1824                                 vt->klass->name_space, vt->klass->name,
1825                                 size);
1826                 */
1827
1828                 old_start = start;
1829                 start += size;
1830         }
1831         if (occ_start)
1832                 sgen_dump_occupied (occ_start, start, section->data);
1833
1834         fprintf (heap_dump_file, "</section>\n");
1835 }
1836
1837 static void
1838 dump_object (MonoObject *obj, gboolean dump_location)
1839 {
1840         static char class_name [1024];
1841
1842         MonoClass *class = mono_object_class (obj);
1843         int i, j;
1844
1845         /*
1846          * Python's XML parser is too stupid to parse angle brackets
1847          * in strings, so we just ignore them;
1848          */
1849         i = j = 0;
1850         while (class->name [i] && j < sizeof (class_name) - 1) {
1851                 if (!strchr ("<>\"", class->name [i]))
1852                         class_name [j++] = class->name [i];
1853                 ++i;
1854         }
1855         g_assert (j < sizeof (class_name));
1856         class_name [j] = 0;
1857
1858         fprintf (heap_dump_file, "<object class=\"%s.%s\" size=\"%zd\"",
1859                         class->name_space, class_name,
1860                         safe_object_get_size (obj));
1861         if (dump_location) {
1862                 const char *location;
1863                 if (ptr_in_nursery (obj))
1864                         location = "nursery";
1865                 else if (safe_object_get_size (obj) <= MAX_SMALL_OBJ_SIZE)
1866                         location = "major";
1867                 else
1868                         location = "LOS";
1869                 fprintf (heap_dump_file, " location=\"%s\"", location);
1870         }
1871         fprintf (heap_dump_file, "/>\n");
1872 }
1873
1874 static void
1875 dump_heap (const char *type, int num, const char *reason)
1876 {
1877         ObjectList *list;
1878         LOSObject *bigobj;
1879
1880         fprintf (heap_dump_file, "<collection type=\"%s\" num=\"%d\"", type, num);
1881         if (reason)
1882                 fprintf (heap_dump_file, " reason=\"%s\"", reason);
1883         fprintf (heap_dump_file, ">\n");
1884         fprintf (heap_dump_file, "<other-mem-usage type=\"mempools\" size=\"%ld\"/>\n", mono_mempool_get_bytes_allocated ());
1885         sgen_dump_internal_mem_usage (heap_dump_file);
1886         fprintf (heap_dump_file, "<pinned type=\"stack\" bytes=\"%zu\"/>\n", sgen_pin_stats_get_pinned_byte_count (PIN_TYPE_STACK));
1887         /* fprintf (heap_dump_file, "<pinned type=\"static-data\" bytes=\"%d\"/>\n", pinned_byte_counts [PIN_TYPE_STATIC_DATA]); */
1888         fprintf (heap_dump_file, "<pinned type=\"other\" bytes=\"%zu\"/>\n", sgen_pin_stats_get_pinned_byte_count (PIN_TYPE_OTHER));
1889
1890         fprintf (heap_dump_file, "<pinned-objects>\n");
1891         for (list = sgen_pin_stats_get_object_list (); list; list = list->next)
1892                 dump_object (list->obj, TRUE);
1893         fprintf (heap_dump_file, "</pinned-objects>\n");
1894
1895         sgen_dump_section (nursery_section, "nursery");
1896
1897         major_collector.dump_heap (heap_dump_file);
1898
1899         fprintf (heap_dump_file, "<los>\n");
1900         for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1901                 dump_object ((MonoObject*)bigobj->data, FALSE);
1902         fprintf (heap_dump_file, "</los>\n");
1903
1904         fprintf (heap_dump_file, "</collection>\n");
1905 }
1906
1907 void
1908 sgen_register_moved_object (void *obj, void *destination)
1909 {
1910         g_assert (mono_profiler_events & MONO_PROFILE_GC_MOVES);
1911
1912         if (moved_objects_idx == MOVED_OBJECTS_NUM) {
1913                 mono_profiler_gc_moves (moved_objects, moved_objects_idx);
1914                 moved_objects_idx = 0;
1915         }
1916         moved_objects [moved_objects_idx++] = obj;
1917         moved_objects [moved_objects_idx++] = destination;
1918 }
1919
1920 static void
1921 init_stats (void)
1922 {
1923         static gboolean inited = FALSE;
1924
1925         if (inited)
1926                 return;
1927
1928         mono_counters_register ("Collection max time",  MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME | MONO_COUNTER_MONOTONIC, &time_max);
1929
1930         mono_counters_register ("Minor fragment clear", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_minor_pre_collection_fragment_clear);
1931         mono_counters_register ("Minor pinning", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_minor_pinning);
1932         mono_counters_register ("Minor scan remembered set", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_minor_scan_remsets);
1933         mono_counters_register ("Minor scan pinned", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_minor_scan_pinned);
1934         mono_counters_register ("Minor scan registered roots", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_minor_scan_registered_roots);
1935         mono_counters_register ("Minor scan thread data", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_minor_scan_thread_data);
1936         mono_counters_register ("Minor finish gray stack", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_minor_finish_gray_stack);
1937         mono_counters_register ("Minor fragment creation", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_minor_fragment_creation);
1938
1939         mono_counters_register ("Major fragment clear", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_major_pre_collection_fragment_clear);
1940         mono_counters_register ("Major pinning", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_major_pinning);
1941         mono_counters_register ("Major scan pinned", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_major_scan_pinned);
1942         mono_counters_register ("Major scan registered roots", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_major_scan_registered_roots);
1943         mono_counters_register ("Major scan thread data", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_major_scan_thread_data);
1944         mono_counters_register ("Major scan alloc_pinned", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_major_scan_alloc_pinned);
1945         mono_counters_register ("Major scan finalized", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_major_scan_finalized);
1946         mono_counters_register ("Major scan big objects", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_major_scan_big_objects);
1947         mono_counters_register ("Major finish gray stack", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_major_finish_gray_stack);
1948         mono_counters_register ("Major free big objects", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_major_free_bigobjs);
1949         mono_counters_register ("Major LOS sweep", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_major_los_sweep);
1950         mono_counters_register ("Major sweep", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_major_sweep);
1951         mono_counters_register ("Major fragment creation", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_major_fragment_creation);
1952
1953         mono_counters_register ("Number of pinned objects", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_pinned_objects);
1954
1955 #ifdef HEAVY_STATISTICS
1956         mono_counters_register ("WBarrier remember pointer", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_add_to_global_remset);
1957         mono_counters_register ("WBarrier set field", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_field);
1958         mono_counters_register ("WBarrier set arrayref", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_arrayref);
1959         mono_counters_register ("WBarrier arrayref copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_arrayref_copy);
1960         mono_counters_register ("WBarrier generic store called", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_generic_store);
1961         mono_counters_register ("WBarrier generic atomic store called", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_generic_store_atomic);
1962         mono_counters_register ("WBarrier set root", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_root);
1963         mono_counters_register ("WBarrier value copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_value_copy);
1964         mono_counters_register ("WBarrier object copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_object_copy);
1965
1966         mono_counters_register ("# objects allocated degraded", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_objects_alloced_degraded);
1967         mono_counters_register ("bytes allocated degraded", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_bytes_alloced_degraded);
1968
1969         mono_counters_register ("# copy_object() called (nursery)", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_copy_object_called_nursery);
1970         mono_counters_register ("# objects copied (nursery)", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_objects_copied_nursery);
1971         mono_counters_register ("# copy_object() called (major)", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_copy_object_called_major);
1972         mono_counters_register ("# objects copied (major)", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_objects_copied_major);
1973
1974         mono_counters_register ("# scan_object() called (nursery)", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_scan_object_called_nursery);
1975         mono_counters_register ("# scan_object() called (major)", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_scan_object_called_major);
1976
1977         mono_counters_register ("Slots allocated in vain", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_slots_allocated_in_vain);
1978
1979         mono_counters_register ("# nursery copy_object() failed from space", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_nursery_copy_object_failed_from_space);
1980         mono_counters_register ("# nursery copy_object() failed forwarded", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_nursery_copy_object_failed_forwarded);
1981         mono_counters_register ("# nursery copy_object() failed pinned", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_nursery_copy_object_failed_pinned);
1982         mono_counters_register ("# nursery copy_object() failed to space", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_nursery_copy_object_failed_to_space);
1983
1984         sgen_nursery_allocator_init_heavy_stats ();
1985         sgen_alloc_init_heavy_stats ();
1986 #endif
1987
1988         inited = TRUE;
1989 }
1990
1991
1992 static void
1993 reset_pinned_from_failed_allocation (void)
1994 {
1995         bytes_pinned_from_failed_allocation = 0;
1996 }
1997
1998 void
1999 sgen_set_pinned_from_failed_allocation (mword objsize)
2000 {
2001         bytes_pinned_from_failed_allocation += objsize;
2002 }
2003
2004 gboolean
2005 sgen_collection_is_concurrent (void)
2006 {
2007         switch (current_collection_generation) {
2008         case GENERATION_NURSERY:
2009                 return FALSE;
2010         case GENERATION_OLD:
2011                 return concurrent_collection_in_progress;
2012         default:
2013                 g_error ("Invalid current generation %d", current_collection_generation);
2014         }
2015 }
2016
2017 gboolean
2018 sgen_concurrent_collection_in_progress (void)
2019 {
2020         return concurrent_collection_in_progress;
2021 }
2022
2023 static void
2024 job_remembered_set_scan (WorkerData *worker_data, void *dummy)
2025 {
2026         remset.scan_remsets (sgen_workers_get_job_gray_queue (worker_data));
2027 }
2028
2029 typedef struct
2030 {
2031         CopyOrMarkObjectFunc copy_or_mark_func;
2032         ScanObjectFunc scan_func;
2033         char *heap_start;
2034         char *heap_end;
2035         int root_type;
2036 } ScanFromRegisteredRootsJobData;
2037
2038 static void
2039 job_scan_from_registered_roots (WorkerData *worker_data, void *job_data_untyped)
2040 {
2041         ScanFromRegisteredRootsJobData *job_data = job_data_untyped;
2042         ScanCopyContext ctx = { job_data->scan_func, job_data->copy_or_mark_func,
2043                 sgen_workers_get_job_gray_queue (worker_data) };
2044
2045         scan_from_registered_roots (job_data->heap_start, job_data->heap_end, job_data->root_type, ctx);
2046         sgen_free_internal_dynamic (job_data, sizeof (ScanFromRegisteredRootsJobData), INTERNAL_MEM_WORKER_JOB_DATA);
2047 }
2048
2049 typedef struct
2050 {
2051         char *heap_start;
2052         char *heap_end;
2053 } ScanThreadDataJobData;
2054
2055 static void
2056 job_scan_thread_data (WorkerData *worker_data, void *job_data_untyped)
2057 {
2058         ScanThreadDataJobData *job_data = job_data_untyped;
2059
2060         scan_thread_data (job_data->heap_start, job_data->heap_end, TRUE,
2061                         sgen_workers_get_job_gray_queue (worker_data));
2062         sgen_free_internal_dynamic (job_data, sizeof (ScanThreadDataJobData), INTERNAL_MEM_WORKER_JOB_DATA);
2063 }
2064
2065 static void
2066 job_scan_finalizer_entries (WorkerData *worker_data, void *job_data_untyped)
2067 {
2068         FinalizeReadyEntry *list = job_data_untyped;
2069         ScanCopyContext ctx = { NULL, current_object_ops.copy_or_mark_object, sgen_workers_get_job_gray_queue (worker_data) };
2070
2071         scan_finalizer_entries (list, ctx);
2072 }
2073
2074 static void
2075 job_scan_major_mod_union_cardtable (WorkerData *worker_data, void *job_data_untyped)
2076 {
2077         g_assert (concurrent_collection_in_progress);
2078         major_collector.scan_card_table (TRUE, sgen_workers_get_job_gray_queue (worker_data));
2079 }
2080
2081 static void
2082 job_scan_los_mod_union_cardtable (WorkerData *worker_data, void *job_data_untyped)
2083 {
2084         g_assert (concurrent_collection_in_progress);
2085         sgen_los_scan_card_table (TRUE, sgen_workers_get_job_gray_queue (worker_data));
2086 }
2087
2088 static void
2089 verify_scan_starts (char *start, char *end)
2090 {
2091         size_t i;
2092
2093         for (i = 0; i < nursery_section->num_scan_start; ++i) {
2094                 char *addr = nursery_section->scan_starts [i];
2095                 if (addr > start && addr < end)
2096                         SGEN_LOG (1, "NFC-BAD SCAN START [%zu] %p for obj [%p %p]", i, addr, start, end);
2097         }
2098 }
2099
2100 static void
2101 verify_nursery (void)
2102 {
2103         char *start, *end, *cur, *hole_start;
2104
2105         if (!do_verify_nursery)
2106                 return;
2107                 
2108         if (nursery_canaries_enabled ())
2109                 SGEN_LOG (1, "Checking nursery canaries...");
2110
2111         /*This cleans up unused fragments */
2112         sgen_nursery_allocator_prepare_for_pinning ();
2113
2114         hole_start = start = cur = sgen_get_nursery_start ();
2115         end = sgen_get_nursery_end ();
2116
2117         while (cur < end) {
2118                 size_t ss, size;
2119
2120                 if (!*(void**)cur) {
2121                         cur += sizeof (void*);
2122                         continue;
2123                 }
2124
2125                 if (object_is_forwarded (cur))
2126                         SGEN_LOG (1, "FORWARDED OBJ %p", cur);
2127                 else if (object_is_pinned (cur))
2128                         SGEN_LOG (1, "PINNED OBJ %p", cur);
2129
2130                 ss = safe_object_get_size ((MonoObject*)cur);
2131                 size = ALIGN_UP (safe_object_get_size ((MonoObject*)cur));
2132                 verify_scan_starts (cur, cur + size);
2133                 if (do_dump_nursery_content) {
2134                         if (cur > hole_start)
2135                                 SGEN_LOG (1, "HOLE [%p %p %d]", hole_start, cur, (int)(cur - hole_start));
2136                         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_get_array_fill_vtable ());
2137                 }
2138                 if (nursery_canaries_enabled () && (MonoVTable*)SGEN_LOAD_VTABLE (cur) != array_fill_vtable) {
2139                         CHECK_CANARY_FOR_OBJECT (cur);
2140                         CANARIFY_SIZE (size);
2141                 }
2142                 cur += size;
2143                 hole_start = cur;
2144         }
2145 }
2146
2147 /*
2148  * Checks that no objects in the nursery are fowarded or pinned.  This
2149  * is a precondition to restarting the mutator while doing a
2150  * concurrent collection.  Note that we don't clear fragments because
2151  * we depend on that having happened earlier.
2152  */
2153 static void
2154 check_nursery_is_clean (void)
2155 {
2156         char *end, *cur;
2157
2158         cur = sgen_get_nursery_start ();
2159         end = sgen_get_nursery_end ();
2160
2161         while (cur < end) {
2162                 size_t size;
2163
2164                 if (!*(void**)cur) {
2165                         cur += sizeof (void*);
2166                         continue;
2167                 }
2168
2169                 g_assert (!object_is_forwarded (cur));
2170                 g_assert (!object_is_pinned (cur));
2171
2172                 size = ALIGN_UP (safe_object_get_size ((MonoObject*)cur));
2173                 verify_scan_starts (cur, cur + size);
2174
2175                 cur += size;
2176         }
2177 }
2178
2179 static void
2180 init_gray_queue (void)
2181 {
2182         if (sgen_collection_is_concurrent ())
2183                 sgen_workers_init_distribute_gray_queue ();
2184         sgen_gray_object_queue_init (&gray_queue, NULL);
2185 }
2186
2187 /*
2188  * Perform a nursery collection.
2189  *
2190  * Return whether any objects were late-pinned due to being out of memory.
2191  */
2192 static gboolean
2193 collect_nursery (SgenGrayQueue *unpin_queue, gboolean finish_up_concurrent_mark)
2194 {
2195         gboolean needs_major;
2196         size_t max_garbage_amount;
2197         char *nursery_next;
2198         ScanFromRegisteredRootsJobData *scrrjd_normal, *scrrjd_wbarrier;
2199         ScanThreadDataJobData *stdjd;
2200         mword fragment_total;
2201         ScanCopyContext ctx;
2202         TV_DECLARE (atv);
2203         TV_DECLARE (btv);
2204
2205         if (disable_minor_collections)
2206                 return TRUE;
2207
2208         TV_GETTIME (last_minor_collection_start_tv);
2209         atv = last_minor_collection_start_tv;
2210
2211         MONO_GC_BEGIN (GENERATION_NURSERY);
2212         binary_protocol_collection_begin (gc_stats.minor_gc_count, GENERATION_NURSERY);
2213
2214         verify_nursery ();
2215
2216 #ifndef DISABLE_PERFCOUNTERS
2217         mono_perfcounters->gc_collections0++;
2218 #endif
2219
2220         current_collection_generation = GENERATION_NURSERY;
2221         current_object_ops = sgen_minor_collector.serial_ops;
2222
2223         reset_pinned_from_failed_allocation ();
2224
2225         check_scan_starts ();
2226
2227         sgen_nursery_alloc_prepare_for_minor ();
2228
2229         degraded_mode = 0;
2230         objects_pinned = 0;
2231         nursery_next = sgen_nursery_alloc_get_upper_alloc_bound ();
2232         /* FIXME: optimize later to use the higher address where an object can be present */
2233         nursery_next = MAX (nursery_next, sgen_get_nursery_end ());
2234
2235         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 ()));
2236         max_garbage_amount = nursery_next - sgen_get_nursery_start ();
2237         g_assert (nursery_section->size >= max_garbage_amount);
2238
2239         /* world must be stopped already */
2240         TV_GETTIME (btv);
2241         time_minor_pre_collection_fragment_clear += TV_ELAPSED (atv, btv);
2242
2243         if (xdomain_checks) {
2244                 sgen_clear_nursery_fragments ();
2245                 sgen_check_for_xdomain_refs ();
2246         }
2247
2248         nursery_section->next_data = nursery_next;
2249
2250         major_collector.start_nursery_collection ();
2251
2252         sgen_memgov_minor_collection_start ();
2253
2254         init_gray_queue ();
2255
2256         gc_stats.minor_gc_count ++;
2257
2258         if (whole_heap_check_before_collection) {
2259                 sgen_clear_nursery_fragments ();
2260                 sgen_check_whole_heap (finish_up_concurrent_mark);
2261         }
2262         if (consistency_check_at_minor_collection)
2263                 sgen_check_consistency ();
2264
2265         MONO_GC_CHECKPOINT_1 (GENERATION_NURSERY);
2266
2267         sgen_process_fin_stage_entries ();
2268         sgen_process_dislink_stage_entries ();
2269
2270         MONO_GC_CHECKPOINT_2 (GENERATION_NURSERY);
2271
2272         /* pin from pinned handles */
2273         sgen_init_pinning ();
2274         mono_profiler_gc_event (MONO_GC_EVENT_MARK_START, 0);
2275         pin_from_roots (sgen_get_nursery_start (), nursery_next, WORKERS_DISTRIBUTE_GRAY_QUEUE);
2276         /* pin cemented objects */
2277         sgen_pin_cemented_objects ();
2278         /* identify pinned objects */
2279         sgen_optimize_pin_queue ();
2280         sgen_pinning_setup_section (nursery_section);
2281         ctx.scan_func = NULL;
2282         ctx.copy_func = NULL;
2283         ctx.queue = WORKERS_DISTRIBUTE_GRAY_QUEUE;
2284         pin_objects_in_nursery (ctx);
2285         sgen_pinning_trim_queue_to_section (nursery_section);
2286
2287         TV_GETTIME (atv);
2288         time_minor_pinning += TV_ELAPSED (btv, atv);
2289         SGEN_LOG (2, "Finding pinned pointers: %zd in %d usecs", sgen_get_pinned_count (), TV_ELAPSED (btv, atv));
2290         SGEN_LOG (4, "Start scan with %zd pinned objects", sgen_get_pinned_count ());
2291
2292         MONO_GC_CHECKPOINT_3 (GENERATION_NURSERY);
2293
2294         /*
2295          * FIXME: When we finish a concurrent collection we do a nursery collection first,
2296          * as part of which we scan the card table.  Then, later, we scan the mod union
2297          * cardtable.  We should only have to do one.
2298          */
2299         sgen_workers_enqueue_job (job_remembered_set_scan, NULL);
2300
2301         /* we don't have complete write barrier yet, so we scan all the old generation sections */
2302         TV_GETTIME (btv);
2303         time_minor_scan_remsets += TV_ELAPSED (atv, btv);
2304         SGEN_LOG (2, "Old generation scan: %d usecs", TV_ELAPSED (atv, btv));
2305
2306         MONO_GC_CHECKPOINT_4 (GENERATION_NURSERY);
2307
2308         /* FIXME: why is this here? */
2309         ctx.scan_func = current_object_ops.scan_object;
2310         ctx.copy_func = NULL;
2311         ctx.queue = &gray_queue;
2312         sgen_drain_gray_stack (-1, ctx);
2313
2314         if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
2315                 report_registered_roots ();
2316         if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
2317                 report_finalizer_roots ();
2318         TV_GETTIME (atv);
2319         time_minor_scan_pinned += TV_ELAPSED (btv, atv);
2320
2321         MONO_GC_CHECKPOINT_5 (GENERATION_NURSERY);
2322
2323         /* registered roots, this includes static fields */
2324         scrrjd_normal = sgen_alloc_internal_dynamic (sizeof (ScanFromRegisteredRootsJobData), INTERNAL_MEM_WORKER_JOB_DATA, TRUE);
2325         scrrjd_normal->copy_or_mark_func = current_object_ops.copy_or_mark_object;
2326         scrrjd_normal->scan_func = current_object_ops.scan_object;
2327         scrrjd_normal->heap_start = sgen_get_nursery_start ();
2328         scrrjd_normal->heap_end = nursery_next;
2329         scrrjd_normal->root_type = ROOT_TYPE_NORMAL;
2330         sgen_workers_enqueue_job (job_scan_from_registered_roots, scrrjd_normal);
2331
2332         scrrjd_wbarrier = sgen_alloc_internal_dynamic (sizeof (ScanFromRegisteredRootsJobData), INTERNAL_MEM_WORKER_JOB_DATA, TRUE);
2333         scrrjd_wbarrier->copy_or_mark_func = current_object_ops.copy_or_mark_object;
2334         scrrjd_wbarrier->scan_func = current_object_ops.scan_object;
2335         scrrjd_wbarrier->heap_start = sgen_get_nursery_start ();
2336         scrrjd_wbarrier->heap_end = nursery_next;
2337         scrrjd_wbarrier->root_type = ROOT_TYPE_WBARRIER;
2338         sgen_workers_enqueue_job (job_scan_from_registered_roots, scrrjd_wbarrier);
2339
2340         TV_GETTIME (btv);
2341         time_minor_scan_registered_roots += TV_ELAPSED (atv, btv);
2342
2343         MONO_GC_CHECKPOINT_6 (GENERATION_NURSERY);
2344
2345         /* thread data */
2346         stdjd = sgen_alloc_internal_dynamic (sizeof (ScanThreadDataJobData), INTERNAL_MEM_WORKER_JOB_DATA, TRUE);
2347         stdjd->heap_start = sgen_get_nursery_start ();
2348         stdjd->heap_end = nursery_next;
2349         sgen_workers_enqueue_job (job_scan_thread_data, stdjd);
2350
2351         TV_GETTIME (atv);
2352         time_minor_scan_thread_data += TV_ELAPSED (btv, atv);
2353         btv = atv;
2354
2355         MONO_GC_CHECKPOINT_7 (GENERATION_NURSERY);
2356
2357         g_assert (!sgen_collection_is_concurrent ());
2358
2359         /* Scan the list of objects ready for finalization. If */
2360         sgen_workers_enqueue_job (job_scan_finalizer_entries, fin_ready_list);
2361         sgen_workers_enqueue_job (job_scan_finalizer_entries, critical_fin_list);
2362
2363         MONO_GC_CHECKPOINT_8 (GENERATION_NURSERY);
2364
2365         finish_gray_stack (GENERATION_NURSERY, &gray_queue);
2366         TV_GETTIME (atv);
2367         time_minor_finish_gray_stack += TV_ELAPSED (btv, atv);
2368         mono_profiler_gc_event (MONO_GC_EVENT_MARK_END, 0);
2369
2370         MONO_GC_CHECKPOINT_9 (GENERATION_NURSERY);
2371
2372         /*
2373          * The (single-threaded) finalization code might have done
2374          * some copying/marking so we can only reset the GC thread's
2375          * worker data here instead of earlier when we joined the
2376          * workers.
2377          */
2378         sgen_workers_reset_data ();
2379
2380         if (objects_pinned) {
2381                 sgen_optimize_pin_queue ();
2382                 sgen_pinning_setup_section (nursery_section);
2383         }
2384
2385         /* walk the pin_queue, build up the fragment list of free memory, unmark
2386          * pinned objects as we go, memzero() the empty fragments so they are ready for the
2387          * next allocations.
2388          */
2389         mono_profiler_gc_event (MONO_GC_EVENT_RECLAIM_START, 0);
2390         fragment_total = sgen_build_nursery_fragments (nursery_section, unpin_queue);
2391         if (!fragment_total)
2392                 degraded_mode = 1;
2393
2394         /* Clear TLABs for all threads */
2395         sgen_clear_tlabs ();
2396
2397         mono_profiler_gc_event (MONO_GC_EVENT_RECLAIM_END, 0);
2398         TV_GETTIME (btv);
2399         time_minor_fragment_creation += TV_ELAPSED (atv, btv);
2400         SGEN_LOG (2, "Fragment creation: %d usecs, %lu bytes available", TV_ELAPSED (atv, btv), (unsigned long)fragment_total);
2401
2402         if (consistency_check_at_minor_collection)
2403                 sgen_check_major_refs ();
2404
2405         major_collector.finish_nursery_collection ();
2406
2407         TV_GETTIME (last_minor_collection_end_tv);
2408         gc_stats.minor_gc_time += TV_ELAPSED (last_minor_collection_start_tv, last_minor_collection_end_tv);
2409
2410         if (heap_dump_file)
2411                 dump_heap ("minor", gc_stats.minor_gc_count - 1, NULL);
2412
2413         /* prepare the pin queue for the next collection */
2414         sgen_finish_pinning ();
2415         if (fin_ready_list || critical_fin_list) {
2416                 SGEN_LOG (4, "Finalizer-thread wakeup: ready %d", num_ready_finalizers);
2417                 mono_gc_finalize_notify ();
2418         }
2419         sgen_pin_stats_reset ();
2420         /* clear cemented hash */
2421         sgen_cement_clear_below_threshold ();
2422
2423         g_assert (sgen_gray_object_queue_is_empty (&gray_queue));
2424
2425         remset.finish_minor_collection ();
2426
2427         check_scan_starts ();
2428
2429         binary_protocol_flush_buffers (FALSE);
2430
2431         sgen_memgov_minor_collection_end ();
2432
2433         /*objects are late pinned because of lack of memory, so a major is a good call*/
2434         needs_major = objects_pinned > 0;
2435         current_collection_generation = -1;
2436         objects_pinned = 0;
2437
2438         MONO_GC_END (GENERATION_NURSERY);
2439         binary_protocol_collection_end (gc_stats.minor_gc_count - 1, GENERATION_NURSERY, 0, 0);
2440
2441         if (check_nursery_objects_pinned && !sgen_minor_collector.is_split)
2442                 sgen_check_nursery_objects_pinned (unpin_queue != NULL);
2443
2444         return needs_major;
2445 }
2446
2447 static void
2448 scan_nursery_objects_callback (char *obj, size_t size, ScanCopyContext *ctx)
2449 {
2450         /*
2451          * This is called on all objects in the nursery, including pinned ones, so we need
2452          * to use sgen_obj_get_descriptor_safe(), which masks out the vtable tag bits.
2453          */
2454         ctx->scan_func (obj, sgen_obj_get_descriptor_safe (obj), ctx->queue);
2455 }
2456
2457 static void
2458 scan_nursery_objects (ScanCopyContext ctx)
2459 {
2460         sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
2461                         (IterateObjectCallbackFunc)scan_nursery_objects_callback, (void*)&ctx, FALSE);
2462 }
2463
2464 static void
2465 major_copy_or_mark_from_roots (size_t *old_next_pin_slot, gboolean start_concurrent_mark, gboolean finish_up_concurrent_mark, gboolean scan_mod_union, gboolean scan_whole_nursery)
2466 {
2467         LOSObject *bigobj;
2468         TV_DECLARE (atv);
2469         TV_DECLARE (btv);
2470         /* FIXME: only use these values for the precise scan
2471          * note that to_space pointers should be excluded anyway...
2472          */
2473         char *heap_start = NULL;
2474         char *heap_end = (char*)-1;
2475         gboolean profile_roots = mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS;
2476         GCRootReport root_report = { 0 };
2477         ScanFromRegisteredRootsJobData *scrrjd_normal, *scrrjd_wbarrier;
2478         ScanThreadDataJobData *stdjd;
2479         ScanCopyContext ctx;
2480
2481         if (concurrent_collection_in_progress) {
2482                 /*This cleans up unused fragments */
2483                 sgen_nursery_allocator_prepare_for_pinning ();
2484
2485                 if (do_concurrent_checks)
2486                         check_nursery_is_clean ();
2487         } else {
2488                 /* The concurrent collector doesn't touch the nursery. */
2489                 sgen_nursery_alloc_prepare_for_major ();
2490         }
2491
2492         init_gray_queue ();
2493
2494         TV_GETTIME (atv);
2495
2496         /* Pinning depends on this */
2497         sgen_clear_nursery_fragments ();
2498
2499         if (whole_heap_check_before_collection)
2500                 sgen_check_whole_heap (finish_up_concurrent_mark);
2501
2502         TV_GETTIME (btv);
2503         time_major_pre_collection_fragment_clear += TV_ELAPSED (atv, btv);
2504
2505         if (!sgen_collection_is_concurrent ())
2506                 nursery_section->next_data = sgen_get_nursery_end ();
2507         /* we should also coalesce scanning from sections close to each other
2508          * and deal with pointers outside of the sections later.
2509          */
2510
2511         objects_pinned = 0;
2512
2513         if (xdomain_checks) {
2514                 sgen_clear_nursery_fragments ();
2515                 sgen_check_for_xdomain_refs ();
2516         }
2517
2518         if (!concurrent_collection_in_progress) {
2519                 /* Remsets are not useful for a major collection */
2520                 remset.clear_cards ();
2521         }
2522
2523         sgen_process_fin_stage_entries ();
2524         sgen_process_dislink_stage_entries ();
2525
2526         TV_GETTIME (atv);
2527         sgen_init_pinning ();
2528         SGEN_LOG (6, "Collecting pinned addresses");
2529         pin_from_roots ((void*)lowest_heap_address, (void*)highest_heap_address, WORKERS_DISTRIBUTE_GRAY_QUEUE);
2530
2531         if (!concurrent_collection_in_progress || finish_up_concurrent_mark) {
2532                 if (major_collector.is_concurrent) {
2533                         /*
2534                          * The concurrent major collector cannot evict
2535                          * yet, so we need to pin cemented objects to
2536                          * not break some asserts.
2537                          *
2538                          * FIXME: We could evict now!
2539                          */
2540                         sgen_pin_cemented_objects ();
2541                 }
2542         }
2543
2544         sgen_optimize_pin_queue ();
2545
2546         /*
2547          * pin_queue now contains all candidate pointers, sorted and
2548          * uniqued.  We must do two passes now to figure out which
2549          * objects are pinned.
2550          *
2551          * The first is to find within the pin_queue the area for each
2552          * section.  This requires that the pin_queue be sorted.  We
2553          * also process the LOS objects and pinned chunks here.
2554          *
2555          * The second, destructive, pass is to reduce the section
2556          * areas to pointers to the actually pinned objects.
2557          */
2558         SGEN_LOG (6, "Pinning from sections");
2559         /* first pass for the sections */
2560         sgen_find_section_pin_queue_start_end (nursery_section);
2561         major_collector.find_pin_queue_start_ends (WORKERS_DISTRIBUTE_GRAY_QUEUE);
2562         /* identify possible pointers to the insize of large objects */
2563         SGEN_LOG (6, "Pinning from large objects");
2564         for (bigobj = los_object_list; bigobj; bigobj = bigobj->next) {
2565                 size_t dummy;
2566                 if (sgen_find_optimized_pin_queue_area (bigobj->data, (char*)bigobj->data + sgen_los_object_size (bigobj), &dummy, &dummy)) {
2567                         binary_protocol_pin (bigobj->data, (gpointer)LOAD_VTABLE (bigobj->data), safe_object_get_size (((MonoObject*)(bigobj->data))));
2568
2569 #ifdef ENABLE_DTRACE
2570                         if (G_UNLIKELY (MONO_GC_OBJ_PINNED_ENABLED ())) {
2571                                 MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (bigobj->data);
2572                                 MONO_GC_OBJ_PINNED ((mword)bigobj->data, sgen_safe_object_get_size ((MonoObject*)bigobj->data), vt->klass->name_space, vt->klass->name, GENERATION_OLD);
2573                         }
2574 #endif
2575
2576                         if (sgen_los_object_is_pinned (bigobj->data)) {
2577                                 g_assert (finish_up_concurrent_mark);
2578                                 continue;
2579                         }
2580                         sgen_los_pin_object (bigobj->data);
2581                         if (SGEN_OBJECT_HAS_REFERENCES (bigobj->data))
2582                                 GRAY_OBJECT_ENQUEUE (WORKERS_DISTRIBUTE_GRAY_QUEUE, bigobj->data, sgen_obj_get_descriptor (bigobj->data));
2583                         if (G_UNLIKELY (do_pin_stats))
2584                                 sgen_pin_stats_register_object ((char*) bigobj->data, safe_object_get_size ((MonoObject*) bigobj->data));
2585                         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));
2586
2587                         if (profile_roots)
2588                                 add_profile_gc_root (&root_report, bigobj->data, MONO_PROFILE_GC_ROOT_PINNING | MONO_PROFILE_GC_ROOT_MISC, 0);
2589                 }
2590         }
2591         if (profile_roots)
2592                 notify_gc_roots (&root_report);
2593         /* second pass for the sections */
2594         ctx.scan_func = concurrent_collection_in_progress ? current_object_ops.scan_object : NULL;
2595         ctx.copy_func = NULL;
2596         ctx.queue = WORKERS_DISTRIBUTE_GRAY_QUEUE;
2597
2598         /*
2599          * Concurrent mark never follows references into the nursery.  In the start and
2600          * finish pauses we must scan live nursery objects, though.
2601          *
2602          * In the finish pause we do this conservatively by scanning all nursery objects.
2603          * Previously we would only scan pinned objects here.  We assumed that all objects
2604          * that were pinned during the nursery collection immediately preceding this finish
2605          * mark would be pinned again here.  Due to the way we get the stack end for the GC
2606          * thread, however, that's not necessarily the case: we scan part of the stack used
2607          * by the GC itself, which changes constantly, so pinning isn't entirely
2608          * deterministic.
2609          *
2610          * The split nursery also complicates things because non-pinned objects can survive
2611          * in the nursery.  That's why we need to do a full scan of the nursery for it, too.
2612          *
2613          * In the future we shouldn't do a preceding nursery collection at all and instead
2614          * do the finish pause with promotion from the nursery.
2615          *
2616          * A further complication arises when we have late-pinned objects from the preceding
2617          * nursery collection.  Those are the result of being out of memory when trying to
2618          * evacuate objects.  They won't be found from the roots, so we just scan the whole
2619          * nursery.
2620          *
2621          * Non-concurrent mark evacuates from the nursery, so it's
2622          * sufficient to just scan pinned nursery objects.
2623          */
2624         if (scan_whole_nursery || finish_up_concurrent_mark || (concurrent_collection_in_progress && sgen_minor_collector.is_split)) {
2625                 scan_nursery_objects (ctx);
2626         } else {
2627                 pin_objects_in_nursery (ctx);
2628                 if (check_nursery_objects_pinned && !sgen_minor_collector.is_split)
2629                         sgen_check_nursery_objects_pinned (!concurrent_collection_in_progress || finish_up_concurrent_mark);
2630         }
2631
2632         major_collector.pin_objects (WORKERS_DISTRIBUTE_GRAY_QUEUE);
2633         if (old_next_pin_slot)
2634                 *old_next_pin_slot = sgen_get_pinned_count ();
2635
2636         TV_GETTIME (btv);
2637         time_major_pinning += TV_ELAPSED (atv, btv);
2638         SGEN_LOG (2, "Finding pinned pointers: %zd in %d usecs", sgen_get_pinned_count (), TV_ELAPSED (atv, btv));
2639         SGEN_LOG (4, "Start scan with %zd pinned objects", sgen_get_pinned_count ());
2640
2641         major_collector.init_to_space ();
2642
2643         /*
2644          * The concurrent collector doesn't move objects, neither on
2645          * the major heap nor in the nursery, so we can mark even
2646          * before pinning has finished.  For the non-concurrent
2647          * collector we start the workers after pinning.
2648          */
2649         if (start_concurrent_mark) {
2650                 sgen_workers_start_all_workers ();
2651                 gray_queue_enable_redirect (WORKERS_DISTRIBUTE_GRAY_QUEUE);
2652         }
2653
2654 #ifdef SGEN_DEBUG_INTERNAL_ALLOC
2655         main_gc_thread = mono_native_thread_self ();
2656 #endif
2657
2658         if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
2659                 report_registered_roots ();
2660         TV_GETTIME (atv);
2661         time_major_scan_pinned += TV_ELAPSED (btv, atv);
2662
2663         /* registered roots, this includes static fields */
2664         scrrjd_normal = sgen_alloc_internal_dynamic (sizeof (ScanFromRegisteredRootsJobData), INTERNAL_MEM_WORKER_JOB_DATA, TRUE);
2665         scrrjd_normal->copy_or_mark_func = current_object_ops.copy_or_mark_object;
2666         scrrjd_normal->scan_func = current_object_ops.scan_object;
2667         scrrjd_normal->heap_start = heap_start;
2668         scrrjd_normal->heap_end = heap_end;
2669         scrrjd_normal->root_type = ROOT_TYPE_NORMAL;
2670         sgen_workers_enqueue_job (job_scan_from_registered_roots, scrrjd_normal);
2671
2672         scrrjd_wbarrier = sgen_alloc_internal_dynamic (sizeof (ScanFromRegisteredRootsJobData), INTERNAL_MEM_WORKER_JOB_DATA, TRUE);
2673         scrrjd_wbarrier->copy_or_mark_func = current_object_ops.copy_or_mark_object;
2674         scrrjd_wbarrier->scan_func = current_object_ops.scan_object;
2675         scrrjd_wbarrier->heap_start = heap_start;
2676         scrrjd_wbarrier->heap_end = heap_end;
2677         scrrjd_wbarrier->root_type = ROOT_TYPE_WBARRIER;
2678         sgen_workers_enqueue_job (job_scan_from_registered_roots, scrrjd_wbarrier);
2679
2680         TV_GETTIME (btv);
2681         time_major_scan_registered_roots += TV_ELAPSED (atv, btv);
2682
2683         /* Threads */
2684         stdjd = sgen_alloc_internal_dynamic (sizeof (ScanThreadDataJobData), INTERNAL_MEM_WORKER_JOB_DATA, TRUE);
2685         stdjd->heap_start = heap_start;
2686         stdjd->heap_end = heap_end;
2687         sgen_workers_enqueue_job (job_scan_thread_data, stdjd);
2688
2689         TV_GETTIME (atv);
2690         time_major_scan_thread_data += TV_ELAPSED (btv, atv);
2691
2692         TV_GETTIME (btv);
2693         time_major_scan_alloc_pinned += TV_ELAPSED (atv, btv);
2694
2695         if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
2696                 report_finalizer_roots ();
2697
2698         /* scan the list of objects ready for finalization */
2699         sgen_workers_enqueue_job (job_scan_finalizer_entries, fin_ready_list);
2700         sgen_workers_enqueue_job (job_scan_finalizer_entries, critical_fin_list);
2701
2702         if (scan_mod_union) {
2703                 g_assert (finish_up_concurrent_mark);
2704
2705                 /* Mod union card table */
2706                 sgen_workers_enqueue_job (job_scan_major_mod_union_cardtable, NULL);
2707                 sgen_workers_enqueue_job (job_scan_los_mod_union_cardtable, NULL);
2708         }
2709
2710         TV_GETTIME (atv);
2711         time_major_scan_finalized += TV_ELAPSED (btv, atv);
2712         SGEN_LOG (2, "Root scan: %d usecs", TV_ELAPSED (btv, atv));
2713
2714         TV_GETTIME (btv);
2715         time_major_scan_big_objects += TV_ELAPSED (atv, btv);
2716 }
2717
2718 static void
2719 major_finish_copy_or_mark (void)
2720 {
2721         if (!concurrent_collection_in_progress)
2722                 return;
2723
2724         /*
2725          * Prepare the pin queue for the next collection.  Since pinning runs on the worker
2726          * threads we must wait for the jobs to finish before we can reset it.
2727          */
2728         sgen_workers_wait_for_jobs_finished ();
2729         sgen_finish_pinning ();
2730
2731         sgen_pin_stats_reset ();
2732
2733         if (do_concurrent_checks)
2734                 check_nursery_is_clean ();
2735 }
2736
2737 static void
2738 major_start_collection (gboolean concurrent, size_t *old_next_pin_slot)
2739 {
2740         MONO_GC_BEGIN (GENERATION_OLD);
2741         binary_protocol_collection_begin (gc_stats.major_gc_count, GENERATION_OLD);
2742
2743         current_collection_generation = GENERATION_OLD;
2744 #ifndef DISABLE_PERFCOUNTERS
2745         mono_perfcounters->gc_collections1++;
2746 #endif
2747
2748         g_assert (sgen_section_gray_queue_is_empty (sgen_workers_get_distribute_section_gray_queue ()));
2749
2750         sgen_cement_reset ();
2751
2752         if (concurrent) {
2753                 g_assert (major_collector.is_concurrent);
2754                 concurrent_collection_in_progress = TRUE;
2755
2756                 current_object_ops = major_collector.major_concurrent_ops;
2757         } else {
2758                 current_object_ops = major_collector.major_ops;
2759         }
2760
2761         reset_pinned_from_failed_allocation ();
2762
2763         sgen_memgov_major_collection_start ();
2764
2765         //count_ref_nonref_objs ();
2766         //consistency_check ();
2767
2768         check_scan_starts ();
2769
2770         degraded_mode = 0;
2771         SGEN_LOG (1, "Start major collection %d", gc_stats.major_gc_count);
2772         gc_stats.major_gc_count ++;
2773
2774         if (major_collector.start_major_collection)
2775                 major_collector.start_major_collection ();
2776
2777         major_copy_or_mark_from_roots (old_next_pin_slot, concurrent, FALSE, FALSE, FALSE);
2778         major_finish_copy_or_mark ();
2779 }
2780
2781 static void
2782 wait_for_workers_to_finish (void)
2783 {
2784         while (!sgen_workers_all_done ())
2785                 g_usleep (200);
2786 }
2787
2788 static void
2789 major_finish_collection (const char *reason, size_t old_next_pin_slot, gboolean scan_whole_nursery)
2790 {
2791         ScannedObjectCounts counts;
2792         LOSObject *bigobj, *prevbo;
2793         TV_DECLARE (atv);
2794         TV_DECLARE (btv);
2795
2796         TV_GETTIME (btv);
2797
2798         if (concurrent_collection_in_progress) {
2799                 sgen_workers_signal_start_nursery_collection_and_wait ();
2800
2801                 current_object_ops = major_collector.major_concurrent_ops;
2802
2803                 major_copy_or_mark_from_roots (NULL, FALSE, TRUE, TRUE, scan_whole_nursery);
2804
2805                 sgen_workers_signal_finish_nursery_collection ();
2806
2807                 major_finish_copy_or_mark ();
2808                 gray_queue_enable_redirect (WORKERS_DISTRIBUTE_GRAY_QUEUE);
2809
2810                 sgen_workers_join ();
2811
2812                 SGEN_ASSERT (0, sgen_gray_object_queue_is_empty (&gray_queue), "Why is the gray queue not empty after workers have finished working?");
2813
2814 #ifdef SGEN_DEBUG_INTERNAL_ALLOC
2815                 main_gc_thread = NULL;
2816 #endif
2817
2818                 if (do_concurrent_checks)
2819                         check_nursery_is_clean ();
2820         } else {
2821                 SGEN_ASSERT (0, !scan_whole_nursery, "scan_whole_nursery only applies to concurrent collections");
2822                 current_object_ops = major_collector.major_ops;
2823         }
2824
2825         /*
2826          * The workers have stopped so we need to finish gray queue
2827          * work that might result from finalization in the main GC
2828          * thread.  Redirection must therefore be turned off.
2829          */
2830         sgen_gray_object_queue_disable_alloc_prepare (&gray_queue);
2831         g_assert (sgen_section_gray_queue_is_empty (sgen_workers_get_distribute_section_gray_queue ()));
2832
2833         /* all the objects in the heap */
2834         finish_gray_stack (GENERATION_OLD, &gray_queue);
2835         TV_GETTIME (atv);
2836         time_major_finish_gray_stack += TV_ELAPSED (btv, atv);
2837
2838         SGEN_ASSERT (0, sgen_workers_all_done (), "Can't have workers working after joining");
2839
2840         /*
2841          * The (single-threaded) finalization code might have done
2842          * some copying/marking so we can only reset the GC thread's
2843          * worker data here instead of earlier when we joined the
2844          * workers.
2845          */
2846         sgen_workers_reset_data ();
2847
2848         if (objects_pinned) {
2849                 g_assert (!concurrent_collection_in_progress);
2850
2851                 /*
2852                  * This is slow, but we just OOM'd.
2853                  *
2854                  * See comment at `sgen_pin_queue_clear_discarded_entries` for how the pin
2855                  * queue is laid out at this point.
2856                  */
2857                 sgen_pin_queue_clear_discarded_entries (nursery_section, old_next_pin_slot);
2858                 /*
2859                  * We need to reestablish all pinned nursery objects in the pin queue
2860                  * because they're needed for fragment creation.  Unpinning happens by
2861                  * walking the whole queue, so it's not necessary to reestablish where major
2862                  * heap block pins are - all we care is that they're still in there
2863                  * somewhere.
2864                  */
2865                 sgen_optimize_pin_queue ();
2866                 sgen_find_section_pin_queue_start_end (nursery_section);
2867                 objects_pinned = 0;
2868         }
2869
2870         reset_heap_boundaries ();
2871         sgen_update_heap_boundaries ((mword)sgen_get_nursery_start (), (mword)sgen_get_nursery_end ());
2872
2873         if (!concurrent_collection_in_progress) {
2874                 /* walk the pin_queue, build up the fragment list of free memory, unmark
2875                  * pinned objects as we go, memzero() the empty fragments so they are ready for the
2876                  * next allocations.
2877                  */
2878                 if (!sgen_build_nursery_fragments (nursery_section, NULL))
2879                         degraded_mode = 1;
2880
2881                 /* prepare the pin queue for the next collection */
2882                 sgen_finish_pinning ();
2883
2884                 /* Clear TLABs for all threads */
2885                 sgen_clear_tlabs ();
2886
2887                 sgen_pin_stats_reset ();
2888         }
2889
2890         sgen_cement_clear_below_threshold ();
2891
2892         if (check_mark_bits_after_major_collection)
2893                 sgen_check_heap_marked (concurrent_collection_in_progress);
2894
2895         TV_GETTIME (btv);
2896         time_major_fragment_creation += TV_ELAPSED (atv, btv);
2897
2898
2899         MONO_GC_SWEEP_BEGIN (GENERATION_OLD, !major_collector.sweeps_lazily);
2900
2901         /* sweep the big objects list */
2902         prevbo = NULL;
2903         for (bigobj = los_object_list; bigobj;) {
2904                 g_assert (!object_is_pinned (bigobj->data));
2905                 if (sgen_los_object_is_pinned (bigobj->data)) {
2906                         sgen_los_unpin_object (bigobj->data);
2907                         sgen_update_heap_boundaries ((mword)bigobj->data, (mword)bigobj->data + sgen_los_object_size (bigobj));
2908                 } else {
2909                         LOSObject *to_free;
2910                         /* not referenced anywhere, so we can free it */
2911                         if (prevbo)
2912                                 prevbo->next = bigobj->next;
2913                         else
2914                                 los_object_list = bigobj->next;
2915                         to_free = bigobj;
2916                         bigobj = bigobj->next;
2917                         sgen_los_free_object (to_free);
2918                         continue;
2919                 }
2920                 prevbo = bigobj;
2921                 bigobj = bigobj->next;
2922         }
2923
2924         TV_GETTIME (atv);
2925         time_major_free_bigobjs += TV_ELAPSED (btv, atv);
2926
2927         sgen_los_sweep ();
2928
2929         TV_GETTIME (btv);
2930         time_major_los_sweep += TV_ELAPSED (atv, btv);
2931
2932         major_collector.sweep ();
2933
2934         MONO_GC_SWEEP_END (GENERATION_OLD, !major_collector.sweeps_lazily);
2935
2936         TV_GETTIME (atv);
2937         time_major_sweep += TV_ELAPSED (btv, atv);
2938
2939         if (heap_dump_file)
2940                 dump_heap ("major", gc_stats.major_gc_count - 1, reason);
2941
2942         if (fin_ready_list || critical_fin_list) {
2943                 SGEN_LOG (4, "Finalizer-thread wakeup: ready %d", num_ready_finalizers);
2944                 mono_gc_finalize_notify ();
2945         }
2946
2947         g_assert (sgen_gray_object_queue_is_empty (&gray_queue));
2948
2949         sgen_memgov_major_collection_end ();
2950         current_collection_generation = -1;
2951
2952         memset (&counts, 0, sizeof (ScannedObjectCounts));
2953         major_collector.finish_major_collection (&counts);
2954
2955         g_assert (sgen_section_gray_queue_is_empty (sgen_workers_get_distribute_section_gray_queue ()));
2956
2957         SGEN_ASSERT (0, sgen_workers_all_done (), "Can't have workers working after major collection has finished");
2958         if (concurrent_collection_in_progress)
2959                 concurrent_collection_in_progress = FALSE;
2960
2961         check_scan_starts ();
2962
2963         binary_protocol_flush_buffers (FALSE);
2964
2965         //consistency_check ();
2966
2967         MONO_GC_END (GENERATION_OLD);
2968         binary_protocol_collection_end (gc_stats.major_gc_count - 1, GENERATION_OLD, counts.num_scanned_objects, counts.num_unique_scanned_objects);
2969 }
2970
2971 static gboolean
2972 major_do_collection (const char *reason)
2973 {
2974         TV_DECLARE (time_start);
2975         TV_DECLARE (time_end);
2976         size_t old_next_pin_slot;
2977
2978         if (disable_major_collections)
2979                 return FALSE;
2980
2981         if (major_collector.get_and_reset_num_major_objects_marked) {
2982                 long long num_marked = major_collector.get_and_reset_num_major_objects_marked ();
2983                 g_assert (!num_marked);
2984         }
2985
2986         /* world must be stopped already */
2987         TV_GETTIME (time_start);
2988
2989         major_start_collection (FALSE, &old_next_pin_slot);
2990         major_finish_collection (reason, old_next_pin_slot, FALSE);
2991
2992         TV_GETTIME (time_end);
2993         gc_stats.major_gc_time += TV_ELAPSED (time_start, time_end);
2994
2995         /* FIXME: also report this to the user, preferably in gc-end. */
2996         if (major_collector.get_and_reset_num_major_objects_marked)
2997                 major_collector.get_and_reset_num_major_objects_marked ();
2998
2999         return bytes_pinned_from_failed_allocation > 0;
3000 }
3001
3002 static void
3003 major_start_concurrent_collection (const char *reason)
3004 {
3005         TV_DECLARE (time_start);
3006         TV_DECLARE (time_end);
3007         long long num_objects_marked;
3008
3009         if (disable_major_collections)
3010                 return;
3011
3012         TV_GETTIME (time_start);
3013         SGEN_TV_GETTIME (time_major_conc_collection_start);
3014
3015         num_objects_marked = major_collector.get_and_reset_num_major_objects_marked ();
3016         g_assert (num_objects_marked == 0);
3017
3018         MONO_GC_CONCURRENT_START_BEGIN (GENERATION_OLD);
3019         binary_protocol_concurrent_start ();
3020
3021         // FIXME: store reason and pass it when finishing
3022         major_start_collection (TRUE, NULL);
3023
3024         gray_queue_redirect (&gray_queue);
3025
3026         num_objects_marked = major_collector.get_and_reset_num_major_objects_marked ();
3027         MONO_GC_CONCURRENT_START_END (GENERATION_OLD, num_objects_marked);
3028
3029         TV_GETTIME (time_end);
3030         gc_stats.major_gc_time += TV_ELAPSED (time_start, time_end);
3031
3032         current_collection_generation = -1;
3033 }
3034
3035 /*
3036  * Returns whether the major collection has finished.
3037  */
3038 static gboolean
3039 major_should_finish_concurrent_collection (void)
3040 {
3041         SGEN_ASSERT (0, sgen_gray_object_queue_is_empty (&gray_queue), "Why is the gray queue not empty before we have started doing anything?");
3042         return sgen_workers_all_done ();
3043 }
3044
3045 static void
3046 major_update_concurrent_collection (void)
3047 {
3048         TV_DECLARE (total_start);
3049         TV_DECLARE (total_end);
3050
3051         TV_GETTIME (total_start);
3052
3053         MONO_GC_CONCURRENT_UPDATE_FINISH_BEGIN (GENERATION_OLD, major_collector.get_and_reset_num_major_objects_marked ());
3054         binary_protocol_concurrent_update ();
3055
3056         major_collector.update_cardtable_mod_union ();
3057         sgen_los_update_cardtable_mod_union ();
3058
3059         MONO_GC_CONCURRENT_UPDATE_END (GENERATION_OLD, major_collector.get_and_reset_num_major_objects_marked ());
3060
3061         TV_GETTIME (total_end);
3062         gc_stats.major_gc_time += TV_ELAPSED (total_start, total_end);
3063 }
3064
3065 static void
3066 major_finish_concurrent_collection (void)
3067 {
3068         TV_DECLARE (total_start);
3069         TV_DECLARE (total_end);
3070         gboolean late_pinned;
3071         SgenGrayQueue unpin_queue;
3072         memset (&unpin_queue, 0, sizeof (unpin_queue));
3073
3074         TV_GETTIME (total_start);
3075
3076         MONO_GC_CONCURRENT_UPDATE_FINISH_BEGIN (GENERATION_OLD, major_collector.get_and_reset_num_major_objects_marked ());
3077         binary_protocol_concurrent_finish ();
3078
3079         /*
3080          * The major collector can add global remsets which are processed in the finishing
3081          * nursery collection, below.  That implies that the workers must have finished
3082          * marking before the nursery collection is allowed to run, otherwise we might miss
3083          * some remsets.
3084          */
3085         wait_for_workers_to_finish ();
3086
3087         SGEN_TV_GETTIME (time_major_conc_collection_end);
3088         gc_stats.major_gc_time_concurrent += SGEN_TV_ELAPSED (time_major_conc_collection_start, time_major_conc_collection_end);
3089
3090         major_collector.update_cardtable_mod_union ();
3091         sgen_los_update_cardtable_mod_union ();
3092
3093         late_pinned = collect_nursery (&unpin_queue, TRUE);
3094
3095         if (mod_union_consistency_check)
3096                 sgen_check_mod_union_consistency ();
3097
3098         current_collection_generation = GENERATION_OLD;
3099         major_finish_collection ("finishing", -1, late_pinned);
3100
3101         if (whole_heap_check_before_collection)
3102                 sgen_check_whole_heap (FALSE);
3103
3104         unpin_objects_from_queue (&unpin_queue);
3105         sgen_gray_object_queue_deinit (&unpin_queue);
3106
3107         MONO_GC_CONCURRENT_FINISH_END (GENERATION_OLD, major_collector.get_and_reset_num_major_objects_marked ());
3108
3109         TV_GETTIME (total_end);
3110         gc_stats.major_gc_time += TV_ELAPSED (total_start, total_end) - TV_ELAPSED (last_minor_collection_start_tv, last_minor_collection_end_tv);
3111
3112         current_collection_generation = -1;
3113 }
3114
3115 /*
3116  * Ensure an allocation request for @size will succeed by freeing enough memory.
3117  *
3118  * LOCKING: The GC lock MUST be held.
3119  */
3120 void
3121 sgen_ensure_free_space (size_t size)
3122 {
3123         int generation_to_collect = -1;
3124         const char *reason = NULL;
3125
3126
3127         if (size > SGEN_MAX_SMALL_OBJ_SIZE) {
3128                 if (sgen_need_major_collection (size)) {
3129                         reason = "LOS overflow";
3130                         generation_to_collect = GENERATION_OLD;
3131                 }
3132         } else {
3133                 if (degraded_mode) {
3134                         if (sgen_need_major_collection (size)) {
3135                                 reason = "Degraded mode overflow";
3136                                 generation_to_collect = GENERATION_OLD;
3137                         }
3138                 } else if (sgen_need_major_collection (size)) {
3139                         reason = "Minor allowance";
3140                         generation_to_collect = GENERATION_OLD;
3141                 } else {
3142                         generation_to_collect = GENERATION_NURSERY;
3143                         reason = "Nursery full";                        
3144                 }
3145         }
3146
3147         if (generation_to_collect == -1) {
3148                 if (concurrent_collection_in_progress && sgen_workers_all_done ()) {
3149                         generation_to_collect = GENERATION_OLD;
3150                         reason = "Finish concurrent collection";
3151                 }
3152         }
3153
3154         if (generation_to_collect == -1)
3155                 return;
3156         sgen_perform_collection (size, generation_to_collect, reason, FALSE);
3157 }
3158
3159 /*
3160  * LOCKING: Assumes the GC lock is held.
3161  */
3162 void
3163 sgen_perform_collection (size_t requested_size, int generation_to_collect, const char *reason, gboolean wait_to_finish)
3164 {
3165         TV_DECLARE (gc_start);
3166         TV_DECLARE (gc_end);
3167         TV_DECLARE (gc_total_start);
3168         TV_DECLARE (gc_total_end);
3169         GGTimingInfo infos [2];
3170         int overflow_generation_to_collect = -1;
3171         int oldest_generation_collected = generation_to_collect;
3172         const char *overflow_reason = NULL;
3173
3174         MONO_GC_REQUESTED (generation_to_collect, requested_size, wait_to_finish ? 1 : 0);
3175         if (wait_to_finish)
3176                 binary_protocol_collection_force (generation_to_collect);
3177
3178         SGEN_ASSERT (0, generation_to_collect == GENERATION_NURSERY || generation_to_collect == GENERATION_OLD, "What generation is this?");
3179
3180         mono_profiler_gc_event (MONO_GC_EVENT_START, generation_to_collect);
3181
3182         TV_GETTIME (gc_start);
3183
3184         sgen_stop_world (generation_to_collect);
3185
3186         TV_GETTIME (gc_total_start);
3187
3188         if (concurrent_collection_in_progress) {
3189                 /*
3190                  * We update the concurrent collection.  If it finished, we're done.  If
3191                  * not, and we've been asked to do a nursery collection, we do that.
3192                  */
3193                 gboolean finish = major_should_finish_concurrent_collection () || (wait_to_finish && generation_to_collect == GENERATION_OLD);
3194
3195                 if (finish) {
3196                         major_finish_concurrent_collection ();
3197                         oldest_generation_collected = GENERATION_OLD;
3198                 } else {
3199                         sgen_workers_signal_start_nursery_collection_and_wait ();
3200
3201                         major_update_concurrent_collection ();
3202                         if (generation_to_collect == GENERATION_NURSERY)
3203                                 collect_nursery (NULL, FALSE);
3204
3205                         sgen_workers_signal_finish_nursery_collection ();
3206                 }
3207
3208                 goto done;
3209         }
3210
3211         /*
3212          * If we've been asked to do a major collection, and the major collector wants to
3213          * run synchronously (to evacuate), we set the flag to do that.
3214          */
3215         if (generation_to_collect == GENERATION_OLD &&
3216                         allow_synchronous_major &&
3217                         major_collector.want_synchronous_collection &&
3218                         *major_collector.want_synchronous_collection) {
3219                 wait_to_finish = TRUE;
3220         }
3221
3222         SGEN_ASSERT (0, !concurrent_collection_in_progress, "Why did this not get handled above?");
3223
3224         /*
3225          * There's no concurrent collection in progress.  Collect the generation we're asked
3226          * to collect.  If the major collector is concurrent and we're not forced to wait,
3227          * start a concurrent collection.
3228          */
3229         // FIXME: extract overflow reason
3230         if (generation_to_collect == GENERATION_NURSERY) {
3231                 if (collect_nursery (NULL, FALSE)) {
3232                         overflow_generation_to_collect = GENERATION_OLD;
3233                         overflow_reason = "Minor overflow";
3234                 }
3235         } else {
3236                 if (major_collector.is_concurrent && !wait_to_finish) {
3237                         collect_nursery (NULL, FALSE);
3238                         major_start_concurrent_collection (reason);
3239                         // FIXME: set infos[0] properly
3240                         goto done;
3241                 }
3242
3243                 if (major_do_collection (reason)) {
3244                         overflow_generation_to_collect = GENERATION_NURSERY;
3245                         overflow_reason = "Excessive pinning";
3246                 }
3247         }
3248
3249         TV_GETTIME (gc_end);
3250
3251         memset (infos, 0, sizeof (infos));
3252         infos [0].generation = generation_to_collect;
3253         infos [0].reason = reason;
3254         infos [0].is_overflow = FALSE;
3255         infos [1].generation = -1;
3256         infos [0].total_time = SGEN_TV_ELAPSED (gc_start, gc_end);
3257
3258         SGEN_ASSERT (0, !concurrent_collection_in_progress, "Why did this not get handled above?");
3259
3260         if (overflow_generation_to_collect != -1) {
3261                 /*
3262                  * We need to do an overflow collection, either because we ran out of memory
3263                  * or the nursery is fully pinned.
3264                  */
3265
3266                 mono_profiler_gc_event (MONO_GC_EVENT_START, overflow_generation_to_collect);
3267                 infos [1].generation = overflow_generation_to_collect;
3268                 infos [1].reason = overflow_reason;
3269                 infos [1].is_overflow = TRUE;
3270                 infos [1].total_time = gc_end;
3271
3272                 if (overflow_generation_to_collect == GENERATION_NURSERY)
3273                         collect_nursery (NULL, FALSE);
3274                 else
3275                         major_do_collection (overflow_reason);
3276
3277                 TV_GETTIME (gc_end);
3278                 infos [1].total_time = SGEN_TV_ELAPSED (infos [1].total_time, gc_end);
3279
3280                 /* keep events symmetric */
3281                 mono_profiler_gc_event (MONO_GC_EVENT_END, overflow_generation_to_collect);
3282
3283                 oldest_generation_collected = MAX (oldest_generation_collected, overflow_generation_to_collect);
3284         }
3285
3286         SGEN_LOG (2, "Heap size: %lu, LOS size: %lu", (unsigned long)mono_gc_get_heap_size (), (unsigned long)los_memory_usage);
3287
3288         /* this also sets the proper pointers for the next allocation */
3289         if (generation_to_collect == GENERATION_NURSERY && !sgen_can_alloc_size (requested_size)) {
3290                 /* TypeBuilder and MonoMethod are killing mcs with fragmentation */
3291                 SGEN_LOG (1, "nursery collection didn't find enough room for %zd alloc (%zd pinned)", requested_size, sgen_get_pinned_count ());
3292                 sgen_dump_pin_queue ();
3293                 degraded_mode = 1;
3294         }
3295
3296  done:
3297         g_assert (sgen_gray_object_queue_is_empty (&gray_queue));
3298
3299         TV_GETTIME (gc_total_end);
3300         time_max = MAX (time_max, TV_ELAPSED (gc_total_start, gc_total_end));
3301
3302         sgen_restart_world (oldest_generation_collected, infos);
3303
3304         mono_profiler_gc_event (MONO_GC_EVENT_END, generation_to_collect);
3305 }
3306
3307 /*
3308  * ######################################################################
3309  * ########  Memory allocation from the OS
3310  * ######################################################################
3311  * This section of code deals with getting memory from the OS and
3312  * allocating memory for GC-internal data structures.
3313  * Internal memory can be handled with a freelist for small objects.
3314  */
3315
3316 /*
3317  * Debug reporting.
3318  */
3319 G_GNUC_UNUSED static void
3320 report_internal_mem_usage (void)
3321 {
3322         printf ("Internal memory usage:\n");
3323         sgen_report_internal_mem_usage ();
3324         printf ("Pinned memory usage:\n");
3325         major_collector.report_pinned_memory_usage ();
3326 }
3327
3328 /*
3329  * ######################################################################
3330  * ########  Finalization support
3331  * ######################################################################
3332  */
3333
3334 static inline gboolean
3335 sgen_major_is_object_alive (void *object)
3336 {
3337         mword objsize;
3338
3339         /* Oldgen objects can be pinned and forwarded too */
3340         if (SGEN_OBJECT_IS_PINNED (object) || SGEN_OBJECT_IS_FORWARDED (object))
3341                 return TRUE;
3342
3343         /*
3344          * FIXME: major_collector.is_object_live() also calculates the
3345          * size.  Avoid the double calculation.
3346          */
3347         objsize = SGEN_ALIGN_UP (sgen_safe_object_get_size ((MonoObject*)object));
3348         if (objsize > SGEN_MAX_SMALL_OBJ_SIZE)
3349                 return sgen_los_object_is_pinned (object);
3350
3351         return major_collector.is_object_live (object);
3352 }
3353
3354 /*
3355  * If the object has been forwarded it means it's still referenced from a root. 
3356  * If it is pinned it's still alive as well.
3357  * A LOS object is only alive if we have pinned it.
3358  * Return TRUE if @obj is ready to be finalized.
3359  */
3360 static inline gboolean
3361 sgen_is_object_alive (void *object)
3362 {
3363         if (ptr_in_nursery (object))
3364                 return sgen_nursery_is_object_alive (object);
3365
3366         return sgen_major_is_object_alive (object);
3367 }
3368
3369 /*
3370  * This function returns true if @object is either alive or it belongs to the old gen
3371  * and we're currently doing a minor collection.
3372  */
3373 static inline int
3374 sgen_is_object_alive_for_current_gen (char *object)
3375 {
3376         if (ptr_in_nursery (object))
3377                 return sgen_nursery_is_object_alive (object);
3378
3379         if (current_collection_generation == GENERATION_NURSERY)
3380                 return TRUE;
3381
3382         return sgen_major_is_object_alive (object);
3383 }
3384
3385 /*
3386  * This function returns true if @object is either alive and belongs to the
3387  * current collection - major collections are full heap, so old gen objects
3388  * are never alive during a minor collection.
3389  */
3390 static inline int
3391 sgen_is_object_alive_and_on_current_collection (char *object)
3392 {
3393         if (ptr_in_nursery (object))
3394                 return sgen_nursery_is_object_alive (object);
3395
3396         if (current_collection_generation == GENERATION_NURSERY)
3397                 return FALSE;
3398
3399         return sgen_major_is_object_alive (object);
3400 }
3401
3402
3403 gboolean
3404 sgen_gc_is_object_ready_for_finalization (void *object)
3405 {
3406         return !sgen_is_object_alive (object);
3407 }
3408
3409 static gboolean
3410 has_critical_finalizer (MonoObject *obj)
3411 {
3412         MonoClass *class;
3413
3414         if (!mono_defaults.critical_finalizer_object)
3415                 return FALSE;
3416
3417         class = ((MonoVTable*)LOAD_VTABLE (obj))->klass;
3418
3419         return mono_class_has_parent_fast (class, mono_defaults.critical_finalizer_object);
3420 }
3421
3422 static gboolean
3423 is_finalization_aware (MonoObject *obj)
3424 {
3425         MonoVTable *vt = ((MonoVTable*)LOAD_VTABLE (obj));
3426         return (vt->gc_bits & SGEN_GC_BIT_FINALIZER_AWARE) == SGEN_GC_BIT_FINALIZER_AWARE;
3427 }
3428
3429 void
3430 sgen_queue_finalization_entry (MonoObject *obj)
3431 {
3432         FinalizeReadyEntry *entry = sgen_alloc_internal (INTERNAL_MEM_FINALIZE_READY_ENTRY);
3433         gboolean critical = has_critical_finalizer (obj);
3434         entry->object = obj;
3435         if (critical) {
3436                 entry->next = critical_fin_list;
3437                 critical_fin_list = entry;
3438         } else {
3439                 entry->next = fin_ready_list;
3440                 fin_ready_list = entry;
3441         }
3442
3443         if (fin_callbacks.object_queued_for_finalization && is_finalization_aware (obj))
3444                 fin_callbacks.object_queued_for_finalization (obj);
3445
3446 #ifdef ENABLE_DTRACE
3447         if (G_UNLIKELY (MONO_GC_FINALIZE_ENQUEUE_ENABLED ())) {
3448                 int gen = sgen_ptr_in_nursery (obj) ? GENERATION_NURSERY : GENERATION_OLD;
3449                 MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (obj);
3450                 MONO_GC_FINALIZE_ENQUEUE ((mword)obj, sgen_safe_object_get_size (obj),
3451                                 vt->klass->name_space, vt->klass->name, gen, critical);
3452         }
3453 #endif
3454 }
3455
3456 gboolean
3457 sgen_object_is_live (void *obj)
3458 {
3459         return sgen_is_object_alive_and_on_current_collection (obj);
3460 }
3461
3462 /* LOCKING: requires that the GC lock is held */
3463 static void
3464 null_ephemerons_for_domain (MonoDomain *domain)
3465 {
3466         EphemeronLinkNode *current = ephemeron_list, *prev = NULL;
3467
3468         while (current) {
3469                 MonoObject *object = (MonoObject*)current->array;
3470
3471                 if (object)
3472                         SGEN_ASSERT (0, object->vtable, "Can't have objects without vtables.");
3473
3474                 if (object && object->vtable->domain == domain) {
3475                         EphemeronLinkNode *tmp = current;
3476
3477                         if (prev)
3478                                 prev->next = current->next;
3479                         else
3480                                 ephemeron_list = current->next;
3481
3482                         current = current->next;
3483                         sgen_free_internal (tmp, INTERNAL_MEM_EPHEMERON_LINK);
3484                 } else {
3485                         prev = current;
3486                         current = current->next;
3487                 }
3488         }
3489 }
3490
3491 /* LOCKING: requires that the GC lock is held */
3492 static void
3493 clear_unreachable_ephemerons (ScanCopyContext ctx)
3494 {
3495         CopyOrMarkObjectFunc copy_func = ctx.copy_func;
3496         GrayQueue *queue = ctx.queue;
3497         EphemeronLinkNode *current = ephemeron_list, *prev = NULL;
3498         MonoArray *array;
3499         Ephemeron *cur, *array_end;
3500         char *tombstone;
3501
3502         while (current) {
3503                 char *object = current->array;
3504
3505                 if (!sgen_is_object_alive_for_current_gen (object)) {
3506                         EphemeronLinkNode *tmp = current;
3507
3508                         SGEN_LOG (5, "Dead Ephemeron array at %p", object);
3509
3510                         if (prev)
3511                                 prev->next = current->next;
3512                         else
3513                                 ephemeron_list = current->next;
3514
3515                         current = current->next;
3516                         sgen_free_internal (tmp, INTERNAL_MEM_EPHEMERON_LINK);
3517
3518                         continue;
3519                 }
3520
3521                 copy_func ((void**)&object, queue);
3522                 current->array = object;
3523
3524                 SGEN_LOG (5, "Clearing unreachable entries for ephemeron array at %p", object);
3525
3526                 array = (MonoArray*)object;
3527                 cur = mono_array_addr (array, Ephemeron, 0);
3528                 array_end = cur + mono_array_length_fast (array);
3529                 tombstone = (char*)((MonoVTable*)LOAD_VTABLE (object))->domain->ephemeron_tombstone;
3530
3531                 for (; cur < array_end; ++cur) {
3532                         char *key = (char*)cur->key;
3533
3534                         if (!key || key == tombstone)
3535                                 continue;
3536
3537                         SGEN_LOG (5, "[%td] key %p (%s) value %p (%s)", cur - mono_array_addr (array, Ephemeron, 0),
3538                                 key, sgen_is_object_alive_for_current_gen (key) ? "reachable" : "unreachable",
3539                                 cur->value, cur->value && sgen_is_object_alive_for_current_gen (cur->value) ? "reachable" : "unreachable");
3540
3541                         if (!sgen_is_object_alive_for_current_gen (key)) {
3542                                 cur->key = tombstone;
3543                                 cur->value = NULL;
3544                                 continue;
3545                         }
3546                 }
3547                 prev = current;
3548                 current = current->next;
3549         }
3550 }
3551
3552 /*
3553 LOCKING: requires that the GC lock is held
3554
3555 Limitations: We scan all ephemerons on every collection since the current design doesn't allow for a simple nursery/mature split.
3556 */
3557 static int
3558 mark_ephemerons_in_range (ScanCopyContext ctx)
3559 {
3560         CopyOrMarkObjectFunc copy_func = ctx.copy_func;
3561         GrayQueue *queue = ctx.queue;
3562         int nothing_marked = 1;
3563         EphemeronLinkNode *current = ephemeron_list;
3564         MonoArray *array;
3565         Ephemeron *cur, *array_end;
3566         char *tombstone;
3567
3568         for (current = ephemeron_list; current; current = current->next) {
3569                 char *object = current->array;
3570                 SGEN_LOG (5, "Ephemeron array at %p", object);
3571
3572                 /*It has to be alive*/
3573                 if (!sgen_is_object_alive_for_current_gen (object)) {
3574                         SGEN_LOG (5, "\tnot reachable");
3575                         continue;
3576                 }
3577
3578                 copy_func ((void**)&object, queue);
3579
3580                 array = (MonoArray*)object;
3581                 cur = mono_array_addr (array, Ephemeron, 0);
3582                 array_end = cur + mono_array_length_fast (array);
3583                 tombstone = (char*)((MonoVTable*)LOAD_VTABLE (object))->domain->ephemeron_tombstone;
3584
3585                 for (; cur < array_end; ++cur) {
3586                         char *key = cur->key;
3587
3588                         if (!key || key == tombstone)
3589                                 continue;
3590
3591                         SGEN_LOG (5, "[%td] key %p (%s) value %p (%s)", cur - mono_array_addr (array, Ephemeron, 0),
3592                                 key, sgen_is_object_alive_for_current_gen (key) ? "reachable" : "unreachable",
3593                                 cur->value, cur->value && sgen_is_object_alive_for_current_gen (cur->value) ? "reachable" : "unreachable");
3594
3595                         if (sgen_is_object_alive_for_current_gen (key)) {
3596                                 char *value = cur->value;
3597
3598                                 copy_func ((void**)&cur->key, queue);
3599                                 if (value) {
3600                                         if (!sgen_is_object_alive_for_current_gen (value))
3601                                                 nothing_marked = 0;
3602                                         copy_func ((void**)&cur->value, queue);
3603                                 }
3604                         }
3605                 }
3606         }
3607
3608         SGEN_LOG (5, "Ephemeron run finished. Is it done %d", nothing_marked);
3609         return nothing_marked;
3610 }
3611
3612 int
3613 mono_gc_invoke_finalizers (void)
3614 {
3615         FinalizeReadyEntry *entry = NULL;
3616         gboolean entry_is_critical = FALSE;
3617         int count = 0;
3618         void *obj;
3619         /* FIXME: batch to reduce lock contention */
3620         while (fin_ready_list || critical_fin_list) {
3621                 LOCK_GC;
3622
3623                 if (entry) {
3624                         FinalizeReadyEntry **list = entry_is_critical ? &critical_fin_list : &fin_ready_list;
3625
3626                         /* We have finalized entry in the last
3627                            interation, now we need to remove it from
3628                            the list. */
3629                         if (*list == entry)
3630                                 *list = entry->next;
3631                         else {
3632                                 FinalizeReadyEntry *e = *list;
3633                                 while (e->next != entry)
3634                                         e = e->next;
3635                                 e->next = entry->next;
3636                         }
3637                         sgen_free_internal (entry, INTERNAL_MEM_FINALIZE_READY_ENTRY);
3638                         entry = NULL;
3639                 }
3640
3641                 /* Now look for the first non-null entry. */
3642                 for (entry = fin_ready_list; entry && !entry->object; entry = entry->next)
3643                         ;
3644                 if (entry) {
3645                         entry_is_critical = FALSE;
3646                 } else {
3647                         entry_is_critical = TRUE;
3648                         for (entry = critical_fin_list; entry && !entry->object; entry = entry->next)
3649                                 ;
3650                 }
3651
3652                 if (entry) {
3653                         g_assert (entry->object);
3654                         num_ready_finalizers--;
3655                         obj = entry->object;
3656                         entry->object = NULL;
3657                         SGEN_LOG (7, "Finalizing object %p (%s)", obj, safe_name (obj));
3658                 }
3659
3660                 UNLOCK_GC;
3661
3662                 if (!entry)
3663                         break;
3664
3665                 g_assert (entry->object == NULL);
3666                 count++;
3667                 /* the object is on the stack so it is pinned */
3668                 /*g_print ("Calling finalizer for object: %p (%s)\n", entry->object, safe_name (entry->object));*/
3669                 mono_gc_run_finalize (obj, NULL);
3670         }
3671         g_assert (!entry);
3672         return count;
3673 }
3674
3675 gboolean
3676 mono_gc_pending_finalizers (void)
3677 {
3678         return fin_ready_list || critical_fin_list;
3679 }
3680
3681 /*
3682  * ######################################################################
3683  * ########  registered roots support
3684  * ######################################################################
3685  */
3686
3687 /*
3688  * We do not coalesce roots.
3689  */
3690 static int
3691 mono_gc_register_root_inner (char *start, size_t size, void *descr, int root_type)
3692 {
3693         RootRecord new_root;
3694         int i;
3695         LOCK_GC;
3696         for (i = 0; i < ROOT_TYPE_NUM; ++i) {
3697                 RootRecord *root = sgen_hash_table_lookup (&roots_hash [i], start);
3698                 /* we allow changing the size and the descriptor (for thread statics etc) */
3699                 if (root) {
3700                         size_t old_size = root->end_root - start;
3701                         root->end_root = start + size;
3702                         g_assert (((root->root_desc != 0) && (descr != NULL)) ||
3703                                           ((root->root_desc == 0) && (descr == NULL)));
3704                         root->root_desc = (mword)descr;
3705                         roots_size += size;
3706                         roots_size -= old_size;
3707                         UNLOCK_GC;
3708                         return TRUE;
3709                 }
3710         }
3711
3712         new_root.end_root = start + size;
3713         new_root.root_desc = (mword)descr;
3714
3715         sgen_hash_table_replace (&roots_hash [root_type], start, &new_root, NULL);
3716         roots_size += size;
3717
3718         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);
3719
3720         UNLOCK_GC;
3721         return TRUE;
3722 }
3723
3724 int
3725 mono_gc_register_root (char *start, size_t size, void *descr)
3726 {
3727         return mono_gc_register_root_inner (start, size, descr, descr ? ROOT_TYPE_NORMAL : ROOT_TYPE_PINNED);
3728 }
3729
3730 int
3731 mono_gc_register_root_wbarrier (char *start, size_t size, void *descr)
3732 {
3733         return mono_gc_register_root_inner (start, size, descr, ROOT_TYPE_WBARRIER);
3734 }
3735
3736 void
3737 mono_gc_deregister_root (char* addr)
3738 {
3739         int root_type;
3740         RootRecord root;
3741
3742         LOCK_GC;
3743         for (root_type = 0; root_type < ROOT_TYPE_NUM; ++root_type) {
3744                 if (sgen_hash_table_remove (&roots_hash [root_type], addr, &root))
3745                         roots_size -= (root.end_root - addr);
3746         }
3747         UNLOCK_GC;
3748 }
3749
3750 /*
3751  * ######################################################################
3752  * ########  Thread handling (stop/start code)
3753  * ######################################################################
3754  */
3755
3756 unsigned int sgen_global_stop_count = 0;
3757
3758 int
3759 sgen_get_current_collection_generation (void)
3760 {
3761         return current_collection_generation;
3762 }
3763
3764 void
3765 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
3766 {
3767         gc_callbacks = *callbacks;
3768 }
3769
3770 MonoGCCallbacks *
3771 mono_gc_get_gc_callbacks ()
3772 {
3773         return &gc_callbacks;
3774 }
3775
3776 /* Variables holding start/end nursery so it won't have to be passed at every call */
3777 static void *scan_area_arg_start, *scan_area_arg_end;
3778
3779 void
3780 mono_gc_conservatively_scan_area (void *start, void *end)
3781 {
3782         conservatively_pin_objects_from (start, end, scan_area_arg_start, scan_area_arg_end, PIN_TYPE_STACK);
3783 }
3784
3785 void*
3786 mono_gc_scan_object (void *obj, void *gc_data)
3787 {
3788         UserCopyOrMarkData *data = gc_data;
3789         current_object_ops.copy_or_mark_object (&obj, data->queue);
3790         return obj;
3791 }
3792
3793 /*
3794  * Mark from thread stacks and registers.
3795  */
3796 static void
3797 scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise, GrayQueue *queue)
3798 {
3799         SgenThreadInfo *info;
3800
3801         scan_area_arg_start = start_nursery;
3802         scan_area_arg_end = end_nursery;
3803
3804         FOREACH_THREAD (info) {
3805                 if (info->skip) {
3806                         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);
3807                         continue;
3808                 }
3809                 if (info->gc_disabled) {
3810                         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);
3811                         continue;
3812                 }
3813                 if (!mono_thread_info_is_live (info)) {
3814                         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);
3815                         continue;
3816                 }
3817                 g_assert (info->suspend_done);
3818                 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 ());
3819                 if (gc_callbacks.thread_mark_func && !conservative_stack_mark) {
3820                         UserCopyOrMarkData data = { NULL, queue };
3821                         gc_callbacks.thread_mark_func (info->runtime_data, info->stack_start, info->stack_end, precise, &data);
3822                 } else if (!precise) {
3823                         if (!conservative_stack_mark) {
3824                                 fprintf (stderr, "Precise stack mark not supported - disabling.\n");
3825                                 conservative_stack_mark = TRUE;
3826                         }
3827                         conservatively_pin_objects_from (info->stack_start, info->stack_end, start_nursery, end_nursery, PIN_TYPE_STACK);
3828                 }
3829
3830                 if (!precise) {
3831 #ifdef USE_MONO_CTX
3832                         conservatively_pin_objects_from ((void**)&info->ctx, (void**)&info->ctx + ARCH_NUM_REGS,
3833                                 start_nursery, end_nursery, PIN_TYPE_STACK);
3834 #else
3835                         conservatively_pin_objects_from ((void**)&info->regs, (void**)&info->regs + ARCH_NUM_REGS,
3836                                         start_nursery, end_nursery, PIN_TYPE_STACK);
3837 #endif
3838                 }
3839         } END_FOREACH_THREAD
3840 }
3841
3842 static gboolean
3843 ptr_on_stack (void *ptr)
3844 {
3845         gpointer stack_start = &stack_start;
3846         SgenThreadInfo *info = mono_thread_info_current ();
3847
3848         if (ptr >= stack_start && ptr < (gpointer)info->stack_end)
3849                 return TRUE;
3850         return FALSE;
3851 }
3852
3853 static void*
3854 sgen_thread_register (SgenThreadInfo* info, void *addr)
3855 {
3856         size_t stsize = 0;
3857         guint8 *staddr = NULL;
3858
3859 #ifndef HAVE_KW_THREAD
3860         info->tlab_start = info->tlab_next = info->tlab_temp_end = info->tlab_real_end = NULL;
3861
3862         g_assert (!mono_native_tls_get_value (thread_info_key));
3863         mono_native_tls_set_value (thread_info_key, info);
3864 #else
3865         sgen_thread_info = info;
3866 #endif
3867
3868 #ifdef SGEN_POSIX_STW
3869         info->stop_count = -1;
3870         info->signal = 0;
3871 #endif
3872         info->skip = 0;
3873         info->stack_start = NULL;
3874         info->stopped_ip = NULL;
3875         info->stopped_domain = NULL;
3876 #ifdef USE_MONO_CTX
3877         memset (&info->ctx, 0, sizeof (MonoContext));
3878 #else
3879         memset (&info->regs, 0, sizeof (info->regs));
3880 #endif
3881
3882         sgen_init_tlab_info (info);
3883
3884         binary_protocol_thread_register ((gpointer)mono_thread_info_get_tid (info));
3885
3886         /* On win32, stack_start_limit should be 0, since the stack can grow dynamically */
3887         mono_thread_info_get_stack_bounds (&staddr, &stsize);
3888         if (staddr) {
3889 #ifndef HOST_WIN32
3890                 info->stack_start_limit = staddr;
3891 #endif
3892                 info->stack_end = staddr + stsize;
3893         } else {
3894                 gsize stack_bottom = (gsize)addr;
3895                 stack_bottom += 4095;
3896                 stack_bottom &= ~4095;
3897                 info->stack_end = (char*)stack_bottom;
3898         }
3899
3900 #ifdef HAVE_KW_THREAD
3901         stack_end = info->stack_end;
3902 #endif
3903
3904         SGEN_LOG (3, "registered thread %p (%p) stack end %p", info, (gpointer)mono_thread_info_get_tid (info), info->stack_end);
3905
3906         if (gc_callbacks.thread_attach_func)
3907                 info->runtime_data = gc_callbacks.thread_attach_func ();
3908         return info;
3909 }
3910
3911 static void
3912 sgen_thread_detach (SgenThreadInfo *p)
3913 {
3914         /* If a delegate is passed to native code and invoked on a thread we dont
3915          * know about, the jit will register it with mono_jit_thread_attach, but
3916          * we have no way of knowing when that thread goes away.  SGen has a TSD
3917          * so we assume that if the domain is still registered, we can detach
3918          * the thread
3919          */
3920         if (mono_domain_get ())
3921                 mono_thread_detach_internal (mono_thread_internal_current ());
3922 }
3923
3924 static void
3925 sgen_thread_unregister (SgenThreadInfo *p)
3926 {
3927         MonoNativeThreadId tid;
3928
3929         tid = mono_thread_info_get_tid (p);
3930         binary_protocol_thread_unregister ((gpointer)tid);
3931         SGEN_LOG (3, "unregister thread %p (%p)", p, (gpointer)tid);
3932
3933 #ifndef HAVE_KW_THREAD
3934         mono_native_tls_set_value (thread_info_key, NULL);
3935 #else
3936         sgen_thread_info = NULL;
3937 #endif
3938
3939         if (p->info.runtime_thread)
3940                 mono_threads_add_joinable_thread ((gpointer)tid);
3941
3942         if (gc_callbacks.thread_detach_func) {
3943                 gc_callbacks.thread_detach_func (p->runtime_data);
3944                 p->runtime_data = NULL;
3945         }
3946 }
3947
3948
3949 static void
3950 sgen_thread_attach (SgenThreadInfo *info)
3951 {
3952         LOCK_GC;
3953         /*this is odd, can we get attached before the gc is inited?*/
3954         init_stats ();
3955         UNLOCK_GC;
3956         
3957         if (gc_callbacks.thread_attach_func && !info->runtime_data)
3958                 info->runtime_data = gc_callbacks.thread_attach_func ();
3959 }
3960 gboolean
3961 mono_gc_register_thread (void *baseptr)
3962 {
3963         return mono_thread_info_attach (baseptr) != NULL;
3964 }
3965
3966 /*
3967  * mono_gc_set_stack_end:
3968  *
3969  *   Set the end of the current threads stack to STACK_END. The stack space between 
3970  * STACK_END and the real end of the threads stack will not be scanned during collections.
3971  */
3972 void
3973 mono_gc_set_stack_end (void *stack_end)
3974 {
3975         SgenThreadInfo *info;
3976
3977         LOCK_GC;
3978         info = mono_thread_info_current ();
3979         if (info) {
3980                 g_assert (stack_end < info->stack_end);
3981                 info->stack_end = stack_end;
3982         }
3983         UNLOCK_GC;
3984 }
3985
3986 #if USE_PTHREAD_INTERCEPT
3987
3988
3989 int
3990 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
3991 {
3992         return pthread_create (new_thread, attr, start_routine, arg);
3993 }
3994
3995 int
3996 mono_gc_pthread_join (pthread_t thread, void **retval)
3997 {
3998         return pthread_join (thread, retval);
3999 }
4000
4001 int
4002 mono_gc_pthread_detach (pthread_t thread)
4003 {
4004         return pthread_detach (thread);
4005 }
4006
4007 void
4008 mono_gc_pthread_exit (void *retval) 
4009 {
4010         mono_thread_info_detach ();
4011         pthread_exit (retval);
4012         g_assert_not_reached ();
4013 }
4014
4015 #endif /* USE_PTHREAD_INTERCEPT */
4016
4017 /*
4018  * ######################################################################
4019  * ########  Write barriers
4020  * ######################################################################
4021  */
4022
4023 /*
4024  * Note: the write barriers first do the needed GC work and then do the actual store:
4025  * this way the value is visible to the conservative GC scan after the write barrier
4026  * itself. If a GC interrupts the barrier in the middle, value will be kept alive by
4027  * the conservative scan, otherwise by the remembered set scan.
4028  */
4029 void
4030 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
4031 {
4032         HEAVY_STAT (++stat_wbarrier_set_field);
4033         if (ptr_in_nursery (field_ptr)) {
4034                 *(void**)field_ptr = value;
4035                 return;
4036         }
4037         SGEN_LOG (8, "Adding remset at %p", field_ptr);
4038         if (value)
4039                 binary_protocol_wbarrier (field_ptr, value, value->vtable);
4040
4041         remset.wbarrier_set_field (obj, field_ptr, value);
4042 }
4043
4044 void
4045 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
4046 {
4047         HEAVY_STAT (++stat_wbarrier_set_arrayref);
4048         if (ptr_in_nursery (slot_ptr)) {
4049                 *(void**)slot_ptr = value;
4050                 return;
4051         }
4052         SGEN_LOG (8, "Adding remset at %p", slot_ptr);
4053         if (value)
4054                 binary_protocol_wbarrier (slot_ptr, value, value->vtable);
4055
4056         remset.wbarrier_set_arrayref (arr, slot_ptr, value);
4057 }
4058
4059 void
4060 mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
4061 {
4062         HEAVY_STAT (++stat_wbarrier_arrayref_copy);
4063         /*This check can be done without taking a lock since dest_ptr array is pinned*/
4064         if (ptr_in_nursery (dest_ptr) || count <= 0) {
4065                 mono_gc_memmove_aligned (dest_ptr, src_ptr, count * sizeof (gpointer));
4066                 return;
4067         }
4068
4069 #ifdef SGEN_HEAVY_BINARY_PROTOCOL
4070         if (binary_protocol_is_heavy_enabled ()) {
4071                 int i;
4072                 for (i = 0; i < count; ++i) {
4073                         gpointer dest = (gpointer*)dest_ptr + i;
4074                         gpointer obj = *((gpointer*)src_ptr + i);
4075                         if (obj)
4076                                 binary_protocol_wbarrier (dest, obj, (gpointer)LOAD_VTABLE (obj));
4077                 }
4078         }
4079 #endif
4080
4081         remset.wbarrier_arrayref_copy (dest_ptr, src_ptr, count);
4082 }
4083
4084 void
4085 mono_gc_wbarrier_generic_nostore (gpointer ptr)
4086 {
4087         gpointer obj;
4088
4089         HEAVY_STAT (++stat_wbarrier_generic_store);
4090
4091 #ifdef XDOMAIN_CHECKS_IN_WBARRIER
4092         /* FIXME: ptr_in_heap must be called with the GC lock held */
4093         if (xdomain_checks && *(MonoObject**)ptr && ptr_in_heap (ptr)) {
4094                 char *start = sgen_find_object_for_ptr (ptr);
4095                 MonoObject *value = *(MonoObject**)ptr;
4096                 LOCK_GC;
4097                 g_assert (start);
4098                 if (start) {
4099                         MonoObject *obj = (MonoObject*)start;
4100                         if (obj->vtable->domain != value->vtable->domain)
4101                                 g_assert (is_xdomain_ref_allowed (ptr, start, obj->vtable->domain));
4102                 }
4103                 UNLOCK_GC;
4104         }
4105 #endif
4106
4107         obj = *(gpointer*)ptr;
4108         if (obj)
4109                 binary_protocol_wbarrier (ptr, obj, (gpointer)LOAD_VTABLE (obj));
4110
4111         if (ptr_in_nursery (ptr) || ptr_on_stack (ptr)) {
4112                 SGEN_LOG (8, "Skipping remset at %p", ptr);
4113                 return;
4114         }
4115
4116         /*
4117          * We need to record old->old pointer locations for the
4118          * concurrent collector.
4119          */
4120         if (!ptr_in_nursery (obj) && !concurrent_collection_in_progress) {
4121                 SGEN_LOG (8, "Skipping remset at %p", ptr);
4122                 return;
4123         }
4124
4125         SGEN_LOG (8, "Adding remset at %p", ptr);
4126
4127         remset.wbarrier_generic_nostore (ptr);
4128 }
4129
4130 void
4131 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
4132 {
4133         SGEN_LOG (8, "Wbarrier store at %p to %p (%s)", ptr, value, value ? safe_name (value) : "null");
4134         SGEN_UPDATE_REFERENCE_ALLOW_NULL (ptr, value);
4135         if (ptr_in_nursery (value))
4136                 mono_gc_wbarrier_generic_nostore (ptr);
4137         sgen_dummy_use (value);
4138 }
4139
4140 /* Same as mono_gc_wbarrier_generic_store () but performs the store
4141  * as an atomic operation with release semantics.
4142  */
4143 void
4144 mono_gc_wbarrier_generic_store_atomic (gpointer ptr, MonoObject *value)
4145 {
4146         HEAVY_STAT (++stat_wbarrier_generic_store_atomic);
4147
4148         SGEN_LOG (8, "Wbarrier atomic store at %p to %p (%s)", ptr, value, value ? safe_name (value) : "null");
4149
4150         InterlockedWritePointer (ptr, value);
4151
4152         if (ptr_in_nursery (value))
4153                 mono_gc_wbarrier_generic_nostore (ptr);
4154
4155         sgen_dummy_use (value);
4156 }
4157
4158 void mono_gc_wbarrier_value_copy_bitmap (gpointer _dest, gpointer _src, int size, unsigned bitmap)
4159 {
4160         mword *dest = _dest;
4161         mword *src = _src;
4162
4163         while (size) {
4164                 if (bitmap & 0x1)
4165                         mono_gc_wbarrier_generic_store (dest, (MonoObject*)*src);
4166                 else
4167                         SGEN_UPDATE_REFERENCE_ALLOW_NULL (dest, *src);
4168                 ++src;
4169                 ++dest;
4170                 size -= SIZEOF_VOID_P;
4171                 bitmap >>= 1;
4172         }
4173 }
4174
4175 #ifdef SGEN_HEAVY_BINARY_PROTOCOL
4176 #undef HANDLE_PTR
4177 #define HANDLE_PTR(ptr,obj) do {                                        \
4178                 gpointer o = *(gpointer*)(ptr);                         \
4179                 if ((o)) {                                              \
4180                         gpointer d = ((char*)dest) + ((char*)(ptr) - (char*)(obj)); \
4181                         binary_protocol_wbarrier (d, o, (gpointer) LOAD_VTABLE (o)); \
4182                 }                                                       \
4183         } while (0)
4184
4185 static void
4186 scan_object_for_binary_protocol_copy_wbarrier (gpointer dest, char *start, mword desc)
4187 {
4188 #define SCAN_OBJECT_NOVTABLE
4189 #include "sgen-scan-object.h"
4190 }
4191 #endif
4192
4193 void
4194 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
4195 {
4196         HEAVY_STAT (++stat_wbarrier_value_copy);
4197         g_assert (klass->valuetype);
4198
4199         SGEN_LOG (8, "Adding value remset at %p, count %d, descr %p for class %s (%p)", dest, count, klass->gc_descr, klass->name, klass);
4200
4201         if (ptr_in_nursery (dest) || ptr_on_stack (dest) || !SGEN_CLASS_HAS_REFERENCES (klass)) {
4202                 size_t element_size = mono_class_value_size (klass, NULL);
4203                 size_t size = count * element_size;
4204                 mono_gc_memmove_atomic (dest, src, size);               
4205                 return;
4206         }
4207
4208 #ifdef SGEN_HEAVY_BINARY_PROTOCOL
4209         if (binary_protocol_is_heavy_enabled ()) {
4210                 size_t element_size = mono_class_value_size (klass, NULL);
4211                 int i;
4212                 for (i = 0; i < count; ++i) {
4213                         scan_object_for_binary_protocol_copy_wbarrier ((char*)dest + i * element_size,
4214                                         (char*)src + i * element_size - sizeof (MonoObject),
4215                                         (mword) klass->gc_descr);
4216                 }
4217         }
4218 #endif
4219
4220         remset.wbarrier_value_copy (dest, src, count, klass);
4221 }
4222
4223 /**
4224  * mono_gc_wbarrier_object_copy:
4225  *
4226  * Write barrier to call when obj is the result of a clone or copy of an object.
4227  */
4228 void
4229 mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
4230 {
4231         int size;
4232
4233         HEAVY_STAT (++stat_wbarrier_object_copy);
4234
4235         if (ptr_in_nursery (obj) || ptr_on_stack (obj)) {
4236                 size = mono_object_class (obj)->instance_size;
4237                 mono_gc_memmove_aligned ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
4238                                 size - sizeof (MonoObject));
4239                 return; 
4240         }
4241
4242 #ifdef SGEN_HEAVY_BINARY_PROTOCOL
4243         if (binary_protocol_is_heavy_enabled ())
4244                 scan_object_for_binary_protocol_copy_wbarrier (obj, (char*)src, (mword) src->vtable->gc_descr);
4245 #endif
4246
4247         remset.wbarrier_object_copy (obj, src);
4248 }
4249
4250
4251 /*
4252  * ######################################################################
4253  * ########  Other mono public interface functions.
4254  * ######################################################################
4255  */
4256
4257 #define REFS_SIZE 128
4258 typedef struct {
4259         void *data;
4260         MonoGCReferences callback;
4261         int flags;
4262         int count;
4263         int called;
4264         MonoObject *refs [REFS_SIZE];
4265         uintptr_t offsets [REFS_SIZE];
4266 } HeapWalkInfo;
4267
4268 #undef HANDLE_PTR
4269 #define HANDLE_PTR(ptr,obj)     do {    \
4270                 if (*(ptr)) {   \
4271                         if (hwi->count == REFS_SIZE) {  \
4272                                 hwi->callback ((MonoObject*)start, mono_object_class (start), hwi->called? 0: size, hwi->count, hwi->refs, hwi->offsets, hwi->data);    \
4273                                 hwi->count = 0; \
4274                                 hwi->called = 1;        \
4275                         }       \
4276                         hwi->offsets [hwi->count] = (char*)(ptr)-(char*)start;  \
4277                         hwi->refs [hwi->count++] = *(ptr);      \
4278                 }       \
4279         } while (0)
4280
4281 static void
4282 collect_references (HeapWalkInfo *hwi, char *start, size_t size)
4283 {
4284         mword desc = sgen_obj_get_descriptor (start);
4285
4286 #include "sgen-scan-object.h"
4287 }
4288
4289 static void
4290 walk_references (char *start, size_t size, void *data)
4291 {
4292         HeapWalkInfo *hwi = data;
4293         hwi->called = 0;
4294         hwi->count = 0;
4295         collect_references (hwi, start, size);
4296         if (hwi->count || !hwi->called)
4297                 hwi->callback ((MonoObject*)start, mono_object_class (start), hwi->called? 0: size, hwi->count, hwi->refs, hwi->offsets, hwi->data);
4298 }
4299
4300 /**
4301  * mono_gc_walk_heap:
4302  * @flags: flags for future use
4303  * @callback: a function pointer called for each object in the heap
4304  * @data: a user data pointer that is passed to callback
4305  *
4306  * This function can be used to iterate over all the live objects in the heap:
4307  * for each object, @callback is invoked, providing info about the object's
4308  * location in memory, its class, its size and the objects it references.
4309  * For each referenced object it's offset from the object address is
4310  * reported in the offsets array.
4311  * The object references may be buffered, so the callback may be invoked
4312  * multiple times for the same object: in all but the first call, the size
4313  * argument will be zero.
4314  * Note that this function can be only called in the #MONO_GC_EVENT_PRE_START_WORLD
4315  * profiler event handler.
4316  *
4317  * Returns: a non-zero value if the GC doesn't support heap walking
4318  */
4319 int
4320 mono_gc_walk_heap (int flags, MonoGCReferences callback, void *data)
4321 {
4322         HeapWalkInfo hwi;
4323
4324         hwi.flags = flags;
4325         hwi.callback = callback;
4326         hwi.data = data;
4327
4328         sgen_clear_nursery_fragments ();
4329         sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data, walk_references, &hwi, FALSE);
4330
4331         major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, walk_references, &hwi);
4332         sgen_los_iterate_objects (walk_references, &hwi);
4333
4334         return 0;
4335 }
4336
4337 void
4338 mono_gc_collect (int generation)
4339 {
4340         LOCK_GC;
4341         if (generation > 1)
4342                 generation = 1;
4343         sgen_perform_collection (0, generation, "user request", TRUE);
4344         UNLOCK_GC;
4345 }
4346
4347 int
4348 mono_gc_max_generation (void)
4349 {
4350         return 1;
4351 }
4352
4353 int
4354 mono_gc_collection_count (int generation)
4355 {
4356         if (generation == 0)
4357                 return gc_stats.minor_gc_count;
4358         return gc_stats.major_gc_count;
4359 }
4360
4361 int64_t
4362 mono_gc_get_used_size (void)
4363 {
4364         gint64 tot = 0;
4365         LOCK_GC;
4366         tot = los_memory_usage;
4367         tot += nursery_section->next_data - nursery_section->data;
4368         tot += major_collector.get_used_size ();
4369         /* FIXME: account for pinned objects */
4370         UNLOCK_GC;
4371         return tot;
4372 }
4373
4374 int
4375 mono_gc_get_los_limit (void)
4376 {
4377         return MAX_SMALL_OBJ_SIZE;
4378 }
4379
4380 void
4381 mono_gc_set_string_length (MonoString *str, gint32 new_length)
4382 {
4383         mono_unichar2 *new_end = str->chars + new_length;
4384         
4385         /* zero the discarded string. This null-delimits the string and allows 
4386          * the space to be reclaimed by SGen. */
4387          
4388         if (nursery_canaries_enabled () && sgen_ptr_in_nursery (str)) {
4389                 CHECK_CANARY_FOR_OBJECT (str);
4390                 memset (new_end, 0, (str->length - new_length + 1) * sizeof (mono_unichar2) + CANARY_SIZE);
4391                 memcpy (new_end + 1 , CANARY_STRING, CANARY_SIZE);
4392         } else {
4393                 memset (new_end, 0, (str->length - new_length + 1) * sizeof (mono_unichar2));
4394         }
4395         
4396         str->length = new_length;
4397 }
4398
4399 gboolean
4400 mono_gc_user_markers_supported (void)
4401 {
4402         return TRUE;
4403 }
4404
4405 gboolean
4406 mono_object_is_alive (MonoObject* o)
4407 {
4408         return TRUE;
4409 }
4410
4411 int
4412 mono_gc_get_generation (MonoObject *obj)
4413 {
4414         if (ptr_in_nursery (obj))
4415                 return 0;
4416         return 1;
4417 }
4418
4419 void
4420 mono_gc_enable_events (void)
4421 {
4422 }
4423
4424 void
4425 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
4426 {
4427         sgen_register_disappearing_link (obj, link_addr, track, FALSE);
4428 }
4429
4430 void
4431 mono_gc_weak_link_remove (void **link_addr, gboolean track)
4432 {
4433         sgen_register_disappearing_link (NULL, link_addr, track, FALSE);
4434 }
4435
4436 MonoObject*
4437 mono_gc_weak_link_get (void **link_addr)
4438 {
4439         void * volatile *link_addr_volatile;
4440         void *ptr;
4441         MonoObject *obj;
4442  retry:
4443         link_addr_volatile = link_addr;
4444         ptr = (void*)*link_addr_volatile;
4445         /*
4446          * At this point we have a hidden pointer.  If the GC runs
4447          * here, it will not recognize the hidden pointer as a
4448          * reference, and if the object behind it is not referenced
4449          * elsewhere, it will be freed.  Once the world is restarted
4450          * we reveal the pointer, giving us a pointer to a freed
4451          * object.  To make sure we don't return it, we load the
4452          * hidden pointer again.  If it's still the same, we can be
4453          * sure the object reference is valid.
4454          */
4455         if (ptr)
4456                 obj = (MonoObject*) REVEAL_POINTER (ptr);
4457         else
4458                 return NULL;
4459
4460         mono_memory_barrier ();
4461
4462         /*
4463          * During the second bridge processing step the world is
4464          * running again.  That step processes all weak links once
4465          * more to null those that refer to dead objects.  Before that
4466          * is completed, those links must not be followed, so we
4467          * conservatively wait for bridge processing when any weak
4468          * link is dereferenced.
4469          */
4470         if (G_UNLIKELY (bridge_processing_in_progress))
4471                 mono_gc_wait_for_bridge_processing ();
4472
4473         if ((void*)*link_addr_volatile != ptr)
4474                 goto retry;
4475
4476         return obj;
4477 }
4478
4479 gboolean
4480 mono_gc_ephemeron_array_add (MonoObject *obj)
4481 {
4482         EphemeronLinkNode *node;
4483
4484         LOCK_GC;
4485
4486         node = sgen_alloc_internal (INTERNAL_MEM_EPHEMERON_LINK);
4487         if (!node) {
4488                 UNLOCK_GC;
4489                 return FALSE;
4490         }
4491         node->array = (char*)obj;
4492         node->next = ephemeron_list;
4493         ephemeron_list = node;
4494
4495         SGEN_LOG (5, "Registered ephemeron array %p", obj);
4496
4497         UNLOCK_GC;
4498         return TRUE;
4499 }
4500
4501 gboolean
4502 mono_gc_set_allow_synchronous_major (gboolean flag)
4503 {
4504         if (!major_collector.is_concurrent)
4505                 return flag;
4506
4507         allow_synchronous_major = flag;
4508         return TRUE;
4509 }
4510
4511 void*
4512 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
4513 {
4514         void *result;
4515         LOCK_INTERRUPTION;
4516         result = func (data);
4517         UNLOCK_INTERRUPTION;
4518         return result;
4519 }
4520
4521 gboolean
4522 mono_gc_is_gc_thread (void)
4523 {
4524         gboolean result;
4525         LOCK_GC;
4526         result = mono_thread_info_current () != NULL;
4527         UNLOCK_GC;
4528         return result;
4529 }
4530
4531 static gboolean
4532 is_critical_method (MonoMethod *method)
4533 {
4534         return mono_runtime_is_critical_method (method) || sgen_is_critical_method (method);
4535 }
4536
4537 void
4538 sgen_env_var_error (const char *env_var, const char *fallback, const char *description_format, ...)
4539 {
4540         va_list ap;
4541
4542         va_start (ap, description_format);
4543
4544         fprintf (stderr, "Warning: In environment variable `%s': ", env_var);
4545         vfprintf (stderr, description_format, ap);
4546         if (fallback)
4547                 fprintf (stderr, " - %s", fallback);
4548         fprintf (stderr, "\n");
4549
4550         va_end (ap);
4551 }
4552
4553 static gboolean
4554 parse_double_in_interval (const char *env_var, const char *opt_name, const char *opt, double min, double max, double *result)
4555 {
4556         char *endptr;
4557         double val = strtod (opt, &endptr);
4558         if (endptr == opt) {
4559                 sgen_env_var_error (env_var, "Using default value.", "`%s` must be a number.", opt_name);
4560                 return FALSE;
4561         }
4562         else if (val < min || val > max) {
4563                 sgen_env_var_error (env_var, "Using default value.", "`%s` must be between %.2f - %.2f.", opt_name, min, max);
4564                 return FALSE;
4565         }
4566         *result = val;
4567         return TRUE;
4568 }
4569
4570 static gboolean
4571 thread_in_critical_region (SgenThreadInfo *info)
4572 {
4573         return info->in_critical_region;
4574 }
4575
4576 void
4577 mono_gc_base_init (void)
4578 {
4579         MonoThreadInfoCallbacks cb;
4580         const char *env;
4581         char **opts, **ptr;
4582         char *major_collector_opt = NULL;
4583         char *minor_collector_opt = NULL;
4584         size_t max_heap = 0;
4585         size_t soft_limit = 0;
4586         int result;
4587         int dummy;
4588         gboolean debug_print_allowance = FALSE;
4589         double allowance_ratio = 0, save_target = 0;
4590         gboolean cement_enabled = TRUE;
4591
4592         mono_counters_init ();
4593
4594         do {
4595                 result = InterlockedCompareExchange (&gc_initialized, -1, 0);
4596                 switch (result) {
4597                 case 1:
4598                         /* already inited */
4599                         return;
4600                 case -1:
4601                         /* being inited by another thread */
4602                         g_usleep (1000);
4603                         break;
4604                 case 0:
4605                         /* we will init it */
4606                         break;
4607                 default:
4608                         g_assert_not_reached ();
4609                 }
4610         } while (result != 0);
4611
4612         SGEN_TV_GETTIME (sgen_init_timestamp);
4613
4614         LOCK_INIT (gc_mutex);
4615
4616         pagesize = mono_pagesize ();
4617         gc_debug_file = stderr;
4618
4619         cb.thread_register = sgen_thread_register;
4620         cb.thread_detach = sgen_thread_detach;
4621         cb.thread_unregister = sgen_thread_unregister;
4622         cb.thread_attach = sgen_thread_attach;
4623         cb.mono_method_is_critical = (gpointer)is_critical_method;
4624         cb.mono_thread_in_critical_region = thread_in_critical_region;
4625 #ifndef HOST_WIN32
4626         cb.thread_exit = mono_gc_pthread_exit;
4627         cb.mono_gc_pthread_create = (gpointer)mono_gc_pthread_create;
4628 #endif
4629
4630         mono_threads_init (&cb, sizeof (SgenThreadInfo));
4631
4632         LOCK_INIT (sgen_interruption_mutex);
4633
4634         if ((env = g_getenv (MONO_GC_PARAMS_NAME))) {
4635                 opts = g_strsplit (env, ",", -1);
4636                 for (ptr = opts; *ptr; ++ptr) {
4637                         char *opt = *ptr;
4638                         if (g_str_has_prefix (opt, "major=")) {
4639                                 opt = strchr (opt, '=') + 1;
4640                                 major_collector_opt = g_strdup (opt);
4641                         } else if (g_str_has_prefix (opt, "minor=")) {
4642                                 opt = strchr (opt, '=') + 1;
4643                                 minor_collector_opt = g_strdup (opt);
4644                         }
4645                 }
4646         } else {
4647                 opts = NULL;
4648         }
4649
4650         init_stats ();
4651         sgen_init_internal_allocator ();
4652         sgen_init_nursery_allocator ();
4653         sgen_init_fin_weak_hash ();
4654         sgen_init_stw ();
4655         sgen_init_hash_table ();
4656         sgen_init_descriptors ();
4657         sgen_init_gray_queues ();
4658
4659         sgen_register_fixed_internal_mem_type (INTERNAL_MEM_SECTION, SGEN_SIZEOF_GC_MEM_SECTION);
4660         sgen_register_fixed_internal_mem_type (INTERNAL_MEM_FINALIZE_READY_ENTRY, sizeof (FinalizeReadyEntry));
4661         sgen_register_fixed_internal_mem_type (INTERNAL_MEM_GRAY_QUEUE, sizeof (GrayQueueSection));
4662         sgen_register_fixed_internal_mem_type (INTERNAL_MEM_EPHEMERON_LINK, sizeof (EphemeronLinkNode));
4663
4664 #ifndef HAVE_KW_THREAD
4665         mono_native_tls_alloc (&thread_info_key, NULL);
4666 #if defined(__APPLE__) || defined (HOST_WIN32)
4667         /* 
4668          * CEE_MONO_TLS requires the tls offset, not the key, so the code below only works on darwin,
4669          * where the two are the same.
4670          */
4671         mono_tls_key_set_offset (TLS_KEY_SGEN_THREAD_INFO, thread_info_key);
4672 #endif
4673 #else
4674         {
4675                 int tls_offset = -1;
4676                 MONO_THREAD_VAR_OFFSET (sgen_thread_info, tls_offset);
4677                 mono_tls_key_set_offset (TLS_KEY_SGEN_THREAD_INFO, tls_offset);
4678         }
4679 #endif
4680
4681         /*
4682          * This needs to happen before any internal allocations because
4683          * it inits the small id which is required for hazard pointer
4684          * operations.
4685          */
4686         sgen_os_init ();
4687
4688         mono_thread_info_attach (&dummy);
4689
4690         if (!minor_collector_opt) {
4691                 sgen_simple_nursery_init (&sgen_minor_collector);
4692         } else {
4693                 if (!strcmp (minor_collector_opt, "simple")) {
4694                 use_simple_nursery:
4695                         sgen_simple_nursery_init (&sgen_minor_collector);
4696                 } else if (!strcmp (minor_collector_opt, "split")) {
4697                         sgen_split_nursery_init (&sgen_minor_collector);
4698                 } else {
4699                         sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using `simple` instead.", "Unknown minor collector `%s'.", minor_collector_opt);
4700                         goto use_simple_nursery;
4701                 }
4702         }
4703
4704         if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep")) {
4705         use_marksweep_major:
4706                 sgen_marksweep_init (&major_collector);
4707         } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-conc")) {
4708                 sgen_marksweep_conc_init (&major_collector);
4709         } else {
4710                 sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using `marksweep` instead.", "Unknown major collector `%s'.", major_collector_opt);
4711                 goto use_marksweep_major;
4712         }
4713
4714         ///* Keep this the default for now */
4715         /* Precise marking is broken on all supported targets. Disable until fixed. */
4716         conservative_stack_mark = TRUE;
4717
4718         sgen_nursery_size = DEFAULT_NURSERY_SIZE;
4719
4720         if (major_collector.is_concurrent)
4721                 cement_enabled = FALSE;
4722
4723         if (opts) {
4724                 gboolean usage_printed = FALSE;
4725
4726                 for (ptr = opts; *ptr; ++ptr) {
4727                         char *opt = *ptr;
4728                         if (!strcmp (opt, ""))
4729                                 continue;
4730                         if (g_str_has_prefix (opt, "major="))
4731                                 continue;
4732                         if (g_str_has_prefix (opt, "minor="))
4733                                 continue;
4734                         if (g_str_has_prefix (opt, "max-heap-size=")) {
4735                                 size_t max_heap_candidate = 0;
4736                                 opt = strchr (opt, '=') + 1;
4737                                 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &max_heap_candidate)) {
4738                                         max_heap = (max_heap_candidate + mono_pagesize () - 1) & ~(size_t)(mono_pagesize () - 1);
4739                                         if (max_heap != max_heap_candidate)
4740                                                 sgen_env_var_error (MONO_GC_PARAMS_NAME, "Rounding up.", "`max-heap-size` size must be a multiple of %d.", mono_pagesize ());
4741                                 } else {
4742                                         sgen_env_var_error (MONO_GC_PARAMS_NAME, NULL, "`max-heap-size` must be an integer.");
4743                                 }
4744                                 continue;
4745                         }
4746                         if (g_str_has_prefix (opt, "soft-heap-limit=")) {
4747                                 opt = strchr (opt, '=') + 1;
4748                                 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &soft_limit)) {
4749                                         if (soft_limit <= 0) {
4750                                                 sgen_env_var_error (MONO_GC_PARAMS_NAME, NULL, "`soft-heap-limit` must be positive.");
4751                                                 soft_limit = 0;
4752                                         }
4753                                 } else {
4754                                         sgen_env_var_error (MONO_GC_PARAMS_NAME, NULL, "`soft-heap-limit` must be an integer.");
4755                                 }
4756                                 continue;
4757                         }
4758                         if (g_str_has_prefix (opt, "stack-mark=")) {
4759                                 opt = strchr (opt, '=') + 1;
4760                                 if (!strcmp (opt, "precise")) {
4761                                         conservative_stack_mark = FALSE;
4762                                 } else if (!strcmp (opt, "conservative")) {
4763                                         conservative_stack_mark = TRUE;
4764                                 } else {
4765                                         sgen_env_var_error (MONO_GC_PARAMS_NAME, conservative_stack_mark ? "Using `conservative`." : "Using `precise`.",
4766                                                         "Invalid value `%s` for `stack-mark` option, possible values are: `precise`, `conservative`.", opt);
4767                                 }
4768                                 continue;
4769                         }
4770                         if (g_str_has_prefix (opt, "bridge-implementation=")) {
4771                                 opt = strchr (opt, '=') + 1;
4772                                 sgen_set_bridge_implementation (opt);
4773                                 continue;
4774                         }
4775                         if (g_str_has_prefix (opt, "toggleref-test")) {
4776                                 sgen_register_test_toggleref_callback ();
4777                                 continue;
4778                         }
4779
4780 #ifdef USER_CONFIG
4781                         if (g_str_has_prefix (opt, "nursery-size=")) {
4782                                 size_t val;
4783                                 opt = strchr (opt, '=') + 1;
4784                                 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &val)) {
4785 #ifdef SGEN_ALIGN_NURSERY
4786                                         if ((val & (val - 1))) {
4787                                                 sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using default value.", "`nursery-size` must be a power of two.");
4788                                                 continue;
4789                                         }
4790
4791                                         if (val < SGEN_MAX_NURSERY_WASTE) {
4792                                                 sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using default value.",
4793                                                                 "`nursery-size` must be at least %d bytes.", SGEN_MAX_NURSERY_WASTE);
4794                                                 continue;
4795                                         }
4796
4797                                         sgen_nursery_size = val;
4798                                         sgen_nursery_bits = 0;
4799                                         while (ONE_P << (++ sgen_nursery_bits) != sgen_nursery_size)
4800                                                 ;
4801 #else
4802                                         sgen_nursery_size = val;
4803 #endif
4804                                 } else {
4805                                         sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using default value.", "`nursery-size` must be an integer.");
4806                                         continue;
4807                                 }
4808                                 continue;
4809                         }
4810 #endif
4811                         if (g_str_has_prefix (opt, "save-target-ratio=")) {
4812                                 double val;
4813                                 opt = strchr (opt, '=') + 1;
4814                                 if (parse_double_in_interval (MONO_GC_PARAMS_NAME, "save-target-ratio", opt,
4815                                                 SGEN_MIN_SAVE_TARGET_RATIO, SGEN_MAX_SAVE_TARGET_RATIO, &val)) {
4816                                         save_target = val;
4817                                 }
4818                                 continue;
4819                         }
4820                         if (g_str_has_prefix (opt, "default-allowance-ratio=")) {
4821                                 double val;
4822                                 opt = strchr (opt, '=') + 1;
4823                                 if (parse_double_in_interval (MONO_GC_PARAMS_NAME, "default-allowance-ratio", opt,
4824                                                 SGEN_MIN_ALLOWANCE_NURSERY_SIZE_RATIO, SGEN_MIN_ALLOWANCE_NURSERY_SIZE_RATIO, &val)) {
4825                                         allowance_ratio = val;
4826                                 }
4827                                 continue;
4828                         }
4829                         if (g_str_has_prefix (opt, "allow-synchronous-major=")) {
4830                                 if (!major_collector.is_concurrent) {
4831                                         sgen_env_var_error (MONO_GC_PARAMS_NAME, "Ignoring.", "`allow-synchronous-major` is only valid for the concurrent major collector.");
4832                                         continue;
4833                                 }
4834
4835                                 opt = strchr (opt, '=') + 1;
4836
4837                                 if (!strcmp (opt, "yes")) {
4838                                         allow_synchronous_major = TRUE;
4839                                 } else if (!strcmp (opt, "no")) {
4840                                         allow_synchronous_major = FALSE;
4841                                 } else {
4842                                         sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using default value.", "`allow-synchronous-major` must be either `yes' or `no'.");
4843                                         continue;
4844                                 }
4845                         }
4846
4847                         if (!strcmp (opt, "cementing")) {
4848                                 cement_enabled = TRUE;
4849                                 continue;
4850                         }
4851                         if (!strcmp (opt, "no-cementing")) {
4852                                 cement_enabled = FALSE;
4853                                 continue;
4854                         }
4855
4856                         if (major_collector.handle_gc_param && major_collector.handle_gc_param (opt))
4857                                 continue;
4858
4859                         if (sgen_minor_collector.handle_gc_param && sgen_minor_collector.handle_gc_param (opt))
4860                                 continue;
4861
4862                         sgen_env_var_error (MONO_GC_PARAMS_NAME, "Ignoring.", "Unknown option `%s`.", opt);
4863
4864                         if (usage_printed)
4865                                 continue;
4866
4867                         fprintf (stderr, "\n%s must be a comma-delimited list of one or more of the following:\n", MONO_GC_PARAMS_NAME);
4868                         fprintf (stderr, "  max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
4869                         fprintf (stderr, "  soft-heap-limit=n (where N is an integer, possibly with a k, m or a g suffix)\n");
4870                         fprintf (stderr, "  nursery-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
4871                         fprintf (stderr, "  major=COLLECTOR (where COLLECTOR is `marksweep', `marksweep-conc', `marksweep-par')\n");
4872                         fprintf (stderr, "  minor=COLLECTOR (where COLLECTOR is `simple' or `split')\n");
4873                         fprintf (stderr, "  wbarrier=WBARRIER (where WBARRIER is `remset' or `cardtable')\n");
4874                         fprintf (stderr, "  stack-mark=MARK-METHOD (where MARK-METHOD is 'precise' or 'conservative')\n");
4875                         fprintf (stderr, "  [no-]cementing\n");
4876                         if (major_collector.is_concurrent)
4877                                 fprintf (stderr, "  allow-synchronous-major=FLAG (where FLAG is `yes' or `no')\n");
4878                         if (major_collector.print_gc_param_usage)
4879                                 major_collector.print_gc_param_usage ();
4880                         if (sgen_minor_collector.print_gc_param_usage)
4881                                 sgen_minor_collector.print_gc_param_usage ();
4882                         fprintf (stderr, " Experimental options:\n");
4883                         fprintf (stderr, "  save-target-ratio=R (where R must be between %.2f - %.2f).\n", SGEN_MIN_SAVE_TARGET_RATIO, SGEN_MAX_SAVE_TARGET_RATIO);
4884                         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);
4885                         fprintf (stderr, "\n");
4886
4887                         usage_printed = TRUE;
4888                 }
4889                 g_strfreev (opts);
4890         }
4891
4892         if (major_collector.is_concurrent)
4893                 sgen_workers_init (1);
4894
4895         if (major_collector_opt)
4896                 g_free (major_collector_opt);
4897
4898         if (minor_collector_opt)
4899                 g_free (minor_collector_opt);
4900
4901         alloc_nursery ();
4902
4903         if (major_collector.is_concurrent && cement_enabled) {
4904                 sgen_env_var_error (MONO_GC_PARAMS_NAME, "Ignoring.", "`cementing` is not supported on concurrent major collectors.");
4905                 cement_enabled = FALSE;
4906         }
4907
4908         sgen_cement_init (cement_enabled);
4909
4910         if ((env = g_getenv (MONO_GC_DEBUG_NAME))) {
4911                 gboolean usage_printed = FALSE;
4912
4913                 opts = g_strsplit (env, ",", -1);
4914                 for (ptr = opts; ptr && *ptr; ptr ++) {
4915                         char *opt = *ptr;
4916                         if (!strcmp (opt, ""))
4917                                 continue;
4918                         if (opt [0] >= '0' && opt [0] <= '9') {
4919                                 gc_debug_level = atoi (opt);
4920                                 opt++;
4921                                 if (opt [0] == ':')
4922                                         opt++;
4923                                 if (opt [0]) {
4924                                         char *rf = g_strdup_printf ("%s.%d", opt, mono_process_current_pid ());
4925                                         gc_debug_file = fopen (rf, "wb");
4926                                         if (!gc_debug_file)
4927                                                 gc_debug_file = stderr;
4928                                         g_free (rf);
4929                                 }
4930                         } else if (!strcmp (opt, "print-allowance")) {
4931                                 debug_print_allowance = TRUE;
4932                         } else if (!strcmp (opt, "print-pinning")) {
4933                                 do_pin_stats = TRUE;
4934                         } else if (!strcmp (opt, "verify-before-allocs")) {
4935                                 verify_before_allocs = 1;
4936                                 has_per_allocation_action = TRUE;
4937                         } else if (g_str_has_prefix (opt, "verify-before-allocs=")) {
4938                                 char *arg = strchr (opt, '=') + 1;
4939                                 verify_before_allocs = atoi (arg);
4940                                 has_per_allocation_action = TRUE;
4941                         } else if (!strcmp (opt, "collect-before-allocs")) {
4942                                 collect_before_allocs = 1;
4943                                 has_per_allocation_action = TRUE;
4944                         } else if (g_str_has_prefix (opt, "collect-before-allocs=")) {
4945                                 char *arg = strchr (opt, '=') + 1;
4946                                 has_per_allocation_action = TRUE;
4947                                 collect_before_allocs = atoi (arg);
4948                         } else if (!strcmp (opt, "verify-before-collections")) {
4949                                 whole_heap_check_before_collection = TRUE;
4950                         } else if (!strcmp (opt, "check-at-minor-collections")) {
4951                                 consistency_check_at_minor_collection = TRUE;
4952                                 nursery_clear_policy = CLEAR_AT_GC;
4953                         } else if (!strcmp (opt, "mod-union-consistency-check")) {
4954                                 if (!major_collector.is_concurrent) {
4955                                         sgen_env_var_error (MONO_GC_DEBUG_NAME, "Ignoring.", "`mod-union-consistency-check` only works with concurrent major collector.");
4956                                         continue;
4957                                 }
4958                                 mod_union_consistency_check = TRUE;
4959                         } else if (!strcmp (opt, "check-mark-bits")) {
4960                                 check_mark_bits_after_major_collection = TRUE;
4961                         } else if (!strcmp (opt, "check-nursery-pinned")) {
4962                                 check_nursery_objects_pinned = TRUE;
4963                         } else if (!strcmp (opt, "xdomain-checks")) {
4964                                 xdomain_checks = TRUE;
4965                         } else if (!strcmp (opt, "clear-at-gc")) {
4966                                 nursery_clear_policy = CLEAR_AT_GC;
4967                         } else if (!strcmp (opt, "clear-nursery-at-gc")) {
4968                                 nursery_clear_policy = CLEAR_AT_GC;
4969                         } else if (!strcmp (opt, "clear-at-tlab-creation")) {
4970                                 nursery_clear_policy = CLEAR_AT_TLAB_CREATION;
4971                         } else if (!strcmp (opt, "debug-clear-at-tlab-creation")) {
4972                                 nursery_clear_policy = CLEAR_AT_TLAB_CREATION_DEBUG;
4973                         } else if (!strcmp (opt, "check-scan-starts")) {
4974                                 do_scan_starts_check = TRUE;
4975                         } else if (!strcmp (opt, "verify-nursery-at-minor-gc")) {
4976                                 do_verify_nursery = TRUE;
4977                         } else if (!strcmp (opt, "check-concurrent")) {
4978                                 if (!major_collector.is_concurrent) {
4979                                         sgen_env_var_error (MONO_GC_DEBUG_NAME, "Ignoring.", "`check-concurrent` only works with concurrent major collectors.");
4980                                         continue;
4981                                 }
4982                                 do_concurrent_checks = TRUE;
4983                         } else if (!strcmp (opt, "dump-nursery-at-minor-gc")) {
4984                                 do_dump_nursery_content = TRUE;
4985                         } else if (!strcmp (opt, "no-managed-allocator")) {
4986                                 sgen_set_use_managed_allocator (FALSE);
4987                         } else if (!strcmp (opt, "disable-minor")) {
4988                                 disable_minor_collections = TRUE;
4989                         } else if (!strcmp (opt, "disable-major")) {
4990                                 disable_major_collections = TRUE;
4991                         } else if (g_str_has_prefix (opt, "heap-dump=")) {
4992                                 char *filename = strchr (opt, '=') + 1;
4993                                 nursery_clear_policy = CLEAR_AT_GC;
4994                                 heap_dump_file = fopen (filename, "w");
4995                                 if (heap_dump_file) {
4996                                         fprintf (heap_dump_file, "<sgen-dump>\n");
4997                                         do_pin_stats = TRUE;
4998                                 }
4999                         } else if (g_str_has_prefix (opt, "binary-protocol=")) {
5000                                 char *filename = strchr (opt, '=') + 1;
5001                                 char *colon = strrchr (filename, ':');
5002                                 size_t limit = -1;
5003                                 if (colon) {
5004                                         if (!mono_gc_parse_environment_string_extract_number (colon + 1, &limit)) {
5005                                                 sgen_env_var_error (MONO_GC_DEBUG_NAME, "Ignoring limit.", "Binary protocol file size limit must be an integer.");
5006                                                 limit = -1;
5007                                         }
5008                                         *colon = '\0';
5009                                 }
5010                                 binary_protocol_init (filename, (long long)limit);
5011                         } else if (!strcmp (opt, "nursery-canaries")) {
5012                                 do_verify_nursery = TRUE;
5013                                 sgen_set_use_managed_allocator (FALSE);
5014                                 enable_nursery_canaries = TRUE;
5015                         } else if (!strcmp (opt, "do-not-finalize")) {
5016                                 do_not_finalize = TRUE;
5017                         } else if (!strcmp (opt, "log-finalizers")) {
5018                                 log_finalizers = TRUE;
5019                         } else if (!sgen_bridge_handle_gc_debug (opt)) {
5020                                 sgen_env_var_error (MONO_GC_DEBUG_NAME, "Ignoring.", "Unknown option `%s`.", opt);
5021
5022                                 if (usage_printed)
5023                                         continue;
5024
5025                                 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);
5026                                 fprintf (stderr, "Valid <option>s are:\n");
5027                                 fprintf (stderr, "  collect-before-allocs[=<n>]\n");
5028                                 fprintf (stderr, "  verify-before-allocs[=<n>]\n");
5029                                 fprintf (stderr, "  check-at-minor-collections\n");
5030                                 fprintf (stderr, "  check-mark-bits\n");
5031                                 fprintf (stderr, "  check-nursery-pinned\n");
5032                                 fprintf (stderr, "  verify-before-collections\n");
5033                                 fprintf (stderr, "  verify-nursery-at-minor-gc\n");
5034                                 fprintf (stderr, "  dump-nursery-at-minor-gc\n");
5035                                 fprintf (stderr, "  disable-minor\n");
5036                                 fprintf (stderr, "  disable-major\n");
5037                                 fprintf (stderr, "  xdomain-checks\n");
5038                                 fprintf (stderr, "  check-concurrent\n");
5039                                 fprintf (stderr, "  clear-[nursery-]at-gc\n");
5040                                 fprintf (stderr, "  clear-at-tlab-creation\n");
5041                                 fprintf (stderr, "  debug-clear-at-tlab-creation\n");
5042                                 fprintf (stderr, "  check-scan-starts\n");
5043                                 fprintf (stderr, "  no-managed-allocator\n");
5044                                 fprintf (stderr, "  print-allowance\n");
5045                                 fprintf (stderr, "  print-pinning\n");
5046                                 fprintf (stderr, "  heap-dump=<filename>\n");
5047                                 fprintf (stderr, "  binary-protocol=<filename>[:<file-size-limit>]\n");
5048                                 fprintf (stderr, "  nursery-canaries\n");
5049                                 fprintf (stderr, "  do-not-finalize\n");
5050                                 fprintf (stderr, "  log-finalizers\n");
5051                                 sgen_bridge_print_gc_debug_usage ();
5052                                 fprintf (stderr, "\n");
5053
5054                                 usage_printed = TRUE;
5055                         }
5056                 }
5057                 g_strfreev (opts);
5058         }
5059
5060         if (check_mark_bits_after_major_collection)
5061                 nursery_clear_policy = CLEAR_AT_GC;
5062
5063         if (major_collector.post_param_init)
5064                 major_collector.post_param_init (&major_collector);
5065
5066         sgen_memgov_init (max_heap, soft_limit, debug_print_allowance, allowance_ratio, save_target);
5067
5068         memset (&remset, 0, sizeof (remset));
5069
5070         sgen_card_table_init (&remset);
5071
5072         gc_initialized = 1;
5073 }
5074
5075 const char *
5076 mono_gc_get_gc_name (void)
5077 {
5078         return "sgen";
5079 }
5080
5081 static MonoMethod *write_barrier_method;
5082
5083 gboolean
5084 sgen_is_critical_method (MonoMethod *method)
5085 {
5086         return (method == write_barrier_method || sgen_is_managed_allocator (method));
5087 }
5088
5089 gboolean
5090 sgen_has_critical_method (void)
5091 {
5092         return write_barrier_method || sgen_has_managed_allocator ();
5093 }
5094
5095 #ifndef DISABLE_JIT
5096
5097 static void
5098 emit_nursery_check (MonoMethodBuilder *mb, int *nursery_check_return_labels)
5099 {
5100         memset (nursery_check_return_labels, 0, sizeof (int) * 3);
5101 #ifdef SGEN_ALIGN_NURSERY
5102         // if (ptr_in_nursery (ptr)) return;
5103         /*
5104          * Masking out the bits might be faster, but we would have to use 64 bit
5105          * immediates, which might be slower.
5106          */
5107         mono_mb_emit_ldarg (mb, 0);
5108         mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
5109         mono_mb_emit_byte (mb, CEE_SHR_UN);
5110         mono_mb_emit_ptr (mb, (gpointer)((mword)sgen_get_nursery_start () >> DEFAULT_NURSERY_BITS));
5111         nursery_check_return_labels [0] = mono_mb_emit_branch (mb, CEE_BEQ);
5112
5113         if (!major_collector.is_concurrent) {
5114                 // if (!ptr_in_nursery (*ptr)) return;
5115                 mono_mb_emit_ldarg (mb, 0);
5116                 mono_mb_emit_byte (mb, CEE_LDIND_I);
5117                 mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
5118                 mono_mb_emit_byte (mb, CEE_SHR_UN);
5119                 mono_mb_emit_ptr (mb, (gpointer)((mword)sgen_get_nursery_start () >> DEFAULT_NURSERY_BITS));
5120                 nursery_check_return_labels [1] = mono_mb_emit_branch (mb, CEE_BNE_UN);
5121         }
5122 #else
5123         int label_continue1, label_continue2;
5124         int dereferenced_var;
5125
5126         // if (ptr < (sgen_get_nursery_start ())) goto continue;
5127         mono_mb_emit_ldarg (mb, 0);
5128         mono_mb_emit_ptr (mb, (gpointer) sgen_get_nursery_start ());
5129         label_continue_1 = mono_mb_emit_branch (mb, CEE_BLT);
5130
5131         // if (ptr >= sgen_get_nursery_end ())) goto continue;
5132         mono_mb_emit_ldarg (mb, 0);
5133         mono_mb_emit_ptr (mb, (gpointer) sgen_get_nursery_end ());
5134         label_continue_2 = mono_mb_emit_branch (mb, CEE_BGE);
5135
5136         // Otherwise return
5137         nursery_check_return_labels [0] = mono_mb_emit_branch (mb, CEE_BR);
5138
5139         // continue:
5140         mono_mb_patch_branch (mb, label_continue_1);
5141         mono_mb_patch_branch (mb, label_continue_2);
5142
5143         // Dereference and store in local var
5144         dereferenced_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
5145         mono_mb_emit_ldarg (mb, 0);
5146         mono_mb_emit_byte (mb, CEE_LDIND_I);
5147         mono_mb_emit_stloc (mb, dereferenced_var);
5148
5149         if (!major_collector.is_concurrent) {
5150                 // if (*ptr < sgen_get_nursery_start ()) return;
5151                 mono_mb_emit_ldloc (mb, dereferenced_var);
5152                 mono_mb_emit_ptr (mb, (gpointer) sgen_get_nursery_start ());
5153                 nursery_check_return_labels [1] = mono_mb_emit_branch (mb, CEE_BLT);
5154
5155                 // if (*ptr >= sgen_get_nursery_end ()) return;
5156                 mono_mb_emit_ldloc (mb, dereferenced_var);
5157                 mono_mb_emit_ptr (mb, (gpointer) sgen_get_nursery_end ());
5158                 nursery_check_return_labels [2] = mono_mb_emit_branch (mb, CEE_BGE);
5159         }
5160 #endif  
5161 }
5162 #endif
5163
5164 MonoMethod*
5165 mono_gc_get_write_barrier (void)
5166 {
5167         MonoMethod *res;
5168         MonoMethodBuilder *mb;
5169         MonoMethodSignature *sig;
5170 #ifdef MANAGED_WBARRIER
5171         int i, nursery_check_labels [3];
5172
5173 #ifdef HAVE_KW_THREAD
5174         int stack_end_offset = -1;
5175
5176         MONO_THREAD_VAR_OFFSET (stack_end, stack_end_offset);
5177         g_assert (stack_end_offset != -1);
5178 #endif
5179 #endif
5180
5181         // FIXME: Maybe create a separate version for ctors (the branch would be
5182         // correctly predicted more times)
5183         if (write_barrier_method)
5184                 return write_barrier_method;
5185
5186         /* Create the IL version of mono_gc_barrier_generic_store () */
5187         sig = mono_metadata_signature_alloc (mono_defaults.corlib, 1);
5188         sig->ret = &mono_defaults.void_class->byval_arg;
5189         sig->params [0] = &mono_defaults.int_class->byval_arg;
5190
5191         mb = mono_mb_new (mono_defaults.object_class, "wbarrier", MONO_WRAPPER_WRITE_BARRIER);
5192
5193 #ifndef DISABLE_JIT
5194 #ifdef MANAGED_WBARRIER
5195         emit_nursery_check (mb, nursery_check_labels);
5196         /*
5197         addr = sgen_cardtable + ((address >> CARD_BITS) & CARD_MASK)
5198         *addr = 1;
5199
5200         sgen_cardtable:
5201                 LDC_PTR sgen_cardtable
5202
5203         address >> CARD_BITS
5204                 LDARG_0
5205                 LDC_I4 CARD_BITS
5206                 SHR_UN
5207         if (SGEN_HAVE_OVERLAPPING_CARDS) {
5208                 LDC_PTR card_table_mask
5209                 AND
5210         }
5211         AND
5212         ldc_i4_1
5213         stind_i1
5214         */
5215         mono_mb_emit_ptr (mb, sgen_cardtable);
5216         mono_mb_emit_ldarg (mb, 0);
5217         mono_mb_emit_icon (mb, CARD_BITS);
5218         mono_mb_emit_byte (mb, CEE_SHR_UN);
5219 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
5220         mono_mb_emit_ptr (mb, (gpointer)CARD_MASK);
5221         mono_mb_emit_byte (mb, CEE_AND);
5222 #endif
5223         mono_mb_emit_byte (mb, CEE_ADD);
5224         mono_mb_emit_icon (mb, 1);
5225         mono_mb_emit_byte (mb, CEE_STIND_I1);
5226
5227         // return;
5228         for (i = 0; i < 3; ++i) {
5229                 if (nursery_check_labels [i])
5230                         mono_mb_patch_branch (mb, nursery_check_labels [i]);
5231         }
5232         mono_mb_emit_byte (mb, CEE_RET);
5233 #else
5234         mono_mb_emit_ldarg (mb, 0);
5235         mono_mb_emit_icall (mb, mono_gc_wbarrier_generic_nostore);
5236         mono_mb_emit_byte (mb, CEE_RET);
5237 #endif
5238 #endif
5239         res = mono_mb_create_method (mb, sig, 16);
5240         mono_mb_free (mb);
5241
5242         LOCK_GC;
5243         if (write_barrier_method) {
5244                 /* Already created */
5245                 mono_free_method (res);
5246         } else {
5247                 /* double-checked locking */
5248                 mono_memory_barrier ();
5249                 write_barrier_method = res;
5250         }
5251         UNLOCK_GC;
5252
5253         return write_barrier_method;
5254 }
5255
5256 char*
5257 mono_gc_get_description (void)
5258 {
5259         return g_strdup ("sgen");
5260 }
5261
5262 void
5263 mono_gc_set_desktop_mode (void)
5264 {
5265 }
5266
5267 gboolean
5268 mono_gc_is_moving (void)
5269 {
5270         return TRUE;
5271 }
5272
5273 gboolean
5274 mono_gc_is_disabled (void)
5275 {
5276         return FALSE;
5277 }
5278
5279 #ifdef HOST_WIN32
5280 BOOL APIENTRY mono_gc_dllmain (HMODULE module_handle, DWORD reason, LPVOID reserved)
5281 {
5282         return TRUE;
5283 }
5284 #endif
5285
5286 NurseryClearPolicy
5287 sgen_get_nursery_clear_policy (void)
5288 {
5289         return nursery_clear_policy;
5290 }
5291
5292 MonoVTable*
5293 sgen_get_array_fill_vtable (void)
5294 {
5295         if (!array_fill_vtable) {
5296                 static MonoClass klass;
5297                 static char _vtable[sizeof(MonoVTable)+8];
5298                 MonoVTable* vtable = (MonoVTable*) ALIGN_TO(_vtable, 8);
5299                 gsize bmap;
5300
5301                 MonoDomain *domain = mono_get_root_domain ();
5302                 g_assert (domain);
5303
5304                 klass.element_class = mono_defaults.byte_class;
5305                 klass.rank = 1;
5306                 klass.instance_size = sizeof (MonoArray);
5307                 klass.sizes.element_size = 1;
5308                 klass.name = "array_filler_type";
5309
5310                 vtable->klass = &klass;
5311                 bmap = 0;
5312                 vtable->gc_descr = mono_gc_make_descr_for_array (TRUE, &bmap, 0, 1);
5313                 vtable->rank = 1;
5314
5315                 array_fill_vtable = vtable;
5316         }
5317         return array_fill_vtable;
5318 }
5319
5320 void
5321 sgen_gc_lock (void)
5322 {
5323         LOCK_GC;
5324 }
5325
5326 void
5327 sgen_gc_unlock (void)
5328 {
5329         gboolean try_free = sgen_try_free_some_memory;
5330         sgen_try_free_some_memory = FALSE;
5331         mono_mutex_unlock (&gc_mutex);
5332         MONO_GC_UNLOCKED ();
5333         if (try_free)
5334                 mono_thread_hazardous_try_free_some ();
5335 }
5336
5337 void
5338 sgen_major_collector_iterate_live_block_ranges (sgen_cardtable_block_callback callback)
5339 {
5340         major_collector.iterate_live_block_ranges (callback);
5341 }
5342
5343 void
5344 sgen_major_collector_scan_card_table (SgenGrayQueue *queue)
5345 {
5346         major_collector.scan_card_table (FALSE, queue);
5347 }
5348
5349 SgenMajorCollector*
5350 sgen_get_major_collector (void)
5351 {
5352         return &major_collector;
5353 }
5354
5355 void mono_gc_set_skip_thread (gboolean skip)
5356 {
5357         SgenThreadInfo *info = mono_thread_info_current ();
5358
5359         LOCK_GC;
5360         info->gc_disabled = skip;
5361         UNLOCK_GC;
5362 }
5363
5364 SgenRememberedSet*
5365 sgen_get_remset (void)
5366 {
5367         return &remset;
5368 }
5369
5370 guint
5371 mono_gc_get_vtable_bits (MonoClass *class)
5372 {
5373         guint res = 0;
5374         /* FIXME move this to the bridge code */
5375         if (sgen_need_bridge_processing ()) {
5376                 switch (sgen_bridge_class_kind (class)) {
5377                 case GC_BRIDGE_TRANSPARENT_BRIDGE_CLASS:
5378                 case GC_BRIDGE_OPAQUE_BRIDGE_CLASS:
5379                         res = SGEN_GC_BIT_BRIDGE_OBJECT;
5380                         break;
5381                 case GC_BRIDGE_OPAQUE_CLASS:
5382                         res = SGEN_GC_BIT_BRIDGE_OPAQUE_OBJECT;
5383                         break;
5384                 case GC_BRIDGE_TRANSPARENT_CLASS:
5385                         break;
5386                 }
5387         }
5388         if (fin_callbacks.is_class_finalization_aware) {
5389                 if (fin_callbacks.is_class_finalization_aware (class))
5390                         res |= SGEN_GC_BIT_FINALIZER_AWARE;
5391         }
5392         return res;
5393 }
5394
5395 void
5396 mono_gc_register_altstack (gpointer stack, gint32 stack_size, gpointer altstack, gint32 altstack_size)
5397 {
5398         // FIXME:
5399 }
5400
5401
5402 void
5403 sgen_check_whole_heap_stw (void)
5404 {
5405         sgen_stop_world (0);
5406         sgen_clear_nursery_fragments ();
5407         sgen_check_whole_heap (FALSE);
5408         sgen_restart_world (0, NULL);
5409 }
5410
5411 void
5412 sgen_gc_event_moves (void)
5413 {
5414         if (moved_objects_idx) {
5415                 mono_profiler_gc_moves (moved_objects, moved_objects_idx);
5416                 moved_objects_idx = 0;
5417         }
5418 }
5419
5420 gint64
5421 sgen_timestamp (void)
5422 {
5423         SGEN_TV_DECLARE (timestamp);
5424         SGEN_TV_GETTIME (timestamp);
5425         return SGEN_TV_ELAPSED (sgen_init_timestamp, timestamp);
5426 }
5427
5428 void
5429 mono_gc_register_finalizer_callbacks (MonoGCFinalizerCallbacks *callbacks)
5430 {
5431         if (callbacks->version != MONO_GC_FINALIZER_EXTENSION_VERSION)
5432                 g_error ("Invalid finalizer callback version. Expected %d but got %d\n", MONO_GC_FINALIZER_EXTENSION_VERSION, callbacks->version);
5433
5434         fin_callbacks = *callbacks;
5435 }
5436
5437
5438
5439
5440
5441 #endif /* HAVE_SGEN_GC */