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