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