Add --enable-minimal= configure options to disable sgen features.
[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 *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                 DEBUG (4, fprintf (gc_debug_file, "Need to cleanup object %p\n", 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                         DEBUG (4, fprintf (gc_debug_file, "Cleaning up remote pointer in %p to object %p\n",
873                                         start, server));
874                         ((MonoRealProxy*)start)->unwrapped_server = NULL;
875                 }
876         }
877 }
878
879 static MonoDomain *check_domain = NULL;
880
881 static void
882 check_obj_not_in_domain (void **o)
883 {
884         g_assert (((MonoObject*)(*o))->vtable->domain != check_domain);
885 }
886
887 static void
888 scan_for_registered_roots_in_domain (MonoDomain *domain, int root_type)
889 {
890         void **start_root;
891         RootRecord *root;
892         check_domain = domain;
893         SGEN_HASH_TABLE_FOREACH (&roots_hash [root_type], start_root, root) {
894                 mword desc = root->root_desc;
895
896                 /* The MonoDomain struct is allowed to hold
897                    references to objects in its own domain. */
898                 if (start_root == (void**)domain)
899                         continue;
900
901                 switch (desc & ROOT_DESC_TYPE_MASK) {
902                 case ROOT_DESC_BITMAP:
903                         desc >>= ROOT_DESC_TYPE_SHIFT;
904                         while (desc) {
905                                 if ((desc & 1) && *start_root)
906                                         check_obj_not_in_domain (*start_root);
907                                 desc >>= 1;
908                                 start_root++;
909                         }
910                         break;
911                 case ROOT_DESC_COMPLEX: {
912                         gsize *bitmap_data = sgen_get_complex_descriptor_bitmap (desc);
913                         int bwords = (*bitmap_data) - 1;
914                         void **start_run = start_root;
915                         bitmap_data++;
916                         while (bwords-- > 0) {
917                                 gsize bmap = *bitmap_data++;
918                                 void **objptr = start_run;
919                                 while (bmap) {
920                                         if ((bmap & 1) && *objptr)
921                                                 check_obj_not_in_domain (*objptr);
922                                         bmap >>= 1;
923                                         ++objptr;
924                                 }
925                                 start_run += GC_BITS_PER_WORD;
926                         }
927                         break;
928                 }
929                 case ROOT_DESC_USER: {
930                         MonoGCRootMarkFunc marker = sgen_get_user_descriptor_func (desc);
931                         marker (start_root, check_obj_not_in_domain);
932                         break;
933                 }
934                 case ROOT_DESC_RUN_LEN:
935                         g_assert_not_reached ();
936                 default:
937                         g_assert_not_reached ();
938                 }
939         } SGEN_HASH_TABLE_FOREACH_END;
940
941         check_domain = NULL;
942 }
943
944 static void
945 check_for_xdomain_refs (void)
946 {
947         LOSObject *bigobj;
948
949         sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
950                         (IterateObjectCallbackFunc)scan_object_for_xdomain_refs, NULL, FALSE);
951
952         major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)scan_object_for_xdomain_refs, NULL);
953
954         for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
955                 scan_object_for_xdomain_refs (bigobj->data, bigobj->size, NULL);
956 }
957
958 static gboolean
959 clear_domain_process_object (char *obj, MonoDomain *domain)
960 {
961         gboolean remove;
962
963         process_object_for_domain_clearing (obj, domain);
964         remove = need_remove_object_for_domain (obj, domain);
965
966         if (remove && ((MonoObject*)obj)->synchronisation) {
967                 void **dislink = mono_monitor_get_object_monitor_weak_link ((MonoObject*)obj);
968                 if (dislink)
969                         sgen_register_disappearing_link (NULL, dislink, FALSE, TRUE);
970         }
971
972         return remove;
973 }
974
975 static void
976 clear_domain_process_minor_object_callback (char *obj, size_t size, MonoDomain *domain)
977 {
978         if (clear_domain_process_object (obj, domain))
979                 memset (obj, 0, size);
980 }
981
982 static void
983 clear_domain_process_major_object_callback (char *obj, size_t size, MonoDomain *domain)
984 {
985         clear_domain_process_object (obj, domain);
986 }
987
988 static void
989 clear_domain_free_major_non_pinned_object_callback (char *obj, size_t size, MonoDomain *domain)
990 {
991         if (need_remove_object_for_domain (obj, domain))
992                 major_collector.free_non_pinned_object (obj, size);
993 }
994
995 static void
996 clear_domain_free_major_pinned_object_callback (char *obj, size_t size, MonoDomain *domain)
997 {
998         if (need_remove_object_for_domain (obj, domain))
999                 major_collector.free_pinned_object (obj, size);
1000 }
1001
1002 /*
1003  * When appdomains are unloaded we can easily remove objects that have finalizers,
1004  * but all the others could still be present in random places on the heap.
1005  * We need a sweep to get rid of them even though it's going to be costly
1006  * with big heaps.
1007  * The reason we need to remove them is because we access the vtable and class
1008  * structures to know the object size and the reference bitmap: once the domain is
1009  * unloaded the point to random memory.
1010  */
1011 void
1012 mono_gc_clear_domain (MonoDomain * domain)
1013 {
1014         LOSObject *bigobj, *prev;
1015         int i;
1016
1017         LOCK_GC;
1018
1019         sgen_process_fin_stage_entries ();
1020         sgen_process_dislink_stage_entries ();
1021
1022         sgen_clear_nursery_fragments ();
1023
1024         if (xdomain_checks && domain != mono_get_root_domain ()) {
1025                 scan_for_registered_roots_in_domain (domain, ROOT_TYPE_NORMAL);
1026                 scan_for_registered_roots_in_domain (domain, ROOT_TYPE_WBARRIER);
1027                 check_for_xdomain_refs ();
1028         }
1029
1030         /*Ephemerons and dislinks must be processed before LOS since they might end up pointing
1031         to memory returned to the OS.*/
1032         null_ephemerons_for_domain (domain);
1033
1034         for (i = GENERATION_NURSERY; i < GENERATION_MAX; ++i)
1035                 sgen_null_links_for_domain (domain, i);
1036
1037         for (i = GENERATION_NURSERY; i < GENERATION_MAX; ++i)
1038                 sgen_remove_finalizers_for_domain (domain, i);
1039
1040         sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1041                         (IterateObjectCallbackFunc)clear_domain_process_minor_object_callback, domain, FALSE);
1042
1043         /* We need two passes over major and large objects because
1044            freeing such objects might give their memory back to the OS
1045            (in the case of large objects) or obliterate its vtable
1046            (pinned objects with major-copying or pinned and non-pinned
1047            objects with major-mark&sweep), but we might need to
1048            dereference a pointer from an object to another object if
1049            the first object is a proxy. */
1050         major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)clear_domain_process_major_object_callback, domain);
1051         for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1052                 clear_domain_process_object (bigobj->data, domain);
1053
1054         prev = NULL;
1055         for (bigobj = los_object_list; bigobj;) {
1056                 if (need_remove_object_for_domain (bigobj->data, domain)) {
1057                         LOSObject *to_free = bigobj;
1058                         if (prev)
1059                                 prev->next = bigobj->next;
1060                         else
1061                                 los_object_list = bigobj->next;
1062                         bigobj = bigobj->next;
1063                         DEBUG (4, fprintf (gc_debug_file, "Freeing large object %p\n",
1064                                         bigobj->data));
1065                         sgen_los_free_object (to_free);
1066                         continue;
1067                 }
1068                 prev = bigobj;
1069                 bigobj = bigobj->next;
1070         }
1071         major_collector.iterate_objects (TRUE, FALSE, (IterateObjectCallbackFunc)clear_domain_free_major_non_pinned_object_callback, domain);
1072         major_collector.iterate_objects (FALSE, TRUE, (IterateObjectCallbackFunc)clear_domain_free_major_pinned_object_callback, domain);
1073
1074         if (G_UNLIKELY (do_pin_stats)) {
1075                 if (domain == mono_get_root_domain ())
1076                         sgen_pin_stats_print_class_stats ();
1077         }
1078
1079         UNLOCK_GC;
1080 }
1081
1082 /*
1083  * sgen_add_to_global_remset:
1084  *
1085  *   The global remset contains locations which point into newspace after
1086  * a minor collection. This can happen if the objects they point to are pinned.
1087  *
1088  * LOCKING: If called from a parallel collector, the global remset
1089  * lock must be held.  For serial collectors that is not necessary.
1090  */
1091 void
1092 sgen_add_to_global_remset (gpointer ptr)
1093 {
1094         remset.record_pointer (ptr);
1095 }
1096
1097 /*
1098  * sgen_drain_gray_stack:
1099  *
1100  *   Scan objects in the gray stack until the stack is empty. This should be called
1101  * frequently after each object is copied, to achieve better locality and cache
1102  * usage.
1103  */
1104 gboolean
1105 sgen_drain_gray_stack (GrayQueue *queue, int max_objs)
1106 {
1107         char *obj;
1108         ScanObjectFunc scan_func = current_object_ops.scan_object;
1109
1110         if (max_objs == -1) {
1111                 for (;;) {
1112                         GRAY_OBJECT_DEQUEUE (queue, obj);
1113                         if (!obj)
1114                                 return TRUE;
1115                         DEBUG (9, fprintf (gc_debug_file, "Precise gray object scan %p (%s)\n", obj, safe_name (obj)));
1116                         scan_func (obj, queue);
1117                 }
1118         } else {
1119                 int i;
1120
1121                 do {
1122                         for (i = 0; i != max_objs; ++i) {
1123                                 GRAY_OBJECT_DEQUEUE (queue, obj);
1124                                 if (!obj)
1125                                         return TRUE;
1126                                 DEBUG (9, fprintf (gc_debug_file, "Precise gray object scan %p (%s)\n", obj, safe_name (obj)));
1127                                 scan_func (obj, queue);
1128                         }
1129                 } while (max_objs < 0);
1130                 return FALSE;
1131         }
1132 }
1133
1134 /*
1135  * Addresses from start to end are already sorted. This function finds
1136  * the object header for each address and pins the object. The
1137  * addresses must be inside the passed section.  The (start of the)
1138  * address array is overwritten with the addresses of the actually
1139  * pinned objects.  Return the number of pinned objects.
1140  */
1141 static int
1142 pin_objects_from_addresses (GCMemSection *section, void **start, void **end, void *start_nursery, void *end_nursery, GrayQueue *queue)
1143 {
1144         void *last = NULL;
1145         int count = 0;
1146         void *search_start;
1147         void *last_obj = NULL;
1148         size_t last_obj_size = 0;
1149         void *addr;
1150         int idx;
1151         void **definitely_pinned = start;
1152
1153         sgen_nursery_allocator_prepare_for_pinning ();
1154
1155         while (start < end) {
1156                 addr = *start;
1157                 /* the range check should be reduntant */
1158                 if (addr != last && addr >= start_nursery && addr < end_nursery) {
1159                         DEBUG (5, fprintf (gc_debug_file, "Considering pinning addr %p\n", addr));
1160                         /* multiple pointers to the same object */
1161                         if (addr >= last_obj && (char*)addr < (char*)last_obj + last_obj_size) {
1162                                 start++;
1163                                 continue;
1164                         }
1165                         idx = ((char*)addr - (char*)section->data) / SCAN_START_SIZE;
1166                         g_assert (idx < section->num_scan_start);
1167                         search_start = (void*)section->scan_starts [idx];
1168                         if (!search_start || search_start > addr) {
1169                                 while (idx) {
1170                                         --idx;
1171                                         search_start = section->scan_starts [idx];
1172                                         if (search_start && search_start <= addr)
1173                                                 break;
1174                                 }
1175                                 if (!search_start || search_start > addr)
1176                                         search_start = start_nursery;
1177                         }
1178                         if (search_start < last_obj)
1179                                 search_start = (char*)last_obj + last_obj_size;
1180                         /* now addr should be in an object a short distance from search_start
1181                          * Note that search_start must point to zeroed mem or point to an object.
1182                          */
1183
1184                         do {
1185                                 if (!*(void**)search_start) {
1186                                         /* Consistency check */
1187                                         /*
1188                                         for (frag = nursery_fragments; frag; frag = frag->next) {
1189                                                 if (search_start >= frag->fragment_start && search_start < frag->fragment_end)
1190                                                         g_assert_not_reached ();
1191                                         }
1192                                         */
1193
1194                                         search_start = (void*)ALIGN_UP ((mword)search_start + sizeof (gpointer));
1195                                         continue;
1196                                 }
1197                                 last_obj = search_start;
1198                                 last_obj_size = ALIGN_UP (safe_object_get_size ((MonoObject*)search_start));
1199
1200                                 if (((MonoObject*)last_obj)->synchronisation == GINT_TO_POINTER (-1)) {
1201                                         /* Marks the beginning of a nursery fragment, skip */
1202                                 } else {
1203                                         DEBUG (8, fprintf (gc_debug_file, "Pinned try match %p (%s), size %zd\n", last_obj, safe_name (last_obj), last_obj_size));
1204                                         if (addr >= search_start && (char*)addr < (char*)last_obj + last_obj_size) {
1205                                                 DEBUG (4, fprintf (gc_debug_file, "Pinned object %p, vtable %p (%s), count %d\n", search_start, *(void**)search_start, safe_name (search_start), count));
1206                                                 binary_protocol_pin (search_start, (gpointer)LOAD_VTABLE (search_start), safe_object_get_size (search_start));
1207                                                 if (G_UNLIKELY (MONO_GC_OBJ_PINNED_ENABLED ())) {
1208                                                         int gen = sgen_ptr_in_nursery (search_start) ? GENERATION_NURSERY : GENERATION_OLD;
1209                                                         MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (search_start);
1210                                                         MONO_GC_OBJ_PINNED ((mword)search_start, sgen_safe_object_get_size (search_start), vt->klass->name_space, vt->klass->name, gen);
1211                                                 }
1212                                                 pin_object (search_start);
1213                                                 GRAY_OBJECT_ENQUEUE (queue, search_start);
1214                                                 if (G_UNLIKELY (do_pin_stats))
1215                                                         sgen_pin_stats_register_object (search_start, last_obj_size);
1216                                                 definitely_pinned [count] = search_start;
1217                                                 count++;
1218                                                 break;
1219                                         }
1220                                 }
1221                                 /* skip to the next object */
1222                                 search_start = (void*)((char*)search_start + last_obj_size);
1223                         } while (search_start <= addr);
1224                         /* we either pinned the correct object or we ignored the addr because
1225                          * it points to unused zeroed memory.
1226                          */
1227                         last = addr;
1228                 }
1229                 start++;
1230         }
1231         //printf ("effective pinned: %d (at the end: %d)\n", count, (char*)end_nursery - (char*)last);
1232         if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS) {
1233                 GCRootReport report;
1234                 report.count = 0;
1235                 for (idx = 0; idx < count; ++idx)
1236                         add_profile_gc_root (&report, definitely_pinned [idx], MONO_PROFILE_GC_ROOT_PINNING | MONO_PROFILE_GC_ROOT_MISC, 0);
1237                 notify_gc_roots (&report);
1238         }
1239         stat_pinned_objects += count;
1240         return count;
1241 }
1242
1243 void
1244 sgen_pin_objects_in_section (GCMemSection *section, GrayQueue *queue)
1245 {
1246         int num_entries = section->pin_queue_num_entries;
1247         if (num_entries) {
1248                 void **start = section->pin_queue_start;
1249                 int reduced_to;
1250                 reduced_to = pin_objects_from_addresses (section, start, start + num_entries,
1251                                 section->data, section->next_data, queue);
1252                 section->pin_queue_num_entries = reduced_to;
1253                 if (!reduced_to)
1254                         section->pin_queue_start = NULL;
1255         }
1256 }
1257
1258
1259 void
1260 sgen_pin_object (void *object, GrayQueue *queue)
1261 {
1262         if (sgen_collection_is_parallel ()) {
1263                 LOCK_PIN_QUEUE;
1264                 /*object arrives pinned*/
1265                 sgen_pin_stage_ptr (object);
1266                 ++objects_pinned ;
1267                 UNLOCK_PIN_QUEUE;
1268         } else {
1269                 SGEN_PIN_OBJECT (object);
1270                 sgen_pin_stage_ptr (object);
1271                 ++objects_pinned;
1272                 if (G_UNLIKELY (do_pin_stats))
1273                         sgen_pin_stats_register_object (object, safe_object_get_size (object));
1274         }
1275         GRAY_OBJECT_ENQUEUE (queue, object);
1276         binary_protocol_pin (object, (gpointer)LOAD_VTABLE (object), safe_object_get_size (object));
1277         if (G_UNLIKELY (MONO_GC_OBJ_PINNED_ENABLED ())) {
1278                 int gen = sgen_ptr_in_nursery (object) ? GENERATION_NURSERY : GENERATION_OLD;
1279                 MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (object);
1280                 MONO_GC_OBJ_PINNED ((mword)object, sgen_safe_object_get_size (object), vt->klass->name_space, vt->klass->name, gen);
1281         }
1282 }
1283
1284 void
1285 sgen_parallel_pin_or_update (void **ptr, void *obj, MonoVTable *vt, SgenGrayQueue *queue)
1286 {
1287         for (;;) {
1288                 mword vtable_word;
1289                 gboolean major_pinned = FALSE;
1290
1291                 if (sgen_ptr_in_nursery (obj)) {
1292                         if (SGEN_CAS_PTR (obj, (void*)((mword)vt | SGEN_PINNED_BIT), vt) == vt) {
1293                                 sgen_pin_object (obj, queue);
1294                                 break;
1295                         }
1296                 } else {
1297                         major_collector.pin_major_object (obj, queue);
1298                         major_pinned = TRUE;
1299                 }
1300
1301                 vtable_word = *(mword*)obj;
1302                 /*someone else forwarded it, update the pointer and bail out*/
1303                 if (vtable_word & SGEN_FORWARDED_BIT) {
1304                         *ptr = (void*)(vtable_word & ~SGEN_VTABLE_BITS_MASK);
1305                         break;
1306                 }
1307
1308                 /*someone pinned it, nothing to do.*/
1309                 if (vtable_word & SGEN_PINNED_BIT || major_pinned)
1310                         break;
1311         }
1312 }
1313
1314 /* Sort the addresses in array in increasing order.
1315  * Done using a by-the book heap sort. Which has decent and stable performance, is pretty cache efficient.
1316  */
1317 void
1318 sgen_sort_addresses (void **array, int size)
1319 {
1320         int i;
1321         void *tmp;
1322
1323         for (i = 1; i < size; ++i) {
1324                 int child = i;
1325                 while (child > 0) {
1326                         int parent = (child - 1) / 2;
1327
1328                         if (array [parent] >= array [child])
1329                                 break;
1330
1331                         tmp = array [parent];
1332                         array [parent] = array [child];
1333                         array [child] = tmp;
1334
1335                         child = parent;
1336                 }
1337         }
1338
1339         for (i = size - 1; i > 0; --i) {
1340                 int end, root;
1341                 tmp = array [i];
1342                 array [i] = array [0];
1343                 array [0] = tmp;
1344
1345                 end = i - 1;
1346                 root = 0;
1347
1348                 while (root * 2 + 1 <= end) {
1349                         int child = root * 2 + 1;
1350
1351                         if (child < end && array [child] < array [child + 1])
1352                                 ++child;
1353                         if (array [root] >= array [child])
1354                                 break;
1355
1356                         tmp = array [root];
1357                         array [root] = array [child];
1358                         array [child] = tmp;
1359
1360                         root = child;
1361                 }
1362         }
1363 }
1364
1365 /* 
1366  * Scan the memory between start and end and queue values which could be pointers
1367  * to the area between start_nursery and end_nursery for later consideration.
1368  * Typically used for thread stacks.
1369  */
1370 static void
1371 conservatively_pin_objects_from (void **start, void **end, void *start_nursery, void *end_nursery, int pin_type)
1372 {
1373         int count = 0;
1374
1375 #ifdef VALGRIND_MAKE_MEM_DEFINED_IF_ADDRESSABLE
1376         VALGRIND_MAKE_MEM_DEFINED_IF_ADDRESSABLE (start, (char*)end - (char*)start);
1377 #endif
1378
1379         while (start < end) {
1380                 if (*start >= start_nursery && *start < end_nursery) {
1381                         /*
1382                          * *start can point to the middle of an object
1383                          * note: should we handle pointing at the end of an object?
1384                          * pinning in C# code disallows pointing at the end of an object
1385                          * but there is some small chance that an optimizing C compiler
1386                          * may keep the only reference to an object by pointing
1387                          * at the end of it. We ignore this small chance for now.
1388                          * Pointers to the end of an object are indistinguishable
1389                          * from pointers to the start of the next object in memory
1390                          * so if we allow that we'd need to pin two objects...
1391                          * We queue the pointer in an array, the
1392                          * array will then be sorted and uniqued. This way
1393                          * we can coalesce several pinning pointers and it should
1394                          * be faster since we'd do a memory scan with increasing
1395                          * addresses. Note: we can align the address to the allocation
1396                          * alignment, so the unique process is more effective.
1397                          */
1398                         mword addr = (mword)*start;
1399                         addr &= ~(ALLOC_ALIGN - 1);
1400                         if (addr >= (mword)start_nursery && addr < (mword)end_nursery)
1401                                 sgen_pin_stage_ptr ((void*)addr);
1402                         if (G_UNLIKELY (do_pin_stats)) { 
1403                                 if (ptr_in_nursery ((void*)addr))
1404                                         sgen_pin_stats_register_address ((char*)addr, pin_type);
1405                         }
1406                         DEBUG (6, if (count) fprintf (gc_debug_file, "Pinning address %p from %p\n", (void*)addr, start));
1407                         count++;
1408                 }
1409                 start++;
1410         }
1411         DEBUG (7, if (count) fprintf (gc_debug_file, "found %d potential pinned heap pointers\n", 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         DEBUG (2, fprintf (gc_debug_file, "Scanning pinned roots (%d bytes, %d/%d entries)\n", (int)roots_size, roots_hash [ROOT_TYPE_NORMAL].num_entries, roots_hash [ROOT_TYPE_PINNED].num_entries));
1425         /* objects pinned from the API are inside these roots */
1426         SGEN_HASH_TABLE_FOREACH (&roots_hash [ROOT_TYPE_PINNED], start_root, root) {
1427                 DEBUG (6, fprintf (gc_debug_file, "Pinned roots %p-%p\n", 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                                 DEBUG (9, fprintf (gc_debug_file, "Overwrote root at %p with %p\n", 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                                         DEBUG (9, fprintf (gc_debug_file, "Overwrote root at %p with %p\n", 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         DEBUG (2, fprintf (gc_debug_file, "Allocating nursery size: %lu\n", (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         DEBUG (4, fprintf (gc_debug_file, "Expanding nursery size (%p-%p): %lu, total: %lu\n", data, data + alloc_size, (unsigned long)sgen_nursery_size, (unsigned long)mono_gc_get_heap_size ()));
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 sgen_get_logfile ();
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                 DEBUG (6, fprintf (gc_debug_file, "Precise root scan %p-%p (desc: %p)\n", 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                 DEBUG (5, fprintf (gc_debug_file, "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         DEBUG (2, fprintf (gc_debug_file, "%s generation done\n", 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         DEBUG (6, fprintf (gc_debug_file, "Precise scan of gray area post fin\n"));
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         DEBUG (2, fprintf (gc_debug_file, "Finalize queue handling scan for %s generation: %d usecs %d ephemeron roundss\n", generation_name (generation), TV_ELAPSED (atv, btv), ephemeron_rounds));
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                 DEBUG (6, fprintf (gc_debug_file, "Precise root scan %p-%p (desc: %p)\n", 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                         fprintf (gc_debug_file, "NFC-BAD SCAN START [%d] %p for obj [%p %p]\n", 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                         fprintf (gc_debug_file, "FORWARDED OBJ %p\n", cur);
2257                 else if (object_is_pinned (cur))
2258                         fprintf (gc_debug_file, "PINNED OBJ %p\n", 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                                 fprintf (gc_debug_file, "HOLE [%p %p %d]\n", hole_start, cur, (int)(cur - hole_start));
2266                         fprintf (gc_debug_file, "OBJ  [%p %p %d %d %s %d]\n", cur, cur + size, (int)size, (int)ss, sgen_safe_name ((MonoObject*)cur), (gpointer)LOAD_VTABLE (cur) == sgen_get_array_fill_vtable ());
2267                 }
2268                 cur += size;
2269                 hole_start = cur;
2270         }
2271         fflush (gc_debug_file);
2272 }
2273
2274 /*
2275  * Collect objects in the nursery.  Returns whether to trigger a major
2276  * collection.
2277  */
2278 static gboolean
2279 collect_nursery (void)
2280 {
2281         gboolean needs_major;
2282         size_t max_garbage_amount;
2283         char *nursery_next;
2284         FinishRememberedSetScanJobData frssjd;
2285         ScanFromRegisteredRootsJobData scrrjd_normal, scrrjd_wbarrier;
2286         ScanFinalizerEntriesJobData sfejd_fin_ready, sfejd_critical_fin;
2287         ScanThreadDataJobData stdjd;
2288         mword fragment_total;
2289         TV_DECLARE (all_atv);
2290         TV_DECLARE (all_btv);
2291         TV_DECLARE (atv);
2292         TV_DECLARE (btv);
2293
2294         if (disable_minor_collections)
2295                 return TRUE;
2296
2297         MONO_GC_BEGIN (GENERATION_NURSERY);
2298
2299         verify_nursery ();
2300
2301 #ifndef DISABLE_PERFCOUNTERS
2302         mono_perfcounters->gc_collections0++;
2303 #endif
2304
2305         current_collection_generation = GENERATION_NURSERY;
2306         if (sgen_collection_is_parallel ())
2307                 current_object_ops = sgen_minor_collector.parallel_ops;
2308         else
2309                 current_object_ops = sgen_minor_collector.serial_ops;
2310         
2311         reset_pinned_from_failed_allocation ();
2312
2313         binary_protocol_collection (stat_minor_gcs, GENERATION_NURSERY);
2314         check_scan_starts ();
2315
2316         sgen_nursery_alloc_prepare_for_minor ();
2317
2318         degraded_mode = 0;
2319         objects_pinned = 0;
2320         nursery_next = sgen_nursery_alloc_get_upper_alloc_bound ();
2321         /* FIXME: optimize later to use the higher address where an object can be present */
2322         nursery_next = MAX (nursery_next, sgen_get_nursery_end ());
2323
2324         DEBUG (1, fprintf (gc_debug_file, "Start nursery collection %d %p-%p, size: %d\n", stat_minor_gcs, sgen_get_nursery_start (), nursery_next, (int)(nursery_next - sgen_get_nursery_start ())));
2325         max_garbage_amount = nursery_next - sgen_get_nursery_start ();
2326         g_assert (nursery_section->size >= max_garbage_amount);
2327
2328         /* world must be stopped already */
2329         TV_GETTIME (all_atv);
2330         atv = all_atv;
2331
2332         TV_GETTIME (btv);
2333         time_minor_pre_collection_fragment_clear += TV_ELAPSED (atv, btv);
2334
2335         if (xdomain_checks) {
2336                 sgen_clear_nursery_fragments ();
2337                 check_for_xdomain_refs ();
2338         }
2339
2340         nursery_section->next_data = nursery_next;
2341
2342         major_collector.start_nursery_collection ();
2343
2344         sgen_memgov_minor_collection_start ();
2345
2346         sgen_gray_object_queue_init (&gray_queue);
2347         sgen_workers_init_distribute_gray_queue ();
2348
2349         stat_minor_gcs++;
2350         gc_stats.minor_gc_count ++;
2351
2352         if (remset.prepare_for_minor_collection)
2353                 remset.prepare_for_minor_collection ();
2354
2355         sgen_process_fin_stage_entries ();
2356         sgen_process_dislink_stage_entries ();
2357
2358         /* pin from pinned handles */
2359         sgen_init_pinning ();
2360         mono_profiler_gc_event (MONO_GC_EVENT_MARK_START, 0);
2361         pin_from_roots (sgen_get_nursery_start (), nursery_next, WORKERS_DISTRIBUTE_GRAY_QUEUE);
2362         /* identify pinned objects */
2363         sgen_optimize_pin_queue (0);
2364         sgen_pinning_setup_section (nursery_section);
2365         sgen_pin_objects_in_section (nursery_section, WORKERS_DISTRIBUTE_GRAY_QUEUE);   
2366         sgen_pinning_trim_queue_to_section (nursery_section);
2367
2368         TV_GETTIME (atv);
2369         time_minor_pinning += TV_ELAPSED (btv, atv);
2370         DEBUG (2, fprintf (gc_debug_file, "Finding pinned pointers: %d in %d usecs\n", sgen_get_pinned_count (), TV_ELAPSED (btv, atv)));
2371         DEBUG (4, fprintf (gc_debug_file, "Start scan with %d pinned objects\n", sgen_get_pinned_count ()));
2372
2373         if (whole_heap_check_before_collection)
2374                 sgen_check_whole_heap ();
2375         if (consistency_check_at_minor_collection)
2376                 sgen_check_consistency ();
2377
2378         sgen_workers_start_all_workers ();
2379
2380         /*
2381          * Perform the sequential part of remembered set scanning.
2382          * This usually involves scanning global information that might later be produced by evacuation.
2383          */
2384         if (remset.begin_scan_remsets)
2385                 remset.begin_scan_remsets (sgen_get_nursery_start (), nursery_next, WORKERS_DISTRIBUTE_GRAY_QUEUE);
2386
2387         sgen_workers_start_marking ();
2388
2389         frssjd.heap_start = sgen_get_nursery_start ();
2390         frssjd.heap_end = nursery_next;
2391         sgen_workers_enqueue_job (job_finish_remembered_set_scan, &frssjd);
2392
2393         /* we don't have complete write barrier yet, so we scan all the old generation sections */
2394         TV_GETTIME (btv);
2395         time_minor_scan_remsets += TV_ELAPSED (atv, btv);
2396         DEBUG (2, fprintf (gc_debug_file, "Old generation scan: %d usecs\n", TV_ELAPSED (atv, btv)));
2397
2398         if (!sgen_collection_is_parallel ())
2399                 sgen_drain_gray_stack (&gray_queue, -1);
2400
2401         if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
2402                 report_registered_roots ();
2403         if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
2404                 report_finalizer_roots ();
2405         TV_GETTIME (atv);
2406         time_minor_scan_pinned += TV_ELAPSED (btv, atv);
2407
2408         /* registered roots, this includes static fields */
2409         scrrjd_normal.func = current_object_ops.copy_or_mark_object;
2410         scrrjd_normal.heap_start = sgen_get_nursery_start ();
2411         scrrjd_normal.heap_end = nursery_next;
2412         scrrjd_normal.root_type = ROOT_TYPE_NORMAL;
2413         sgen_workers_enqueue_job (job_scan_from_registered_roots, &scrrjd_normal);
2414
2415         scrrjd_wbarrier.func = current_object_ops.copy_or_mark_object;
2416         scrrjd_wbarrier.heap_start = sgen_get_nursery_start ();
2417         scrrjd_wbarrier.heap_end = nursery_next;
2418         scrrjd_wbarrier.root_type = ROOT_TYPE_WBARRIER;
2419         sgen_workers_enqueue_job (job_scan_from_registered_roots, &scrrjd_wbarrier);
2420
2421         TV_GETTIME (btv);
2422         time_minor_scan_registered_roots += TV_ELAPSED (atv, btv);
2423
2424         /* thread data */
2425         stdjd.heap_start = sgen_get_nursery_start ();
2426         stdjd.heap_end = nursery_next;
2427         sgen_workers_enqueue_job (job_scan_thread_data, &stdjd);
2428
2429         TV_GETTIME (atv);
2430         time_minor_scan_thread_data += TV_ELAPSED (btv, atv);
2431         btv = atv;
2432
2433         if (sgen_collection_is_parallel ()) {
2434                 while (!sgen_gray_object_queue_is_empty (WORKERS_DISTRIBUTE_GRAY_QUEUE)) {
2435                         sgen_workers_distribute_gray_queue_sections ();
2436                         g_usleep (1000);
2437                 }
2438         }
2439         sgen_workers_join ();
2440
2441         if (sgen_collection_is_parallel ())
2442                 g_assert (sgen_gray_object_queue_is_empty (&gray_queue));
2443
2444         /* Scan the list of objects ready for finalization. If */
2445         sfejd_fin_ready.list = fin_ready_list;
2446         sgen_workers_enqueue_job (job_scan_finalizer_entries, &sfejd_fin_ready);
2447
2448         sfejd_critical_fin.list = critical_fin_list;
2449         sgen_workers_enqueue_job (job_scan_finalizer_entries, &sfejd_critical_fin);
2450
2451         finish_gray_stack (sgen_get_nursery_start (), nursery_next, GENERATION_NURSERY, &gray_queue);
2452         TV_GETTIME (atv);
2453         time_minor_finish_gray_stack += TV_ELAPSED (btv, atv);
2454         mono_profiler_gc_event (MONO_GC_EVENT_MARK_END, 0);
2455
2456         /*
2457          * The (single-threaded) finalization code might have done
2458          * some copying/marking so we can only reset the GC thread's
2459          * worker data here instead of earlier when we joined the
2460          * workers.
2461          */
2462         sgen_workers_reset_data ();
2463
2464         if (objects_pinned) {
2465                 sgen_optimize_pin_queue (0);
2466                 sgen_pinning_setup_section (nursery_section);
2467         }
2468
2469         /* walk the pin_queue, build up the fragment list of free memory, unmark
2470          * pinned objects as we go, memzero() the empty fragments so they are ready for the
2471          * next allocations.
2472          */
2473         mono_profiler_gc_event (MONO_GC_EVENT_RECLAIM_START, 0);
2474         fragment_total = sgen_build_nursery_fragments (nursery_section, nursery_section->pin_queue_start, nursery_section->pin_queue_num_entries);
2475         if (!fragment_total)
2476                 degraded_mode = 1;
2477
2478         /* Clear TLABs for all threads */
2479         sgen_clear_tlabs ();
2480
2481         mono_profiler_gc_event (MONO_GC_EVENT_RECLAIM_END, 0);
2482         TV_GETTIME (btv);
2483         time_minor_fragment_creation += TV_ELAPSED (atv, btv);
2484         DEBUG (2, fprintf (gc_debug_file, "Fragment creation: %d usecs, %lu bytes available\n", TV_ELAPSED (atv, btv), (unsigned long)fragment_total));
2485
2486         if (consistency_check_at_minor_collection)
2487                 sgen_check_major_refs ();
2488
2489         major_collector.finish_nursery_collection ();
2490
2491         TV_GETTIME (all_btv);
2492         gc_stats.minor_gc_time_usecs += TV_ELAPSED (all_atv, all_btv);
2493
2494         if (heap_dump_file)
2495                 dump_heap ("minor", stat_minor_gcs - 1, NULL);
2496
2497         /* prepare the pin queue for the next collection */
2498         sgen_finish_pinning ();
2499         if (fin_ready_list || critical_fin_list) {
2500                 DEBUG (4, fprintf (gc_debug_file, "Finalizer-thread wakeup: ready %d\n", num_ready_finalizers));
2501                 mono_gc_finalize_notify ();
2502         }
2503         sgen_pin_stats_reset ();
2504
2505         g_assert (sgen_gray_object_queue_is_empty (&gray_queue));
2506
2507         if (remset.finish_minor_collection)
2508                 remset.finish_minor_collection ();
2509
2510         check_scan_starts ();
2511
2512         binary_protocol_flush_buffers (FALSE);
2513
2514         sgen_memgov_minor_collection_end ();
2515
2516         /*objects are late pinned because of lack of memory, so a major is a good call*/
2517         needs_major = objects_pinned > 0;
2518         current_collection_generation = -1;
2519         objects_pinned = 0;
2520
2521         MONO_GC_END (GENERATION_NURSERY);
2522
2523         return needs_major;
2524 }
2525
2526 static gboolean
2527 major_do_collection (const char *reason)
2528 {
2529         LOSObject *bigobj, *prevbo;
2530         TV_DECLARE (all_atv);
2531         TV_DECLARE (all_btv);
2532         TV_DECLARE (atv);
2533         TV_DECLARE (btv);
2534         /* FIXME: only use these values for the precise scan
2535          * note that to_space pointers should be excluded anyway...
2536          */
2537         char *heap_start = NULL;
2538         char *heap_end = (char*)-1;
2539         int old_next_pin_slot;
2540         ScanFromRegisteredRootsJobData scrrjd_normal, scrrjd_wbarrier;
2541         ScanThreadDataJobData stdjd;
2542         ScanFinalizerEntriesJobData sfejd_fin_ready, sfejd_critical_fin;
2543
2544         MONO_GC_BEGIN (GENERATION_OLD);
2545
2546         current_collection_generation = GENERATION_OLD;
2547 #ifndef DISABLE_PERFCOUNTERS
2548         mono_perfcounters->gc_collections1++;
2549 #endif
2550
2551         current_object_ops = major_collector.major_ops;
2552
2553         reset_pinned_from_failed_allocation ();
2554
2555         sgen_memgov_major_collection_start ();
2556
2557         //count_ref_nonref_objs ();
2558         //consistency_check ();
2559
2560         binary_protocol_collection (stat_major_gcs, GENERATION_OLD);
2561         check_scan_starts ();
2562
2563         sgen_gray_object_queue_init (&gray_queue);
2564         sgen_workers_init_distribute_gray_queue ();
2565         sgen_nursery_alloc_prepare_for_major ();
2566
2567         degraded_mode = 0;
2568         DEBUG (1, fprintf (gc_debug_file, "Start major collection %d\n", stat_major_gcs));
2569         stat_major_gcs++;
2570         gc_stats.major_gc_count ++;
2571
2572         /* world must be stopped already */
2573         TV_GETTIME (all_atv);
2574         atv = all_atv;
2575
2576         /* Pinning depends on this */
2577         sgen_clear_nursery_fragments ();
2578
2579         if (whole_heap_check_before_collection)
2580                 sgen_check_whole_heap ();
2581
2582         TV_GETTIME (btv);
2583         time_major_pre_collection_fragment_clear += TV_ELAPSED (atv, btv);
2584
2585         nursery_section->next_data = sgen_get_nursery_end ();
2586         /* we should also coalesce scanning from sections close to each other
2587          * and deal with pointers outside of the sections later.
2588          */
2589
2590         if (major_collector.start_major_collection)
2591                 major_collector.start_major_collection ();
2592
2593         objects_pinned = 0;
2594         *major_collector.have_swept = FALSE;
2595
2596         if (xdomain_checks) {
2597                 sgen_clear_nursery_fragments ();
2598                 check_for_xdomain_refs ();
2599         }
2600
2601         /* Remsets are not useful for a major collection */
2602         remset.prepare_for_major_collection ();
2603
2604         sgen_process_fin_stage_entries ();
2605         sgen_process_dislink_stage_entries ();
2606
2607         TV_GETTIME (atv);
2608         sgen_init_pinning ();
2609         DEBUG (6, fprintf (gc_debug_file, "Collecting pinned addresses\n"));
2610         pin_from_roots ((void*)lowest_heap_address, (void*)highest_heap_address, WORKERS_DISTRIBUTE_GRAY_QUEUE);
2611         sgen_optimize_pin_queue (0);
2612
2613         /*
2614          * pin_queue now contains all candidate pointers, sorted and
2615          * uniqued.  We must do two passes now to figure out which
2616          * objects are pinned.
2617          *
2618          * The first is to find within the pin_queue the area for each
2619          * section.  This requires that the pin_queue be sorted.  We
2620          * also process the LOS objects and pinned chunks here.
2621          *
2622          * The second, destructive, pass is to reduce the section
2623          * areas to pointers to the actually pinned objects.
2624          */
2625         DEBUG (6, fprintf (gc_debug_file, "Pinning from sections\n"));
2626         /* first pass for the sections */
2627         sgen_find_section_pin_queue_start_end (nursery_section);
2628         major_collector.find_pin_queue_start_ends (WORKERS_DISTRIBUTE_GRAY_QUEUE);
2629         /* identify possible pointers to the insize of large objects */
2630         DEBUG (6, fprintf (gc_debug_file, "Pinning from large objects\n"));
2631         for (bigobj = los_object_list; bigobj; bigobj = bigobj->next) {
2632                 int dummy;
2633                 gboolean profile_roots = mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS;
2634                 GCRootReport report;
2635                 report.count = 0;
2636                 if (sgen_find_optimized_pin_queue_area (bigobj->data, (char*)bigobj->data + bigobj->size, &dummy)) {
2637                         binary_protocol_pin (bigobj->data, (gpointer)LOAD_VTABLE (bigobj->data), safe_object_get_size (((MonoObject*)(bigobj->data))));
2638                         if (G_UNLIKELY (MONO_GC_OBJ_PINNED_ENABLED ())) {
2639                                 MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (bigobj->data);
2640                                 MONO_GC_OBJ_PINNED ((mword)bigobj->data, sgen_safe_object_get_size ((MonoObject*)bigobj->data), vt->klass->name_space, vt->klass->name, GENERATION_OLD);
2641                         }
2642                         pin_object (bigobj->data);
2643                         /* FIXME: only enqueue if object has references */
2644                         GRAY_OBJECT_ENQUEUE (WORKERS_DISTRIBUTE_GRAY_QUEUE, bigobj->data);
2645                         if (G_UNLIKELY (do_pin_stats))
2646                                 sgen_pin_stats_register_object ((char*) bigobj->data, safe_object_get_size ((MonoObject*) bigobj->data));
2647                         DEBUG (6, fprintf (gc_debug_file, "Marked large object %p (%s) size: %lu from roots\n", bigobj->data, safe_name (bigobj->data), (unsigned long)bigobj->size));
2648                         
2649                         if (profile_roots)
2650                                 add_profile_gc_root (&report, bigobj->data, MONO_PROFILE_GC_ROOT_PINNING | MONO_PROFILE_GC_ROOT_MISC, 0);
2651                 }
2652                 if (profile_roots)
2653                         notify_gc_roots (&report);
2654         }
2655         /* second pass for the sections */
2656         sgen_pin_objects_in_section (nursery_section, WORKERS_DISTRIBUTE_GRAY_QUEUE);
2657         major_collector.pin_objects (WORKERS_DISTRIBUTE_GRAY_QUEUE);
2658         old_next_pin_slot = sgen_get_pinned_count ();
2659
2660         TV_GETTIME (btv);
2661         time_major_pinning += TV_ELAPSED (atv, btv);
2662         DEBUG (2, fprintf (gc_debug_file, "Finding pinned pointers: %d in %d usecs\n", sgen_get_pinned_count (), TV_ELAPSED (atv, btv)));
2663         DEBUG (4, fprintf (gc_debug_file, "Start scan with %d pinned objects\n", sgen_get_pinned_count ()));
2664
2665         major_collector.init_to_space ();
2666
2667 #ifdef SGEN_DEBUG_INTERNAL_ALLOC
2668         main_gc_thread = mono_native_thread_self ();
2669 #endif
2670
2671         sgen_workers_start_all_workers ();
2672         sgen_workers_start_marking ();
2673
2674         if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
2675                 report_registered_roots ();
2676         TV_GETTIME (atv);
2677         time_major_scan_pinned += TV_ELAPSED (btv, atv);
2678
2679         /* registered roots, this includes static fields */
2680         scrrjd_normal.func = current_object_ops.copy_or_mark_object;
2681         scrrjd_normal.heap_start = heap_start;
2682         scrrjd_normal.heap_end = heap_end;
2683         scrrjd_normal.root_type = ROOT_TYPE_NORMAL;
2684         sgen_workers_enqueue_job (job_scan_from_registered_roots, &scrrjd_normal);
2685
2686         scrrjd_wbarrier.func = current_object_ops.copy_or_mark_object;
2687         scrrjd_wbarrier.heap_start = heap_start;
2688         scrrjd_wbarrier.heap_end = heap_end;
2689         scrrjd_wbarrier.root_type = ROOT_TYPE_WBARRIER;
2690         sgen_workers_enqueue_job (job_scan_from_registered_roots, &scrrjd_wbarrier);
2691
2692         TV_GETTIME (btv);
2693         time_major_scan_registered_roots += TV_ELAPSED (atv, btv);
2694
2695         /* Threads */
2696         stdjd.heap_start = heap_start;
2697         stdjd.heap_end = heap_end;
2698         sgen_workers_enqueue_job (job_scan_thread_data, &stdjd);
2699
2700         TV_GETTIME (atv);
2701         time_major_scan_thread_data += TV_ELAPSED (btv, atv);
2702
2703         TV_GETTIME (btv);
2704         time_major_scan_alloc_pinned += TV_ELAPSED (atv, btv);
2705
2706         if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
2707                 report_finalizer_roots ();
2708
2709         /* scan the list of objects ready for finalization */
2710         sfejd_fin_ready.list = fin_ready_list;
2711         sgen_workers_enqueue_job (job_scan_finalizer_entries, &sfejd_fin_ready);
2712
2713         sfejd_critical_fin.list = critical_fin_list;
2714         sgen_workers_enqueue_job (job_scan_finalizer_entries, &sfejd_critical_fin);
2715
2716         TV_GETTIME (atv);
2717         time_major_scan_finalized += TV_ELAPSED (btv, atv);
2718         DEBUG (2, fprintf (gc_debug_file, "Root scan: %d usecs\n", TV_ELAPSED (btv, atv)));
2719
2720         TV_GETTIME (btv);
2721         time_major_scan_big_objects += TV_ELAPSED (atv, btv);
2722
2723         if (major_collector.is_parallel) {
2724                 while (!sgen_gray_object_queue_is_empty (WORKERS_DISTRIBUTE_GRAY_QUEUE)) {
2725                         sgen_workers_distribute_gray_queue_sections ();
2726                         g_usleep (1000);
2727                 }
2728         }
2729         sgen_workers_join ();
2730
2731 #ifdef SGEN_DEBUG_INTERNAL_ALLOC
2732         main_gc_thread = NULL;
2733 #endif
2734
2735         if (major_collector.is_parallel)
2736                 g_assert (sgen_gray_object_queue_is_empty (&gray_queue));
2737
2738         /* all the objects in the heap */
2739         finish_gray_stack (heap_start, heap_end, GENERATION_OLD, &gray_queue);
2740         TV_GETTIME (atv);
2741         time_major_finish_gray_stack += TV_ELAPSED (btv, atv);
2742
2743         /*
2744          * The (single-threaded) finalization code might have done
2745          * some copying/marking so we can only reset the GC thread's
2746          * worker data here instead of earlier when we joined the
2747          * workers.
2748          */
2749         sgen_workers_reset_data ();
2750
2751         if (objects_pinned) {
2752                 /*This is slow, but we just OOM'd*/
2753                 sgen_pin_queue_clear_discarded_entries (nursery_section, old_next_pin_slot);
2754                 sgen_optimize_pin_queue (0);
2755                 sgen_find_section_pin_queue_start_end (nursery_section);
2756                 objects_pinned = 0;
2757         }
2758
2759         reset_heap_boundaries ();
2760         sgen_update_heap_boundaries ((mword)sgen_get_nursery_start (), (mword)sgen_get_nursery_end ());
2761
2762         /* sweep the big objects list */
2763         prevbo = NULL;
2764         for (bigobj = los_object_list; bigobj;) {
2765                 if (object_is_pinned (bigobj->data)) {
2766                         unpin_object (bigobj->data);
2767                         sgen_update_heap_boundaries ((mword)bigobj->data, (mword)bigobj->data + bigobj->size);
2768                 } else {
2769                         LOSObject *to_free;
2770                         /* not referenced anywhere, so we can free it */
2771                         if (prevbo)
2772                                 prevbo->next = bigobj->next;
2773                         else
2774                                 los_object_list = bigobj->next;
2775                         to_free = bigobj;
2776                         bigobj = bigobj->next;
2777                         sgen_los_free_object (to_free);
2778                         continue;
2779                 }
2780                 prevbo = bigobj;
2781                 bigobj = bigobj->next;
2782         }
2783
2784         TV_GETTIME (btv);
2785         time_major_free_bigobjs += TV_ELAPSED (atv, btv);
2786
2787         sgen_los_sweep ();
2788
2789         TV_GETTIME (atv);
2790         time_major_los_sweep += TV_ELAPSED (btv, atv);
2791
2792         major_collector.sweep ();
2793
2794         TV_GETTIME (btv);
2795         time_major_sweep += TV_ELAPSED (atv, btv);
2796
2797         /* walk the pin_queue, build up the fragment list of free memory, unmark
2798          * pinned objects as we go, memzero() the empty fragments so they are ready for the
2799          * next allocations.
2800          */
2801         if (!sgen_build_nursery_fragments (nursery_section, nursery_section->pin_queue_start, nursery_section->pin_queue_num_entries))
2802                 degraded_mode = 1;
2803
2804         /* Clear TLABs for all threads */
2805         sgen_clear_tlabs ();
2806
2807         TV_GETTIME (atv);
2808         time_major_fragment_creation += TV_ELAPSED (btv, atv);
2809
2810         TV_GETTIME (all_btv);
2811         gc_stats.major_gc_time_usecs += TV_ELAPSED (all_atv, all_btv);
2812
2813         if (heap_dump_file)
2814                 dump_heap ("major", stat_major_gcs - 1, reason);
2815
2816         /* prepare the pin queue for the next collection */
2817         sgen_finish_pinning ();
2818
2819         if (fin_ready_list || critical_fin_list) {
2820                 DEBUG (4, fprintf (gc_debug_file, "Finalizer-thread wakeup: ready %d\n", num_ready_finalizers));
2821                 mono_gc_finalize_notify ();
2822         }
2823         sgen_pin_stats_reset ();
2824
2825         g_assert (sgen_gray_object_queue_is_empty (&gray_queue));
2826
2827         sgen_memgov_major_collection_end ();
2828         current_collection_generation = -1;
2829
2830         major_collector.finish_major_collection ();
2831
2832         check_scan_starts ();
2833
2834         binary_protocol_flush_buffers (FALSE);
2835
2836         //consistency_check ();
2837
2838         MONO_GC_END (GENERATION_OLD);
2839
2840         return bytes_pinned_from_failed_allocation > 0;
2841 }
2842
2843 static gboolean major_do_collection (const char *reason);
2844
2845 /*
2846  * Ensure an allocation request for @size will succeed by freeing enough memory.
2847  *
2848  * LOCKING: The GC lock MUST be held.
2849  */
2850 void
2851 sgen_ensure_free_space (size_t size)
2852 {
2853         int generation_to_collect = -1;
2854         const char *reason = NULL;
2855
2856
2857         if (size > SGEN_MAX_SMALL_OBJ_SIZE) {
2858                 if (sgen_need_major_collection (size)) {
2859                         reason = "LOS overflow";
2860                         generation_to_collect = GENERATION_OLD;
2861                 }
2862         } else {
2863                 if (degraded_mode) {
2864                         if (sgen_need_major_collection (size)) {
2865                                 reason = "Degraded mode overflow";
2866                                 generation_to_collect = GENERATION_OLD;
2867                         }
2868                 } else if (sgen_need_major_collection (size)) {
2869                         reason = "Minor allowance";
2870                         generation_to_collect = GENERATION_OLD;
2871                 } else {
2872                         generation_to_collect = GENERATION_NURSERY;
2873                         reason = "Nursery full";                        
2874                 }
2875         }
2876
2877         if (generation_to_collect == -1)
2878                 return;
2879         sgen_perform_collection (size, generation_to_collect, reason);
2880 }
2881
2882 void
2883 sgen_perform_collection (size_t requested_size, int generation_to_collect, const char *reason)
2884 {
2885         TV_DECLARE (gc_end);
2886         GGTimingInfo infos [2];
2887         int overflow_generation_to_collect = -1;
2888         const char *overflow_reason = NULL;
2889
2890         memset (infos, 0, sizeof (infos));
2891         mono_profiler_gc_event (MONO_GC_EVENT_START, generation_to_collect);
2892
2893         infos [0].generation = generation_to_collect;
2894         infos [0].reason = reason;
2895         infos [0].is_overflow = FALSE;
2896         TV_GETTIME (infos [0].total_time);
2897         infos [1].generation = -1;
2898
2899         sgen_stop_world (generation_to_collect);
2900         //FIXME extract overflow reason
2901         if (generation_to_collect == GENERATION_NURSERY) {
2902                 if (collect_nursery ()) {
2903                         overflow_generation_to_collect = GENERATION_OLD;
2904                         overflow_reason = "Minor overflow";
2905                 }
2906         } else {
2907                 if (major_do_collection (reason)) {
2908                         overflow_generation_to_collect = GENERATION_NURSERY;
2909                         overflow_reason = "Excessive pinning";
2910                 }
2911         }
2912
2913         TV_GETTIME (gc_end);
2914         infos [0].total_time = SGEN_TV_ELAPSED (infos [0].total_time, gc_end);
2915
2916
2917         if (overflow_generation_to_collect != -1) {
2918                 mono_profiler_gc_event (MONO_GC_EVENT_START, overflow_generation_to_collect);
2919                 infos [1].generation = overflow_generation_to_collect;
2920                 infos [1].reason = overflow_reason;
2921                 infos [1].is_overflow = TRUE;
2922                 infos [1].total_time = gc_end;
2923
2924                 if (overflow_generation_to_collect == GENERATION_NURSERY)
2925                         collect_nursery ();
2926                 else
2927                         major_do_collection (overflow_reason);
2928
2929                 TV_GETTIME (gc_end);
2930                 infos [1].total_time = SGEN_TV_ELAPSED (infos [1].total_time, gc_end);
2931
2932                 /* keep events symmetric */
2933                 mono_profiler_gc_event (MONO_GC_EVENT_END, overflow_generation_to_collect);
2934         }
2935
2936         DEBUG (2, fprintf (gc_debug_file, "Heap size: %lu, LOS size: %lu\n", (unsigned long)mono_gc_get_heap_size (), (unsigned long)los_memory_usage));
2937
2938         /* this also sets the proper pointers for the next allocation */
2939         if (generation_to_collect == GENERATION_NURSERY && !sgen_can_alloc_size (requested_size)) {
2940                 /* TypeBuilder and MonoMethod are killing mcs with fragmentation */
2941                 DEBUG (1, fprintf (gc_debug_file, "nursery collection didn't find enough room for %zd alloc (%d pinned)\n", requested_size, sgen_get_pinned_count ()));
2942                 sgen_dump_pin_queue ();
2943                 degraded_mode = 1;
2944         }
2945
2946         sgen_restart_world (generation_to_collect, infos);
2947
2948         mono_profiler_gc_event (MONO_GC_EVENT_END, generation_to_collect);
2949 }
2950
2951 /*
2952  * ######################################################################
2953  * ########  Memory allocation from the OS
2954  * ######################################################################
2955  * This section of code deals with getting memory from the OS and
2956  * allocating memory for GC-internal data structures.
2957  * Internal memory can be handled with a freelist for small objects.
2958  */
2959
2960 /*
2961  * Debug reporting.
2962  */
2963 G_GNUC_UNUSED static void
2964 report_internal_mem_usage (void)
2965 {
2966         printf ("Internal memory usage:\n");
2967         sgen_report_internal_mem_usage ();
2968         printf ("Pinned memory usage:\n");
2969         major_collector.report_pinned_memory_usage ();
2970 }
2971
2972 /*
2973  * ######################################################################
2974  * ########  Finalization support
2975  * ######################################################################
2976  */
2977
2978 /*
2979  * If the object has been forwarded it means it's still referenced from a root. 
2980  * If it is pinned it's still alive as well.
2981  * A LOS object is only alive if we have pinned it.
2982  * Return TRUE if @obj is ready to be finalized.
2983  */
2984 static inline gboolean
2985 sgen_is_object_alive (void *object)
2986 {
2987         if (ptr_in_nursery (object))
2988                 return sgen_nursery_is_object_alive (object);
2989         /* Oldgen objects can be pinned and forwarded too */
2990         if (SGEN_OBJECT_IS_PINNED (object) || SGEN_OBJECT_IS_FORWARDED (object))
2991                 return TRUE;
2992         return major_collector.is_object_live (object);
2993 }
2994
2995 gboolean
2996 sgen_gc_is_object_ready_for_finalization (void *object)
2997 {
2998         return !sgen_is_object_alive (object);
2999 }
3000
3001 static gboolean
3002 has_critical_finalizer (MonoObject *obj)
3003 {
3004         MonoClass *class;
3005
3006         if (!mono_defaults.critical_finalizer_object)
3007                 return FALSE;
3008
3009         class = ((MonoVTable*)LOAD_VTABLE (obj))->klass;
3010
3011         return mono_class_has_parent_fast (class, mono_defaults.critical_finalizer_object);
3012 }
3013
3014 void
3015 sgen_queue_finalization_entry (MonoObject *obj)
3016 {
3017         FinalizeReadyEntry *entry = sgen_alloc_internal (INTERNAL_MEM_FINALIZE_READY_ENTRY);
3018         entry->object = obj;
3019         if (has_critical_finalizer (obj)) {
3020                 entry->next = critical_fin_list;
3021                 critical_fin_list = entry;
3022         } else {
3023                 entry->next = fin_ready_list;
3024                 fin_ready_list = entry;
3025         }
3026 }
3027
3028 static inline int
3029 object_is_reachable (char *object, char *start, char *end)
3030 {
3031         /*This happens for non nursery objects during minor collections. We just treat all objects as alive.*/
3032         if (object < start || object >= end)
3033                 return TRUE;
3034
3035         return sgen_is_object_alive (object);
3036 }
3037
3038 gboolean
3039 sgen_object_is_live (void *obj)
3040 {
3041         if (ptr_in_nursery (obj))
3042                 return object_is_pinned (obj);
3043         /* FIXME This is semantically wrong! All tenured object are considered alive during a nursery collection. */
3044         if (current_collection_generation == GENERATION_NURSERY)
3045                 return FALSE;
3046         return major_collector.is_object_live (obj);
3047 }
3048
3049 /* LOCKING: requires that the GC lock is held */
3050 static void
3051 null_ephemerons_for_domain (MonoDomain *domain)
3052 {
3053         EphemeronLinkNode *current = ephemeron_list, *prev = NULL;
3054
3055         while (current) {
3056                 MonoObject *object = (MonoObject*)current->array;
3057
3058                 if (object && !object->vtable) {
3059                         EphemeronLinkNode *tmp = current;
3060
3061                         if (prev)
3062                                 prev->next = current->next;
3063                         else
3064                                 ephemeron_list = current->next;
3065
3066                         current = current->next;
3067                         sgen_free_internal (tmp, INTERNAL_MEM_EPHEMERON_LINK);
3068                 } else {
3069                         prev = current;
3070                         current = current->next;
3071                 }
3072         }
3073 }
3074
3075 /* LOCKING: requires that the GC lock is held */
3076 static void
3077 clear_unreachable_ephemerons (CopyOrMarkObjectFunc copy_func, char *start, char *end, GrayQueue *queue)
3078 {
3079         int was_in_nursery, was_promoted;
3080         EphemeronLinkNode *current = ephemeron_list, *prev = NULL;
3081         MonoArray *array;
3082         Ephemeron *cur, *array_end;
3083         char *tombstone;
3084
3085         while (current) {
3086                 char *object = current->array;
3087
3088                 if (!object_is_reachable (object, start, end)) {
3089                         EphemeronLinkNode *tmp = current;
3090
3091                         DEBUG (5, fprintf (gc_debug_file, "Dead Ephemeron array at %p\n", object));
3092
3093                         if (prev)
3094                                 prev->next = current->next;
3095                         else
3096                                 ephemeron_list = current->next;
3097
3098                         current = current->next;
3099                         sgen_free_internal (tmp, INTERNAL_MEM_EPHEMERON_LINK);
3100
3101                         continue;
3102                 }
3103
3104                 was_in_nursery = ptr_in_nursery (object);
3105                 copy_func ((void**)&object, queue);
3106                 current->array = object;
3107
3108                 /*The array was promoted, add global remsets for key/values left behind in nursery.*/
3109                 was_promoted = was_in_nursery && !ptr_in_nursery (object);
3110
3111                 DEBUG (5, fprintf (gc_debug_file, "Clearing unreachable entries for ephemeron array at %p\n", object));
3112
3113                 array = (MonoArray*)object;
3114                 cur = mono_array_addr (array, Ephemeron, 0);
3115                 array_end = cur + mono_array_length_fast (array);
3116                 tombstone = (char*)((MonoVTable*)LOAD_VTABLE (object))->domain->ephemeron_tombstone;
3117
3118                 for (; cur < array_end; ++cur) {
3119                         char *key = (char*)cur->key;
3120
3121                         if (!key || key == tombstone)
3122                                 continue;
3123
3124                         DEBUG (5, fprintf (gc_debug_file, "[%td] key %p (%s) value %p (%s)\n", cur - mono_array_addr (array, Ephemeron, 0),
3125                                 key, object_is_reachable (key, start, end) ? "reachable" : "unreachable",
3126                                 cur->value, cur->value && object_is_reachable (cur->value, start, end) ? "reachable" : "unreachable"));
3127
3128                         if (!object_is_reachable (key, start, end)) {
3129                                 cur->key = tombstone;
3130                                 cur->value = NULL;
3131                                 continue;
3132                         }
3133
3134                         if (was_promoted) {
3135                                 if (ptr_in_nursery (key)) {/*key was not promoted*/
3136                                         DEBUG (5, fprintf (gc_debug_file, "\tAdded remset to key %p\n", key));
3137                                         sgen_add_to_global_remset (&cur->key);
3138                                 }
3139                                 if (ptr_in_nursery (cur->value)) {/*value was not promoted*/
3140                                         DEBUG (5, fprintf (gc_debug_file, "\tAdded remset to value %p\n", cur->value));
3141                                         sgen_add_to_global_remset (&cur->value);
3142                                 }
3143                         }
3144                 }
3145                 prev = current;
3146                 current = current->next;
3147         }
3148 }
3149
3150 /* LOCKING: requires that the GC lock is held */
3151 static int
3152 mark_ephemerons_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, GrayQueue *queue)
3153 {
3154         int nothing_marked = 1;
3155         EphemeronLinkNode *current = ephemeron_list;
3156         MonoArray *array;
3157         Ephemeron *cur, *array_end;
3158         char *tombstone;
3159
3160         for (current = ephemeron_list; current; current = current->next) {
3161                 char *object = current->array;
3162                 DEBUG (5, fprintf (gc_debug_file, "Ephemeron array at %p\n", object));
3163
3164                 /*
3165                 For now we process all ephemerons during all collections.
3166                 Ideally we should use remset information to partially scan those
3167                 arrays.
3168                 We already emit write barriers for Ephemeron fields, it's
3169                 just that we don't process them.
3170                 */
3171                 /*if (object < start || object >= end)
3172                         continue;*/
3173
3174                 /*It has to be alive*/
3175                 if (!object_is_reachable (object, start, end)) {
3176                         DEBUG (5, fprintf (gc_debug_file, "\tnot reachable\n"));
3177                         continue;
3178                 }
3179
3180                 copy_func ((void**)&object, queue);
3181
3182                 array = (MonoArray*)object;
3183                 cur = mono_array_addr (array, Ephemeron, 0);
3184                 array_end = cur + mono_array_length_fast (array);
3185                 tombstone = (char*)((MonoVTable*)LOAD_VTABLE (object))->domain->ephemeron_tombstone;
3186
3187                 for (; cur < array_end; ++cur) {
3188                         char *key = cur->key;
3189
3190                         if (!key || key == tombstone)
3191                                 continue;
3192
3193                         DEBUG (5, fprintf (gc_debug_file, "[%td] key %p (%s) value %p (%s)\n", cur - mono_array_addr (array, Ephemeron, 0),
3194                                 key, object_is_reachable (key, start, end) ? "reachable" : "unreachable",
3195                                 cur->value, cur->value && object_is_reachable (cur->value, start, end) ? "reachable" : "unreachable"));
3196
3197                         if (object_is_reachable (key, start, end)) {
3198                                 char *value = cur->value;
3199
3200                                 copy_func ((void**)&cur->key, queue);
3201                                 if (value) {
3202                                         if (!object_is_reachable (value, start, end))
3203                                                 nothing_marked = 0;
3204                                         copy_func ((void**)&cur->value, queue);
3205                                 }
3206                         }
3207                 }
3208         }
3209
3210         DEBUG (5, fprintf (gc_debug_file, "Ephemeron run finished. Is it done %d\n", nothing_marked));
3211         return nothing_marked;
3212 }
3213
3214 int
3215 mono_gc_invoke_finalizers (void)
3216 {
3217         FinalizeReadyEntry *entry = NULL;
3218         gboolean entry_is_critical = FALSE;
3219         int count = 0;
3220         void *obj;
3221         /* FIXME: batch to reduce lock contention */
3222         while (fin_ready_list || critical_fin_list) {
3223                 LOCK_GC;
3224
3225                 if (entry) {
3226                         FinalizeReadyEntry **list = entry_is_critical ? &critical_fin_list : &fin_ready_list;
3227
3228                         /* We have finalized entry in the last
3229                            interation, now we need to remove it from
3230                            the list. */
3231                         if (*list == entry)
3232                                 *list = entry->next;
3233                         else {
3234                                 FinalizeReadyEntry *e = *list;
3235                                 while (e->next != entry)
3236                                         e = e->next;
3237                                 e->next = entry->next;
3238                         }
3239                         sgen_free_internal (entry, INTERNAL_MEM_FINALIZE_READY_ENTRY);
3240                         entry = NULL;
3241                 }
3242
3243                 /* Now look for the first non-null entry. */
3244                 for (entry = fin_ready_list; entry && !entry->object; entry = entry->next)
3245                         ;
3246                 if (entry) {
3247                         entry_is_critical = FALSE;
3248                 } else {
3249                         entry_is_critical = TRUE;
3250                         for (entry = critical_fin_list; entry && !entry->object; entry = entry->next)
3251                                 ;
3252                 }
3253
3254                 if (entry) {
3255                         g_assert (entry->object);
3256                         num_ready_finalizers--;
3257                         obj = entry->object;
3258                         entry->object = NULL;
3259                         DEBUG (7, fprintf (gc_debug_file, "Finalizing object %p (%s)\n", obj, safe_name (obj)));
3260                 }
3261
3262                 UNLOCK_GC;
3263
3264                 if (!entry)
3265                         break;
3266
3267                 g_assert (entry->object == NULL);
3268                 count++;
3269                 /* the object is on the stack so it is pinned */
3270                 /*g_print ("Calling finalizer for object: %p (%s)\n", entry->object, safe_name (entry->object));*/
3271                 mono_gc_run_finalize (obj, NULL);
3272         }
3273         g_assert (!entry);
3274         return count;
3275 }
3276
3277 gboolean
3278 mono_gc_pending_finalizers (void)
3279 {
3280         return fin_ready_list || critical_fin_list;
3281 }
3282
3283 /*
3284  * ######################################################################
3285  * ########  registered roots support
3286  * ######################################################################
3287  */
3288
3289 /*
3290  * We do not coalesce roots.
3291  */
3292 static int
3293 mono_gc_register_root_inner (char *start, size_t size, void *descr, int root_type)
3294 {
3295         RootRecord new_root;
3296         int i;
3297         LOCK_GC;
3298         for (i = 0; i < ROOT_TYPE_NUM; ++i) {
3299                 RootRecord *root = sgen_hash_table_lookup (&roots_hash [i], start);
3300                 /* we allow changing the size and the descriptor (for thread statics etc) */
3301                 if (root) {
3302                         size_t old_size = root->end_root - start;
3303                         root->end_root = start + size;
3304                         g_assert (((root->root_desc != 0) && (descr != NULL)) ||
3305                                           ((root->root_desc == 0) && (descr == NULL)));
3306                         root->root_desc = (mword)descr;
3307                         roots_size += size;
3308                         roots_size -= old_size;
3309                         UNLOCK_GC;
3310                         return TRUE;
3311                 }
3312         }
3313
3314         new_root.end_root = start + size;
3315         new_root.root_desc = (mword)descr;
3316
3317         sgen_hash_table_replace (&roots_hash [root_type], start, &new_root, NULL);
3318         roots_size += size;
3319
3320         DEBUG (3, fprintf (gc_debug_file, "Added root for range: %p-%p, descr: %p  (%d/%d bytes)\n", start, new_root.end_root, descr, (int)size, (int)roots_size));
3321
3322         UNLOCK_GC;
3323         return TRUE;
3324 }
3325
3326 int
3327 mono_gc_register_root (char *start, size_t size, void *descr)
3328 {
3329         return mono_gc_register_root_inner (start, size, descr, descr ? ROOT_TYPE_NORMAL : ROOT_TYPE_PINNED);
3330 }
3331
3332 int
3333 mono_gc_register_root_wbarrier (char *start, size_t size, void *descr)
3334 {
3335         return mono_gc_register_root_inner (start, size, descr, ROOT_TYPE_WBARRIER);
3336 }
3337
3338 void
3339 mono_gc_deregister_root (char* addr)
3340 {
3341         int root_type;
3342         RootRecord root;
3343
3344         LOCK_GC;
3345         for (root_type = 0; root_type < ROOT_TYPE_NUM; ++root_type) {
3346                 if (sgen_hash_table_remove (&roots_hash [root_type], addr, &root))
3347                         roots_size -= (root.end_root - addr);
3348         }
3349         UNLOCK_GC;
3350 }
3351
3352 /*
3353  * ######################################################################
3354  * ########  Thread handling (stop/start code)
3355  * ######################################################################
3356  */
3357
3358 unsigned int sgen_global_stop_count = 0;
3359
3360 void
3361 sgen_fill_thread_info_for_suspend (SgenThreadInfo *info)
3362 {
3363         if (remset.fill_thread_info_for_suspend)
3364                 remset.fill_thread_info_for_suspend (info);
3365 }
3366
3367 int
3368 sgen_get_current_collection_generation (void)
3369 {
3370         return current_collection_generation;
3371 }
3372
3373 void
3374 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
3375 {
3376         gc_callbacks = *callbacks;
3377 }
3378
3379 MonoGCCallbacks *
3380 mono_gc_get_gc_callbacks ()
3381 {
3382         return &gc_callbacks;
3383 }
3384
3385 /* Variables holding start/end nursery so it won't have to be passed at every call */
3386 static void *scan_area_arg_start, *scan_area_arg_end;
3387
3388 void
3389 mono_gc_conservatively_scan_area (void *start, void *end)
3390 {
3391         conservatively_pin_objects_from (start, end, scan_area_arg_start, scan_area_arg_end, PIN_TYPE_STACK);
3392 }
3393
3394 void*
3395 mono_gc_scan_object (void *obj)
3396 {
3397         UserCopyOrMarkData *data = mono_native_tls_get_value (user_copy_or_mark_key);
3398         current_object_ops.copy_or_mark_object (&obj, data->queue);
3399         return obj;
3400 }
3401
3402 /*
3403  * Mark from thread stacks and registers.
3404  */
3405 static void
3406 scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise, GrayQueue *queue)
3407 {
3408         SgenThreadInfo *info;
3409
3410         scan_area_arg_start = start_nursery;
3411         scan_area_arg_end = end_nursery;
3412
3413         FOREACH_THREAD (info) {
3414                 if (info->skip) {
3415                         DEBUG (3, fprintf (gc_debug_file, "Skipping dead thread %p, range: %p-%p, size: %td\n", info, info->stack_start, info->stack_end, (char*)info->stack_end - (char*)info->stack_start));
3416                         continue;
3417                 }
3418                 if (info->gc_disabled) {
3419                         DEBUG (3, fprintf (gc_debug_file, "GC disabled for thread %p, range: %p-%p, size: %td\n", info, info->stack_start, info->stack_end, (char*)info->stack_end - (char*)info->stack_start));
3420                         continue;
3421                 }
3422
3423                 if (!info->joined_stw) {
3424                         DEBUG (3, fprintf (gc_debug_file, "Skipping thread not seen in STW %p, range: %p-%p, size: %td\n", info, info->stack_start, info->stack_end, (char*)info->stack_end - (char*)info->stack_start));
3425                         continue;
3426                 }
3427                 
3428                 DEBUG (3, fprintf (gc_debug_file, "Scanning thread %p, range: %p-%p, size: %td, pinned=%d\n", info, info->stack_start, info->stack_end, (char*)info->stack_end - (char*)info->stack_start, sgen_get_pinned_count ()));
3429                 if (!info->thread_is_dying) {
3430                         if (gc_callbacks.thread_mark_func && !conservative_stack_mark) {
3431                                 UserCopyOrMarkData data = { NULL, queue };
3432                                 set_user_copy_or_mark_data (&data);
3433                                 gc_callbacks.thread_mark_func (info->runtime_data, info->stack_start, info->stack_end, precise);
3434                                 set_user_copy_or_mark_data (NULL);
3435                         } else if (!precise) {
3436                                 conservatively_pin_objects_from (info->stack_start, info->stack_end, start_nursery, end_nursery, PIN_TYPE_STACK);
3437                         }
3438                 }
3439
3440                 if (!info->thread_is_dying && !precise) {
3441 #ifdef USE_MONO_CTX
3442                         conservatively_pin_objects_from ((void**)&info->ctx, (void**)&info->ctx + ARCH_NUM_REGS,
3443                                 start_nursery, end_nursery, PIN_TYPE_STACK);
3444 #else
3445                         conservatively_pin_objects_from (&info->regs, &info->regs + ARCH_NUM_REGS,
3446                                         start_nursery, end_nursery, PIN_TYPE_STACK);
3447 #endif
3448                 }
3449         } END_FOREACH_THREAD
3450 }
3451
3452 static gboolean
3453 ptr_on_stack (void *ptr)
3454 {
3455         gpointer stack_start = &stack_start;
3456         SgenThreadInfo *info = mono_thread_info_current ();
3457
3458         if (ptr >= stack_start && ptr < (gpointer)info->stack_end)
3459                 return TRUE;
3460         return FALSE;
3461 }
3462
3463 static void*
3464 sgen_thread_register (SgenThreadInfo* info, void *addr)
3465 {
3466 #ifndef HAVE_KW_THREAD
3467         SgenThreadInfo *__thread_info__ = info;
3468 #endif
3469
3470         LOCK_GC;
3471 #ifndef HAVE_KW_THREAD
3472         info->tlab_start = info->tlab_next = info->tlab_temp_end = info->tlab_real_end = NULL;
3473
3474         g_assert (!mono_native_tls_get_value (thread_info_key));
3475         mono_native_tls_set_value (thread_info_key, info);
3476 #else
3477         thread_info = info;
3478 #endif
3479
3480 #if !defined(__MACH__)
3481         info->stop_count = -1;
3482         info->signal = 0;
3483 #endif
3484         info->skip = 0;
3485         info->joined_stw = FALSE;
3486         info->doing_handshake = FALSE;
3487         info->thread_is_dying = FALSE;
3488         info->stack_start = NULL;
3489         info->store_remset_buffer_addr = &STORE_REMSET_BUFFER;
3490         info->store_remset_buffer_index_addr = &STORE_REMSET_BUFFER_INDEX;
3491         info->stopped_ip = NULL;
3492         info->stopped_domain = NULL;
3493 #ifdef USE_MONO_CTX
3494         memset (&info->ctx, 0, sizeof (MonoContext));
3495 #else
3496         memset (&info->regs, 0, sizeof (info->regs));
3497 #endif
3498
3499         sgen_init_tlab_info (info);
3500
3501         binary_protocol_thread_register ((gpointer)mono_thread_info_get_tid (info));
3502
3503 #ifdef HAVE_KW_THREAD
3504         store_remset_buffer_index_addr = &store_remset_buffer_index;
3505 #endif
3506
3507         /* try to get it with attributes first */
3508 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
3509         {
3510                 size_t size;
3511                 void *sstart;
3512                 pthread_attr_t attr;
3513                 pthread_getattr_np (pthread_self (), &attr);
3514                 pthread_attr_getstack (&attr, &sstart, &size);
3515                 info->stack_start_limit = sstart;
3516                 info->stack_end = (char*)sstart + size;
3517                 pthread_attr_destroy (&attr);
3518         }
3519 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
3520                  info->stack_end = (char*)pthread_get_stackaddr_np (pthread_self ());
3521                  info->stack_start_limit = (char*)info->stack_end - pthread_get_stacksize_np (pthread_self ());
3522 #else
3523         {
3524                 /* FIXME: we assume the stack grows down */
3525                 gsize stack_bottom = (gsize)addr;
3526                 stack_bottom += 4095;
3527                 stack_bottom &= ~4095;
3528                 info->stack_end = (char*)stack_bottom;
3529         }
3530 #endif
3531
3532 #ifdef HAVE_KW_THREAD
3533         stack_end = info->stack_end;
3534 #endif
3535
3536         if (remset.register_thread)
3537                 remset.register_thread (info);
3538
3539         DEBUG (3, fprintf (gc_debug_file, "registered thread %p (%p) stack end %p\n", info, (gpointer)mono_thread_info_get_tid (info), info->stack_end));
3540
3541         if (gc_callbacks.thread_attach_func)
3542                 info->runtime_data = gc_callbacks.thread_attach_func ();
3543
3544         UNLOCK_GC;
3545         return info;
3546 }
3547
3548 static void
3549 sgen_wbarrier_cleanup_thread (SgenThreadInfo *p)
3550 {
3551         if (remset.cleanup_thread)
3552                 remset.cleanup_thread (p);
3553 }
3554
3555 static void
3556 sgen_thread_unregister (SgenThreadInfo *p)
3557 {
3558         /* If a delegate is passed to native code and invoked on a thread we dont
3559          * know about, the jit will register it with mono_jit_thread_attach, but
3560          * we have no way of knowing when that thread goes away.  SGen has a TSD
3561          * so we assume that if the domain is still registered, we can detach
3562          * the thread
3563          */
3564         if (mono_domain_get ())
3565                 mono_thread_detach (mono_thread_current ());
3566
3567         p->thread_is_dying = TRUE;
3568
3569         /*
3570         There is a race condition between a thread finishing executing and been removed
3571         from the GC thread set.
3572         This happens on posix systems when TLS data is been cleaned-up, libpthread will
3573         set the thread_info slot to NULL before calling the cleanup function. This
3574         opens a window in which the thread is registered but has a NULL TLS.
3575
3576         The suspend signal handler needs TLS data to know where to store thread state
3577         data or otherwise it will simply ignore the thread.
3578
3579         This solution works because the thread doing STW will wait until all threads been
3580         suspended handshake back, so there is no race between the doing_hankshake test
3581         and the suspend_thread call.
3582
3583         This is not required on systems that do synchronous STW as those can deal with
3584         the above race at suspend time.
3585
3586         FIXME: I believe we could avoid this by using mono_thread_info_lookup when
3587         mono_thread_info_current returns NULL. Or fix mono_thread_info_lookup to do so.
3588         */
3589 #if (defined(__MACH__) && MONO_MACH_ARCH_SUPPORTED) || !defined(HAVE_PTHREAD_KILL)
3590         LOCK_GC;
3591 #else
3592         while (!TRYLOCK_GC) {
3593                 if (!sgen_park_current_thread_if_doing_handshake (p))
3594                         g_usleep (50);
3595         }
3596         MONO_GC_LOCKED ();
3597 #endif
3598
3599         binary_protocol_thread_unregister ((gpointer)mono_thread_info_get_tid (p));
3600         DEBUG (3, fprintf (gc_debug_file, "unregister thread %p (%p)\n", p, (gpointer)mono_thread_info_get_tid (p)));
3601
3602         if (gc_callbacks.thread_detach_func) {
3603                 gc_callbacks.thread_detach_func (p->runtime_data);
3604                 p->runtime_data = NULL;
3605         }
3606         sgen_wbarrier_cleanup_thread (p);
3607
3608         mono_threads_unregister_current_thread (p);
3609         UNLOCK_GC;
3610 }
3611
3612
3613 static void
3614 sgen_thread_attach (SgenThreadInfo *info)
3615 {
3616         LOCK_GC;
3617         /*this is odd, can we get attached before the gc is inited?*/
3618         init_stats ();
3619         UNLOCK_GC;
3620         
3621         if (gc_callbacks.thread_attach_func && !info->runtime_data)
3622                 info->runtime_data = gc_callbacks.thread_attach_func ();
3623 }
3624 gboolean
3625 mono_gc_register_thread (void *baseptr)
3626 {
3627         return mono_thread_info_attach (baseptr) != NULL;
3628 }
3629
3630 /*
3631  * mono_gc_set_stack_end:
3632  *
3633  *   Set the end of the current threads stack to STACK_END. The stack space between 
3634  * STACK_END and the real end of the threads stack will not be scanned during collections.
3635  */
3636 void
3637 mono_gc_set_stack_end (void *stack_end)
3638 {
3639         SgenThreadInfo *info;
3640
3641         LOCK_GC;
3642         info = mono_thread_info_current ();
3643         if (info) {
3644                 g_assert (stack_end < info->stack_end);
3645                 info->stack_end = stack_end;
3646         }
3647         UNLOCK_GC;
3648 }
3649
3650 #if USE_PTHREAD_INTERCEPT
3651
3652
3653 int
3654 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
3655 {
3656         return pthread_create (new_thread, attr, start_routine, arg);
3657 }
3658
3659 int
3660 mono_gc_pthread_join (pthread_t thread, void **retval)
3661 {
3662         return pthread_join (thread, retval);
3663 }
3664
3665 int
3666 mono_gc_pthread_detach (pthread_t thread)
3667 {
3668         return pthread_detach (thread);
3669 }
3670
3671 void
3672 mono_gc_pthread_exit (void *retval)
3673 {
3674         pthread_exit (retval);
3675 }
3676
3677 #endif /* USE_PTHREAD_INTERCEPT */
3678
3679 /*
3680  * ######################################################################
3681  * ########  Write barriers
3682  * ######################################################################
3683  */
3684
3685 /*
3686  * Note: the write barriers first do the needed GC work and then do the actual store:
3687  * this way the value is visible to the conservative GC scan after the write barrier
3688  * itself. If a GC interrupts the barrier in the middle, value will be kept alive by
3689  * the conservative scan, otherwise by the remembered set scan.
3690  */
3691 void
3692 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
3693 {
3694         HEAVY_STAT (++stat_wbarrier_set_field);
3695         if (ptr_in_nursery (field_ptr)) {
3696                 *(void**)field_ptr = value;
3697                 return;
3698         }
3699         DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", field_ptr));
3700         if (value)
3701                 binary_protocol_wbarrier (field_ptr, value, value->vtable);
3702
3703         remset.wbarrier_set_field (obj, field_ptr, value);
3704 }
3705
3706 void
3707 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
3708 {
3709         HEAVY_STAT (++stat_wbarrier_set_arrayref);
3710         if (ptr_in_nursery (slot_ptr)) {
3711                 *(void**)slot_ptr = value;
3712                 return;
3713         }
3714         DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", slot_ptr));
3715         if (value)
3716                 binary_protocol_wbarrier (slot_ptr, value, value->vtable);
3717
3718         remset.wbarrier_set_arrayref (arr, slot_ptr, value);
3719 }
3720
3721 void
3722 mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
3723 {
3724         HEAVY_STAT (++stat_wbarrier_arrayref_copy);
3725         /*This check can be done without taking a lock since dest_ptr array is pinned*/
3726         if (ptr_in_nursery (dest_ptr) || count <= 0) {
3727                 mono_gc_memmove (dest_ptr, src_ptr, count * sizeof (gpointer));
3728                 return;
3729         }
3730
3731 #ifdef SGEN_BINARY_PROTOCOL
3732         {
3733                 int i;
3734                 for (i = 0; i < count; ++i) {
3735                         gpointer dest = (gpointer*)dest_ptr + i;
3736                         gpointer obj = *((gpointer*)src_ptr + i);
3737                         if (obj)
3738                                 binary_protocol_wbarrier (dest, obj, (gpointer)LOAD_VTABLE (obj));
3739                 }
3740         }
3741 #endif
3742
3743         remset.wbarrier_arrayref_copy (dest_ptr, src_ptr, count);
3744 }
3745
3746 static char *found_obj;
3747
3748 static void
3749 find_object_for_ptr_callback (char *obj, size_t size, void *user_data)
3750 {
3751         char *ptr = user_data;
3752
3753         if (ptr >= obj && ptr < obj + size) {
3754                 g_assert (!found_obj);
3755                 found_obj = obj;
3756         }
3757 }
3758
3759 /* for use in the debugger */
3760 char* find_object_for_ptr (char *ptr);
3761 char*
3762 find_object_for_ptr (char *ptr)
3763 {
3764         if (ptr >= nursery_section->data && ptr < nursery_section->end_data) {
3765                 found_obj = NULL;
3766                 sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
3767                                 find_object_for_ptr_callback, ptr, TRUE);
3768                 if (found_obj)
3769                         return found_obj;
3770         }
3771
3772         found_obj = NULL;
3773         sgen_los_iterate_objects (find_object_for_ptr_callback, ptr);
3774         if (found_obj)
3775                 return found_obj;
3776
3777         /*
3778          * Very inefficient, but this is debugging code, supposed to
3779          * be called from gdb, so we don't care.
3780          */
3781         found_obj = NULL;
3782         major_collector.iterate_objects (TRUE, TRUE, find_object_for_ptr_callback, ptr);
3783         return found_obj;
3784 }
3785
3786 void
3787 mono_gc_wbarrier_generic_nostore (gpointer ptr)
3788 {
3789         HEAVY_STAT (++stat_wbarrier_generic_store);
3790
3791 #ifdef XDOMAIN_CHECKS_IN_WBARRIER
3792         /* FIXME: ptr_in_heap must be called with the GC lock held */
3793         if (xdomain_checks && *(MonoObject**)ptr && ptr_in_heap (ptr)) {
3794                 char *start = find_object_for_ptr (ptr);
3795                 MonoObject *value = *(MonoObject**)ptr;
3796                 LOCK_GC;
3797                 g_assert (start);
3798                 if (start) {
3799                         MonoObject *obj = (MonoObject*)start;
3800                         if (obj->vtable->domain != value->vtable->domain)
3801                                 g_assert (is_xdomain_ref_allowed (ptr, start, obj->vtable->domain));
3802                 }
3803                 UNLOCK_GC;
3804         }
3805 #endif
3806
3807         if (*(gpointer*)ptr)
3808                 binary_protocol_wbarrier (ptr, *(gpointer*)ptr, (gpointer)LOAD_VTABLE (*(gpointer*)ptr));
3809
3810         if (ptr_in_nursery (ptr) || ptr_on_stack (ptr) || !ptr_in_nursery (*(gpointer*)ptr)) {
3811                 DEBUG (8, fprintf (gc_debug_file, "Skipping remset at %p\n", ptr));
3812                 return;
3813         }
3814
3815         DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", ptr));
3816
3817         remset.wbarrier_generic_nostore (ptr);
3818 }
3819
3820 void
3821 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
3822 {
3823         DEBUG (8, fprintf (gc_debug_file, "Wbarrier store at %p to %p (%s)\n", ptr, value, value ? safe_name (value) : "null"));
3824         *(void**)ptr = value;
3825         if (ptr_in_nursery (value))
3826                 mono_gc_wbarrier_generic_nostore (ptr);
3827         sgen_dummy_use (value);
3828 }
3829
3830 void mono_gc_wbarrier_value_copy_bitmap (gpointer _dest, gpointer _src, int size, unsigned bitmap)
3831 {
3832         mword *dest = _dest;
3833         mword *src = _src;
3834
3835         while (size) {
3836                 if (bitmap & 0x1)
3837                         mono_gc_wbarrier_generic_store (dest, (MonoObject*)*src);
3838                 else
3839                         *dest = *src;
3840                 ++src;
3841                 ++dest;
3842                 size -= SIZEOF_VOID_P;
3843                 bitmap >>= 1;
3844         }
3845 }
3846
3847 #ifdef SGEN_BINARY_PROTOCOL
3848 #undef HANDLE_PTR
3849 #define HANDLE_PTR(ptr,obj) do {                                        \
3850                 gpointer o = *(gpointer*)(ptr);                         \
3851                 if ((o)) {                                              \
3852                         gpointer d = ((char*)dest) + ((char*)(ptr) - (char*)(obj)); \
3853                         binary_protocol_wbarrier (d, o, (gpointer) LOAD_VTABLE (o)); \
3854                 }                                                       \
3855         } while (0)
3856
3857 static void
3858 scan_object_for_binary_protocol_copy_wbarrier (gpointer dest, char *start, mword desc)
3859 {
3860 #define SCAN_OBJECT_NOVTABLE
3861 #include "sgen-scan-object.h"
3862 }
3863 #endif
3864
3865 void
3866 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
3867 {
3868         HEAVY_STAT (++stat_wbarrier_value_copy);
3869         g_assert (klass->valuetype);
3870
3871         DEBUG (8, fprintf (gc_debug_file, "Adding value remset at %p, count %d, descr %p for class %s (%p)\n", dest, count, klass->gc_descr, klass->name, klass));
3872
3873         if (ptr_in_nursery (dest) || ptr_on_stack (dest) || !SGEN_CLASS_HAS_REFERENCES (klass)) {
3874                 size_t element_size = mono_class_value_size (klass, NULL);
3875                 size_t size = count * element_size;
3876                 mono_gc_memmove (dest, src, size);              
3877                 return;
3878         }
3879
3880 #ifdef SGEN_BINARY_PROTOCOL
3881         {
3882                 size_t element_size = mono_class_value_size (klass, NULL);
3883                 int i;
3884                 for (i = 0; i < count; ++i) {
3885                         scan_object_for_binary_protocol_copy_wbarrier ((char*)dest + i * element_size,
3886                                         (char*)src + i * element_size - sizeof (MonoObject),
3887                                         (mword) klass->gc_descr);
3888                 }
3889         }
3890 #endif
3891
3892         remset.wbarrier_value_copy (dest, src, count, klass);
3893 }
3894
3895 /**
3896  * mono_gc_wbarrier_object_copy:
3897  *
3898  * Write barrier to call when obj is the result of a clone or copy of an object.
3899  */
3900 void
3901 mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
3902 {
3903         int size;
3904
3905         HEAVY_STAT (++stat_wbarrier_object_copy);
3906
3907         if (ptr_in_nursery (obj) || ptr_on_stack (obj)) {
3908                 size = mono_object_class (obj)->instance_size;
3909                 mono_gc_memmove ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
3910                                 size - sizeof (MonoObject));
3911                 return; 
3912         }
3913
3914 #ifdef SGEN_BINARY_PROTOCOL
3915         scan_object_for_binary_protocol_copy_wbarrier (obj, (char*)src, (mword) src->vtable->gc_descr);
3916 #endif
3917
3918         remset.wbarrier_object_copy (obj, src);
3919 }
3920
3921
3922 /*
3923  * ######################################################################
3924  * ########  Other mono public interface functions.
3925  * ######################################################################
3926  */
3927
3928 #define REFS_SIZE 128
3929 typedef struct {
3930         void *data;
3931         MonoGCReferences callback;
3932         int flags;
3933         int count;
3934         int called;
3935         MonoObject *refs [REFS_SIZE];
3936         uintptr_t offsets [REFS_SIZE];
3937 } HeapWalkInfo;
3938
3939 #undef HANDLE_PTR
3940 #define HANDLE_PTR(ptr,obj)     do {    \
3941                 if (*(ptr)) {   \
3942                         if (hwi->count == REFS_SIZE) {  \
3943                                 hwi->callback ((MonoObject*)start, mono_object_class (start), hwi->called? 0: size, hwi->count, hwi->refs, hwi->offsets, hwi->data);    \
3944                                 hwi->count = 0; \
3945                                 hwi->called = 1;        \
3946                         }       \
3947                         hwi->offsets [hwi->count] = (char*)(ptr)-(char*)start;  \
3948                         hwi->refs [hwi->count++] = *(ptr);      \
3949                 }       \
3950         } while (0)
3951
3952 static void
3953 collect_references (HeapWalkInfo *hwi, char *start, size_t size)
3954 {
3955 #include "sgen-scan-object.h"
3956 }
3957
3958 static void
3959 walk_references (char *start, size_t size, void *data)
3960 {
3961         HeapWalkInfo *hwi = data;
3962         hwi->called = 0;
3963         hwi->count = 0;
3964         collect_references (hwi, start, size);
3965         if (hwi->count || !hwi->called)
3966                 hwi->callback ((MonoObject*)start, mono_object_class (start), hwi->called? 0: size, hwi->count, hwi->refs, hwi->offsets, hwi->data);
3967 }
3968
3969 /**
3970  * mono_gc_walk_heap:
3971  * @flags: flags for future use
3972  * @callback: a function pointer called for each object in the heap
3973  * @data: a user data pointer that is passed to callback
3974  *
3975  * This function can be used to iterate over all the live objects in the heap:
3976  * for each object, @callback is invoked, providing info about the object's
3977  * location in memory, its class, its size and the objects it references.
3978  * For each referenced object it's offset from the object address is
3979  * reported in the offsets array.
3980  * The object references may be buffered, so the callback may be invoked
3981  * multiple times for the same object: in all but the first call, the size
3982  * argument will be zero.
3983  * Note that this function can be only called in the #MONO_GC_EVENT_PRE_START_WORLD
3984  * profiler event handler.
3985  *
3986  * Returns: a non-zero value if the GC doesn't support heap walking
3987  */
3988 int
3989 mono_gc_walk_heap (int flags, MonoGCReferences callback, void *data)
3990 {
3991         HeapWalkInfo hwi;
3992
3993         hwi.flags = flags;
3994         hwi.callback = callback;
3995         hwi.data = data;
3996
3997         sgen_clear_nursery_fragments ();
3998         sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data, walk_references, &hwi, FALSE);
3999
4000         major_collector.iterate_objects (TRUE, TRUE, walk_references, &hwi);
4001         sgen_los_iterate_objects (walk_references, &hwi);
4002
4003         return 0;
4004 }
4005
4006 void
4007 mono_gc_collect (int generation)
4008 {
4009         LOCK_GC;
4010         if (generation > 1)
4011                 generation = 1;
4012         sgen_perform_collection (0, generation, "user request");
4013         UNLOCK_GC;
4014 }
4015
4016 int
4017 mono_gc_max_generation (void)
4018 {
4019         return 1;
4020 }
4021
4022 int
4023 mono_gc_collection_count (int generation)
4024 {
4025         if (generation == 0)
4026                 return stat_minor_gcs;
4027         return stat_major_gcs;
4028 }
4029
4030 int64_t
4031 mono_gc_get_used_size (void)
4032 {
4033         gint64 tot = 0;
4034         LOCK_GC;
4035         tot = los_memory_usage;
4036         tot += nursery_section->next_data - nursery_section->data;
4037         tot += major_collector.get_used_size ();
4038         /* FIXME: account for pinned objects */
4039         UNLOCK_GC;
4040         return tot;
4041 }
4042
4043 void
4044 mono_gc_disable (void)
4045 {
4046         LOCK_GC;
4047         gc_disabled++;
4048         UNLOCK_GC;
4049 }
4050
4051 void
4052 mono_gc_enable (void)
4053 {
4054         LOCK_GC;
4055         gc_disabled--;
4056         UNLOCK_GC;
4057 }
4058
4059 int
4060 mono_gc_get_los_limit (void)
4061 {
4062         return MAX_SMALL_OBJ_SIZE;
4063 }
4064
4065 gboolean
4066 mono_gc_user_markers_supported (void)
4067 {
4068         return TRUE;
4069 }
4070
4071 gboolean
4072 mono_object_is_alive (MonoObject* o)
4073 {
4074         return TRUE;
4075 }
4076
4077 int
4078 mono_gc_get_generation (MonoObject *obj)
4079 {
4080         if (ptr_in_nursery (obj))
4081                 return 0;
4082         return 1;
4083 }
4084
4085 void
4086 mono_gc_enable_events (void)
4087 {
4088 }
4089
4090 void
4091 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
4092 {
4093         sgen_register_disappearing_link (obj, link_addr, track, FALSE);
4094 }
4095
4096 void
4097 mono_gc_weak_link_remove (void **link_addr)
4098 {
4099         sgen_register_disappearing_link (NULL, link_addr, FALSE, FALSE);
4100 }
4101
4102 MonoObject*
4103 mono_gc_weak_link_get (void **link_addr)
4104 {
4105         /*
4106          * We must only load *link_addr once because it might change
4107          * under our feet, and REVEAL_POINTER (NULL) results in an
4108          * invalid reference.
4109          */
4110         void *ptr = *link_addr;
4111         if (!ptr)
4112                 return NULL;
4113
4114         /*
4115          * During the second bridge processing step the world is
4116          * running again.  That step processes all weak links once
4117          * more to null those that refer to dead objects.  Before that
4118          * is completed, those links must not be followed, so we
4119          * conservatively wait for bridge processing when any weak
4120          * link is dereferenced.
4121          */
4122         if (G_UNLIKELY (bridge_processing_in_progress))
4123                 mono_gc_wait_for_bridge_processing ();
4124
4125         return (MonoObject*) REVEAL_POINTER (ptr);
4126 }
4127
4128 gboolean
4129 mono_gc_ephemeron_array_add (MonoObject *obj)
4130 {
4131         EphemeronLinkNode *node;
4132
4133         LOCK_GC;
4134
4135         node = sgen_alloc_internal (INTERNAL_MEM_EPHEMERON_LINK);
4136         if (!node) {
4137                 UNLOCK_GC;
4138                 return FALSE;
4139         }
4140         node->array = (char*)obj;
4141         node->next = ephemeron_list;
4142         ephemeron_list = node;
4143
4144         DEBUG (5, fprintf (gc_debug_file, "Registered ephemeron array %p\n", obj));
4145
4146         UNLOCK_GC;
4147         return TRUE;
4148 }
4149
4150 void*
4151 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
4152 {
4153         void *result;
4154         LOCK_INTERRUPTION;
4155         result = func (data);
4156         UNLOCK_INTERRUPTION;
4157         return result;
4158 }
4159
4160 gboolean
4161 mono_gc_is_gc_thread (void)
4162 {
4163         gboolean result;
4164         LOCK_GC;
4165         result = mono_thread_info_current () != NULL;
4166         UNLOCK_GC;
4167         return result;
4168 }
4169
4170 static gboolean
4171 is_critical_method (MonoMethod *method)
4172 {
4173         return mono_runtime_is_critical_method (method) || sgen_is_critical_method (method);
4174 }
4175         
4176 void
4177 mono_gc_base_init (void)
4178 {
4179         MonoThreadInfoCallbacks cb;
4180         char *env;
4181         char **opts, **ptr;
4182         char *major_collector_opt = NULL;
4183         char *minor_collector_opt = NULL;
4184         glong max_heap = 0;
4185         glong soft_limit = 0;
4186         int num_workers;
4187         int result;
4188         int dummy;
4189         gboolean debug_print_allowance = FALSE;
4190         double allowance_ratio = 0, save_target = 0;
4191
4192         do {
4193                 result = InterlockedCompareExchange (&gc_initialized, -1, 0);
4194                 switch (result) {
4195                 case 1:
4196                         /* already inited */
4197                         return;
4198                 case -1:
4199                         /* being inited by another thread */
4200                         g_usleep (1000);
4201                         break;
4202                 case 0:
4203                         /* we will init it */
4204                         break;
4205                 default:
4206                         g_assert_not_reached ();
4207                 }
4208         } while (result != 0);
4209
4210         LOCK_INIT (gc_mutex);
4211
4212         pagesize = mono_pagesize ();
4213         gc_debug_file = stderr;
4214
4215         cb.thread_register = sgen_thread_register;
4216         cb.thread_unregister = sgen_thread_unregister;
4217         cb.thread_attach = sgen_thread_attach;
4218         cb.mono_method_is_critical = (gpointer)is_critical_method;
4219 #ifndef HOST_WIN32
4220         cb.mono_gc_pthread_create = (gpointer)mono_gc_pthread_create;
4221 #endif
4222
4223         mono_threads_init (&cb, sizeof (SgenThreadInfo));
4224
4225         LOCK_INIT (sgen_interruption_mutex);
4226         LOCK_INIT (pin_queue_mutex);
4227
4228         init_user_copy_or_mark_key ();
4229
4230         if ((env = getenv ("MONO_GC_PARAMS"))) {
4231                 opts = g_strsplit (env, ",", -1);
4232                 for (ptr = opts; *ptr; ++ptr) {
4233                         char *opt = *ptr;
4234                         if (g_str_has_prefix (opt, "major=")) {
4235                                 opt = strchr (opt, '=') + 1;
4236                                 major_collector_opt = g_strdup (opt);
4237                         } else if (g_str_has_prefix (opt, "minor=")) {
4238                                 opt = strchr (opt, '=') + 1;
4239                                 minor_collector_opt = g_strdup (opt);
4240                         }
4241                 }
4242         } else {
4243                 opts = NULL;
4244         }
4245
4246         init_stats ();
4247         sgen_init_internal_allocator ();
4248         sgen_init_nursery_allocator ();
4249
4250         sgen_register_fixed_internal_mem_type (INTERNAL_MEM_SECTION, SGEN_SIZEOF_GC_MEM_SECTION);
4251         sgen_register_fixed_internal_mem_type (INTERNAL_MEM_FINALIZE_READY_ENTRY, sizeof (FinalizeReadyEntry));
4252         sgen_register_fixed_internal_mem_type (INTERNAL_MEM_GRAY_QUEUE, sizeof (GrayQueueSection));
4253         g_assert (sizeof (GenericStoreRememberedSet) == sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
4254         sgen_register_fixed_internal_mem_type (INTERNAL_MEM_STORE_REMSET, sizeof (GenericStoreRememberedSet));
4255         sgen_register_fixed_internal_mem_type (INTERNAL_MEM_EPHEMERON_LINK, sizeof (EphemeronLinkNode));
4256
4257 #ifndef HAVE_KW_THREAD
4258         mono_native_tls_alloc (&thread_info_key, NULL);
4259 #endif
4260
4261         /*
4262          * This needs to happen before any internal allocations because
4263          * it inits the small id which is required for hazard pointer
4264          * operations.
4265          */
4266         sgen_os_init ();
4267
4268         mono_thread_info_attach (&dummy);
4269
4270         if (!minor_collector_opt) {
4271                 sgen_simple_nursery_init (&sgen_minor_collector);
4272         } else {
4273                 if (!strcmp (minor_collector_opt, "simple"))
4274                         sgen_simple_nursery_init (&sgen_minor_collector);
4275                 else if (!strcmp (minor_collector_opt, "split"))
4276                         sgen_split_nursery_init (&sgen_minor_collector);
4277                 else {
4278                         fprintf (stderr, "Unknown minor collector `%s'.\n", minor_collector_opt);
4279                         exit (1);
4280                 }
4281         }
4282
4283         if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep")) {
4284                 sgen_marksweep_init (&major_collector);
4285         } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-fixed")) {
4286                 sgen_marksweep_fixed_init (&major_collector);
4287         } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-par")) {
4288                 sgen_marksweep_par_init (&major_collector);
4289         } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-fixed-par")) {
4290                 sgen_marksweep_fixed_par_init (&major_collector);
4291         } else if (!strcmp (major_collector_opt, "copying")) {
4292                 sgen_copying_init (&major_collector);
4293         } else {
4294                 fprintf (stderr, "Unknown major collector `%s'.\n", major_collector_opt);
4295                 exit (1);
4296         }
4297
4298 #ifdef SGEN_HAVE_CARDTABLE
4299         use_cardtable = major_collector.supports_cardtable;
4300 #else
4301         use_cardtable = FALSE;
4302 #endif
4303
4304         num_workers = mono_cpu_count ();
4305         g_assert (num_workers > 0);
4306         if (num_workers > 16)
4307                 num_workers = 16;
4308
4309         ///* Keep this the default for now */
4310         /* Precise marking is broken on all supported targets. Disable until fixed. */
4311         conservative_stack_mark = TRUE;
4312
4313         sgen_nursery_size = DEFAULT_NURSERY_SIZE;
4314
4315         if (opts) {
4316                 for (ptr = opts; *ptr; ++ptr) {
4317                         char *opt = *ptr;
4318                         if (g_str_has_prefix (opt, "major="))
4319                                 continue;
4320                         if (g_str_has_prefix (opt, "minor="))
4321                                 continue;
4322                         if (g_str_has_prefix (opt, "wbarrier=")) {
4323                                 opt = strchr (opt, '=') + 1;
4324                                 if (strcmp (opt, "remset") == 0) {
4325                                         use_cardtable = FALSE;
4326                                 } else if (strcmp (opt, "cardtable") == 0) {
4327                                         if (!use_cardtable) {
4328                                                 if (major_collector.supports_cardtable)
4329                                                         fprintf (stderr, "The cardtable write barrier is not supported on this platform.\n");
4330                                                 else
4331                                                         fprintf (stderr, "The major collector does not support the cardtable write barrier.\n");
4332                                                 exit (1);
4333                                         }
4334                                 } else {
4335                                         fprintf (stderr, "wbarrier must either be `remset' or `cardtable'.");
4336                                         exit (1);
4337                                 }
4338                                 continue;
4339                         }
4340                         if (g_str_has_prefix (opt, "max-heap-size=")) {
4341                                 opt = strchr (opt, '=') + 1;
4342                                 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &max_heap)) {
4343                                         if ((max_heap & (mono_pagesize () - 1))) {
4344                                                 fprintf (stderr, "max-heap-size size must be a multiple of %d.\n", mono_pagesize ());
4345                                                 exit (1);
4346                                         }
4347                                 } else {
4348                                         fprintf (stderr, "max-heap-size must be an integer.\n");
4349                                         exit (1);
4350                                 }
4351                                 continue;
4352                         }
4353                         if (g_str_has_prefix (opt, "soft-heap-limit=")) {
4354                                 opt = strchr (opt, '=') + 1;
4355                                 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &soft_limit)) {
4356                                         if (soft_limit <= 0) {
4357                                                 fprintf (stderr, "soft-heap-limit must be positive.\n");
4358                                                 exit (1);
4359                                         }
4360                                 } else {
4361                                         fprintf (stderr, "soft-heap-limit must be an integer.\n");
4362                                         exit (1);
4363                                 }
4364                                 continue;
4365                         }
4366                         if (g_str_has_prefix (opt, "workers=")) {
4367                                 long val;
4368                                 char *endptr;
4369                                 if (!major_collector.is_parallel) {
4370                                         fprintf (stderr, "The workers= option can only be used for parallel collectors.");
4371                                         exit (1);
4372                                 }
4373                                 opt = strchr (opt, '=') + 1;
4374                                 val = strtol (opt, &endptr, 10);
4375                                 if (!*opt || *endptr) {
4376                                         fprintf (stderr, "Cannot parse the workers= option value.");
4377                                         exit (1);
4378                                 }
4379                                 if (val <= 0 || val > 16) {
4380                                         fprintf (stderr, "The number of workers must be in the range 1 to 16.");
4381                                         exit (1);
4382                                 }
4383                                 num_workers = (int)val;
4384                                 continue;
4385                         }
4386                         if (g_str_has_prefix (opt, "stack-mark=")) {
4387                                 opt = strchr (opt, '=') + 1;
4388                                 if (!strcmp (opt, "precise")) {
4389                                         conservative_stack_mark = FALSE;
4390                                 } else if (!strcmp (opt, "conservative")) {
4391                                         conservative_stack_mark = TRUE;
4392                                 } else {
4393                                         fprintf (stderr, "Invalid value '%s' for stack-mark= option, possible values are: 'precise', 'conservative'.\n", opt);
4394                                         exit (1);
4395                                 }
4396                                 continue;
4397                         }
4398                         if (g_str_has_prefix (opt, "bridge=")) {
4399                                 opt = strchr (opt, '=') + 1;
4400                                 sgen_register_test_bridge_callbacks (g_strdup (opt));
4401                                 continue;
4402                         }
4403 #ifdef USER_CONFIG
4404                         if (g_str_has_prefix (opt, "nursery-size=")) {
4405                                 long val;
4406                                 opt = strchr (opt, '=') + 1;
4407                                 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &val)) {
4408                                         sgen_nursery_size = val;
4409 #ifdef SGEN_ALIGN_NURSERY
4410                                         if ((val & (val - 1))) {
4411                                                 fprintf (stderr, "The nursery size must be a power of two.\n");
4412                                                 exit (1);
4413                                         }
4414
4415                                         if (val < SGEN_MAX_NURSERY_WASTE) {
4416                                                 fprintf (stderr, "The nursery size must be at least %d bytes.\n", SGEN_MAX_NURSERY_WASTE);
4417                                                 exit (1);
4418                                         }
4419
4420                                         sgen_nursery_bits = 0;
4421                                         while (1 << (++ sgen_nursery_bits) != sgen_nursery_size)
4422                                                 ;
4423 #endif
4424                                 } else {
4425                                         fprintf (stderr, "nursery-size must be an integer.\n");
4426                                         exit (1);
4427                                 }
4428                                 continue;
4429                         }
4430 #endif
4431                         if (g_str_has_prefix (opt, "save-target-ratio=")) {
4432                                 char *endptr;
4433                                 opt = strchr (opt, '=') + 1;
4434                                 save_target = strtod (opt, &endptr);
4435                                 if (endptr == opt) {
4436                                         fprintf (stderr, "save-target-ratio must be a number.");
4437                                         exit (1);
4438                                 }
4439                                 if (save_target < SGEN_MIN_SAVE_TARGET_RATIO || save_target > SGEN_MAX_SAVE_TARGET_RATIO) {
4440                                         fprintf (stderr, "save-target-ratio must be between %.2f - %.2f.", SGEN_MIN_SAVE_TARGET_RATIO, SGEN_MAX_SAVE_TARGET_RATIO);
4441                                         exit (1);
4442                                 }
4443                                 continue;
4444                         }
4445                         if (g_str_has_prefix (opt, "default-allowance-ratio=")) {
4446                                 char *endptr;
4447                                 opt = strchr (opt, '=') + 1;
4448
4449                                 allowance_ratio = strtod (opt, &endptr);
4450                                 if (endptr == opt) {
4451                                         fprintf (stderr, "save-target-ratio must be a number.");
4452                                         exit (1);
4453                                 }
4454                                 if (allowance_ratio < SGEN_MIN_ALLOWANCE_NURSERY_SIZE_RATIO || allowance_ratio > SGEN_MIN_ALLOWANCE_NURSERY_SIZE_RATIO) {
4455                                         fprintf (stderr, "default-allowance-ratio must be between %.2f - %.2f.", SGEN_MIN_ALLOWANCE_NURSERY_SIZE_RATIO, SGEN_MIN_ALLOWANCE_NURSERY_SIZE_RATIO);
4456                                         exit (1);
4457                                 }
4458                                 continue;
4459                         }
4460
4461                         if (major_collector.handle_gc_param && major_collector.handle_gc_param (opt))
4462                                 continue;
4463
4464                         if (sgen_minor_collector.handle_gc_param && sgen_minor_collector.handle_gc_param (opt))
4465                                 continue;
4466
4467                         fprintf (stderr, "MONO_GC_PARAMS must be a comma-delimited list of one or more of the following:\n");
4468                         fprintf (stderr, "  max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
4469                         fprintf (stderr, "  soft-heap-limit=n (where N is an integer, possibly with a k, m or a g suffix)\n");
4470                         fprintf (stderr, "  nursery-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
4471                         fprintf (stderr, "  major=COLLECTOR (where COLLECTOR is `marksweep', `marksweep-par', 'marksweep-fixed', 'marksweep-fixed-par' or `copying')\n");
4472                         fprintf (stderr, "  minor=COLLECTOR (where COLLECTOR is `simple' or `split')\n");
4473                         fprintf (stderr, "  wbarrier=WBARRIER (where WBARRIER is `remset' or `cardtable')\n");
4474                         fprintf (stderr, "  stack-mark=MARK-METHOD (where MARK-METHOD is 'precise' or 'conservative')\n");
4475                         if (major_collector.print_gc_param_usage)
4476                                 major_collector.print_gc_param_usage ();
4477                         if (sgen_minor_collector.print_gc_param_usage)
4478                                 sgen_minor_collector.print_gc_param_usage ();
4479                         fprintf (stderr, " Experimental options:\n");
4480                         fprintf (stderr, "  save-target-ratio=R (where R must be between %.2f - %.2f).\n", SGEN_MIN_SAVE_TARGET_RATIO, SGEN_MAX_SAVE_TARGET_RATIO);
4481                         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);
4482                         exit (1);
4483                 }
4484                 g_strfreev (opts);
4485         }
4486
4487         if (major_collector.is_parallel)
4488                 sgen_workers_init (num_workers);
4489
4490         if (major_collector_opt)
4491                 g_free (major_collector_opt);
4492
4493         if (minor_collector_opt)
4494                 g_free (minor_collector_opt);
4495
4496         alloc_nursery ();
4497
4498         if ((env = getenv ("MONO_GC_DEBUG"))) {
4499                 opts = g_strsplit (env, ",", -1);
4500                 for (ptr = opts; ptr && *ptr; ptr ++) {
4501                         char *opt = *ptr;
4502                         if (opt [0] >= '0' && opt [0] <= '9') {
4503                                 gc_debug_level = atoi (opt);
4504                                 opt++;
4505                                 if (opt [0] == ':')
4506                                         opt++;
4507                                 if (opt [0]) {
4508 #ifdef HOST_WIN32
4509                                         char *rf = g_strdup_printf ("%s.%d", opt, GetCurrentProcessId ());
4510 #else
4511                                         char *rf = g_strdup_printf ("%s.%d", opt, getpid ());
4512 #endif
4513                                         gc_debug_file = fopen (rf, "wb");
4514                                         if (!gc_debug_file)
4515                                                 gc_debug_file = stderr;
4516                                         g_free (rf);
4517                                 }
4518                         } else if (!strcmp (opt, "print-allowance")) {
4519                                 debug_print_allowance = TRUE;
4520                         } else if (!strcmp (opt, "print-pinning")) {
4521                                 do_pin_stats = TRUE;
4522                         } else if (!strcmp (opt, "verify-before-allocs")) {
4523                                 verify_before_allocs = 1;
4524                                 has_per_allocation_action = TRUE;
4525                         } else if (g_str_has_prefix (opt, "verify-before-allocs=")) {
4526                                 char *arg = strchr (opt, '=') + 1;
4527                                 verify_before_allocs = atoi (arg);
4528                                 has_per_allocation_action = TRUE;
4529                         } else if (!strcmp (opt, "collect-before-allocs")) {
4530                                 collect_before_allocs = 1;
4531                                 has_per_allocation_action = TRUE;
4532                         } else if (g_str_has_prefix (opt, "collect-before-allocs=")) {
4533                                 char *arg = strchr (opt, '=') + 1;
4534                                 has_per_allocation_action = TRUE;
4535                                 collect_before_allocs = atoi (arg);
4536                         } else if (!strcmp (opt, "verify-before-collections")) {
4537                                 whole_heap_check_before_collection = TRUE;
4538                         } else if (!strcmp (opt, "check-at-minor-collections")) {
4539                                 consistency_check_at_minor_collection = TRUE;
4540                                 nursery_clear_policy = CLEAR_AT_GC;
4541                         } else if (!strcmp (opt, "xdomain-checks")) {
4542                                 xdomain_checks = TRUE;
4543                         } else if (!strcmp (opt, "clear-at-gc")) {
4544                                 nursery_clear_policy = CLEAR_AT_GC;
4545                         } else if (!strcmp (opt, "clear-nursery-at-gc")) {
4546                                 nursery_clear_policy = CLEAR_AT_GC;
4547                         } else if (!strcmp (opt, "check-scan-starts")) {
4548                                 do_scan_starts_check = TRUE;
4549                         } else if (!strcmp (opt, "verify-nursery-at-minor-gc")) {
4550                                 do_verify_nursery = TRUE;
4551                         } else if (!strcmp (opt, "dump-nursery-at-minor-gc")) {
4552                                 do_dump_nursery_content = TRUE;
4553                         } else if (!strcmp (opt, "no-managed-allocator")) {
4554                                 sgen_set_use_managed_allocator (FALSE);
4555                         } else if (!strcmp (opt, "disable-minor")) {
4556                                 disable_minor_collections = TRUE;
4557                         } else if (!strcmp (opt, "disable-major")) {
4558                                 disable_major_collections = TRUE;
4559                         } else if (g_str_has_prefix (opt, "heap-dump=")) {
4560                                 char *filename = strchr (opt, '=') + 1;
4561                                 nursery_clear_policy = CLEAR_AT_GC;
4562                                 heap_dump_file = fopen (filename, "w");
4563                                 if (heap_dump_file) {
4564                                         fprintf (heap_dump_file, "<sgen-dump>\n");
4565                                         do_pin_stats = TRUE;
4566                                 }
4567 #ifdef SGEN_BINARY_PROTOCOL
4568                         } else if (g_str_has_prefix (opt, "binary-protocol=")) {
4569                                 char *filename = strchr (opt, '=') + 1;
4570                                 binary_protocol_init (filename);
4571                                 if (use_cardtable)
4572                                         fprintf (stderr, "Warning: Cardtable write barriers will not be binary-protocolled.\n");
4573 #endif
4574                         } else {
4575                                 fprintf (stderr, "Invalid format for the MONO_GC_DEBUG env variable: '%s'\n", env);
4576                                 fprintf (stderr, "The format is: MONO_GC_DEBUG=[l[:filename]|<option>]+ where l is a debug level 0-9.\n");
4577                                 fprintf (stderr, "Valid options are:\n");
4578                                 fprintf (stderr, "  collect-before-allocs[=<n>]\n");
4579                                 fprintf (stderr, "  verify-before-allocs[=<n>]\n");
4580                                 fprintf (stderr, "  check-at-minor-collections\n");
4581                                 fprintf (stderr, "  verify-before-collections\n");
4582                                 fprintf (stderr, "  verify-nursery-at-minor-gc\n");
4583                                 fprintf (stderr, "  dump-nursery-at-minor-gc\n");
4584                                 fprintf (stderr, "  disable-minor\n");
4585                                 fprintf (stderr, "  disable-major\n");
4586                                 fprintf (stderr, "  xdomain-checks\n");
4587                                 fprintf (stderr, "  clear-at-gc\n");
4588                                 fprintf (stderr, "  clear-nursery-at-gc\n");
4589                                 fprintf (stderr, "  check-scan-starts\n");
4590                                 fprintf (stderr, "  no-managed-allocator\n");
4591                                 fprintf (stderr, "  print-allowance\n");
4592                                 fprintf (stderr, "  print-pinning\n");
4593                                 fprintf (stderr, "  heap-dump=<filename>\n");
4594 #ifdef SGEN_BINARY_PROTOCOL
4595                                 fprintf (stderr, "  binary-protocol=<filename>\n");
4596 #endif
4597                                 exit (1);
4598                         }
4599                 }
4600                 g_strfreev (opts);
4601         }
4602
4603         if (major_collector.is_parallel) {
4604                 if (heap_dump_file) {
4605                         fprintf (stderr, "Error: Cannot do heap dump with the parallel collector.\n");
4606                         exit (1);
4607                 }
4608                 if (do_pin_stats) {
4609                         fprintf (stderr, "Error: Cannot gather pinning statistics with the parallel collector.\n");
4610                         exit (1);
4611                 }
4612         }
4613
4614         if (major_collector.post_param_init)
4615                 major_collector.post_param_init ();
4616
4617         sgen_memgov_init (max_heap, soft_limit, debug_print_allowance, allowance_ratio, save_target);
4618
4619         memset (&remset, 0, sizeof (remset));
4620
4621 #ifdef SGEN_HAVE_CARDTABLE
4622         if (use_cardtable)
4623                 sgen_card_table_init (&remset);
4624         else
4625 #endif
4626                 sgen_ssb_init (&remset);
4627
4628         if (remset.register_thread)
4629                 remset.register_thread (mono_thread_info_current ());
4630
4631         gc_initialized = 1;
4632 }
4633
4634 const char *
4635 mono_gc_get_gc_name (void)
4636 {
4637         return "sgen";
4638 }
4639
4640 static MonoMethod *write_barrier_method;
4641
4642 gboolean
4643 sgen_is_critical_method (MonoMethod *method)
4644 {
4645         return (method == write_barrier_method || sgen_is_managed_allocator (method));
4646 }
4647
4648 gboolean
4649 sgen_has_critical_method (void)
4650 {
4651         return write_barrier_method || sgen_has_managed_allocator ();
4652 }
4653
4654 static void
4655 emit_nursery_check (MonoMethodBuilder *mb, int *nursery_check_return_labels)
4656 {
4657         memset (nursery_check_return_labels, 0, sizeof (int) * 3);
4658 #ifdef SGEN_ALIGN_NURSERY
4659         // if (ptr_in_nursery (ptr)) return;
4660         /*
4661          * Masking out the bits might be faster, but we would have to use 64 bit
4662          * immediates, which might be slower.
4663          */
4664         mono_mb_emit_ldarg (mb, 0);
4665         mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
4666         mono_mb_emit_byte (mb, CEE_SHR_UN);
4667         mono_mb_emit_icon (mb, (mword)sgen_get_nursery_start () >> DEFAULT_NURSERY_BITS);
4668         nursery_check_return_labels [0] = mono_mb_emit_branch (mb, CEE_BEQ);
4669
4670         // if (!ptr_in_nursery (*ptr)) return;
4671         mono_mb_emit_ldarg (mb, 0);
4672         mono_mb_emit_byte (mb, CEE_LDIND_I);
4673         mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
4674         mono_mb_emit_byte (mb, CEE_SHR_UN);
4675         mono_mb_emit_icon (mb, (mword)sgen_get_nursery_start () >> DEFAULT_NURSERY_BITS);
4676         nursery_check_return_labels [1] = mono_mb_emit_branch (mb, CEE_BNE_UN);
4677 #else
4678         int label_continue1, label_continue2;
4679         int dereferenced_var;
4680
4681         // if (ptr < (sgen_get_nursery_start ())) goto continue;
4682         mono_mb_emit_ldarg (mb, 0);
4683         mono_mb_emit_ptr (mb, (gpointer) sgen_get_nursery_start ());
4684         label_continue_1 = mono_mb_emit_branch (mb, CEE_BLT);
4685
4686         // if (ptr >= sgen_get_nursery_end ())) goto continue;
4687         mono_mb_emit_ldarg (mb, 0);
4688         mono_mb_emit_ptr (mb, (gpointer) sgen_get_nursery_end ());
4689         label_continue_2 = mono_mb_emit_branch (mb, CEE_BGE);
4690
4691         // Otherwise return
4692         nursery_check_return_labels [0] = mono_mb_emit_branch (mb, CEE_BR);
4693
4694         // continue:
4695         mono_mb_patch_branch (mb, label_continue_1);
4696         mono_mb_patch_branch (mb, label_continue_2);
4697
4698         // Dereference and store in local var
4699         dereferenced_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
4700         mono_mb_emit_ldarg (mb, 0);
4701         mono_mb_emit_byte (mb, CEE_LDIND_I);
4702         mono_mb_emit_stloc (mb, dereferenced_var);
4703
4704         // if (*ptr < sgen_get_nursery_start ()) return;
4705         mono_mb_emit_ldloc (mb, dereferenced_var);
4706         mono_mb_emit_ptr (mb, (gpointer) sgen_get_nursery_start ());
4707         nursery_check_return_labels [1] = mono_mb_emit_branch (mb, CEE_BLT);
4708
4709         // if (*ptr >= sgen_get_nursery_end ()) return;
4710         mono_mb_emit_ldloc (mb, dereferenced_var);
4711         mono_mb_emit_ptr (mb, (gpointer) sgen_get_nursery_end ());
4712         nursery_check_return_labels [2] = mono_mb_emit_branch (mb, CEE_BGE);
4713 #endif  
4714 }
4715
4716 MonoMethod*
4717 mono_gc_get_write_barrier (void)
4718 {
4719         MonoMethod *res;
4720         MonoMethodBuilder *mb;
4721         MonoMethodSignature *sig;
4722 #ifdef MANAGED_WBARRIER
4723         int i, nursery_check_labels [3];
4724         int label_no_wb_3, label_no_wb_4, label_need_wb, label_slow_path;
4725         int buffer_var, buffer_index_var, dummy_var;
4726
4727 #ifdef HAVE_KW_THREAD
4728         int stack_end_offset = -1, store_remset_buffer_offset = -1;
4729         int store_remset_buffer_index_offset = -1, store_remset_buffer_index_addr_offset = -1;
4730
4731         MONO_THREAD_VAR_OFFSET (stack_end, stack_end_offset);
4732         g_assert (stack_end_offset != -1);
4733         MONO_THREAD_VAR_OFFSET (store_remset_buffer, store_remset_buffer_offset);
4734         g_assert (store_remset_buffer_offset != -1);
4735         MONO_THREAD_VAR_OFFSET (store_remset_buffer_index, store_remset_buffer_index_offset);
4736         g_assert (store_remset_buffer_index_offset != -1);
4737         MONO_THREAD_VAR_OFFSET (store_remset_buffer_index_addr, store_remset_buffer_index_addr_offset);
4738         g_assert (store_remset_buffer_index_addr_offset != -1);
4739 #endif
4740 #endif
4741
4742         // FIXME: Maybe create a separate version for ctors (the branch would be
4743         // correctly predicted more times)
4744         if (write_barrier_method)
4745                 return write_barrier_method;
4746
4747         /* Create the IL version of mono_gc_barrier_generic_store () */
4748         sig = mono_metadata_signature_alloc (mono_defaults.corlib, 1);
4749         sig->ret = &mono_defaults.void_class->byval_arg;
4750         sig->params [0] = &mono_defaults.int_class->byval_arg;
4751
4752         mb = mono_mb_new (mono_defaults.object_class, "wbarrier", MONO_WRAPPER_WRITE_BARRIER);
4753
4754 #ifdef MANAGED_WBARRIER
4755         if (use_cardtable) {
4756                 emit_nursery_check (mb, nursery_check_labels);
4757                 /*
4758                 addr = sgen_cardtable + ((address >> CARD_BITS) & CARD_MASK)
4759                 *addr = 1;
4760
4761                 sgen_cardtable: 
4762                         LDC_PTR sgen_cardtable
4763
4764                 address >> CARD_BITS
4765                         LDARG_0
4766                         LDC_I4 CARD_BITS
4767                         SHR_UN
4768                 if (SGEN_HAVE_OVERLAPPING_CARDS) {
4769                         LDC_PTR card_table_mask
4770                         AND
4771                 }
4772                 AND
4773                 ldc_i4_1
4774                 stind_i1
4775                 */
4776                 mono_mb_emit_ptr (mb, sgen_cardtable);
4777                 mono_mb_emit_ldarg (mb, 0);
4778                 mono_mb_emit_icon (mb, CARD_BITS);
4779                 mono_mb_emit_byte (mb, CEE_SHR_UN);
4780 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
4781                 mono_mb_emit_ptr (mb, (gpointer)CARD_MASK);
4782                 mono_mb_emit_byte (mb, CEE_AND);
4783 #endif
4784                 mono_mb_emit_byte (mb, CEE_ADD);
4785                 mono_mb_emit_icon (mb, 1);
4786                 mono_mb_emit_byte (mb, CEE_STIND_I1);
4787
4788                 // return;
4789                 for (i = 0; i < 3; ++i) {
4790                         if (nursery_check_labels [i])
4791                                 mono_mb_patch_branch (mb, nursery_check_labels [i]);
4792                 }               
4793                 mono_mb_emit_byte (mb, CEE_RET);
4794         } else if (mono_runtime_has_tls_get ()) {
4795                 emit_nursery_check (mb, nursery_check_labels);
4796
4797                 // if (ptr >= stack_end) goto need_wb;
4798                 mono_mb_emit_ldarg (mb, 0);
4799                 EMIT_TLS_ACCESS (mb, stack_end, stack_end_offset);
4800                 label_need_wb = mono_mb_emit_branch (mb, CEE_BGE_UN);
4801
4802                 // if (ptr >= stack_start) return;
4803                 dummy_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
4804                 mono_mb_emit_ldarg (mb, 0);
4805                 mono_mb_emit_ldloc_addr (mb, dummy_var);
4806                 label_no_wb_3 = mono_mb_emit_branch (mb, CEE_BGE_UN);
4807
4808                 // need_wb:
4809                 mono_mb_patch_branch (mb, label_need_wb);
4810
4811                 // buffer = STORE_REMSET_BUFFER;
4812                 buffer_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
4813                 EMIT_TLS_ACCESS (mb, store_remset_buffer, store_remset_buffer_offset);
4814                 mono_mb_emit_stloc (mb, buffer_var);
4815
4816                 // buffer_index = STORE_REMSET_BUFFER_INDEX;
4817                 buffer_index_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
4818                 EMIT_TLS_ACCESS (mb, store_remset_buffer_index, store_remset_buffer_index_offset);
4819                 mono_mb_emit_stloc (mb, buffer_index_var);
4820
4821                 // if (buffer [buffer_index] == ptr) return;
4822                 mono_mb_emit_ldloc (mb, buffer_var);
4823                 mono_mb_emit_ldloc (mb, buffer_index_var);
4824                 g_assert (sizeof (gpointer) == 4 || sizeof (gpointer) == 8);
4825                 mono_mb_emit_icon (mb, sizeof (gpointer) == 4 ? 2 : 3);
4826                 mono_mb_emit_byte (mb, CEE_SHL);
4827                 mono_mb_emit_byte (mb, CEE_ADD);
4828                 mono_mb_emit_byte (mb, CEE_LDIND_I);
4829                 mono_mb_emit_ldarg (mb, 0);
4830                 label_no_wb_4 = mono_mb_emit_branch (mb, CEE_BEQ);
4831
4832                 // ++buffer_index;
4833                 mono_mb_emit_ldloc (mb, buffer_index_var);
4834                 mono_mb_emit_icon (mb, 1);
4835                 mono_mb_emit_byte (mb, CEE_ADD);
4836                 mono_mb_emit_stloc (mb, buffer_index_var);
4837
4838                 // if (buffer_index >= STORE_REMSET_BUFFER_SIZE) goto slow_path;
4839                 mono_mb_emit_ldloc (mb, buffer_index_var);
4840                 mono_mb_emit_icon (mb, STORE_REMSET_BUFFER_SIZE);
4841                 label_slow_path = mono_mb_emit_branch (mb, CEE_BGE);
4842
4843                 // buffer [buffer_index] = ptr;
4844                 mono_mb_emit_ldloc (mb, buffer_var);
4845                 mono_mb_emit_ldloc (mb, buffer_index_var);
4846                 g_assert (sizeof (gpointer) == 4 || sizeof (gpointer) == 8);
4847                 mono_mb_emit_icon (mb, sizeof (gpointer) == 4 ? 2 : 3);
4848                 mono_mb_emit_byte (mb, CEE_SHL);
4849                 mono_mb_emit_byte (mb, CEE_ADD);
4850                 mono_mb_emit_ldarg (mb, 0);
4851                 mono_mb_emit_byte (mb, CEE_STIND_I);
4852
4853                 // STORE_REMSET_BUFFER_INDEX = buffer_index;
4854                 EMIT_TLS_ACCESS (mb, store_remset_buffer_index_addr, store_remset_buffer_index_addr_offset);
4855                 mono_mb_emit_ldloc (mb, buffer_index_var);
4856                 mono_mb_emit_byte (mb, CEE_STIND_I);
4857
4858                 // return;
4859                 for (i = 0; i < 3; ++i) {
4860                         if (nursery_check_labels [i])
4861                                 mono_mb_patch_branch (mb, nursery_check_labels [i]);
4862                 }
4863                 mono_mb_patch_branch (mb, label_no_wb_3);
4864                 mono_mb_patch_branch (mb, label_no_wb_4);
4865                 mono_mb_emit_byte (mb, CEE_RET);
4866
4867                 // slow path
4868                 mono_mb_patch_branch (mb, label_slow_path);
4869
4870                 mono_mb_emit_ldarg (mb, 0);
4871                 mono_mb_emit_icall (mb, mono_gc_wbarrier_generic_nostore);
4872                 mono_mb_emit_byte (mb, CEE_RET);
4873         } else
4874 #endif
4875         {
4876                 mono_mb_emit_ldarg (mb, 0);
4877                 mono_mb_emit_icall (mb, mono_gc_wbarrier_generic_nostore);
4878                 mono_mb_emit_byte (mb, CEE_RET);
4879         }
4880
4881         res = mono_mb_create_method (mb, sig, 16);
4882         mono_mb_free (mb);
4883
4884         mono_loader_lock ();
4885         if (write_barrier_method) {
4886                 /* Already created */
4887                 mono_free_method (res);
4888         } else {
4889                 /* double-checked locking */
4890                 mono_memory_barrier ();
4891                 write_barrier_method = res;
4892         }
4893         mono_loader_unlock ();
4894
4895         return write_barrier_method;
4896 }
4897
4898 char*
4899 mono_gc_get_description (void)
4900 {
4901         return g_strdup ("sgen");
4902 }
4903
4904 void
4905 mono_gc_set_desktop_mode (void)
4906 {
4907 }
4908
4909 gboolean
4910 mono_gc_is_moving (void)
4911 {
4912         return TRUE;
4913 }
4914
4915 gboolean
4916 mono_gc_is_disabled (void)
4917 {
4918         return FALSE;
4919 }
4920
4921 void
4922 sgen_debug_printf (int level, const char *format, ...)
4923 {
4924         va_list ap;
4925
4926         if (level > gc_debug_level)
4927                 return;
4928
4929         va_start (ap, format);
4930         vfprintf (gc_debug_file, format, ap);
4931         va_end (ap);
4932 }
4933
4934 FILE*
4935 sgen_get_logfile (void)
4936 {
4937         return gc_debug_file;
4938 }
4939
4940 #ifdef HOST_WIN32
4941 BOOL APIENTRY mono_gc_dllmain (HMODULE module_handle, DWORD reason, LPVOID reserved)
4942 {
4943         return TRUE;
4944 }
4945 #endif
4946
4947 NurseryClearPolicy
4948 sgen_get_nursery_clear_policy (void)
4949 {
4950         return nursery_clear_policy;
4951 }
4952
4953 MonoVTable*
4954 sgen_get_array_fill_vtable (void)
4955 {
4956         if (!array_fill_vtable) {
4957                 static MonoClass klass;
4958                 static MonoVTable vtable;
4959                 gsize bmap;
4960
4961                 MonoDomain *domain = mono_get_root_domain ();
4962                 g_assert (domain);
4963
4964                 klass.element_class = mono_defaults.byte_class;
4965                 klass.rank = 1;
4966                 klass.instance_size = sizeof (MonoArray);
4967                 klass.sizes.element_size = 1;
4968                 klass.name = "array_filler_type";
4969
4970                 vtable.klass = &klass;
4971                 bmap = 0;
4972                 vtable.gc_descr = mono_gc_make_descr_for_array (TRUE, &bmap, 0, 1);
4973                 vtable.rank = 1;
4974
4975                 array_fill_vtable = &vtable;
4976         }
4977         return array_fill_vtable;
4978 }
4979
4980 void
4981 sgen_gc_lock (void)
4982 {
4983         LOCK_GC;
4984 }
4985
4986 void
4987 sgen_gc_unlock (void)
4988 {
4989         UNLOCK_GC;
4990 }
4991
4992 void
4993 sgen_major_collector_iterate_live_block_ranges (sgen_cardtable_block_callback callback)
4994 {
4995         major_collector.iterate_live_block_ranges (callback);
4996 }
4997
4998 void
4999 sgen_major_collector_scan_card_table (SgenGrayQueue *queue)
5000 {
5001         major_collector.scan_card_table (queue);
5002 }
5003
5004 SgenMajorCollector*
5005 sgen_get_major_collector (void)
5006 {
5007         return &major_collector;
5008 }
5009
5010 void mono_gc_set_skip_thread (gboolean skip)
5011 {
5012         SgenThreadInfo *info = mono_thread_info_current ();
5013
5014         LOCK_GC;
5015         info->gc_disabled = skip;
5016         UNLOCK_GC;
5017 }
5018
5019 SgenRemeberedSet*
5020 sgen_get_remset (void)
5021 {
5022         return &remset;
5023 }
5024
5025 guint
5026 mono_gc_get_vtable_bits (MonoClass *class)
5027 {
5028         if (sgen_need_bridge_processing () && sgen_is_bridge_class (class))
5029                 return SGEN_GC_BIT_BRIDGE_OBJECT;
5030         return 0;
5031 }
5032
5033 void
5034 mono_gc_register_altstack (gpointer stack, gint32 stack_size, gpointer altstack, gint32 altstack_size)
5035 {
5036         // FIXME:
5037 }
5038
5039
5040 void
5041 sgen_check_whole_heap_stw (void)
5042 {
5043         sgen_stop_world (0);
5044         sgen_clear_nursery_fragments ();
5045         sgen_check_whole_heap ();
5046         sgen_restart_world (0, NULL);
5047 }
5048
5049 void
5050 sgen_gc_event_moves (void)
5051 {
5052         if (moved_objects_idx) {
5053                 mono_profiler_gc_moves (moved_objects, moved_objects_idx);
5054                 moved_objects_idx = 0;
5055         }
5056 }
5057
5058 #endif /* HAVE_SGEN_GC */