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