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