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