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