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