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