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