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