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