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