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