bed7176aade2d4bfb2fb384f22f9987cd73ff7e0
[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, !major_collector.sweeps_lazily);
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, !major_collector.sweeps_lazily);
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         int oldest_generation_collected = generation_to_collect;
3169         const char *overflow_reason = NULL;
3170
3171         g_assert (generation_to_collect == GENERATION_NURSERY || generation_to_collect == GENERATION_OLD);
3172
3173         memset (infos, 0, sizeof (infos));
3174         mono_profiler_gc_event (MONO_GC_EVENT_START, generation_to_collect);
3175
3176         infos [0].generation = generation_to_collect;
3177         infos [0].reason = reason;
3178         infos [0].is_overflow = FALSE;
3179         TV_GETTIME (infos [0].total_time);
3180         infos [1].generation = -1;
3181
3182         sgen_stop_world (generation_to_collect);
3183
3184         if (concurrent_collection_in_progress) {
3185                 g_print ("finishing concurrent collection\n");
3186                 if (major_update_or_finish_concurrent_collection (generation_to_collect == GENERATION_OLD)) {
3187                         oldest_generation_collected = GENERATION_OLD;
3188                         goto done;
3189                 }
3190         }
3191
3192         //FIXME extract overflow reason
3193         if (generation_to_collect == GENERATION_NURSERY) {
3194                 if (collect_nursery ()) {
3195                         overflow_generation_to_collect = GENERATION_OLD;
3196                         overflow_reason = "Minor overflow";
3197                 }
3198                 if (concurrent_collection_in_progress) {
3199                         gray_queue_redirect (&remember_major_objects_gray_queue);
3200                         sgen_workers_wake_up_all ();
3201                 }
3202         } else {
3203                 if (major_collector.is_concurrent) {
3204                         g_assert (!concurrent_collection_in_progress);
3205                         collect_nursery ();
3206                 }
3207
3208                 if (major_collector.is_concurrent && !wait_to_finish) {
3209                         major_start_concurrent_collection (reason);
3210                         // FIXME: set infos[0] properly
3211                         goto done;
3212                 } else {
3213                         if (major_do_collection (reason)) {
3214                                 overflow_generation_to_collect = GENERATION_NURSERY;
3215                                 overflow_reason = "Excessive pinning";
3216                         }
3217                 }
3218         }
3219
3220         TV_GETTIME (gc_end);
3221         infos [0].total_time = SGEN_TV_ELAPSED (infos [0].total_time, gc_end);
3222
3223
3224         if (!major_collector.is_concurrent && overflow_generation_to_collect != -1) {
3225                 mono_profiler_gc_event (MONO_GC_EVENT_START, overflow_generation_to_collect);
3226                 infos [1].generation = overflow_generation_to_collect;
3227                 infos [1].reason = overflow_reason;
3228                 infos [1].is_overflow = TRUE;
3229                 infos [1].total_time = gc_end;
3230
3231                 if (overflow_generation_to_collect == GENERATION_NURSERY)
3232                         collect_nursery ();
3233                 else
3234                         major_do_collection (overflow_reason);
3235
3236                 TV_GETTIME (gc_end);
3237                 infos [1].total_time = SGEN_TV_ELAPSED (infos [1].total_time, gc_end);
3238
3239                 /* keep events symmetric */
3240                 mono_profiler_gc_event (MONO_GC_EVENT_END, overflow_generation_to_collect);
3241
3242                 oldest_generation_collected = MAX (oldest_generation_collected, overflow_generation_to_collect);
3243         }
3244
3245         SGEN_LOG (2, "Heap size: %lu, LOS size: %lu", (unsigned long)mono_gc_get_heap_size (), (unsigned long)los_memory_usage);
3246
3247         /* this also sets the proper pointers for the next allocation */
3248         if (generation_to_collect == GENERATION_NURSERY && !sgen_can_alloc_size (requested_size)) {
3249                 /* TypeBuilder and MonoMethod are killing mcs with fragmentation */
3250                 SGEN_LOG (1, "nursery collection didn't find enough room for %zd alloc (%d pinned)", requested_size, sgen_get_pinned_count ());
3251                 sgen_dump_pin_queue ();
3252                 degraded_mode = 1;
3253         }
3254
3255  done:
3256         g_assert (sgen_gray_object_queue_is_empty (&gray_queue));
3257         g_assert (sgen_gray_object_queue_is_empty (&remember_major_objects_gray_queue));
3258
3259         sgen_restart_world (oldest_generation_collected, infos);
3260
3261         mono_profiler_gc_event (MONO_GC_EVENT_END, generation_to_collect);
3262 }
3263
3264 /*
3265  * ######################################################################
3266  * ########  Memory allocation from the OS
3267  * ######################################################################
3268  * This section of code deals with getting memory from the OS and
3269  * allocating memory for GC-internal data structures.
3270  * Internal memory can be handled with a freelist for small objects.
3271  */
3272
3273 /*
3274  * Debug reporting.
3275  */
3276 G_GNUC_UNUSED static void
3277 report_internal_mem_usage (void)
3278 {
3279         printf ("Internal memory usage:\n");
3280         sgen_report_internal_mem_usage ();
3281         printf ("Pinned memory usage:\n");
3282         major_collector.report_pinned_memory_usage ();
3283 }
3284
3285 /*
3286  * ######################################################################
3287  * ########  Finalization support
3288  * ######################################################################
3289  */
3290
3291 /*
3292  * If the object has been forwarded it means it's still referenced from a root. 
3293  * If it is pinned it's still alive as well.
3294  * A LOS object is only alive if we have pinned it.
3295  * Return TRUE if @obj is ready to be finalized.
3296  */
3297 static inline gboolean
3298 sgen_is_object_alive (void *object)
3299 {
3300         mword objsize;
3301
3302         if (ptr_in_nursery (object))
3303                 return sgen_nursery_is_object_alive (object);
3304         /* Oldgen objects can be pinned and forwarded too */
3305         if (SGEN_OBJECT_IS_PINNED (object) || SGEN_OBJECT_IS_FORWARDED (object))
3306                 return TRUE;
3307
3308         /*
3309          * FIXME: major_collector.is_object_live() also calculates the
3310          * size.  Avoid the double calculation.
3311          */
3312         objsize = SGEN_ALIGN_UP (sgen_safe_object_get_size ((MonoObject*)object));
3313         if (objsize > SGEN_MAX_SMALL_OBJ_SIZE)
3314                 return sgen_los_object_is_pinned (object);
3315
3316         return major_collector.is_object_live (object);
3317 }
3318
3319 gboolean
3320 sgen_gc_is_object_ready_for_finalization (void *object)
3321 {
3322         return !sgen_is_object_alive (object);
3323 }
3324
3325 static gboolean
3326 has_critical_finalizer (MonoObject *obj)
3327 {
3328         MonoClass *class;
3329
3330         if (!mono_defaults.critical_finalizer_object)
3331                 return FALSE;
3332
3333         class = ((MonoVTable*)LOAD_VTABLE (obj))->klass;
3334
3335         return mono_class_has_parent_fast (class, mono_defaults.critical_finalizer_object);
3336 }
3337
3338 void
3339 sgen_queue_finalization_entry (MonoObject *obj)
3340 {
3341         FinalizeReadyEntry *entry = sgen_alloc_internal (INTERNAL_MEM_FINALIZE_READY_ENTRY);
3342         entry->object = obj;
3343         if (has_critical_finalizer (obj)) {
3344                 entry->next = critical_fin_list;
3345                 critical_fin_list = entry;
3346         } else {
3347                 entry->next = fin_ready_list;
3348                 fin_ready_list = entry;
3349         }
3350 }
3351
3352 static inline int
3353 object_is_reachable (char *object, char *start, char *end)
3354 {
3355         /*This happens for non nursery objects during minor collections. We just treat all objects as alive.*/
3356         if (object < start || object >= end)
3357                 return TRUE;
3358
3359         return sgen_is_object_alive (object);
3360 }
3361
3362 gboolean
3363 sgen_object_is_live (void *obj)
3364 {
3365         if (ptr_in_nursery (obj))
3366                 return object_is_pinned (obj);
3367         /* FIXME This is semantically wrong! All tenured object are considered alive during a nursery collection. */
3368         if (current_collection_generation == GENERATION_NURSERY)
3369                 return FALSE;
3370         return major_collector.is_object_live (obj);
3371 }
3372
3373 /* LOCKING: requires that the GC lock is held */
3374 static void
3375 null_ephemerons_for_domain (MonoDomain *domain)
3376 {
3377         EphemeronLinkNode *current = ephemeron_list, *prev = NULL;
3378
3379         while (current) {
3380                 MonoObject *object = (MonoObject*)current->array;
3381
3382                 if (object && !object->vtable) {
3383                         EphemeronLinkNode *tmp = current;
3384
3385                         if (prev)
3386                                 prev->next = current->next;
3387                         else
3388                                 ephemeron_list = current->next;
3389
3390                         current = current->next;
3391                         sgen_free_internal (tmp, INTERNAL_MEM_EPHEMERON_LINK);
3392                 } else {
3393                         prev = current;
3394                         current = current->next;
3395                 }
3396         }
3397 }
3398
3399 /* LOCKING: requires that the GC lock is held */
3400 static void
3401 clear_unreachable_ephemerons (CopyOrMarkObjectFunc copy_func, char *start, char *end, GrayQueue *queue)
3402 {
3403         int was_in_nursery, was_promoted;
3404         EphemeronLinkNode *current = ephemeron_list, *prev = NULL;
3405         MonoArray *array;
3406         Ephemeron *cur, *array_end;
3407         char *tombstone;
3408
3409         while (current) {
3410                 char *object = current->array;
3411
3412                 if (!object_is_reachable (object, start, end)) {
3413                         EphemeronLinkNode *tmp = current;
3414
3415                         SGEN_LOG (5, "Dead Ephemeron array at %p", object);
3416
3417                         if (prev)
3418                                 prev->next = current->next;
3419                         else
3420                                 ephemeron_list = current->next;
3421
3422                         current = current->next;
3423                         sgen_free_internal (tmp, INTERNAL_MEM_EPHEMERON_LINK);
3424
3425                         continue;
3426                 }
3427
3428                 was_in_nursery = ptr_in_nursery (object);
3429                 copy_func ((void**)&object, queue);
3430                 current->array = object;
3431
3432                 /*The array was promoted, add global remsets for key/values left behind in nursery.*/
3433                 was_promoted = was_in_nursery && !ptr_in_nursery (object);
3434
3435                 SGEN_LOG (5, "Clearing unreachable entries for ephemeron array at %p", object);
3436
3437                 array = (MonoArray*)object;
3438                 cur = mono_array_addr (array, Ephemeron, 0);
3439                 array_end = cur + mono_array_length_fast (array);
3440                 tombstone = (char*)((MonoVTable*)LOAD_VTABLE (object))->domain->ephemeron_tombstone;
3441
3442                 for (; cur < array_end; ++cur) {
3443                         char *key = (char*)cur->key;
3444
3445                         if (!key || key == tombstone)
3446                                 continue;
3447
3448                         SGEN_LOG (5, "[%td] key %p (%s) value %p (%s)", cur - mono_array_addr (array, Ephemeron, 0),
3449                                 key, object_is_reachable (key, start, end) ? "reachable" : "unreachable",
3450                                 cur->value, cur->value && object_is_reachable (cur->value, start, end) ? "reachable" : "unreachable");
3451
3452                         if (!object_is_reachable (key, start, end)) {
3453                                 cur->key = tombstone;
3454                                 cur->value = NULL;
3455                                 continue;
3456                         }
3457
3458                         if (was_promoted) {
3459                                 if (ptr_in_nursery (key)) {/*key was not promoted*/
3460                                         SGEN_LOG (5, "\tAdded remset to key %p", key);
3461                                         sgen_add_to_global_remset (&cur->key);
3462                                 }
3463                                 if (ptr_in_nursery (cur->value)) {/*value was not promoted*/
3464                                         SGEN_LOG (5, "\tAdded remset to value %p", cur->value);
3465                                         sgen_add_to_global_remset (&cur->value);
3466                                 }
3467                         }
3468                 }
3469                 prev = current;
3470                 current = current->next;
3471         }
3472 }
3473
3474 /* LOCKING: requires that the GC lock is held */
3475 static int
3476 mark_ephemerons_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, GrayQueue *queue)
3477 {
3478         int nothing_marked = 1;
3479         EphemeronLinkNode *current = ephemeron_list;
3480         MonoArray *array;
3481         Ephemeron *cur, *array_end;
3482         char *tombstone;
3483
3484         for (current = ephemeron_list; current; current = current->next) {
3485                 char *object = current->array;
3486                 SGEN_LOG (5, "Ephemeron array at %p", object);
3487
3488                 /*
3489                 For now we process all ephemerons during all collections.
3490                 Ideally we should use remset information to partially scan those
3491                 arrays.
3492                 We already emit write barriers for Ephemeron fields, it's
3493                 just that we don't process them.
3494                 */
3495                 /*if (object < start || object >= end)
3496                         continue;*/
3497
3498                 /*It has to be alive*/
3499                 if (!object_is_reachable (object, start, end)) {
3500                         SGEN_LOG (5, "\tnot reachable");
3501                         continue;
3502                 }
3503
3504                 copy_func ((void**)&object, queue);
3505
3506                 array = (MonoArray*)object;
3507                 cur = mono_array_addr (array, Ephemeron, 0);
3508                 array_end = cur + mono_array_length_fast (array);
3509                 tombstone = (char*)((MonoVTable*)LOAD_VTABLE (object))->domain->ephemeron_tombstone;
3510
3511                 for (; cur < array_end; ++cur) {
3512                         char *key = cur->key;
3513
3514                         if (!key || key == tombstone)
3515                                 continue;
3516
3517                         SGEN_LOG (5, "[%td] key %p (%s) value %p (%s)", cur - mono_array_addr (array, Ephemeron, 0),
3518                                 key, object_is_reachable (key, start, end) ? "reachable" : "unreachable",
3519                                 cur->value, cur->value && object_is_reachable (cur->value, start, end) ? "reachable" : "unreachable");
3520
3521                         if (object_is_reachable (key, start, end)) {
3522                                 char *value = cur->value;
3523
3524                                 copy_func ((void**)&cur->key, queue);
3525                                 if (value) {
3526                                         if (!object_is_reachable (value, start, end))
3527                                                 nothing_marked = 0;
3528                                         copy_func ((void**)&cur->value, queue);
3529                                 }
3530                         }
3531                 }
3532         }
3533
3534         SGEN_LOG (5, "Ephemeron run finished. Is it done %d", nothing_marked);
3535         return nothing_marked;
3536 }
3537
3538 int
3539 mono_gc_invoke_finalizers (void)
3540 {
3541         FinalizeReadyEntry *entry = NULL;
3542         gboolean entry_is_critical = FALSE;
3543         int count = 0;
3544         void *obj;
3545         /* FIXME: batch to reduce lock contention */
3546         while (fin_ready_list || critical_fin_list) {
3547                 LOCK_GC;
3548
3549                 if (entry) {
3550                         FinalizeReadyEntry **list = entry_is_critical ? &critical_fin_list : &fin_ready_list;
3551
3552                         /* We have finalized entry in the last
3553                            interation, now we need to remove it from
3554                            the list. */
3555                         if (*list == entry)
3556                                 *list = entry->next;
3557                         else {
3558                                 FinalizeReadyEntry *e = *list;
3559                                 while (e->next != entry)
3560                                         e = e->next;
3561                                 e->next = entry->next;
3562                         }
3563                         sgen_free_internal (entry, INTERNAL_MEM_FINALIZE_READY_ENTRY);
3564                         entry = NULL;
3565                 }
3566
3567                 /* Now look for the first non-null entry. */
3568                 for (entry = fin_ready_list; entry && !entry->object; entry = entry->next)
3569                         ;
3570                 if (entry) {
3571                         entry_is_critical = FALSE;
3572                 } else {
3573                         entry_is_critical = TRUE;
3574                         for (entry = critical_fin_list; entry && !entry->object; entry = entry->next)
3575                                 ;
3576                 }
3577
3578                 if (entry) {
3579                         g_assert (entry->object);
3580                         num_ready_finalizers--;
3581                         obj = entry->object;
3582                         entry->object = NULL;
3583                         SGEN_LOG (7, "Finalizing object %p (%s)", obj, safe_name (obj));
3584                 }
3585
3586                 UNLOCK_GC;
3587
3588                 if (!entry)
3589                         break;
3590
3591                 g_assert (entry->object == NULL);
3592                 count++;
3593                 /* the object is on the stack so it is pinned */
3594                 /*g_print ("Calling finalizer for object: %p (%s)\n", entry->object, safe_name (entry->object));*/
3595                 mono_gc_run_finalize (obj, NULL);
3596         }
3597         g_assert (!entry);
3598         return count;
3599 }
3600
3601 gboolean
3602 mono_gc_pending_finalizers (void)
3603 {
3604         return fin_ready_list || critical_fin_list;
3605 }
3606
3607 /*
3608  * ######################################################################
3609  * ########  registered roots support
3610  * ######################################################################
3611  */
3612
3613 /*
3614  * We do not coalesce roots.
3615  */
3616 static int
3617 mono_gc_register_root_inner (char *start, size_t size, void *descr, int root_type)
3618 {
3619         RootRecord new_root;
3620         int i;
3621         LOCK_GC;
3622         for (i = 0; i < ROOT_TYPE_NUM; ++i) {
3623                 RootRecord *root = sgen_hash_table_lookup (&roots_hash [i], start);
3624                 /* we allow changing the size and the descriptor (for thread statics etc) */
3625                 if (root) {
3626                         size_t old_size = root->end_root - start;
3627                         root->end_root = start + size;
3628                         g_assert (((root->root_desc != 0) && (descr != NULL)) ||
3629                                           ((root->root_desc == 0) && (descr == NULL)));
3630                         root->root_desc = (mword)descr;
3631                         roots_size += size;
3632                         roots_size -= old_size;
3633                         UNLOCK_GC;
3634                         return TRUE;
3635                 }
3636         }
3637
3638         new_root.end_root = start + size;
3639         new_root.root_desc = (mword)descr;
3640
3641         sgen_hash_table_replace (&roots_hash [root_type], start, &new_root, NULL);
3642         roots_size += size;
3643
3644         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);
3645
3646         UNLOCK_GC;
3647         return TRUE;
3648 }
3649
3650 int
3651 mono_gc_register_root (char *start, size_t size, void *descr)
3652 {
3653         return mono_gc_register_root_inner (start, size, descr, descr ? ROOT_TYPE_NORMAL : ROOT_TYPE_PINNED);
3654 }
3655
3656 int
3657 mono_gc_register_root_wbarrier (char *start, size_t size, void *descr)
3658 {
3659         return mono_gc_register_root_inner (start, size, descr, ROOT_TYPE_WBARRIER);
3660 }
3661
3662 void
3663 mono_gc_deregister_root (char* addr)
3664 {
3665         int root_type;
3666         RootRecord root;
3667
3668         LOCK_GC;
3669         for (root_type = 0; root_type < ROOT_TYPE_NUM; ++root_type) {
3670                 if (sgen_hash_table_remove (&roots_hash [root_type], addr, &root))
3671                         roots_size -= (root.end_root - addr);
3672         }
3673         UNLOCK_GC;
3674 }
3675
3676 /*
3677  * ######################################################################
3678  * ########  Thread handling (stop/start code)
3679  * ######################################################################
3680  */
3681
3682 unsigned int sgen_global_stop_count = 0;
3683
3684 void
3685 sgen_fill_thread_info_for_suspend (SgenThreadInfo *info)
3686 {
3687         if (remset.fill_thread_info_for_suspend)
3688                 remset.fill_thread_info_for_suspend (info);
3689 }
3690
3691 int
3692 sgen_get_current_collection_generation (void)
3693 {
3694         return current_collection_generation;
3695 }
3696
3697 void
3698 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
3699 {
3700         gc_callbacks = *callbacks;
3701 }
3702
3703 MonoGCCallbacks *
3704 mono_gc_get_gc_callbacks ()
3705 {
3706         return &gc_callbacks;
3707 }
3708
3709 /* Variables holding start/end nursery so it won't have to be passed at every call */
3710 static void *scan_area_arg_start, *scan_area_arg_end;
3711
3712 void
3713 mono_gc_conservatively_scan_area (void *start, void *end)
3714 {
3715         conservatively_pin_objects_from (start, end, scan_area_arg_start, scan_area_arg_end, PIN_TYPE_STACK);
3716 }
3717
3718 void*
3719 mono_gc_scan_object (void *obj)
3720 {
3721         UserCopyOrMarkData *data = mono_native_tls_get_value (user_copy_or_mark_key);
3722         current_object_ops.copy_or_mark_object (&obj, data->queue);
3723         return obj;
3724 }
3725
3726 /*
3727  * Mark from thread stacks and registers.
3728  */
3729 static void
3730 scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise, GrayQueue *queue)
3731 {
3732         SgenThreadInfo *info;
3733
3734         scan_area_arg_start = start_nursery;
3735         scan_area_arg_end = end_nursery;
3736
3737         FOREACH_THREAD (info) {
3738                 if (info->skip) {
3739                         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);
3740                         continue;
3741                 }
3742                 if (info->gc_disabled) {
3743                         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);
3744                         continue;
3745                 }
3746
3747                 if (!info->joined_stw) {
3748                         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);
3749                         continue;
3750                 }
3751                 
3752                 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 ());
3753                 if (!info->thread_is_dying) {
3754                         if (gc_callbacks.thread_mark_func && !conservative_stack_mark) {
3755                                 UserCopyOrMarkData data = { NULL, queue };
3756                                 set_user_copy_or_mark_data (&data);
3757                                 gc_callbacks.thread_mark_func (info->runtime_data, info->stack_start, info->stack_end, precise);
3758                                 set_user_copy_or_mark_data (NULL);
3759                         } else if (!precise) {
3760                                 conservatively_pin_objects_from (info->stack_start, info->stack_end, start_nursery, end_nursery, PIN_TYPE_STACK);
3761                         }
3762                 }
3763
3764                 if (!info->thread_is_dying && !precise) {
3765 #ifdef USE_MONO_CTX
3766                         conservatively_pin_objects_from ((void**)&info->ctx, (void**)&info->ctx + ARCH_NUM_REGS,
3767                                 start_nursery, end_nursery, PIN_TYPE_STACK);
3768 #else
3769                         conservatively_pin_objects_from (&info->regs, &info->regs + ARCH_NUM_REGS,
3770                                         start_nursery, end_nursery, PIN_TYPE_STACK);
3771 #endif
3772                 }
3773         } END_FOREACH_THREAD
3774 }
3775
3776 static gboolean
3777 ptr_on_stack (void *ptr)
3778 {
3779         gpointer stack_start = &stack_start;
3780         SgenThreadInfo *info = mono_thread_info_current ();
3781
3782         if (ptr >= stack_start && ptr < (gpointer)info->stack_end)
3783                 return TRUE;
3784         return FALSE;
3785 }
3786
3787 static void*
3788 sgen_thread_register (SgenThreadInfo* info, void *addr)
3789 {
3790 #ifndef HAVE_KW_THREAD
3791         SgenThreadInfo *__thread_info__ = info;
3792 #endif
3793
3794         LOCK_GC;
3795 #ifndef HAVE_KW_THREAD
3796         info->tlab_start = info->tlab_next = info->tlab_temp_end = info->tlab_real_end = NULL;
3797
3798         g_assert (!mono_native_tls_get_value (thread_info_key));
3799         mono_native_tls_set_value (thread_info_key, info);
3800 #else
3801         sgen_thread_info = info;
3802 #endif
3803
3804 #if !defined(__MACH__)
3805         info->stop_count = -1;
3806         info->signal = 0;
3807 #endif
3808         info->skip = 0;
3809         info->joined_stw = FALSE;
3810         info->doing_handshake = FALSE;
3811         info->thread_is_dying = FALSE;
3812         info->stack_start = NULL;
3813         info->store_remset_buffer_addr = &STORE_REMSET_BUFFER;
3814         info->store_remset_buffer_index_addr = &STORE_REMSET_BUFFER_INDEX;
3815         info->stopped_ip = NULL;
3816         info->stopped_domain = NULL;
3817 #ifdef USE_MONO_CTX
3818         memset (&info->ctx, 0, sizeof (MonoContext));
3819 #else
3820         memset (&info->regs, 0, sizeof (info->regs));
3821 #endif
3822
3823         sgen_init_tlab_info (info);
3824
3825         binary_protocol_thread_register ((gpointer)mono_thread_info_get_tid (info));
3826
3827 #ifdef HAVE_KW_THREAD
3828         store_remset_buffer_index_addr = &store_remset_buffer_index;
3829 #endif
3830
3831         /* try to get it with attributes first */
3832 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
3833         {
3834                 size_t size;
3835                 void *sstart;
3836                 pthread_attr_t attr;
3837                 pthread_getattr_np (pthread_self (), &attr);
3838                 pthread_attr_getstack (&attr, &sstart, &size);
3839                 info->stack_start_limit = sstart;
3840                 info->stack_end = (char*)sstart + size;
3841                 pthread_attr_destroy (&attr);
3842         }
3843 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
3844                  info->stack_end = (char*)pthread_get_stackaddr_np (pthread_self ());
3845                  info->stack_start_limit = (char*)info->stack_end - pthread_get_stacksize_np (pthread_self ());
3846 #else
3847         {
3848                 /* FIXME: we assume the stack grows down */
3849                 gsize stack_bottom = (gsize)addr;
3850                 stack_bottom += 4095;
3851                 stack_bottom &= ~4095;
3852                 info->stack_end = (char*)stack_bottom;
3853         }
3854 #endif
3855
3856 #ifdef HAVE_KW_THREAD
3857         stack_end = info->stack_end;
3858 #endif
3859
3860         if (remset.register_thread)
3861                 remset.register_thread (info);
3862
3863         SGEN_LOG (3, "registered thread %p (%p) stack end %p", info, (gpointer)mono_thread_info_get_tid (info), info->stack_end);
3864
3865         if (gc_callbacks.thread_attach_func)
3866                 info->runtime_data = gc_callbacks.thread_attach_func ();
3867
3868         UNLOCK_GC;
3869         return info;
3870 }
3871
3872 static void
3873 sgen_wbarrier_cleanup_thread (SgenThreadInfo *p)
3874 {
3875         if (remset.cleanup_thread)
3876                 remset.cleanup_thread (p);
3877 }
3878
3879 static void
3880 sgen_thread_unregister (SgenThreadInfo *p)
3881 {
3882         /* If a delegate is passed to native code and invoked on a thread we dont
3883          * know about, the jit will register it with mono_jit_thread_attach, but
3884          * we have no way of knowing when that thread goes away.  SGen has a TSD
3885          * so we assume that if the domain is still registered, we can detach
3886          * the thread
3887          */
3888         if (mono_domain_get ())
3889                 mono_thread_detach (mono_thread_current ());
3890
3891         p->thread_is_dying = TRUE;
3892
3893         /*
3894         There is a race condition between a thread finishing executing and been removed
3895         from the GC thread set.
3896         This happens on posix systems when TLS data is been cleaned-up, libpthread will
3897         set the thread_info slot to NULL before calling the cleanup function. This
3898         opens a window in which the thread is registered but has a NULL TLS.
3899
3900         The suspend signal handler needs TLS data to know where to store thread state
3901         data or otherwise it will simply ignore the thread.
3902
3903         This solution works because the thread doing STW will wait until all threads been
3904         suspended handshake back, so there is no race between the doing_hankshake test
3905         and the suspend_thread call.
3906
3907         This is not required on systems that do synchronous STW as those can deal with
3908         the above race at suspend time.
3909
3910         FIXME: I believe we could avoid this by using mono_thread_info_lookup when
3911         mono_thread_info_current returns NULL. Or fix mono_thread_info_lookup to do so.
3912         */
3913 #if (defined(__MACH__) && MONO_MACH_ARCH_SUPPORTED) || !defined(HAVE_PTHREAD_KILL)
3914         LOCK_GC;
3915 #else
3916         while (!TRYLOCK_GC) {
3917                 if (!sgen_park_current_thread_if_doing_handshake (p))
3918                         g_usleep (50);
3919         }
3920         MONO_GC_LOCKED ();
3921 #endif
3922
3923         binary_protocol_thread_unregister ((gpointer)mono_thread_info_get_tid (p));
3924         SGEN_LOG (3, "unregister thread %p (%p)", p, (gpointer)mono_thread_info_get_tid (p));
3925
3926         if (gc_callbacks.thread_detach_func) {
3927                 gc_callbacks.thread_detach_func (p->runtime_data);
3928                 p->runtime_data = NULL;
3929         }
3930         sgen_wbarrier_cleanup_thread (p);
3931
3932         mono_threads_unregister_current_thread (p);
3933         UNLOCK_GC;
3934 }
3935
3936
3937 static void
3938 sgen_thread_attach (SgenThreadInfo *info)
3939 {
3940         LOCK_GC;
3941         /*this is odd, can we get attached before the gc is inited?*/
3942         init_stats ();
3943         UNLOCK_GC;
3944         
3945         if (gc_callbacks.thread_attach_func && !info->runtime_data)
3946                 info->runtime_data = gc_callbacks.thread_attach_func ();
3947 }
3948 gboolean
3949 mono_gc_register_thread (void *baseptr)
3950 {
3951         return mono_thread_info_attach (baseptr) != NULL;
3952 }
3953
3954 /*
3955  * mono_gc_set_stack_end:
3956  *
3957  *   Set the end of the current threads stack to STACK_END. The stack space between 
3958  * STACK_END and the real end of the threads stack will not be scanned during collections.
3959  */
3960 void
3961 mono_gc_set_stack_end (void *stack_end)
3962 {
3963         SgenThreadInfo *info;
3964
3965         LOCK_GC;
3966         info = mono_thread_info_current ();
3967         if (info) {
3968                 g_assert (stack_end < info->stack_end);
3969                 info->stack_end = stack_end;
3970         }
3971         UNLOCK_GC;
3972 }
3973
3974 #if USE_PTHREAD_INTERCEPT
3975
3976
3977 int
3978 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
3979 {
3980         return pthread_create (new_thread, attr, start_routine, arg);
3981 }
3982
3983 int
3984 mono_gc_pthread_join (pthread_t thread, void **retval)
3985 {
3986         return pthread_join (thread, retval);
3987 }
3988
3989 int
3990 mono_gc_pthread_detach (pthread_t thread)
3991 {
3992         return pthread_detach (thread);
3993 }
3994
3995 void
3996 mono_gc_pthread_exit (void *retval)
3997 {
3998         pthread_exit (retval);
3999 }
4000
4001 #endif /* USE_PTHREAD_INTERCEPT */
4002
4003 /*
4004  * ######################################################################
4005  * ########  Write barriers
4006  * ######################################################################
4007  */
4008
4009 /*
4010  * Note: the write barriers first do the needed GC work and then do the actual store:
4011  * this way the value is visible to the conservative GC scan after the write barrier
4012  * itself. If a GC interrupts the barrier in the middle, value will be kept alive by
4013  * the conservative scan, otherwise by the remembered set scan.
4014  */
4015 void
4016 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
4017 {
4018         HEAVY_STAT (++stat_wbarrier_set_field);
4019         if (ptr_in_nursery (field_ptr)) {
4020                 *(void**)field_ptr = value;
4021                 return;
4022         }
4023         SGEN_LOG (8, "Adding remset at %p", field_ptr);
4024         if (value)
4025                 binary_protocol_wbarrier (field_ptr, value, value->vtable);
4026
4027         remset.wbarrier_set_field (obj, field_ptr, value);
4028 }
4029
4030 void
4031 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
4032 {
4033         HEAVY_STAT (++stat_wbarrier_set_arrayref);
4034         if (ptr_in_nursery (slot_ptr)) {
4035                 *(void**)slot_ptr = value;
4036                 return;
4037         }
4038         SGEN_LOG (8, "Adding remset at %p", slot_ptr);
4039         if (value)
4040                 binary_protocol_wbarrier (slot_ptr, value, value->vtable);
4041
4042         remset.wbarrier_set_arrayref (arr, slot_ptr, value);
4043 }
4044
4045 void
4046 mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
4047 {
4048         HEAVY_STAT (++stat_wbarrier_arrayref_copy);
4049         /*This check can be done without taking a lock since dest_ptr array is pinned*/
4050         if (ptr_in_nursery (dest_ptr) || count <= 0) {
4051                 mono_gc_memmove (dest_ptr, src_ptr, count * sizeof (gpointer));
4052                 return;
4053         }
4054
4055 #ifdef SGEN_BINARY_PROTOCOL
4056         {
4057                 int i;
4058                 for (i = 0; i < count; ++i) {
4059                         gpointer dest = (gpointer*)dest_ptr + i;
4060                         gpointer obj = *((gpointer*)src_ptr + i);
4061                         if (obj)
4062                                 binary_protocol_wbarrier (dest, obj, (gpointer)LOAD_VTABLE (obj));
4063                 }
4064         }
4065 #endif
4066
4067         remset.wbarrier_arrayref_copy (dest_ptr, src_ptr, count);
4068 }
4069
4070 static char *found_obj;
4071
4072 static void
4073 find_object_for_ptr_callback (char *obj, size_t size, void *user_data)
4074 {
4075         char *ptr = user_data;
4076
4077         if (ptr >= obj && ptr < obj + size) {
4078                 g_assert (!found_obj);
4079                 found_obj = obj;
4080         }
4081 }
4082
4083 /* for use in the debugger */
4084 char* find_object_for_ptr (char *ptr);
4085 char*
4086 find_object_for_ptr (char *ptr)
4087 {
4088         if (ptr >= nursery_section->data && ptr < nursery_section->end_data) {
4089                 found_obj = NULL;
4090                 sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
4091                                 find_object_for_ptr_callback, ptr, TRUE);
4092                 if (found_obj)
4093                         return found_obj;
4094         }
4095
4096         found_obj = NULL;
4097         sgen_los_iterate_objects (find_object_for_ptr_callback, ptr);
4098         if (found_obj)
4099                 return found_obj;
4100
4101         /*
4102          * Very inefficient, but this is debugging code, supposed to
4103          * be called from gdb, so we don't care.
4104          */
4105         found_obj = NULL;
4106         major_collector.iterate_objects (TRUE, TRUE, find_object_for_ptr_callback, ptr);
4107         return found_obj;
4108 }
4109
4110 void
4111 mono_gc_wbarrier_generic_nostore (gpointer ptr)
4112 {
4113         gpointer obj;
4114
4115         HEAVY_STAT (++stat_wbarrier_generic_store);
4116
4117 #ifdef XDOMAIN_CHECKS_IN_WBARRIER
4118         /* FIXME: ptr_in_heap must be called with the GC lock held */
4119         if (xdomain_checks && *(MonoObject**)ptr && ptr_in_heap (ptr)) {
4120                 char *start = find_object_for_ptr (ptr);
4121                 MonoObject *value = *(MonoObject**)ptr;
4122                 LOCK_GC;
4123                 g_assert (start);
4124                 if (start) {
4125                         MonoObject *obj = (MonoObject*)start;
4126                         if (obj->vtable->domain != value->vtable->domain)
4127                                 g_assert (is_xdomain_ref_allowed (ptr, start, obj->vtable->domain));
4128                 }
4129                 UNLOCK_GC;
4130         }
4131 #endif
4132
4133         obj = *(gpointer*)ptr;
4134         if (obj)
4135                 binary_protocol_wbarrier (ptr, obj, (gpointer)LOAD_VTABLE (obj));
4136
4137         if (ptr_in_nursery (ptr) || ptr_on_stack (ptr)) {
4138                 SGEN_LOG (8, "Skipping remset at %p", ptr);
4139                 return;
4140         }
4141
4142         /*
4143          * We need to record old->old pointer locations for the
4144          * concurrent collector.
4145          */
4146         if (!ptr_in_nursery (obj) && !concurrent_collection_in_progress) {
4147                 SGEN_LOG (8, "Skipping remset at %p", ptr);
4148                 return;
4149         }
4150
4151         SGEN_LOG (8, "Adding remset at %p", ptr);
4152
4153         remset.wbarrier_generic_nostore (ptr);
4154 }
4155
4156 void
4157 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
4158 {
4159         SGEN_LOG (8, "Wbarrier store at %p to %p (%s)", ptr, value, value ? safe_name (value) : "null");
4160         *(void**)ptr = value;
4161         if (ptr_in_nursery (value))
4162                 mono_gc_wbarrier_generic_nostore (ptr);
4163         sgen_dummy_use (value);
4164 }
4165
4166 void mono_gc_wbarrier_value_copy_bitmap (gpointer _dest, gpointer _src, int size, unsigned bitmap)
4167 {
4168         mword *dest = _dest;
4169         mword *src = _src;
4170
4171         while (size) {
4172                 if (bitmap & 0x1)
4173                         mono_gc_wbarrier_generic_store (dest, (MonoObject*)*src);
4174                 else
4175                         *dest = *src;
4176                 ++src;
4177                 ++dest;
4178                 size -= SIZEOF_VOID_P;
4179                 bitmap >>= 1;
4180         }
4181 }
4182
4183 #ifdef SGEN_BINARY_PROTOCOL
4184 #undef HANDLE_PTR
4185 #define HANDLE_PTR(ptr,obj) do {                                        \
4186                 gpointer o = *(gpointer*)(ptr);                         \
4187                 if ((o)) {                                              \
4188                         gpointer d = ((char*)dest) + ((char*)(ptr) - (char*)(obj)); \
4189                         binary_protocol_wbarrier (d, o, (gpointer) LOAD_VTABLE (o)); \
4190                 }                                                       \
4191         } while (0)
4192
4193 static void
4194 scan_object_for_binary_protocol_copy_wbarrier (gpointer dest, char *start, mword desc)
4195 {
4196 #define SCAN_OBJECT_NOVTABLE
4197 #include "sgen-scan-object.h"
4198 }
4199 #endif
4200
4201 void
4202 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
4203 {
4204         HEAVY_STAT (++stat_wbarrier_value_copy);
4205         g_assert (klass->valuetype);
4206
4207         SGEN_LOG (8, "Adding value remset at %p, count %d, descr %p for class %s (%p)", dest, count, klass->gc_descr, klass->name, klass);
4208
4209         if (ptr_in_nursery (dest) || ptr_on_stack (dest) || !SGEN_CLASS_HAS_REFERENCES (klass)) {
4210                 size_t element_size = mono_class_value_size (klass, NULL);
4211                 size_t size = count * element_size;
4212                 mono_gc_memmove (dest, src, size);              
4213                 return;
4214         }
4215
4216 #ifdef SGEN_BINARY_PROTOCOL
4217         {
4218                 size_t element_size = mono_class_value_size (klass, NULL);
4219                 int i;
4220                 for (i = 0; i < count; ++i) {
4221                         scan_object_for_binary_protocol_copy_wbarrier ((char*)dest + i * element_size,
4222                                         (char*)src + i * element_size - sizeof (MonoObject),
4223                                         (mword) klass->gc_descr);
4224                 }
4225         }
4226 #endif
4227
4228         remset.wbarrier_value_copy (dest, src, count, klass);
4229 }
4230
4231 /**
4232  * mono_gc_wbarrier_object_copy:
4233  *
4234  * Write barrier to call when obj is the result of a clone or copy of an object.
4235  */
4236 void
4237 mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
4238 {
4239         int size;
4240
4241         HEAVY_STAT (++stat_wbarrier_object_copy);
4242
4243         if (ptr_in_nursery (obj) || ptr_on_stack (obj)) {
4244                 size = mono_object_class (obj)->instance_size;
4245                 mono_gc_memmove ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
4246                                 size - sizeof (MonoObject));
4247                 return; 
4248         }
4249
4250 #ifdef SGEN_BINARY_PROTOCOL
4251         scan_object_for_binary_protocol_copy_wbarrier (obj, (char*)src, (mword) src->vtable->gc_descr);
4252 #endif
4253
4254         remset.wbarrier_object_copy (obj, src);
4255 }
4256
4257
4258 /*
4259  * ######################################################################
4260  * ########  Other mono public interface functions.
4261  * ######################################################################
4262  */
4263
4264 #define REFS_SIZE 128
4265 typedef struct {
4266         void *data;
4267         MonoGCReferences callback;
4268         int flags;
4269         int count;
4270         int called;
4271         MonoObject *refs [REFS_SIZE];
4272         uintptr_t offsets [REFS_SIZE];
4273 } HeapWalkInfo;
4274
4275 #undef HANDLE_PTR
4276 #define HANDLE_PTR(ptr,obj)     do {    \
4277                 if (*(ptr)) {   \
4278                         if (hwi->count == REFS_SIZE) {  \
4279                                 hwi->callback ((MonoObject*)start, mono_object_class (start), hwi->called? 0: size, hwi->count, hwi->refs, hwi->offsets, hwi->data);    \
4280                                 hwi->count = 0; \
4281                                 hwi->called = 1;        \
4282                         }       \
4283                         hwi->offsets [hwi->count] = (char*)(ptr)-(char*)start;  \
4284                         hwi->refs [hwi->count++] = *(ptr);      \
4285                 }       \
4286         } while (0)
4287
4288 static void
4289 collect_references (HeapWalkInfo *hwi, char *start, size_t size)
4290 {
4291 #include "sgen-scan-object.h"
4292 }
4293
4294 static void
4295 walk_references (char *start, size_t size, void *data)
4296 {
4297         HeapWalkInfo *hwi = data;
4298         hwi->called = 0;
4299         hwi->count = 0;
4300         collect_references (hwi, start, size);
4301         if (hwi->count || !hwi->called)
4302                 hwi->callback ((MonoObject*)start, mono_object_class (start), hwi->called? 0: size, hwi->count, hwi->refs, hwi->offsets, hwi->data);
4303 }
4304
4305 /**
4306  * mono_gc_walk_heap:
4307  * @flags: flags for future use
4308  * @callback: a function pointer called for each object in the heap
4309  * @data: a user data pointer that is passed to callback
4310  *
4311  * This function can be used to iterate over all the live objects in the heap:
4312  * for each object, @callback is invoked, providing info about the object's
4313  * location in memory, its class, its size and the objects it references.
4314  * For each referenced object it's offset from the object address is
4315  * reported in the offsets array.
4316  * The object references may be buffered, so the callback may be invoked
4317  * multiple times for the same object: in all but the first call, the size
4318  * argument will be zero.
4319  * Note that this function can be only called in the #MONO_GC_EVENT_PRE_START_WORLD
4320  * profiler event handler.
4321  *
4322  * Returns: a non-zero value if the GC doesn't support heap walking
4323  */
4324 int
4325 mono_gc_walk_heap (int flags, MonoGCReferences callback, void *data)
4326 {
4327         HeapWalkInfo hwi;
4328
4329         hwi.flags = flags;
4330         hwi.callback = callback;
4331         hwi.data = data;
4332
4333         sgen_clear_nursery_fragments ();
4334         sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data, walk_references, &hwi, FALSE);
4335
4336         major_collector.iterate_objects (TRUE, TRUE, walk_references, &hwi);
4337         sgen_los_iterate_objects (walk_references, &hwi);
4338
4339         return 0;
4340 }
4341
4342 void
4343 mono_gc_collect (int generation)
4344 {
4345         LOCK_GC;
4346         if (generation > 1)
4347                 generation = 1;
4348         sgen_perform_collection (0, generation, "user request", TRUE);
4349         UNLOCK_GC;
4350 }
4351
4352 int
4353 mono_gc_max_generation (void)
4354 {
4355         return 1;
4356 }
4357
4358 int
4359 mono_gc_collection_count (int generation)
4360 {
4361         if (generation == 0)
4362                 return stat_minor_gcs;
4363         return stat_major_gcs;
4364 }
4365
4366 int64_t
4367 mono_gc_get_used_size (void)
4368 {
4369         gint64 tot = 0;
4370         LOCK_GC;
4371         tot = los_memory_usage;
4372         tot += nursery_section->next_data - nursery_section->data;
4373         tot += major_collector.get_used_size ();
4374         /* FIXME: account for pinned objects */
4375         UNLOCK_GC;
4376         return tot;
4377 }
4378
4379 void
4380 mono_gc_disable (void)
4381 {
4382         LOCK_GC;
4383         gc_disabled++;
4384         UNLOCK_GC;
4385 }
4386
4387 void
4388 mono_gc_enable (void)
4389 {
4390         LOCK_GC;
4391         gc_disabled--;
4392         UNLOCK_GC;
4393 }
4394
4395 int
4396 mono_gc_get_los_limit (void)
4397 {
4398         return MAX_SMALL_OBJ_SIZE;
4399 }
4400
4401 gboolean
4402 mono_gc_user_markers_supported (void)
4403 {
4404         return TRUE;
4405 }
4406
4407 gboolean
4408 mono_object_is_alive (MonoObject* o)
4409 {
4410         return TRUE;
4411 }
4412
4413 int
4414 mono_gc_get_generation (MonoObject *obj)
4415 {
4416         if (ptr_in_nursery (obj))
4417                 return 0;
4418         return 1;
4419 }
4420
4421 void
4422 mono_gc_enable_events (void)
4423 {
4424 }
4425
4426 void
4427 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
4428 {
4429         sgen_register_disappearing_link (obj, link_addr, track, FALSE);
4430 }
4431
4432 void
4433 mono_gc_weak_link_remove (void **link_addr)
4434 {
4435         sgen_register_disappearing_link (NULL, link_addr, FALSE, FALSE);
4436 }
4437
4438 MonoObject*
4439 mono_gc_weak_link_get (void **link_addr)
4440 {
4441         /*
4442          * We must only load *link_addr once because it might change
4443          * under our feet, and REVEAL_POINTER (NULL) results in an
4444          * invalid reference.
4445          */
4446         void *ptr = *link_addr;
4447         if (!ptr)
4448                 return NULL;
4449
4450         /*
4451          * During the second bridge processing step the world is
4452          * running again.  That step processes all weak links once
4453          * more to null those that refer to dead objects.  Before that
4454          * is completed, those links must not be followed, so we
4455          * conservatively wait for bridge processing when any weak
4456          * link is dereferenced.
4457          */
4458         if (G_UNLIKELY (bridge_processing_in_progress))
4459                 mono_gc_wait_for_bridge_processing ();
4460
4461         return (MonoObject*) REVEAL_POINTER (ptr);
4462 }
4463
4464 gboolean
4465 mono_gc_ephemeron_array_add (MonoObject *obj)
4466 {
4467         EphemeronLinkNode *node;
4468
4469         LOCK_GC;
4470
4471         node = sgen_alloc_internal (INTERNAL_MEM_EPHEMERON_LINK);
4472         if (!node) {
4473                 UNLOCK_GC;
4474                 return FALSE;
4475         }
4476         node->array = (char*)obj;
4477         node->next = ephemeron_list;
4478         ephemeron_list = node;
4479
4480         SGEN_LOG (5, "Registered ephemeron array %p", obj);
4481
4482         UNLOCK_GC;
4483         return TRUE;
4484 }
4485
4486 void*
4487 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
4488 {
4489         void *result;
4490         LOCK_INTERRUPTION;
4491         result = func (data);
4492         UNLOCK_INTERRUPTION;
4493         return result;
4494 }
4495
4496 gboolean
4497 mono_gc_is_gc_thread (void)
4498 {
4499         gboolean result;
4500         LOCK_GC;
4501         result = mono_thread_info_current () != NULL;
4502         UNLOCK_GC;
4503         return result;
4504 }
4505
4506 static gboolean
4507 is_critical_method (MonoMethod *method)
4508 {
4509         return mono_runtime_is_critical_method (method) || sgen_is_critical_method (method);
4510 }
4511         
4512 void
4513 mono_gc_base_init (void)
4514 {
4515         MonoThreadInfoCallbacks cb;
4516         char *env;
4517         char **opts, **ptr;
4518         char *major_collector_opt = NULL;
4519         char *minor_collector_opt = NULL;
4520         glong max_heap = 0;
4521         glong soft_limit = 0;
4522         int num_workers;
4523         int result;
4524         int dummy;
4525         gboolean debug_print_allowance = FALSE;
4526         double allowance_ratio = 0, save_target = 0;
4527
4528         do {
4529                 result = InterlockedCompareExchange (&gc_initialized, -1, 0);
4530                 switch (result) {
4531                 case 1:
4532                         /* already inited */
4533                         return;
4534                 case -1:
4535                         /* being inited by another thread */
4536                         g_usleep (1000);
4537                         break;
4538                 case 0:
4539                         /* we will init it */
4540                         break;
4541                 default:
4542                         g_assert_not_reached ();
4543                 }
4544         } while (result != 0);
4545
4546         LOCK_INIT (gc_mutex);
4547
4548         pagesize = mono_pagesize ();
4549         gc_debug_file = stderr;
4550
4551         cb.thread_register = sgen_thread_register;
4552         cb.thread_unregister = sgen_thread_unregister;
4553         cb.thread_attach = sgen_thread_attach;
4554         cb.mono_method_is_critical = (gpointer)is_critical_method;
4555 #ifndef HOST_WIN32
4556         cb.mono_gc_pthread_create = (gpointer)mono_gc_pthread_create;
4557 #endif
4558
4559         mono_threads_init (&cb, sizeof (SgenThreadInfo));
4560
4561         LOCK_INIT (sgen_interruption_mutex);
4562         LOCK_INIT (pin_queue_mutex);
4563
4564         init_user_copy_or_mark_key ();
4565
4566         if ((env = getenv ("MONO_GC_PARAMS"))) {
4567                 opts = g_strsplit (env, ",", -1);
4568                 for (ptr = opts; *ptr; ++ptr) {
4569                         char *opt = *ptr;
4570                         if (g_str_has_prefix (opt, "major=")) {
4571                                 opt = strchr (opt, '=') + 1;
4572                                 major_collector_opt = g_strdup (opt);
4573                         } else if (g_str_has_prefix (opt, "minor=")) {
4574                                 opt = strchr (opt, '=') + 1;
4575                                 minor_collector_opt = g_strdup (opt);
4576                         }
4577                 }
4578         } else {
4579                 opts = NULL;
4580         }
4581
4582         init_stats ();
4583         sgen_init_internal_allocator ();
4584         sgen_init_nursery_allocator ();
4585
4586         sgen_register_fixed_internal_mem_type (INTERNAL_MEM_SECTION, SGEN_SIZEOF_GC_MEM_SECTION);
4587         sgen_register_fixed_internal_mem_type (INTERNAL_MEM_FINALIZE_READY_ENTRY, sizeof (FinalizeReadyEntry));
4588         sgen_register_fixed_internal_mem_type (INTERNAL_MEM_GRAY_QUEUE, sizeof (GrayQueueSection));
4589         g_assert (sizeof (GenericStoreRememberedSet) == sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
4590         sgen_register_fixed_internal_mem_type (INTERNAL_MEM_STORE_REMSET, sizeof (GenericStoreRememberedSet));
4591         sgen_register_fixed_internal_mem_type (INTERNAL_MEM_EPHEMERON_LINK, sizeof (EphemeronLinkNode));
4592
4593 #ifndef HAVE_KW_THREAD
4594         mono_native_tls_alloc (&thread_info_key, NULL);
4595 #endif
4596
4597         /*
4598          * This needs to happen before any internal allocations because
4599          * it inits the small id which is required for hazard pointer
4600          * operations.
4601          */
4602         sgen_os_init ();
4603
4604         mono_thread_info_attach (&dummy);
4605
4606         if (!minor_collector_opt) {
4607                 sgen_simple_nursery_init (&sgen_minor_collector);
4608         } else {
4609                 if (!strcmp (minor_collector_opt, "simple"))
4610                         sgen_simple_nursery_init (&sgen_minor_collector);
4611                 else if (!strcmp (minor_collector_opt, "split"))
4612                         sgen_split_nursery_init (&sgen_minor_collector);
4613                 else {
4614                         fprintf (stderr, "Unknown minor collector `%s'.\n", minor_collector_opt);
4615                         exit (1);
4616                 }
4617         }
4618
4619         if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep")) {
4620                 sgen_marksweep_init (&major_collector);
4621         } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-fixed")) {
4622                 sgen_marksweep_fixed_init (&major_collector);
4623         } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-par")) {
4624                 sgen_marksweep_par_init (&major_collector);
4625         } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-fixed-par")) {
4626                 sgen_marksweep_fixed_par_init (&major_collector);
4627         } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-conc")) {
4628                 sgen_marksweep_conc_init (&major_collector);
4629         } else if (!strcmp (major_collector_opt, "copying")) {
4630                 sgen_copying_init (&major_collector);
4631         } else {
4632                 fprintf (stderr, "Unknown major collector `%s'.\n", major_collector_opt);
4633                 exit (1);
4634         }
4635
4636 #ifdef SGEN_HAVE_CARDTABLE
4637         use_cardtable = major_collector.supports_cardtable;
4638 #else
4639         use_cardtable = FALSE;
4640 #endif
4641
4642         num_workers = mono_cpu_count ();
4643         g_assert (num_workers > 0);
4644         if (num_workers > 16)
4645                 num_workers = 16;
4646
4647         ///* Keep this the default for now */
4648         /* Precise marking is broken on all supported targets. Disable until fixed. */
4649         conservative_stack_mark = TRUE;
4650
4651         sgen_nursery_size = DEFAULT_NURSERY_SIZE;
4652
4653         if (opts) {
4654                 for (ptr = opts; *ptr; ++ptr) {
4655                         char *opt = *ptr;
4656                         if (g_str_has_prefix (opt, "major="))
4657                                 continue;
4658                         if (g_str_has_prefix (opt, "minor="))
4659                                 continue;
4660                         if (g_str_has_prefix (opt, "wbarrier=")) {
4661                                 opt = strchr (opt, '=') + 1;
4662                                 if (strcmp (opt, "remset") == 0) {
4663                                         if (major_collector.is_concurrent) {
4664                                                 fprintf (stderr, "The concurrent collector does not support the SSB write barrier.\n");
4665                                                 exit (1);
4666                                         }
4667                                         use_cardtable = FALSE;
4668                                 } else if (strcmp (opt, "cardtable") == 0) {
4669                                         if (!use_cardtable) {
4670                                                 if (major_collector.supports_cardtable)
4671                                                         fprintf (stderr, "The cardtable write barrier is not supported on this platform.\n");
4672                                                 else
4673                                                         fprintf (stderr, "The major collector does not support the cardtable write barrier.\n");
4674                                                 exit (1);
4675                                         }
4676                                 } else {
4677                                         fprintf (stderr, "wbarrier must either be `remset' or `cardtable'.");
4678                                         exit (1);
4679                                 }
4680                                 continue;
4681                         }
4682                         if (g_str_has_prefix (opt, "max-heap-size=")) {
4683                                 opt = strchr (opt, '=') + 1;
4684                                 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &max_heap)) {
4685                                         if ((max_heap & (mono_pagesize () - 1))) {
4686                                                 fprintf (stderr, "max-heap-size size must be a multiple of %d.\n", mono_pagesize ());
4687                                                 exit (1);
4688                                         }
4689                                 } else {
4690                                         fprintf (stderr, "max-heap-size must be an integer.\n");
4691                                         exit (1);
4692                                 }
4693                                 continue;
4694                         }
4695                         if (g_str_has_prefix (opt, "soft-heap-limit=")) {
4696                                 opt = strchr (opt, '=') + 1;
4697                                 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &soft_limit)) {
4698                                         if (soft_limit <= 0) {
4699                                                 fprintf (stderr, "soft-heap-limit must be positive.\n");
4700                                                 exit (1);
4701                                         }
4702                                 } else {
4703                                         fprintf (stderr, "soft-heap-limit must be an integer.\n");
4704                                         exit (1);
4705                                 }
4706                                 continue;
4707                         }
4708                         if (g_str_has_prefix (opt, "workers=")) {
4709                                 long val;
4710                                 char *endptr;
4711                                 if (!major_collector.is_parallel) {
4712                                         fprintf (stderr, "The workers= option can only be used for parallel collectors.");
4713                                         exit (1);
4714                                 }
4715                                 opt = strchr (opt, '=') + 1;
4716                                 val = strtol (opt, &endptr, 10);
4717                                 if (!*opt || *endptr) {
4718                                         fprintf (stderr, "Cannot parse the workers= option value.");
4719                                         exit (1);
4720                                 }
4721                                 if (val <= 0 || val > 16) {
4722                                         fprintf (stderr, "The number of workers must be in the range 1 to 16.");
4723                                         exit (1);
4724                                 }
4725                                 num_workers = (int)val;
4726                                 continue;
4727                         }
4728                         if (g_str_has_prefix (opt, "stack-mark=")) {
4729                                 opt = strchr (opt, '=') + 1;
4730                                 if (!strcmp (opt, "precise")) {
4731                                         conservative_stack_mark = FALSE;
4732                                 } else if (!strcmp (opt, "conservative")) {
4733                                         conservative_stack_mark = TRUE;
4734                                 } else {
4735                                         fprintf (stderr, "Invalid value '%s' for stack-mark= option, possible values are: 'precise', 'conservative'.\n", opt);
4736                                         exit (1);
4737                                 }
4738                                 continue;
4739                         }
4740                         if (g_str_has_prefix (opt, "bridge=")) {
4741                                 opt = strchr (opt, '=') + 1;
4742                                 sgen_register_test_bridge_callbacks (g_strdup (opt));
4743                                 continue;
4744                         }
4745 #ifdef USER_CONFIG
4746                         if (g_str_has_prefix (opt, "nursery-size=")) {
4747                                 long val;
4748                                 opt = strchr (opt, '=') + 1;
4749                                 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &val)) {
4750                                         sgen_nursery_size = val;
4751 #ifdef SGEN_ALIGN_NURSERY
4752                                         if ((val & (val - 1))) {
4753                                                 fprintf (stderr, "The nursery size must be a power of two.\n");
4754                                                 exit (1);
4755                                         }
4756
4757                                         if (val < SGEN_MAX_NURSERY_WASTE) {
4758                                                 fprintf (stderr, "The nursery size must be at least %d bytes.\n", SGEN_MAX_NURSERY_WASTE);
4759                                                 exit (1);
4760                                         }
4761
4762                                         sgen_nursery_bits = 0;
4763                                         while (1 << (++ sgen_nursery_bits) != sgen_nursery_size)
4764                                                 ;
4765 #endif
4766                                 } else {
4767                                         fprintf (stderr, "nursery-size must be an integer.\n");
4768                                         exit (1);
4769                                 }
4770                                 continue;
4771                         }
4772 #endif
4773                         if (g_str_has_prefix (opt, "save-target-ratio=")) {
4774                                 char *endptr;
4775                                 opt = strchr (opt, '=') + 1;
4776                                 save_target = strtod (opt, &endptr);
4777                                 if (endptr == opt) {
4778                                         fprintf (stderr, "save-target-ratio must be a number.");
4779                                         exit (1);
4780                                 }
4781                                 if (save_target < SGEN_MIN_SAVE_TARGET_RATIO || save_target > SGEN_MAX_SAVE_TARGET_RATIO) {
4782                                         fprintf (stderr, "save-target-ratio must be between %.2f - %.2f.", SGEN_MIN_SAVE_TARGET_RATIO, SGEN_MAX_SAVE_TARGET_RATIO);
4783                                         exit (1);
4784                                 }
4785                                 continue;
4786                         }
4787                         if (g_str_has_prefix (opt, "default-allowance-ratio=")) {
4788                                 char *endptr;
4789                                 opt = strchr (opt, '=') + 1;
4790
4791                                 allowance_ratio = strtod (opt, &endptr);
4792                                 if (endptr == opt) {
4793                                         fprintf (stderr, "save-target-ratio must be a number.");
4794                                         exit (1);
4795                                 }
4796                                 if (allowance_ratio < SGEN_MIN_ALLOWANCE_NURSERY_SIZE_RATIO || allowance_ratio > SGEN_MIN_ALLOWANCE_NURSERY_SIZE_RATIO) {
4797                                         fprintf (stderr, "default-allowance-ratio must be between %.2f - %.2f.", SGEN_MIN_ALLOWANCE_NURSERY_SIZE_RATIO, SGEN_MIN_ALLOWANCE_NURSERY_SIZE_RATIO);
4798                                         exit (1);
4799                                 }
4800                                 continue;
4801                         }
4802
4803                         if (major_collector.handle_gc_param && major_collector.handle_gc_param (opt))
4804                                 continue;
4805
4806                         if (sgen_minor_collector.handle_gc_param && sgen_minor_collector.handle_gc_param (opt))
4807                                 continue;
4808
4809                         fprintf (stderr, "MONO_GC_PARAMS must be a comma-delimited list of one or more of the following:\n");
4810                         fprintf (stderr, "  max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
4811                         fprintf (stderr, "  soft-heap-limit=n (where N is an integer, possibly with a k, m or a g suffix)\n");
4812                         fprintf (stderr, "  nursery-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
4813                         fprintf (stderr, "  major=COLLECTOR (where COLLECTOR is `marksweep', `marksweep-par', 'marksweep-fixed', 'marksweep-fixed-par' or `copying')\n");
4814                         fprintf (stderr, "  minor=COLLECTOR (where COLLECTOR is `simple' or `split')\n");
4815                         fprintf (stderr, "  wbarrier=WBARRIER (where WBARRIER is `remset' or `cardtable')\n");
4816                         fprintf (stderr, "  stack-mark=MARK-METHOD (where MARK-METHOD is 'precise' or 'conservative')\n");
4817                         if (major_collector.print_gc_param_usage)
4818                                 major_collector.print_gc_param_usage ();
4819                         if (sgen_minor_collector.print_gc_param_usage)
4820                                 sgen_minor_collector.print_gc_param_usage ();
4821                         fprintf (stderr, " Experimental options:\n");
4822                         fprintf (stderr, "  save-target-ratio=R (where R must be between %.2f - %.2f).\n", SGEN_MIN_SAVE_TARGET_RATIO, SGEN_MAX_SAVE_TARGET_RATIO);
4823                         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);
4824                         exit (1);
4825                 }
4826                 g_strfreev (opts);
4827         }
4828
4829         if (major_collector.is_parallel)
4830                 sgen_workers_init (num_workers);
4831         else if (major_collector.is_concurrent)
4832                 sgen_workers_init (1);
4833
4834         if (major_collector_opt)
4835                 g_free (major_collector_opt);
4836
4837         if (minor_collector_opt)
4838                 g_free (minor_collector_opt);
4839
4840         alloc_nursery ();
4841
4842         if ((env = getenv ("MONO_GC_DEBUG"))) {
4843                 opts = g_strsplit (env, ",", -1);
4844                 for (ptr = opts; ptr && *ptr; ptr ++) {
4845                         char *opt = *ptr;
4846                         if (opt [0] >= '0' && opt [0] <= '9') {
4847                                 gc_debug_level = atoi (opt);
4848                                 opt++;
4849                                 if (opt [0] == ':')
4850                                         opt++;
4851                                 if (opt [0]) {
4852 #ifdef HOST_WIN32
4853                                         char *rf = g_strdup_printf ("%s.%d", opt, GetCurrentProcessId ());
4854 #else
4855                                         char *rf = g_strdup_printf ("%s.%d", opt, getpid ());
4856 #endif
4857                                         gc_debug_file = fopen (rf, "wb");
4858                                         if (!gc_debug_file)
4859                                                 gc_debug_file = stderr;
4860                                         g_free (rf);
4861                                 }
4862                         } else if (!strcmp (opt, "print-allowance")) {
4863                                 debug_print_allowance = TRUE;
4864                         } else if (!strcmp (opt, "print-pinning")) {
4865                                 do_pin_stats = TRUE;
4866                         } else if (!strcmp (opt, "verify-before-allocs")) {
4867                                 verify_before_allocs = 1;
4868                                 has_per_allocation_action = TRUE;
4869                         } else if (g_str_has_prefix (opt, "verify-before-allocs=")) {
4870                                 char *arg = strchr (opt, '=') + 1;
4871                                 verify_before_allocs = atoi (arg);
4872                                 has_per_allocation_action = TRUE;
4873                         } else if (!strcmp (opt, "collect-before-allocs")) {
4874                                 collect_before_allocs = 1;
4875                                 has_per_allocation_action = TRUE;
4876                         } else if (g_str_has_prefix (opt, "collect-before-allocs=")) {
4877                                 char *arg = strchr (opt, '=') + 1;
4878                                 has_per_allocation_action = TRUE;
4879                                 collect_before_allocs = atoi (arg);
4880                         } else if (!strcmp (opt, "verify-before-collections")) {
4881                                 whole_heap_check_before_collection = TRUE;
4882                         } else if (!strcmp (opt, "check-at-minor-collections")) {
4883                                 consistency_check_at_minor_collection = TRUE;
4884                                 nursery_clear_policy = CLEAR_AT_GC;
4885                         } else if (!strcmp (opt, "xdomain-checks")) {
4886                                 xdomain_checks = TRUE;
4887                         } else if (!strcmp (opt, "clear-at-gc")) {
4888                                 nursery_clear_policy = CLEAR_AT_GC;
4889                         } else if (!strcmp (opt, "clear-nursery-at-gc")) {
4890                                 nursery_clear_policy = CLEAR_AT_GC;
4891                         } else if (!strcmp (opt, "check-scan-starts")) {
4892                                 do_scan_starts_check = TRUE;
4893                         } else if (!strcmp (opt, "verify-nursery-at-minor-gc")) {
4894                                 do_verify_nursery = TRUE;
4895                         } else if (!strcmp (opt, "check-concurrent")) {
4896                                 if (!major_collector.is_concurrent) {
4897                                         fprintf (stderr, "Error: check-concurrent only world with concurrent major collectors.\n");
4898                                         exit (1);
4899                                 }
4900                                 do_concurrent_checks = TRUE;
4901                         } else if (!strcmp (opt, "dump-nursery-at-minor-gc")) {
4902                                 do_dump_nursery_content = TRUE;
4903                         } else if (!strcmp (opt, "no-managed-allocator")) {
4904                                 sgen_set_use_managed_allocator (FALSE);
4905                         } else if (!strcmp (opt, "disable-minor")) {
4906                                 disable_minor_collections = TRUE;
4907                         } else if (!strcmp (opt, "disable-major")) {
4908                                 disable_major_collections = TRUE;
4909                         } else if (g_str_has_prefix (opt, "heap-dump=")) {
4910                                 char *filename = strchr (opt, '=') + 1;
4911                                 nursery_clear_policy = CLEAR_AT_GC;
4912                                 heap_dump_file = fopen (filename, "w");
4913                                 if (heap_dump_file) {
4914                                         fprintf (heap_dump_file, "<sgen-dump>\n");
4915                                         do_pin_stats = TRUE;
4916                                 }
4917 #ifdef SGEN_BINARY_PROTOCOL
4918                         } else if (g_str_has_prefix (opt, "binary-protocol=")) {
4919                                 char *filename = strchr (opt, '=') + 1;
4920                                 binary_protocol_init (filename);
4921                                 if (use_cardtable)
4922                                         fprintf (stderr, "Warning: Cardtable write barriers will not be binary-protocolled.\n");
4923 #endif
4924                         } else {
4925                                 fprintf (stderr, "Invalid format for the MONO_GC_DEBUG env variable: '%s'\n", env);
4926                                 fprintf (stderr, "The format is: MONO_GC_DEBUG=[l[:filename]|<option>]+ where l is a debug level 0-9.\n");
4927                                 fprintf (stderr, "Valid options are:\n");
4928                                 fprintf (stderr, "  collect-before-allocs[=<n>]\n");
4929                                 fprintf (stderr, "  verify-before-allocs[=<n>]\n");
4930                                 fprintf (stderr, "  check-at-minor-collections\n");
4931                                 fprintf (stderr, "  verify-before-collections\n");
4932                                 fprintf (stderr, "  verify-nursery-at-minor-gc\n");
4933                                 fprintf (stderr, "  dump-nursery-at-minor-gc\n");
4934                                 fprintf (stderr, "  disable-minor\n");
4935                                 fprintf (stderr, "  disable-major\n");
4936                                 fprintf (stderr, "  xdomain-checks\n");
4937                                 fprintf (stderr, "  check-concurrent\n");
4938                                 fprintf (stderr, "  clear-at-gc\n");
4939                                 fprintf (stderr, "  clear-nursery-at-gc\n");
4940                                 fprintf (stderr, "  check-scan-starts\n");
4941                                 fprintf (stderr, "  no-managed-allocator\n");
4942                                 fprintf (stderr, "  print-allowance\n");
4943                                 fprintf (stderr, "  print-pinning\n");
4944                                 fprintf (stderr, "  heap-dump=<filename>\n");
4945 #ifdef SGEN_BINARY_PROTOCOL
4946                                 fprintf (stderr, "  binary-protocol=<filename>\n");
4947 #endif
4948                                 exit (1);
4949                         }
4950                 }
4951                 g_strfreev (opts);
4952         }
4953
4954         if (major_collector.is_parallel) {
4955                 if (heap_dump_file) {
4956                         fprintf (stderr, "Error: Cannot do heap dump with the parallel collector.\n");
4957                         exit (1);
4958                 }
4959                 if (do_pin_stats) {
4960                         fprintf (stderr, "Error: Cannot gather pinning statistics with the parallel collector.\n");
4961                         exit (1);
4962                 }
4963         }
4964
4965         if (major_collector.post_param_init)
4966                 major_collector.post_param_init (&major_collector);
4967
4968         sgen_memgov_init (max_heap, soft_limit, debug_print_allowance, allowance_ratio, save_target);
4969
4970         memset (&remset, 0, sizeof (remset));
4971
4972 #ifdef SGEN_HAVE_CARDTABLE
4973         if (use_cardtable)
4974                 sgen_card_table_init (&remset);
4975         else
4976 #endif
4977                 sgen_ssb_init (&remset);
4978
4979         if (remset.register_thread)
4980                 remset.register_thread (mono_thread_info_current ());
4981
4982         gc_initialized = 1;
4983 }
4984
4985 const char *
4986 mono_gc_get_gc_name (void)
4987 {
4988         return "sgen";
4989 }
4990
4991 static MonoMethod *write_barrier_method;
4992
4993 gboolean
4994 sgen_is_critical_method (MonoMethod *method)
4995 {
4996         return (method == write_barrier_method || sgen_is_managed_allocator (method));
4997 }
4998
4999 gboolean
5000 sgen_has_critical_method (void)
5001 {
5002         return write_barrier_method || sgen_has_managed_allocator ();
5003 }
5004
5005 static void
5006 emit_nursery_check (MonoMethodBuilder *mb, int *nursery_check_return_labels)
5007 {
5008         memset (nursery_check_return_labels, 0, sizeof (int) * 3);
5009 #ifdef SGEN_ALIGN_NURSERY
5010         // if (ptr_in_nursery (ptr)) return;
5011         /*
5012          * Masking out the bits might be faster, but we would have to use 64 bit
5013          * immediates, which might be slower.
5014          */
5015         mono_mb_emit_ldarg (mb, 0);
5016         mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
5017         mono_mb_emit_byte (mb, CEE_SHR_UN);
5018         mono_mb_emit_icon (mb, (mword)sgen_get_nursery_start () >> DEFAULT_NURSERY_BITS);
5019         nursery_check_return_labels [0] = mono_mb_emit_branch (mb, CEE_BEQ);
5020
5021         if (!major_collector.is_concurrent) {
5022                 // if (!ptr_in_nursery (*ptr)) return;
5023                 mono_mb_emit_ldarg (mb, 0);
5024                 mono_mb_emit_byte (mb, CEE_LDIND_I);
5025                 mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
5026                 mono_mb_emit_byte (mb, CEE_SHR_UN);
5027                 mono_mb_emit_icon (mb, (mword)sgen_get_nursery_start () >> DEFAULT_NURSERY_BITS);
5028                 nursery_check_return_labels [1] = mono_mb_emit_branch (mb, CEE_BNE_UN);
5029         }
5030 #else
5031         int label_continue1, label_continue2;
5032         int dereferenced_var;
5033
5034         // if (ptr < (sgen_get_nursery_start ())) goto continue;
5035         mono_mb_emit_ldarg (mb, 0);
5036         mono_mb_emit_ptr (mb, (gpointer) sgen_get_nursery_start ());
5037         label_continue_1 = mono_mb_emit_branch (mb, CEE_BLT);
5038
5039         // if (ptr >= sgen_get_nursery_end ())) goto continue;
5040         mono_mb_emit_ldarg (mb, 0);
5041         mono_mb_emit_ptr (mb, (gpointer) sgen_get_nursery_end ());
5042         label_continue_2 = mono_mb_emit_branch (mb, CEE_BGE);
5043
5044         // Otherwise return
5045         nursery_check_return_labels [0] = mono_mb_emit_branch (mb, CEE_BR);
5046
5047         // continue:
5048         mono_mb_patch_branch (mb, label_continue_1);
5049         mono_mb_patch_branch (mb, label_continue_2);
5050
5051         // Dereference and store in local var
5052         dereferenced_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
5053         mono_mb_emit_ldarg (mb, 0);
5054         mono_mb_emit_byte (mb, CEE_LDIND_I);
5055         mono_mb_emit_stloc (mb, dereferenced_var);
5056
5057         if (!major_collector.is_concurrent) {
5058                 // if (*ptr < sgen_get_nursery_start ()) return;
5059                 mono_mb_emit_ldloc (mb, dereferenced_var);
5060                 mono_mb_emit_ptr (mb, (gpointer) sgen_get_nursery_start ());
5061                 nursery_check_return_labels [1] = mono_mb_emit_branch (mb, CEE_BLT);
5062
5063                 // if (*ptr >= sgen_get_nursery_end ()) return;
5064                 mono_mb_emit_ldloc (mb, dereferenced_var);
5065                 mono_mb_emit_ptr (mb, (gpointer) sgen_get_nursery_end ());
5066                 nursery_check_return_labels [2] = mono_mb_emit_branch (mb, CEE_BGE);
5067         }
5068 #endif  
5069 }
5070
5071 MonoMethod*
5072 mono_gc_get_write_barrier (void)
5073 {
5074         MonoMethod *res;
5075         MonoMethodBuilder *mb;
5076         MonoMethodSignature *sig;
5077 #ifdef MANAGED_WBARRIER
5078         int i, nursery_check_labels [3];
5079         int label_no_wb_3, label_no_wb_4, label_need_wb, label_slow_path;
5080         int buffer_var, buffer_index_var, dummy_var;
5081
5082 #ifdef HAVE_KW_THREAD
5083         int stack_end_offset = -1, store_remset_buffer_offset = -1;
5084         int store_remset_buffer_index_offset = -1, store_remset_buffer_index_addr_offset = -1;
5085
5086         MONO_THREAD_VAR_OFFSET (stack_end, stack_end_offset);
5087         g_assert (stack_end_offset != -1);
5088         MONO_THREAD_VAR_OFFSET (store_remset_buffer, store_remset_buffer_offset);
5089         g_assert (store_remset_buffer_offset != -1);
5090         MONO_THREAD_VAR_OFFSET (store_remset_buffer_index, store_remset_buffer_index_offset);
5091         g_assert (store_remset_buffer_index_offset != -1);
5092         MONO_THREAD_VAR_OFFSET (store_remset_buffer_index_addr, store_remset_buffer_index_addr_offset);
5093         g_assert (store_remset_buffer_index_addr_offset != -1);
5094 #endif
5095 #endif
5096
5097         // FIXME: Maybe create a separate version for ctors (the branch would be
5098         // correctly predicted more times)
5099         if (write_barrier_method)
5100                 return write_barrier_method;
5101
5102         /* Create the IL version of mono_gc_barrier_generic_store () */
5103         sig = mono_metadata_signature_alloc (mono_defaults.corlib, 1);
5104         sig->ret = &mono_defaults.void_class->byval_arg;
5105         sig->params [0] = &mono_defaults.int_class->byval_arg;
5106
5107         mb = mono_mb_new (mono_defaults.object_class, "wbarrier", MONO_WRAPPER_WRITE_BARRIER);
5108
5109 #ifdef MANAGED_WBARRIER
5110         if (use_cardtable) {
5111                 emit_nursery_check (mb, nursery_check_labels);
5112                 /*
5113                 addr = sgen_cardtable + ((address >> CARD_BITS) & CARD_MASK)
5114                 *addr = 1;
5115
5116                 sgen_cardtable: 
5117                         LDC_PTR sgen_cardtable
5118
5119                 address >> CARD_BITS
5120                         LDARG_0
5121                         LDC_I4 CARD_BITS
5122                         SHR_UN
5123                 if (SGEN_HAVE_OVERLAPPING_CARDS) {
5124                         LDC_PTR card_table_mask
5125                         AND
5126                 }
5127                 AND
5128                 ldc_i4_1
5129                 stind_i1
5130                 */
5131                 mono_mb_emit_ptr (mb, sgen_cardtable);
5132                 mono_mb_emit_ldarg (mb, 0);
5133                 mono_mb_emit_icon (mb, CARD_BITS);
5134                 mono_mb_emit_byte (mb, CEE_SHR_UN);
5135 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
5136                 mono_mb_emit_ptr (mb, (gpointer)CARD_MASK);
5137                 mono_mb_emit_byte (mb, CEE_AND);
5138 #endif
5139                 mono_mb_emit_byte (mb, CEE_ADD);
5140                 mono_mb_emit_icon (mb, 1);
5141                 mono_mb_emit_byte (mb, CEE_STIND_I1);
5142
5143                 // return;
5144                 for (i = 0; i < 3; ++i) {
5145                         if (nursery_check_labels [i])
5146                                 mono_mb_patch_branch (mb, nursery_check_labels [i]);
5147                 }               
5148                 mono_mb_emit_byte (mb, CEE_RET);
5149         } else if (mono_runtime_has_tls_get ()) {
5150                 emit_nursery_check (mb, nursery_check_labels);
5151
5152                 // if (ptr >= stack_end) goto need_wb;
5153                 mono_mb_emit_ldarg (mb, 0);
5154                 EMIT_TLS_ACCESS (mb, stack_end, stack_end_offset);
5155                 label_need_wb = mono_mb_emit_branch (mb, CEE_BGE_UN);
5156
5157                 // if (ptr >= stack_start) return;
5158                 dummy_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
5159                 mono_mb_emit_ldarg (mb, 0);
5160                 mono_mb_emit_ldloc_addr (mb, dummy_var);
5161                 label_no_wb_3 = mono_mb_emit_branch (mb, CEE_BGE_UN);
5162
5163                 // need_wb:
5164                 mono_mb_patch_branch (mb, label_need_wb);
5165
5166                 // buffer = STORE_REMSET_BUFFER;
5167                 buffer_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
5168                 EMIT_TLS_ACCESS (mb, store_remset_buffer, store_remset_buffer_offset);
5169                 mono_mb_emit_stloc (mb, buffer_var);
5170
5171                 // buffer_index = STORE_REMSET_BUFFER_INDEX;
5172                 buffer_index_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
5173                 EMIT_TLS_ACCESS (mb, store_remset_buffer_index, store_remset_buffer_index_offset);
5174                 mono_mb_emit_stloc (mb, buffer_index_var);
5175
5176                 // if (buffer [buffer_index] == ptr) return;
5177                 mono_mb_emit_ldloc (mb, buffer_var);
5178                 mono_mb_emit_ldloc (mb, buffer_index_var);
5179                 g_assert (sizeof (gpointer) == 4 || sizeof (gpointer) == 8);
5180                 mono_mb_emit_icon (mb, sizeof (gpointer) == 4 ? 2 : 3);
5181                 mono_mb_emit_byte (mb, CEE_SHL);
5182                 mono_mb_emit_byte (mb, CEE_ADD);
5183                 mono_mb_emit_byte (mb, CEE_LDIND_I);
5184                 mono_mb_emit_ldarg (mb, 0);
5185                 label_no_wb_4 = mono_mb_emit_branch (mb, CEE_BEQ);
5186
5187                 // ++buffer_index;
5188                 mono_mb_emit_ldloc (mb, buffer_index_var);
5189                 mono_mb_emit_icon (mb, 1);
5190                 mono_mb_emit_byte (mb, CEE_ADD);
5191                 mono_mb_emit_stloc (mb, buffer_index_var);
5192
5193                 // if (buffer_index >= STORE_REMSET_BUFFER_SIZE) goto slow_path;
5194                 mono_mb_emit_ldloc (mb, buffer_index_var);
5195                 mono_mb_emit_icon (mb, STORE_REMSET_BUFFER_SIZE);
5196                 label_slow_path = mono_mb_emit_branch (mb, CEE_BGE);
5197
5198                 // buffer [buffer_index] = ptr;
5199                 mono_mb_emit_ldloc (mb, buffer_var);
5200                 mono_mb_emit_ldloc (mb, buffer_index_var);
5201                 g_assert (sizeof (gpointer) == 4 || sizeof (gpointer) == 8);
5202                 mono_mb_emit_icon (mb, sizeof (gpointer) == 4 ? 2 : 3);
5203                 mono_mb_emit_byte (mb, CEE_SHL);
5204                 mono_mb_emit_byte (mb, CEE_ADD);
5205                 mono_mb_emit_ldarg (mb, 0);
5206                 mono_mb_emit_byte (mb, CEE_STIND_I);
5207
5208                 // STORE_REMSET_BUFFER_INDEX = buffer_index;
5209                 EMIT_TLS_ACCESS (mb, store_remset_buffer_index_addr, store_remset_buffer_index_addr_offset);
5210                 mono_mb_emit_ldloc (mb, buffer_index_var);
5211                 mono_mb_emit_byte (mb, CEE_STIND_I);
5212
5213                 // return;
5214                 for (i = 0; i < 3; ++i) {
5215                         if (nursery_check_labels [i])
5216                                 mono_mb_patch_branch (mb, nursery_check_labels [i]);
5217                 }
5218                 mono_mb_patch_branch (mb, label_no_wb_3);
5219                 mono_mb_patch_branch (mb, label_no_wb_4);
5220                 mono_mb_emit_byte (mb, CEE_RET);
5221
5222                 // slow path
5223                 mono_mb_patch_branch (mb, label_slow_path);
5224
5225                 mono_mb_emit_ldarg (mb, 0);
5226                 mono_mb_emit_icall (mb, mono_gc_wbarrier_generic_nostore);
5227                 mono_mb_emit_byte (mb, CEE_RET);
5228         } else
5229 #endif
5230         {
5231                 mono_mb_emit_ldarg (mb, 0);
5232                 mono_mb_emit_icall (mb, mono_gc_wbarrier_generic_nostore);
5233                 mono_mb_emit_byte (mb, CEE_RET);
5234         }
5235
5236         res = mono_mb_create_method (mb, sig, 16);
5237         mono_mb_free (mb);
5238
5239         mono_loader_lock ();
5240         if (write_barrier_method) {
5241                 /* Already created */
5242                 mono_free_method (res);
5243         } else {
5244                 /* double-checked locking */
5245                 mono_memory_barrier ();
5246                 write_barrier_method = res;
5247         }
5248         mono_loader_unlock ();
5249
5250         return write_barrier_method;
5251 }
5252
5253 char*
5254 mono_gc_get_description (void)
5255 {
5256         return g_strdup ("sgen");
5257 }
5258
5259 void
5260 mono_gc_set_desktop_mode (void)
5261 {
5262 }
5263
5264 gboolean
5265 mono_gc_is_moving (void)
5266 {
5267         return TRUE;
5268 }
5269
5270 gboolean
5271 mono_gc_is_disabled (void)
5272 {
5273         return FALSE;
5274 }
5275
5276 #ifdef HOST_WIN32
5277 BOOL APIENTRY mono_gc_dllmain (HMODULE module_handle, DWORD reason, LPVOID reserved)
5278 {
5279         return TRUE;
5280 }
5281 #endif
5282
5283 NurseryClearPolicy
5284 sgen_get_nursery_clear_policy (void)
5285 {
5286         return nursery_clear_policy;
5287 }
5288
5289 MonoVTable*
5290 sgen_get_array_fill_vtable (void)
5291 {
5292         if (!array_fill_vtable) {
5293                 static MonoClass klass;
5294                 static MonoVTable vtable;
5295                 gsize bmap;
5296
5297                 MonoDomain *domain = mono_get_root_domain ();
5298                 g_assert (domain);
5299
5300                 klass.element_class = mono_defaults.byte_class;
5301                 klass.rank = 1;
5302                 klass.instance_size = sizeof (MonoArray);
5303                 klass.sizes.element_size = 1;
5304                 klass.name = "array_filler_type";
5305
5306                 vtable.klass = &klass;
5307                 bmap = 0;
5308                 vtable.gc_descr = mono_gc_make_descr_for_array (TRUE, &bmap, 0, 1);
5309                 vtable.rank = 1;
5310
5311                 array_fill_vtable = &vtable;
5312         }
5313         return array_fill_vtable;
5314 }
5315
5316 void
5317 sgen_gc_lock (void)
5318 {
5319         LOCK_GC;
5320 }
5321
5322 void
5323 sgen_gc_unlock (void)
5324 {
5325         UNLOCK_GC;
5326 }
5327
5328 void
5329 sgen_major_collector_iterate_live_block_ranges (sgen_cardtable_block_callback callback)
5330 {
5331         major_collector.iterate_live_block_ranges (callback);
5332 }
5333
5334 void
5335 sgen_major_collector_scan_card_table (SgenGrayQueue *queue)
5336 {
5337         major_collector.scan_card_table (FALSE, queue);
5338 }
5339
5340 SgenMajorCollector*
5341 sgen_get_major_collector (void)
5342 {
5343         return &major_collector;
5344 }
5345
5346 void mono_gc_set_skip_thread (gboolean skip)
5347 {
5348         SgenThreadInfo *info = mono_thread_info_current ();
5349
5350         LOCK_GC;
5351         info->gc_disabled = skip;
5352         UNLOCK_GC;
5353 }
5354
5355 SgenRemeberedSet*
5356 sgen_get_remset (void)
5357 {
5358         return &remset;
5359 }
5360
5361 guint
5362 mono_gc_get_vtable_bits (MonoClass *class)
5363 {
5364         if (sgen_need_bridge_processing () && sgen_is_bridge_class (class))
5365                 return SGEN_GC_BIT_BRIDGE_OBJECT;
5366         return 0;
5367 }
5368
5369 void
5370 mono_gc_register_altstack (gpointer stack, gint32 stack_size, gpointer altstack, gint32 altstack_size)
5371 {
5372         // FIXME:
5373 }
5374
5375
5376 void
5377 sgen_check_whole_heap_stw (void)
5378 {
5379         sgen_stop_world (0);
5380         sgen_clear_nursery_fragments ();
5381         sgen_check_whole_heap ();
5382         sgen_restart_world (0, NULL);
5383 }
5384
5385 void
5386 sgen_gc_event_moves (void)
5387 {
5388         if (moved_objects_idx) {
5389                 mono_profiler_gc_moves (moved_objects, moved_objects_idx);
5390                 moved_objects_idx = 0;
5391         }
5392 }
5393
5394 #endif /* HAVE_SGEN_GC */