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