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