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