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