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