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