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