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