8a3a8129777fcfff0b826e63540d6f883799a793
[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  *
7  * Copyright 2005-2010 Novell, Inc (http://www.novell.com)
8  *
9  * Thread start/stop adapted from Boehm's GC:
10  * Copyright (c) 1994 by Xerox Corporation.  All rights reserved.
11  * Copyright (c) 1996 by Silicon Graphics.  All rights reserved.
12  * Copyright (c) 1998 by Fergus Henderson.  All rights reserved.
13  * Copyright (c) 2000-2004 by Hewlett-Packard Company.  All rights reserved.
14  *
15  * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
16  * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
17  *
18  * Permission is hereby granted to use or copy this program
19  * for any purpose,  provided the above notices are retained on all copies.
20  * Permission to modify the code and to distribute modified code is granted,
21  * provided the above notices are retained, and a notice that the code was
22  * modified is included with the above copyright notice.
23  *
24  *
25  * Copyright 2001-2003 Ximian, Inc
26  * Copyright 2003-2010 Novell, Inc.
27  * 
28  * Permission is hereby granted, free of charge, to any person obtaining
29  * a copy of this software and associated documentation files (the
30  * "Software"), to deal in the Software without restriction, including
31  * without limitation the rights to use, copy, modify, merge, publish,
32  * distribute, sublicense, and/or sell copies of the Software, and to
33  * permit persons to whom the Software is furnished to do so, subject to
34  * the following conditions:
35  * 
36  * The above copyright notice and this permission notice shall be
37  * included in all copies or substantial portions of the Software.
38  * 
39  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
40  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
41  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
42  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
43  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
44  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
45  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
46  *
47  *
48  * Important: allocation provides always zeroed memory, having to do
49  * a memset after allocation is deadly for performance.
50  * Memory usage at startup is currently as follows:
51  * 64 KB pinned space
52  * 64 KB internal space
53  * size of nursery
54  * We should provide a small memory config with half the sizes
55  *
56  * We currently try to make as few mono assumptions as possible:
57  * 1) 2-word header with no GC pointers in it (first vtable, second to store the
58  *    forwarding ptr)
59  * 2) gc descriptor is the second word in the vtable (first word in the class)
60  * 3) 8 byte alignment is the minimum and enough (not true for special structures (SIMD), FIXME)
61  * 4) there is a function to get an object's size and the number of
62  *    elements in an array.
63  * 5) we know the special way bounds are allocated for complex arrays
64  * 6) we know about proxies and how to treat them when domains are unloaded
65  *
66  * Always try to keep stack usage to a minimum: no recursive behaviour
67  * and no large stack allocs.
68  *
69  * General description.
70  * Objects are initially allocated in a nursery using a fast bump-pointer technique.
71  * When the nursery is full we start a nursery collection: this is performed with a
72  * copying GC.
73  * When the old generation is full we start a copying GC of the old generation as well:
74  * this will be changed to mark&sweep with copying when fragmentation becomes to severe
75  * in the future.  Maybe we'll even do both during the same collection like IMMIX.
76  *
77  * The things that complicate this description are:
78  * *) pinned objects: we can't move them so we need to keep track of them
79  * *) no precise info of the thread stacks and registers: we need to be able to
80  *    quickly find the objects that may be referenced conservatively and pin them
81  *    (this makes the first issues more important)
82  * *) large objects are too expensive to be dealt with using copying GC: we handle them
83  *    with mark/sweep during major collections
84  * *) some objects need to not move even if they are small (interned strings, Type handles):
85  *    we use mark/sweep for them, too: they are not allocated in the nursery, but inside
86  *    PinnedChunks regions
87  */
88
89 /*
90  * TODO:
91
92  *) we could have a function pointer in MonoClass to implement
93   customized write barriers for value types
94
95  *) investigate the stuff needed to advance a thread to a GC-safe
96   point (single-stepping, read from unmapped memory etc) and implement it.
97   This would enable us to inline allocations and write barriers, for example,
98   or at least parts of them, like the write barrier checks.
99   We may need this also for handling precise info on stacks, even simple things
100   as having uninitialized data on the stack and having to wait for the prolog
101   to zero it. Not an issue for the last frame that we scan conservatively.
102   We could always not trust the value in the slots anyway.
103
104  *) modify the jit to save info about references in stack locations:
105   this can be done just for locals as a start, so that at least
106   part of the stack is handled precisely.
107
108  *) test/fix endianess issues
109
110  *) Implement a card table as the write barrier instead of remembered
111     sets?  Card tables are not easy to implement with our current
112     memory layout.  We have several different kinds of major heap
113     objects: Small objects in regular blocks, small objects in pinned
114     chunks and LOS objects.  If we just have a pointer we have no way
115     to tell which kind of object it points into, therefore we cannot
116     know where its card table is.  The least we have to do to make
117     this happen is to get rid of write barriers for indirect stores.
118     (See next item)
119
120  *) Get rid of write barriers for indirect stores.  We can do this by
121     telling the GC to wbarrier-register an object once we do an ldloca
122     or ldelema on it, and to unregister it once it's not used anymore
123     (it can only travel downwards on the stack).  The problem with
124     unregistering is that it needs to happen eventually no matter
125     what, even if exceptions are thrown, the thread aborts, etc.
126     Rodrigo suggested that we could do only the registering part and
127     let the collector find out (pessimistically) when it's safe to
128     unregister, namely when the stack pointer of the thread that
129     registered the object is higher than it was when the registering
130     happened.  This might make for a good first implementation to get
131     some data on performance.
132
133  *) Some sort of blacklist support?  Blacklists is a concept from the
134     Boehm GC: if during a conservative scan we find pointers to an
135     area which we might use as heap, we mark that area as unusable, so
136     pointer retention by random pinning pointers is reduced.
137
138  *) experiment with max small object size (very small right now - 2kb,
139     because it's tied to the max freelist size)
140
141   *) add an option to mmap the whole heap in one chunk: it makes for many
142      simplifications in the checks (put the nursery at the top and just use a single
143      check for inclusion/exclusion): the issue this has is that on 32 bit systems it's
144      not flexible (too much of the address space may be used by default or we can't
145      increase the heap as needed) and we'd need a race-free mechanism to return memory
146      back to the system (mprotect(PROT_NONE) will still keep the memory allocated if it
147      was written to, munmap is needed, but the following mmap may not find the same segment
148      free...)
149
150  *) memzero the major fragments after restarting the world and optionally a smaller
151     chunk at a time
152
153  *) investigate having fragment zeroing threads
154
155  *) separate locks for finalization and other minor stuff to reduce
156     lock contention
157
158  *) try a different copying order to improve memory locality
159
160  *) a thread abort after a store but before the write barrier will
161     prevent the write barrier from executing
162
163  *) specialized dynamically generated markers/copiers
164
165  *) Dynamically adjust TLAB size to the number of threads.  If we have
166     too many threads that do allocation, we might need smaller TLABs,
167     and we might get better performance with larger TLABs if we only
168     have a handful of threads.  We could sum up the space left in all
169     assigned TLABs and if that's more than some percentage of the
170     nursery size, reduce the TLAB size.
171
172  *) Explore placing unreachable objects on unused nursery memory.
173         Instead of memset'ng a region to zero, place an int[] covering it.
174         A good place to start is add_nursery_frag. The tricky thing here is
175         placing those objects atomically outside of a collection.
176
177
178  */
179 #include "config.h"
180 #ifdef HAVE_SGEN_GC
181
182 #include <unistd.h>
183 #include <stdio.h>
184 #include <string.h>
185 #include <semaphore.h>
186 #include <signal.h>
187 #include <errno.h>
188 #include <assert.h>
189 #ifdef __MACH__
190 #undef _XOPEN_SOURCE
191 #endif
192 #include <pthread.h>
193 #ifdef __MACH__
194 #define _XOPEN_SOURCE
195 #endif
196
197 #include "metadata/sgen-gc.h"
198 #include "metadata/metadata-internals.h"
199 #include "metadata/class-internals.h"
200 #include "metadata/gc-internal.h"
201 #include "metadata/object-internals.h"
202 #include "metadata/threads.h"
203 #include "metadata/sgen-cardtable.h"
204 #include "metadata/sgen-protocol.h"
205 #include "metadata/sgen-archdep.h"
206 #include "metadata/sgen-bridge.h"
207 #include "metadata/mono-gc.h"
208 #include "metadata/method-builder.h"
209 #include "metadata/profiler-private.h"
210 #include "metadata/monitor.h"
211 #include "metadata/threadpool-internals.h"
212 #include "metadata/mempool-internals.h"
213 #include "metadata/marshal.h"
214 #include "metadata/runtime.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
221 #include <mono/utils/memcheck.h>
222
223 #if defined(__MACH__)
224 #include "utils/mach-support.h"
225 #endif
226
227 #define OPDEF(a,b,c,d,e,f,g,h,i,j) \
228         a = i,
229
230 enum {
231 #include "mono/cil/opcode.def"
232         CEE_LAST
233 };
234
235 #undef OPDEF
236
237 #undef pthread_create
238 #undef pthread_join
239 #undef pthread_detach
240
241 /*
242  * ######################################################################
243  * ########  Types and constants used by the GC.
244  * ######################################################################
245  */
246
247 /* 0 means not initialized, 1 is initialized, -1 means in progress */
248 static gint32 gc_initialized = 0;
249 /* If set, do a minor collection before every X allocation */
250 static guint32 collect_before_allocs = 0;
251 /* If set, do a heap consistency check before each minor collection */
252 static gboolean consistency_check_at_minor_collection = FALSE;
253 /* If set, check that there are no references to the domain left at domain unload */
254 static gboolean xdomain_checks = FALSE;
255 /* If not null, dump the heap after each collection into this file */
256 static FILE *heap_dump_file = NULL;
257 /* If set, mark stacks conservatively, even if precise marking is possible */
258 static gboolean conservative_stack_mark = FALSE;
259 /* If set, do a plausibility check on the scan_starts before and after
260    each collection */
261 static gboolean do_scan_starts_check = FALSE;
262 static gboolean disable_minor_collections = FALSE;
263 static gboolean disable_major_collections = FALSE;
264
265 #ifdef HEAVY_STATISTICS
266 static long long stat_objects_alloced = 0;
267 static long long stat_bytes_alloced = 0;
268 long long stat_objects_alloced_degraded = 0;
269 long long stat_bytes_alloced_degraded = 0;
270 static long long stat_bytes_alloced_los = 0;
271
272 long long stat_copy_object_called_nursery = 0;
273 long long stat_objects_copied_nursery = 0;
274 long long stat_copy_object_called_major = 0;
275 long long stat_objects_copied_major = 0;
276
277 long long stat_scan_object_called_nursery = 0;
278 long long stat_scan_object_called_major = 0;
279
280 long long stat_nursery_copy_object_failed_from_space = 0;
281 long long stat_nursery_copy_object_failed_forwarded = 0;
282 long long stat_nursery_copy_object_failed_pinned = 0;
283
284 static long long stat_store_remsets = 0;
285 static long long stat_store_remsets_unique = 0;
286 static long long stat_saved_remsets_1 = 0;
287 static long long stat_saved_remsets_2 = 0;
288 static long long stat_local_remsets_processed = 0;
289 static long long stat_global_remsets_added = 0;
290 static long long stat_global_remsets_readded = 0;
291 static long long stat_global_remsets_processed = 0;
292 static long long stat_global_remsets_discarded = 0;
293
294 static int stat_wbarrier_set_field = 0;
295 static int stat_wbarrier_set_arrayref = 0;
296 static int stat_wbarrier_arrayref_copy = 0;
297 static int stat_wbarrier_generic_store = 0;
298 static int stat_wbarrier_generic_store_remset = 0;
299 static int stat_wbarrier_set_root = 0;
300 static int stat_wbarrier_value_copy = 0;
301 static int stat_wbarrier_object_copy = 0;
302 #endif
303
304 static long long stat_pinned_objects = 0;
305
306 static long long time_minor_pre_collection_fragment_clear = 0;
307 static long long time_minor_pinning = 0;
308 static long long time_minor_scan_remsets = 0;
309 static long long time_minor_scan_card_table = 0;
310 static long long time_minor_scan_pinned = 0;
311 static long long time_minor_scan_registered_roots = 0;
312 static long long time_minor_scan_thread_data = 0;
313 static long long time_minor_finish_gray_stack = 0;
314 static long long time_minor_fragment_creation = 0;
315
316 static long long time_major_pre_collection_fragment_clear = 0;
317 static long long time_major_pinning = 0;
318 static long long time_major_scan_pinned = 0;
319 static long long time_major_scan_registered_roots = 0;
320 static long long time_major_scan_thread_data = 0;
321 static long long time_major_scan_alloc_pinned = 0;
322 static long long time_major_scan_finalized = 0;
323 static long long time_major_scan_big_objects = 0;
324 static long long time_major_finish_gray_stack = 0;
325 static long long time_major_free_bigobjs = 0;
326 static long long time_major_los_sweep = 0;
327 static long long time_major_sweep = 0;
328 static long long time_major_fragment_creation = 0;
329
330 #define DEBUG(level,a) do {if (G_UNLIKELY ((level) <= SGEN_MAX_DEBUG_LEVEL && (level) <= gc_debug_level)) a;} while (0)
331
332 int gc_debug_level = 0;
333 FILE* gc_debug_file;
334
335 /*
336 void
337 mono_gc_flush_info (void)
338 {
339         fflush (gc_debug_file);
340 }
341 */
342
343 /*
344  * Define this to allow the user to change the nursery size by
345  * specifying its value in the MONO_GC_PARAMS environmental
346  * variable. See mono_gc_base_init for details.
347  */
348 #define USER_CONFIG 1
349
350 #define TV_DECLARE SGEN_TV_DECLARE
351 #define TV_GETTIME SGEN_TV_GETTIME
352 #define TV_ELAPSED SGEN_TV_ELAPSED
353 #define TV_ELAPSED_MS SGEN_TV_ELAPSED_MS
354
355 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
356
357 static NurseryClearPolicy nursery_clear_policy = CLEAR_AT_TLAB_CREATION;
358
359 /* the runtime can register areas of memory as roots: we keep two lists of roots,
360  * a pinned root set for conservatively scanned roots and a normal one for
361  * precisely scanned roots (currently implemented as a single list).
362  */
363 typedef struct _RootRecord RootRecord;
364 struct _RootRecord {
365         RootRecord *next;
366         char *start_root;
367         char *end_root;
368         mword root_desc;
369 };
370
371 /*
372  * We're never actually using the first element.  It's always set to
373  * NULL to simplify the elimination of consecutive duplicate
374  * entries.
375  */
376 #define STORE_REMSET_BUFFER_SIZE        1023
377
378 typedef struct _GenericStoreRememberedSet GenericStoreRememberedSet;
379 struct _GenericStoreRememberedSet {
380         GenericStoreRememberedSet *next;
381         /* We need one entry less because the first entry of store
382            remset buffers is always a dummy and we don't copy it. */
383         gpointer data [STORE_REMSET_BUFFER_SIZE - 1];
384 };
385
386 /* we have 4 possible values in the low 2 bits */
387 enum {
388         REMSET_LOCATION, /* just a pointer to the exact location */
389         REMSET_RANGE,    /* range of pointer fields */
390         REMSET_OBJECT,   /* mark all the object for scanning */
391         REMSET_VTYPE,    /* a valuetype array described by a gc descriptor, a count and a size */
392         REMSET_TYPE_MASK = 0x3
393 };
394
395 #ifdef HAVE_KW_THREAD
396 static __thread RememberedSet *remembered_set MONO_TLS_FAST;
397 #endif
398 static pthread_key_t remembered_set_key;
399 static RememberedSet *global_remset;
400 static RememberedSet *freed_thread_remsets;
401 static GenericStoreRememberedSet *generic_store_remsets = NULL;
402
403 /*A two slots cache for recently inserted remsets */
404 static gpointer global_remset_cache [2];
405
406 /* FIXME: later choose a size that takes into account the RememberedSet struct
407  * and doesn't waste any alloc paddin space.
408  */
409 #define DEFAULT_REMSET_SIZE 1024
410 static RememberedSet* alloc_remset (int size, gpointer id, gboolean global);
411
412 #define object_is_forwarded     SGEN_OBJECT_IS_FORWARDED
413 #define object_is_pinned        SGEN_OBJECT_IS_PINNED
414 #define pin_object              SGEN_PIN_OBJECT
415 #define unpin_object            SGEN_UNPIN_OBJECT
416
417 #define ptr_in_nursery(p)       (SGEN_PTR_IN_NURSERY ((p), DEFAULT_NURSERY_BITS, nursery_start, nursery_end))
418
419 #define LOAD_VTABLE     SGEN_LOAD_VTABLE
420
421 static const char*
422 safe_name (void* obj)
423 {
424         MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (obj);
425         return vt->klass->name;
426 }
427
428 #define safe_object_get_size    mono_sgen_safe_object_get_size
429
430 const char*
431 mono_sgen_safe_name (void* obj)
432 {
433         return safe_name (obj);
434 }
435
436 /*
437  * ######################################################################
438  * ########  Global data.
439  * ######################################################################
440  */
441 static LOCK_DECLARE (gc_mutex);
442 static int gc_disabled = 0;
443 static int num_minor_gcs = 0;
444 static int num_major_gcs = 0;
445
446 static gboolean use_cardtable;
447
448 #ifdef USER_CONFIG
449
450 /* good sizes are 512KB-1MB: larger ones increase a lot memzeroing time */
451 #define DEFAULT_NURSERY_SIZE (default_nursery_size)
452 static int default_nursery_size = (1 << 22);
453 #ifdef SGEN_ALIGN_NURSERY
454 /* The number of trailing 0 bits in DEFAULT_NURSERY_SIZE */
455 #define DEFAULT_NURSERY_BITS (default_nursery_bits)
456 static int default_nursery_bits = 22;
457 #endif
458
459 #else
460
461 #define DEFAULT_NURSERY_SIZE (4*1024*1024)
462 #ifdef SGEN_ALIGN_NURSERY
463 #define DEFAULT_NURSERY_BITS 22
464 #endif
465
466 #endif
467
468 #ifndef SGEN_ALIGN_NURSERY
469 #define DEFAULT_NURSERY_BITS -1
470 #endif
471
472 #define MIN_MINOR_COLLECTION_ALLOWANCE  (DEFAULT_NURSERY_SIZE * 4)
473
474 #define SCAN_START_SIZE SGEN_SCAN_START_SIZE
475
476 static mword pagesize = 4096;
477 static mword nursery_size;
478 static int degraded_mode = 0;
479
480 static mword total_alloc = 0;
481 /* use this to tune when to do a major/minor collection */
482 static mword memory_pressure = 0;
483 static mword minor_collection_allowance;
484 static int minor_collection_sections_alloced = 0;
485
486 static GCMemSection *nursery_section = NULL;
487 static mword lowest_heap_address = ~(mword)0;
488 static mword highest_heap_address = 0;
489
490 static LOCK_DECLARE (interruption_mutex);
491 static LOCK_DECLARE (global_remset_mutex);
492 static LOCK_DECLARE (pin_queue_mutex);
493
494 #define LOCK_GLOBAL_REMSET pthread_mutex_lock (&global_remset_mutex)
495 #define UNLOCK_GLOBAL_REMSET pthread_mutex_unlock (&global_remset_mutex)
496
497 #define LOCK_PIN_QUEUE pthread_mutex_lock (&pin_queue_mutex)
498 #define UNLOCK_PIN_QUEUE pthread_mutex_unlock (&pin_queue_mutex)
499
500 typedef struct _FinalizeEntry FinalizeEntry;
501 struct _FinalizeEntry {
502         FinalizeEntry *next;
503         void *object;
504 };
505
506 typedef struct _FinalizeEntryHashTable FinalizeEntryHashTable;
507 struct _FinalizeEntryHashTable {
508         FinalizeEntry **table;
509         mword size;
510         int num_registered;
511 };
512
513 typedef struct _DisappearingLink DisappearingLink;
514 struct _DisappearingLink {
515         DisappearingLink *next;
516         void **link;
517 };
518
519 typedef struct _DisappearingLinkHashTable DisappearingLinkHashTable;
520 struct _DisappearingLinkHashTable {
521         DisappearingLink **table;
522         mword size;
523         int num_links;
524 };
525
526 typedef struct _EphemeronLinkNode EphemeronLinkNode;
527
528 struct _EphemeronLinkNode {
529         EphemeronLinkNode *next;
530         char *array;
531 };
532
533 typedef struct {
534        void *key;
535        void *value;
536 } Ephemeron;
537
538 int current_collection_generation = -1;
539
540 /*
541  * The link pointer is hidden by negating each bit.  We use the lowest
542  * bit of the link (before negation) to store whether it needs
543  * resurrection tracking.
544  */
545 #define HIDE_POINTER(p,t)       ((gpointer)(~((gulong)(p)|((t)?1:0))))
546 #define REVEAL_POINTER(p)       ((gpointer)((~(gulong)(p))&~3L))
547
548 #define DISLINK_OBJECT(d)       (REVEAL_POINTER (*(d)->link))
549 #define DISLINK_TRACK(d)        ((~(gulong)(*(d)->link)) & 1)
550
551 /* objects that are ready to be finalized */
552 static FinalizeEntry *fin_ready_list = NULL;
553 static FinalizeEntry *critical_fin_list = NULL;
554
555 static EphemeronLinkNode *ephemeron_list;
556
557 static int num_ready_finalizers = 0;
558 static int no_finalize = 0;
559
560 enum {
561         ROOT_TYPE_NORMAL = 0, /* "normal" roots */
562         ROOT_TYPE_PINNED = 1, /* roots without a GC descriptor */
563         ROOT_TYPE_WBARRIER = 2, /* roots with a write barrier */
564         ROOT_TYPE_NUM
565 };
566
567 /* registered roots: the key to the hash is the root start address */
568 /* 
569  * Different kinds of roots are kept separate to speed up pin_from_roots () for example.
570  */
571 static RootRecord **roots_hash [ROOT_TYPE_NUM] = { NULL, NULL };
572 static int roots_hash_size [ROOT_TYPE_NUM] = { 0, 0, 0 };
573 static mword roots_size = 0; /* amount of memory in the root set */
574 static int num_roots_entries [ROOT_TYPE_NUM] = { 0, 0, 0 };
575
576 #define GC_ROOT_NUM 32
577 typedef struct {
578         int count;
579         void *objects [GC_ROOT_NUM];
580         int root_types [GC_ROOT_NUM];
581         uintptr_t extra_info [GC_ROOT_NUM];
582 } GCRootReport;
583
584 static void
585 notify_gc_roots (GCRootReport *report)
586 {
587         if (!report->count)
588                 return;
589         mono_profiler_gc_roots (report->count, report->objects, report->root_types, report->extra_info);
590         report->count = 0;
591 }
592
593 static void
594 add_profile_gc_root (GCRootReport *report, void *object, int rtype, uintptr_t extra_info)
595 {
596         if (report->count == GC_ROOT_NUM)
597                 notify_gc_roots (report);
598         report->objects [report->count] = object;
599         report->root_types [report->count] = rtype;
600         report->extra_info [report->count++] = (uintptr_t)((MonoVTable*)LOAD_VTABLE (object))->klass;
601 }
602
603 /* 
604  * The current allocation cursors
605  * We allocate objects in the nursery.
606  * The nursery is the area between nursery_start and nursery_end.
607  * Allocation is done from a Thread Local Allocation Buffer (TLAB). TLABs are allocated
608  * from nursery fragments.
609  * tlab_next is the pointer to the space inside the TLAB where the next object will 
610  * be allocated.
611  * tlab_temp_end is the pointer to the end of the temporary space reserved for
612  * the allocation: it allows us to set the scan starts at reasonable intervals.
613  * tlab_real_end points to the end of the TLAB.
614  * nursery_frag_real_end points to the end of the currently used nursery fragment.
615  * nursery_first_pinned_start points to the start of the first pinned object in the nursery
616  * nursery_last_pinned_end points to the end of the last pinned object in the nursery
617  * At the next allocation, the area of the nursery where objects can be present is
618  * between MIN(nursery_first_pinned_start, first_fragment_start) and
619  * MAX(nursery_last_pinned_end, nursery_frag_real_end)
620  */
621 static char *nursery_start = NULL;
622 static char *nursery_end = NULL;
623 static char *nursery_alloc_bound = NULL;
624
625 #ifdef HAVE_KW_THREAD
626 #define TLAB_ACCESS_INIT
627 #define TLAB_START      tlab_start
628 #define TLAB_NEXT       tlab_next
629 #define TLAB_TEMP_END   tlab_temp_end
630 #define TLAB_REAL_END   tlab_real_end
631 #define REMEMBERED_SET  remembered_set
632 #define STORE_REMSET_BUFFER     store_remset_buffer
633 #define STORE_REMSET_BUFFER_INDEX       store_remset_buffer_index
634 #define IN_CRITICAL_REGION thread_info->in_critical_region
635 #else
636 static pthread_key_t thread_info_key;
637 #define TLAB_ACCESS_INIT        SgenThreadInfo *__thread_info__ = pthread_getspecific (thread_info_key)
638 #define TLAB_START      (__thread_info__->tlab_start)
639 #define TLAB_NEXT       (__thread_info__->tlab_next)
640 #define TLAB_TEMP_END   (__thread_info__->tlab_temp_end)
641 #define TLAB_REAL_END   (__thread_info__->tlab_real_end)
642 #define REMEMBERED_SET  (__thread_info__->remset)
643 #define STORE_REMSET_BUFFER     (__thread_info__->store_remset_buffer)
644 #define STORE_REMSET_BUFFER_INDEX       (__thread_info__->store_remset_buffer_index)
645 #define IN_CRITICAL_REGION (__thread_info__->in_critical_region)
646 #endif
647
648 #ifndef DISABLE_CRITICAL_REGION
649 /* we use the memory barrier only to prevent compiler reordering (a memory constraint may be enough) */
650 #define ENTER_CRITICAL_REGION do {IN_CRITICAL_REGION = 1;mono_memory_barrier ();} while (0)
651 #define EXIT_CRITICAL_REGION  do {IN_CRITICAL_REGION = 0;mono_memory_barrier ();} while (0)
652 #endif
653
654 /*
655  * FIXME: What is faster, a TLS variable pointing to a structure, or separate TLS 
656  * variables for next+temp_end ?
657  */
658 #ifdef HAVE_KW_THREAD
659 static __thread SgenThreadInfo *thread_info;
660 static __thread char *tlab_start;
661 static __thread char *tlab_next;
662 static __thread char *tlab_temp_end;
663 static __thread char *tlab_real_end;
664 static __thread gpointer *store_remset_buffer;
665 static __thread long store_remset_buffer_index;
666 /* Used by the managed allocator/wbarrier */
667 static __thread char **tlab_next_addr;
668 static __thread char *stack_end;
669 static __thread long *store_remset_buffer_index_addr;
670 #endif
671
672 /* The size of a TLAB */
673 /* The bigger the value, the less often we have to go to the slow path to allocate a new 
674  * one, but the more space is wasted by threads not allocating much memory.
675  * FIXME: Tune this.
676  * FIXME: Make this self-tuning for each thread.
677  */
678 static guint32 tlab_size = (1024 * 4);
679
680 #define MAX_SMALL_OBJ_SIZE      SGEN_MAX_SMALL_OBJ_SIZE
681
682 /* Functions supplied by the runtime to be called by the GC */
683 static MonoGCCallbacks gc_callbacks;
684
685 #define ALLOC_ALIGN             SGEN_ALLOC_ALIGN
686 #define ALLOC_ALIGN_BITS        SGEN_ALLOC_ALIGN_BITS
687
688 #define ALIGN_UP                SGEN_ALIGN_UP
689
690 #define MOVED_OBJECTS_NUM 64
691 static void *moved_objects [MOVED_OBJECTS_NUM];
692 static int moved_objects_idx = 0;
693
694 /* Vtable of the objects used to fill out nursery fragments before a collection */
695 static MonoVTable *array_fill_vtable;
696
697 #ifdef SGEN_DEBUG_INTERNAL_ALLOC
698 pthread_t main_gc_thread = NULL;
699 #endif
700
701 /*
702  * ######################################################################
703  * ########  Heap size accounting
704  * ######################################################################
705  */
706 /*heap limits*/
707 static mword max_heap_size = ((mword)0)- ((mword)1);
708 static mword allocated_heap;
709
710 /*Object was pinned during the current collection*/
711 static mword objects_pinned;
712
713 void
714 mono_sgen_release_space (mword size, int space)
715 {
716         allocated_heap -= size;
717 }
718
719 static size_t
720 available_free_space (void)
721 {
722         return max_heap_size - MIN (allocated_heap, max_heap_size);
723 }
724
725 gboolean
726 mono_sgen_try_alloc_space (mword size, int space)
727 {
728         if (available_free_space () < size)
729                 return FALSE;
730
731         allocated_heap += size;
732         return TRUE;
733 }
734
735 static void
736 init_heap_size_limits (glong max_heap)
737 {
738         if (max_heap == 0)
739                 return;
740
741         if (max_heap < nursery_size * 4) {
742                 fprintf (stderr, "max-heap-size must be at least 4 times larger than nursery size.\n");
743                 exit (1);
744         }
745         max_heap_size = max_heap - nursery_size;
746 }
747
748 /*
749  * ######################################################################
750  * ########  Macros and function declarations.
751  * ######################################################################
752  */
753
754 inline static void*
755 align_pointer (void *ptr)
756 {
757         mword p = (mword)ptr;
758         p += sizeof (gpointer) - 1;
759         p &= ~ (sizeof (gpointer) - 1);
760         return (void*)p;
761 }
762
763 typedef SgenGrayQueue GrayQueue;
764
765 typedef void (*CopyOrMarkObjectFunc) (void**, GrayQueue*);
766 typedef char* (*ScanObjectFunc) (char*, GrayQueue*);
767
768 /* forward declarations */
769 static int stop_world (int generation);
770 static int restart_world (int generation);
771 static void scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise, GrayQueue *queue);
772 static void scan_from_global_remsets (void *start_nursery, void *end_nursery, GrayQueue *queue);
773 static void scan_from_remsets (void *start_nursery, void *end_nursery, GrayQueue *queue);
774 static void scan_from_registered_roots (CopyOrMarkObjectFunc copy_func, char *addr_start, char *addr_end, int root_type, GrayQueue *queue);
775 static void scan_finalizer_entries (CopyOrMarkObjectFunc copy_func, FinalizeEntry *list, GrayQueue *queue);
776 static void report_finalizer_roots (void);
777 static void report_registered_roots (void);
778 static void find_pinning_ref_from_thread (char *obj, size_t size);
779 static void update_current_thread_stack (void *start);
780 static void finalize_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, GrayQueue *queue);
781 static void process_fin_stage_entries (void);
782 static void null_link_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, gboolean before_finalization, GrayQueue *queue);
783 static void null_links_for_domain (MonoDomain *domain, int generation);
784 static void process_dislink_stage_entries (void);
785
786 static void pin_from_roots (void *start_nursery, void *end_nursery, GrayQueue *queue);
787 static int pin_objects_from_addresses (GCMemSection *section, void **start, void **end, void *start_nursery, void *end_nursery, GrayQueue *queue);
788 static void optimize_pin_queue (int start_slot);
789 static void clear_remsets (void);
790 static void clear_tlabs (void);
791 static void sort_addresses (void **array, int size);
792 static gboolean drain_gray_stack (GrayQueue *queue, int max_objs);
793 static void finish_gray_stack (char *start_addr, char *end_addr, int generation, GrayQueue *queue);
794 static gboolean need_major_collection (mword space_needed);
795 static void major_collection (const char *reason);
796
797 static void mono_gc_register_disappearing_link (MonoObject *obj, void **link, gboolean track, gboolean in_gc);
798 static gboolean mono_gc_is_critical_method (MonoMethod *method);
799
800 void describe_ptr (char *ptr);
801 void check_object (char *start);
802
803 static void check_consistency (void);
804 static void check_major_refs (void);
805 static void check_scan_starts (void);
806 static void check_for_xdomain_refs (void);
807 static void dump_heap (const char *type, int num, const char *reason);
808
809 void mono_gc_scan_for_specific_ref (MonoObject *key, gboolean precise);
810
811 static void init_stats (void);
812
813 static int mark_ephemerons_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, GrayQueue *queue);
814 static void clear_unreachable_ephemerons (CopyOrMarkObjectFunc copy_func, char *start, char *end, GrayQueue *queue);
815 static void null_ephemerons_for_domain (MonoDomain *domain);
816
817 SgenMajorCollector major_collector;
818
819 #include "sgen-pinning.c"
820 #include "sgen-pinning-stats.c"
821 #include "sgen-gray.c"
822 #include "sgen-workers.c"
823 #include "sgen-cardtable.c"
824
825 /* Root bitmap descriptors are simpler: the lower three bits describe the type
826  * and we either have 30/62 bitmap bits or nibble-based run-length,
827  * or a complex descriptor, or a user defined marker function.
828  */
829 enum {
830         ROOT_DESC_CONSERVATIVE, /* 0, so matches NULL value */
831         ROOT_DESC_BITMAP,
832         ROOT_DESC_RUN_LEN, 
833         ROOT_DESC_COMPLEX,
834         ROOT_DESC_USER,
835         ROOT_DESC_TYPE_MASK = 0x7,
836         ROOT_DESC_TYPE_SHIFT = 3,
837 };
838
839 #define MAKE_ROOT_DESC(type,val) ((type) | ((val) << ROOT_DESC_TYPE_SHIFT))
840
841 #define MAX_USER_DESCRIPTORS 16
842
843 static gsize* complex_descriptors = NULL;
844 static int complex_descriptors_size = 0;
845 static int complex_descriptors_next = 0;
846 static MonoGCRootMarkFunc user_descriptors [MAX_USER_DESCRIPTORS];
847 static int user_descriptors_next = 0;
848
849 static int
850 alloc_complex_descriptor (gsize *bitmap, int numbits)
851 {
852         int nwords, res, i;
853
854         numbits = ALIGN_TO (numbits, GC_BITS_PER_WORD);
855         nwords = numbits / GC_BITS_PER_WORD + 1;
856
857         LOCK_GC;
858         res = complex_descriptors_next;
859         /* linear search, so we don't have duplicates with domain load/unload
860          * this should not be performance critical or we'd have bigger issues
861          * (the number and size of complex descriptors should be small).
862          */
863         for (i = 0; i < complex_descriptors_next; ) {
864                 if (complex_descriptors [i] == nwords) {
865                         int j, found = TRUE;
866                         for (j = 0; j < nwords - 1; ++j) {
867                                 if (complex_descriptors [i + 1 + j] != bitmap [j]) {
868                                         found = FALSE;
869                                         break;
870                                 }
871                         }
872                         if (found) {
873                                 UNLOCK_GC;
874                                 return i;
875                         }
876                 }
877                 i += complex_descriptors [i];
878         }
879         if (complex_descriptors_next + nwords > complex_descriptors_size) {
880                 int new_size = complex_descriptors_size * 2 + nwords;
881                 complex_descriptors = g_realloc (complex_descriptors, new_size * sizeof (gsize));
882                 complex_descriptors_size = new_size;
883         }
884         DEBUG (6, fprintf (gc_debug_file, "Complex descriptor %d, size: %d (total desc memory: %d)\n", res, nwords, complex_descriptors_size));
885         complex_descriptors_next += nwords;
886         complex_descriptors [res] = nwords;
887         for (i = 0; i < nwords - 1; ++i) {
888                 complex_descriptors [res + 1 + i] = bitmap [i];
889                 DEBUG (6, fprintf (gc_debug_file, "\tvalue: %p\n", (void*)complex_descriptors [res + 1 + i]));
890         }
891         UNLOCK_GC;
892         return res;
893 }
894
895 gsize*
896 mono_sgen_get_complex_descriptor (mword desc)
897 {
898         return complex_descriptors + (desc >> LOW_TYPE_BITS);
899 }
900
901 /*
902  * Descriptor builders.
903  */
904 void*
905 mono_gc_make_descr_for_string (gsize *bitmap, int numbits)
906 {
907         return (void*) DESC_TYPE_RUN_LENGTH;
908 }
909
910 void*
911 mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size)
912 {
913         int first_set = -1, num_set = 0, last_set = -1, i;
914         mword desc = 0;
915         size_t stored_size = obj_size;
916         for (i = 0; i < numbits; ++i) {
917                 if (bitmap [i / GC_BITS_PER_WORD] & ((gsize)1 << (i % GC_BITS_PER_WORD))) {
918                         if (first_set < 0)
919                                 first_set = i;
920                         last_set = i;
921                         num_set++;
922                 }
923         }
924         /*
925          * We don't encode the size of types that don't contain
926          * references because they might not be aligned, i.e. the
927          * bottom two bits might be set, which would clash with the
928          * bits we need to encode the descriptor type.  Since we don't
929          * use the encoded size to skip objects, other than for
930          * processing remsets, in which case only the positions of
931          * references are relevant, this is not a problem.
932          */
933         if (first_set < 0)
934                 return (void*)DESC_TYPE_RUN_LENGTH;
935         g_assert (!(stored_size & 0x3));
936         if (stored_size <= MAX_SMALL_OBJ_SIZE) {
937                 /* check run-length encoding first: one byte offset, one byte number of pointers
938                  * on 64 bit archs, we can have 3 runs, just one on 32.
939                  * It may be better to use nibbles.
940                  */
941                 if (first_set < 0) {
942                         desc = DESC_TYPE_RUN_LENGTH | (stored_size << 1);
943                         DEBUG (6, fprintf (gc_debug_file, "Ptrfree descriptor %p, size: %zd\n", (void*)desc, stored_size));
944                         return (void*) desc;
945                 } else if (first_set < 256 && num_set < 256 && (first_set + num_set == last_set + 1)) {
946                         desc = DESC_TYPE_RUN_LENGTH | (stored_size << 1) | (first_set << 16) | (num_set << 24);
947                         DEBUG (6, fprintf (gc_debug_file, "Runlen descriptor %p, size: %zd, first set: %d, num set: %d\n", (void*)desc, stored_size, first_set, num_set));
948                         return (void*) desc;
949                 }
950         }
951         /* we know the 2-word header is ptr-free */
952         if (last_set < LARGE_BITMAP_SIZE + OBJECT_HEADER_WORDS) {
953                 desc = DESC_TYPE_LARGE_BITMAP | ((*bitmap >> OBJECT_HEADER_WORDS) << LOW_TYPE_BITS);
954                 DEBUG (6, fprintf (gc_debug_file, "Largebitmap descriptor %p, size: %zd, last set: %d\n", (void*)desc, stored_size, last_set));
955                 return (void*) desc;
956         }
957         /* it's a complex object ... */
958         desc = DESC_TYPE_COMPLEX | (alloc_complex_descriptor (bitmap, last_set + 1) << LOW_TYPE_BITS);
959         return (void*) desc;
960 }
961
962 /* If the array holds references, numbits == 1 and the first bit is set in elem_bitmap */
963 void*
964 mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size)
965 {
966         int first_set = -1, num_set = 0, last_set = -1, i;
967         mword desc = vector? DESC_TYPE_VECTOR: DESC_TYPE_ARRAY;
968         for (i = 0; i < numbits; ++i) {
969                 if (elem_bitmap [i / GC_BITS_PER_WORD] & ((gsize)1 << (i % GC_BITS_PER_WORD))) {
970                         if (first_set < 0)
971                                 first_set = i;
972                         last_set = i;
973                         num_set++;
974                 }
975         }
976         /* See comment at the definition of DESC_TYPE_RUN_LENGTH. */
977         if (first_set < 0)
978                 return (void*)DESC_TYPE_RUN_LENGTH;
979         if (elem_size <= MAX_ELEMENT_SIZE) {
980                 desc |= elem_size << VECTOR_ELSIZE_SHIFT;
981                 if (!num_set) {
982                         return (void*)(desc | VECTOR_SUBTYPE_PTRFREE);
983                 }
984                 /* Note: we also handle structs with just ref fields */
985                 if (num_set * sizeof (gpointer) == elem_size) {
986                         return (void*)(desc | VECTOR_SUBTYPE_REFS | ((gssize)(-1) << 16));
987                 }
988                 /* FIXME: try run-len first */
989                 /* Note: we can't skip the object header here, because it's not present */
990                 if (last_set <= SMALL_BITMAP_SIZE) {
991                         return (void*)(desc | VECTOR_SUBTYPE_BITMAP | (*elem_bitmap << 16));
992                 }
993         }
994         /* it's am array of complex structs ... */
995         desc = DESC_TYPE_COMPLEX_ARR;
996         desc |= alloc_complex_descriptor (elem_bitmap, last_set + 1) << LOW_TYPE_BITS;
997         return (void*) desc;
998 }
999
1000 /* Return the bitmap encoded by a descriptor */
1001 gsize*
1002 mono_gc_get_bitmap_for_descr (void *descr, int *numbits)
1003 {
1004         mword d = (mword)descr;
1005         gsize *bitmap;
1006
1007         switch (d & 0x7) {
1008         case DESC_TYPE_RUN_LENGTH: {            
1009                 int first_set = (d >> 16) & 0xff;
1010                 int num_set = (d >> 24) & 0xff;
1011                 int i;
1012
1013                 bitmap = g_new0 (gsize, (first_set + num_set + 7) / 8);
1014
1015                 for (i = first_set; i < first_set + num_set; ++i)
1016                         bitmap [i / GC_BITS_PER_WORD] |= ((gsize)1 << (i % GC_BITS_PER_WORD));
1017
1018                 *numbits = first_set + num_set;
1019
1020                 return bitmap;
1021         }
1022         default:
1023                 g_assert_not_reached ();
1024         }
1025 }
1026
1027 static gboolean
1028 is_xdomain_ref_allowed (gpointer *ptr, char *obj, MonoDomain *domain)
1029 {
1030         MonoObject *o = (MonoObject*)(obj);
1031         MonoObject *ref = (MonoObject*)*(ptr);
1032         int offset = (char*)(ptr) - (char*)o;
1033
1034         if (o->vtable->klass == mono_defaults.thread_class && offset == G_STRUCT_OFFSET (MonoThread, internal_thread))
1035                 return TRUE;
1036         if (o->vtable->klass == mono_defaults.internal_thread_class && offset == G_STRUCT_OFFSET (MonoInternalThread, current_appcontext))
1037                 return TRUE;
1038         if (mono_class_has_parent (o->vtable->klass, mono_defaults.real_proxy_class) &&
1039                         offset == G_STRUCT_OFFSET (MonoRealProxy, unwrapped_server))
1040                 return TRUE;
1041         /* Thread.cached_culture_info */
1042         if (!strcmp (ref->vtable->klass->name_space, "System.Globalization") &&
1043                         !strcmp (ref->vtable->klass->name, "CultureInfo") &&
1044                         !strcmp(o->vtable->klass->name_space, "System") &&
1045                         !strcmp(o->vtable->klass->name, "Object[]"))
1046                 return TRUE;
1047         /*
1048          *  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
1049          * at System.IO.MemoryStream..ctor (byte[]) [0x00017] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.IO/MemoryStream.cs:81
1050          * at (wrapper remoting-invoke-with-check) System.IO.MemoryStream..ctor (byte[]) <IL 0x00020, 0xffffffff>
1051          * at System.Runtime.Remoting.Messaging.CADMethodCallMessage.GetArguments () [0x0000d] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.Runtime.Remoting.Messaging/CADMessages.cs:327
1052          * 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
1053          * 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
1054          * at (wrapper remoting-invoke-with-check) System.AppDomain.ProcessMessageInDomain (byte[],System.Runtime.Remoting.Messaging.CADMethodCallMessage,byte[]&,System.Runtime.Remoting.Messaging.CADMethodReturnMessage&) <IL 0x0003d, 0xffffffff>
1055          * 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
1056          * at (wrapper runtime-invoke) object.runtime_invoke_CrossAppDomainSink/ProcessMessageRes_object_object (object,intptr,intptr,intptr) <IL 0x0004c, 0xffffffff>
1057          */
1058         if (!strcmp (ref->vtable->klass->name_space, "System") &&
1059                         !strcmp (ref->vtable->klass->name, "Byte[]") &&
1060                         !strcmp (o->vtable->klass->name_space, "System.IO") &&
1061                         !strcmp (o->vtable->klass->name, "MemoryStream"))
1062                 return TRUE;
1063         /* append_job() in threadpool.c */
1064         if (!strcmp (ref->vtable->klass->name_space, "System.Runtime.Remoting.Messaging") &&
1065                         !strcmp (ref->vtable->klass->name, "AsyncResult") &&
1066                         !strcmp (o->vtable->klass->name_space, "System") &&
1067                         !strcmp (o->vtable->klass->name, "Object[]") &&
1068                         mono_thread_pool_is_queue_array ((MonoArray*) o))
1069                 return TRUE;
1070         return FALSE;
1071 }
1072
1073 static void
1074 check_reference_for_xdomain (gpointer *ptr, char *obj, MonoDomain *domain)
1075 {
1076         MonoObject *o = (MonoObject*)(obj);
1077         MonoObject *ref = (MonoObject*)*(ptr);
1078         int offset = (char*)(ptr) - (char*)o;
1079         MonoClass *class;
1080         MonoClassField *field;
1081         char *str;
1082
1083         if (!ref || ref->vtable->domain == domain)
1084                 return;
1085         if (is_xdomain_ref_allowed (ptr, obj, domain))
1086                 return;
1087
1088         field = NULL;
1089         for (class = o->vtable->klass; class; class = class->parent) {
1090                 int i;
1091
1092                 for (i = 0; i < class->field.count; ++i) {
1093                         if (class->fields[i].offset == offset) {
1094                                 field = &class->fields[i];
1095                                 break;
1096                         }
1097                 }
1098                 if (field)
1099                         break;
1100         }
1101
1102         if (ref->vtable->klass == mono_defaults.string_class)
1103                 str = mono_string_to_utf8 ((MonoString*)ref);
1104         else
1105                 str = NULL;
1106         g_print ("xdomain reference in %p (%s.%s) at offset %d (%s) to %p (%s.%s) (%s)  -  pointed to by:\n",
1107                         o, o->vtable->klass->name_space, o->vtable->klass->name,
1108                         offset, field ? field->name : "",
1109                         ref, ref->vtable->klass->name_space, ref->vtable->klass->name, str ? str : "");
1110         mono_gc_scan_for_specific_ref (o, TRUE);
1111         if (str)
1112                 g_free (str);
1113 }
1114
1115 #undef HANDLE_PTR
1116 #define HANDLE_PTR(ptr,obj)     check_reference_for_xdomain ((ptr), (obj), domain)
1117
1118 static void
1119 scan_object_for_xdomain_refs (char *start, mword size, void *data)
1120 {
1121         MonoDomain *domain = ((MonoObject*)start)->vtable->domain;
1122
1123         #include "sgen-scan-object.h"
1124 }
1125
1126 static gboolean scan_object_for_specific_ref_precise = TRUE;
1127
1128 #undef HANDLE_PTR
1129 #define HANDLE_PTR(ptr,obj) do {                \
1130         if ((MonoObject*)*(ptr) == key) {       \
1131         g_print ("found ref to %p in object %p (%s) at offset %td\n",   \
1132                         key, (obj), safe_name ((obj)), ((char*)(ptr) - (char*)(obj))); \
1133         }                                                               \
1134         } while (0)
1135
1136 static void
1137 scan_object_for_specific_ref (char *start, MonoObject *key)
1138 {
1139         char *forwarded;
1140
1141         if ((forwarded = SGEN_OBJECT_IS_FORWARDED (start)))
1142                 start = forwarded;
1143
1144         if (scan_object_for_specific_ref_precise) {
1145                 #include "sgen-scan-object.h"
1146         } else {
1147                 mword *words = (mword*)start;
1148                 size_t size = safe_object_get_size ((MonoObject*)start);
1149                 int i;
1150                 for (i = 0; i < size / sizeof (mword); ++i) {
1151                         if (words [i] == (mword)key) {
1152                                 g_print ("found possible ref to %p in object %p (%s) at offset %td\n",
1153                                                 key, start, safe_name (start), i * sizeof (mword));
1154                         }
1155                 }
1156         }
1157 }
1158
1159 void
1160 mono_sgen_scan_area_with_callback (char *start, char *end, IterateObjectCallbackFunc callback, void *data, gboolean allow_flags)
1161 {
1162         while (start < end) {
1163                 size_t size;
1164                 char *obj;
1165
1166                 if (!*(void**)start) {
1167                         start += sizeof (void*); /* should be ALLOC_ALIGN, really */
1168                         continue;
1169                 }
1170
1171                 if (allow_flags) {
1172                         if (!(obj = SGEN_OBJECT_IS_FORWARDED (start)))
1173                                 obj = start;
1174                 } else {
1175                         obj = start;
1176                 }
1177
1178                 size = ALIGN_UP (safe_object_get_size ((MonoObject*)obj));
1179
1180                 callback (obj, size, data);
1181
1182                 start += size;
1183         }
1184 }
1185
1186 static void
1187 scan_object_for_specific_ref_callback (char *obj, size_t size, MonoObject *key)
1188 {
1189         scan_object_for_specific_ref (obj, key);
1190 }
1191
1192 static void
1193 check_root_obj_specific_ref (RootRecord *root, MonoObject *key, MonoObject *obj)
1194 {
1195         if (key != obj)
1196                 return;
1197         g_print ("found ref to %p in root record %p\n", key, root);
1198 }
1199
1200 static MonoObject *check_key = NULL;
1201 static RootRecord *check_root = NULL;
1202
1203 static void
1204 check_root_obj_specific_ref_from_marker (void **obj)
1205 {
1206         check_root_obj_specific_ref (check_root, check_key, *obj);
1207 }
1208
1209 static void
1210 scan_roots_for_specific_ref (MonoObject *key, int root_type)
1211 {
1212         int i;
1213         RootRecord *root;
1214         check_key = key;
1215         for (i = 0; i < roots_hash_size [root_type]; ++i) {
1216                 for (root = roots_hash [root_type][i]; root; root = root->next) {
1217                         void **start_root = (void**)root->start_root;
1218                         mword desc = root->root_desc;
1219
1220                         check_root = root;
1221
1222                         switch (desc & ROOT_DESC_TYPE_MASK) {
1223                         case ROOT_DESC_BITMAP:
1224                                 desc >>= ROOT_DESC_TYPE_SHIFT;
1225                                 while (desc) {
1226                                         if (desc & 1)
1227                                                 check_root_obj_specific_ref (root, key, *start_root);
1228                                         desc >>= 1;
1229                                         start_root++;
1230                                 }
1231                                 return;
1232                         case ROOT_DESC_COMPLEX: {
1233                                 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
1234                                 int bwords = (*bitmap_data) - 1;
1235                                 void **start_run = start_root;
1236                                 bitmap_data++;
1237                                 while (bwords-- > 0) {
1238                                         gsize bmap = *bitmap_data++;
1239                                         void **objptr = start_run;
1240                                         while (bmap) {
1241                                                 if (bmap & 1)
1242                                                         check_root_obj_specific_ref (root, key, *objptr);
1243                                                 bmap >>= 1;
1244                                                 ++objptr;
1245                                         }
1246                                         start_run += GC_BITS_PER_WORD;
1247                                 }
1248                                 break;
1249                         }
1250                         case ROOT_DESC_USER: {
1251                                 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
1252                                 marker (start_root, check_root_obj_specific_ref_from_marker);
1253                                 break;
1254                         }
1255                         case ROOT_DESC_RUN_LEN:
1256                                 g_assert_not_reached ();
1257                         default:
1258                                 g_assert_not_reached ();
1259                         }
1260                 }
1261         }
1262         check_key = NULL;
1263         check_root = NULL;
1264 }
1265
1266 void
1267 mono_gc_scan_for_specific_ref (MonoObject *key, gboolean precise)
1268 {
1269         RootRecord *root;
1270         int i;
1271
1272         scan_object_for_specific_ref_precise = precise;
1273
1274         mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1275                         (IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key, TRUE);
1276
1277         major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
1278
1279         mono_sgen_los_iterate_objects ((IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
1280
1281         scan_roots_for_specific_ref (key, ROOT_TYPE_NORMAL);
1282         scan_roots_for_specific_ref (key, ROOT_TYPE_WBARRIER);
1283
1284         for (i = 0; i < roots_hash_size [ROOT_TYPE_PINNED]; ++i) {
1285                 for (root = roots_hash [ROOT_TYPE_PINNED][i]; root; root = root->next) {
1286                         void **ptr = (void**)root->start_root;
1287
1288                         while (ptr < (void**)root->end_root) {
1289                                 check_root_obj_specific_ref (root, *ptr, key);
1290                                 ++ptr;
1291                         }
1292                 }
1293         }
1294 }
1295
1296 static gboolean
1297 need_remove_object_for_domain (char *start, MonoDomain *domain)
1298 {
1299         if (mono_object_domain (start) == domain) {
1300                 DEBUG (4, fprintf (gc_debug_file, "Need to cleanup object %p\n", start));
1301                 binary_protocol_cleanup (start, (gpointer)LOAD_VTABLE (start), safe_object_get_size ((MonoObject*)start));
1302                 return TRUE;
1303         }
1304         return FALSE;
1305 }
1306
1307 static void
1308 process_object_for_domain_clearing (char *start, MonoDomain *domain)
1309 {
1310         GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
1311         if (vt->klass == mono_defaults.internal_thread_class)
1312                 g_assert (mono_object_domain (start) == mono_get_root_domain ());
1313         /* The object could be a proxy for an object in the domain
1314            we're deleting. */
1315         if (mono_class_has_parent (vt->klass, mono_defaults.real_proxy_class)) {
1316                 MonoObject *server = ((MonoRealProxy*)start)->unwrapped_server;
1317
1318                 /* The server could already have been zeroed out, so
1319                    we need to check for that, too. */
1320                 if (server && (!LOAD_VTABLE (server) || mono_object_domain (server) == domain)) {
1321                         DEBUG (4, fprintf (gc_debug_file, "Cleaning up remote pointer in %p to object %p\n",
1322                                         start, server));
1323                         ((MonoRealProxy*)start)->unwrapped_server = NULL;
1324                 }
1325         }
1326 }
1327
1328 static MonoDomain *check_domain = NULL;
1329
1330 static void
1331 check_obj_not_in_domain (void **o)
1332 {
1333         g_assert (((MonoObject*)(*o))->vtable->domain != check_domain);
1334 }
1335
1336 static void
1337 scan_for_registered_roots_in_domain (MonoDomain *domain, int root_type)
1338 {
1339         int i;
1340         RootRecord *root;
1341         check_domain = domain;
1342         for (i = 0; i < roots_hash_size [root_type]; ++i) {
1343                 for (root = roots_hash [root_type][i]; root; root = root->next) {
1344                         void **start_root = (void**)root->start_root;
1345                         mword desc = root->root_desc;
1346
1347                         /* The MonoDomain struct is allowed to hold
1348                            references to objects in its own domain. */
1349                         if (start_root == (void**)domain)
1350                                 continue;
1351
1352                         switch (desc & ROOT_DESC_TYPE_MASK) {
1353                         case ROOT_DESC_BITMAP:
1354                                 desc >>= ROOT_DESC_TYPE_SHIFT;
1355                                 while (desc) {
1356                                         if ((desc & 1) && *start_root)
1357                                                 check_obj_not_in_domain (*start_root);
1358                                         desc >>= 1;
1359                                         start_root++;
1360                                 }
1361                                 break;
1362                         case ROOT_DESC_COMPLEX: {
1363                                 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
1364                                 int bwords = (*bitmap_data) - 1;
1365                                 void **start_run = start_root;
1366                                 bitmap_data++;
1367                                 while (bwords-- > 0) {
1368                                         gsize bmap = *bitmap_data++;
1369                                         void **objptr = start_run;
1370                                         while (bmap) {
1371                                                 if ((bmap & 1) && *objptr)
1372                                                         check_obj_not_in_domain (*objptr);
1373                                                 bmap >>= 1;
1374                                                 ++objptr;
1375                                         }
1376                                         start_run += GC_BITS_PER_WORD;
1377                                 }
1378                                 break;
1379                         }
1380                         case ROOT_DESC_USER: {
1381                                 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
1382                                 marker (start_root, check_obj_not_in_domain);
1383                                 break;
1384                         }
1385                         case ROOT_DESC_RUN_LEN:
1386                                 g_assert_not_reached ();
1387                         default:
1388                                 g_assert_not_reached ();
1389                         }
1390                 }
1391         }
1392         check_domain = NULL;
1393 }
1394
1395 static void
1396 check_for_xdomain_refs (void)
1397 {
1398         LOSObject *bigobj;
1399
1400         mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1401                         (IterateObjectCallbackFunc)scan_object_for_xdomain_refs, NULL, FALSE);
1402
1403         major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)scan_object_for_xdomain_refs, NULL);
1404
1405         for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1406                 scan_object_for_xdomain_refs (bigobj->data, bigobj->size, NULL);
1407 }
1408
1409 static gboolean
1410 clear_domain_process_object (char *obj, MonoDomain *domain)
1411 {
1412         gboolean remove;
1413
1414         process_object_for_domain_clearing (obj, domain);
1415         remove = need_remove_object_for_domain (obj, domain);
1416
1417         if (remove && ((MonoObject*)obj)->synchronisation) {
1418                 void **dislink = mono_monitor_get_object_monitor_weak_link ((MonoObject*)obj);
1419                 if (dislink)
1420                         mono_gc_register_disappearing_link (NULL, dislink, FALSE, TRUE);
1421         }
1422
1423         return remove;
1424 }
1425
1426 static void
1427 clear_domain_process_minor_object_callback (char *obj, size_t size, MonoDomain *domain)
1428 {
1429         if (clear_domain_process_object (obj, domain))
1430                 memset (obj, 0, size);
1431 }
1432
1433 static void
1434 clear_domain_process_major_object_callback (char *obj, size_t size, MonoDomain *domain)
1435 {
1436         clear_domain_process_object (obj, domain);
1437 }
1438
1439 static void
1440 clear_domain_free_major_non_pinned_object_callback (char *obj, size_t size, MonoDomain *domain)
1441 {
1442         if (need_remove_object_for_domain (obj, domain))
1443                 major_collector.free_non_pinned_object (obj, size);
1444 }
1445
1446 static void
1447 clear_domain_free_major_pinned_object_callback (char *obj, size_t size, MonoDomain *domain)
1448 {
1449         if (need_remove_object_for_domain (obj, domain))
1450                 major_collector.free_pinned_object (obj, size);
1451 }
1452
1453 /*
1454  * When appdomains are unloaded we can easily remove objects that have finalizers,
1455  * but all the others could still be present in random places on the heap.
1456  * We need a sweep to get rid of them even though it's going to be costly
1457  * with big heaps.
1458  * The reason we need to remove them is because we access the vtable and class
1459  * structures to know the object size and the reference bitmap: once the domain is
1460  * unloaded the point to random memory.
1461  */
1462 void
1463 mono_gc_clear_domain (MonoDomain * domain)
1464 {
1465         LOSObject *bigobj, *prev;
1466         int i;
1467
1468         LOCK_GC;
1469
1470         process_fin_stage_entries ();
1471         process_dislink_stage_entries ();
1472
1473         mono_sgen_clear_nursery_fragments ();
1474
1475         if (xdomain_checks && domain != mono_get_root_domain ()) {
1476                 scan_for_registered_roots_in_domain (domain, ROOT_TYPE_NORMAL);
1477                 scan_for_registered_roots_in_domain (domain, ROOT_TYPE_WBARRIER);
1478                 check_for_xdomain_refs ();
1479         }
1480
1481         mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1482                         (IterateObjectCallbackFunc)clear_domain_process_minor_object_callback, domain, FALSE);
1483
1484         /*Ephemerons and dislinks must be processed before LOS since they might end up pointing
1485         to memory returned to the OS.*/
1486         null_ephemerons_for_domain (domain);
1487
1488         for (i = GENERATION_NURSERY; i < GENERATION_MAX; ++i)
1489                 null_links_for_domain (domain, i);
1490
1491         /* We need two passes over major and large objects because
1492            freeing such objects might give their memory back to the OS
1493            (in the case of large objects) or obliterate its vtable
1494            (pinned objects with major-copying or pinned and non-pinned
1495            objects with major-mark&sweep), but we might need to
1496            dereference a pointer from an object to another object if
1497            the first object is a proxy. */
1498         major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)clear_domain_process_major_object_callback, domain);
1499         for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1500                 clear_domain_process_object (bigobj->data, domain);
1501
1502         prev = NULL;
1503         for (bigobj = los_object_list; bigobj;) {
1504                 if (need_remove_object_for_domain (bigobj->data, domain)) {
1505                         LOSObject *to_free = bigobj;
1506                         if (prev)
1507                                 prev->next = bigobj->next;
1508                         else
1509                                 los_object_list = bigobj->next;
1510                         bigobj = bigobj->next;
1511                         DEBUG (4, fprintf (gc_debug_file, "Freeing large object %p\n",
1512                                         bigobj->data));
1513                         mono_sgen_los_free_object (to_free);
1514                         continue;
1515                 }
1516                 prev = bigobj;
1517                 bigobj = bigobj->next;
1518         }
1519         major_collector.iterate_objects (TRUE, FALSE, (IterateObjectCallbackFunc)clear_domain_free_major_non_pinned_object_callback, domain);
1520         major_collector.iterate_objects (FALSE, TRUE, (IterateObjectCallbackFunc)clear_domain_free_major_pinned_object_callback, domain);
1521
1522         UNLOCK_GC;
1523 }
1524
1525 static void
1526 global_remset_cache_clear (void)
1527 {
1528         memset (global_remset_cache, 0, sizeof (global_remset_cache));
1529 }
1530
1531 /*
1532  * Tries to check if a given remset location was already added to the global remset.
1533  * It can
1534  *
1535  * A 2 entry, LRU cache of recently saw location remsets.
1536  *
1537  * It's hand-coded instead of done using loops to reduce the number of memory references on cache hit.
1538  *
1539  * Returns TRUE is the element was added..
1540  */
1541 static gboolean
1542 global_remset_location_was_not_added (gpointer ptr)
1543 {
1544
1545         gpointer first = global_remset_cache [0], second;
1546         if (first == ptr) {
1547                 HEAVY_STAT (++stat_global_remsets_discarded);
1548                 return FALSE;
1549         }
1550
1551         second = global_remset_cache [1];
1552
1553         if (second == ptr) {
1554                 /*Move the second to the front*/
1555                 global_remset_cache [0] = second;
1556                 global_remset_cache [1] = first;
1557
1558                 HEAVY_STAT (++stat_global_remsets_discarded);
1559                 return FALSE;
1560         }
1561
1562         global_remset_cache [0] = second;
1563         global_remset_cache [1] = ptr;
1564         return TRUE;
1565 }
1566
1567 /*
1568  * mono_sgen_add_to_global_remset:
1569  *
1570  *   The global remset contains locations which point into newspace after
1571  * a minor collection. This can happen if the objects they point to are pinned.
1572  *
1573  * LOCKING: If called from a parallel collector, the global remset
1574  * lock must be held.  For serial collectors that is not necessary.
1575  */
1576 void
1577 mono_sgen_add_to_global_remset (gpointer ptr)
1578 {
1579         RememberedSet *rs;
1580         gboolean lock = major_collector.is_parallel;
1581
1582         if (use_cardtable) {
1583                 sgen_card_table_mark_address ((mword)ptr);
1584                 return;
1585         }
1586
1587         g_assert (!ptr_in_nursery (ptr) && ptr_in_nursery (*(gpointer*)ptr));
1588
1589         if (lock)
1590                 LOCK_GLOBAL_REMSET;
1591
1592         if (!global_remset_location_was_not_added (ptr))
1593                 goto done;
1594
1595         DEBUG (8, fprintf (gc_debug_file, "Adding global remset for %p\n", ptr));
1596         binary_protocol_global_remset (ptr, *(gpointer*)ptr, (gpointer)LOAD_VTABLE (*(gpointer*)ptr));
1597
1598         HEAVY_STAT (++stat_global_remsets_added);
1599
1600         /* 
1601          * FIXME: If an object remains pinned, we need to add it at every minor collection.
1602          * To avoid uncontrolled growth of the global remset, only add each pointer once.
1603          */
1604         if (global_remset->store_next + 3 < global_remset->end_set) {
1605                 *(global_remset->store_next++) = (mword)ptr;
1606                 goto done;
1607         }
1608         rs = alloc_remset (global_remset->end_set - global_remset->data, NULL, TRUE);
1609         rs->next = global_remset;
1610         global_remset = rs;
1611         *(global_remset->store_next++) = (mword)ptr;
1612
1613         {
1614                 int global_rs_size = 0;
1615
1616                 for (rs = global_remset; rs; rs = rs->next) {
1617                         global_rs_size += rs->store_next - rs->data;
1618                 }
1619                 DEBUG (4, fprintf (gc_debug_file, "Global remset now has size %d\n", global_rs_size));
1620         }
1621
1622  done:
1623         if (lock)
1624                 UNLOCK_GLOBAL_REMSET;
1625 }
1626
1627 /*
1628  * drain_gray_stack:
1629  *
1630  *   Scan objects in the gray stack until the stack is empty. This should be called
1631  * frequently after each object is copied, to achieve better locality and cache
1632  * usage.
1633  */
1634 static gboolean
1635 drain_gray_stack (GrayQueue *queue, int max_objs)
1636 {
1637         char *obj;
1638
1639         if (current_collection_generation == GENERATION_NURSERY) {
1640                 for (;;) {
1641                         GRAY_OBJECT_DEQUEUE (queue, obj);
1642                         if (!obj)
1643                                 return TRUE;
1644                         DEBUG (9, fprintf (gc_debug_file, "Precise gray object scan %p (%s)\n", obj, safe_name (obj)));
1645                         major_collector.minor_scan_object (obj, queue);
1646                 }
1647         } else {
1648                 int i;
1649
1650                 if (major_collector.is_parallel && queue == &workers_distribute_gray_queue)
1651                         return TRUE;
1652
1653                 do {
1654                         for (i = 0; i != max_objs; ++i) {
1655                                 GRAY_OBJECT_DEQUEUE (queue, obj);
1656                                 if (!obj)
1657                                         return TRUE;
1658                                 DEBUG (9, fprintf (gc_debug_file, "Precise gray object scan %p (%s)\n", obj, safe_name (obj)));
1659                                 major_collector.major_scan_object (obj, queue);
1660                         }
1661                 } while (max_objs < 0);
1662                 return FALSE;
1663         }
1664 }
1665
1666 /*
1667  * Addresses from start to end are already sorted. This function finds
1668  * the object header for each address and pins the object. The
1669  * addresses must be inside the passed section.  The (start of the)
1670  * address array is overwritten with the addresses of the actually
1671  * pinned objects.  Return the number of pinned objects.
1672  */
1673 static int
1674 pin_objects_from_addresses (GCMemSection *section, void **start, void **end, void *start_nursery, void *end_nursery, GrayQueue *queue)
1675 {
1676         void *last = NULL;
1677         int count = 0;
1678         void *search_start;
1679         void *last_obj = NULL;
1680         size_t last_obj_size = 0;
1681         void *addr;
1682         int idx;
1683         void **definitely_pinned = start;
1684
1685         mono_sgen_nursery_allocator_prepare_for_pinning ();
1686
1687         while (start < end) {
1688                 addr = *start;
1689                 /* the range check should be reduntant */
1690                 if (addr != last && addr >= start_nursery && addr < end_nursery) {
1691                         DEBUG (5, fprintf (gc_debug_file, "Considering pinning addr %p\n", addr));
1692                         /* multiple pointers to the same object */
1693                         if (addr >= last_obj && (char*)addr < (char*)last_obj + last_obj_size) {
1694                                 start++;
1695                                 continue;
1696                         }
1697                         idx = ((char*)addr - (char*)section->data) / SCAN_START_SIZE;
1698                         g_assert (idx < section->num_scan_start);
1699                         search_start = (void*)section->scan_starts [idx];
1700                         if (!search_start || search_start > addr) {
1701                                 while (idx) {
1702                                         --idx;
1703                                         search_start = section->scan_starts [idx];
1704                                         if (search_start && search_start <= addr)
1705                                                 break;
1706                                 }
1707                                 if (!search_start || search_start > addr)
1708                                         search_start = start_nursery;
1709                         }
1710                         if (search_start < last_obj)
1711                                 search_start = (char*)last_obj + last_obj_size;
1712                         /* now addr should be in an object a short distance from search_start
1713                          * Note that search_start must point to zeroed mem or point to an object.
1714                          */
1715
1716                         do {
1717                                 if (!*(void**)search_start) {
1718                                         /* Consistency check */
1719                                         /*
1720                                         for (frag = nursery_fragments; frag; frag = frag->next) {
1721                                                 if (search_start >= frag->fragment_start && search_start < frag->fragment_end)
1722                                                         g_assert_not_reached ();
1723                                         }
1724                                         */
1725
1726                                         search_start = (void*)ALIGN_UP ((mword)search_start + sizeof (gpointer));
1727                                         continue;
1728                                 }
1729                                 last_obj = search_start;
1730                                 last_obj_size = ALIGN_UP (safe_object_get_size ((MonoObject*)search_start));
1731
1732                                 if (((MonoObject*)last_obj)->synchronisation == GINT_TO_POINTER (-1)) {
1733                                         /* Marks the beginning of a nursery fragment, skip */
1734                                 } else {
1735                                         DEBUG (8, fprintf (gc_debug_file, "Pinned try match %p (%s), size %zd\n", last_obj, safe_name (last_obj), last_obj_size));
1736                                         if (addr >= search_start && (char*)addr < (char*)last_obj + last_obj_size) {
1737                                                 DEBUG (4, fprintf (gc_debug_file, "Pinned object %p, vtable %p (%s), count %d\n", search_start, *(void**)search_start, safe_name (search_start), count));
1738                                                 binary_protocol_pin (search_start, (gpointer)LOAD_VTABLE (search_start), safe_object_get_size (search_start));
1739                                                 pin_object (search_start);
1740                                                 GRAY_OBJECT_ENQUEUE (queue, search_start);
1741                                                 if (heap_dump_file)
1742                                                         mono_sgen_pin_stats_register_object (search_start, last_obj_size);
1743                                                 definitely_pinned [count] = search_start;
1744                                                 count++;
1745                                                 break;
1746                                         }
1747                                 }
1748                                 /* skip to the next object */
1749                                 search_start = (void*)((char*)search_start + last_obj_size);
1750                         } while (search_start <= addr);
1751                         /* we either pinned the correct object or we ignored the addr because
1752                          * it points to unused zeroed memory.
1753                          */
1754                         last = addr;
1755                 }
1756                 start++;
1757         }
1758         //printf ("effective pinned: %d (at the end: %d)\n", count, (char*)end_nursery - (char*)last);
1759         if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS) {
1760                 GCRootReport report;
1761                 report.count = 0;
1762                 for (idx = 0; idx < count; ++idx)
1763                         add_profile_gc_root (&report, definitely_pinned [idx], MONO_PROFILE_GC_ROOT_PINNING, 0);
1764                 notify_gc_roots (&report);
1765         }
1766         stat_pinned_objects += count;
1767         return count;
1768 }
1769
1770 void
1771 mono_sgen_pin_objects_in_section (GCMemSection *section, GrayQueue *queue)
1772 {
1773         int num_entries = section->pin_queue_num_entries;
1774         if (num_entries) {
1775                 void **start = section->pin_queue_start;
1776                 int reduced_to;
1777                 reduced_to = pin_objects_from_addresses (section, start, start + num_entries,
1778                                 section->data, section->next_data, queue);
1779                 section->pin_queue_num_entries = reduced_to;
1780                 if (!reduced_to)
1781                         section->pin_queue_start = NULL;
1782         }
1783 }
1784
1785
1786 void
1787 mono_sgen_pin_object (void *object, GrayQueue *queue)
1788 {
1789         if (major_collector.is_parallel) {
1790                 LOCK_PIN_QUEUE;
1791                 /*object arrives pinned*/
1792                 pin_stage_ptr (object);
1793                 ++objects_pinned ;
1794                 UNLOCK_PIN_QUEUE;
1795         } else {
1796                 SGEN_PIN_OBJECT (object);
1797                 pin_stage_ptr (object);
1798                 ++objects_pinned;
1799         }
1800         GRAY_OBJECT_ENQUEUE (queue, object);
1801 }
1802
1803 /* Sort the addresses in array in increasing order.
1804  * Done using a by-the book heap sort. Which has decent and stable performance, is pretty cache efficient.
1805  */
1806 static void
1807 sort_addresses (void **array, int size)
1808 {
1809         int i;
1810         void *tmp;
1811
1812         for (i = 1; i < size; ++i) {
1813                 int child = i;
1814                 while (child > 0) {
1815                         int parent = (child - 1) / 2;
1816
1817                         if (array [parent] >= array [child])
1818                                 break;
1819
1820                         tmp = array [parent];
1821                         array [parent] = array [child];
1822                         array [child] = tmp;
1823
1824                         child = parent;
1825                 }
1826         }
1827
1828         for (i = size - 1; i > 0; --i) {
1829                 int end, root;
1830                 tmp = array [i];
1831                 array [i] = array [0];
1832                 array [0] = tmp;
1833
1834                 end = i - 1;
1835                 root = 0;
1836
1837                 while (root * 2 + 1 <= end) {
1838                         int child = root * 2 + 1;
1839
1840                         if (child < end && array [child] < array [child + 1])
1841                                 ++child;
1842                         if (array [root] >= array [child])
1843                                 break;
1844
1845                         tmp = array [root];
1846                         array [root] = array [child];
1847                         array [child] = tmp;
1848
1849                         root = child;
1850                 }
1851         }
1852 }
1853
1854 static G_GNUC_UNUSED void
1855 print_nursery_gaps (void* start_nursery, void *end_nursery)
1856 {
1857         int i;
1858         gpointer first = start_nursery;
1859         gpointer next;
1860         for (i = 0; i < next_pin_slot; ++i) {
1861                 next = pin_queue [i];
1862                 fprintf (gc_debug_file, "Nursery range: %p-%p, size: %td\n", first, next, (char*)next-(char*)first);
1863                 first = next;
1864         }
1865         next = end_nursery;
1866         fprintf (gc_debug_file, "Nursery range: %p-%p, size: %td\n", first, next, (char*)next-(char*)first);
1867 }
1868
1869 /* reduce the info in the pin queue, removing duplicate pointers and sorting them */
1870 static void
1871 optimize_pin_queue (int start_slot)
1872 {
1873         void **start, **cur, **end;
1874         /* sort and uniq pin_queue: we just sort and we let the rest discard multiple values */
1875         /* it may be better to keep ranges of pinned memory instead of individually pinning objects */
1876         DEBUG (5, fprintf (gc_debug_file, "Sorting pin queue, size: %d\n", next_pin_slot));
1877         if ((next_pin_slot - start_slot) > 1)
1878                 sort_addresses (pin_queue + start_slot, next_pin_slot - start_slot);
1879         start = cur = pin_queue + start_slot;
1880         end = pin_queue + next_pin_slot;
1881         while (cur < end) {
1882                 *start = *cur++;
1883                 while (*start == *cur && cur < end)
1884                         cur++;
1885                 start++;
1886         };
1887         next_pin_slot = start - pin_queue;
1888         DEBUG (5, fprintf (gc_debug_file, "Pin queue reduced to size: %d\n", next_pin_slot));
1889         //DEBUG (6, print_nursery_gaps (start_nursery, end_nursery));
1890         
1891 }
1892
1893 /* 
1894  * Scan the memory between start and end and queue values which could be pointers
1895  * to the area between start_nursery and end_nursery for later consideration.
1896  * Typically used for thread stacks.
1897  */
1898 static void
1899 conservatively_pin_objects_from (void **start, void **end, void *start_nursery, void *end_nursery, int pin_type)
1900 {
1901         int count = 0;
1902         while (start < end) {
1903                 if (*start >= start_nursery && *start < end_nursery) {
1904                         /*
1905                          * *start can point to the middle of an object
1906                          * note: should we handle pointing at the end of an object?
1907                          * pinning in C# code disallows pointing at the end of an object
1908                          * but there is some small chance that an optimizing C compiler
1909                          * may keep the only reference to an object by pointing
1910                          * at the end of it. We ignore this small chance for now.
1911                          * Pointers to the end of an object are indistinguishable
1912                          * from pointers to the start of the next object in memory
1913                          * so if we allow that we'd need to pin two objects...
1914                          * We queue the pointer in an array, the
1915                          * array will then be sorted and uniqued. This way
1916                          * we can coalesce several pinning pointers and it should
1917                          * be faster since we'd do a memory scan with increasing
1918                          * addresses. Note: we can align the address to the allocation
1919                          * alignment, so the unique process is more effective.
1920                          */
1921                         mword addr = (mword)*start;
1922                         addr &= ~(ALLOC_ALIGN - 1);
1923                         if (addr >= (mword)start_nursery && addr < (mword)end_nursery)
1924                                 pin_stage_ptr ((void*)addr);
1925                         if (heap_dump_file)
1926                                 pin_stats_register_address ((char*)addr, pin_type);
1927                         DEBUG (6, if (count) fprintf (gc_debug_file, "Pinning address %p from %p\n", (void*)addr, start));
1928                         count++;
1929                 }
1930                 start++;
1931         }
1932         DEBUG (7, if (count) fprintf (gc_debug_file, "found %d potential pinned heap pointers\n", count));
1933 }
1934
1935 /*
1936  * Debugging function: find in the conservative roots where @obj is being pinned.
1937  */
1938 static G_GNUC_UNUSED void
1939 find_pinning_reference (char *obj, size_t size)
1940 {
1941         RootRecord *root;
1942         int i;
1943         char *endobj = obj + size;
1944         for (i = 0; i < roots_hash_size [0]; ++i) {
1945                 for (root = roots_hash [0][i]; root; root = root->next) {
1946                         /* if desc is non-null it has precise info */
1947                         if (!root->root_desc) {
1948                                 char ** start = (char**)root->start_root;
1949                                 while (start < (char**)root->end_root) {
1950                                         if (*start >= obj && *start < endobj) {
1951                                                 DEBUG (0, fprintf (gc_debug_file, "Object %p referenced in pinned roots %p-%p (at %p in record %p)\n", obj, root->start_root, root->end_root, start, root));
1952                                         }
1953                                         start++;
1954                                 }
1955                         }
1956                 }
1957         }
1958         find_pinning_ref_from_thread (obj, size);
1959 }
1960
1961 /*
1962  * The first thing we do in a collection is to identify pinned objects.
1963  * This function considers all the areas of memory that need to be
1964  * conservatively scanned.
1965  */
1966 static void
1967 pin_from_roots (void *start_nursery, void *end_nursery, GrayQueue *queue)
1968 {
1969         RootRecord *root;
1970         int i;
1971         DEBUG (2, fprintf (gc_debug_file, "Scanning pinned roots (%d bytes, %d/%d entries)\n", (int)roots_size, num_roots_entries [ROOT_TYPE_NORMAL], num_roots_entries [ROOT_TYPE_PINNED]));
1972         /* objects pinned from the API are inside these roots */
1973         for (i = 0; i < roots_hash_size [ROOT_TYPE_PINNED]; ++i) {
1974                 for (root = roots_hash [ROOT_TYPE_PINNED][i]; root; root = root->next) {
1975                         DEBUG (6, fprintf (gc_debug_file, "Pinned roots %p-%p\n", root->start_root, root->end_root));
1976                         conservatively_pin_objects_from ((void**)root->start_root, (void**)root->end_root, start_nursery, end_nursery, PIN_TYPE_OTHER);
1977                 }
1978         }
1979         /* now deal with the thread stacks
1980          * in the future we should be able to conservatively scan only:
1981          * *) the cpu registers
1982          * *) the unmanaged stack frames
1983          * *) the _last_ managed stack frame
1984          * *) pointers slots in managed frames
1985          */
1986         scan_thread_data (start_nursery, end_nursery, FALSE, queue);
1987
1988         evacuate_pin_staging_area ();
1989 }
1990
1991 typedef struct {
1992         CopyOrMarkObjectFunc func;
1993         GrayQueue *queue;
1994 } UserCopyOrMarkData;
1995
1996 static pthread_key_t user_copy_or_mark_key;
1997
1998 static void
1999 init_user_copy_or_mark_key (void)
2000 {
2001         pthread_key_create (&user_copy_or_mark_key, NULL);
2002 }
2003
2004 static void
2005 set_user_copy_or_mark_data (UserCopyOrMarkData *data)
2006 {
2007         static pthread_once_t init_control = PTHREAD_ONCE_INIT;
2008         pthread_once (&init_control, init_user_copy_or_mark_key);
2009
2010         pthread_setspecific (user_copy_or_mark_key, data);
2011 }
2012
2013 static void
2014 single_arg_user_copy_or_mark (void **obj)
2015 {
2016         UserCopyOrMarkData *data = pthread_getspecific (user_copy_or_mark_key);
2017
2018         data->func (obj, data->queue);
2019 }
2020
2021 /*
2022  * The memory area from start_root to end_root contains pointers to objects.
2023  * Their position is precisely described by @desc (this means that the pointer
2024  * can be either NULL or the pointer to the start of an object).
2025  * This functions copies them to to_space updates them.
2026  *
2027  * This function is not thread-safe!
2028  */
2029 static void
2030 precisely_scan_objects_from (CopyOrMarkObjectFunc copy_func, void** start_root, void** end_root, char* n_start, char *n_end, mword desc, GrayQueue *queue)
2031 {
2032         switch (desc & ROOT_DESC_TYPE_MASK) {
2033         case ROOT_DESC_BITMAP:
2034                 desc >>= ROOT_DESC_TYPE_SHIFT;
2035                 while (desc) {
2036                         if ((desc & 1) && *start_root) {
2037                                 copy_func (start_root, queue);
2038                                 DEBUG (9, fprintf (gc_debug_file, "Overwrote root at %p with %p\n", start_root, *start_root));
2039                                 drain_gray_stack (queue, -1);
2040                         }
2041                         desc >>= 1;
2042                         start_root++;
2043                 }
2044                 return;
2045         case ROOT_DESC_COMPLEX: {
2046                 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
2047                 int bwords = (*bitmap_data) - 1;
2048                 void **start_run = start_root;
2049                 bitmap_data++;
2050                 while (bwords-- > 0) {
2051                         gsize bmap = *bitmap_data++;
2052                         void **objptr = start_run;
2053                         while (bmap) {
2054                                 if ((bmap & 1) && *objptr) {
2055                                         copy_func (objptr, queue);
2056                                         DEBUG (9, fprintf (gc_debug_file, "Overwrote root at %p with %p\n", objptr, *objptr));
2057                                         drain_gray_stack (queue, -1);
2058                                 }
2059                                 bmap >>= 1;
2060                                 ++objptr;
2061                         }
2062                         start_run += GC_BITS_PER_WORD;
2063                 }
2064                 break;
2065         }
2066         case ROOT_DESC_USER: {
2067                 UserCopyOrMarkData data = { copy_func, queue };
2068                 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
2069                 set_user_copy_or_mark_data (&data);
2070                 marker (start_root, single_arg_user_copy_or_mark);
2071                 set_user_copy_or_mark_data (NULL);
2072                 break;
2073         }
2074         case ROOT_DESC_RUN_LEN:
2075                 g_assert_not_reached ();
2076         default:
2077                 g_assert_not_reached ();
2078         }
2079 }
2080
2081 static void
2082 reset_heap_boundaries (void)
2083 {
2084         lowest_heap_address = ~(mword)0;
2085         highest_heap_address = 0;
2086 }
2087
2088 void
2089 mono_sgen_update_heap_boundaries (mword low, mword high)
2090 {
2091         mword old;
2092
2093         do {
2094                 old = lowest_heap_address;
2095                 if (low >= old)
2096                         break;
2097         } while (SGEN_CAS_PTR ((gpointer*)&lowest_heap_address, (gpointer)low, (gpointer)old) != (gpointer)old);
2098
2099         do {
2100                 old = highest_heap_address;
2101                 if (high <= old)
2102                         break;
2103         } while (SGEN_CAS_PTR ((gpointer*)&highest_heap_address, (gpointer)high, (gpointer)old) != (gpointer)old);
2104 }
2105
2106 /* size must be a power of 2 */
2107 void*
2108 mono_sgen_alloc_os_memory_aligned (mword size, mword alignment, gboolean activate)
2109 {
2110         /* Allocate twice the memory to be able to put the block on an aligned address */
2111         char *mem = mono_sgen_alloc_os_memory (size + alignment, activate);
2112         char *aligned;
2113
2114         g_assert (mem);
2115
2116         aligned = (char*)((mword)(mem + (alignment - 1)) & ~(alignment - 1));
2117         g_assert (aligned >= mem && aligned + size <= mem + size + alignment && !((mword)aligned & (alignment - 1)));
2118
2119         if (aligned > mem)
2120                 mono_sgen_free_os_memory (mem, aligned - mem);
2121         if (aligned + size < mem + size + alignment)
2122                 mono_sgen_free_os_memory (aligned + size, (mem + size + alignment) - (aligned + size));
2123
2124         return aligned;
2125 }
2126
2127 /*
2128  * Allocate and setup the data structures needed to be able to allocate objects
2129  * in the nursery. The nursery is stored in nursery_section.
2130  */
2131 static void
2132 alloc_nursery (void)
2133 {
2134         GCMemSection *section;
2135         char *data;
2136         int scan_starts;
2137         int alloc_size;
2138
2139         if (nursery_section)
2140                 return;
2141         DEBUG (2, fprintf (gc_debug_file, "Allocating nursery size: %lu\n", (unsigned long)nursery_size));
2142         /* later we will alloc a larger area for the nursery but only activate
2143          * what we need. The rest will be used as expansion if we have too many pinned
2144          * objects in the existing nursery.
2145          */
2146         /* FIXME: handle OOM */
2147         section = mono_sgen_alloc_internal (INTERNAL_MEM_SECTION);
2148
2149         g_assert (nursery_size == DEFAULT_NURSERY_SIZE);
2150         alloc_size = nursery_size;
2151 #ifdef SGEN_ALIGN_NURSERY
2152         data = major_collector.alloc_heap (alloc_size, alloc_size, DEFAULT_NURSERY_BITS);
2153 #else
2154         data = major_collector.alloc_heap (alloc_size, 0, DEFAULT_NURSERY_BITS);
2155 #endif
2156         nursery_start = data;
2157         nursery_end = nursery_start + nursery_size;
2158         mono_sgen_update_heap_boundaries ((mword)nursery_start, (mword)nursery_end);
2159         DEBUG (4, fprintf (gc_debug_file, "Expanding nursery size (%p-%p): %lu, total: %lu\n", data, data + alloc_size, (unsigned long)nursery_size, (unsigned long)total_alloc));
2160         section->data = section->next_data = data;
2161         section->size = alloc_size;
2162         section->end_data = nursery_end;
2163         scan_starts = (alloc_size + SCAN_START_SIZE - 1) / SCAN_START_SIZE;
2164         section->scan_starts = mono_sgen_alloc_internal_dynamic (sizeof (char*) * scan_starts, INTERNAL_MEM_SCAN_STARTS);
2165         section->num_scan_start = scan_starts;
2166         section->block.role = MEMORY_ROLE_GEN0;
2167         section->block.next = NULL;
2168
2169         nursery_section = section;
2170
2171         mono_sgen_nursery_allocator_set_nursery_bounds (nursery_start, nursery_end);
2172 }
2173
2174 void*
2175 mono_gc_get_nursery (int *shift_bits, size_t *size)
2176 {
2177         *size = nursery_size;
2178 #ifdef SGEN_ALIGN_NURSERY
2179         *shift_bits = DEFAULT_NURSERY_BITS;
2180 #else
2181         *shift_bits = -1;
2182 #endif
2183         return nursery_start;
2184 }
2185
2186 gboolean
2187 mono_gc_precise_stack_mark_enabled (void)
2188 {
2189         return !conservative_stack_mark;
2190 }
2191
2192 FILE *
2193 mono_gc_get_logfile (void)
2194 {
2195         return mono_sgen_get_logfile ();
2196 }
2197
2198 static void
2199 report_finalizer_roots_list (FinalizeEntry *list)
2200 {
2201         GCRootReport report;
2202         FinalizeEntry *fin;
2203
2204         report.count = 0;
2205         for (fin = list; fin; fin = fin->next) {
2206                 if (!fin->object)
2207                         continue;
2208                 add_profile_gc_root (&report, fin->object, MONO_PROFILE_GC_ROOT_FINALIZER, 0);
2209         }
2210         notify_gc_roots (&report);
2211 }
2212
2213 static void
2214 report_finalizer_roots (void)
2215 {
2216         report_finalizer_roots_list (fin_ready_list);
2217         report_finalizer_roots_list (critical_fin_list);
2218 }
2219
2220 static GCRootReport *root_report;
2221
2222 static void
2223 single_arg_report_root (void **obj)
2224 {
2225         if (*obj)
2226                 add_profile_gc_root (root_report, *obj, MONO_PROFILE_GC_ROOT_OTHER, 0);
2227 }
2228
2229 static void
2230 precisely_report_roots_from (GCRootReport *report, void** start_root, void** end_root, mword desc)
2231 {
2232         switch (desc & ROOT_DESC_TYPE_MASK) {
2233         case ROOT_DESC_BITMAP:
2234                 desc >>= ROOT_DESC_TYPE_SHIFT;
2235                 while (desc) {
2236                         if ((desc & 1) && *start_root) {
2237                                 add_profile_gc_root (report, *start_root, MONO_PROFILE_GC_ROOT_OTHER, 0);
2238                         }
2239                         desc >>= 1;
2240                         start_root++;
2241                 }
2242                 return;
2243         case ROOT_DESC_COMPLEX: {
2244                 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
2245                 int bwords = (*bitmap_data) - 1;
2246                 void **start_run = start_root;
2247                 bitmap_data++;
2248                 while (bwords-- > 0) {
2249                         gsize bmap = *bitmap_data++;
2250                         void **objptr = start_run;
2251                         while (bmap) {
2252                                 if ((bmap & 1) && *objptr) {
2253                                         add_profile_gc_root (report, *objptr, MONO_PROFILE_GC_ROOT_OTHER, 0);
2254                                 }
2255                                 bmap >>= 1;
2256                                 ++objptr;
2257                         }
2258                         start_run += GC_BITS_PER_WORD;
2259                 }
2260                 break;
2261         }
2262         case ROOT_DESC_USER: {
2263                 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
2264                 root_report = report;
2265                 marker (start_root, single_arg_report_root);
2266                 break;
2267         }
2268         case ROOT_DESC_RUN_LEN:
2269                 g_assert_not_reached ();
2270         default:
2271                 g_assert_not_reached ();
2272         }
2273 }
2274
2275 static void
2276 report_registered_roots_by_type (int root_type)
2277 {
2278         GCRootReport report;
2279         int i;
2280         RootRecord *root;
2281         report.count = 0;
2282         for (i = 0; i < roots_hash_size [root_type]; ++i) {
2283                 for (root = roots_hash [root_type][i]; root; root = root->next) {
2284                         DEBUG (6, fprintf (gc_debug_file, "Precise root scan %p-%p (desc: %p)\n", root->start_root, root->end_root, (void*)root->root_desc));
2285                         precisely_report_roots_from (&report, (void**)root->start_root, (void**)root->end_root, root->root_desc);
2286                 }
2287         }
2288         notify_gc_roots (&report);
2289 }
2290
2291 static void
2292 report_registered_roots (void)
2293 {
2294         report_registered_roots_by_type (ROOT_TYPE_NORMAL);
2295         report_registered_roots_by_type (ROOT_TYPE_WBARRIER);
2296 }
2297
2298 static void
2299 scan_finalizer_entries (CopyOrMarkObjectFunc copy_func, FinalizeEntry *list, GrayQueue *queue)
2300 {
2301         FinalizeEntry *fin;
2302
2303         for (fin = list; fin; fin = fin->next) {
2304                 if (!fin->object)
2305                         continue;
2306                 DEBUG (5, fprintf (gc_debug_file, "Scan of fin ready object: %p (%s)\n", fin->object, safe_name (fin->object)));
2307                 copy_func (&fin->object, queue);
2308         }
2309 }
2310
2311 static const char*
2312 generation_name (int generation)
2313 {
2314         switch (generation) {
2315         case GENERATION_NURSERY: return "nursery";
2316         case GENERATION_OLD: return "old";
2317         default: g_assert_not_reached ();
2318         }
2319 }
2320
2321 static MonoObject **finalized_array = NULL;
2322 static int finalized_array_capacity = 0;
2323 static int finalized_array_entries = 0;
2324
2325 static void
2326 bridge_register_finalized_object (MonoObject *object)
2327 {
2328         if (!finalized_array)
2329                 return;
2330
2331         if (finalized_array_entries >= finalized_array_capacity) {
2332                 MonoObject **new_array;
2333                 g_assert (finalized_array_entries == finalized_array_capacity);
2334                 finalized_array_capacity *= 2;
2335                 new_array = mono_sgen_alloc_internal_dynamic (sizeof (MonoObject*) * finalized_array_capacity, INTERNAL_MEM_BRIDGE_DATA);
2336                 memcpy (new_array, finalized_array, sizeof (MonoObject*) * finalized_array_entries);
2337                 mono_sgen_free_internal_dynamic (finalized_array, sizeof (MonoObject*) * finalized_array_entries, INTERNAL_MEM_BRIDGE_DATA);
2338                 finalized_array = new_array;
2339         }
2340         finalized_array [finalized_array_entries++] = object;
2341 }
2342
2343 static void
2344 finish_gray_stack (char *start_addr, char *end_addr, int generation, GrayQueue *queue)
2345 {
2346         TV_DECLARE (atv);
2347         TV_DECLARE (btv);
2348         int fin_ready;
2349         int ephemeron_rounds = 0;
2350         int num_loops;
2351         CopyOrMarkObjectFunc copy_func = current_collection_generation == GENERATION_NURSERY ? major_collector.copy_object : major_collector.copy_or_mark_object;
2352
2353         /*
2354          * We copied all the reachable objects. Now it's the time to copy
2355          * the objects that were not referenced by the roots, but by the copied objects.
2356          * we built a stack of objects pointed to by gray_start: they are
2357          * additional roots and we may add more items as we go.
2358          * We loop until gray_start == gray_objects which means no more objects have
2359          * been added. Note this is iterative: no recursion is involved.
2360          * We need to walk the LO list as well in search of marked big objects
2361          * (use a flag since this is needed only on major collections). We need to loop
2362          * here as well, so keep a counter of marked LO (increasing it in copy_object).
2363          *   To achieve better cache locality and cache usage, we drain the gray stack 
2364          * frequently, after each object is copied, and just finish the work here.
2365          */
2366         drain_gray_stack (queue, -1);
2367         TV_GETTIME (atv);
2368         DEBUG (2, fprintf (gc_debug_file, "%s generation done\n", generation_name (generation)));
2369
2370         /*
2371         We must clear weak links that don't track resurrection before processing object ready for
2372         finalization so they can be cleared before that.
2373         */
2374         null_link_in_range (copy_func, start_addr, end_addr, generation, TRUE, queue);
2375         if (generation == GENERATION_OLD)
2376                 null_link_in_range (copy_func, start_addr, end_addr, GENERATION_NURSERY, TRUE, queue);
2377
2378         if (finalized_array == NULL && mono_sgen_need_bridge_processing ()) {
2379                 finalized_array_capacity = 32;
2380                 finalized_array = mono_sgen_alloc_internal_dynamic (sizeof (MonoObject*) * finalized_array_capacity, INTERNAL_MEM_BRIDGE_DATA);
2381         }
2382         finalized_array_entries = 0;
2383
2384         /* walk the finalization queue and move also the objects that need to be
2385          * finalized: use the finalized objects as new roots so the objects they depend
2386          * on are also not reclaimed. As with the roots above, only objects in the nursery
2387          * are marked/copied.
2388          * We need a loop here, since objects ready for finalizers may reference other objects
2389          * that are fin-ready. Speedup with a flag?
2390          */
2391         num_loops = 0;
2392         do {
2393                 /*
2394                  * Walk the ephemeron tables marking all values with reachable keys. This must be completely done
2395                  * before processing finalizable objects to avoid finalizing reachable values.
2396                  *
2397                  * It must be done inside the finalizaters loop since objects must not be removed from CWT tables
2398                  * while they are been finalized.
2399                  */
2400                 int done_with_ephemerons = 0;
2401                 do {
2402                         done_with_ephemerons = mark_ephemerons_in_range (copy_func, start_addr, end_addr, queue);
2403                         drain_gray_stack (queue, -1);
2404                         ++ephemeron_rounds;
2405                 } while (!done_with_ephemerons);
2406
2407                 fin_ready = num_ready_finalizers;
2408                 finalize_in_range (copy_func, start_addr, end_addr, generation, queue);
2409                 if (generation == GENERATION_OLD)
2410                         finalize_in_range (copy_func, nursery_start, nursery_end, GENERATION_NURSERY, queue);
2411
2412                 if (fin_ready != num_ready_finalizers) {
2413                         ++num_loops;
2414                         if (finalized_array != NULL)
2415                                 mono_sgen_bridge_processing (finalized_array_entries, finalized_array);
2416                 }
2417
2418                 /* drain the new stack that might have been created */
2419                 DEBUG (6, fprintf (gc_debug_file, "Precise scan of gray area post fin\n"));
2420                 drain_gray_stack (queue, -1);
2421         } while (fin_ready != num_ready_finalizers);
2422
2423         if (mono_sgen_need_bridge_processing ())
2424                 g_assert (num_loops <= 1);
2425
2426         /*
2427          * Clear ephemeron pairs with unreachable keys.
2428          * We pass the copy func so we can figure out if an array was promoted or not.
2429          */
2430         clear_unreachable_ephemerons (copy_func, start_addr, end_addr, queue);
2431
2432         TV_GETTIME (btv);
2433         DEBUG (2, fprintf (gc_debug_file, "Finalize queue handling scan for %s generation: %d usecs %d ephemeron roundss\n", generation_name (generation), TV_ELAPSED (atv, btv), ephemeron_rounds));
2434
2435         /*
2436          * handle disappearing links
2437          * Note we do this after checking the finalization queue because if an object
2438          * survives (at least long enough to be finalized) we don't clear the link.
2439          * This also deals with a possible issue with the monitor reclamation: with the Boehm
2440          * GC a finalized object my lose the monitor because it is cleared before the finalizer is
2441          * called.
2442          */
2443         g_assert (gray_object_queue_is_empty (queue));
2444         for (;;) {
2445                 null_link_in_range (copy_func, start_addr, end_addr, generation, FALSE, queue);
2446                 if (generation == GENERATION_OLD)
2447                         null_link_in_range (copy_func, start_addr, end_addr, GENERATION_NURSERY, FALSE, queue);
2448                 if (gray_object_queue_is_empty (queue))
2449                         break;
2450                 drain_gray_stack (queue, -1);
2451         }
2452
2453         g_assert (gray_object_queue_is_empty (queue));
2454 }
2455
2456 void
2457 mono_sgen_check_section_scan_starts (GCMemSection *section)
2458 {
2459         int i;
2460         for (i = 0; i < section->num_scan_start; ++i) {
2461                 if (section->scan_starts [i]) {
2462                         guint size = safe_object_get_size ((MonoObject*) section->scan_starts [i]);
2463                         g_assert (size >= sizeof (MonoObject) && size <= MAX_SMALL_OBJ_SIZE);
2464                 }
2465         }
2466 }
2467
2468 static void
2469 check_scan_starts (void)
2470 {
2471         if (!do_scan_starts_check)
2472                 return;
2473         mono_sgen_check_section_scan_starts (nursery_section);
2474         major_collector.check_scan_starts ();
2475 }
2476
2477 static int last_num_pinned = 0;
2478
2479 static void
2480 scan_from_registered_roots (CopyOrMarkObjectFunc copy_func, char *addr_start, char *addr_end, int root_type, GrayQueue *queue)
2481 {
2482         int i;
2483         RootRecord *root;
2484         for (i = 0; i < roots_hash_size [root_type]; ++i) {
2485                 for (root = roots_hash [root_type][i]; root; root = root->next) {
2486                         DEBUG (6, fprintf (gc_debug_file, "Precise root scan %p-%p (desc: %p)\n", root->start_root, root->end_root, (void*)root->root_desc));
2487                         precisely_scan_objects_from (copy_func, (void**)root->start_root, (void**)root->end_root, addr_start, addr_end, root->root_desc, queue);
2488                 }
2489         }
2490 }
2491
2492 void
2493 mono_sgen_dump_occupied (char *start, char *end, char *section_start)
2494 {
2495         fprintf (heap_dump_file, "<occupied offset=\"%td\" size=\"%td\"/>\n", start - section_start, end - start);
2496 }
2497
2498 void
2499 mono_sgen_dump_section (GCMemSection *section, const char *type)
2500 {
2501         char *start = section->data;
2502         char *end = section->data + section->size;
2503         char *occ_start = NULL;
2504         GCVTable *vt;
2505         char *old_start = NULL; /* just for debugging */
2506
2507         fprintf (heap_dump_file, "<section type=\"%s\" size=\"%lu\">\n", type, (unsigned long)section->size);
2508
2509         while (start < end) {
2510                 guint size;
2511                 MonoClass *class;
2512
2513                 if (!*(void**)start) {
2514                         if (occ_start) {
2515                                 mono_sgen_dump_occupied (occ_start, start, section->data);
2516                                 occ_start = NULL;
2517                         }
2518                         start += sizeof (void*); /* should be ALLOC_ALIGN, really */
2519                         continue;
2520                 }
2521                 g_assert (start < section->next_data);
2522
2523                 if (!occ_start)
2524                         occ_start = start;
2525
2526                 vt = (GCVTable*)LOAD_VTABLE (start);
2527                 class = vt->klass;
2528
2529                 size = ALIGN_UP (safe_object_get_size ((MonoObject*) start));
2530
2531                 /*
2532                 fprintf (heap_dump_file, "<object offset=\"%d\" class=\"%s.%s\" size=\"%d\"/>\n",
2533                                 start - section->data,
2534                                 vt->klass->name_space, vt->klass->name,
2535                                 size);
2536                 */
2537
2538                 old_start = start;
2539                 start += size;
2540         }
2541         if (occ_start)
2542                 mono_sgen_dump_occupied (occ_start, start, section->data);
2543
2544         fprintf (heap_dump_file, "</section>\n");
2545 }
2546
2547 static void
2548 dump_object (MonoObject *obj, gboolean dump_location)
2549 {
2550         static char class_name [1024];
2551
2552         MonoClass *class = mono_object_class (obj);
2553         int i, j;
2554
2555         /*
2556          * Python's XML parser is too stupid to parse angle brackets
2557          * in strings, so we just ignore them;
2558          */
2559         i = j = 0;
2560         while (class->name [i] && j < sizeof (class_name) - 1) {
2561                 if (!strchr ("<>\"", class->name [i]))
2562                         class_name [j++] = class->name [i];
2563                 ++i;
2564         }
2565         g_assert (j < sizeof (class_name));
2566         class_name [j] = 0;
2567
2568         fprintf (heap_dump_file, "<object class=\"%s.%s\" size=\"%d\"",
2569                         class->name_space, class_name,
2570                         safe_object_get_size (obj));
2571         if (dump_location) {
2572                 const char *location;
2573                 if (ptr_in_nursery (obj))
2574                         location = "nursery";
2575                 else if (safe_object_get_size (obj) <= MAX_SMALL_OBJ_SIZE)
2576                         location = "major";
2577                 else
2578                         location = "LOS";
2579                 fprintf (heap_dump_file, " location=\"%s\"", location);
2580         }
2581         fprintf (heap_dump_file, "/>\n");
2582 }
2583
2584 static void
2585 dump_heap (const char *type, int num, const char *reason)
2586 {
2587         ObjectList *list;
2588         LOSObject *bigobj;
2589
2590         fprintf (heap_dump_file, "<collection type=\"%s\" num=\"%d\"", type, num);
2591         if (reason)
2592                 fprintf (heap_dump_file, " reason=\"%s\"", reason);
2593         fprintf (heap_dump_file, ">\n");
2594         fprintf (heap_dump_file, "<other-mem-usage type=\"mempools\" size=\"%ld\"/>\n", mono_mempool_get_bytes_allocated ());
2595         mono_sgen_dump_internal_mem_usage (heap_dump_file);
2596         fprintf (heap_dump_file, "<pinned type=\"stack\" bytes=\"%zu\"/>\n", pinned_byte_counts [PIN_TYPE_STACK]);
2597         /* fprintf (heap_dump_file, "<pinned type=\"static-data\" bytes=\"%d\"/>\n", pinned_byte_counts [PIN_TYPE_STATIC_DATA]); */
2598         fprintf (heap_dump_file, "<pinned type=\"other\" bytes=\"%zu\"/>\n", pinned_byte_counts [PIN_TYPE_OTHER]);
2599
2600         fprintf (heap_dump_file, "<pinned-objects>\n");
2601         for (list = pinned_objects; list; list = list->next)
2602                 dump_object (list->obj, TRUE);
2603         fprintf (heap_dump_file, "</pinned-objects>\n");
2604
2605         mono_sgen_dump_section (nursery_section, "nursery");
2606
2607         major_collector.dump_heap (heap_dump_file);
2608
2609         fprintf (heap_dump_file, "<los>\n");
2610         for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
2611                 dump_object ((MonoObject*)bigobj->data, FALSE);
2612         fprintf (heap_dump_file, "</los>\n");
2613
2614         fprintf (heap_dump_file, "</collection>\n");
2615 }
2616
2617 void
2618 mono_sgen_register_moved_object (void *obj, void *destination)
2619 {
2620         g_assert (mono_profiler_events & MONO_PROFILE_GC_MOVES);
2621
2622         /* FIXME: handle this for parallel collector */
2623         g_assert (!major_collector.is_parallel);
2624
2625         if (moved_objects_idx == MOVED_OBJECTS_NUM) {
2626                 mono_profiler_gc_moves (moved_objects, moved_objects_idx);
2627                 moved_objects_idx = 0;
2628         }
2629         moved_objects [moved_objects_idx++] = obj;
2630         moved_objects [moved_objects_idx++] = destination;
2631 }
2632
2633 static void
2634 init_stats (void)
2635 {
2636         static gboolean inited = FALSE;
2637
2638         if (inited)
2639                 return;
2640
2641         mono_counters_register ("Minor fragment clear", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_pre_collection_fragment_clear);
2642         mono_counters_register ("Minor pinning", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_pinning);
2643         mono_counters_register ("Minor scan remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_remsets);
2644         mono_counters_register ("Minor scan cardtables", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_card_table);
2645         mono_counters_register ("Minor scan pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_pinned);
2646         mono_counters_register ("Minor scan registered roots", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_registered_roots);
2647         mono_counters_register ("Minor scan thread data", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_thread_data);
2648         mono_counters_register ("Minor finish gray stack", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_finish_gray_stack);
2649         mono_counters_register ("Minor fragment creation", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_fragment_creation);
2650
2651         mono_counters_register ("Major fragment clear", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_pre_collection_fragment_clear);
2652         mono_counters_register ("Major pinning", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_pinning);
2653         mono_counters_register ("Major scan pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_pinned);
2654         mono_counters_register ("Major scan registered roots", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_registered_roots);
2655         mono_counters_register ("Major scan thread data", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_thread_data);
2656         mono_counters_register ("Major scan alloc_pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_alloc_pinned);
2657         mono_counters_register ("Major scan finalized", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_finalized);
2658         mono_counters_register ("Major scan big objects", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_big_objects);
2659         mono_counters_register ("Major finish gray stack", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_finish_gray_stack);
2660         mono_counters_register ("Major free big objects", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_free_bigobjs);
2661         mono_counters_register ("Major LOS sweep", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_los_sweep);
2662         mono_counters_register ("Major sweep", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_sweep);
2663         mono_counters_register ("Major fragment creation", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_fragment_creation);
2664
2665         mono_counters_register ("Number of pinned objects", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_pinned_objects);
2666
2667 #ifdef HEAVY_STATISTICS
2668         mono_counters_register ("WBarrier set field", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_field);
2669         mono_counters_register ("WBarrier set arrayref", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_arrayref);
2670         mono_counters_register ("WBarrier arrayref copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_arrayref_copy);
2671         mono_counters_register ("WBarrier generic store called", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_generic_store);
2672         mono_counters_register ("WBarrier generic store stored", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_generic_store_remset);
2673         mono_counters_register ("WBarrier set root", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_root);
2674         mono_counters_register ("WBarrier value copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_value_copy);
2675         mono_counters_register ("WBarrier object copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_object_copy);
2676
2677         mono_counters_register ("# objects allocated", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_alloced);
2678         mono_counters_register ("bytes allocated", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_bytes_alloced);
2679         mono_counters_register ("# objects allocated degraded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_alloced_degraded);
2680         mono_counters_register ("bytes allocated degraded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_bytes_alloced_degraded);
2681         mono_counters_register ("bytes allocated in LOS", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_bytes_alloced_los);
2682
2683         mono_counters_register ("# copy_object() called (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_copy_object_called_nursery);
2684         mono_counters_register ("# objects copied (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_copied_nursery);
2685         mono_counters_register ("# copy_object() called (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_copy_object_called_major);
2686         mono_counters_register ("# objects copied (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_copied_major);
2687
2688         mono_counters_register ("# scan_object() called (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_scan_object_called_nursery);
2689         mono_counters_register ("# scan_object() called (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_scan_object_called_major);
2690
2691         mono_counters_register ("# nursery copy_object() failed from space", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_from_space);
2692         mono_counters_register ("# nursery copy_object() failed forwarded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_forwarded);
2693         mono_counters_register ("# nursery copy_object() failed pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_pinned);
2694
2695         mono_sgen_nursery_allocator_init_heavy_stats ();
2696
2697         mono_counters_register ("Store remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_store_remsets);
2698         mono_counters_register ("Unique store remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_store_remsets_unique);
2699         mono_counters_register ("Saved remsets 1", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_saved_remsets_1);
2700         mono_counters_register ("Saved remsets 2", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_saved_remsets_2);
2701         mono_counters_register ("Non-global remsets processed", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_local_remsets_processed);
2702         mono_counters_register ("Global remsets added", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_added);
2703         mono_counters_register ("Global remsets re-added", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_readded);
2704         mono_counters_register ("Global remsets processed", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_processed);
2705         mono_counters_register ("Global remsets discarded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_discarded);
2706 #endif
2707
2708         inited = TRUE;
2709 }
2710
2711 static gboolean need_calculate_minor_collection_allowance;
2712
2713 static int last_collection_old_num_major_sections;
2714 static mword last_collection_los_memory_usage = 0;
2715 static mword last_collection_old_los_memory_usage;
2716 static mword last_collection_los_memory_alloced;
2717
2718 static void
2719 reset_minor_collection_allowance (void)
2720 {
2721         need_calculate_minor_collection_allowance = TRUE;
2722 }
2723
2724 static void
2725 try_calculate_minor_collection_allowance (gboolean overwrite)
2726 {
2727         int num_major_sections, num_major_sections_saved, save_target, allowance_target;
2728         mword los_memory_saved;
2729
2730         if (overwrite)
2731                 g_assert (need_calculate_minor_collection_allowance);
2732
2733         if (!need_calculate_minor_collection_allowance)
2734                 return;
2735
2736         if (!*major_collector.have_swept) {
2737                 if (overwrite)
2738                         minor_collection_allowance = MIN_MINOR_COLLECTION_ALLOWANCE;
2739                 return;
2740         }
2741
2742         num_major_sections = major_collector.get_num_major_sections ();
2743
2744         num_major_sections_saved = MAX (last_collection_old_num_major_sections - num_major_sections, 0);
2745         los_memory_saved = MAX (last_collection_old_los_memory_usage - last_collection_los_memory_usage, 1);
2746
2747         save_target = ((num_major_sections * major_collector.section_size) + los_memory_saved) / 2;
2748
2749         /*
2750          * We aim to allow the allocation of as many sections as is
2751          * necessary to reclaim save_target sections in the next
2752          * collection.  We assume the collection pattern won't change.
2753          * In the last cycle, we had num_major_sections_saved for
2754          * minor_collection_sections_alloced.  Assuming things won't
2755          * change, this must be the same ratio as save_target for
2756          * allowance_target, i.e.
2757          *
2758          *    num_major_sections_saved            save_target
2759          * --------------------------------- == ----------------
2760          * minor_collection_sections_alloced    allowance_target
2761          *
2762          * hence:
2763          */
2764         allowance_target = (mword)((double)save_target * (double)(minor_collection_sections_alloced * major_collector.section_size + last_collection_los_memory_alloced) / (double)(num_major_sections_saved * major_collector.section_size + los_memory_saved));
2765
2766         minor_collection_allowance = MAX (MIN (allowance_target, num_major_sections * major_collector.section_size + los_memory_usage), MIN_MINOR_COLLECTION_ALLOWANCE);
2767
2768         if (major_collector.have_computed_minor_collection_allowance)
2769                 major_collector.have_computed_minor_collection_allowance ();
2770
2771         need_calculate_minor_collection_allowance = FALSE;
2772 }
2773
2774 static gboolean
2775 need_major_collection (mword space_needed)
2776 {
2777         mword los_alloced = los_memory_usage - MIN (last_collection_los_memory_usage, los_memory_usage);
2778         return (space_needed > available_free_space ()) ||
2779                 minor_collection_sections_alloced * major_collector.section_size + los_alloced > minor_collection_allowance;
2780 }
2781
2782 gboolean
2783 mono_sgen_need_major_collection (mword space_needed)
2784 {
2785         return need_major_collection (space_needed);
2786 }
2787
2788 static GrayQueue*
2789 job_gray_queue (WorkerData *worker_data)
2790 {
2791         return worker_data ? &worker_data->private_gray_queue : WORKERS_DISTRIBUTE_GRAY_QUEUE;
2792 }
2793
2794 typedef struct
2795 {
2796         char *heap_start;
2797         char *heap_end;
2798 } ScanFromRemsetsJobData;
2799
2800 static void
2801 job_scan_from_remsets (WorkerData *worker_data, void *job_data_untyped)
2802 {
2803         ScanFromRemsetsJobData *job_data = job_data_untyped;
2804
2805         scan_from_remsets (job_data->heap_start, job_data->heap_end, job_gray_queue (worker_data));
2806 }
2807
2808 typedef struct
2809 {
2810         CopyOrMarkObjectFunc func;
2811         char *heap_start;
2812         char *heap_end;
2813         int root_type;
2814 } ScanFromRegisteredRootsJobData;
2815
2816 static void
2817 job_scan_from_registered_roots (WorkerData *worker_data, void *job_data_untyped)
2818 {
2819         ScanFromRegisteredRootsJobData *job_data = job_data_untyped;
2820
2821         scan_from_registered_roots (job_data->func,
2822                         job_data->heap_start, job_data->heap_end,
2823                         job_data->root_type,
2824                         job_gray_queue (worker_data));
2825 }
2826
2827 typedef struct
2828 {
2829         char *heap_start;
2830         char *heap_end;
2831 } ScanThreadDataJobData;
2832
2833 static void
2834 job_scan_thread_data (WorkerData *worker_data, void *job_data_untyped)
2835 {
2836         ScanThreadDataJobData *job_data = job_data_untyped;
2837
2838         scan_thread_data (job_data->heap_start, job_data->heap_end, TRUE,
2839                         job_gray_queue (worker_data));
2840 }
2841
2842 /*
2843  * Collect objects in the nursery.  Returns whether to trigger a major
2844  * collection.
2845  */
2846 static gboolean
2847 collect_nursery (size_t requested_size)
2848 {
2849         gboolean needs_major;
2850         size_t max_garbage_amount;
2851         char *nursery_next;
2852         ScanFromRemsetsJobData sfrjd;
2853         ScanFromRegisteredRootsJobData scrrjd_normal, scrrjd_wbarrier;
2854         ScanThreadDataJobData stdjd;
2855         mword fragment_total;
2856         TV_DECLARE (all_atv);
2857         TV_DECLARE (all_btv);
2858         TV_DECLARE (atv);
2859         TV_DECLARE (btv);
2860
2861         if (disable_minor_collections)
2862                 return TRUE;
2863
2864         mono_perfcounters->gc_collections0++;
2865
2866         current_collection_generation = GENERATION_NURSERY;
2867
2868         binary_protocol_collection (GENERATION_NURSERY);
2869         check_scan_starts ();
2870
2871         degraded_mode = 0;
2872         objects_pinned = 0;
2873         nursery_next = mono_sgen_nursery_alloc_get_upper_alloc_bound ();
2874         /* FIXME: optimize later to use the higher address where an object can be present */
2875         nursery_next = MAX (nursery_next, nursery_end);
2876
2877         nursery_alloc_bound = nursery_next;
2878
2879         DEBUG (1, fprintf (gc_debug_file, "Start nursery collection %d %p-%p, size: %d\n", num_minor_gcs, nursery_start, nursery_next, (int)(nursery_next - nursery_start)));
2880         max_garbage_amount = nursery_next - nursery_start;
2881         g_assert (nursery_section->size >= max_garbage_amount);
2882
2883         /* world must be stopped already */
2884         TV_GETTIME (all_atv);
2885         atv = all_atv;
2886
2887         /* Pinning no longer depends on clearing all nursery fragments */
2888         mono_sgen_clear_current_nursery_fragment ();
2889
2890         TV_GETTIME (btv);
2891         time_minor_pre_collection_fragment_clear += TV_ELAPSED_MS (atv, btv);
2892
2893         if (xdomain_checks)
2894                 check_for_xdomain_refs ();
2895
2896         nursery_section->next_data = nursery_next;
2897
2898         major_collector.start_nursery_collection ();
2899
2900         try_calculate_minor_collection_allowance (FALSE);
2901
2902         gray_object_queue_init (&gray_queue);
2903         workers_init_distribute_gray_queue ();
2904
2905         num_minor_gcs++;
2906         mono_stats.minor_gc_count ++;
2907
2908         global_remset_cache_clear ();
2909
2910         process_fin_stage_entries ();
2911         process_dislink_stage_entries ();
2912
2913         /* pin from pinned handles */
2914         init_pinning ();
2915         mono_profiler_gc_event (MONO_GC_EVENT_MARK_START, 0);
2916         pin_from_roots (nursery_start, nursery_next, WORKERS_DISTRIBUTE_GRAY_QUEUE);
2917         /* identify pinned objects */
2918         optimize_pin_queue (0);
2919         next_pin_slot = pin_objects_from_addresses (nursery_section, pin_queue, pin_queue + next_pin_slot, nursery_start, nursery_next, WORKERS_DISTRIBUTE_GRAY_QUEUE);
2920         nursery_section->pin_queue_start = pin_queue;
2921         nursery_section->pin_queue_num_entries = next_pin_slot;
2922         TV_GETTIME (atv);
2923         time_minor_pinning += TV_ELAPSED_MS (btv, atv);
2924         DEBUG (2, fprintf (gc_debug_file, "Finding pinned pointers: %d in %d usecs\n", next_pin_slot, TV_ELAPSED (btv, atv)));
2925         DEBUG (4, fprintf (gc_debug_file, "Start scan with %d pinned objects\n", next_pin_slot));
2926
2927         if (consistency_check_at_minor_collection)
2928                 check_consistency ();
2929
2930         workers_start_all_workers ();
2931
2932         /*
2933          * Walk all the roots and copy the young objects to the old
2934          * generation, starting from to_space.
2935          *
2936          * The global remsets must be processed before the workers start
2937          * marking because they might add global remsets.
2938          */
2939         scan_from_global_remsets (nursery_start, nursery_next, WORKERS_DISTRIBUTE_GRAY_QUEUE);
2940
2941         workers_start_marking ();
2942
2943         sfrjd.heap_start = nursery_start;
2944         sfrjd.heap_end = nursery_next;
2945         workers_enqueue_job (job_scan_from_remsets, &sfrjd);
2946
2947         /* we don't have complete write barrier yet, so we scan all the old generation sections */
2948         TV_GETTIME (btv);
2949         time_minor_scan_remsets += TV_ELAPSED_MS (atv, btv);
2950         DEBUG (2, fprintf (gc_debug_file, "Old generation scan: %d usecs\n", TV_ELAPSED (atv, btv)));
2951
2952         if (use_cardtable) {
2953                 atv = btv;
2954                 card_tables_collect_stats (TRUE);
2955                 scan_from_card_tables (nursery_start, nursery_next, WORKERS_DISTRIBUTE_GRAY_QUEUE);
2956                 TV_GETTIME (btv);
2957                 time_minor_scan_card_table += TV_ELAPSED_MS (atv, btv);
2958         }
2959
2960         if (!major_collector.is_parallel)
2961                 drain_gray_stack (&gray_queue, -1);
2962
2963         if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
2964                 report_registered_roots ();
2965         if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
2966                 report_finalizer_roots ();
2967         TV_GETTIME (atv);
2968         time_minor_scan_pinned += TV_ELAPSED_MS (btv, atv);
2969
2970         /* registered roots, this includes static fields */
2971         scrrjd_normal.func = major_collector.copy_object;
2972         scrrjd_normal.heap_start = nursery_start;
2973         scrrjd_normal.heap_end = nursery_next;
2974         scrrjd_normal.root_type = ROOT_TYPE_NORMAL;
2975         workers_enqueue_job (job_scan_from_registered_roots, &scrrjd_normal);
2976
2977         scrrjd_wbarrier.func = major_collector.copy_object;
2978         scrrjd_wbarrier.heap_start = nursery_start;
2979         scrrjd_wbarrier.heap_end = nursery_next;
2980         scrrjd_wbarrier.root_type = ROOT_TYPE_WBARRIER;
2981         workers_enqueue_job (job_scan_from_registered_roots, &scrrjd_wbarrier);
2982
2983         TV_GETTIME (btv);
2984         time_minor_scan_registered_roots += TV_ELAPSED_MS (atv, btv);
2985
2986         /* thread data */
2987         stdjd.heap_start = nursery_start;
2988         stdjd.heap_end = nursery_next;
2989         workers_enqueue_job (job_scan_thread_data, &stdjd);
2990
2991         TV_GETTIME (atv);
2992         time_minor_scan_thread_data += TV_ELAPSED_MS (btv, atv);
2993         btv = atv;
2994
2995         if (major_collector.is_parallel) {
2996                 while (!gray_object_queue_is_empty (WORKERS_DISTRIBUTE_GRAY_QUEUE)) {
2997                         workers_distribute_gray_queue_sections ();
2998                         usleep (1000);
2999                 }
3000         }
3001         workers_join ();
3002
3003         if (major_collector.is_parallel)
3004                 g_assert (gray_object_queue_is_empty (&gray_queue));
3005
3006         finish_gray_stack (nursery_start, nursery_next, GENERATION_NURSERY, &gray_queue);
3007         TV_GETTIME (atv);
3008         time_minor_finish_gray_stack += TV_ELAPSED_MS (btv, atv);
3009         mono_profiler_gc_event (MONO_GC_EVENT_MARK_END, 0);
3010
3011         /*
3012          * The (single-threaded) finalization code might have done
3013          * some copying/marking so we can only reset the GC thread's
3014          * worker data here instead of earlier when we joined the
3015          * workers.
3016          */
3017         if (major_collector.reset_worker_data)
3018                 major_collector.reset_worker_data (workers_gc_thread_data.major_collector_data);
3019
3020         if (objects_pinned) {
3021                 evacuate_pin_staging_area ();
3022                 optimize_pin_queue (0);
3023                 nursery_section->pin_queue_start = pin_queue;
3024                 nursery_section->pin_queue_num_entries = next_pin_slot;
3025         }
3026
3027         /* walk the pin_queue, build up the fragment list of free memory, unmark
3028          * pinned objects as we go, memzero() the empty fragments so they are ready for the
3029          * next allocations.
3030          */
3031         mono_profiler_gc_event (MONO_GC_EVENT_RECLAIM_START, 0);
3032         fragment_total = mono_sgen_build_nursery_fragments (nursery_section, pin_queue, next_pin_slot);
3033         if (!fragment_total)
3034                 degraded_mode = 1;
3035
3036         /* Clear TLABs for all threads */
3037         clear_tlabs ();
3038
3039         mono_profiler_gc_event (MONO_GC_EVENT_RECLAIM_END, 0);
3040         TV_GETTIME (btv);
3041         time_minor_fragment_creation += TV_ELAPSED_MS (atv, btv);
3042         DEBUG (2, fprintf (gc_debug_file, "Fragment creation: %d usecs, %lu bytes available\n", TV_ELAPSED (atv, btv), (unsigned long)fragment_total));
3043
3044         if (consistency_check_at_minor_collection)
3045                 check_major_refs ();
3046
3047         major_collector.finish_nursery_collection ();
3048
3049         TV_GETTIME (all_btv);
3050         mono_stats.minor_gc_time_usecs += TV_ELAPSED (all_atv, all_btv);
3051
3052         if (heap_dump_file)
3053                 dump_heap ("minor", num_minor_gcs - 1, NULL);
3054
3055         /* prepare the pin queue for the next collection */
3056         last_num_pinned = next_pin_slot;
3057         next_pin_slot = 0;
3058         if (fin_ready_list || critical_fin_list) {
3059                 DEBUG (4, fprintf (gc_debug_file, "Finalizer-thread wakeup: ready %d\n", num_ready_finalizers));
3060                 mono_gc_finalize_notify ();
3061         }
3062         pin_stats_reset ();
3063
3064         g_assert (gray_object_queue_is_empty (&gray_queue));
3065
3066         if (use_cardtable)
3067                 card_tables_collect_stats (FALSE);
3068
3069         check_scan_starts ();
3070
3071         binary_protocol_flush_buffers (FALSE);
3072
3073         /*objects are late pinned because of lack of memory, so a major is a good call*/
3074         needs_major = need_major_collection (0) || objects_pinned;
3075         current_collection_generation = -1;
3076         objects_pinned = 0;
3077
3078         return needs_major;
3079 }
3080
3081 typedef struct
3082 {
3083         FinalizeEntry *list;
3084 } ScanFinalizerEntriesJobData;
3085
3086 static void
3087 job_scan_finalizer_entries (WorkerData *worker_data, void *job_data_untyped)
3088 {
3089         ScanFinalizerEntriesJobData *job_data = job_data_untyped;
3090
3091         scan_finalizer_entries (major_collector.copy_or_mark_object,
3092                         job_data->list,
3093                         job_gray_queue (worker_data));
3094 }
3095
3096 static void
3097 major_do_collection (const char *reason)
3098 {
3099         LOSObject *bigobj, *prevbo;
3100         TV_DECLARE (all_atv);
3101         TV_DECLARE (all_btv);
3102         TV_DECLARE (atv);
3103         TV_DECLARE (btv);
3104         /* FIXME: only use these values for the precise scan
3105          * note that to_space pointers should be excluded anyway...
3106          */
3107         char *heap_start = NULL;
3108         char *heap_end = (char*)-1;
3109         int old_next_pin_slot;
3110         ScanFromRegisteredRootsJobData scrrjd_normal, scrrjd_wbarrier;
3111         ScanThreadDataJobData stdjd;
3112         ScanFinalizerEntriesJobData sfejd_fin_ready, sfejd_critical_fin;
3113
3114         mono_perfcounters->gc_collections1++;
3115
3116         last_collection_old_num_major_sections = major_collector.get_num_major_sections ();
3117
3118         /*
3119          * A domain could have been freed, resulting in
3120          * los_memory_usage being less than last_collection_los_memory_usage.
3121          */
3122         last_collection_los_memory_alloced = los_memory_usage - MIN (last_collection_los_memory_usage, los_memory_usage);
3123         last_collection_old_los_memory_usage = los_memory_usage;
3124         objects_pinned = 0;
3125
3126         //count_ref_nonref_objs ();
3127         //consistency_check ();
3128
3129         binary_protocol_collection (GENERATION_OLD);
3130         check_scan_starts ();
3131         gray_object_queue_init (&gray_queue);
3132         workers_init_distribute_gray_queue ();
3133
3134         degraded_mode = 0;
3135         DEBUG (1, fprintf (gc_debug_file, "Start major collection %d\n", num_major_gcs));
3136         num_major_gcs++;
3137         mono_stats.major_gc_count ++;
3138
3139         /* world must be stopped already */
3140         TV_GETTIME (all_atv);
3141         atv = all_atv;
3142
3143         /* Pinning depends on this */
3144         mono_sgen_clear_nursery_fragments ();
3145
3146         TV_GETTIME (btv);
3147         time_major_pre_collection_fragment_clear += TV_ELAPSED_MS (atv, btv);
3148
3149         nursery_section->next_data = nursery_end;
3150         /* we should also coalesce scanning from sections close to each other
3151          * and deal with pointers outside of the sections later.
3152          */
3153
3154         if (major_collector.start_major_collection)
3155                 major_collector.start_major_collection ();
3156
3157         *major_collector.have_swept = FALSE;
3158         reset_minor_collection_allowance ();
3159
3160         if (xdomain_checks)
3161                 check_for_xdomain_refs ();
3162
3163         /* The remsets are not useful for a major collection */
3164         clear_remsets ();
3165         global_remset_cache_clear ();
3166         if (use_cardtable)
3167                 card_table_clear ();
3168
3169         process_fin_stage_entries ();
3170         process_dislink_stage_entries ();
3171
3172         TV_GETTIME (atv);
3173         init_pinning ();
3174         DEBUG (6, fprintf (gc_debug_file, "Collecting pinned addresses\n"));
3175         pin_from_roots ((void*)lowest_heap_address, (void*)highest_heap_address, WORKERS_DISTRIBUTE_GRAY_QUEUE);
3176         optimize_pin_queue (0);
3177
3178         /*
3179          * pin_queue now contains all candidate pointers, sorted and
3180          * uniqued.  We must do two passes now to figure out which
3181          * objects are pinned.
3182          *
3183          * The first is to find within the pin_queue the area for each
3184          * section.  This requires that the pin_queue be sorted.  We
3185          * also process the LOS objects and pinned chunks here.
3186          *
3187          * The second, destructive, pass is to reduce the section
3188          * areas to pointers to the actually pinned objects.
3189          */
3190         DEBUG (6, fprintf (gc_debug_file, "Pinning from sections\n"));
3191         /* first pass for the sections */
3192         mono_sgen_find_section_pin_queue_start_end (nursery_section);
3193         major_collector.find_pin_queue_start_ends (WORKERS_DISTRIBUTE_GRAY_QUEUE);
3194         /* identify possible pointers to the insize of large objects */
3195         DEBUG (6, fprintf (gc_debug_file, "Pinning from large objects\n"));
3196         for (bigobj = los_object_list; bigobj; bigobj = bigobj->next) {
3197                 int dummy;
3198                 if (mono_sgen_find_optimized_pin_queue_area (bigobj->data, (char*)bigobj->data + bigobj->size, &dummy)) {
3199                         pin_object (bigobj->data);
3200                         /* FIXME: only enqueue if object has references */
3201                         GRAY_OBJECT_ENQUEUE (WORKERS_DISTRIBUTE_GRAY_QUEUE, bigobj->data);
3202                         if (heap_dump_file)
3203                                 mono_sgen_pin_stats_register_object ((char*) bigobj->data, safe_object_get_size ((MonoObject*) bigobj->data));
3204                         DEBUG (6, fprintf (gc_debug_file, "Marked large object %p (%s) size: %lu from roots\n", bigobj->data, safe_name (bigobj->data), (unsigned long)bigobj->size));
3205                 }
3206         }
3207         /* second pass for the sections */
3208         mono_sgen_pin_objects_in_section (nursery_section, WORKERS_DISTRIBUTE_GRAY_QUEUE);
3209         major_collector.pin_objects (WORKERS_DISTRIBUTE_GRAY_QUEUE);
3210         old_next_pin_slot = next_pin_slot;
3211
3212         TV_GETTIME (btv);
3213         time_major_pinning += TV_ELAPSED_MS (atv, btv);
3214         DEBUG (2, fprintf (gc_debug_file, "Finding pinned pointers: %d in %d usecs\n", next_pin_slot, TV_ELAPSED (atv, btv)));
3215         DEBUG (4, fprintf (gc_debug_file, "Start scan with %d pinned objects\n", next_pin_slot));
3216
3217         major_collector.init_to_space ();
3218
3219 #ifdef SGEN_DEBUG_INTERNAL_ALLOC
3220         main_gc_thread = pthread_self ();
3221 #endif
3222
3223         workers_start_all_workers ();
3224         workers_start_marking ();
3225
3226         if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
3227                 report_registered_roots ();
3228         TV_GETTIME (atv);
3229         time_major_scan_pinned += TV_ELAPSED_MS (btv, atv);
3230
3231         /* registered roots, this includes static fields */
3232         scrrjd_normal.func = major_collector.copy_or_mark_object;
3233         scrrjd_normal.heap_start = heap_start;
3234         scrrjd_normal.heap_end = heap_end;
3235         scrrjd_normal.root_type = ROOT_TYPE_NORMAL;
3236         workers_enqueue_job (job_scan_from_registered_roots, &scrrjd_normal);
3237
3238         scrrjd_wbarrier.func = major_collector.copy_or_mark_object;
3239         scrrjd_wbarrier.heap_start = heap_start;
3240         scrrjd_wbarrier.heap_end = heap_end;
3241         scrrjd_wbarrier.root_type = ROOT_TYPE_WBARRIER;
3242         workers_enqueue_job (job_scan_from_registered_roots, &scrrjd_wbarrier);
3243
3244         TV_GETTIME (btv);
3245         time_major_scan_registered_roots += TV_ELAPSED_MS (atv, btv);
3246
3247         /* Threads */
3248         stdjd.heap_start = heap_start;
3249         stdjd.heap_end = heap_end;
3250         workers_enqueue_job (job_scan_thread_data, &stdjd);
3251
3252         TV_GETTIME (atv);
3253         time_major_scan_thread_data += TV_ELAPSED_MS (btv, atv);
3254
3255         TV_GETTIME (btv);
3256         time_major_scan_alloc_pinned += TV_ELAPSED_MS (atv, btv);
3257
3258         if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
3259                 report_finalizer_roots ();
3260
3261         /* scan the list of objects ready for finalization */
3262         sfejd_fin_ready.list = fin_ready_list;
3263         workers_enqueue_job (job_scan_finalizer_entries, &sfejd_fin_ready);
3264
3265         sfejd_critical_fin.list = critical_fin_list;
3266         workers_enqueue_job (job_scan_finalizer_entries, &sfejd_critical_fin);
3267
3268         TV_GETTIME (atv);
3269         time_major_scan_finalized += TV_ELAPSED_MS (btv, atv);
3270         DEBUG (2, fprintf (gc_debug_file, "Root scan: %d usecs\n", TV_ELAPSED (btv, atv)));
3271
3272         TV_GETTIME (btv);
3273         time_major_scan_big_objects += TV_ELAPSED_MS (atv, btv);
3274
3275         if (major_collector.is_parallel) {
3276                 while (!gray_object_queue_is_empty (WORKERS_DISTRIBUTE_GRAY_QUEUE)) {
3277                         workers_distribute_gray_queue_sections ();
3278                         usleep (1000);
3279                 }
3280         }
3281         workers_join ();
3282
3283 #ifdef SGEN_DEBUG_INTERNAL_ALLOC
3284         main_gc_thread = NULL;
3285 #endif
3286
3287         if (major_collector.is_parallel)
3288                 g_assert (gray_object_queue_is_empty (&gray_queue));
3289
3290         /* all the objects in the heap */
3291         finish_gray_stack (heap_start, heap_end, GENERATION_OLD, &gray_queue);
3292         TV_GETTIME (atv);
3293         time_major_finish_gray_stack += TV_ELAPSED_MS (btv, atv);
3294
3295         /*
3296          * The (single-threaded) finalization code might have done
3297          * some copying/marking so we can only reset the GC thread's
3298          * worker data here instead of earlier when we joined the
3299          * workers.
3300          */
3301         if (major_collector.reset_worker_data)
3302                 major_collector.reset_worker_data (workers_gc_thread_data.major_collector_data);
3303
3304         if (objects_pinned) {
3305                 /*This is slow, but we just OOM'd*/
3306                 mono_sgen_pin_queue_clear_discarded_entries (nursery_section, old_next_pin_slot);
3307                 evacuate_pin_staging_area ();
3308                 optimize_pin_queue (0);
3309                 mono_sgen_find_section_pin_queue_start_end (nursery_section);
3310                 objects_pinned = 0;
3311         }
3312
3313         reset_heap_boundaries ();
3314         mono_sgen_update_heap_boundaries ((mword)nursery_start, (mword)nursery_end);
3315
3316         /* sweep the big objects list */
3317         prevbo = NULL;
3318         for (bigobj = los_object_list; bigobj;) {
3319                 if (object_is_pinned (bigobj->data)) {
3320                         unpin_object (bigobj->data);
3321                         mono_sgen_update_heap_boundaries ((mword)bigobj->data, (mword)bigobj->data + bigobj->size);
3322                 } else {
3323                         LOSObject *to_free;
3324                         /* not referenced anywhere, so we can free it */
3325                         if (prevbo)
3326                                 prevbo->next = bigobj->next;
3327                         else
3328                                 los_object_list = bigobj->next;
3329                         to_free = bigobj;
3330                         bigobj = bigobj->next;
3331                         mono_sgen_los_free_object (to_free);
3332                         continue;
3333                 }
3334                 prevbo = bigobj;
3335                 bigobj = bigobj->next;
3336         }
3337
3338         TV_GETTIME (btv);
3339         time_major_free_bigobjs += TV_ELAPSED_MS (atv, btv);
3340
3341         mono_sgen_los_sweep ();
3342
3343         TV_GETTIME (atv);
3344         time_major_los_sweep += TV_ELAPSED_MS (btv, atv);
3345
3346         major_collector.sweep ();
3347
3348         TV_GETTIME (btv);
3349         time_major_sweep += TV_ELAPSED_MS (atv, btv);
3350
3351         /* walk the pin_queue, build up the fragment list of free memory, unmark
3352          * pinned objects as we go, memzero() the empty fragments so they are ready for the
3353          * next allocations.
3354          */
3355         if (!mono_sgen_build_nursery_fragments (nursery_section, nursery_section->pin_queue_start, nursery_section->pin_queue_num_entries))
3356                 degraded_mode = 1;
3357
3358         /* Clear TLABs for all threads */
3359         clear_tlabs ();
3360
3361         TV_GETTIME (atv);
3362         time_major_fragment_creation += TV_ELAPSED_MS (btv, atv);
3363
3364         TV_GETTIME (all_btv);
3365         mono_stats.major_gc_time_usecs += TV_ELAPSED (all_atv, all_btv);
3366
3367         if (heap_dump_file)
3368                 dump_heap ("major", num_major_gcs - 1, reason);
3369
3370         /* prepare the pin queue for the next collection */
3371         next_pin_slot = 0;
3372         if (fin_ready_list || critical_fin_list) {
3373                 DEBUG (4, fprintf (gc_debug_file, "Finalizer-thread wakeup: ready %d\n", num_ready_finalizers));
3374                 mono_gc_finalize_notify ();
3375         }
3376         pin_stats_reset ();
3377
3378         g_assert (gray_object_queue_is_empty (&gray_queue));
3379
3380         try_calculate_minor_collection_allowance (TRUE);
3381
3382         minor_collection_sections_alloced = 0;
3383         last_collection_los_memory_usage = los_memory_usage;
3384
3385         major_collector.finish_major_collection ();
3386
3387         check_scan_starts ();
3388
3389         binary_protocol_flush_buffers (FALSE);
3390
3391         //consistency_check ();
3392 }
3393
3394 static void
3395 major_collection (const char *reason)
3396 {
3397         if (disable_major_collections) {
3398                 collect_nursery (0);
3399                 return;
3400         }
3401
3402         current_collection_generation = GENERATION_OLD;
3403         major_do_collection (reason);
3404         current_collection_generation = -1;
3405 }
3406
3407 void
3408 sgen_collect_major_no_lock (const char *reason)
3409 {
3410          mono_profiler_gc_event (MONO_GC_EVENT_START, 1);
3411          stop_world (1);
3412          major_collection (reason);
3413          restart_world (1);
3414          mono_profiler_gc_event (MONO_GC_EVENT_END, 1);
3415 }
3416
3417 /*
3418  * When deciding if it's better to collect or to expand, keep track
3419  * of how much garbage was reclaimed with the last collection: if it's too
3420  * little, expand.
3421  * This is called when we could not allocate a small object.
3422  */
3423 static void __attribute__((noinline))
3424 minor_collect_or_expand_inner (size_t size)
3425 {
3426         int do_minor_collection = 1;
3427
3428         g_assert (nursery_section);
3429         if (do_minor_collection) {
3430                 mono_profiler_gc_event (MONO_GC_EVENT_START, 0);
3431                 stop_world (0);
3432                 if (collect_nursery (size)) {
3433                         mono_profiler_gc_event (MONO_GC_EVENT_START, 1);
3434                         major_collection ("minor overflow");
3435                         /* keep events symmetric */
3436                         mono_profiler_gc_event (MONO_GC_EVENT_END, 1);
3437                 }
3438                 DEBUG (2, fprintf (gc_debug_file, "Heap size: %lu, LOS size: %lu\n", (unsigned long)total_alloc, (unsigned long)los_memory_usage));
3439                 restart_world (0);
3440                 /* this also sets the proper pointers for the next allocation */
3441                 if (!mono_sgen_can_alloc_size (size)) {
3442                         int i;
3443                         /* TypeBuilder and MonoMethod are killing mcs with fragmentation */
3444                         DEBUG (1, fprintf (gc_debug_file, "nursery collection didn't find enough room for %zd alloc (%d pinned)\n", size, last_num_pinned));
3445                         for (i = 0; i < last_num_pinned; ++i) {
3446                                 DEBUG (3, fprintf (gc_debug_file, "Bastard pinning obj %p (%s), size: %d\n", pin_queue [i], safe_name (pin_queue [i]), safe_object_get_size (pin_queue [i])));
3447                         }
3448                         degraded_mode = 1;
3449                 }
3450                 mono_profiler_gc_event (MONO_GC_EVENT_END, 0);
3451         }
3452         //report_internal_mem_usage ();
3453 }
3454
3455 /*
3456  * ######################################################################
3457  * ########  Memory allocation from the OS
3458  * ######################################################################
3459  * This section of code deals with getting memory from the OS and
3460  * allocating memory for GC-internal data structures.
3461  * Internal memory can be handled with a freelist for small objects.
3462  */
3463
3464 /*
3465  * Debug reporting.
3466  */
3467 G_GNUC_UNUSED static void
3468 report_internal_mem_usage (void)
3469 {
3470         printf ("Internal memory usage:\n");
3471         mono_sgen_report_internal_mem_usage ();
3472         printf ("Pinned memory usage:\n");
3473         major_collector.report_pinned_memory_usage ();
3474 }
3475
3476 /*
3477  * Allocate a big chunk of memory from the OS (usually 64KB to several megabytes).
3478  * This must not require any lock.
3479  */
3480 void*
3481 mono_sgen_alloc_os_memory (size_t size, int activate)
3482 {
3483         void *ptr;
3484         unsigned long prot_flags = activate? MONO_MMAP_READ|MONO_MMAP_WRITE: MONO_MMAP_NONE;
3485
3486         prot_flags |= MONO_MMAP_PRIVATE | MONO_MMAP_ANON;
3487         size += pagesize - 1;
3488         size &= ~(pagesize - 1);
3489         ptr = mono_valloc (0, size, prot_flags);
3490         /* FIXME: CAS */
3491         total_alloc += size;
3492         return ptr;
3493 }
3494
3495 /*
3496  * Free the memory returned by mono_sgen_alloc_os_memory (), returning it to the OS.
3497  */
3498 void
3499 mono_sgen_free_os_memory (void *addr, size_t size)
3500 {
3501         mono_vfree (addr, size);
3502
3503         size += pagesize - 1;
3504         size &= ~(pagesize - 1);
3505         /* FIXME: CAS */
3506         total_alloc -= size;
3507 }
3508
3509 /*
3510  * ######################################################################
3511  * ########  Object allocation
3512  * ######################################################################
3513  * This section of code deals with allocating memory for objects.
3514  * There are several ways:
3515  * *) allocate large objects
3516  * *) allocate normal objects
3517  * *) fast lock-free allocation
3518  * *) allocation of pinned objects
3519  */
3520
3521 static void*
3522 alloc_degraded (MonoVTable *vtable, size_t size)
3523 {
3524         if (need_major_collection (0)) {
3525                 mono_profiler_gc_event (MONO_GC_EVENT_START, 1);
3526                 stop_world (1);
3527                 major_collection ("degraded overflow");
3528                 restart_world (1);
3529                 mono_profiler_gc_event (MONO_GC_EVENT_END, 1);
3530         }
3531
3532         return major_collector.alloc_degraded (vtable, size);
3533 }
3534
3535 /*
3536  * Provide a variant that takes just the vtable for small fixed-size objects.
3537  * The aligned size is already computed and stored in vt->gc_descr.
3538  * Note: every SCAN_START_SIZE or so we are given the chance to do some special
3539  * processing. We can keep track of where objects start, for example,
3540  * so when we scan the thread stacks for pinned objects, we can start
3541  * a search for the pinned object in SCAN_START_SIZE chunks.
3542  */
3543 static void*
3544 mono_gc_alloc_obj_nolock (MonoVTable *vtable, size_t size)
3545 {
3546         /* FIXME: handle OOM */
3547         void **p;
3548         char *new_next;
3549         TLAB_ACCESS_INIT;
3550
3551         HEAVY_STAT (++stat_objects_alloced);
3552         if (size <= MAX_SMALL_OBJ_SIZE)
3553                 HEAVY_STAT (stat_bytes_alloced += size);
3554         else
3555                 HEAVY_STAT (stat_bytes_alloced_los += size);
3556
3557         size = ALIGN_UP (size);
3558
3559         g_assert (vtable->gc_descr);
3560
3561         if (G_UNLIKELY (collect_before_allocs)) {
3562                 static int alloc_count;
3563
3564                 InterlockedIncrement (&alloc_count);
3565                 if (((alloc_count % collect_before_allocs) == 0) && nursery_section) {
3566                         mono_profiler_gc_event (MONO_GC_EVENT_START, 0);
3567                         stop_world (0);
3568                         collect_nursery (0);
3569                         restart_world (0);
3570                         mono_profiler_gc_event (MONO_GC_EVENT_END, 0);
3571                         if (!degraded_mode && !mono_sgen_can_alloc_size (size) && size <= MAX_SMALL_OBJ_SIZE) {
3572                                 // FIXME:
3573                                 g_assert_not_reached ();
3574                         }
3575                 }
3576         }
3577
3578         /*
3579          * We must already have the lock here instead of after the
3580          * fast path because we might be interrupted in the fast path
3581          * (after confirming that new_next < TLAB_TEMP_END) by the GC,
3582          * and we'll end up allocating an object in a fragment which
3583          * no longer belongs to us.
3584          *
3585          * The managed allocator does not do this, but it's treated
3586          * specially by the world-stopping code.
3587          */
3588
3589         if (size > MAX_SMALL_OBJ_SIZE) {
3590                 p = mono_sgen_los_alloc_large_inner (vtable, size);
3591         } else {
3592                 /* tlab_next and tlab_temp_end are TLS vars so accessing them might be expensive */
3593
3594                 p = (void**)TLAB_NEXT;
3595                 /* FIXME: handle overflow */
3596                 new_next = (char*)p + size;
3597                 TLAB_NEXT = new_next;
3598
3599                 if (G_LIKELY (new_next < TLAB_TEMP_END)) {
3600                         /* Fast path */
3601
3602                         /* 
3603                          * FIXME: We might need a memory barrier here so the change to tlab_next is 
3604                          * visible before the vtable store.
3605                          */
3606
3607                         DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
3608                         binary_protocol_alloc (p , vtable, size);
3609                         g_assert (*p == NULL);
3610                         *p = vtable;
3611
3612                         g_assert (TLAB_NEXT == new_next);
3613
3614                         return p;
3615                 }
3616
3617                 /* Slow path */
3618
3619                 /* there are two cases: the object is too big or we run out of space in the TLAB */
3620                 /* we also reach here when the thread does its first allocation after a minor 
3621                  * collection, since the tlab_ variables are initialized to NULL.
3622                  * there can be another case (from ORP), if we cooperate with the runtime a bit:
3623                  * objects that need finalizers can have the high bit set in their size
3624                  * so the above check fails and we can readily add the object to the queue.
3625                  * This avoids taking again the GC lock when registering, but this is moot when
3626                  * doing thread-local allocation, so it may not be a good idea.
3627                  */
3628                 g_assert (TLAB_NEXT == new_next);
3629                 if (TLAB_NEXT >= TLAB_REAL_END) {
3630                         /* 
3631                          * Run out of space in the TLAB. When this happens, some amount of space
3632                          * remains in the TLAB, but not enough to satisfy the current allocation
3633                          * request. Currently, we retire the TLAB in all cases, later we could
3634                          * keep it if the remaining space is above a treshold, and satisfy the
3635                          * allocation directly from the nursery.
3636                          */
3637                         TLAB_NEXT -= size;
3638                         /* when running in degraded mode, we continue allocing that way
3639                          * for a while, to decrease the number of useless nursery collections.
3640                          */
3641                         if (degraded_mode && degraded_mode < DEFAULT_NURSERY_SIZE) {
3642                                 p = alloc_degraded (vtable, size);
3643                                 binary_protocol_alloc_degraded (p, vtable, size);
3644                                 return p;
3645                         }
3646
3647                         if (size > tlab_size) {
3648                                 /* Allocate directly from the nursery */
3649                                 do {
3650                                         p = mono_sgen_nursery_alloc (size);
3651                                         if (!p) {
3652                                                 minor_collect_or_expand_inner (size);
3653                                                 if (degraded_mode) {
3654                                                         p = alloc_degraded (vtable, size);
3655                                                         binary_protocol_alloc_degraded (p, vtable, size);
3656                                                         return p;
3657                                                 } else {
3658                                                         p = mono_sgen_nursery_alloc (size);
3659                                                 }
3660                                         }
3661                                 } while (!p);
3662                                 if (!p) {
3663                                         // no space left
3664                                         g_assert (0);
3665                                 }
3666
3667                                 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
3668                                         memset (p, 0, size);
3669                                 }
3670                         } else {
3671                                 int alloc_size = 0;
3672                                 if (TLAB_START)
3673                                         DEBUG (3, fprintf (gc_debug_file, "Retire TLAB: %p-%p [%ld]\n", TLAB_START, TLAB_REAL_END, (long)(TLAB_REAL_END - TLAB_NEXT - size)));
3674
3675                                 do {
3676                                         p = mono_sgen_nursery_alloc_range (tlab_size, size, &alloc_size);
3677                                         if (!p) {
3678                                                 minor_collect_or_expand_inner (tlab_size);
3679                                                 if (degraded_mode) {
3680                                                         p = alloc_degraded (vtable, size);
3681                                                         binary_protocol_alloc_degraded (p, vtable, size);
3682                                                         return p;
3683                                                 } else {
3684                                                         p = mono_sgen_nursery_alloc_range (tlab_size, size, &alloc_size);
3685                                                 }               
3686                                         }
3687                                 } while (!p);
3688                                         
3689                                 if (!p) {
3690                                         // no space left
3691                                         g_assert (0);
3692                                 }
3693
3694                                 /* Allocate a new TLAB from the current nursery fragment */
3695                                 TLAB_START = (char*)p;
3696                                 TLAB_NEXT = TLAB_START;
3697                                 TLAB_REAL_END = TLAB_START + alloc_size;
3698                                 TLAB_TEMP_END = TLAB_START + MIN (SCAN_START_SIZE, alloc_size);
3699
3700                                 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
3701                                         memset (TLAB_START, 0, alloc_size);
3702                                 }
3703
3704                                 /* Allocate from the TLAB */
3705                                 p = (void*)TLAB_NEXT;
3706                                 TLAB_NEXT += size;
3707                                 g_assert (TLAB_NEXT <= TLAB_REAL_END);
3708
3709                                 nursery_section->scan_starts [((char*)p - (char*)nursery_section->data)/SCAN_START_SIZE] = (char*)p;
3710                         }
3711                 } else {
3712                         /* Reached tlab_temp_end */
3713
3714                         /* record the scan start so we can find pinned objects more easily */
3715                         nursery_section->scan_starts [((char*)p - (char*)nursery_section->data)/SCAN_START_SIZE] = (char*)p;
3716                         /* we just bump tlab_temp_end as well */
3717                         TLAB_TEMP_END = MIN (TLAB_REAL_END, TLAB_NEXT + SCAN_START_SIZE);
3718                         DEBUG (5, fprintf (gc_debug_file, "Expanding local alloc: %p-%p\n", TLAB_NEXT, TLAB_TEMP_END));
3719                 }
3720         }
3721
3722         if (G_LIKELY (p)) {
3723                 DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
3724                 binary_protocol_alloc (p, vtable, size);
3725                 *p = vtable;
3726         }
3727
3728         return p;
3729 }
3730
3731 static void*
3732 mono_gc_try_alloc_obj_nolock (MonoVTable *vtable, size_t size)
3733 {
3734         void **p;
3735         char *new_next;
3736         TLAB_ACCESS_INIT;
3737
3738         size = ALIGN_UP (size);
3739
3740         g_assert (vtable->gc_descr);
3741         if (size > MAX_SMALL_OBJ_SIZE)
3742                 return NULL;
3743
3744         if (G_UNLIKELY (size > tlab_size)) {
3745                 /* Allocate directly from the nursery */
3746                 p = mono_sgen_nursery_alloc (size);
3747                 if (!p)
3748                         return NULL;
3749
3750                 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION)
3751                         memset (p, 0, size);
3752                 g_assert (*p == NULL);
3753         } else {
3754                 /* tlab_next and tlab_temp_end are TLS vars so accessing them might be expensive */
3755
3756                 p = (void**)TLAB_NEXT;
3757                 /* FIXME: handle overflow */
3758                 new_next = (char*)p + size;
3759                 TLAB_NEXT = new_next;
3760
3761                 /*First case, we overflowed the tlab, get a new one*/
3762                 if (G_UNLIKELY (new_next >= TLAB_REAL_END)) {
3763                         int alloc_size = 0;
3764
3765                         new_next = mono_sgen_nursery_alloc_range (tlab_size, size, &alloc_size);
3766                         p = (void**)new_next;
3767                         if (!p)
3768                                 return NULL;
3769
3770                         g_assert (alloc_size >= size);
3771                         TLAB_START = (char*)new_next;
3772                         TLAB_NEXT = new_next + size;
3773                         TLAB_REAL_END = new_next + alloc_size;
3774                         TLAB_TEMP_END = new_next + MIN (SCAN_START_SIZE, alloc_size);
3775
3776                         if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION)
3777                                 memset (new_next, 0, alloc_size);
3778                         new_next += size;
3779                         g_assert (*p == NULL);
3780                 }
3781
3782
3783                 /* Second case, we overflowed temp end */
3784                 if (G_UNLIKELY (new_next >= TLAB_TEMP_END)) {
3785                         nursery_section->scan_starts [((char*)p - (char*)nursery_section->data)/SCAN_START_SIZE] = (char*)p;
3786                         /* we just bump tlab_temp_end as well */
3787                         TLAB_TEMP_END = MIN (TLAB_REAL_END, TLAB_NEXT + SCAN_START_SIZE);
3788                         DEBUG (5, fprintf (gc_debug_file, "Expanding local alloc: %p-%p\n", TLAB_NEXT, TLAB_TEMP_END));         
3789                 }
3790
3791                 g_assert (TLAB_NEXT == new_next);
3792         }
3793
3794         /* 
3795          * FIXME: We might need a memory barrier here so the change to tlab_next is 
3796          * visible before the vtable store.
3797          */
3798
3799         HEAVY_STAT (++stat_objects_alloced);
3800         HEAVY_STAT (stat_bytes_alloced += size);
3801
3802         DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
3803         binary_protocol_alloc (p, vtable, size);
3804         g_assert (*p == NULL);
3805         *p = vtable;
3806
3807         return p;
3808 }
3809
3810 void*
3811 mono_gc_alloc_obj (MonoVTable *vtable, size_t size)
3812 {
3813         void *res;
3814 #ifndef DISABLE_CRITICAL_REGION
3815         TLAB_ACCESS_INIT;
3816         ENTER_CRITICAL_REGION;
3817         res = mono_gc_try_alloc_obj_nolock (vtable, size);
3818         if (res) {
3819                 EXIT_CRITICAL_REGION;
3820                 return res;
3821         }
3822         EXIT_CRITICAL_REGION;
3823 #endif
3824         LOCK_GC;
3825         res = mono_gc_alloc_obj_nolock (vtable, size);
3826         UNLOCK_GC;
3827         if (G_UNLIKELY (!res))
3828                 return mono_gc_out_of_memory (size);
3829         return res;
3830 }
3831
3832 void*
3833 mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length)
3834 {
3835         MonoArray *arr;
3836 #ifndef DISABLE_CRITICAL_REGION
3837         TLAB_ACCESS_INIT;
3838         ENTER_CRITICAL_REGION;
3839         arr = mono_gc_try_alloc_obj_nolock (vtable, size);
3840         if (arr) {
3841                 arr->max_length = max_length;
3842                 EXIT_CRITICAL_REGION;
3843                 return arr;
3844         }
3845         EXIT_CRITICAL_REGION;
3846 #endif
3847
3848         LOCK_GC;
3849
3850         arr = mono_gc_alloc_obj_nolock (vtable, size);
3851         if (G_UNLIKELY (!arr)) {
3852                 UNLOCK_GC;
3853                 return mono_gc_out_of_memory (size);
3854         }
3855
3856         arr->max_length = max_length;
3857
3858         UNLOCK_GC;
3859
3860         return arr;
3861 }
3862
3863 void*
3864 mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uintptr_t bounds_size)
3865 {
3866         MonoArray *arr;
3867         MonoArrayBounds *bounds;
3868
3869         LOCK_GC;
3870
3871         arr = mono_gc_alloc_obj_nolock (vtable, size);
3872         if (G_UNLIKELY (!arr)) {
3873                 UNLOCK_GC;
3874                 return mono_gc_out_of_memory (size);
3875         }
3876
3877         arr->max_length = max_length;
3878
3879         bounds = (MonoArrayBounds*)((char*)arr + size - bounds_size);
3880         arr->bounds = bounds;
3881
3882         UNLOCK_GC;
3883
3884         return arr;
3885 }
3886
3887 void*
3888 mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len)
3889 {
3890         MonoString *str;
3891 #ifndef DISABLE_CRITICAL_REGION
3892         TLAB_ACCESS_INIT;
3893         ENTER_CRITICAL_REGION;
3894         str = mono_gc_try_alloc_obj_nolock (vtable, size);
3895         if (str) {
3896                 str->length = len;
3897                 EXIT_CRITICAL_REGION;
3898                 return str;
3899         }
3900         EXIT_CRITICAL_REGION;
3901 #endif
3902
3903         LOCK_GC;
3904
3905         str = mono_gc_alloc_obj_nolock (vtable, size);
3906         if (G_UNLIKELY (!str)) {
3907                 UNLOCK_GC;
3908                 return mono_gc_out_of_memory (size);
3909         }
3910
3911         str->length = len;
3912
3913         UNLOCK_GC;
3914
3915         return str;
3916 }
3917
3918 /*
3919  * To be used for interned strings and possibly MonoThread, reflection handles.
3920  * We may want to explicitly free these objects.
3921  */
3922 void*
3923 mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size)
3924 {
3925         void **p;
3926         size = ALIGN_UP (size);
3927         LOCK_GC;
3928
3929         if (size > MAX_SMALL_OBJ_SIZE) {
3930                 /* large objects are always pinned anyway */
3931                 p = mono_sgen_los_alloc_large_inner (vtable, size);
3932         } else {
3933                 DEBUG (9, g_assert (vtable->klass->inited));
3934                 p = major_collector.alloc_small_pinned_obj (size, SGEN_VTABLE_HAS_REFERENCES (vtable));
3935         }
3936         if (G_LIKELY (p)) {
3937                 DEBUG (6, fprintf (gc_debug_file, "Allocated pinned object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
3938                 binary_protocol_alloc_pinned (p, vtable, size);
3939                 *p = vtable;
3940         }
3941         UNLOCK_GC;
3942         return p;
3943 }
3944
3945 void*
3946 mono_gc_alloc_mature (MonoVTable *vtable)
3947 {
3948         void **res;
3949         size_t size = ALIGN_UP (vtable->klass->instance_size);
3950         LOCK_GC;
3951         res = alloc_degraded (vtable, size);
3952         *res = vtable;
3953         UNLOCK_GC;
3954         if (G_UNLIKELY (vtable->klass->has_finalize))
3955                 mono_object_register_finalizer ((MonoObject*)res);
3956
3957         return res;
3958 }
3959
3960 /*
3961  * ######################################################################
3962  * ########  Finalization support
3963  * ######################################################################
3964  */
3965
3966 /*
3967  * this is valid for the nursery: if the object has been forwarded it means it's
3968  * still refrenced from a root. If it is pinned it's still alive as well.
3969  * Return TRUE if @obj is ready to be finalized.
3970  */
3971 #define object_is_fin_ready(obj) (!object_is_pinned (obj) && !object_is_forwarded (obj))
3972
3973 static gboolean
3974 is_critical_finalizer (FinalizeEntry *entry)
3975 {
3976         MonoObject *obj;
3977         MonoClass *class;
3978
3979         if (!mono_defaults.critical_finalizer_object)
3980                 return FALSE;
3981
3982         obj = entry->object;
3983         class = ((MonoVTable*)LOAD_VTABLE (obj))->klass;
3984
3985         return mono_class_has_parent (class, mono_defaults.critical_finalizer_object);
3986 }
3987
3988 static void
3989 queue_finalization_entry (FinalizeEntry *entry) {
3990         if (is_critical_finalizer (entry)) {
3991                 entry->next = critical_fin_list;
3992                 critical_fin_list = entry;
3993         } else {
3994                 entry->next = fin_ready_list;
3995                 fin_ready_list = entry;
3996         }
3997 }
3998
3999 static int
4000 object_is_reachable (char *object, char *start, char *end)
4001 {
4002         /*This happens for non nursery objects during minor collections. We just treat all objects as alive.*/
4003         if (object < start || object >= end)
4004                 return TRUE;
4005         return !object_is_fin_ready (object) || major_collector.is_object_live (object);
4006 }
4007
4008 #include "sgen-fin-weak-hash.c"
4009
4010 gboolean
4011 mono_sgen_object_is_live (void *obj)
4012 {
4013         if (ptr_in_nursery (obj))
4014                 return object_is_pinned (obj);
4015         if (current_collection_generation == GENERATION_NURSERY)
4016                 return FALSE;
4017         return major_collector.is_object_live (obj);
4018 }
4019
4020 /* LOCKING: requires that the GC lock is held */
4021 static void
4022 null_ephemerons_for_domain (MonoDomain *domain)
4023 {
4024         EphemeronLinkNode *current = ephemeron_list, *prev = NULL;
4025
4026         while (current) {
4027                 MonoObject *object = (MonoObject*)current->array;
4028
4029                 if (object && !object->vtable) {
4030                         EphemeronLinkNode *tmp = current;
4031
4032                         if (prev)
4033                                 prev->next = current->next;
4034                         else
4035                                 ephemeron_list = current->next;
4036
4037                         current = current->next;
4038                         mono_sgen_free_internal (tmp, INTERNAL_MEM_EPHEMERON_LINK);
4039                 } else {
4040                         prev = current;
4041                         current = current->next;
4042                 }
4043         }
4044 }
4045
4046 /* LOCKING: requires that the GC lock is held */
4047 static void
4048 clear_unreachable_ephemerons (CopyOrMarkObjectFunc copy_func, char *start, char *end, GrayQueue *queue)
4049 {
4050         int was_in_nursery, was_promoted;
4051         EphemeronLinkNode *current = ephemeron_list, *prev = NULL;
4052         MonoArray *array;
4053         Ephemeron *cur, *array_end;
4054         char *tombstone;
4055
4056         while (current) {
4057                 char *object = current->array;
4058
4059                 if (!object_is_reachable (object, start, end)) {
4060                         EphemeronLinkNode *tmp = current;
4061
4062                         DEBUG (5, fprintf (gc_debug_file, "Dead Ephemeron array at %p\n", object));
4063
4064                         if (prev)
4065                                 prev->next = current->next;
4066                         else
4067                                 ephemeron_list = current->next;
4068
4069                         current = current->next;
4070                         mono_sgen_free_internal (tmp, INTERNAL_MEM_EPHEMERON_LINK);
4071
4072                         continue;
4073                 }
4074
4075                 was_in_nursery = ptr_in_nursery (object);
4076                 copy_func ((void**)&object, queue);
4077                 current->array = object;
4078
4079                 /*The array was promoted, add global remsets for key/values left behind in nursery.*/
4080                 was_promoted = was_in_nursery && !ptr_in_nursery (object);
4081
4082                 DEBUG (5, fprintf (gc_debug_file, "Clearing unreachable entries for ephemeron array at %p\n", object));
4083
4084                 array = (MonoArray*)object;
4085                 cur = mono_array_addr (array, Ephemeron, 0);
4086                 array_end = cur + mono_array_length_fast (array);
4087                 tombstone = (char*)((MonoVTable*)LOAD_VTABLE (object))->domain->ephemeron_tombstone;
4088
4089                 for (; cur < array_end; ++cur) {
4090                         char *key = (char*)cur->key;
4091
4092                         if (!key || key == tombstone)
4093                                 continue;
4094
4095                         DEBUG (5, fprintf (gc_debug_file, "[%td] key %p (%s) value %p (%s)\n", cur - mono_array_addr (array, Ephemeron, 0),
4096                                 key, object_is_reachable (key, start, end) ? "reachable" : "unreachable",
4097                                 cur->value, cur->value && object_is_reachable (cur->value, start, end) ? "reachable" : "unreachable"));
4098
4099                         if (!object_is_reachable (key, start, end)) {
4100                                 cur->key = tombstone;
4101                                 cur->value = NULL;
4102                                 continue;
4103                         }
4104
4105                         if (was_promoted) {
4106                                 if (ptr_in_nursery (key)) {/*key was not promoted*/
4107                                         DEBUG (5, fprintf (gc_debug_file, "\tAdded remset to key %p\n", key));
4108                                         mono_sgen_add_to_global_remset (&cur->key);
4109                                 }
4110                                 if (ptr_in_nursery (cur->value)) {/*value was not promoted*/
4111                                         DEBUG (5, fprintf (gc_debug_file, "\tAdded remset to value %p\n", cur->value));
4112                                         mono_sgen_add_to_global_remset (&cur->value);
4113                                 }
4114                         }
4115                 }
4116                 prev = current;
4117                 current = current->next;
4118         }
4119 }
4120
4121 /* LOCKING: requires that the GC lock is held */
4122 static int
4123 mark_ephemerons_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, GrayQueue *queue)
4124 {
4125         int nothing_marked = 1;
4126         EphemeronLinkNode *current = ephemeron_list;
4127         MonoArray *array;
4128         Ephemeron *cur, *array_end;
4129         char *tombstone;
4130
4131         for (current = ephemeron_list; current; current = current->next) {
4132                 char *object = current->array;
4133                 DEBUG (5, fprintf (gc_debug_file, "Ephemeron array at %p\n", object));
4134
4135                 /*We ignore arrays in old gen during minor collections since all objects are promoted by the remset machinery.*/
4136                 if (object < start || object >= end)
4137                         continue;
4138
4139                 /*It has to be alive*/
4140                 if (!object_is_reachable (object, start, end)) {
4141                         DEBUG (5, fprintf (gc_debug_file, "\tnot reachable\n"));
4142                         continue;
4143                 }
4144
4145                 copy_func ((void**)&object, queue);
4146
4147                 array = (MonoArray*)object;
4148                 cur = mono_array_addr (array, Ephemeron, 0);
4149                 array_end = cur + mono_array_length_fast (array);
4150                 tombstone = (char*)((MonoVTable*)LOAD_VTABLE (object))->domain->ephemeron_tombstone;
4151
4152                 for (; cur < array_end; ++cur) {
4153                         char *key = cur->key;
4154
4155                         if (!key || key == tombstone)
4156                                 continue;
4157
4158                         DEBUG (5, fprintf (gc_debug_file, "[%td] key %p (%s) value %p (%s)\n", cur - mono_array_addr (array, Ephemeron, 0),
4159                                 key, object_is_reachable (key, start, end) ? "reachable" : "unreachable",
4160                                 cur->value, cur->value && object_is_reachable (cur->value, start, end) ? "reachable" : "unreachable"));
4161
4162                         if (object_is_reachable (key, start, end)) {
4163                                 char *value = cur->value;
4164
4165                                 copy_func ((void**)&cur->key, queue);
4166                                 if (value) {
4167                                         if (!object_is_reachable (value, start, end))
4168                                                 nothing_marked = 0;
4169                                         copy_func ((void**)&cur->value, queue);
4170                                 }
4171                         }
4172                 }
4173         }
4174
4175         DEBUG (5, fprintf (gc_debug_file, "Ephemeron run finished. Is it done %d\n", nothing_marked));
4176         return nothing_marked;
4177 }
4178
4179 int
4180 mono_gc_invoke_finalizers (void)
4181 {
4182         FinalizeEntry *entry = NULL;
4183         gboolean entry_is_critical = FALSE;
4184         int count = 0;
4185         void *obj;
4186         /* FIXME: batch to reduce lock contention */
4187         while (fin_ready_list || critical_fin_list) {
4188                 LOCK_GC;
4189
4190                 if (entry) {
4191                         FinalizeEntry **list = entry_is_critical ? &critical_fin_list : &fin_ready_list;
4192
4193                         /* We have finalized entry in the last
4194                            interation, now we need to remove it from
4195                            the list. */
4196                         if (*list == entry)
4197                                 *list = entry->next;
4198                         else {
4199                                 FinalizeEntry *e = *list;
4200                                 while (e->next != entry)
4201                                         e = e->next;
4202                                 e->next = entry->next;
4203                         }
4204                         mono_sgen_free_internal (entry, INTERNAL_MEM_FINALIZE_ENTRY);
4205                         entry = NULL;
4206                 }
4207
4208                 /* Now look for the first non-null entry. */
4209                 for (entry = fin_ready_list; entry && !entry->object; entry = entry->next)
4210                         ;
4211                 if (entry) {
4212                         entry_is_critical = FALSE;
4213                 } else {
4214                         entry_is_critical = TRUE;
4215                         for (entry = critical_fin_list; entry && !entry->object; entry = entry->next)
4216                                 ;
4217                 }
4218
4219                 if (entry) {
4220                         g_assert (entry->object);
4221                         num_ready_finalizers--;
4222                         obj = entry->object;
4223                         entry->object = NULL;
4224                         DEBUG (7, fprintf (gc_debug_file, "Finalizing object %p (%s)\n", obj, safe_name (obj)));
4225                 }
4226
4227                 UNLOCK_GC;
4228
4229                 if (!entry)
4230                         break;
4231
4232                 g_assert (entry->object == NULL);
4233                 count++;
4234                 /* the object is on the stack so it is pinned */
4235                 /*g_print ("Calling finalizer for object: %p (%s)\n", entry->object, safe_name (entry->object));*/
4236                 mono_gc_run_finalize (obj, NULL);
4237         }
4238         g_assert (!entry);
4239         return count;
4240 }
4241
4242 gboolean
4243 mono_gc_pending_finalizers (void)
4244 {
4245         return fin_ready_list || critical_fin_list;
4246 }
4247
4248 /* Negative value to remove */
4249 void
4250 mono_gc_add_memory_pressure (gint64 value)
4251 {
4252         /* FIXME: Use interlocked functions */
4253         LOCK_GC;
4254         memory_pressure += value;
4255         UNLOCK_GC;
4256 }
4257
4258 void
4259 mono_sgen_register_major_sections_alloced (int num_sections)
4260 {
4261         minor_collection_sections_alloced += num_sections;
4262 }
4263
4264 mword
4265 mono_sgen_get_minor_collection_allowance (void)
4266 {
4267         return minor_collection_allowance;
4268 }
4269
4270 /*
4271  * ######################################################################
4272  * ########  registered roots support
4273  * ######################################################################
4274  */
4275
4276 static void
4277 rehash_roots (gboolean pinned)
4278 {
4279         int i;
4280         unsigned int hash;
4281         RootRecord **new_hash;
4282         RootRecord *entry, *next;
4283         int new_size;
4284
4285         new_size = g_spaced_primes_closest (num_roots_entries [pinned]);
4286         new_hash = mono_sgen_alloc_internal_dynamic (new_size * sizeof (RootRecord*), INTERNAL_MEM_ROOTS_TABLE);
4287         for (i = 0; i < roots_hash_size [pinned]; ++i) {
4288                 for (entry = roots_hash [pinned][i]; entry; entry = next) {
4289                         hash = mono_aligned_addr_hash (entry->start_root) % new_size;
4290                         next = entry->next;
4291                         entry->next = new_hash [hash];
4292                         new_hash [hash] = entry;
4293                 }
4294         }
4295         mono_sgen_free_internal_dynamic (roots_hash [pinned], roots_hash_size [pinned] * sizeof (RootRecord*), INTERNAL_MEM_ROOTS_TABLE);
4296         roots_hash [pinned] = new_hash;
4297         roots_hash_size [pinned] = new_size;
4298 }
4299
4300 static RootRecord*
4301 find_root (int root_type, char *start, guint32 addr_hash)
4302 {
4303         RootRecord *new_root;
4304
4305         guint32 hash = addr_hash % roots_hash_size [root_type];
4306         for (new_root = roots_hash [root_type][hash]; new_root; new_root = new_root->next) {
4307                 /* we allow changing the size and the descriptor (for thread statics etc) */
4308                 if (new_root->start_root == start) {
4309                         return new_root;
4310                 }
4311         }
4312
4313         return NULL;
4314 }
4315
4316 /*
4317  * We do not coalesce roots.
4318  */
4319 static int
4320 mono_gc_register_root_inner (char *start, size_t size, void *descr, int root_type)
4321 {
4322         RootRecord *new_root;
4323         unsigned int hash, addr_hash = mono_aligned_addr_hash (start);
4324         int i;
4325         LOCK_GC;
4326         for (i = 0; i < ROOT_TYPE_NUM; ++i) {
4327                 if (num_roots_entries [i] >= roots_hash_size [i] * 2)
4328                         rehash_roots (i);
4329         }
4330         for (i = 0; i < ROOT_TYPE_NUM; ++i) {
4331                 new_root = find_root (i, start, addr_hash);
4332                 /* we allow changing the size and the descriptor (for thread statics etc) */
4333                 if (new_root) {
4334                         size_t old_size = new_root->end_root - new_root->start_root;
4335                         new_root->end_root = new_root->start_root + size;
4336                         g_assert (((new_root->root_desc != 0) && (descr != NULL)) ||
4337                                           ((new_root->root_desc == 0) && (descr == NULL)));
4338                         new_root->root_desc = (mword)descr;
4339                         roots_size += size;
4340                         roots_size -= old_size;
4341                         UNLOCK_GC;
4342                         return TRUE;
4343                 }
4344         }
4345         new_root = mono_sgen_alloc_internal (INTERNAL_MEM_ROOT_RECORD);
4346         if (new_root) {
4347                 new_root->start_root = start;
4348                 new_root->end_root = new_root->start_root + size;
4349                 new_root->root_desc = (mword)descr;
4350                 roots_size += size;
4351                 hash = addr_hash % roots_hash_size [root_type];
4352                 num_roots_entries [root_type]++;
4353                 new_root->next = roots_hash [root_type] [hash];
4354                 roots_hash [root_type][hash] = new_root;
4355                 DEBUG (3, fprintf (gc_debug_file, "Added root %p for range: %p-%p, descr: %p  (%d/%d bytes)\n", new_root, new_root->start_root, new_root->end_root, descr, (int)size, (int)roots_size));
4356         } else {
4357                 UNLOCK_GC;
4358                 return FALSE;
4359         }
4360         UNLOCK_GC;
4361         return TRUE;
4362 }
4363
4364 int
4365 mono_gc_register_root (char *start, size_t size, void *descr)
4366 {
4367         return mono_gc_register_root_inner (start, size, descr, descr ? ROOT_TYPE_NORMAL : ROOT_TYPE_PINNED);
4368 }
4369
4370 int
4371 mono_gc_register_root_wbarrier (char *start, size_t size, void *descr)
4372 {
4373         return mono_gc_register_root_inner (start, size, descr, ROOT_TYPE_WBARRIER);
4374 }
4375
4376 void
4377 mono_gc_deregister_root (char* addr)
4378 {
4379         RootRecord *tmp, *prev;
4380         unsigned int hash, addr_hash = mono_aligned_addr_hash (addr);
4381         int root_type;
4382
4383         LOCK_GC;
4384         for (root_type = 0; root_type < ROOT_TYPE_NUM; ++root_type) {
4385                 hash = addr_hash % roots_hash_size [root_type];
4386                 tmp = roots_hash [root_type][hash];
4387                 prev = NULL;
4388                 while (tmp) {
4389                         if (tmp->start_root == (char*)addr) {
4390                                 if (prev)
4391                                         prev->next = tmp->next;
4392                                 else
4393                                         roots_hash [root_type][hash] = tmp->next;
4394                                 roots_size -= (tmp->end_root - tmp->start_root);
4395                                 num_roots_entries [root_type]--;
4396                                 DEBUG (3, fprintf (gc_debug_file, "Removed root %p for range: %p-%p\n", tmp, tmp->start_root, tmp->end_root));
4397                                 mono_sgen_free_internal (tmp, INTERNAL_MEM_ROOT_RECORD);
4398                                 break;
4399                         }
4400                         prev = tmp;
4401                         tmp = tmp->next;
4402                 }
4403         }
4404         UNLOCK_GC;
4405 }
4406
4407 /*
4408  * ######################################################################
4409  * ########  Thread handling (stop/start code)
4410  * ######################################################################
4411  */
4412
4413 #if USE_SIGNAL_BASED_START_STOP_WORLD
4414
4415 static MonoSemType suspend_ack_semaphore;
4416 static MonoSemType *suspend_ack_semaphore_ptr;
4417 static unsigned int global_stop_count = 0;
4418
4419 static sigset_t suspend_signal_mask;
4420
4421 #ifdef USE_MONO_CTX
4422 static MonoContext cur_thread_ctx = {0};
4423 #else
4424 static mword cur_thread_regs [ARCH_NUM_REGS] = {0};
4425 #endif
4426
4427 static void
4428 update_current_thread_stack (void *start)
4429 {
4430         int stack_guard = 0;
4431 #ifndef USE_MONO_CTX
4432         void *ptr = cur_thread_regs;
4433 #endif
4434         SgenThreadInfo *info = mono_thread_info_current ();
4435         
4436         info->stack_start = align_pointer (&stack_guard);
4437         g_assert (info->stack_start >= info->stack_start_limit && info->stack_start < info->stack_end);
4438 #ifdef USE_MONO_CTX
4439         MONO_CONTEXT_GET_CURRENT (cur_thread_ctx);
4440         info->monoctx = &cur_thread_ctx;
4441 #else
4442         ARCH_STORE_REGS (ptr);
4443         info->stopped_regs = ptr;
4444 #endif
4445         if (gc_callbacks.thread_suspend_func)
4446                 gc_callbacks.thread_suspend_func (info->runtime_data, NULL);
4447 }
4448
4449 /*
4450  * Define this and use the "xdomain-checks" MONO_GC_DEBUG option to
4451  * have cross-domain checks in the write barrier.
4452  */
4453 //#define XDOMAIN_CHECKS_IN_WBARRIER
4454
4455 #ifndef SGEN_BINARY_PROTOCOL
4456 #ifndef HEAVY_STATISTICS
4457 #define MANAGED_ALLOCATION
4458 #ifndef XDOMAIN_CHECKS_IN_WBARRIER
4459 #define MANAGED_WBARRIER
4460 #endif
4461 #endif
4462 #endif
4463
4464 static gboolean
4465 is_ip_in_managed_allocator (MonoDomain *domain, gpointer ip);
4466
4467 void
4468 mono_sgen_wait_for_suspend_ack (int count)
4469 {
4470 #if defined(__MACH__) && MONO_MACH_ARCH_SUPPORTED
4471                 /* mach thread_resume is synchronous so we dont need to wait for them */
4472 #else
4473         int i, result;
4474
4475         for (i = 0; i < count; ++i) {
4476                 while ((result = MONO_SEM_WAIT (suspend_ack_semaphore_ptr)) != 0) {
4477                         if (errno != EINTR) {
4478                                 g_error ("sem_wait ()");
4479                         }
4480                 }
4481         }
4482 #endif
4483 }
4484
4485 static int
4486 restart_threads_until_none_in_managed_allocator (void)
4487 {
4488         SgenThreadInfo *info;
4489         int num_threads_died = 0;
4490         int sleep_duration = -1;
4491
4492         for (;;) {
4493                 int restart_count = 0, restarted_count = 0;
4494                 /* restart all threads that stopped in the
4495                    allocator */
4496                 FOREACH_THREAD_SAFE (info) {
4497                         gboolean result;
4498                         if (info->skip)
4499                                 continue;
4500                         if (!info->thread_is_dying && (!info->stack_start || info->in_critical_region ||
4501                                         is_ip_in_managed_allocator (info->stopped_domain, info->stopped_ip))) {
4502                                 binary_protocol_thread_restart ((gpointer)mono_thread_info_get_tid (info));
4503                                 result = mono_sgen_resume_thread (info);
4504                                 if (result) {
4505                                         ++restart_count;
4506                                 } else {
4507                                         info->skip = 1;
4508                                 }
4509                         } else {
4510                                 /* we set the stopped_ip to
4511                                    NULL for threads which
4512                                    we're not restarting so
4513                                    that we can easily identify
4514                                    the others */
4515                                 info->stopped_ip = NULL;
4516                                 info->stopped_domain = NULL;
4517                         }
4518                 } END_FOREACH_THREAD_SAFE
4519                 /* if no threads were restarted, we're done */
4520                 if (restart_count == 0)
4521                         break;
4522
4523                 /* wait for the threads to signal their restart */
4524                 mono_sgen_wait_for_suspend_ack (restart_count);
4525
4526                 if (sleep_duration < 0) {
4527                         sched_yield ();
4528                         sleep_duration = 0;
4529                 } else {
4530                         g_usleep (sleep_duration);
4531                         sleep_duration += 10;
4532                 }
4533
4534                 /* stop them again */
4535                 FOREACH_THREAD (info) {
4536                         gboolean result;
4537                         if (info->skip || info->stopped_ip == NULL)
4538                                 continue;
4539                         result = mono_sgen_suspend_thread (info);
4540
4541                         if (result) {
4542                                 ++restarted_count;
4543                         } else {
4544                                 info->skip = 1;
4545                         }
4546                 } END_FOREACH_THREAD
4547                 /* some threads might have died */
4548                 num_threads_died += restart_count - restarted_count;
4549                 /* wait for the threads to signal their suspension
4550                    again */
4551                 mono_sgen_wait_for_suspend_ack (restart_count);
4552         }
4553
4554         return num_threads_died;
4555 }
4556
4557 static void
4558 suspend_thread (SgenThreadInfo *info, void *context)
4559 {
4560         int stop_count;
4561 #ifdef USE_MONO_CTX
4562         MonoContext monoctx;
4563 #else
4564         gpointer regs [ARCH_NUM_REGS];
4565 #endif
4566         gpointer stack_start;
4567
4568         g_assert (info->doing_handshake);
4569
4570         info->stopped_domain = mono_domain_get ();
4571         info->stopped_ip = context ? (gpointer) ARCH_SIGCTX_IP (context) : NULL;
4572         stop_count = global_stop_count;
4573         /* duplicate signal */
4574         if (0 && info->stop_count == stop_count)
4575                 return;
4576 #ifdef HAVE_KW_THREAD
4577         /* update the remset info in the thread data structure */
4578         info->remset = remembered_set;
4579 #endif
4580         stack_start = context ? (char*) ARCH_SIGCTX_SP (context) - REDZONE_SIZE : NULL;
4581         /* If stack_start is not within the limits, then don't set it
4582            in info and we will be restarted. */
4583         if (stack_start >= info->stack_start_limit && info->stack_start <= info->stack_end) {
4584                 info->stack_start = stack_start;
4585
4586 #ifdef USE_MONO_CTX
4587                 if (context) {
4588                         mono_sigctx_to_monoctx (context, &monoctx);
4589                         info->monoctx = &monoctx;
4590                 } else {
4591                         info->monoctx = NULL;
4592                 }
4593 #else
4594                 if (context) {
4595                         ARCH_COPY_SIGCTX_REGS (regs, context);
4596                         info->stopped_regs = regs;
4597                 } else {
4598                         info->stopped_regs = NULL;
4599                 }
4600 #endif
4601         } else {
4602                 g_assert (!info->stack_start);
4603         }
4604
4605         /* Notify the JIT */
4606         if (gc_callbacks.thread_suspend_func)
4607                 gc_callbacks.thread_suspend_func (info->runtime_data, context);
4608
4609         DEBUG (4, fprintf (gc_debug_file, "Posting suspend_ack_semaphore for suspend from %p %p\n", info, (gpointer)mono_native_thread_id_get ()));
4610         /* notify the waiting thread */
4611         MONO_SEM_POST (suspend_ack_semaphore_ptr);
4612         info->stop_count = stop_count;
4613
4614         /* wait until we receive the restart signal */
4615         do {
4616                 info->signal = 0;
4617                 sigsuspend (&suspend_signal_mask);
4618         } while (info->signal != restart_signal_num && info->doing_handshake);
4619
4620         DEBUG (4, fprintf (gc_debug_file, "Posting suspend_ack_semaphore for resume from %p %p\n", info, (gpointer)mono_native_thread_id_get ()));
4621         /* notify the waiting thread */
4622         MONO_SEM_POST (suspend_ack_semaphore_ptr);
4623 }
4624
4625 /* LOCKING: assumes the GC lock is held (by the stopping thread) */
4626 static void
4627 suspend_handler (int sig, siginfo_t *siginfo, void *context)
4628 {
4629         SgenThreadInfo *info;
4630         int old_errno = errno;
4631
4632         info = mono_thread_info_current ();
4633
4634         if (info) {
4635                 suspend_thread (info, context);
4636         } else {
4637                 /* This can happen while a thread is dying */
4638                 g_print ("no thread info in suspend\n");
4639         }
4640
4641         errno = old_errno;
4642 }
4643
4644 static void
4645 restart_handler (int sig)
4646 {
4647         SgenThreadInfo *info;
4648         int old_errno = errno;
4649
4650         info = mono_thread_info_current ();
4651
4652         /*
4653          * If a thread is dying there might be no thread info.  In
4654          * that case we rely on info->doing_handshake.
4655          */
4656         if (info) {
4657                 info->signal = restart_signal_num;
4658                 DEBUG (4, fprintf (gc_debug_file, "Restart handler in %p %p\n", info, (gpointer)mono_native_thread_id_get ()));
4659         }
4660
4661         errno = old_errno;
4662 }
4663
4664 static void
4665 acquire_gc_locks (void)
4666 {
4667         LOCK_INTERRUPTION;
4668         mono_thread_info_suspend_lock ();
4669 }
4670
4671 static void
4672 release_gc_locks (void)
4673 {
4674         mono_thread_info_suspend_unlock ();
4675         UNLOCK_INTERRUPTION;
4676 }
4677
4678 static TV_DECLARE (stop_world_time);
4679 static unsigned long max_pause_usec = 0;
4680
4681 /* LOCKING: assumes the GC lock is held */
4682 static int
4683 stop_world (int generation)
4684 {
4685         int count;
4686
4687         mono_profiler_gc_event (MONO_GC_EVENT_PRE_STOP_WORLD, generation);
4688         acquire_gc_locks ();
4689
4690         update_current_thread_stack (&count);
4691
4692         global_stop_count++;
4693         DEBUG (3, fprintf (gc_debug_file, "stopping world n %d from %p %p\n", global_stop_count, mono_thread_info_current (), (gpointer)mono_native_thread_id_get ()));
4694         TV_GETTIME (stop_world_time);
4695         count = mono_sgen_thread_handshake (suspend_signal_num);
4696         count -= restart_threads_until_none_in_managed_allocator ();
4697         g_assert (count >= 0);
4698         DEBUG (3, fprintf (gc_debug_file, "world stopped %d thread(s)\n", count));
4699         mono_profiler_gc_event (MONO_GC_EVENT_POST_STOP_WORLD, generation);
4700         return count;
4701 }
4702
4703 /* LOCKING: assumes the GC lock is held */
4704 static int
4705 restart_world (int generation)
4706 {
4707         int count;
4708         SgenThreadInfo *info;
4709         TV_DECLARE (end_sw);
4710         unsigned long usec;
4711
4712         /* notify the profiler of the leftovers */
4713         if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_GC_MOVES)) {
4714                 if (moved_objects_idx) {
4715                         mono_profiler_gc_moves (moved_objects, moved_objects_idx);
4716                         moved_objects_idx = 0;
4717                 }
4718         }
4719         mono_profiler_gc_event (MONO_GC_EVENT_PRE_START_WORLD, generation);
4720         FOREACH_THREAD (info) {
4721                 info->stack_start = NULL;
4722 #ifdef USE_MONO_CTX
4723                 info->monoctx = NULL;
4724 #else
4725                 info->stopped_regs = NULL;
4726 #endif
4727         } END_FOREACH_THREAD
4728
4729         release_gc_locks ();
4730
4731         count = mono_sgen_thread_handshake (restart_signal_num);
4732         TV_GETTIME (end_sw);
4733         usec = TV_ELAPSED (stop_world_time, end_sw);
4734         max_pause_usec = MAX (usec, max_pause_usec);
4735         DEBUG (2, fprintf (gc_debug_file, "restarted %d thread(s) (pause time: %d usec, max: %d)\n", count, (int)usec, (int)max_pause_usec));
4736         mono_profiler_gc_event (MONO_GC_EVENT_POST_START_WORLD, generation);
4737         return count;
4738 }
4739
4740 #endif /* USE_SIGNAL_BASED_START_STOP_WORLD */
4741
4742 int
4743 mono_sgen_get_current_collection_generation (void)
4744 {
4745         return current_collection_generation;
4746 }
4747
4748 void
4749 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
4750 {
4751         gc_callbacks = *callbacks;
4752 }
4753
4754 MonoGCCallbacks *
4755 mono_gc_get_gc_callbacks ()
4756 {
4757         return &gc_callbacks;
4758 }
4759
4760 /* Variables holding start/end nursery so it won't have to be passed at every call */
4761 static void *scan_area_arg_start, *scan_area_arg_end;
4762
4763 void
4764 mono_gc_conservatively_scan_area (void *start, void *end)
4765 {
4766         conservatively_pin_objects_from (start, end, scan_area_arg_start, scan_area_arg_end, PIN_TYPE_STACK);
4767 }
4768
4769 void*
4770 mono_gc_scan_object (void *obj)
4771 {
4772         UserCopyOrMarkData *data = pthread_getspecific (user_copy_or_mark_key);
4773
4774         if (current_collection_generation == GENERATION_NURSERY)
4775                 major_collector.copy_object (&obj, data->queue);
4776         else
4777                 major_collector.copy_or_mark_object (&obj, data->queue);
4778         return obj;
4779 }
4780
4781 /*
4782  * Mark from thread stacks and registers.
4783  */
4784 static void
4785 scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise, GrayQueue *queue)
4786 {
4787         SgenThreadInfo *info;
4788
4789         scan_area_arg_start = start_nursery;
4790         scan_area_arg_end = end_nursery;
4791
4792         FOREACH_THREAD (info) {
4793                 if (info->skip) {
4794                         DEBUG (3, fprintf (gc_debug_file, "Skipping dead thread %p, range: %p-%p, size: %td\n", info, info->stack_start, info->stack_end, (char*)info->stack_end - (char*)info->stack_start));
4795                         continue;
4796                 }
4797                 DEBUG (3, fprintf (gc_debug_file, "Scanning thread %p, range: %p-%p, size: %td, pinned=%d\n", info, info->stack_start, info->stack_end, (char*)info->stack_end - (char*)info->stack_start, next_pin_slot));
4798                 if (gc_callbacks.thread_mark_func && !conservative_stack_mark) {
4799                         UserCopyOrMarkData data = { NULL, queue };
4800                         set_user_copy_or_mark_data (&data);
4801                         gc_callbacks.thread_mark_func (info->runtime_data, info->stack_start, info->stack_end, precise);
4802                         set_user_copy_or_mark_data (NULL);
4803                 } else if (!precise) {
4804                         if (!info->thread_is_dying)
4805                                 conservatively_pin_objects_from (info->stack_start, info->stack_end, start_nursery, end_nursery, PIN_TYPE_STACK);
4806                 }
4807
4808 #ifdef USE_MONO_CTX
4809                 if (!info->thread_is_dying && !precise)
4810                         conservatively_pin_objects_from ((void**)info->monoctx, (void**)info->monoctx + ARCH_NUM_REGS,
4811                                 start_nursery, end_nursery, PIN_TYPE_STACK);
4812 #else
4813                 if (!info->thread_is_dying && !precise)
4814                         conservatively_pin_objects_from (info->stopped_regs, info->stopped_regs + ARCH_NUM_REGS,
4815                                         start_nursery, end_nursery, PIN_TYPE_STACK);
4816 #endif
4817         } END_FOREACH_THREAD
4818 }
4819
4820 static void
4821 find_pinning_ref_from_thread (char *obj, size_t size)
4822 {
4823         int j;
4824         SgenThreadInfo *info;
4825         char *endobj = obj + size;
4826
4827         FOREACH_THREAD (info) {
4828                 char **start = (char**)info->stack_start;
4829                 if (info->skip)
4830                         continue;
4831                 while (start < (char**)info->stack_end) {
4832                         if (*start >= obj && *start < endobj) {
4833                                 DEBUG (0, fprintf (gc_debug_file, "Object %p referenced in thread %p (id %p) at %p, stack: %p-%p\n", obj, info, (gpointer)mono_thread_info_get_tid (info), start, info->stack_start, info->stack_end));
4834                         }
4835                         start++;
4836                 }
4837
4838                 for (j = 0; j < ARCH_NUM_REGS; ++j) {
4839 #ifdef USE_MONO_CTX
4840                         mword w = ((mword*)info->monoctx) [j];
4841 #else
4842                         mword w = (mword)info->stopped_regs [j];
4843 #endif
4844
4845                         if (w >= (mword)obj && w < (mword)obj + size)
4846                                 DEBUG (0, fprintf (gc_debug_file, "Object %p referenced in saved reg %d of thread %p (id %p)\n", obj, j, info, (gpointer)mono_thread_info_get_tid (info)));
4847                 } END_FOREACH_THREAD
4848         }
4849 }
4850
4851 static gboolean
4852 ptr_on_stack (void *ptr)
4853 {
4854         gpointer stack_start = &stack_start;
4855         SgenThreadInfo *info = mono_thread_info_current ();
4856
4857         if (ptr >= stack_start && ptr < (gpointer)info->stack_end)
4858                 return TRUE;
4859         return FALSE;
4860 }
4861
4862 static mword*
4863 handle_remset (mword *p, void *start_nursery, void *end_nursery, gboolean global, GrayQueue *queue)
4864 {
4865         void **ptr;
4866         mword count;
4867         mword desc;
4868
4869         if (global)
4870                 HEAVY_STAT (++stat_global_remsets_processed);
4871         else
4872                 HEAVY_STAT (++stat_local_remsets_processed);
4873
4874         /* FIXME: exclude stack locations */
4875         switch ((*p) & REMSET_TYPE_MASK) {
4876         case REMSET_LOCATION:
4877                 ptr = (void**)(*p);
4878                 //__builtin_prefetch (ptr);
4879                 if (((void*)ptr < start_nursery || (void*)ptr >= end_nursery)) {
4880                         gpointer old = *ptr;
4881                         major_collector.copy_object (ptr, queue);
4882                         DEBUG (9, fprintf (gc_debug_file, "Overwrote remset at %p with %p\n", ptr, *ptr));
4883                         if (old)
4884                                 binary_protocol_ptr_update (ptr, old, *ptr, (gpointer)LOAD_VTABLE (*ptr), safe_object_get_size (*ptr));
4885                         if (!global && *ptr >= start_nursery && *ptr < end_nursery) {
4886                                 /*
4887                                  * If the object is pinned, each reference to it from nonpinned objects
4888                                  * becomes part of the global remset, which can grow very large.
4889                                  */
4890                                 DEBUG (9, fprintf (gc_debug_file, "Add to global remset because of pinning %p (%p %s)\n", ptr, *ptr, safe_name (*ptr)));
4891                                 mono_sgen_add_to_global_remset (ptr);
4892                         }
4893                 } else {
4894                         DEBUG (9, fprintf (gc_debug_file, "Skipping remset at %p holding %p\n", ptr, *ptr));
4895                 }
4896                 return p + 1;
4897         case REMSET_RANGE:
4898                 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
4899                 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
4900                         return p + 2;
4901                 count = p [1];
4902                 while (count-- > 0) {
4903                         major_collector.copy_object (ptr, queue);
4904                         DEBUG (9, fprintf (gc_debug_file, "Overwrote remset at %p with %p (count: %d)\n", ptr, *ptr, (int)count));
4905                         if (!global && *ptr >= start_nursery && *ptr < end_nursery)
4906                                 mono_sgen_add_to_global_remset (ptr);
4907                         ++ptr;
4908                 }
4909                 return p + 2;
4910         case REMSET_OBJECT:
4911                 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
4912                 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
4913                         return p + 1;
4914                 major_collector.minor_scan_object ((char*)ptr, queue);
4915                 return p + 1;
4916         case REMSET_VTYPE: {
4917                 size_t skip_size;
4918
4919                 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
4920                 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
4921                         return p + 4;
4922                 desc = p [1];
4923                 count = p [2];
4924                 skip_size = p [3];
4925                 while (count-- > 0) {
4926                         major_collector.minor_scan_vtype ((char*)ptr, desc, queue);
4927                         ptr = (void**)((char*)ptr + skip_size);
4928                 }
4929                 return p + 4;
4930         }
4931         default:
4932                 g_assert_not_reached ();
4933         }
4934         return NULL;
4935 }
4936
4937 #ifdef HEAVY_STATISTICS
4938 static mword*
4939 collect_store_remsets (RememberedSet *remset, mword *bumper)
4940 {
4941         mword *p = remset->data;
4942         mword last = 0;
4943         mword last1 = 0;
4944         mword last2 = 0;
4945
4946         while (p < remset->store_next) {
4947                 switch ((*p) & REMSET_TYPE_MASK) {
4948                 case REMSET_LOCATION:
4949                         *bumper++ = *p;
4950                         if (*p == last)
4951                                 ++stat_saved_remsets_1;
4952                         last = *p;
4953                         if (*p == last1 || *p == last2) {
4954                                 ++stat_saved_remsets_2;
4955                         } else {
4956                                 last2 = last1;
4957                                 last1 = *p;
4958                         }
4959                         p += 1;
4960                         break;
4961                 case REMSET_RANGE:
4962                         p += 2;
4963                         break;
4964                 case REMSET_OBJECT:
4965                         p += 1;
4966                         break;
4967                 case REMSET_VTYPE:
4968                         p += 4;
4969                         break;
4970                 default:
4971                         g_assert_not_reached ();
4972                 }
4973         }
4974
4975         return bumper;
4976 }
4977
4978 static void
4979 remset_stats (void)
4980 {
4981         RememberedSet *remset;
4982         int size = 0;
4983         SgenThreadInfo *info;
4984         int i;
4985         mword *addresses, *bumper, *p, *r;
4986
4987         FOREACH_THREAD (info) {
4988                 for (remset = info->remset; remset; remset = remset->next)
4989                         size += remset->store_next - remset->data;
4990         } END_FOREACH_THREAD
4991         for (remset = freed_thread_remsets; remset; remset = remset->next)
4992                 size += remset->store_next - remset->data;
4993         for (remset = global_remset; remset; remset = remset->next)
4994                 size += remset->store_next - remset->data;
4995
4996         bumper = addresses = mono_sgen_alloc_internal_dynamic (sizeof (mword) * size, INTERNAL_MEM_STATISTICS);
4997
4998         FOREACH_THREAD (info) {
4999                 for (remset = info->remset; remset; remset = remset->next)
5000                         bumper = collect_store_remsets (remset, bumper);
5001         } END_FOREACH_THREAD
5002         for (remset = global_remset; remset; remset = remset->next)
5003                 bumper = collect_store_remsets (remset, bumper);
5004         for (remset = freed_thread_remsets; remset; remset = remset->next)
5005                 bumper = collect_store_remsets (remset, bumper);
5006
5007         g_assert (bumper <= addresses + size);
5008
5009         stat_store_remsets += bumper - addresses;
5010
5011         sort_addresses ((void**)addresses, bumper - addresses);
5012         p = addresses;
5013         r = addresses + 1;
5014         while (r < bumper) {
5015                 if (*r != *p)
5016                         *++p = *r;
5017                 ++r;
5018         }
5019
5020         stat_store_remsets_unique += p - addresses;
5021
5022         mono_sgen_free_internal_dynamic (addresses, sizeof (mword) * size, INTERNAL_MEM_STATISTICS);
5023 }
5024 #endif
5025
5026 static void
5027 clear_thread_store_remset_buffer (SgenThreadInfo *info)
5028 {
5029         *info->store_remset_buffer_index_addr = 0;
5030         /* See the comment at the end of sgen_thread_unregister() */
5031         if (*info->store_remset_buffer_addr)
5032                 memset (*info->store_remset_buffer_addr, 0, sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
5033 }
5034
5035 static size_t
5036 remset_byte_size (RememberedSet *remset)
5037 {
5038         return sizeof (RememberedSet) + (remset->end_set - remset->data) * sizeof (gpointer);
5039 }
5040
5041 static void
5042 scan_from_global_remsets (void *start_nursery, void *end_nursery, GrayQueue *queue)
5043 {
5044         RememberedSet *remset;
5045         mword *p, *next_p, *store_pos;
5046
5047         /* the global one */
5048         for (remset = global_remset; remset; remset = remset->next) {
5049                 DEBUG (4, fprintf (gc_debug_file, "Scanning global remset range: %p-%p, size: %td\n", remset->data, remset->store_next, remset->store_next - remset->data));
5050                 store_pos = remset->data;
5051                 for (p = remset->data; p < remset->store_next; p = next_p) {
5052                         void **ptr = (void**)p [0];
5053
5054                         /*Ignore previously processed remset.*/
5055                         if (!global_remset_location_was_not_added (ptr)) {
5056                                 next_p = p + 1;
5057                                 continue;
5058                         }
5059
5060                         next_p = handle_remset (p, start_nursery, end_nursery, TRUE, queue);
5061
5062                         /* 
5063                          * Clear global remsets of locations which no longer point to the 
5064                          * nursery. Otherwise, they could grow indefinitely between major 
5065                          * collections.
5066                          *
5067                          * Since all global remsets are location remsets, we don't need to unmask the pointer.
5068                          */
5069                         if (ptr_in_nursery (*ptr)) {
5070                                 *store_pos ++ = p [0];
5071                                 HEAVY_STAT (++stat_global_remsets_readded);
5072                         }
5073                 }
5074
5075                 /* Truncate the remset */
5076                 remset->store_next = store_pos;
5077         }
5078 }
5079
5080 static void
5081 scan_from_remsets (void *start_nursery, void *end_nursery, GrayQueue *queue)
5082 {
5083         int i;
5084         SgenThreadInfo *info;
5085         RememberedSet *remset;
5086         GenericStoreRememberedSet *store_remset;
5087         mword *p;
5088
5089 #ifdef HEAVY_STATISTICS
5090         remset_stats ();
5091 #endif
5092
5093         /* the generic store ones */
5094         store_remset = generic_store_remsets;
5095         while (store_remset) {
5096                 GenericStoreRememberedSet *next = store_remset->next;
5097
5098                 for (i = 0; i < STORE_REMSET_BUFFER_SIZE - 1; ++i) {
5099                         gpointer addr = store_remset->data [i];
5100                         if (addr)
5101                                 handle_remset ((mword*)&addr, start_nursery, end_nursery, FALSE, queue);
5102                 }
5103
5104                 mono_sgen_free_internal (store_remset, INTERNAL_MEM_STORE_REMSET);
5105
5106                 store_remset = next;
5107         }
5108         generic_store_remsets = NULL;
5109
5110         /* the per-thread ones */
5111         FOREACH_THREAD (info) {
5112                 RememberedSet *next;
5113                 int j;
5114                 for (remset = info->remset; remset; remset = next) {
5115                         DEBUG (4, fprintf (gc_debug_file, "Scanning remset for thread %p, range: %p-%p, size: %td\n", info, remset->data, remset->store_next, remset->store_next - remset->data));
5116                         for (p = remset->data; p < remset->store_next;)
5117                                 p = handle_remset (p, start_nursery, end_nursery, FALSE, queue);
5118                         remset->store_next = remset->data;
5119                         next = remset->next;
5120                         remset->next = NULL;
5121                         if (remset != info->remset) {
5122                                 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5123                                 mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
5124                         }
5125                 }
5126                 for (j = 0; j < *info->store_remset_buffer_index_addr; ++j)
5127                         handle_remset ((mword*)*info->store_remset_buffer_addr + j + 1, start_nursery, end_nursery, FALSE, queue);
5128                 clear_thread_store_remset_buffer (info);
5129         } END_FOREACH_THREAD
5130
5131         /* the freed thread ones */
5132         while (freed_thread_remsets) {
5133                 RememberedSet *next;
5134                 remset = freed_thread_remsets;
5135                 DEBUG (4, fprintf (gc_debug_file, "Scanning remset for freed thread, range: %p-%p, size: %td\n", remset->data, remset->store_next, remset->store_next - remset->data));
5136                 for (p = remset->data; p < remset->store_next;)
5137                         p = handle_remset (p, start_nursery, end_nursery, FALSE, queue);
5138                 next = remset->next;
5139                 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5140                 mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
5141                 freed_thread_remsets = next;
5142         }
5143 }
5144
5145 /*
5146  * Clear the info in the remembered sets: we're doing a major collection, so
5147  * the per-thread ones are not needed and the global ones will be reconstructed
5148  * during the copy.
5149  */
5150 static void
5151 clear_remsets (void)
5152 {
5153         SgenThreadInfo *info;
5154         RememberedSet *remset, *next;
5155
5156         /* the global list */
5157         for (remset = global_remset; remset; remset = next) {
5158                 remset->store_next = remset->data;
5159                 next = remset->next;
5160                 remset->next = NULL;
5161                 if (remset != global_remset) {
5162                         DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5163                         mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
5164                 }
5165         }
5166         /* the generic store ones */
5167         while (generic_store_remsets) {
5168                 GenericStoreRememberedSet *gs_next = generic_store_remsets->next;
5169                 mono_sgen_free_internal (generic_store_remsets, INTERNAL_MEM_STORE_REMSET);
5170                 generic_store_remsets = gs_next;
5171         }
5172         /* the per-thread ones */
5173         FOREACH_THREAD (info) {
5174                 for (remset = info->remset; remset; remset = next) {
5175                         remset->store_next = remset->data;
5176                         next = remset->next;
5177                         remset->next = NULL;
5178                         if (remset != info->remset) {
5179                                 DEBUG (3, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5180                                 mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
5181                         }
5182                 }
5183                 clear_thread_store_remset_buffer (info);
5184         } END_FOREACH_THREAD
5185
5186         /* the freed thread ones */
5187         while (freed_thread_remsets) {
5188                 next = freed_thread_remsets->next;
5189                 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", freed_thread_remsets->data));
5190                 mono_sgen_free_internal_dynamic (freed_thread_remsets, remset_byte_size (freed_thread_remsets), INTERNAL_MEM_REMSET);
5191                 freed_thread_remsets = next;
5192         }
5193 }
5194
5195 /*
5196  * Clear the thread local TLAB variables for all threads.
5197  */
5198 static void
5199 clear_tlabs (void)
5200 {
5201         SgenThreadInfo *info;
5202
5203         FOREACH_THREAD (info) {
5204                 /* A new TLAB will be allocated when the thread does its first allocation */
5205                 *info->tlab_start_addr = NULL;
5206                 *info->tlab_next_addr = NULL;
5207                 *info->tlab_temp_end_addr = NULL;
5208                 *info->tlab_real_end_addr = NULL;
5209         } END_FOREACH_THREAD
5210 }
5211
5212 static void*
5213 sgen_thread_register (SgenThreadInfo* info, void *addr)
5214 {
5215 #ifndef HAVE_KW_THREAD
5216         SgenThreadInfo *__thread_info__ = info;
5217 #endif
5218
5219         LOCK_GC;
5220 #ifndef HAVE_KW_THREAD
5221         info->tlab_start = info->tlab_next = info->tlab_temp_end = info->tlab_real_end = NULL;
5222
5223         g_assert (!pthread_getspecific (thread_info_key));
5224         pthread_setspecific (thread_info_key, info);
5225 #else
5226         thread_info = info;
5227 #endif
5228
5229         info->stop_count = -1;
5230         info->skip = 0;
5231         info->signal = 0;
5232         info->doing_handshake = FALSE;
5233         info->thread_is_dying = FALSE;
5234         info->stack_start = NULL;
5235         info->tlab_start_addr = &TLAB_START;
5236         info->tlab_next_addr = &TLAB_NEXT;
5237         info->tlab_temp_end_addr = &TLAB_TEMP_END;
5238         info->tlab_real_end_addr = &TLAB_REAL_END;
5239         info->store_remset_buffer_addr = &STORE_REMSET_BUFFER;
5240         info->store_remset_buffer_index_addr = &STORE_REMSET_BUFFER_INDEX;
5241         info->stopped_ip = NULL;
5242         info->stopped_domain = NULL;
5243 #ifdef USE_MONO_CTX
5244         info->monoctx = NULL;
5245 #else
5246         info->stopped_regs = NULL;
5247 #endif
5248
5249         binary_protocol_thread_register ((gpointer)mono_thread_info_get_tid (info));
5250
5251 #ifdef HAVE_KW_THREAD
5252         tlab_next_addr = &tlab_next;
5253         store_remset_buffer_index_addr = &store_remset_buffer_index;
5254 #endif
5255
5256 #if defined(__MACH__)
5257         info->mach_port = mach_thread_self ();
5258 #endif
5259
5260         /* try to get it with attributes first */
5261 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
5262         {
5263                 size_t size;
5264                 void *sstart;
5265                 pthread_attr_t attr;
5266                 pthread_getattr_np (pthread_self (), &attr);
5267                 pthread_attr_getstack (&attr, &sstart, &size);
5268                 info->stack_start_limit = sstart;
5269                 info->stack_end = (char*)sstart + size;
5270                 pthread_attr_destroy (&attr);
5271         }
5272 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
5273                  info->stack_end = (char*)pthread_get_stackaddr_np (pthread_self ());
5274                  info->stack_start_limit = (char*)info->stack_end - pthread_get_stacksize_np (pthread_self ());
5275 #else
5276         {
5277                 /* FIXME: we assume the stack grows down */
5278                 gsize stack_bottom = (gsize)addr;
5279                 stack_bottom += 4095;
5280                 stack_bottom &= ~4095;
5281                 info->stack_end = (char*)stack_bottom;
5282         }
5283 #endif
5284
5285 #ifdef HAVE_KW_THREAD
5286         stack_end = info->stack_end;
5287 #endif
5288
5289         info->remset = alloc_remset (DEFAULT_REMSET_SIZE, info, FALSE);
5290         pthread_setspecific (remembered_set_key, info->remset);
5291 #ifdef HAVE_KW_THREAD
5292         remembered_set = info->remset;
5293 #endif
5294
5295         STORE_REMSET_BUFFER = mono_sgen_alloc_internal (INTERNAL_MEM_STORE_REMSET);
5296         STORE_REMSET_BUFFER_INDEX = 0;
5297
5298         DEBUG (3, fprintf (gc_debug_file, "registered thread %p (%p)\n", info, (gpointer)mono_thread_info_get_tid (info)));
5299
5300         if (gc_callbacks.thread_attach_func)
5301                 info->runtime_data = gc_callbacks.thread_attach_func ();
5302
5303         UNLOCK_GC;
5304         return info;
5305 }
5306
5307 static void
5308 add_generic_store_remset_from_buffer (gpointer *buffer)
5309 {
5310         GenericStoreRememberedSet *remset = mono_sgen_alloc_internal (INTERNAL_MEM_STORE_REMSET);
5311         memcpy (remset->data, buffer + 1, sizeof (gpointer) * (STORE_REMSET_BUFFER_SIZE - 1));
5312         remset->next = generic_store_remsets;
5313         generic_store_remsets = remset;
5314 }
5315
5316 static void
5317 sgen_thread_unregister (SgenThreadInfo *p)
5318 {
5319         RememberedSet *rset;
5320
5321         /* If a delegate is passed to native code and invoked on a thread we dont
5322          * know about, the jit will register it with mono_jit_thread_attach, but
5323          * we have no way of knowing when that thread goes away.  SGen has a TSD
5324          * so we assume that if the domain is still registered, we can detach
5325          * the thread
5326          */
5327         if (mono_domain_get ())
5328                 mono_thread_detach (mono_thread_current ());
5329
5330         p->thread_is_dying = TRUE;
5331
5332         /*
5333         There is a race condition between a thread finishing executing and been removed
5334         from the GC thread set.
5335         This happens on posix systems when TLS data is been cleaned-up, libpthread will
5336         set the thread_info slot to NULL before calling the cleanup function. This
5337         opens a window in which the thread is registered but has a NULL TLS.
5338
5339         The suspend signal handler needs TLS data to know where to store thread state
5340         data or otherwise it will simply ignore the thread.
5341
5342         This solution works because the thread doing STW will wait until all threads been
5343         suspended handshake back, so there is no race between the doing_hankshake test
5344         and the suspend_thread call.
5345
5346         This is not required on systems that do synchronous STW as those can deal with
5347         the above race at suspend time.
5348
5349         FIXME: I believe we could avoid this by using mono_thread_info_lookup when
5350         mono_thread_info_current returns NULL. Or fix mono_thread_info_lookup to do so.
5351         */
5352 #if defined(__MACH__) && MONO_MACH_ARCH_SUPPORTED
5353         LOCK_GC;
5354 #else
5355         while (!TRYLOCK_GC) {
5356                 if (p->doing_handshake)
5357                         suspend_thread (p, NULL);
5358                 else
5359                         usleep (50);
5360         }
5361 #endif
5362
5363         binary_protocol_thread_unregister ((gpointer)id);
5364         DEBUG (3, fprintf (gc_debug_file, "unregister thread %p (%p)\n", p, (gpointer)mono_thread_info_get_tid (p)));
5365
5366 #if defined(__MACH__)
5367         mach_port_deallocate (current_task (), p->mach_port);
5368 #endif
5369
5370         if (gc_callbacks.thread_detach_func) {
5371                 gc_callbacks.thread_detach_func (p->runtime_data);
5372                 p->runtime_data = NULL;
5373         }
5374
5375         if (p->remset) {
5376                 if (freed_thread_remsets) {
5377                         for (rset = p->remset; rset->next; rset = rset->next)
5378                                 ;
5379                         rset->next = freed_thread_remsets;
5380                         freed_thread_remsets = p->remset;
5381                 } else {
5382                         freed_thread_remsets = p->remset;
5383                 }
5384         }
5385         if (*p->store_remset_buffer_index_addr)
5386                 add_generic_store_remset_from_buffer (*p->store_remset_buffer_addr);
5387         mono_sgen_free_internal (*p->store_remset_buffer_addr, INTERNAL_MEM_STORE_REMSET);
5388         /*
5389          * This is currently not strictly required, but we do it
5390          * anyway in case we change thread unregistering:
5391
5392          * If the thread is removed from the thread list after
5393          * unregistering (this is currently not the case), and a
5394          * collection occurs, clear_remsets() would want to memset
5395          * this buffer, which would either clobber memory or crash.
5396          */
5397         *p->store_remset_buffer_addr = NULL;
5398
5399         mono_threads_unregister_current_thread (p);
5400         UNLOCK_GC;
5401 }
5402
5403
5404 static void
5405 sgen_thread_attach (SgenThreadInfo *info)
5406 {
5407         LOCK_GC;
5408         /*this is odd, can we get attached before the gc is inited?*/
5409         init_stats ();
5410         UNLOCK_GC;
5411         
5412         if (gc_callbacks.thread_attach_func && !info->runtime_data)
5413                 info->runtime_data = gc_callbacks.thread_attach_func ();
5414
5415         /* Need a better place to initialize this */
5416         if (!array_fill_vtable && mono_get_root_domain ()) {
5417                 array_fill_vtable = mono_class_vtable (mono_get_root_domain (), mono_array_class_get (mono_defaults.byte_class, 1));
5418         }
5419         
5420 }
5421 gboolean
5422 mono_gc_register_thread (void *baseptr)
5423 {
5424         return mono_thread_info_attach (baseptr) != NULL;
5425 }
5426
5427 /*
5428  * mono_gc_set_stack_end:
5429  *
5430  *   Set the end of the current threads stack to STACK_END. The stack space between 
5431  * STACK_END and the real end of the threads stack will not be scanned during collections.
5432  */
5433 void
5434 mono_gc_set_stack_end (void *stack_end)
5435 {
5436         SgenThreadInfo *info;
5437
5438         LOCK_GC;
5439         info = mono_thread_info_current ();
5440         if (info) {
5441                 g_assert (stack_end < info->stack_end);
5442                 info->stack_end = stack_end;
5443         }
5444         UNLOCK_GC;
5445 }
5446
5447 #if USE_PTHREAD_INTERCEPT
5448
5449
5450 int
5451 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
5452 {
5453         return mono_threads_pthread_create (new_thread, attr, start_routine, arg);
5454 }
5455
5456 int
5457 mono_gc_pthread_join (pthread_t thread, void **retval)
5458 {
5459         return pthread_join (thread, retval);
5460 }
5461
5462 int
5463 mono_gc_pthread_detach (pthread_t thread)
5464 {
5465         return pthread_detach (thread);
5466 }
5467
5468 #endif /* USE_PTHREAD_INTERCEPT */
5469
5470 /*
5471  * ######################################################################
5472  * ########  Write barriers
5473  * ######################################################################
5474  */
5475
5476 /*
5477  * This causes the compile to extend the liveness of 'v' till the call to dummy_use
5478  */
5479 static void
5480 dummy_use (gpointer v) {
5481         __asm__ volatile ("" : "=r"(v) : "r"(v));
5482 }
5483
5484
5485 static RememberedSet*
5486 alloc_remset (int size, gpointer id, gboolean global)
5487 {
5488         RememberedSet* res = mono_sgen_alloc_internal_dynamic (sizeof (RememberedSet) + (size * sizeof (gpointer)), INTERNAL_MEM_REMSET);
5489         res->store_next = res->data;
5490         res->end_set = res->data + size;
5491         res->next = NULL;
5492         DEBUG (4, fprintf (gc_debug_file, "Allocated%s remset size %d at %p for %p\n", global ? " global" : "", size, res->data, id));
5493         return res;
5494 }
5495
5496 /*
5497  * Note: the write barriers first do the needed GC work and then do the actual store:
5498  * this way the value is visible to the conservative GC scan after the write barrier
5499  * itself. If a GC interrupts the barrier in the middle, value will be kept alive by
5500  * the conservative scan, otherwise by the remembered set scan.
5501  */
5502 void
5503 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
5504 {
5505         HEAVY_STAT (++stat_wbarrier_set_field);
5506         if (ptr_in_nursery (field_ptr)) {
5507                 *(void**)field_ptr = value;
5508                 return;
5509         }
5510         DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", field_ptr));
5511         if (use_cardtable) {
5512                 *(void**)field_ptr = value;
5513                 if (ptr_in_nursery (value))
5514                         sgen_card_table_mark_address ((mword)field_ptr);
5515                 dummy_use (value);
5516         } else {
5517                 RememberedSet *rs;
5518                 TLAB_ACCESS_INIT;
5519
5520                 LOCK_GC;
5521                 rs = REMEMBERED_SET;
5522                 if (rs->store_next < rs->end_set) {
5523                         *(rs->store_next++) = (mword)field_ptr;
5524                         *(void**)field_ptr = value;
5525                         UNLOCK_GC;
5526                         return;
5527                 }
5528                 rs = alloc_remset (rs->end_set - rs->data, (void*)1, FALSE);
5529                 rs->next = REMEMBERED_SET;
5530                 REMEMBERED_SET = rs;
5531 #ifdef HAVE_KW_THREAD
5532                 mono_thread_info_current ()->remset = rs;
5533 #endif
5534                 *(rs->store_next++) = (mword)field_ptr;
5535                 *(void**)field_ptr = value;
5536                 UNLOCK_GC;
5537         }
5538 }
5539
5540 void
5541 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
5542 {
5543         HEAVY_STAT (++stat_wbarrier_set_arrayref);
5544         if (ptr_in_nursery (slot_ptr)) {
5545                 *(void**)slot_ptr = value;
5546                 return;
5547         }
5548         DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", slot_ptr));
5549         if (use_cardtable) {
5550                 *(void**)slot_ptr = value;
5551                 if (ptr_in_nursery (value))
5552                         sgen_card_table_mark_address ((mword)slot_ptr);
5553                 dummy_use (value);
5554         } else {
5555                 RememberedSet *rs;
5556                 TLAB_ACCESS_INIT;
5557
5558                 LOCK_GC;
5559                 rs = REMEMBERED_SET;
5560                 if (rs->store_next < rs->end_set) {
5561                         *(rs->store_next++) = (mword)slot_ptr;
5562                         *(void**)slot_ptr = value;
5563                         UNLOCK_GC;
5564                         return;
5565                 }
5566                 rs = alloc_remset (rs->end_set - rs->data, (void*)1, FALSE);
5567                 rs->next = REMEMBERED_SET;
5568                 REMEMBERED_SET = rs;
5569 #ifdef HAVE_KW_THREAD
5570                 mono_thread_info_current ()->remset = rs;
5571 #endif
5572                 *(rs->store_next++) = (mword)slot_ptr;
5573                 *(void**)slot_ptr = value;
5574                 UNLOCK_GC;
5575         }
5576 }
5577
5578 void
5579 mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
5580 {
5581         HEAVY_STAT (++stat_wbarrier_arrayref_copy);
5582         /*This check can be done without taking a lock since dest_ptr array is pinned*/
5583         if (ptr_in_nursery (dest_ptr) || count <= 0) {
5584                 mono_gc_memmove (dest_ptr, src_ptr, count * sizeof (gpointer));
5585                 return;
5586         }
5587
5588         if (use_cardtable) {
5589                 gpointer *dest = dest_ptr;
5590                 gpointer *src = src_ptr;
5591
5592                 /*overlapping that required backward copying*/
5593                 if (src < dest && (src + count) > dest) {
5594                         gpointer *start = dest;
5595                         dest += count - 1;
5596                         src += count - 1;
5597
5598                         for (; dest >= start; --src, --dest) {
5599                                 gpointer value = *src;
5600                                 *dest = value;
5601                                 if (ptr_in_nursery (value))
5602                                         sgen_card_table_mark_address ((mword)dest);
5603                                 dummy_use (value);
5604                         }
5605                 } else {
5606                         gpointer *end = dest + count;
5607                         for (; dest < end; ++src, ++dest) {
5608                                 gpointer value = *src;
5609                                 *dest = value;
5610                                 if (ptr_in_nursery (value))
5611                                         sgen_card_table_mark_address ((mword)dest);
5612                                 dummy_use (value);
5613                         }
5614                 }
5615         } else {
5616                 RememberedSet *rs;
5617                 TLAB_ACCESS_INIT;
5618                 LOCK_GC;
5619                 mono_gc_memmove (dest_ptr, src_ptr, count * sizeof (gpointer));
5620
5621                 rs = REMEMBERED_SET;
5622                 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p, %d\n", dest_ptr, count));
5623                 if (rs->store_next + 1 < rs->end_set) {
5624                         *(rs->store_next++) = (mword)dest_ptr | REMSET_RANGE;
5625                         *(rs->store_next++) = count;
5626                         UNLOCK_GC;
5627                         return;
5628                 }
5629                 rs = alloc_remset (rs->end_set - rs->data, (void*)1, FALSE);
5630                 rs->next = REMEMBERED_SET;
5631                 REMEMBERED_SET = rs;
5632 #ifdef HAVE_KW_THREAD
5633                 mono_thread_info_current ()->remset = rs;
5634 #endif
5635                 *(rs->store_next++) = (mword)dest_ptr | REMSET_RANGE;
5636                 *(rs->store_next++) = count;
5637
5638                 UNLOCK_GC;
5639         }
5640 }
5641
5642 static char *found_obj;
5643
5644 static void
5645 find_object_for_ptr_callback (char *obj, size_t size, void *user_data)
5646 {
5647         char *ptr = user_data;
5648
5649         if (ptr >= obj && ptr < obj + size) {
5650                 g_assert (!found_obj);
5651                 found_obj = obj;
5652         }
5653 }
5654
5655 /* for use in the debugger */
5656 char* find_object_for_ptr (char *ptr);
5657 char*
5658 find_object_for_ptr (char *ptr)
5659 {
5660         if (ptr >= nursery_section->data && ptr < nursery_section->end_data) {
5661                 found_obj = NULL;
5662                 mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
5663                                 find_object_for_ptr_callback, ptr, TRUE);
5664                 if (found_obj)
5665                         return found_obj;
5666         }
5667
5668         found_obj = NULL;
5669         mono_sgen_los_iterate_objects (find_object_for_ptr_callback, ptr);
5670         if (found_obj)
5671                 return found_obj;
5672
5673         /*
5674          * Very inefficient, but this is debugging code, supposed to
5675          * be called from gdb, so we don't care.
5676          */
5677         found_obj = NULL;
5678         major_collector.iterate_objects (TRUE, TRUE, find_object_for_ptr_callback, ptr);
5679         return found_obj;
5680 }
5681
5682 static void
5683 evacuate_remset_buffer (void)
5684 {
5685         gpointer *buffer;
5686         TLAB_ACCESS_INIT;
5687
5688         buffer = STORE_REMSET_BUFFER;
5689
5690         add_generic_store_remset_from_buffer (buffer);
5691         memset (buffer, 0, sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
5692
5693         STORE_REMSET_BUFFER_INDEX = 0;
5694 }
5695
5696 void
5697 mono_gc_wbarrier_generic_nostore (gpointer ptr)
5698 {
5699         gpointer *buffer;
5700         int index;
5701         TLAB_ACCESS_INIT;
5702
5703         HEAVY_STAT (++stat_wbarrier_generic_store);
5704
5705 #ifdef XDOMAIN_CHECKS_IN_WBARRIER
5706         /* FIXME: ptr_in_heap must be called with the GC lock held */
5707         if (xdomain_checks && *(MonoObject**)ptr && ptr_in_heap (ptr)) {
5708                 char *start = find_object_for_ptr (ptr);
5709                 MonoObject *value = *(MonoObject**)ptr;
5710                 LOCK_GC;
5711                 g_assert (start);
5712                 if (start) {
5713                         MonoObject *obj = (MonoObject*)start;
5714                         if (obj->vtable->domain != value->vtable->domain)
5715                                 g_assert (is_xdomain_ref_allowed (ptr, start, obj->vtable->domain));
5716                 }
5717                 UNLOCK_GC;
5718         }
5719 #endif
5720
5721         if (*(gpointer*)ptr)
5722                 binary_protocol_wbarrier (ptr, *(gpointer*)ptr, (gpointer)LOAD_VTABLE (*(gpointer*)ptr));
5723
5724         if (ptr_in_nursery (ptr) || ptr_on_stack (ptr) || !ptr_in_nursery (*(gpointer*)ptr)) {
5725                 DEBUG (8, fprintf (gc_debug_file, "Skipping remset at %p\n", ptr));
5726                 return;
5727         }
5728
5729         if (use_cardtable) {
5730                 if (ptr_in_nursery(*(gpointer*)ptr))
5731                         sgen_card_table_mark_address ((mword)ptr);
5732                 return;
5733         }
5734
5735         LOCK_GC;
5736
5737         buffer = STORE_REMSET_BUFFER;
5738         index = STORE_REMSET_BUFFER_INDEX;
5739         /* This simple optimization eliminates a sizable portion of
5740            entries.  Comparing it to the last but one entry as well
5741            doesn't eliminate significantly more entries. */
5742         if (buffer [index] == ptr) {
5743                 UNLOCK_GC;
5744                 return;
5745         }
5746
5747         DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", ptr));
5748         HEAVY_STAT (++stat_wbarrier_generic_store_remset);
5749
5750         ++index;
5751         if (index >= STORE_REMSET_BUFFER_SIZE) {
5752                 evacuate_remset_buffer ();
5753                 index = STORE_REMSET_BUFFER_INDEX;
5754                 g_assert (index == 0);
5755                 ++index;
5756         }
5757         buffer [index] = ptr;
5758         STORE_REMSET_BUFFER_INDEX = index;
5759
5760         UNLOCK_GC;
5761 }
5762
5763 void
5764 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
5765 {
5766         DEBUG (8, fprintf (gc_debug_file, "Wbarrier store at %p to %p (%s)\n", ptr, value, value ? safe_name (value) : "null"));
5767         *(void**)ptr = value;
5768         if (ptr_in_nursery (value))
5769                 mono_gc_wbarrier_generic_nostore (ptr);
5770         dummy_use (value);
5771 }
5772
5773 void mono_gc_wbarrier_value_copy_bitmap (gpointer _dest, gpointer _src, int size, unsigned bitmap)
5774 {
5775         mword *dest = _dest;
5776         mword *src = _src;
5777
5778         while (size) {
5779                 if (bitmap & 0x1)
5780                         mono_gc_wbarrier_generic_store (dest, (MonoObject*)*src);
5781                 else
5782                         *dest = *src;
5783                 ++src;
5784                 ++dest;
5785                 size -= SIZEOF_VOID_P;
5786                 bitmap >>= 1;
5787         }
5788 }
5789
5790
5791 void
5792 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
5793 {
5794         RememberedSet *rs;
5795         size_t size = count * mono_class_value_size (klass, NULL);
5796         TLAB_ACCESS_INIT;
5797         HEAVY_STAT (++stat_wbarrier_value_copy);
5798         g_assert (klass->valuetype);
5799         if (use_cardtable) {
5800 #ifdef DISABLE_CRITICAL_REGION
5801                 LOCK_GC;
5802 #else
5803                 ENTER_CRITICAL_REGION;
5804 #endif
5805                 mono_gc_memmove (dest, src, size);
5806                 sgen_card_table_mark_range ((mword)dest, size);
5807 #ifdef DISABLE_CRITICAL_REGION
5808                 UNLOCK_GC;
5809 #else
5810                 EXIT_CRITICAL_REGION;
5811 #endif
5812         } else {
5813                 LOCK_GC;
5814                 mono_gc_memmove (dest, src, size);
5815                 rs = REMEMBERED_SET;
5816                 if (ptr_in_nursery (dest) || ptr_on_stack (dest) || !SGEN_CLASS_HAS_REFERENCES (klass)) {
5817                         UNLOCK_GC;
5818                         return;
5819                 }
5820                 g_assert (klass->gc_descr_inited);
5821                 DEBUG (8, fprintf (gc_debug_file, "Adding value remset at %p, count %d, descr %p for class %s (%p)\n", dest, count, klass->gc_descr, klass->name, klass));
5822
5823                 if (rs->store_next + 4 < rs->end_set) {
5824                         *(rs->store_next++) = (mword)dest | REMSET_VTYPE;
5825                         *(rs->store_next++) = (mword)klass->gc_descr;
5826                         *(rs->store_next++) = (mword)count;
5827                         *(rs->store_next++) = (mword)size;
5828                         UNLOCK_GC;
5829                         return;
5830                 }
5831                 rs = alloc_remset (rs->end_set - rs->data, (void*)1, FALSE);
5832                 rs->next = REMEMBERED_SET;
5833                 REMEMBERED_SET = rs;
5834 #ifdef HAVE_KW_THREAD
5835                 mono_thread_info_current ()->remset = rs;
5836 #endif
5837                 *(rs->store_next++) = (mword)dest | REMSET_VTYPE;
5838                 *(rs->store_next++) = (mword)klass->gc_descr;
5839                 *(rs->store_next++) = (mword)count;
5840                 *(rs->store_next++) = (mword)size;
5841                 UNLOCK_GC;
5842         }
5843 }
5844
5845 /**
5846  * mono_gc_wbarrier_object_copy:
5847  *
5848  * Write barrier to call when obj is the result of a clone or copy of an object.
5849  */
5850 void
5851 mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
5852 {
5853         RememberedSet *rs;
5854         int size;
5855
5856         TLAB_ACCESS_INIT;
5857         HEAVY_STAT (++stat_wbarrier_object_copy);
5858         rs = REMEMBERED_SET;
5859         DEBUG (6, fprintf (gc_debug_file, "Adding object remset for %p\n", obj));
5860         size = mono_object_class (obj)->instance_size;
5861         LOCK_GC;
5862         /* do not copy the sync state */
5863         mono_gc_memmove ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
5864                         size - sizeof (MonoObject));
5865         if (ptr_in_nursery (obj) || ptr_on_stack (obj)) {
5866                 UNLOCK_GC;
5867                 return;
5868         }
5869         if (rs->store_next < rs->end_set) {
5870                 *(rs->store_next++) = (mword)obj | REMSET_OBJECT;
5871                 UNLOCK_GC;
5872                 return;
5873         }
5874         rs = alloc_remset (rs->end_set - rs->data, (void*)1, FALSE);
5875         rs->next = REMEMBERED_SET;
5876         REMEMBERED_SET = rs;
5877
5878 #ifdef HAVE_KW_THREAD
5879         mono_thread_info_current ()->remset = rs;
5880 #endif
5881         *(rs->store_next++) = (mword)obj | REMSET_OBJECT;
5882         UNLOCK_GC;
5883 }
5884
5885 /*
5886  * ######################################################################
5887  * ########  Collector debugging
5888  * ######################################################################
5889  */
5890
5891 const char*descriptor_types [] = {
5892         "run_length",
5893         "small_bitmap",
5894         "string",
5895         "complex",
5896         "vector",
5897         "array",
5898         "large_bitmap",
5899         "complex_arr"
5900 };
5901
5902 void
5903 describe_ptr (char *ptr)
5904 {
5905         MonoVTable *vtable;
5906         mword desc;
5907         int type;
5908         char *start;
5909
5910         if (ptr_in_nursery (ptr)) {
5911                 printf ("Pointer inside nursery.\n");
5912         } else {
5913                 if (mono_sgen_ptr_is_in_los (ptr, &start)) {
5914                         if (ptr == start)
5915                                 printf ("Pointer is the start of object %p in LOS space.\n", start);
5916                         else
5917                                 printf ("Pointer is at offset 0x%x of object %p in LOS space.\n", (int)(ptr - start), start);
5918                         ptr = start;
5919                 } else if (major_collector.ptr_is_in_non_pinned_space (ptr)) {
5920                         printf ("Pointer inside oldspace.\n");
5921                 } else if (major_collector.obj_is_from_pinned_alloc (ptr)) {
5922                         printf ("Pointer is inside a pinned chunk.\n");
5923                 } else {
5924                         printf ("Pointer unknown.\n");
5925                         return;
5926                 }
5927         }
5928
5929         if (object_is_pinned (ptr))
5930                 printf ("Object is pinned.\n");
5931
5932         if (object_is_forwarded (ptr))
5933                 printf ("Object is forwared.\n");
5934
5935         // FIXME: Handle pointers to the inside of objects
5936         vtable = (MonoVTable*)LOAD_VTABLE (ptr);
5937
5938         printf ("VTable: %p\n", vtable);
5939         if (vtable == NULL) {
5940                 printf ("VTable is invalid (empty).\n");
5941                 return;
5942         }
5943         if (ptr_in_nursery (vtable)) {
5944                 printf ("VTable is invalid (points inside nursery).\n");
5945                 return;
5946         }
5947         printf ("Class: %s\n", vtable->klass->name);
5948
5949         desc = ((GCVTable*)vtable)->desc;
5950         printf ("Descriptor: %lx\n", (long)desc);
5951
5952         type = desc & 0x7;
5953         printf ("Descriptor type: %d (%s)\n", type, descriptor_types [type]);
5954 }
5955
5956 static mword*
5957 find_in_remset_loc (mword *p, char *addr, gboolean *found)
5958 {
5959         void **ptr;
5960         mword count, desc;
5961         size_t skip_size;
5962
5963         switch ((*p) & REMSET_TYPE_MASK) {
5964         case REMSET_LOCATION:
5965                 if (*p == (mword)addr)
5966                         *found = TRUE;
5967                 return p + 1;
5968         case REMSET_RANGE:
5969                 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
5970                 count = p [1];
5971                 if ((void**)addr >= ptr && (void**)addr < ptr + count)
5972                         *found = TRUE;
5973                 return p + 2;
5974         case REMSET_OBJECT:
5975                 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
5976                 count = safe_object_get_size ((MonoObject*)ptr); 
5977                 count = ALIGN_UP (count);
5978                 count /= sizeof (mword);
5979                 if ((void**)addr >= ptr && (void**)addr < ptr + count)
5980                         *found = TRUE;
5981                 return p + 1;
5982         case REMSET_VTYPE:
5983                 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
5984                 desc = p [1];
5985                 count = p [2];
5986                 skip_size = p [3];
5987
5988                 /* The descriptor includes the size of MonoObject */
5989                 skip_size -= sizeof (MonoObject);
5990                 skip_size *= count;
5991                 if ((void**)addr >= ptr && (void**)addr < ptr + (skip_size / sizeof (gpointer)))
5992                         *found = TRUE;
5993
5994                 return p + 4;
5995         default:
5996                 g_assert_not_reached ();
5997         }
5998         return NULL;
5999 }
6000
6001 /*
6002  * Return whenever ADDR occurs in the remembered sets
6003  */
6004 static gboolean
6005 find_in_remsets (char *addr)
6006 {
6007         int i;
6008         SgenThreadInfo *info;
6009         RememberedSet *remset;
6010         GenericStoreRememberedSet *store_remset;
6011         mword *p;
6012         gboolean found = FALSE;
6013
6014         /* the global one */
6015         for (remset = global_remset; remset; remset = remset->next) {
6016                 DEBUG (4, fprintf (gc_debug_file, "Scanning global remset range: %p-%p, size: %td\n", remset->data, remset->store_next, remset->store_next - remset->data));
6017                 for (p = remset->data; p < remset->store_next;) {
6018                         p = find_in_remset_loc (p, addr, &found);
6019                         if (found)
6020                                 return TRUE;
6021                 }
6022         }
6023
6024         /* the generic store ones */
6025         for (store_remset = generic_store_remsets; store_remset; store_remset = store_remset->next) {
6026                 for (i = 0; i < STORE_REMSET_BUFFER_SIZE - 1; ++i) {
6027                         if (store_remset->data [i] == addr)
6028                                 return TRUE;
6029                 }
6030         }
6031
6032         /* the per-thread ones */
6033         FOREACH_THREAD (info) {
6034                 int j;
6035                 for (remset = info->remset; remset; remset = remset->next) {
6036                         DEBUG (4, fprintf (gc_debug_file, "Scanning remset for thread %p, range: %p-%p, size: %td\n", info, remset->data, remset->store_next, remset->store_next - remset->data));
6037                         for (p = remset->data; p < remset->store_next;) {
6038                                 p = find_in_remset_loc (p, addr, &found);
6039                                 if (found)
6040                                         return TRUE;
6041                         }
6042                 }
6043                 for (j = 0; j < *info->store_remset_buffer_index_addr; ++j) {
6044                         if ((*info->store_remset_buffer_addr) [j + 1] == addr)
6045                                 return TRUE;
6046                 }
6047         } END_FOREACH_THREAD
6048
6049         /* the freed thread ones */
6050         for (remset = freed_thread_remsets; remset; remset = remset->next) {
6051                 DEBUG (4, fprintf (gc_debug_file, "Scanning remset for freed thread, range: %p-%p, size: %td\n", remset->data, remset->store_next, remset->store_next - remset->data));
6052                 for (p = remset->data; p < remset->store_next;) {
6053                         p = find_in_remset_loc (p, addr, &found);
6054                         if (found)
6055                                 return TRUE;
6056                 }
6057         }
6058
6059         return FALSE;
6060 }
6061
6062 static gboolean missing_remsets;
6063
6064 /*
6065  * We let a missing remset slide if the target object is pinned,
6066  * because the store might have happened but the remset not yet added,
6067  * but in that case the target must be pinned.  We might theoretically
6068  * miss some missing remsets this way, but it's very unlikely.
6069  */
6070 #undef HANDLE_PTR
6071 #define HANDLE_PTR(ptr,obj)     do {    \
6072                 if (*(ptr) && (char*)*(ptr) >= nursery_start && (char*)*(ptr) < nursery_end) {  \
6073                 if (!find_in_remsets ((char*)(ptr)) && (!use_cardtable || !sgen_card_table_address_is_marked ((mword)ptr))) { \
6074                 fprintf (gc_debug_file, "Oldspace->newspace reference %p at offset %td in object %p (%s.%s) not found in remsets.\n", *(ptr), (char*)(ptr) - (char*)(obj), (obj), ((MonoObject*)(obj))->vtable->klass->name_space, ((MonoObject*)(obj))->vtable->klass->name); \
6075                 binary_protocol_missing_remset ((obj), (gpointer)LOAD_VTABLE ((obj)), (char*)(ptr) - (char*)(obj), *(ptr), (gpointer)LOAD_VTABLE(*(ptr)), object_is_pinned (*(ptr))); \
6076                 if (!object_is_pinned (*(ptr)))                         \
6077                         missing_remsets = TRUE;                         \
6078             } \
6079         } \
6080         } while (0)
6081
6082 /*
6083  * Check that each object reference which points into the nursery can
6084  * be found in the remembered sets.
6085  */
6086 static void
6087 check_consistency_callback (char *start, size_t size, void *dummy)
6088 {
6089         GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
6090         DEBUG (8, fprintf (gc_debug_file, "Scanning object %p, vtable: %p (%s)\n", start, vt, vt->klass->name));
6091
6092 #define SCAN_OBJECT_ACTION
6093 #include "sgen-scan-object.h"
6094 }
6095
6096 /*
6097  * Perform consistency check of the heap.
6098  *
6099  * Assumes the world is stopped.
6100  */
6101 static void
6102 check_consistency (void)
6103 {
6104         // Need to add more checks
6105
6106         missing_remsets = FALSE;
6107
6108         DEBUG (1, fprintf (gc_debug_file, "Begin heap consistency check...\n"));
6109
6110         // Check that oldspace->newspace pointers are registered with the collector
6111         major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)check_consistency_callback, NULL);
6112
6113         mono_sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_consistency_callback, NULL);
6114
6115         DEBUG (1, fprintf (gc_debug_file, "Heap consistency check done.\n"));
6116
6117         if (!binary_protocol_is_enabled ())
6118                 g_assert (!missing_remsets);
6119 }
6120
6121
6122 #undef HANDLE_PTR
6123 #define HANDLE_PTR(ptr,obj)     do {                                    \
6124                 if (*(ptr) && !LOAD_VTABLE (*(ptr)))                                            \
6125                         g_error ("Could not load vtable for obj %p slot %d (size %d)", obj, (char*)ptr - (char*)obj, safe_object_get_size ((MonoObject*)obj));          \
6126         } while (0)
6127
6128 static void
6129 check_major_refs_callback (char *start, size_t size, void *dummy)
6130 {
6131 #define SCAN_OBJECT_ACTION
6132 #include "sgen-scan-object.h"
6133 }
6134
6135 static void
6136 check_major_refs (void)
6137 {
6138         major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)check_major_refs_callback, NULL);
6139         mono_sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_major_refs_callback, NULL);
6140 }
6141
6142 /* Check that the reference is valid */
6143 #undef HANDLE_PTR
6144 #define HANDLE_PTR(ptr,obj)     do {    \
6145                 if (*(ptr)) {   \
6146                         g_assert (safe_name (*(ptr)) != NULL);  \
6147                 }       \
6148         } while (0)
6149
6150 /*
6151  * check_object:
6152  *
6153  *   Perform consistency check on an object. Currently we only check that the
6154  * reference fields are valid.
6155  */
6156 void
6157 check_object (char *start)
6158 {
6159         if (!start)
6160                 return;
6161
6162 #include "sgen-scan-object.h"
6163 }
6164
6165 /*
6166  * ######################################################################
6167  * ########  Other mono public interface functions.
6168  * ######################################################################
6169  */
6170
6171 #define REFS_SIZE 128
6172 typedef struct {
6173         void *data;
6174         MonoGCReferences callback;
6175         int flags;
6176         int count;
6177         int called;
6178         MonoObject *refs [REFS_SIZE];
6179         uintptr_t offsets [REFS_SIZE];
6180 } HeapWalkInfo;
6181
6182 #undef HANDLE_PTR
6183 #define HANDLE_PTR(ptr,obj)     do {    \
6184                 if (*(ptr)) {   \
6185                         if (hwi->count == REFS_SIZE) {  \
6186                                 hwi->callback ((MonoObject*)start, mono_object_class (start), hwi->called? 0: size, hwi->count, hwi->refs, hwi->offsets, hwi->data);    \
6187                                 hwi->count = 0; \
6188                                 hwi->called = 1;        \
6189                         }       \
6190                         hwi->offsets [hwi->count] = (char*)(ptr)-(char*)start;  \
6191                         hwi->refs [hwi->count++] = *(ptr);      \
6192                 }       \
6193         } while (0)
6194
6195 static void
6196 collect_references (HeapWalkInfo *hwi, char *start, size_t size)
6197 {
6198 #include "sgen-scan-object.h"
6199 }
6200
6201 static void
6202 walk_references (char *start, size_t size, void *data)
6203 {
6204         HeapWalkInfo *hwi = data;
6205         hwi->called = 0;
6206         hwi->count = 0;
6207         collect_references (hwi, start, size);
6208         if (hwi->count || !hwi->called)
6209                 hwi->callback ((MonoObject*)start, mono_object_class (start), hwi->called? 0: size, hwi->count, hwi->refs, hwi->offsets, hwi->data);
6210 }
6211
6212 /**
6213  * mono_gc_walk_heap:
6214  * @flags: flags for future use
6215  * @callback: a function pointer called for each object in the heap
6216  * @data: a user data pointer that is passed to callback
6217  *
6218  * This function can be used to iterate over all the live objects in the heap:
6219  * for each object, @callback is invoked, providing info about the object's
6220  * location in memory, its class, its size and the objects it references.
6221  * For each referenced object it's offset from the object address is
6222  * reported in the offsets array.
6223  * The object references may be buffered, so the callback may be invoked
6224  * multiple times for the same object: in all but the first call, the size
6225  * argument will be zero.
6226  * Note that this function can be only called in the #MONO_GC_EVENT_PRE_START_WORLD
6227  * profiler event handler.
6228  *
6229  * Returns: a non-zero value if the GC doesn't support heap walking
6230  */
6231 int
6232 mono_gc_walk_heap (int flags, MonoGCReferences callback, void *data)
6233 {
6234         HeapWalkInfo hwi;
6235
6236         hwi.flags = flags;
6237         hwi.callback = callback;
6238         hwi.data = data;
6239
6240         mono_sgen_clear_nursery_fragments ();
6241         mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data, walk_references, &hwi, FALSE);
6242
6243         major_collector.iterate_objects (TRUE, TRUE, walk_references, &hwi);
6244         mono_sgen_los_iterate_objects (walk_references, &hwi);
6245
6246         return 0;
6247 }
6248
6249 void
6250 mono_gc_collect (int generation)
6251 {
6252         LOCK_GC;
6253         if (generation > 1)
6254                 generation = 1;
6255         mono_profiler_gc_event (MONO_GC_EVENT_START, generation);
6256         stop_world (generation);
6257         if (generation == 0) {
6258                 collect_nursery (0);
6259         } else {
6260                 major_collection ("user request");
6261         }
6262         restart_world (generation);
6263         mono_profiler_gc_event (MONO_GC_EVENT_END, generation);
6264         UNLOCK_GC;
6265 }
6266
6267 int
6268 mono_gc_max_generation (void)
6269 {
6270         return 1;
6271 }
6272
6273 int
6274 mono_gc_collection_count (int generation)
6275 {
6276         if (generation == 0)
6277                 return num_minor_gcs;
6278         return num_major_gcs;
6279 }
6280
6281 int64_t
6282 mono_gc_get_used_size (void)
6283 {
6284         gint64 tot = 0;
6285         LOCK_GC;
6286         tot = los_memory_usage;
6287         tot += nursery_section->next_data - nursery_section->data;
6288         tot += major_collector.get_used_size ();
6289         /* FIXME: account for pinned objects */
6290         UNLOCK_GC;
6291         return tot;
6292 }
6293
6294 int64_t
6295 mono_gc_get_heap_size (void)
6296 {
6297         return total_alloc;
6298 }
6299
6300 void
6301 mono_gc_disable (void)
6302 {
6303         LOCK_GC;
6304         gc_disabled++;
6305         UNLOCK_GC;
6306 }
6307
6308 void
6309 mono_gc_enable (void)
6310 {
6311         LOCK_GC;
6312         gc_disabled--;
6313         UNLOCK_GC;
6314 }
6315
6316 int
6317 mono_gc_get_los_limit (void)
6318 {
6319         return MAX_SMALL_OBJ_SIZE;
6320 }
6321
6322 gboolean
6323 mono_object_is_alive (MonoObject* o)
6324 {
6325         return TRUE;
6326 }
6327
6328 int
6329 mono_gc_get_generation (MonoObject *obj)
6330 {
6331         if (ptr_in_nursery (obj))
6332                 return 0;
6333         return 1;
6334 }
6335
6336 void
6337 mono_gc_enable_events (void)
6338 {
6339 }
6340
6341 void
6342 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
6343 {
6344         mono_gc_register_disappearing_link (obj, link_addr, track, FALSE);
6345 }
6346
6347 void
6348 mono_gc_weak_link_remove (void **link_addr)
6349 {
6350         mono_gc_register_disappearing_link (NULL, link_addr, FALSE, FALSE);
6351 }
6352
6353 MonoObject*
6354 mono_gc_weak_link_get (void **link_addr)
6355 {
6356         if (!*link_addr)
6357                 return NULL;
6358         return (MonoObject*) REVEAL_POINTER (*link_addr);
6359 }
6360
6361 gboolean
6362 mono_gc_ephemeron_array_add (MonoObject *obj)
6363 {
6364         EphemeronLinkNode *node;
6365
6366         LOCK_GC;
6367
6368         node = mono_sgen_alloc_internal (INTERNAL_MEM_EPHEMERON_LINK);
6369         if (!node) {
6370                 UNLOCK_GC;
6371                 return FALSE;
6372         }
6373         node->array = (char*)obj;
6374         node->next = ephemeron_list;
6375         ephemeron_list = node;
6376
6377         DEBUG (5, fprintf (gc_debug_file, "Registered ephemeron array %p\n", obj));
6378
6379         UNLOCK_GC;
6380         return TRUE;
6381 }
6382
6383 void*
6384 mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
6385 {
6386         if (numbits == 0) {
6387                 return (void*)MAKE_ROOT_DESC (ROOT_DESC_BITMAP, 0);
6388         } else if (numbits < ((sizeof (*bitmap) * 8) - ROOT_DESC_TYPE_SHIFT)) {
6389                 return (void*)MAKE_ROOT_DESC (ROOT_DESC_BITMAP, bitmap [0]);
6390         } else {
6391                 mword complex = alloc_complex_descriptor (bitmap, numbits);
6392                 return (void*)MAKE_ROOT_DESC (ROOT_DESC_COMPLEX, complex);
6393         }
6394 }
6395
6396  static void *all_ref_root_descrs [32];
6397
6398 void*
6399 mono_gc_make_root_descr_all_refs (int numbits)
6400 {
6401         gsize *gc_bitmap;
6402         void *descr;
6403         int num_bytes = numbits / 8;
6404
6405         if (numbits < 32 && all_ref_root_descrs [numbits])
6406                 return all_ref_root_descrs [numbits];
6407
6408         gc_bitmap = g_malloc0 (ALIGN_TO (ALIGN_TO (numbits, 8) + 1, sizeof (gsize)));
6409         memset (gc_bitmap, 0xff, num_bytes);
6410         if (numbits < ((sizeof (*gc_bitmap) * 8) - ROOT_DESC_TYPE_SHIFT)) 
6411                 gc_bitmap[0] = GUINT64_TO_LE(gc_bitmap[0]);
6412         else if (numbits && num_bytes % (sizeof (*gc_bitmap)))
6413                 gc_bitmap[num_bytes / 8] = GUINT64_TO_LE(gc_bitmap [num_bytes / 8]);
6414         if (numbits % 8)
6415                 gc_bitmap [numbits / 8] = (1 << (numbits % 8)) - 1;
6416         descr = mono_gc_make_descr_from_bitmap (gc_bitmap, numbits);
6417         g_free (gc_bitmap);
6418
6419         if (numbits < 32)
6420                 all_ref_root_descrs [numbits] = descr;
6421
6422         return descr;
6423 }
6424
6425 void*
6426 mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
6427 {
6428         void *descr;
6429
6430         g_assert (user_descriptors_next < MAX_USER_DESCRIPTORS);
6431         descr = (void*)MAKE_ROOT_DESC (ROOT_DESC_USER, (mword)user_descriptors_next);
6432         user_descriptors [user_descriptors_next ++] = marker;
6433
6434         return descr;
6435 }
6436
6437 void*
6438 mono_gc_alloc_fixed (size_t size, void *descr)
6439 {
6440         /* FIXME: do a single allocation */
6441         void *res = calloc (1, size);
6442         if (!res)
6443                 return NULL;
6444         if (!mono_gc_register_root (res, size, descr)) {
6445                 free (res);
6446                 res = NULL;
6447         }
6448         return res;
6449 }
6450
6451 void
6452 mono_gc_free_fixed (void* addr)
6453 {
6454         mono_gc_deregister_root (addr);
6455         free (addr);
6456 }
6457
6458 void*
6459 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
6460 {
6461         void *result;
6462         LOCK_INTERRUPTION;
6463         result = func (data);
6464         UNLOCK_INTERRUPTION;
6465         return result;
6466 }
6467
6468 gboolean
6469 mono_gc_is_gc_thread (void)
6470 {
6471         gboolean result;
6472         LOCK_GC;
6473         result = mono_thread_info_current () != NULL;
6474         UNLOCK_GC;
6475         return result;
6476 }
6477
6478 static gboolean
6479 is_critical_method (MonoMethod *method)
6480 {
6481         return mono_runtime_is_critical_method (method) || mono_gc_is_critical_method (method);
6482 }
6483
6484 void
6485 mono_gc_base_init (void)
6486 {
6487         MonoThreadInfoCallbacks cb;
6488         char *env;
6489         char **opts, **ptr;
6490         char *major_collector_opt = NULL;
6491         struct sigaction sinfo;
6492         glong max_heap = 0;
6493         int num_workers;
6494         int result;
6495
6496         do {
6497                 result = InterlockedCompareExchange (&gc_initialized, -1, 0);
6498                 switch (result) {
6499                 case 1:
6500                         /* already inited */
6501                         return;
6502                 case -1:
6503                         /* being inited by another thread */
6504                         usleep (1000);
6505                         break;
6506                 case 0:
6507                         /* we will init it */
6508                         break;
6509                 default:
6510                         g_assert_not_reached ();
6511                 }
6512         } while (result != 0);
6513
6514         LOCK_INIT (gc_mutex);
6515
6516         pagesize = mono_pagesize ();
6517         gc_debug_file = stdout;
6518
6519         cb.thread_register = sgen_thread_register;
6520         cb.thread_unregister = sgen_thread_unregister;
6521         cb.thread_attach = sgen_thread_attach;
6522         cb.mono_method_is_critical = is_critical_method;
6523
6524         mono_threads_init (&cb, sizeof (SgenThreadInfo));
6525
6526         LOCK_INIT (interruption_mutex);
6527         LOCK_INIT (global_remset_mutex);
6528         LOCK_INIT (pin_queue_mutex);
6529
6530         if ((env = getenv ("MONO_GC_PARAMS"))) {
6531                 opts = g_strsplit (env, ",", -1);
6532                 for (ptr = opts; *ptr; ++ptr) {
6533                         char *opt = *ptr;
6534                         if (g_str_has_prefix (opt, "major=")) {
6535                                 opt = strchr (opt, '=') + 1;
6536                                 major_collector_opt = g_strdup (opt);
6537                         }
6538                 }
6539         } else {
6540                 opts = NULL;
6541         }
6542
6543         init_stats ();
6544         mono_sgen_init_internal_allocator ();
6545         mono_sgen_init_nursery_allocator ();
6546
6547         mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_SECTION, SGEN_SIZEOF_GC_MEM_SECTION);
6548         mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_FINALIZE_ENTRY, sizeof (FinalizeEntry));
6549         mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_DISLINK, sizeof (DisappearingLink));
6550         mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_ROOT_RECORD, sizeof (RootRecord));
6551         mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_GRAY_QUEUE, sizeof (GrayQueueSection));
6552         g_assert (sizeof (GenericStoreRememberedSet) == sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
6553         mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_STORE_REMSET, sizeof (GenericStoreRememberedSet));
6554         mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_EPHEMERON_LINK, sizeof (EphemeronLinkNode));
6555
6556         pthread_key_create (&remembered_set_key, NULL);
6557
6558 #ifndef HAVE_KW_THREAD
6559         pthread_key_create (&thread_info_key, NULL);
6560 #endif
6561
6562         /*
6563          * This needs to happen before any internal allocations because
6564          * it inits the small id which is required for hazard pointer
6565          * operations.
6566          */
6567         suspend_ack_semaphore_ptr = &suspend_ack_semaphore;
6568         MONO_SEM_INIT (&suspend_ack_semaphore, 0);
6569
6570         sigfillset (&sinfo.sa_mask);
6571         sinfo.sa_flags = SA_RESTART | SA_SIGINFO;
6572         sinfo.sa_sigaction = suspend_handler;
6573         if (sigaction (suspend_signal_num, &sinfo, NULL) != 0) {
6574                 g_error ("failed sigaction");
6575         }
6576
6577         sinfo.sa_handler = restart_handler;
6578         if (sigaction (restart_signal_num, &sinfo, NULL) != 0) {
6579                 g_error ("failed sigaction");
6580         }
6581
6582         sigfillset (&suspend_signal_mask);
6583         sigdelset (&suspend_signal_mask, restart_signal_num);
6584
6585         mono_thread_info_attach (&sinfo);
6586
6587         if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep")) {
6588                 mono_sgen_marksweep_init (&major_collector);
6589         } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-fixed")) {
6590                 mono_sgen_marksweep_fixed_init (&major_collector);
6591         } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-par")) {
6592                 mono_sgen_marksweep_par_init (&major_collector);
6593         } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-fixed-par")) {
6594                 mono_sgen_marksweep_fixed_par_init (&major_collector);
6595         } else if (!strcmp (major_collector_opt, "copying")) {
6596                 mono_sgen_copying_init (&major_collector);
6597         } else {
6598                 fprintf (stderr, "Unknown major collector `%s'.\n", major_collector_opt);
6599                 exit (1);
6600         }
6601
6602 #ifdef SGEN_HAVE_CARDTABLE
6603         use_cardtable = major_collector.supports_cardtable;
6604 #else
6605         use_cardtable = FALSE;
6606 #endif
6607
6608         num_workers = mono_cpu_count ();
6609         g_assert (num_workers > 0);
6610         if (num_workers > 16)
6611                 num_workers = 16;
6612
6613         /* Keep this the default for now */
6614         conservative_stack_mark = TRUE;
6615
6616         if (opts) {
6617                 for (ptr = opts; *ptr; ++ptr) {
6618                         char *opt = *ptr;
6619                         if (g_str_has_prefix (opt, "major="))
6620                                 continue;
6621                         if (g_str_has_prefix (opt, "wbarrier=")) {
6622                                 opt = strchr (opt, '=') + 1;
6623                                 if (strcmp (opt, "remset") == 0) {
6624                                         use_cardtable = FALSE;
6625                                 } else if (strcmp (opt, "cardtable") == 0) {
6626                                         if (!use_cardtable) {
6627                                                 if (major_collector.supports_cardtable)
6628                                                         fprintf (stderr, "The cardtable write barrier is not supported on this platform.\n");
6629                                                 else
6630                                                         fprintf (stderr, "The major collector does not support the cardtable write barrier.\n");
6631                                                 exit (1);
6632                                         }
6633                                 }
6634                                 continue;
6635                         }
6636                         if (g_str_has_prefix (opt, "max-heap-size=")) {
6637                                 opt = strchr (opt, '=') + 1;
6638                                 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &max_heap)) {
6639                                         if ((max_heap & (mono_pagesize () - 1))) {
6640                                                 fprintf (stderr, "max-heap-size size must be a multiple of %d.\n", mono_pagesize ());
6641                                                 exit (1);
6642                                         }
6643                                 } else {
6644                                         fprintf (stderr, "max-heap-size must be an integer.\n");
6645                                         exit (1);
6646                                 }
6647                                 continue;
6648                         }
6649                         if (g_str_has_prefix (opt, "workers=")) {
6650                                 long val;
6651                                 char *endptr;
6652                                 if (!major_collector.is_parallel) {
6653                                         fprintf (stderr, "The workers= option can only be used for parallel collectors.");
6654                                         exit (1);
6655                                 }
6656                                 opt = strchr (opt, '=') + 1;
6657                                 val = strtol (opt, &endptr, 10);
6658                                 if (!*opt || *endptr) {
6659                                         fprintf (stderr, "Cannot parse the workers= option value.");
6660                                         exit (1);
6661                                 }
6662                                 if (val <= 0 || val > 16) {
6663                                         fprintf (stderr, "The number of workers must be in the range 1 to 16.");
6664                                         exit (1);
6665                                 }
6666                                 num_workers = (int)val;
6667                                 continue;
6668                         }
6669                         if (g_str_has_prefix (opt, "stack-mark=")) {
6670                                 opt = strchr (opt, '=') + 1;
6671                                 if (!strcmp (opt, "precise")) {
6672                                         conservative_stack_mark = FALSE;
6673                                 } else if (!strcmp (opt, "conservative")) {
6674                                         conservative_stack_mark = TRUE;
6675                                 } else {
6676                                         fprintf (stderr, "Invalid value '%s' for stack-mark= option, possible values are: 'precise', 'conservative'.\n", opt);
6677                                         exit (1);
6678                                 }
6679                                 continue;
6680                         }
6681 #ifdef USER_CONFIG
6682                         if (g_str_has_prefix (opt, "nursery-size=")) {
6683                                 long val;
6684                                 opt = strchr (opt, '=') + 1;
6685                                 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &val)) {
6686                                         default_nursery_size = val;
6687 #ifdef SGEN_ALIGN_NURSERY
6688                                         if ((val & (val - 1))) {
6689                                                 fprintf (stderr, "The nursery size must be a power of two.\n");
6690                                                 exit (1);
6691                                         }
6692
6693                                         default_nursery_bits = 0;
6694                                         while (1 << (++ default_nursery_bits) != default_nursery_size)
6695                                                 ;
6696 #endif
6697                                 } else {
6698                                         fprintf (stderr, "nursery-size must be an integer.\n");
6699                                         exit (1);
6700                                 }
6701                                 continue;
6702                         }
6703 #endif
6704                         if (!(major_collector.handle_gc_param && major_collector.handle_gc_param (opt))) {
6705                                 fprintf (stderr, "MONO_GC_PARAMS must be a comma-delimited list of one or more of the following:\n");
6706                                 fprintf (stderr, "  max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
6707                                 fprintf (stderr, "  nursery-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
6708                                 fprintf (stderr, "  major=COLLECTOR (where COLLECTOR is `marksweep', `marksweep-par' or `copying')\n");
6709                                 fprintf (stderr, "  wbarrier=WBARRIER (where WBARRIER is `remset' or `cardtable')\n");
6710                                 fprintf (stderr, "  stack-mark=MARK-METHOD (where MARK-METHOD is 'precise' or 'conservative')\n");
6711                                 if (major_collector.print_gc_param_usage)
6712                                         major_collector.print_gc_param_usage ();
6713                                 exit (1);
6714                         }
6715                 }
6716                 g_strfreev (opts);
6717         }
6718
6719         if (major_collector.is_parallel)
6720                 workers_init (num_workers);
6721
6722         if (major_collector_opt)
6723                 g_free (major_collector_opt);
6724
6725         nursery_size = DEFAULT_NURSERY_SIZE;
6726         minor_collection_allowance = MIN_MINOR_COLLECTION_ALLOWANCE;
6727         init_heap_size_limits (max_heap);
6728
6729         alloc_nursery ();
6730
6731         if ((env = getenv ("MONO_GC_DEBUG"))) {
6732                 opts = g_strsplit (env, ",", -1);
6733                 for (ptr = opts; ptr && *ptr; ptr ++) {
6734                         char *opt = *ptr;
6735                         if (opt [0] >= '0' && opt [0] <= '9') {
6736                                 gc_debug_level = atoi (opt);
6737                                 opt++;
6738                                 if (opt [0] == ':')
6739                                         opt++;
6740                                 if (opt [0]) {
6741                                         char *rf = g_strdup_printf ("%s.%d", opt, getpid ());
6742                                         gc_debug_file = fopen (rf, "wb");
6743                                         if (!gc_debug_file)
6744                                                 gc_debug_file = stderr;
6745                                         g_free (rf);
6746                                 }
6747                         } else if (!strcmp (opt, "collect-before-allocs")) {
6748                                 collect_before_allocs = 1;
6749                         } else if (g_str_has_prefix (opt, "collect-before-allocs=")) {
6750                                 char *arg = strchr (opt, '=') + 1;
6751                                 collect_before_allocs = atoi (arg);
6752                         } else if (!strcmp (opt, "check-at-minor-collections")) {
6753                                 consistency_check_at_minor_collection = TRUE;
6754                                 nursery_clear_policy = CLEAR_AT_GC;
6755                         } else if (!strcmp (opt, "xdomain-checks")) {
6756                                 xdomain_checks = TRUE;
6757                         } else if (!strcmp (opt, "clear-at-gc")) {
6758                                 nursery_clear_policy = CLEAR_AT_GC;
6759                         } else if (!strcmp (opt, "clear-nursery-at-gc")) {
6760                                 nursery_clear_policy = CLEAR_AT_GC;
6761                         } else if (!strcmp (opt, "check-scan-starts")) {
6762                                 do_scan_starts_check = TRUE;
6763                         } else if (!strcmp (opt, "disable-minor")) {
6764                                 disable_minor_collections = TRUE;
6765                         } else if (!strcmp (opt, "disable-major")) {
6766                                 disable_major_collections = TRUE;
6767                         } else if (g_str_has_prefix (opt, "heap-dump=")) {
6768                                 char *filename = strchr (opt, '=') + 1;
6769                                 nursery_clear_policy = CLEAR_AT_GC;
6770                                 heap_dump_file = fopen (filename, "w");
6771                                 if (heap_dump_file)
6772                                         fprintf (heap_dump_file, "<sgen-dump>\n");
6773 #ifdef SGEN_BINARY_PROTOCOL
6774                         } else if (g_str_has_prefix (opt, "binary-protocol=")) {
6775                                 char *filename = strchr (opt, '=') + 1;
6776                                 binary_protocol_init (filename);
6777 #endif
6778                         } else {
6779                                 fprintf (stderr, "Invalid format for the MONO_GC_DEBUG env variable: '%s'\n", env);
6780                                 fprintf (stderr, "The format is: MONO_GC_DEBUG=[l[:filename]|<option>]+ where l is a debug level 0-9.\n");
6781                                 fprintf (stderr, "Valid options are:\n");
6782                                 fprintf (stderr, "  collect-before-allocs[=<n>]\n");
6783                                 fprintf (stderr, "  check-at-minor-collections\n");
6784                                 fprintf (stderr, "  disable-minor\n");
6785                                 fprintf (stderr, "  disable-major\n");
6786                                 fprintf (stderr, "  xdomain-checks\n");
6787                                 fprintf (stderr, "  clear-at-gc\n");
6788                                 exit (1);
6789                         }
6790                 }
6791                 g_strfreev (opts);
6792         }
6793
6794         if (major_collector.post_param_init)
6795                 major_collector.post_param_init ();
6796
6797         global_remset = alloc_remset (1024, NULL, FALSE);
6798         global_remset->next = NULL;
6799
6800         if (use_cardtable)
6801                 card_table_init ();
6802
6803         gc_initialized = 1;
6804 }
6805
6806 int
6807 mono_gc_get_suspend_signal (void)
6808 {
6809         return suspend_signal_num;
6810 }
6811
6812 enum {
6813         ATYPE_NORMAL,
6814         ATYPE_VECTOR,
6815         ATYPE_SMALL,
6816         ATYPE_NUM
6817 };
6818
6819 #ifdef HAVE_KW_THREAD
6820 #define EMIT_TLS_ACCESS(mb,dummy,offset)        do {    \
6821         mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX);   \
6822         mono_mb_emit_byte ((mb), CEE_MONO_TLS);         \
6823         mono_mb_emit_i4 ((mb), (offset));               \
6824         } while (0)
6825 #else
6826
6827 /* 
6828  * CEE_MONO_TLS requires the tls offset, not the key, so the code below only works on darwin,
6829  * where the two are the same.
6830  */
6831 #ifdef __APPLE__
6832 #define EMIT_TLS_ACCESS(mb,member,dummy)        do {    \
6833         mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX);   \
6834         mono_mb_emit_byte ((mb), CEE_MONO_TLS);         \
6835         mono_mb_emit_i4 ((mb), thread_info_key);        \
6836         mono_mb_emit_icon ((mb), G_STRUCT_OFFSET (SgenThreadInfo, member));     \
6837         mono_mb_emit_byte ((mb), CEE_ADD);              \
6838         mono_mb_emit_byte ((mb), CEE_LDIND_I);          \
6839         } while (0)
6840 #else
6841 #define EMIT_TLS_ACCESS(mb,member,dummy)        do { g_error ("sgen is not supported when using --with-tls=pthread.\n"); } while (0)
6842 #endif
6843
6844 #endif
6845
6846 #ifdef MANAGED_ALLOCATION
6847 /* FIXME: Do this in the JIT, where specialized allocation sequences can be created
6848  * for each class. This is currently not easy to do, as it is hard to generate basic 
6849  * blocks + branches, but it is easy with the linear IL codebase.
6850  *
6851  * For this to work we'd need to solve the TLAB race, first.  Now we
6852  * require the allocator to be in a few known methods to make sure
6853  * that they are executed atomically via the restart mechanism.
6854  */
6855 static MonoMethod*
6856 create_allocator (int atype)
6857 {
6858         int p_var, size_var;
6859         guint32 slowpath_branch, max_size_branch;
6860         MonoMethodBuilder *mb;
6861         MonoMethod *res;
6862         MonoMethodSignature *csig;
6863         static gboolean registered = FALSE;
6864         int tlab_next_addr_var, new_next_var;
6865         int num_params, i;
6866         const char *name = NULL;
6867         AllocatorWrapperInfo *info;
6868
6869 #ifdef HAVE_KW_THREAD
6870         int tlab_next_addr_offset = -1;
6871         int tlab_temp_end_offset = -1;
6872
6873         MONO_THREAD_VAR_OFFSET (tlab_next_addr, tlab_next_addr_offset);
6874         MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
6875
6876         g_assert (tlab_next_addr_offset != -1);
6877         g_assert (tlab_temp_end_offset != -1);
6878 #endif
6879
6880         if (!registered) {
6881                 mono_register_jit_icall (mono_gc_alloc_obj, "mono_gc_alloc_obj", mono_create_icall_signature ("object ptr int"), FALSE);
6882                 mono_register_jit_icall (mono_gc_alloc_vector, "mono_gc_alloc_vector", mono_create_icall_signature ("object ptr int int"), FALSE);
6883                 registered = TRUE;
6884         }
6885
6886         if (atype == ATYPE_SMALL) {
6887                 num_params = 1;
6888                 name = "AllocSmall";
6889         } else if (atype == ATYPE_NORMAL) {
6890                 num_params = 1;
6891                 name = "Alloc";
6892         } else if (atype == ATYPE_VECTOR) {
6893                 num_params = 2;
6894                 name = "AllocVector";
6895         } else {
6896                 g_assert_not_reached ();
6897         }
6898
6899         csig = mono_metadata_signature_alloc (mono_defaults.corlib, num_params);
6900         csig->ret = &mono_defaults.object_class->byval_arg;
6901         for (i = 0; i < num_params; ++i)
6902                 csig->params [i] = &mono_defaults.int_class->byval_arg;
6903
6904         mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_ALLOC);
6905         size_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
6906         if (atype == ATYPE_NORMAL || atype == ATYPE_SMALL) {
6907                 /* size = vtable->klass->instance_size; */
6908                 mono_mb_emit_ldarg (mb, 0);
6909                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoVTable, klass));
6910                 mono_mb_emit_byte (mb, CEE_ADD);
6911                 mono_mb_emit_byte (mb, CEE_LDIND_I);
6912                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoClass, instance_size));
6913                 mono_mb_emit_byte (mb, CEE_ADD);
6914                 /* FIXME: assert instance_size stays a 4 byte integer */
6915                 mono_mb_emit_byte (mb, CEE_LDIND_U4);
6916                 mono_mb_emit_stloc (mb, size_var);
6917         } else if (atype == ATYPE_VECTOR) {
6918                 MonoExceptionClause *clause;
6919                 int pos, pos_leave;
6920                 MonoClass *oom_exc_class;
6921                 MonoMethod *ctor;
6922
6923                 /* n >  MONO_ARRAY_MAX_INDEX -> OverflowException */
6924                 mono_mb_emit_ldarg (mb, 1);
6925                 mono_mb_emit_icon (mb, MONO_ARRAY_MAX_INDEX);
6926                 pos = mono_mb_emit_short_branch (mb, CEE_BLE_UN_S);
6927                 mono_mb_emit_exception (mb, "OverflowException", NULL);
6928                 mono_mb_patch_short_branch (mb, pos);
6929
6930                 clause = mono_image_alloc0 (mono_defaults.corlib, sizeof (MonoExceptionClause));
6931                 clause->try_offset = mono_mb_get_label (mb);
6932
6933                 /* vtable->klass->sizes.element_size */
6934                 mono_mb_emit_ldarg (mb, 0);
6935                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoVTable, klass));
6936                 mono_mb_emit_byte (mb, CEE_ADD);
6937                 mono_mb_emit_byte (mb, CEE_LDIND_I);
6938                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoClass, sizes.element_size));
6939                 mono_mb_emit_byte (mb, CEE_ADD);
6940                 mono_mb_emit_byte (mb, CEE_LDIND_U4);
6941
6942                 /* * n */
6943                 mono_mb_emit_ldarg (mb, 1);
6944                 mono_mb_emit_byte (mb, CEE_MUL_OVF_UN);
6945                 /* + sizeof (MonoArray) */
6946                 mono_mb_emit_icon (mb, sizeof (MonoArray));
6947                 mono_mb_emit_byte (mb, CEE_ADD_OVF_UN);
6948                 mono_mb_emit_stloc (mb, size_var);
6949
6950                 pos_leave = mono_mb_emit_branch (mb, CEE_LEAVE);
6951
6952                 /* catch */
6953                 clause->flags = MONO_EXCEPTION_CLAUSE_NONE;
6954                 clause->try_len = mono_mb_get_pos (mb) - clause->try_offset;
6955                 clause->data.catch_class = mono_class_from_name (mono_defaults.corlib,
6956                                 "System", "OverflowException");
6957                 g_assert (clause->data.catch_class);
6958                 clause->handler_offset = mono_mb_get_label (mb);
6959
6960                 oom_exc_class = mono_class_from_name (mono_defaults.corlib,
6961                                 "System", "OutOfMemoryException");
6962                 g_assert (oom_exc_class);
6963                 ctor = mono_class_get_method_from_name (oom_exc_class, ".ctor", 0);
6964                 g_assert (ctor);
6965
6966                 mono_mb_emit_byte (mb, CEE_POP);
6967                 mono_mb_emit_op (mb, CEE_NEWOBJ, ctor);
6968                 mono_mb_emit_byte (mb, CEE_THROW);
6969
6970                 clause->handler_len = mono_mb_get_pos (mb) - clause->handler_offset;
6971                 mono_mb_set_clauses (mb, 1, clause);
6972                 mono_mb_patch_branch (mb, pos_leave);
6973                 /* end catch */
6974         } else {
6975                 g_assert_not_reached ();
6976         }
6977
6978         /* size += ALLOC_ALIGN - 1; */
6979         mono_mb_emit_ldloc (mb, size_var);
6980         mono_mb_emit_icon (mb, ALLOC_ALIGN - 1);
6981         mono_mb_emit_byte (mb, CEE_ADD);
6982         /* size &= ~(ALLOC_ALIGN - 1); */
6983         mono_mb_emit_icon (mb, ~(ALLOC_ALIGN - 1));
6984         mono_mb_emit_byte (mb, CEE_AND);
6985         mono_mb_emit_stloc (mb, size_var);
6986
6987         /* if (size > MAX_SMALL_OBJ_SIZE) goto slowpath */
6988         if (atype != ATYPE_SMALL) {
6989                 mono_mb_emit_ldloc (mb, size_var);
6990                 mono_mb_emit_icon (mb, MAX_SMALL_OBJ_SIZE);
6991                 max_size_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_S);
6992         }
6993
6994         /*
6995          * We need to modify tlab_next, but the JIT only supports reading, so we read
6996          * another tls var holding its address instead.
6997          */
6998
6999         /* tlab_next_addr (local) = tlab_next_addr (TLS var) */
7000         tlab_next_addr_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7001         EMIT_TLS_ACCESS (mb, tlab_next_addr, tlab_next_addr_offset);
7002         mono_mb_emit_stloc (mb, tlab_next_addr_var);
7003
7004         /* p = (void**)tlab_next; */
7005         p_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7006         mono_mb_emit_ldloc (mb, tlab_next_addr_var);
7007         mono_mb_emit_byte (mb, CEE_LDIND_I);
7008         mono_mb_emit_stloc (mb, p_var);
7009         
7010         /* new_next = (char*)p + size; */
7011         new_next_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7012         mono_mb_emit_ldloc (mb, p_var);
7013         mono_mb_emit_ldloc (mb, size_var);
7014         mono_mb_emit_byte (mb, CEE_CONV_I);
7015         mono_mb_emit_byte (mb, CEE_ADD);
7016         mono_mb_emit_stloc (mb, new_next_var);
7017
7018         /* tlab_next = new_next */
7019         mono_mb_emit_ldloc (mb, tlab_next_addr_var);
7020         mono_mb_emit_ldloc (mb, new_next_var);
7021         mono_mb_emit_byte (mb, CEE_STIND_I);
7022
7023         /* if (G_LIKELY (new_next < tlab_temp_end)) */
7024         mono_mb_emit_ldloc (mb, new_next_var);
7025         EMIT_TLS_ACCESS (mb, tlab_temp_end, tlab_temp_end_offset);
7026         slowpath_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
7027
7028         /* Slowpath */
7029         if (atype != ATYPE_SMALL)
7030                 mono_mb_patch_short_branch (mb, max_size_branch);
7031
7032         mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
7033         mono_mb_emit_byte (mb, CEE_MONO_NOT_TAKEN);
7034
7035         /* FIXME: mono_gc_alloc_obj takes a 'size_t' as an argument, not an int32 */
7036         mono_mb_emit_ldarg (mb, 0);
7037         mono_mb_emit_ldloc (mb, size_var);
7038         if (atype == ATYPE_NORMAL || atype == ATYPE_SMALL) {
7039                 mono_mb_emit_icall (mb, mono_gc_alloc_obj);
7040         } else if (atype == ATYPE_VECTOR) {
7041                 mono_mb_emit_ldarg (mb, 1);
7042                 mono_mb_emit_icall (mb, mono_gc_alloc_vector);
7043         } else {
7044                 g_assert_not_reached ();
7045         }
7046         mono_mb_emit_byte (mb, CEE_RET);
7047
7048         /* Fastpath */
7049         mono_mb_patch_short_branch (mb, slowpath_branch);
7050
7051         /* FIXME: Memory barrier */
7052
7053         /* *p = vtable; */
7054         mono_mb_emit_ldloc (mb, p_var);
7055         mono_mb_emit_ldarg (mb, 0);
7056         mono_mb_emit_byte (mb, CEE_STIND_I);
7057
7058         if (atype == ATYPE_VECTOR) {
7059                 /* arr->max_length = max_length; */
7060                 mono_mb_emit_ldloc (mb, p_var);
7061                 mono_mb_emit_ldflda (mb, G_STRUCT_OFFSET (MonoArray, max_length));
7062                 mono_mb_emit_ldarg (mb, 1);
7063                 mono_mb_emit_byte (mb, CEE_STIND_I);
7064         }
7065
7066         /* return p */
7067         mono_mb_emit_ldloc (mb, p_var);
7068         mono_mb_emit_byte (mb, CEE_RET);
7069
7070         res = mono_mb_create_method (mb, csig, 8);
7071         mono_mb_free (mb);
7072         mono_method_get_header (res)->init_locals = FALSE;
7073
7074         info = mono_image_alloc0 (mono_defaults.corlib, sizeof (AllocatorWrapperInfo));
7075         info->gc_name = "sgen";
7076         info->alloc_type = atype;
7077         mono_marshal_set_wrapper_info (res, info);
7078
7079         return res;
7080 }
7081 #endif
7082
7083 const char *
7084 mono_gc_get_gc_name (void)
7085 {
7086         return "sgen";
7087 }
7088
7089 static MonoMethod* alloc_method_cache [ATYPE_NUM];
7090 static MonoMethod *write_barrier_method;
7091
7092 static gboolean
7093 mono_gc_is_critical_method (MonoMethod *method)
7094 {
7095         int i;
7096         if (method == write_barrier_method)
7097                 return TRUE;
7098
7099         for (i = 0; i < ATYPE_NUM; ++i)
7100                 if (method == alloc_method_cache [i])
7101                         return TRUE;
7102
7103         return FALSE;
7104 }
7105
7106 static gboolean
7107 is_ip_in_managed_allocator (MonoDomain *domain, gpointer ip)
7108 {
7109         MonoJitInfo *ji;
7110
7111         if (!mono_thread_internal_current ())
7112                 /* Happens during thread attach */
7113                 return FALSE;
7114
7115         if (!ip || !domain)
7116                 return FALSE;
7117         ji = mono_jit_info_table_find (domain, ip);
7118         if (!ji)
7119                 return FALSE;
7120
7121         return mono_gc_is_critical_method (ji->method);
7122 }
7123
7124 /*
7125  * Generate an allocator method implementing the fast path of mono_gc_alloc_obj ().
7126  * The signature of the called method is:
7127  *      object allocate (MonoVTable *vtable)
7128  */
7129 MonoMethod*
7130 mono_gc_get_managed_allocator (MonoVTable *vtable, gboolean for_box)
7131 {
7132 #ifdef MANAGED_ALLOCATION
7133         MonoClass *klass = vtable->klass;
7134
7135 #ifdef HAVE_KW_THREAD
7136         int tlab_next_offset = -1;
7137         int tlab_temp_end_offset = -1;
7138         MONO_THREAD_VAR_OFFSET (tlab_next, tlab_next_offset);
7139         MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
7140
7141         if (tlab_next_offset == -1 || tlab_temp_end_offset == -1)
7142                 return NULL;
7143 #endif
7144
7145         if (!mono_runtime_has_tls_get ())
7146                 return NULL;
7147         if (klass->instance_size > tlab_size)
7148                 return NULL;
7149         if (klass->has_finalize || klass->marshalbyref || (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS))
7150                 return NULL;
7151         if (klass->rank)
7152                 return NULL;
7153         if (klass->byval_arg.type == MONO_TYPE_STRING)
7154                 return NULL;
7155         if (collect_before_allocs)
7156                 return NULL;
7157
7158         if (ALIGN_TO (klass->instance_size, ALLOC_ALIGN) < MAX_SMALL_OBJ_SIZE)
7159                 return mono_gc_get_managed_allocator_by_type (ATYPE_SMALL);
7160         else
7161                 return mono_gc_get_managed_allocator_by_type (ATYPE_NORMAL);
7162 #else
7163         return NULL;
7164 #endif
7165 }
7166
7167 MonoMethod*
7168 mono_gc_get_managed_array_allocator (MonoVTable *vtable, int rank)
7169 {
7170 #ifdef MANAGED_ALLOCATION
7171         MonoClass *klass = vtable->klass;
7172
7173 #ifdef HAVE_KW_THREAD
7174         int tlab_next_offset = -1;
7175         int tlab_temp_end_offset = -1;
7176         MONO_THREAD_VAR_OFFSET (tlab_next, tlab_next_offset);
7177         MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
7178
7179         if (tlab_next_offset == -1 || tlab_temp_end_offset == -1)
7180                 return NULL;
7181 #endif
7182
7183         if (rank != 1)
7184                 return NULL;
7185         if (!mono_runtime_has_tls_get ())
7186                 return NULL;
7187         if (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS)
7188                 return NULL;
7189         if (collect_before_allocs)
7190                 return NULL;
7191         g_assert (!mono_class_has_finalizer (klass) && !klass->marshalbyref);
7192
7193         return mono_gc_get_managed_allocator_by_type (ATYPE_VECTOR);
7194 #else
7195         return NULL;
7196 #endif
7197 }
7198
7199 MonoMethod*
7200 mono_gc_get_managed_allocator_by_type (int atype)
7201 {
7202 #ifdef MANAGED_ALLOCATION
7203         MonoMethod *res;
7204
7205         if (!mono_runtime_has_tls_get ())
7206                 return NULL;
7207
7208         mono_loader_lock ();
7209         res = alloc_method_cache [atype];
7210         if (!res)
7211                 res = alloc_method_cache [atype] = create_allocator (atype);
7212         mono_loader_unlock ();
7213         return res;
7214 #else
7215         return NULL;
7216 #endif
7217 }
7218
7219 guint32
7220 mono_gc_get_managed_allocator_types (void)
7221 {
7222         return ATYPE_NUM;
7223 }
7224
7225
7226 MonoMethod*
7227 mono_gc_get_write_barrier (void)
7228 {
7229         MonoMethod *res;
7230         MonoMethodBuilder *mb;
7231         MonoMethodSignature *sig;
7232 #ifdef MANAGED_WBARRIER
7233         int label_no_wb_1, label_no_wb_2, label_no_wb_3, label_no_wb_4, label_need_wb, label_slow_path;
7234 #ifndef SGEN_ALIGN_NURSERY
7235         int label_continue_1, label_continue_2, label_no_wb_5;
7236         int dereferenced_var;
7237 #endif
7238         int buffer_var, buffer_index_var, dummy_var;
7239
7240 #ifdef HAVE_KW_THREAD
7241         int stack_end_offset = -1, store_remset_buffer_offset = -1;
7242         int store_remset_buffer_index_offset = -1, store_remset_buffer_index_addr_offset = -1;
7243
7244         MONO_THREAD_VAR_OFFSET (stack_end, stack_end_offset);
7245         g_assert (stack_end_offset != -1);
7246         MONO_THREAD_VAR_OFFSET (store_remset_buffer, store_remset_buffer_offset);
7247         g_assert (store_remset_buffer_offset != -1);
7248         MONO_THREAD_VAR_OFFSET (store_remset_buffer_index, store_remset_buffer_index_offset);
7249         g_assert (store_remset_buffer_index_offset != -1);
7250         MONO_THREAD_VAR_OFFSET (store_remset_buffer_index_addr, store_remset_buffer_index_addr_offset);
7251         g_assert (store_remset_buffer_index_addr_offset != -1);
7252 #endif
7253 #endif
7254
7255         g_assert (!use_cardtable);
7256
7257         // FIXME: Maybe create a separate version for ctors (the branch would be
7258         // correctly predicted more times)
7259         if (write_barrier_method)
7260                 return write_barrier_method;
7261
7262         /* Create the IL version of mono_gc_barrier_generic_store () */
7263         sig = mono_metadata_signature_alloc (mono_defaults.corlib, 1);
7264         sig->ret = &mono_defaults.void_class->byval_arg;
7265         sig->params [0] = &mono_defaults.int_class->byval_arg;
7266
7267         mb = mono_mb_new (mono_defaults.object_class, "wbarrier", MONO_WRAPPER_WRITE_BARRIER);
7268
7269 #ifdef MANAGED_WBARRIER
7270         if (mono_runtime_has_tls_get ()) {
7271 #ifdef SGEN_ALIGN_NURSERY
7272                 // if (ptr_in_nursery (ptr)) return;
7273                 /*
7274                  * Masking out the bits might be faster, but we would have to use 64 bit
7275                  * immediates, which might be slower.
7276                  */
7277                 mono_mb_emit_ldarg (mb, 0);
7278                 mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
7279                 mono_mb_emit_byte (mb, CEE_SHR_UN);
7280                 mono_mb_emit_icon (mb, (mword)nursery_start >> DEFAULT_NURSERY_BITS);
7281                 label_no_wb_1 = mono_mb_emit_branch (mb, CEE_BEQ);
7282
7283                 // if (!ptr_in_nursery (*ptr)) return;
7284                 mono_mb_emit_ldarg (mb, 0);
7285                 mono_mb_emit_byte (mb, CEE_LDIND_I);
7286                 mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
7287                 mono_mb_emit_byte (mb, CEE_SHR_UN);
7288                 mono_mb_emit_icon (mb, (mword)nursery_start >> DEFAULT_NURSERY_BITS);
7289                 label_no_wb_2 = mono_mb_emit_branch (mb, CEE_BNE_UN);
7290 #else
7291
7292                 // if (ptr < (nursery_start)) goto continue;
7293                 mono_mb_emit_ldarg (mb, 0);
7294                 mono_mb_emit_ptr (mb, (gpointer) nursery_start);
7295                 label_continue_1 = mono_mb_emit_branch (mb, CEE_BLT);
7296
7297                 // if (ptr >= nursery_end)) goto continue;
7298                 mono_mb_emit_ldarg (mb, 0);
7299                 mono_mb_emit_ptr (mb, (gpointer) nursery_end);
7300                 label_continue_2 = mono_mb_emit_branch (mb, CEE_BGE);
7301
7302                 // Otherwise return
7303                 label_no_wb_1 = mono_mb_emit_branch (mb, CEE_BR);
7304
7305                 // continue:
7306                 mono_mb_patch_branch (mb, label_continue_1);
7307                 mono_mb_patch_branch (mb, label_continue_2);
7308
7309                 // Dereference and store in local var
7310                 dereferenced_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7311                 mono_mb_emit_ldarg (mb, 0);
7312                 mono_mb_emit_byte (mb, CEE_LDIND_I);
7313                 mono_mb_emit_stloc (mb, dereferenced_var);
7314
7315                 // if (*ptr < nursery_start) return;
7316                 mono_mb_emit_ldloc (mb, dereferenced_var);
7317                 mono_mb_emit_ptr (mb, (gpointer) nursery_start);
7318                 label_no_wb_2 = mono_mb_emit_branch (mb, CEE_BLT);
7319
7320                 // if (*ptr >= nursery_end) return;
7321                 mono_mb_emit_ldloc (mb, dereferenced_var);
7322                 mono_mb_emit_ptr (mb, (gpointer) nursery_end);
7323                 label_no_wb_5 = mono_mb_emit_branch (mb, CEE_BGE);
7324
7325 #endif 
7326                 // if (ptr >= stack_end) goto need_wb;
7327                 mono_mb_emit_ldarg (mb, 0);
7328                 EMIT_TLS_ACCESS (mb, stack_end, stack_end_offset);
7329                 label_need_wb = mono_mb_emit_branch (mb, CEE_BGE_UN);
7330
7331                 // if (ptr >= stack_start) return;
7332                 dummy_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7333                 mono_mb_emit_ldarg (mb, 0);
7334                 mono_mb_emit_ldloc_addr (mb, dummy_var);
7335                 label_no_wb_3 = mono_mb_emit_branch (mb, CEE_BGE_UN);
7336
7337                 // need_wb:
7338                 mono_mb_patch_branch (mb, label_need_wb);
7339
7340                 // buffer = STORE_REMSET_BUFFER;
7341                 buffer_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7342                 EMIT_TLS_ACCESS (mb, store_remset_buffer, store_remset_buffer_offset);
7343                 mono_mb_emit_stloc (mb, buffer_var);
7344
7345                 // buffer_index = STORE_REMSET_BUFFER_INDEX;
7346                 buffer_index_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7347                 EMIT_TLS_ACCESS (mb, store_remset_buffer_index, store_remset_buffer_index_offset);
7348                 mono_mb_emit_stloc (mb, buffer_index_var);
7349
7350                 // if (buffer [buffer_index] == ptr) return;
7351                 mono_mb_emit_ldloc (mb, buffer_var);
7352                 mono_mb_emit_ldloc (mb, buffer_index_var);
7353                 g_assert (sizeof (gpointer) == 4 || sizeof (gpointer) == 8);
7354                 mono_mb_emit_icon (mb, sizeof (gpointer) == 4 ? 2 : 3);
7355                 mono_mb_emit_byte (mb, CEE_SHL);
7356                 mono_mb_emit_byte (mb, CEE_ADD);
7357                 mono_mb_emit_byte (mb, CEE_LDIND_I);
7358                 mono_mb_emit_ldarg (mb, 0);
7359                 label_no_wb_4 = mono_mb_emit_branch (mb, CEE_BEQ);
7360
7361                 // ++buffer_index;
7362                 mono_mb_emit_ldloc (mb, buffer_index_var);
7363                 mono_mb_emit_icon (mb, 1);
7364                 mono_mb_emit_byte (mb, CEE_ADD);
7365                 mono_mb_emit_stloc (mb, buffer_index_var);
7366
7367                 // if (buffer_index >= STORE_REMSET_BUFFER_SIZE) goto slow_path;
7368                 mono_mb_emit_ldloc (mb, buffer_index_var);
7369                 mono_mb_emit_icon (mb, STORE_REMSET_BUFFER_SIZE);
7370                 label_slow_path = mono_mb_emit_branch (mb, CEE_BGE);
7371
7372                 // buffer [buffer_index] = ptr;
7373                 mono_mb_emit_ldloc (mb, buffer_var);
7374                 mono_mb_emit_ldloc (mb, buffer_index_var);
7375                 g_assert (sizeof (gpointer) == 4 || sizeof (gpointer) == 8);
7376                 mono_mb_emit_icon (mb, sizeof (gpointer) == 4 ? 2 : 3);
7377                 mono_mb_emit_byte (mb, CEE_SHL);
7378                 mono_mb_emit_byte (mb, CEE_ADD);
7379                 mono_mb_emit_ldarg (mb, 0);
7380                 mono_mb_emit_byte (mb, CEE_STIND_I);
7381
7382                 // STORE_REMSET_BUFFER_INDEX = buffer_index;
7383                 EMIT_TLS_ACCESS (mb, store_remset_buffer_index_addr, store_remset_buffer_index_addr_offset);
7384                 mono_mb_emit_ldloc (mb, buffer_index_var);
7385                 mono_mb_emit_byte (mb, CEE_STIND_I);
7386
7387                 // return;
7388                 mono_mb_patch_branch (mb, label_no_wb_1);
7389                 mono_mb_patch_branch (mb, label_no_wb_2);
7390                 mono_mb_patch_branch (mb, label_no_wb_3);
7391                 mono_mb_patch_branch (mb, label_no_wb_4);
7392 #ifndef SGEN_ALIGN_NURSERY
7393                 mono_mb_patch_branch (mb, label_no_wb_5);
7394 #endif
7395                 mono_mb_emit_byte (mb, CEE_RET);
7396
7397                 // slow path
7398                 mono_mb_patch_branch (mb, label_slow_path);
7399         }
7400 #endif
7401
7402         mono_mb_emit_ldarg (mb, 0);
7403         mono_mb_emit_icall (mb, mono_gc_wbarrier_generic_nostore);
7404         mono_mb_emit_byte (mb, CEE_RET);
7405
7406         res = mono_mb_create_method (mb, sig, 16);
7407         mono_mb_free (mb);
7408
7409         mono_loader_lock ();
7410         if (write_barrier_method) {
7411                 /* Already created */
7412                 mono_free_method (res);
7413         } else {
7414                 /* double-checked locking */
7415                 mono_memory_barrier ();
7416                 write_barrier_method = res;
7417         }
7418         mono_loader_unlock ();
7419
7420         return write_barrier_method;
7421 }
7422
7423 char*
7424 mono_gc_get_description (void)
7425 {
7426         return g_strdup ("sgen");
7427 }
7428
7429 void
7430 mono_gc_set_desktop_mode (void)
7431 {
7432 }
7433
7434 gboolean
7435 mono_gc_is_moving (void)
7436 {
7437         return TRUE;
7438 }
7439
7440 gboolean
7441 mono_gc_is_disabled (void)
7442 {
7443         return FALSE;
7444 }
7445
7446 void
7447 mono_sgen_debug_printf (int level, const char *format, ...)
7448 {
7449         va_list ap;
7450
7451         if (level > gc_debug_level)
7452                 return;
7453
7454         va_start (ap, format);
7455         vfprintf (gc_debug_file, format, ap);
7456         va_end (ap);
7457 }
7458
7459 FILE*
7460 mono_sgen_get_logfile (void)
7461 {
7462         return gc_debug_file;
7463 }
7464
7465 #ifdef HOST_WIN32
7466 BOOL APIENTRY mono_gc_dllmain (HMODULE module_handle, DWORD reason, LPVOID reserved)
7467 {
7468         return TRUE;
7469 }
7470 #endif
7471
7472 NurseryClearPolicy
7473 mono_sgen_get_nursery_clear_policy (void)
7474 {
7475         return nursery_clear_policy;
7476 }
7477
7478 MonoVTable*
7479 mono_sgen_get_array_fill_vtable (void)
7480 {
7481         return array_fill_vtable;
7482 }
7483
7484 #endif /* HAVE_SGEN_GC */