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