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