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