Facilitate the merge
[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 (int generation);
735 static int restart_world (int generation);
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         mono_perfcounters->gc_collections0++;
2583
2584         current_collection_generation = GENERATION_NURSERY;
2585
2586         binary_protocol_collection (GENERATION_NURSERY);
2587         check_scan_starts ();
2588
2589         degraded_mode = 0;
2590         orig_nursery_next = nursery_next;
2591         nursery_next = MAX (nursery_next, nursery_last_pinned_end);
2592         /* FIXME: optimize later to use the higher address where an object can be present */
2593         nursery_next = MAX (nursery_next, nursery_real_end);
2594
2595         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)));
2596         max_garbage_amount = nursery_next - nursery_start;
2597         g_assert (nursery_section->size >= max_garbage_amount);
2598
2599         /* world must be stopped already */
2600         TV_GETTIME (all_atv);
2601         atv = all_atv;
2602
2603         /* Pinning depends on this */
2604         clear_nursery_fragments (orig_nursery_next);
2605
2606         TV_GETTIME (btv);
2607         time_minor_pre_collection_fragment_clear += TV_ELAPSED_MS (atv, btv);
2608
2609         if (xdomain_checks)
2610                 check_for_xdomain_refs ();
2611
2612         nursery_section->next_data = nursery_next;
2613
2614         major_collector.start_nursery_collection ();
2615
2616         gray_object_queue_init (&gray_queue, mono_sgen_get_unmanaged_allocator ());
2617
2618         num_minor_gcs++;
2619         mono_stats.minor_gc_count ++;
2620
2621         global_remset_cache_clear ();
2622
2623         /* pin from pinned handles */
2624         init_pinning ();
2625         mono_profiler_gc_event (MONO_GC_EVENT_MARK_START, 0);
2626         pin_from_roots (nursery_start, nursery_next);
2627         /* identify pinned objects */
2628         optimize_pin_queue (0);
2629         next_pin_slot = pin_objects_from_addresses (nursery_section, pin_queue, pin_queue + next_pin_slot, nursery_start, nursery_next, &gray_queue);
2630         nursery_section->pin_queue_start = pin_queue;
2631         nursery_section->pin_queue_num_entries = next_pin_slot;
2632         TV_GETTIME (atv);
2633         time_minor_pinning += TV_ELAPSED_MS (btv, atv);
2634         DEBUG (2, fprintf (gc_debug_file, "Finding pinned pointers: %d in %d usecs\n", next_pin_slot, TV_ELAPSED (btv, atv)));
2635         DEBUG (4, fprintf (gc_debug_file, "Start scan with %d pinned objects\n", next_pin_slot));
2636
2637         if (consistency_check_at_minor_collection)
2638                 check_consistency ();
2639
2640         /* 
2641          * walk all the roots and copy the young objects to the old generation,
2642          * starting from to_space
2643          */
2644
2645         scan_from_remsets (nursery_start, nursery_next, &gray_queue);
2646         /* we don't have complete write barrier yet, so we scan all the old generation sections */
2647         TV_GETTIME (btv);
2648         time_minor_scan_remsets += TV_ELAPSED_MS (atv, btv);
2649         DEBUG (2, fprintf (gc_debug_file, "Old generation scan: %d usecs\n", TV_ELAPSED (atv, btv)));
2650
2651         if (use_cardtable) {
2652                 atv = btv;
2653                 card_tables_collect_stats (TRUE);
2654                 scan_from_card_tables (nursery_start, nursery_next, &gray_queue);
2655                 TV_GETTIME (btv);
2656                 time_minor_scan_card_table += TV_ELAPSED_MS (atv, btv);
2657         }
2658
2659         drain_gray_stack (&gray_queue);
2660
2661         TV_GETTIME (atv);
2662         time_minor_scan_pinned += TV_ELAPSED_MS (btv, atv);
2663         /* registered roots, this includes static fields */
2664         scan_from_registered_roots (major_collector.copy_object, nursery_start, nursery_next, ROOT_TYPE_NORMAL, &gray_queue);
2665         scan_from_registered_roots (major_collector.copy_object, nursery_start, nursery_next, ROOT_TYPE_WBARRIER, &gray_queue);
2666         TV_GETTIME (btv);
2667         time_minor_scan_registered_roots += TV_ELAPSED_MS (atv, btv);
2668         /* thread data */
2669         scan_thread_data (nursery_start, nursery_next, TRUE);
2670         TV_GETTIME (atv);
2671         time_minor_scan_thread_data += TV_ELAPSED_MS (btv, atv);
2672         btv = atv;
2673
2674         finish_gray_stack (nursery_start, nursery_next, GENERATION_NURSERY, &gray_queue);
2675         TV_GETTIME (atv);
2676         time_minor_finish_gray_stack += TV_ELAPSED_MS (btv, atv);
2677         mono_profiler_gc_event (MONO_GC_EVENT_MARK_END, 0);
2678
2679         /* walk the pin_queue, build up the fragment list of free memory, unmark
2680          * pinned objects as we go, memzero() the empty fragments so they are ready for the
2681          * next allocations.
2682          */
2683         mono_profiler_gc_event (MONO_GC_EVENT_RECLAIM_START, 0);
2684         build_nursery_fragments (pin_queue, next_pin_slot);
2685         mono_profiler_gc_event (MONO_GC_EVENT_RECLAIM_END, 0);
2686         TV_GETTIME (btv);
2687         time_minor_fragment_creation += TV_ELAPSED_MS (atv, btv);
2688         DEBUG (2, fprintf (gc_debug_file, "Fragment creation: %d usecs, %lu bytes available\n", TV_ELAPSED (atv, btv), (unsigned long)fragment_total));
2689
2690         if (consistency_check_at_minor_collection)
2691                 check_major_refs ();
2692
2693         major_collector.finish_nursery_collection ();
2694
2695         TV_GETTIME (all_btv);
2696         mono_stats.minor_gc_time_usecs += TV_ELAPSED (all_atv, all_btv);
2697
2698         if (heap_dump_file)
2699                 dump_heap ("minor", num_minor_gcs - 1, NULL);
2700
2701         /* prepare the pin queue for the next collection */
2702         last_num_pinned = next_pin_slot;
2703         next_pin_slot = 0;
2704         if (fin_ready_list || critical_fin_list) {
2705                 DEBUG (4, fprintf (gc_debug_file, "Finalizer-thread wakeup: ready %d\n", num_ready_finalizers));
2706                 mono_gc_finalize_notify ();
2707         }
2708         pin_stats_reset ();
2709
2710         g_assert (gray_object_queue_is_empty (&gray_queue));
2711
2712         if (use_cardtable)
2713                 card_tables_collect_stats (FALSE);
2714
2715         check_scan_starts ();
2716
2717         binary_protocol_flush_buffers (FALSE);
2718
2719         current_collection_generation = -1;
2720
2721         return need_major_collection ();
2722 }
2723
2724 static void
2725 major_do_collection (const char *reason)
2726 {
2727         LOSObject *bigobj, *prevbo;
2728         TV_DECLARE (all_atv);
2729         TV_DECLARE (all_btv);
2730         TV_DECLARE (atv);
2731         TV_DECLARE (btv);
2732         /* FIXME: only use these values for the precise scan
2733          * note that to_space pointers should be excluded anyway...
2734          */
2735         char *heap_start = NULL;
2736         char *heap_end = (char*)-1;
2737         int old_num_major_sections = major_collector.get_num_major_sections ();
2738         int num_major_sections, num_major_sections_saved, save_target, allowance_target;
2739         mword los_memory_saved, los_memory_alloced, old_los_memory_usage;
2740
2741         mono_perfcounters->gc_collections1++;
2742
2743         /*
2744          * A domain could have been freed, resulting in
2745          * los_memory_usage being less than last_los_memory_usage.
2746          */
2747         los_memory_alloced = los_memory_usage - MIN (last_los_memory_usage, los_memory_usage);
2748         old_los_memory_usage = los_memory_usage;
2749
2750         //count_ref_nonref_objs ();
2751         //consistency_check ();
2752
2753         binary_protocol_collection (GENERATION_OLD);
2754         check_scan_starts ();
2755         gray_object_queue_init (&gray_queue, mono_sgen_get_unmanaged_allocator ());
2756         if (major_collector.is_parallel)
2757                 gray_object_queue_init (&workers_distribute_gray_queue, mono_sgen_get_unmanaged_allocator ());
2758
2759         degraded_mode = 0;
2760         DEBUG (1, fprintf (gc_debug_file, "Start major collection %d\n", num_major_gcs));
2761         num_major_gcs++;
2762         mono_stats.major_gc_count ++;
2763
2764         /* world must be stopped already */
2765         TV_GETTIME (all_atv);
2766         atv = all_atv;
2767
2768         /* Pinning depends on this */
2769         clear_nursery_fragments (nursery_next);
2770
2771         TV_GETTIME (btv);
2772         time_major_pre_collection_fragment_clear += TV_ELAPSED_MS (atv, btv);
2773
2774         if (xdomain_checks)
2775                 check_for_xdomain_refs ();
2776
2777         nursery_section->next_data = nursery_real_end;
2778         /* we should also coalesce scanning from sections close to each other
2779          * and deal with pointers outside of the sections later.
2780          */
2781         /* The remsets are not useful for a major collection */
2782         clear_remsets ();
2783         global_remset_cache_clear ();
2784         if (use_cardtable)
2785                 card_table_clear ();
2786
2787         TV_GETTIME (atv);
2788         init_pinning ();
2789         DEBUG (6, fprintf (gc_debug_file, "Collecting pinned addresses\n"));
2790         pin_from_roots ((void*)lowest_heap_address, (void*)highest_heap_address);
2791         optimize_pin_queue (0);
2792
2793         /*
2794          * pin_queue now contains all candidate pointers, sorted and
2795          * uniqued.  We must do two passes now to figure out which
2796          * objects are pinned.
2797          *
2798          * The first is to find within the pin_queue the area for each
2799          * section.  This requires that the pin_queue be sorted.  We
2800          * also process the LOS objects and pinned chunks here.
2801          *
2802          * The second, destructive, pass is to reduce the section
2803          * areas to pointers to the actually pinned objects.
2804          */
2805         DEBUG (6, fprintf (gc_debug_file, "Pinning from sections\n"));
2806         /* first pass for the sections */
2807         mono_sgen_find_section_pin_queue_start_end (nursery_section);
2808         major_collector.find_pin_queue_start_ends (WORKERS_DISTRIBUTE_GRAY_QUEUE);
2809         /* identify possible pointers to the insize of large objects */
2810         DEBUG (6, fprintf (gc_debug_file, "Pinning from large objects\n"));
2811         for (bigobj = los_object_list; bigobj; bigobj = bigobj->next) {
2812                 int dummy;
2813                 if (mono_sgen_find_optimized_pin_queue_area (bigobj->data, (char*)bigobj->data + bigobj->size, &dummy)) {
2814                         pin_object (bigobj->data);
2815                         /* FIXME: only enqueue if object has references */
2816                         GRAY_OBJECT_ENQUEUE (WORKERS_DISTRIBUTE_GRAY_QUEUE, bigobj->data);
2817                         if (heap_dump_file)
2818                                 mono_sgen_pin_stats_register_object ((char*) bigobj->data, safe_object_get_size ((MonoObject*) bigobj->data));
2819                         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));
2820                 }
2821         }
2822         /* second pass for the sections */
2823         mono_sgen_pin_objects_in_section (nursery_section, WORKERS_DISTRIBUTE_GRAY_QUEUE);
2824         major_collector.pin_objects (WORKERS_DISTRIBUTE_GRAY_QUEUE);
2825
2826         TV_GETTIME (btv);
2827         time_major_pinning += TV_ELAPSED_MS (atv, btv);
2828         DEBUG (2, fprintf (gc_debug_file, "Finding pinned pointers: %d in %d usecs\n", next_pin_slot, TV_ELAPSED (atv, btv)));
2829         DEBUG (4, fprintf (gc_debug_file, "Start scan with %d pinned objects\n", next_pin_slot));
2830
2831         major_collector.init_to_space ();
2832
2833         workers_start_all_workers (1);
2834
2835         TV_GETTIME (atv);
2836         time_major_scan_pinned += TV_ELAPSED_MS (btv, atv);
2837
2838         /* registered roots, this includes static fields */
2839         scan_from_registered_roots (major_collector.copy_or_mark_object, heap_start, heap_end, ROOT_TYPE_NORMAL, WORKERS_DISTRIBUTE_GRAY_QUEUE);
2840         scan_from_registered_roots (major_collector.copy_or_mark_object, heap_start, heap_end, ROOT_TYPE_WBARRIER, WORKERS_DISTRIBUTE_GRAY_QUEUE);
2841         TV_GETTIME (btv);
2842         time_major_scan_registered_roots += TV_ELAPSED_MS (atv, btv);
2843
2844         /* Threads */
2845         /* FIXME: This is the wrong place for this, because it does
2846            pinning */
2847         scan_thread_data (heap_start, heap_end, TRUE);
2848         TV_GETTIME (atv);
2849         time_major_scan_thread_data += TV_ELAPSED_MS (btv, atv);
2850
2851         TV_GETTIME (btv);
2852         time_major_scan_alloc_pinned += TV_ELAPSED_MS (atv, btv);
2853
2854         /* scan the list of objects ready for finalization */
2855         scan_finalizer_entries (major_collector.copy_or_mark_object, fin_ready_list, WORKERS_DISTRIBUTE_GRAY_QUEUE);
2856         scan_finalizer_entries (major_collector.copy_or_mark_object, critical_fin_list, WORKERS_DISTRIBUTE_GRAY_QUEUE);
2857         TV_GETTIME (atv);
2858         time_major_scan_finalized += TV_ELAPSED_MS (btv, atv);
2859         DEBUG (2, fprintf (gc_debug_file, "Root scan: %d usecs\n", TV_ELAPSED (btv, atv)));
2860
2861         TV_GETTIME (btv);
2862         time_major_scan_big_objects += TV_ELAPSED_MS (atv, btv);
2863
2864         if (major_collector.is_parallel) {
2865                 while (!gray_object_queue_is_empty (WORKERS_DISTRIBUTE_GRAY_QUEUE)) {
2866                         workers_distribute_gray_queue_sections ();
2867                         usleep (2000);
2868                 }
2869         }
2870         workers_change_num_working (-1);
2871         workers_join ();
2872
2873         if (major_collector.is_parallel)
2874                 g_assert (gray_object_queue_is_empty (&gray_queue));
2875
2876         /* all the objects in the heap */
2877         finish_gray_stack (heap_start, heap_end, GENERATION_OLD, &gray_queue);
2878         TV_GETTIME (atv);
2879         time_major_finish_gray_stack += TV_ELAPSED_MS (btv, atv);
2880
2881         /* sweep the big objects list */
2882         prevbo = NULL;
2883         for (bigobj = los_object_list; bigobj;) {
2884                 if (object_is_pinned (bigobj->data)) {
2885                         unpin_object (bigobj->data);
2886                 } else {
2887                         LOSObject *to_free;
2888                         /* not referenced anywhere, so we can free it */
2889                         if (prevbo)
2890                                 prevbo->next = bigobj->next;
2891                         else
2892                                 los_object_list = bigobj->next;
2893                         to_free = bigobj;
2894                         bigobj = bigobj->next;
2895                         free_large_object (to_free);
2896                         continue;
2897                 }
2898                 prevbo = bigobj;
2899                 bigobj = bigobj->next;
2900         }
2901
2902         TV_GETTIME (btv);
2903         time_major_free_bigobjs += TV_ELAPSED_MS (atv, btv);
2904
2905         los_sweep ();
2906
2907         TV_GETTIME (atv);
2908         time_major_los_sweep += TV_ELAPSED_MS (btv, atv);
2909
2910         major_collector.sweep ();
2911
2912         TV_GETTIME (btv);
2913         time_major_sweep += TV_ELAPSED_MS (atv, btv);
2914
2915         /* walk the pin_queue, build up the fragment list of free memory, unmark
2916          * pinned objects as we go, memzero() the empty fragments so they are ready for the
2917          * next allocations.
2918          */
2919         build_nursery_fragments (nursery_section->pin_queue_start, nursery_section->pin_queue_num_entries);
2920
2921         TV_GETTIME (atv);
2922         time_major_fragment_creation += TV_ELAPSED_MS (btv, atv);
2923
2924         TV_GETTIME (all_btv);
2925         mono_stats.major_gc_time_usecs += TV_ELAPSED (all_atv, all_btv);
2926
2927         if (heap_dump_file)
2928                 dump_heap ("major", num_major_gcs - 1, reason);
2929
2930         /* prepare the pin queue for the next collection */
2931         next_pin_slot = 0;
2932         if (fin_ready_list || critical_fin_list) {
2933                 DEBUG (4, fprintf (gc_debug_file, "Finalizer-thread wakeup: ready %d\n", num_ready_finalizers));
2934                 mono_gc_finalize_notify ();
2935         }
2936         pin_stats_reset ();
2937
2938         g_assert (gray_object_queue_is_empty (&gray_queue));
2939
2940         num_major_sections = major_collector.get_num_major_sections ();
2941
2942         num_major_sections_saved = MAX (old_num_major_sections - num_major_sections, 0);
2943         los_memory_saved = MAX (old_los_memory_usage - los_memory_usage, 1);
2944
2945         save_target = ((num_major_sections * major_collector.section_size) + los_memory_saved) / 2;
2946         /*
2947          * We aim to allow the allocation of as many sections as is
2948          * necessary to reclaim save_target sections in the next
2949          * collection.  We assume the collection pattern won't change.
2950          * In the last cycle, we had num_major_sections_saved for
2951          * minor_collection_sections_alloced.  Assuming things won't
2952          * change, this must be the same ratio as save_target for
2953          * allowance_target, i.e.
2954          *
2955          *    num_major_sections_saved            save_target
2956          * --------------------------------- == ----------------
2957          * minor_collection_sections_alloced    allowance_target
2958          *
2959          * hence:
2960          */
2961         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));
2962
2963         minor_collection_allowance = MAX (MIN (allowance_target, num_major_sections * major_collector.section_size + los_memory_usage), MIN_MINOR_COLLECTION_ALLOWANCE);
2964
2965         minor_collection_sections_alloced = 0;
2966         last_los_memory_usage = los_memory_usage;
2967
2968         major_collector.finish_major_collection ();
2969
2970         check_scan_starts ();
2971
2972         binary_protocol_flush_buffers (FALSE);
2973
2974         //consistency_check ();
2975 }
2976
2977 static void
2978 major_collection (const char *reason)
2979 {
2980         if (g_getenv ("MONO_GC_NO_MAJOR")) {
2981                 collect_nursery (0);
2982                 return;
2983         }
2984
2985         current_collection_generation = GENERATION_OLD;
2986         major_do_collection (reason);
2987         current_collection_generation = -1;
2988 }
2989
2990 /*
2991  * When deciding if it's better to collect or to expand, keep track
2992  * of how much garbage was reclaimed with the last collection: if it's too
2993  * little, expand.
2994  * This is called when we could not allocate a small object.
2995  */
2996 static void __attribute__((noinline))
2997 minor_collect_or_expand_inner (size_t size)
2998 {
2999         int do_minor_collection = 1;
3000
3001         g_assert (nursery_section);
3002         if (do_minor_collection) {
3003                 mono_profiler_gc_event (MONO_GC_EVENT_START, 0);
3004                 stop_world (0);
3005                 if (collect_nursery (size)) {
3006                         mono_profiler_gc_event (MONO_GC_EVENT_START, 1);
3007                         major_collection ("minor overflow");
3008                         /* keep events symmetric */
3009                         mono_profiler_gc_event (MONO_GC_EVENT_END, 1);
3010                 }
3011                 DEBUG (2, fprintf (gc_debug_file, "Heap size: %lu, LOS size: %lu\n", (unsigned long)total_alloc, (unsigned long)los_memory_usage));
3012                 restart_world (0);
3013                 /* this also sets the proper pointers for the next allocation */
3014                 if (!search_fragment_for_size (size)) {
3015                         int i;
3016                         /* TypeBuilder and MonoMethod are killing mcs with fragmentation */
3017                         DEBUG (1, fprintf (gc_debug_file, "nursery collection didn't find enough room for %zd alloc (%d pinned)\n", size, last_num_pinned));
3018                         for (i = 0; i < last_num_pinned; ++i) {
3019                                 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])));
3020                         }
3021                         degraded_mode = 1;
3022                 }
3023                 mono_profiler_gc_event (MONO_GC_EVENT_END, 0);
3024         }
3025         //report_internal_mem_usage ();
3026 }
3027
3028 /*
3029  * ######################################################################
3030  * ########  Memory allocation from the OS
3031  * ######################################################################
3032  * This section of code deals with getting memory from the OS and
3033  * allocating memory for GC-internal data structures.
3034  * Internal memory can be handled with a freelist for small objects.
3035  */
3036
3037 /*
3038  * Debug reporting.
3039  */
3040 G_GNUC_UNUSED static void
3041 report_internal_mem_usage (void)
3042 {
3043         printf ("Internal memory usage:\n");
3044         mono_sgen_report_internal_mem_usage ();
3045         printf ("Pinned memory usage:\n");
3046         major_collector.report_pinned_memory_usage ();
3047 }
3048
3049 /*
3050  * Allocate a big chunk of memory from the OS (usually 64KB to several megabytes).
3051  * This must not require any lock.
3052  */
3053 void*
3054 mono_sgen_alloc_os_memory (size_t size, int activate)
3055 {
3056         void *ptr;
3057         unsigned long prot_flags = activate? MONO_MMAP_READ|MONO_MMAP_WRITE: MONO_MMAP_NONE;
3058
3059         prot_flags |= MONO_MMAP_PRIVATE | MONO_MMAP_ANON;
3060         size += pagesize - 1;
3061         size &= ~(pagesize - 1);
3062         ptr = mono_valloc (0, size, prot_flags);
3063         /* FIXME: CAS */
3064         total_alloc += size;
3065         return ptr;
3066 }
3067
3068 /*
3069  * Free the memory returned by mono_sgen_alloc_os_memory (), returning it to the OS.
3070  */
3071 void
3072 mono_sgen_free_os_memory (void *addr, size_t size)
3073 {
3074         mono_vfree (addr, size);
3075
3076         size += pagesize - 1;
3077         size &= ~(pagesize - 1);
3078         /* FIXME: CAS */
3079         total_alloc -= size;
3080 }
3081
3082 /*
3083  * ######################################################################
3084  * ########  Object allocation
3085  * ######################################################################
3086  * This section of code deals with allocating memory for objects.
3087  * There are several ways:
3088  * *) allocate large objects
3089  * *) allocate normal objects
3090  * *) fast lock-free allocation
3091  * *) allocation of pinned objects
3092  */
3093
3094 static void
3095 setup_fragment (Fragment *frag, Fragment *prev, size_t size)
3096 {
3097         /* remove from the list */
3098         if (prev)
3099                 prev->next = frag->next;
3100         else
3101                 nursery_fragments = frag->next;
3102         nursery_next = frag->fragment_start;
3103         nursery_frag_real_end = frag->fragment_end;
3104
3105         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));
3106         frag->next = fragment_freelist;
3107         fragment_freelist = frag;
3108 }
3109
3110 /* check if we have a suitable fragment in nursery_fragments to be able to allocate
3111  * an object of size @size
3112  * Return FALSE if not found (which means we need a collection)
3113  */
3114 static gboolean
3115 search_fragment_for_size (size_t size)
3116 {
3117         Fragment *frag, *prev;
3118         DEBUG (4, fprintf (gc_debug_file, "Searching nursery fragment %p, size: %zd\n", nursery_frag_real_end, size));
3119
3120         if (nursery_frag_real_end > nursery_next && nursery_clear_policy == CLEAR_AT_TLAB_CREATION)
3121                 /* Clear the remaining space, pinning depends on this */
3122                 memset (nursery_next, 0, nursery_frag_real_end - nursery_next);
3123
3124         prev = NULL;
3125         for (frag = nursery_fragments; frag; frag = frag->next) {
3126                 if (size <= (frag->fragment_end - frag->fragment_start)) {
3127                         setup_fragment (frag, prev, size);
3128                         return TRUE;
3129                 }
3130                 prev = frag;
3131         }
3132         return FALSE;
3133 }
3134
3135 /*
3136  * Same as search_fragment_for_size but if search for @desired_size fails, try to satisfy @minimum_size.
3137  * This improves nursery usage.
3138  */
3139 static int
3140 search_fragment_for_size_range (size_t desired_size, size_t minimum_size)
3141 {
3142         Fragment *frag, *prev, *min_prev;
3143         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));
3144
3145         if (nursery_frag_real_end > nursery_next && nursery_clear_policy == CLEAR_AT_TLAB_CREATION)
3146                 /* Clear the remaining space, pinning depends on this */
3147                 memset (nursery_next, 0, nursery_frag_real_end - nursery_next);
3148
3149         min_prev = GINT_TO_POINTER (-1);
3150         prev = NULL;
3151
3152         for (frag = nursery_fragments; frag; frag = frag->next) {
3153                 int frag_size = frag->fragment_end - frag->fragment_start;
3154                 if (desired_size <= frag_size) {
3155                         setup_fragment (frag, prev, desired_size);
3156                         return desired_size;
3157                 }
3158                 if (minimum_size <= frag_size)
3159                         min_prev = prev;
3160
3161                 prev = frag;
3162         }
3163
3164         if (min_prev != GINT_TO_POINTER (-1)) {
3165                 int frag_size;
3166                 if (min_prev)
3167                         frag = min_prev->next;
3168                 else
3169                         frag = nursery_fragments;
3170
3171                 frag_size = frag->fragment_end - frag->fragment_start;
3172                 HEAVY_STAT (++stat_wasted_fragments_used);
3173                 HEAVY_STAT (stat_wasted_fragments_bytes += frag_size);
3174
3175                 setup_fragment (frag, min_prev, minimum_size);
3176                 return frag_size;
3177         }
3178
3179         return 0;
3180 }
3181
3182 static void*
3183 alloc_degraded (MonoVTable *vtable, size_t size)
3184 {
3185         if (need_major_collection ()) {
3186                 mono_profiler_gc_event (MONO_GC_EVENT_START, 1);
3187                 stop_world (1);
3188                 major_collection ("degraded overflow");
3189                 restart_world (1);
3190                 mono_profiler_gc_event (MONO_GC_EVENT_END, 1);
3191         }
3192
3193         degraded_mode += size;
3194         return major_collector.alloc_degraded (vtable, size);
3195 }
3196
3197 /*
3198  * Provide a variant that takes just the vtable for small fixed-size objects.
3199  * The aligned size is already computed and stored in vt->gc_descr.
3200  * Note: every SCAN_START_SIZE or so we are given the chance to do some special
3201  * processing. We can keep track of where objects start, for example,
3202  * so when we scan the thread stacks for pinned objects, we can start
3203  * a search for the pinned object in SCAN_START_SIZE chunks.
3204  */
3205 static void*
3206 mono_gc_alloc_obj_nolock (MonoVTable *vtable, size_t size)
3207 {
3208         /* FIXME: handle OOM */
3209         void **p;
3210         char *new_next;
3211         TLAB_ACCESS_INIT;
3212
3213         HEAVY_STAT (++stat_objects_alloced);
3214         if (size <= MAX_SMALL_OBJ_SIZE)
3215                 HEAVY_STAT (stat_bytes_alloced += size);
3216         else
3217                 HEAVY_STAT (stat_bytes_alloced_los += size);
3218
3219         size = ALIGN_UP (size);
3220
3221         g_assert (vtable->gc_descr);
3222
3223         if (G_UNLIKELY (collect_before_allocs)) {
3224                 if (nursery_section) {
3225                         mono_profiler_gc_event (MONO_GC_EVENT_START, 0);
3226                         stop_world (0);
3227                         collect_nursery (0);
3228                         restart_world (0);
3229                         mono_profiler_gc_event (MONO_GC_EVENT_END, 0);
3230                         if (!degraded_mode && !search_fragment_for_size (size)) {
3231                                 // FIXME:
3232                                 g_assert_not_reached ();
3233                         }
3234                 }
3235         }
3236
3237         /*
3238          * We must already have the lock here instead of after the
3239          * fast path because we might be interrupted in the fast path
3240          * (after confirming that new_next < TLAB_TEMP_END) by the GC,
3241          * and we'll end up allocating an object in a fragment which
3242          * no longer belongs to us.
3243          *
3244          * The managed allocator does not do this, but it's treated
3245          * specially by the world-stopping code.
3246          */
3247
3248         if (size > MAX_SMALL_OBJ_SIZE) {
3249                 p = alloc_large_inner (vtable, size);
3250         } else {
3251                 /* tlab_next and tlab_temp_end are TLS vars so accessing them might be expensive */
3252
3253                 p = (void**)TLAB_NEXT;
3254                 /* FIXME: handle overflow */
3255                 new_next = (char*)p + size;
3256                 TLAB_NEXT = new_next;
3257
3258                 if (G_LIKELY (new_next < TLAB_TEMP_END)) {
3259                         /* Fast path */
3260
3261                         /* 
3262                          * FIXME: We might need a memory barrier here so the change to tlab_next is 
3263                          * visible before the vtable store.
3264                          */
3265
3266                         DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
3267                         binary_protocol_alloc (p , vtable, size);
3268                         g_assert (*p == NULL);
3269                         *p = vtable;
3270
3271                         g_assert (TLAB_NEXT == new_next);
3272
3273                         return p;
3274                 }
3275
3276                 /* Slow path */
3277
3278                 /* there are two cases: the object is too big or we run out of space in the TLAB */
3279                 /* we also reach here when the thread does its first allocation after a minor 
3280                  * collection, since the tlab_ variables are initialized to NULL.
3281                  * there can be another case (from ORP), if we cooperate with the runtime a bit:
3282                  * objects that need finalizers can have the high bit set in their size
3283                  * so the above check fails and we can readily add the object to the queue.
3284                  * This avoids taking again the GC lock when registering, but this is moot when
3285                  * doing thread-local allocation, so it may not be a good idea.
3286                  */
3287                 g_assert (TLAB_NEXT == new_next);
3288                 if (TLAB_NEXT >= TLAB_REAL_END) {
3289                         /* 
3290                          * Run out of space in the TLAB. When this happens, some amount of space
3291                          * remains in the TLAB, but not enough to satisfy the current allocation
3292                          * request. Currently, we retire the TLAB in all cases, later we could
3293                          * keep it if the remaining space is above a treshold, and satisfy the
3294                          * allocation directly from the nursery.
3295                          */
3296                         TLAB_NEXT -= size;
3297                         /* when running in degraded mode, we continue allocing that way
3298                          * for a while, to decrease the number of useless nursery collections.
3299                          */
3300                         if (degraded_mode && degraded_mode < DEFAULT_NURSERY_SIZE) {
3301                                 p = alloc_degraded (vtable, size);
3302                                 binary_protocol_alloc_degraded (p, vtable, size);
3303                                 return p;
3304                         }
3305
3306                         /*FIXME This codepath is current deadcode since tlab_size > MAX_SMALL_OBJ_SIZE*/
3307                         if (size > tlab_size) {
3308                                 /* Allocate directly from the nursery */
3309                                 if (nursery_next + size >= nursery_frag_real_end) {
3310                                         if (!search_fragment_for_size (size)) {
3311                                                 minor_collect_or_expand_inner (size);
3312                                                 if (degraded_mode) {
3313                                                         p = alloc_degraded (vtable, size);
3314                                                         binary_protocol_alloc_degraded (p, vtable, size);
3315                                                         return p;
3316                                                 }
3317                                         }
3318                                 }
3319
3320                                 p = (void*)nursery_next;
3321                                 nursery_next += size;
3322                                 if (nursery_next > nursery_frag_real_end) {
3323                                         // no space left
3324                                         g_assert (0);
3325                                 }
3326
3327                                 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION)
3328                                         memset (p, 0, size);
3329                         } else {
3330                                 int alloc_size = tlab_size;
3331                                 int available_in_nursery = nursery_frag_real_end - nursery_next;
3332                                 if (TLAB_START)
3333                                         DEBUG (3, fprintf (gc_debug_file, "Retire TLAB: %p-%p [%ld]\n", TLAB_START, TLAB_REAL_END, (long)(TLAB_REAL_END - TLAB_NEXT - size)));
3334
3335                                 if (alloc_size >= available_in_nursery) {
3336                                         if (available_in_nursery > MAX_NURSERY_TLAB_WASTE && available_in_nursery > size) {
3337                                                 alloc_size = available_in_nursery;
3338                                         } else {
3339                                                 alloc_size = search_fragment_for_size_range (tlab_size, size);
3340                                                 if (!alloc_size) {
3341                                                         alloc_size = tlab_size;
3342                                                         minor_collect_or_expand_inner (tlab_size);
3343                                                         if (degraded_mode) {
3344                                                                 p = alloc_degraded (vtable, size);
3345                                                                 binary_protocol_alloc_degraded (p, vtable, size);
3346                                                                 return p;
3347                                                         }
3348                                                 }
3349                                         }
3350                                 }
3351
3352                                 /* Allocate a new TLAB from the current nursery fragment */
3353                                 TLAB_START = nursery_next;
3354                                 nursery_next += alloc_size;
3355                                 TLAB_NEXT = TLAB_START;
3356                                 TLAB_REAL_END = TLAB_START + alloc_size;
3357                                 TLAB_TEMP_END = TLAB_START + MIN (SCAN_START_SIZE, alloc_size);
3358
3359                                 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION)
3360                                         memset (TLAB_START, 0, alloc_size);
3361
3362                                 /* Allocate from the TLAB */
3363                                 p = (void*)TLAB_NEXT;
3364                                 TLAB_NEXT += size;
3365                                 g_assert (TLAB_NEXT <= TLAB_REAL_END);
3366
3367                                 nursery_section->scan_starts [((char*)p - (char*)nursery_section->data)/SCAN_START_SIZE] = (char*)p;
3368                         }
3369                 } else {
3370                         /* Reached tlab_temp_end */
3371
3372                         /* record the scan start so we can find pinned objects more easily */
3373                         nursery_section->scan_starts [((char*)p - (char*)nursery_section->data)/SCAN_START_SIZE] = (char*)p;
3374                         /* we just bump tlab_temp_end as well */
3375                         TLAB_TEMP_END = MIN (TLAB_REAL_END, TLAB_NEXT + SCAN_START_SIZE);
3376                         DEBUG (5, fprintf (gc_debug_file, "Expanding local alloc: %p-%p\n", TLAB_NEXT, TLAB_TEMP_END));
3377                 }
3378         }
3379
3380         DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
3381         binary_protocol_alloc (p, vtable, size);
3382         *p = vtable;
3383
3384         return p;
3385 }
3386
3387 static void*
3388 mono_gc_try_alloc_obj_nolock (MonoVTable *vtable, size_t size)
3389 {
3390         void **p;
3391         char *new_next;
3392         TLAB_ACCESS_INIT;
3393
3394         size = ALIGN_UP (size);
3395
3396         g_assert (vtable->gc_descr);
3397         if (size <= MAX_SMALL_OBJ_SIZE) {
3398                 /* tlab_next and tlab_temp_end are TLS vars so accessing them might be expensive */
3399
3400                 p = (void**)TLAB_NEXT;
3401                 /* FIXME: handle overflow */
3402                 new_next = (char*)p + size;
3403                 TLAB_NEXT = new_next;
3404
3405                 if (G_LIKELY (new_next < TLAB_TEMP_END)) {
3406                         /* Fast path */
3407
3408                         /* 
3409                          * FIXME: We might need a memory barrier here so the change to tlab_next is 
3410                          * visible before the vtable store.
3411                          */
3412
3413                         HEAVY_STAT (++stat_objects_alloced);
3414                         HEAVY_STAT (stat_bytes_alloced += size);
3415
3416                         DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
3417                         binary_protocol_alloc (p, vtable, size);
3418                         g_assert (*p == NULL);
3419                         *p = vtable;
3420
3421                         g_assert (TLAB_NEXT == new_next);
3422
3423                         return p;
3424                 }
3425         }
3426         return NULL;
3427 }
3428
3429 void*
3430 mono_gc_alloc_obj (MonoVTable *vtable, size_t size)
3431 {
3432         void *res;
3433 #ifndef DISABLE_CRITICAL_REGION
3434         TLAB_ACCESS_INIT;
3435         ENTER_CRITICAL_REGION;
3436         res = mono_gc_try_alloc_obj_nolock (vtable, size);
3437         if (res) {
3438                 EXIT_CRITICAL_REGION;
3439                 return res;
3440         }
3441         EXIT_CRITICAL_REGION;
3442 #endif
3443         LOCK_GC;
3444         res = mono_gc_alloc_obj_nolock (vtable, size);
3445         UNLOCK_GC;
3446         return res;
3447 }
3448
3449 void*
3450 mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length)
3451 {
3452         MonoArray *arr;
3453 #ifndef DISABLE_CRITICAL_REGION
3454         TLAB_ACCESS_INIT;
3455         ENTER_CRITICAL_REGION;
3456         arr = mono_gc_try_alloc_obj_nolock (vtable, size);
3457         if (arr) {
3458                 arr->max_length = max_length;
3459                 EXIT_CRITICAL_REGION;
3460                 return arr;
3461         }
3462         EXIT_CRITICAL_REGION;
3463 #endif
3464
3465         LOCK_GC;
3466
3467         arr = mono_gc_alloc_obj_nolock (vtable, size);
3468         arr->max_length = max_length;
3469
3470         UNLOCK_GC;
3471
3472         return arr;
3473 }
3474
3475 void*
3476 mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uintptr_t bounds_size)
3477 {
3478         MonoArray *arr;
3479         MonoArrayBounds *bounds;
3480
3481         LOCK_GC;
3482
3483         arr = mono_gc_alloc_obj_nolock (vtable, size);
3484         arr->max_length = max_length;
3485
3486         bounds = (MonoArrayBounds*)((char*)arr + size - bounds_size);
3487         arr->bounds = bounds;
3488
3489         UNLOCK_GC;
3490
3491         return arr;
3492 }
3493
3494 void*
3495 mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len)
3496 {
3497         MonoString *str;
3498 #ifndef DISABLE_CRITICAL_REGION
3499         TLAB_ACCESS_INIT;
3500         ENTER_CRITICAL_REGION;
3501         str = mono_gc_try_alloc_obj_nolock (vtable, size);
3502         if (str) {
3503                 str->length = len;
3504                 EXIT_CRITICAL_REGION;
3505                 return str;
3506         }
3507         EXIT_CRITICAL_REGION;
3508 #endif
3509
3510         LOCK_GC;
3511
3512         str = mono_gc_alloc_obj_nolock (vtable, size);
3513         str->length = len;
3514
3515         UNLOCK_GC;
3516
3517         return str;
3518 }
3519
3520 /*
3521  * To be used for interned strings and possibly MonoThread, reflection handles.
3522  * We may want to explicitly free these objects.
3523  */
3524 void*
3525 mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size)
3526 {
3527         /* FIXME: handle OOM */
3528         void **p;
3529         size = ALIGN_UP (size);
3530         LOCK_GC;
3531         if (size > MAX_SMALL_OBJ_SIZE) {
3532                 /* large objects are always pinned anyway */
3533                 p = alloc_large_inner (vtable, size);
3534         } else {
3535                 DEBUG (9, g_assert (vtable->klass->inited));
3536                 p = major_collector.alloc_small_pinned_obj (size, vtable->klass->has_references);
3537         }
3538         DEBUG (6, fprintf (gc_debug_file, "Allocated pinned object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
3539         binary_protocol_alloc_pinned (p, vtable, size);
3540         *p = vtable;
3541         UNLOCK_GC;
3542         return p;
3543 }
3544
3545 /*
3546  * ######################################################################
3547  * ########  Finalization support
3548  * ######################################################################
3549  */
3550
3551 /*
3552  * this is valid for the nursery: if the object has been forwarded it means it's
3553  * still refrenced from a root. If it is pinned it's still alive as well.
3554  * Return TRUE if @obj is ready to be finalized.
3555  */
3556 #define object_is_fin_ready(obj) (!object_is_pinned (obj) && !object_is_forwarded (obj))
3557
3558 static gboolean
3559 is_critical_finalizer (FinalizeEntry *entry)
3560 {
3561         MonoObject *obj;
3562         MonoClass *class;
3563
3564         if (!mono_defaults.critical_finalizer_object)
3565                 return FALSE;
3566
3567         obj = entry->object;
3568         class = ((MonoVTable*)LOAD_VTABLE (obj))->klass;
3569
3570         return mono_class_has_parent (class, mono_defaults.critical_finalizer_object);
3571 }
3572
3573 static void
3574 queue_finalization_entry (FinalizeEntry *entry) {
3575         if (is_critical_finalizer (entry)) {
3576                 entry->next = critical_fin_list;
3577                 critical_fin_list = entry;
3578         } else {
3579                 entry->next = fin_ready_list;
3580                 fin_ready_list = entry;
3581         }
3582 }
3583
3584 /* LOCKING: requires that the GC lock is held */
3585 static void
3586 rehash_fin_table (FinalizeEntryHashTable *hash_table)
3587 {
3588         FinalizeEntry **finalizable_hash = hash_table->table;
3589         mword finalizable_hash_size = hash_table->size;
3590         int i;
3591         unsigned int hash;
3592         FinalizeEntry **new_hash;
3593         FinalizeEntry *entry, *next;
3594         int new_size = g_spaced_primes_closest (hash_table->num_registered);
3595
3596         new_hash = mono_sgen_alloc_internal_dynamic (new_size * sizeof (FinalizeEntry*), INTERNAL_MEM_FIN_TABLE);
3597         for (i = 0; i < finalizable_hash_size; ++i) {
3598                 for (entry = finalizable_hash [i]; entry; entry = next) {
3599                         hash = mono_object_hash (entry->object) % new_size;
3600                         next = entry->next;
3601                         entry->next = new_hash [hash];
3602                         new_hash [hash] = entry;
3603                 }
3604         }
3605         mono_sgen_free_internal_dynamic (finalizable_hash, finalizable_hash_size * sizeof (FinalizeEntry*), INTERNAL_MEM_FIN_TABLE);
3606         hash_table->table = new_hash;
3607         hash_table->size = new_size;
3608 }
3609
3610 /* LOCKING: requires that the GC lock is held */
3611 static void
3612 rehash_fin_table_if_necessary (FinalizeEntryHashTable *hash_table)
3613 {
3614         if (hash_table->num_registered >= hash_table->size * 2)
3615                 rehash_fin_table (hash_table);
3616 }
3617
3618 /* LOCKING: requires that the GC lock is held */
3619 static void
3620 finalize_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, GrayQueue *queue)
3621 {
3622         FinalizeEntryHashTable *hash_table = get_finalize_entry_hash_table (generation);
3623         FinalizeEntry *entry, *prev;
3624         int i;
3625         FinalizeEntry **finalizable_hash = hash_table->table;
3626         mword finalizable_hash_size = hash_table->size;
3627
3628         if (no_finalize)
3629                 return;
3630         for (i = 0; i < finalizable_hash_size; ++i) {
3631                 prev = NULL;
3632                 for (entry = finalizable_hash [i]; entry;) {
3633                         if ((char*)entry->object >= start && (char*)entry->object < end && !major_collector.is_object_live (entry->object)) {
3634                                 gboolean is_fin_ready = object_is_fin_ready (entry->object);
3635                                 char *copy = entry->object;
3636                                 copy_func ((void**)&copy, queue);
3637                                 if (is_fin_ready) {
3638                                         char *from;
3639                                         FinalizeEntry *next;
3640                                         /* remove and put in fin_ready_list */
3641                                         if (prev)
3642                                                 prev->next = entry->next;
3643                                         else
3644                                                 finalizable_hash [i] = entry->next;
3645                                         next = entry->next;
3646                                         num_ready_finalizers++;
3647                                         hash_table->num_registered--;
3648                                         queue_finalization_entry (entry);
3649                                         /* Make it survive */
3650                                         from = entry->object;
3651                                         entry->object = copy;
3652                                         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));
3653                                         entry = next;
3654                                         continue;
3655                                 } else {
3656                                         char *from = entry->object;
3657                                         if (hash_table == &minor_finalizable_hash && !ptr_in_nursery (copy)) {
3658                                                 FinalizeEntry *next = entry->next;
3659                                                 unsigned int major_hash;
3660                                                 /* remove from the list */
3661                                                 if (prev)
3662                                                         prev->next = entry->next;
3663                                                 else
3664                                                         finalizable_hash [i] = entry->next;
3665                                                 hash_table->num_registered--;
3666
3667                                                 entry->object = copy;
3668
3669                                                 /* insert it into the major hash */
3670                                                 rehash_fin_table_if_necessary (&major_finalizable_hash);
3671                                                 major_hash = mono_object_hash ((MonoObject*) copy) %
3672                                                         major_finalizable_hash.size;
3673                                                 entry->next = major_finalizable_hash.table [major_hash];
3674                                                 major_finalizable_hash.table [major_hash] = entry;
3675                                                 major_finalizable_hash.num_registered++;
3676
3677                                                 DEBUG (5, fprintf (gc_debug_file, "Promoting finalization of object %p (%s) (was at %p) to major table\n", copy, safe_name (copy), from));
3678
3679                                                 entry = next;
3680                                                 continue;
3681                                         } else {
3682                                                 /* update pointer */
3683                                                 DEBUG (5, fprintf (gc_debug_file, "Updating object for finalization: %p (%s) (was at %p)\n", entry->object, safe_name (entry->object), from));
3684                                                 entry->object = copy;
3685                                         }
3686                                 }
3687                         }
3688                         prev = entry;
3689                         entry = entry->next;
3690                 }
3691         }
3692 }
3693
3694 static int
3695 object_is_reachable (char *object, char *start, char *end)
3696 {
3697         /*This happens for non nursery objects during minor collections. We just treat all objects as alive.*/
3698         if (object < start || object >= end)
3699                 return TRUE;
3700         return !object_is_fin_ready (object) || major_collector.is_object_live (object);
3701 }
3702
3703 /* LOCKING: requires that the GC lock is held */
3704 static void
3705 null_ephemerons_for_domain (MonoDomain *domain)
3706 {
3707         EphemeronLinkNode *current = ephemeron_list, *prev = NULL;
3708
3709         while (current) {
3710                 MonoObject *object = (MonoObject*)current->array;
3711
3712                 if (object && !object->vtable) {
3713                         EphemeronLinkNode *tmp = current;
3714
3715                         if (prev)
3716                                 prev->next = current->next;
3717                         else
3718                                 ephemeron_list = current->next;
3719
3720                         current = current->next;
3721                         mono_sgen_free_internal (tmp, INTERNAL_MEM_EPHEMERON_LINK);
3722                 } else {
3723                         prev = current;
3724                         current = current->next;
3725                 }
3726         }
3727 }
3728
3729 /* LOCKING: requires that the GC lock is held */
3730 static void
3731 clear_unreachable_ephemerons (CopyOrMarkObjectFunc copy_func, char *start, char *end, GrayQueue *queue)
3732 {
3733         int was_in_nursery, was_promoted;
3734         EphemeronLinkNode *current = ephemeron_list, *prev = NULL;
3735         MonoArray *array;
3736         Ephemeron *cur, *array_end;
3737         char *tombstone;
3738
3739         while (current) {
3740                 char *object = current->array;
3741
3742                 if (!object_is_reachable (object, start, end)) {
3743                         EphemeronLinkNode *tmp = current;
3744
3745                         DEBUG (5, fprintf (gc_debug_file, "Dead Ephemeron array at %p\n", object));
3746
3747                         if (prev)
3748                                 prev->next = current->next;
3749                         else
3750                                 ephemeron_list = current->next;
3751
3752                         current = current->next;
3753                         mono_sgen_free_internal (tmp, INTERNAL_MEM_EPHEMERON_LINK);
3754
3755                         continue;
3756                 }
3757
3758                 was_in_nursery = ptr_in_nursery (object);
3759                 copy_func ((void**)&object, queue);
3760                 current->array = object;
3761
3762                 /*The array was promoted, add global remsets for key/values left behind in nursery.*/
3763                 was_promoted = was_in_nursery && !ptr_in_nursery (object);
3764
3765                 DEBUG (5, fprintf (gc_debug_file, "Clearing unreachable entries for ephemeron array at %p\n", object));
3766
3767                 array = (MonoArray*)object;
3768                 cur = mono_array_addr (array, Ephemeron, 0);
3769                 array_end = cur + mono_array_length_fast (array);
3770                 tombstone = (char*)((MonoVTable*)LOAD_VTABLE (object))->domain->ephemeron_tombstone;
3771
3772                 for (; cur < array_end; ++cur) {
3773                         char *key = (char*)cur->key;
3774
3775                         if (!key || key == tombstone)
3776                                 continue;
3777
3778                         DEBUG (5, fprintf (gc_debug_file, "[%td] key %p (%s) value %p (%s)\n", cur - mono_array_addr (array, Ephemeron, 0),
3779                                 key, object_is_reachable (key, start, end) ? "reachable" : "unreachable",
3780                                 cur->value, cur->value && object_is_reachable (cur->value, start, end) ? "reachable" : "unreachable"));
3781
3782                         if (!object_is_reachable (key, start, end)) {
3783                                 cur->key = tombstone;
3784                                 cur->value = NULL;
3785                                 continue;
3786                         }
3787
3788                         if (was_promoted) {
3789                                 if (ptr_in_nursery (key)) {/*key was not promoted*/
3790                                         DEBUG (5, fprintf (gc_debug_file, "\tAdded remset to key %p\n", key));
3791                                         mono_sgen_add_to_global_remset (&cur->key);
3792                                 }
3793                                 if (ptr_in_nursery (cur->value)) {/*value was not promoted*/
3794                                         DEBUG (5, fprintf (gc_debug_file, "\tAdded remset to value %p\n", cur->value));
3795                                         mono_sgen_add_to_global_remset (&cur->value);
3796                                 }
3797                         }
3798                 }
3799                 prev = current;
3800                 current = current->next;
3801         }
3802 }
3803
3804 /* LOCKING: requires that the GC lock is held */
3805 static int
3806 mark_ephemerons_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, GrayQueue *queue)
3807 {
3808         int nothing_marked = 1;
3809         EphemeronLinkNode *current = ephemeron_list;
3810         MonoArray *array;
3811         Ephemeron *cur, *array_end;
3812         char *tombstone;
3813
3814         for (current = ephemeron_list; current; current = current->next) {
3815                 char *object = current->array;
3816                 DEBUG (5, fprintf (gc_debug_file, "Ephemeron array at %p\n", object));
3817
3818                 /*We ignore arrays in old gen during minor collections since all objects are promoted by the remset machinery.*/
3819                 if (object < start || object >= end)
3820                         continue;
3821
3822                 /*It has to be alive*/
3823                 if (!object_is_reachable (object, start, end)) {
3824                         DEBUG (5, fprintf (gc_debug_file, "\tnot reachable\n"));
3825                         continue;
3826                 }
3827
3828                 copy_func ((void**)&object, queue);
3829
3830                 array = (MonoArray*)object;
3831                 cur = mono_array_addr (array, Ephemeron, 0);
3832                 array_end = cur + mono_array_length_fast (array);
3833                 tombstone = (char*)((MonoVTable*)LOAD_VTABLE (object))->domain->ephemeron_tombstone;
3834
3835                 for (; cur < array_end; ++cur) {
3836                         char *key = cur->key;
3837
3838                         if (!key || key == tombstone)
3839                                 continue;
3840
3841                         DEBUG (5, fprintf (gc_debug_file, "[%td] key %p (%s) value %p (%s)\n", cur - mono_array_addr (array, Ephemeron, 0),
3842                                 key, object_is_reachable (key, start, end) ? "reachable" : "unreachable",
3843                                 cur->value, cur->value && object_is_reachable (cur->value, start, end) ? "reachable" : "unreachable"));
3844
3845                         if (object_is_reachable (key, start, end)) {
3846                                 char *value = cur->value;
3847
3848                                 copy_func ((void**)&cur->key, queue);
3849                                 if (value) {
3850                                         if (!object_is_reachable (value, start, end))
3851                                                 nothing_marked = 0;
3852                                         copy_func ((void**)&cur->value, queue);
3853                                 }
3854                         }
3855                 }
3856         }
3857
3858         DEBUG (5, fprintf (gc_debug_file, "Ephemeron run finished. Is it done %d\n", nothing_marked));
3859         return nothing_marked;
3860 }
3861
3862 /* LOCKING: requires that the GC lock is held */
3863 static void
3864 null_link_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, GrayQueue *queue)
3865 {
3866         DisappearingLinkHashTable *hash = get_dislink_hash_table (generation);
3867         DisappearingLink **disappearing_link_hash = hash->table;
3868         int disappearing_link_hash_size = hash->size;
3869         DisappearingLink *entry, *prev;
3870         int i;
3871         if (!hash->num_links)
3872                 return;
3873         for (i = 0; i < disappearing_link_hash_size; ++i) {
3874                 prev = NULL;
3875                 for (entry = disappearing_link_hash [i]; entry;) {
3876                         char *object = DISLINK_OBJECT (entry);
3877                         if (object >= start && object < end && !major_collector.is_object_live (object)) {
3878                                 gboolean track = DISLINK_TRACK (entry);
3879                                 if (!track && object_is_fin_ready (object)) {
3880                                         void **p = entry->link;
3881                                         DisappearingLink *old;
3882                                         *p = NULL;
3883                                         /* remove from list */
3884                                         if (prev)
3885                                                 prev->next = entry->next;
3886                                         else
3887                                                 disappearing_link_hash [i] = entry->next;
3888                                         DEBUG (5, fprintf (gc_debug_file, "Dislink nullified at %p to GCed object %p\n", p, object));
3889                                         old = entry->next;
3890                                         mono_sgen_free_internal (entry, INTERNAL_MEM_DISLINK);
3891                                         entry = old;
3892                                         hash->num_links--;
3893                                         continue;
3894                                 } else {
3895                                         char *copy = object;
3896                                         copy_func ((void**)&copy, queue);
3897
3898                                         /* Update pointer if it's moved.  If the object
3899                                          * has been moved out of the nursery, we need to
3900                                          * remove the link from the minor hash table to
3901                                          * the major one.
3902                                          *
3903                                          * FIXME: what if an object is moved earlier?
3904                                          */
3905
3906                                         if (hash == &minor_disappearing_link_hash && !ptr_in_nursery (copy)) {
3907                                                 void **link = entry->link;
3908                                                 DisappearingLink *old;
3909                                                 /* remove from list */
3910                                                 if (prev)
3911                                                         prev->next = entry->next;
3912                                                 else
3913                                                         disappearing_link_hash [i] = entry->next;
3914                                                 old = entry->next;
3915                                                 mono_sgen_free_internal (entry, INTERNAL_MEM_DISLINK);
3916                                                 entry = old;
3917                                                 hash->num_links--;
3918
3919                                                 add_or_remove_disappearing_link ((MonoObject*)copy, link,
3920                                                         track, GENERATION_OLD);
3921
3922                                                 DEBUG (5, fprintf (gc_debug_file, "Upgraded dislink at %p to major because object %p moved to %p\n", link, object, copy));
3923
3924                                                 continue;
3925                                         } else {
3926                                                 /* We set the track resurrection bit to
3927                                                  * FALSE if the object is to be finalized
3928                                                  * so that the object can be collected in
3929                                                  * the next cycle (i.e. after it was
3930                                                  * finalized).
3931                                                  */
3932                                                 *entry->link = HIDE_POINTER (copy,
3933                                                         object_is_fin_ready (object) ? FALSE : track);
3934                                                 DEBUG (5, fprintf (gc_debug_file, "Updated dislink at %p to %p\n", entry->link, DISLINK_OBJECT (entry)));
3935                                         }
3936                                 }
3937                         }
3938                         prev = entry;
3939                         entry = entry->next;
3940                 }
3941         }
3942 }
3943
3944 /* LOCKING: requires that the GC lock is held */
3945 static void
3946 null_links_for_domain (MonoDomain *domain, int generation)
3947 {
3948         DisappearingLinkHashTable *hash = get_dislink_hash_table (generation);
3949         DisappearingLink **disappearing_link_hash = hash->table;
3950         int disappearing_link_hash_size = hash->size;
3951         DisappearingLink *entry, *prev;
3952         int i;
3953         for (i = 0; i < disappearing_link_hash_size; ++i) {
3954                 prev = NULL;
3955                 for (entry = disappearing_link_hash [i]; entry; ) {
3956                         char *object = DISLINK_OBJECT (entry);
3957                         if (object && !((MonoObject*)object)->vtable) {
3958                                 DisappearingLink *next = entry->next;
3959
3960                                 if (prev)
3961                                         prev->next = next;
3962                                 else
3963                                         disappearing_link_hash [i] = next;
3964
3965                                 if (*(entry->link)) {
3966                                         *(entry->link) = NULL;
3967                                         g_warning ("Disappearing link %p not freed", entry->link);
3968                                 } else {
3969                                         mono_sgen_free_internal (entry, INTERNAL_MEM_DISLINK);
3970                                 }
3971
3972                                 entry = next;
3973                                 continue;
3974                         }
3975                         prev = entry;
3976                         entry = entry->next;
3977                 }
3978         }
3979 }
3980
3981 /* LOCKING: requires that the GC lock is held */
3982 static int
3983 finalizers_for_domain (MonoDomain *domain, MonoObject **out_array, int out_size,
3984         FinalizeEntryHashTable *hash_table)
3985 {
3986         FinalizeEntry **finalizable_hash = hash_table->table;
3987         mword finalizable_hash_size = hash_table->size;
3988         FinalizeEntry *entry, *prev;
3989         int i, count;
3990
3991         if (no_finalize || !out_size || !out_array)
3992                 return 0;
3993         count = 0;
3994         for (i = 0; i < finalizable_hash_size; ++i) {
3995                 prev = NULL;
3996                 for (entry = finalizable_hash [i]; entry;) {
3997                         if (mono_object_domain (entry->object) == domain) {
3998                                 FinalizeEntry *next;
3999                                 /* remove and put in out_array */
4000                                 if (prev)
4001                                         prev->next = entry->next;
4002                                 else
4003                                         finalizable_hash [i] = entry->next;
4004                                 next = entry->next;
4005                                 hash_table->num_registered--;
4006                                 out_array [count ++] = entry->object;
4007                                 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));
4008                                 entry = next;
4009                                 if (count == out_size)
4010                                         return count;
4011                                 continue;
4012                         }
4013                         prev = entry;
4014                         entry = entry->next;
4015                 }
4016         }
4017         return count;
4018 }
4019
4020 /**
4021  * mono_gc_finalizers_for_domain:
4022  * @domain: the unloading appdomain
4023  * @out_array: output array
4024  * @out_size: size of output array
4025  *
4026  * Store inside @out_array up to @out_size objects that belong to the unloading
4027  * appdomain @domain. Returns the number of stored items. Can be called repeteadly
4028  * until it returns 0.
4029  * The items are removed from the finalizer data structure, so the caller is supposed
4030  * to finalize them.
4031  * @out_array should be on the stack to allow the GC to know the objects are still alive.
4032  */
4033 int
4034 mono_gc_finalizers_for_domain (MonoDomain *domain, MonoObject **out_array, int out_size)
4035 {
4036         int result;
4037
4038         LOCK_GC;
4039         result = finalizers_for_domain (domain, out_array, out_size, &minor_finalizable_hash);
4040         if (result < out_size) {
4041                 result += finalizers_for_domain (domain, out_array + result, out_size - result,
4042                         &major_finalizable_hash);
4043         }
4044         UNLOCK_GC;
4045
4046         return result;
4047 }
4048
4049 static void
4050 register_for_finalization (MonoObject *obj, void *user_data, int generation)
4051 {
4052         FinalizeEntryHashTable *hash_table = get_finalize_entry_hash_table (generation);
4053         FinalizeEntry **finalizable_hash;
4054         mword finalizable_hash_size;
4055         FinalizeEntry *entry, *prev;
4056         unsigned int hash;
4057         if (no_finalize)
4058                 return;
4059         g_assert (user_data == NULL || user_data == mono_gc_run_finalize);
4060         hash = mono_object_hash (obj);
4061         LOCK_GC;
4062         rehash_fin_table_if_necessary (hash_table);
4063         finalizable_hash = hash_table->table;
4064         finalizable_hash_size = hash_table->size;
4065         hash %= finalizable_hash_size;
4066         prev = NULL;
4067         for (entry = finalizable_hash [hash]; entry; entry = entry->next) {
4068                 if (entry->object == obj) {
4069                         if (!user_data) {
4070                                 /* remove from the list */
4071                                 if (prev)
4072                                         prev->next = entry->next;
4073                                 else
4074                                         finalizable_hash [hash] = entry->next;
4075                                 hash_table->num_registered--;
4076                                 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));
4077                                 mono_sgen_free_internal (entry, INTERNAL_MEM_FINALIZE_ENTRY);
4078                         }
4079                         UNLOCK_GC;
4080                         return;
4081                 }
4082                 prev = entry;
4083         }
4084         if (!user_data) {
4085                 /* request to deregister, but already out of the list */
4086                 UNLOCK_GC;
4087                 return;
4088         }
4089         entry = mono_sgen_alloc_internal (INTERNAL_MEM_FINALIZE_ENTRY);
4090         entry->object = obj;
4091         entry->next = finalizable_hash [hash];
4092         finalizable_hash [hash] = entry;
4093         hash_table->num_registered++;
4094         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)));
4095         UNLOCK_GC;
4096 }
4097
4098 void
4099 mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
4100 {
4101         if (ptr_in_nursery (obj))
4102                 register_for_finalization (obj, user_data, GENERATION_NURSERY);
4103         else
4104                 register_for_finalization (obj, user_data, GENERATION_OLD);
4105 }
4106
4107 static void
4108 rehash_dislink (DisappearingLinkHashTable *hash_table)
4109 {
4110         DisappearingLink **disappearing_link_hash = hash_table->table;
4111         int disappearing_link_hash_size = hash_table->size;
4112         int i;
4113         unsigned int hash;
4114         DisappearingLink **new_hash;
4115         DisappearingLink *entry, *next;
4116         int new_size = g_spaced_primes_closest (hash_table->num_links);
4117
4118         new_hash = mono_sgen_alloc_internal_dynamic (new_size * sizeof (DisappearingLink*), INTERNAL_MEM_DISLINK_TABLE);
4119         for (i = 0; i < disappearing_link_hash_size; ++i) {
4120                 for (entry = disappearing_link_hash [i]; entry; entry = next) {
4121                         hash = mono_aligned_addr_hash (entry->link) % new_size;
4122                         next = entry->next;
4123                         entry->next = new_hash [hash];
4124                         new_hash [hash] = entry;
4125                 }
4126         }
4127         mono_sgen_free_internal_dynamic (disappearing_link_hash,
4128                         disappearing_link_hash_size * sizeof (DisappearingLink*), INTERNAL_MEM_DISLINK_TABLE);
4129         hash_table->table = new_hash;
4130         hash_table->size = new_size;
4131 }
4132
4133 /* LOCKING: assumes the GC lock is held */
4134 static void
4135 add_or_remove_disappearing_link (MonoObject *obj, void **link, gboolean track, int generation)
4136 {
4137         DisappearingLinkHashTable *hash_table = get_dislink_hash_table (generation);
4138         DisappearingLink *entry, *prev;
4139         unsigned int hash;
4140         DisappearingLink **disappearing_link_hash = hash_table->table;
4141         int disappearing_link_hash_size = hash_table->size;
4142
4143         if (hash_table->num_links >= disappearing_link_hash_size * 2) {
4144                 rehash_dislink (hash_table);
4145                 disappearing_link_hash = hash_table->table;
4146                 disappearing_link_hash_size = hash_table->size;
4147         }
4148         /* FIXME: add check that link is not in the heap */
4149         hash = mono_aligned_addr_hash (link) % disappearing_link_hash_size;
4150         entry = disappearing_link_hash [hash];
4151         prev = NULL;
4152         for (; entry; entry = entry->next) {
4153                 /* link already added */
4154                 if (link == entry->link) {
4155                         /* NULL obj means remove */
4156                         if (obj == NULL) {
4157                                 if (prev)
4158                                         prev->next = entry->next;
4159                                 else
4160                                         disappearing_link_hash [hash] = entry->next;
4161                                 hash_table->num_links--;
4162                                 DEBUG (5, fprintf (gc_debug_file, "Removed dislink %p (%d) from %s table\n", entry, hash_table->num_links, generation_name (generation)));
4163                                 mono_sgen_free_internal (entry, INTERNAL_MEM_DISLINK);
4164                                 *link = NULL;
4165                         } else {
4166                                 *link = HIDE_POINTER (obj, track); /* we allow the change of object */
4167                         }
4168                         return;
4169                 }
4170                 prev = entry;
4171         }
4172         if (obj == NULL)
4173                 return;
4174         entry = mono_sgen_alloc_internal (INTERNAL_MEM_DISLINK);
4175         *link = HIDE_POINTER (obj, track);
4176         entry->link = link;
4177         entry->next = disappearing_link_hash [hash];
4178         disappearing_link_hash [hash] = entry;
4179         hash_table->num_links++;
4180         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)));
4181 }
4182
4183 /* LOCKING: assumes the GC lock is held */
4184 static void
4185 mono_gc_register_disappearing_link (MonoObject *obj, void **link, gboolean track)
4186 {
4187         add_or_remove_disappearing_link (NULL, link, FALSE, GENERATION_NURSERY);
4188         add_or_remove_disappearing_link (NULL, link, FALSE, GENERATION_OLD);
4189         if (obj) {
4190                 if (ptr_in_nursery (obj))
4191                         add_or_remove_disappearing_link (obj, link, track, GENERATION_NURSERY);
4192                 else
4193                         add_or_remove_disappearing_link (obj, link, track, GENERATION_OLD);
4194         }
4195 }
4196
4197 int
4198 mono_gc_invoke_finalizers (void)
4199 {
4200         FinalizeEntry *entry = NULL;
4201         gboolean entry_is_critical = FALSE;
4202         int count = 0;
4203         void *obj;
4204         /* FIXME: batch to reduce lock contention */
4205         while (fin_ready_list || critical_fin_list) {
4206                 LOCK_GC;
4207
4208                 if (entry) {
4209                         FinalizeEntry **list = entry_is_critical ? &critical_fin_list : &fin_ready_list;
4210
4211                         /* We have finalized entry in the last
4212                            interation, now we need to remove it from
4213                            the list. */
4214                         if (*list == entry)
4215                                 *list = entry->next;
4216                         else {
4217                                 FinalizeEntry *e = *list;
4218                                 while (e->next != entry)
4219                                         e = e->next;
4220                                 e->next = entry->next;
4221                         }
4222                         mono_sgen_free_internal (entry, INTERNAL_MEM_FINALIZE_ENTRY);
4223                         entry = NULL;
4224                 }
4225
4226                 /* Now look for the first non-null entry. */
4227                 for (entry = fin_ready_list; entry && !entry->object; entry = entry->next)
4228                         ;
4229                 if (entry) {
4230                         entry_is_critical = FALSE;
4231                 } else {
4232                         entry_is_critical = TRUE;
4233                         for (entry = critical_fin_list; entry && !entry->object; entry = entry->next)
4234                                 ;
4235                 }
4236
4237                 if (entry) {
4238                         g_assert (entry->object);
4239                         num_ready_finalizers--;
4240                         obj = entry->object;
4241                         entry->object = NULL;
4242                         DEBUG (7, fprintf (gc_debug_file, "Finalizing object %p (%s)\n", obj, safe_name (obj)));
4243                 }
4244
4245                 UNLOCK_GC;
4246
4247                 if (!entry)
4248                         break;
4249
4250                 g_assert (entry->object == NULL);
4251                 count++;
4252                 /* the object is on the stack so it is pinned */
4253                 /*g_print ("Calling finalizer for object: %p (%s)\n", entry->object, safe_name (entry->object));*/
4254                 mono_gc_run_finalize (obj, NULL);
4255         }
4256         g_assert (!entry);
4257         return count;
4258 }
4259
4260 gboolean
4261 mono_gc_pending_finalizers (void)
4262 {
4263         return fin_ready_list || critical_fin_list;
4264 }
4265
4266 /* Negative value to remove */
4267 void
4268 mono_gc_add_memory_pressure (gint64 value)
4269 {
4270         /* FIXME: Use interlocked functions */
4271         LOCK_GC;
4272         memory_pressure += value;
4273         UNLOCK_GC;
4274 }
4275
4276 void
4277 mono_sgen_register_major_sections_alloced (int num_sections)
4278 {
4279         minor_collection_sections_alloced += num_sections;
4280 }
4281
4282 mword
4283 mono_sgen_get_minor_collection_allowance (void)
4284 {
4285         return minor_collection_allowance;
4286 }
4287
4288 /*
4289  * ######################################################################
4290  * ########  registered roots support
4291  * ######################################################################
4292  */
4293
4294 static void
4295 rehash_roots (gboolean pinned)
4296 {
4297         int i;
4298         unsigned int hash;
4299         RootRecord **new_hash;
4300         RootRecord *entry, *next;
4301         int new_size;
4302
4303         new_size = g_spaced_primes_closest (num_roots_entries [pinned]);
4304         new_hash = mono_sgen_alloc_internal_dynamic (new_size * sizeof (RootRecord*), INTERNAL_MEM_ROOTS_TABLE);
4305         for (i = 0; i < roots_hash_size [pinned]; ++i) {
4306                 for (entry = roots_hash [pinned][i]; entry; entry = next) {
4307                         hash = mono_aligned_addr_hash (entry->start_root) % new_size;
4308                         next = entry->next;
4309                         entry->next = new_hash [hash];
4310                         new_hash [hash] = entry;
4311                 }
4312         }
4313         mono_sgen_free_internal_dynamic (roots_hash [pinned], roots_hash_size [pinned] * sizeof (RootRecord*), INTERNAL_MEM_ROOTS_TABLE);
4314         roots_hash [pinned] = new_hash;
4315         roots_hash_size [pinned] = new_size;
4316 }
4317
4318 static RootRecord*
4319 find_root (int root_type, char *start, guint32 addr_hash)
4320 {
4321         RootRecord *new_root;
4322
4323         guint32 hash = addr_hash % roots_hash_size [root_type];
4324         for (new_root = roots_hash [root_type][hash]; new_root; new_root = new_root->next) {
4325                 /* we allow changing the size and the descriptor (for thread statics etc) */
4326                 if (new_root->start_root == start) {
4327                         return new_root;
4328                 }
4329         }
4330
4331         return NULL;
4332 }
4333
4334 /*
4335  * We do not coalesce roots.
4336  */
4337 static int
4338 mono_gc_register_root_inner (char *start, size_t size, void *descr, int root_type)
4339 {
4340         RootRecord *new_root;
4341         unsigned int hash, addr_hash = mono_aligned_addr_hash (start);
4342         int i;
4343         LOCK_GC;
4344         for (i = 0; i < ROOT_TYPE_NUM; ++i) {
4345                 if (num_roots_entries [i] >= roots_hash_size [i] * 2)
4346                         rehash_roots (i);
4347         }
4348         for (i = 0; i < ROOT_TYPE_NUM; ++i) {
4349                 new_root = find_root (i, start, addr_hash);
4350                 /* we allow changing the size and the descriptor (for thread statics etc) */
4351                 if (new_root) {
4352                         size_t old_size = new_root->end_root - new_root->start_root;
4353                         new_root->end_root = new_root->start_root + size;
4354                         g_assert (((new_root->root_desc != 0) && (descr != NULL)) ||
4355                                           ((new_root->root_desc == 0) && (descr == NULL)));
4356                         new_root->root_desc = (mword)descr;
4357                         roots_size += size;
4358                         roots_size -= old_size;
4359                         UNLOCK_GC;
4360                         return TRUE;
4361                 }
4362         }
4363         new_root = mono_sgen_alloc_internal (INTERNAL_MEM_ROOT_RECORD);
4364         if (new_root) {
4365                 new_root->start_root = start;
4366                 new_root->end_root = new_root->start_root + size;
4367                 new_root->root_desc = (mword)descr;
4368                 roots_size += size;
4369                 hash = addr_hash % roots_hash_size [root_type];
4370                 num_roots_entries [root_type]++;
4371                 new_root->next = roots_hash [root_type] [hash];
4372                 roots_hash [root_type][hash] = new_root;
4373                 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));
4374         } else {
4375                 UNLOCK_GC;
4376                 return FALSE;
4377         }
4378         UNLOCK_GC;
4379         return TRUE;
4380 }
4381
4382 int
4383 mono_gc_register_root (char *start, size_t size, void *descr)
4384 {
4385         return mono_gc_register_root_inner (start, size, descr, descr ? ROOT_TYPE_NORMAL : ROOT_TYPE_PINNED);
4386 }
4387
4388 int
4389 mono_gc_register_root_wbarrier (char *start, size_t size, void *descr)
4390 {
4391         return mono_gc_register_root_inner (start, size, descr, ROOT_TYPE_WBARRIER);
4392 }
4393
4394 void
4395 mono_gc_deregister_root (char* addr)
4396 {
4397         RootRecord *tmp, *prev;
4398         unsigned int hash, addr_hash = mono_aligned_addr_hash (addr);
4399         int root_type;
4400
4401         LOCK_GC;
4402         for (root_type = 0; root_type < ROOT_TYPE_NUM; ++root_type) {
4403                 hash = addr_hash % roots_hash_size [root_type];
4404                 tmp = roots_hash [root_type][hash];
4405                 prev = NULL;
4406                 while (tmp) {
4407                         if (tmp->start_root == (char*)addr) {
4408                                 if (prev)
4409                                         prev->next = tmp->next;
4410                                 else
4411                                         roots_hash [root_type][hash] = tmp->next;
4412                                 roots_size -= (tmp->end_root - tmp->start_root);
4413                                 num_roots_entries [root_type]--;
4414                                 DEBUG (3, fprintf (gc_debug_file, "Removed root %p for range: %p-%p\n", tmp, tmp->start_root, tmp->end_root));
4415                                 mono_sgen_free_internal (tmp, INTERNAL_MEM_ROOT_RECORD);
4416                                 break;
4417                         }
4418                         prev = tmp;
4419                         tmp = tmp->next;
4420                 }
4421         }
4422         UNLOCK_GC;
4423 }
4424
4425 /*
4426  * ######################################################################
4427  * ########  Thread handling (stop/start code)
4428  * ######################################################################
4429  */
4430
4431 /* FIXME: handle large/small config */
4432 #define HASH_PTHREAD_T(id) (((unsigned int)(id) >> 4) * 2654435761u)
4433
4434 static SgenThreadInfo* thread_table [THREAD_HASH_SIZE];
4435
4436 #if USE_SIGNAL_BASED_START_STOP_WORLD
4437
4438 static MonoSemType suspend_ack_semaphore;
4439 static MonoSemType *suspend_ack_semaphore_ptr;
4440 static unsigned int global_stop_count = 0;
4441
4442 static sigset_t suspend_signal_mask;
4443 static mword cur_thread_regs [ARCH_NUM_REGS] = {0};
4444
4445 /* LOCKING: assumes the GC lock is held */
4446 SgenThreadInfo**
4447 mono_sgen_get_thread_table (void)
4448 {
4449         return thread_table;
4450 }
4451
4452 SgenThreadInfo*
4453 mono_sgen_thread_info_lookup (ARCH_THREAD_TYPE id)
4454 {
4455         unsigned int hash = HASH_PTHREAD_T (id) % THREAD_HASH_SIZE;
4456         SgenThreadInfo *info;
4457
4458         info = thread_table [hash];
4459         while (info && !ARCH_THREAD_EQUALS (info->id, id)) {
4460                 info = info->next;
4461         }
4462         return info;
4463 }
4464
4465 static void
4466 update_current_thread_stack (void *start)
4467 {
4468         void *ptr = cur_thread_regs;
4469         SgenThreadInfo *info = mono_sgen_thread_info_lookup (ARCH_GET_THREAD ());
4470         
4471         info->stack_start = align_pointer (&ptr);
4472         g_assert (info->stack_start >= info->stack_start_limit && info->stack_start < info->stack_end);
4473         ARCH_STORE_REGS (ptr);
4474         info->stopped_regs = ptr;
4475         if (gc_callbacks.thread_suspend_func)
4476                 gc_callbacks.thread_suspend_func (info->runtime_data, NULL);
4477 }
4478
4479 /*
4480  * Define this and use the "xdomain-checks" MONO_GC_DEBUG option to
4481  * have cross-domain checks in the write barrier.
4482  */
4483 //#define XDOMAIN_CHECKS_IN_WBARRIER
4484
4485 #ifndef SGEN_BINARY_PROTOCOL
4486 #ifndef HEAVY_STATISTICS
4487 #define MANAGED_ALLOCATION
4488 #ifndef XDOMAIN_CHECKS_IN_WBARRIER
4489 #define MANAGED_WBARRIER
4490 #endif
4491 #endif
4492 #endif
4493
4494 static gboolean
4495 is_ip_in_managed_allocator (MonoDomain *domain, gpointer ip);
4496
4497 void
4498 mono_sgen_wait_for_suspend_ack (int count)
4499 {
4500         int i, result;
4501
4502         for (i = 0; i < count; ++i) {
4503                 while ((result = MONO_SEM_WAIT (suspend_ack_semaphore_ptr)) != 0) {
4504                         if (errno != EINTR) {
4505                                 g_error ("sem_wait ()");
4506                         }
4507                 }
4508         }
4509 }
4510
4511 static int
4512 restart_threads_until_none_in_managed_allocator (void)
4513 {
4514         SgenThreadInfo *info;
4515         int i, result, num_threads_died = 0;
4516         int sleep_duration = -1;
4517
4518         for (;;) {
4519                 int restart_count = 0, restarted_count = 0;
4520                 /* restart all threads that stopped in the
4521                    allocator */
4522                 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
4523                         for (info = thread_table [i]; info; info = info->next) {
4524                                 if (info->skip)
4525                                         continue;
4526                                 if (!info->stack_start || info->in_critical_region ||
4527                                                 is_ip_in_managed_allocator (info->stopped_domain, info->stopped_ip)) {
4528                                         binary_protocol_thread_restart ((gpointer)info->id);
4529 #if defined(__MACH__) && MONO_MACH_ARCH_SUPPORTED
4530                                         result = thread_resume (pthread_mach_thread_np (info->id));
4531 #else
4532                                         result = pthread_kill (info->id, restart_signal_num);
4533 #endif
4534                                         if (result == 0) {
4535                                                 ++restart_count;
4536                                         } else {
4537                                                 info->skip = 1;
4538                                         }
4539                                 } else {
4540                                         /* we set the stopped_ip to
4541                                            NULL for threads which
4542                                            we're not restarting so
4543                                            that we can easily identify
4544                                            the others */
4545                                         info->stopped_ip = NULL;
4546                                         info->stopped_domain = NULL;
4547                                 }
4548                         }
4549                 }
4550                 /* if no threads were restarted, we're done */
4551                 if (restart_count == 0)
4552                         break;
4553
4554 #if defined(__MACH__) && MONO_MACH_ARCH_SUPPORTED
4555                 /* mach thread_resume is synchronous so we dont need to wait for them */
4556 #else
4557                 /* wait for the threads to signal their restart */
4558                 mono_sgen_wait_for_suspend_ack (restart_count);
4559 #endif
4560
4561                 if (sleep_duration < 0) {
4562                         sched_yield ();
4563                         sleep_duration = 0;
4564                 } else {
4565                         g_usleep (sleep_duration);
4566                         sleep_duration += 10;
4567                 }
4568
4569                 /* stop them again */
4570                 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
4571                         for (info = thread_table [i]; info; info = info->next) {
4572                                 if (info->skip || info->stopped_ip == NULL)
4573                                         continue;
4574 #if defined(__MACH__) && MONO_MACH_ARCH_SUPPORTED
4575                                 result = thread_suspend (pthread_mach_thread_np (info->id));
4576 #else
4577                                 result = pthread_kill (info->id, suspend_signal_num);
4578 #endif
4579                                 if (result == 0) {
4580                                         ++restarted_count;
4581                                 } else {
4582                                         info->skip = 1;
4583                                 }
4584                         }
4585                 }
4586                 /* some threads might have died */
4587                 num_threads_died += restart_count - restarted_count;
4588 #if defined(__MACH__) && MONO_MACH_ARCH_SUPPORTED
4589                 /* mach thread_resume is synchronous so we dont need to wait for them */
4590 #else
4591                 /* wait for the threads to signal their suspension
4592                    again */
4593                 mono_sgen_wait_for_suspend_ack (restart_count);
4594 #endif
4595         }
4596
4597         return num_threads_died;
4598 }
4599
4600 /* LOCKING: assumes the GC lock is held (by the stopping thread) */
4601 static void
4602 suspend_handler (int sig, siginfo_t *siginfo, void *context)
4603 {
4604         SgenThreadInfo *info;
4605         pthread_t id;
4606         int stop_count;
4607         int old_errno = errno;
4608         gpointer regs [ARCH_NUM_REGS];
4609         gpointer stack_start;
4610
4611         id = pthread_self ();
4612         info = mono_sgen_thread_info_lookup (id);
4613         info->stopped_domain = mono_domain_get ();
4614         info->stopped_ip = (gpointer) ARCH_SIGCTX_IP (context);
4615         stop_count = global_stop_count;
4616         /* duplicate signal */
4617         if (0 && info->stop_count == stop_count) {
4618                 errno = old_errno;
4619                 return;
4620         }
4621 #ifdef HAVE_KW_THREAD
4622         /* update the remset info in the thread data structure */
4623         info->remset = remembered_set;
4624 #endif
4625         stack_start = (char*) ARCH_SIGCTX_SP (context) - REDZONE_SIZE;
4626         /* If stack_start is not within the limits, then don't set it
4627            in info and we will be restarted. */
4628         if (stack_start >= info->stack_start_limit && info->stack_start <= info->stack_end) {
4629                 info->stack_start = stack_start;
4630
4631                 ARCH_COPY_SIGCTX_REGS (regs, context);
4632                 info->stopped_regs = regs;
4633         } else {
4634                 g_assert (!info->stack_start);
4635         }
4636
4637         /* Notify the JIT */
4638         if (gc_callbacks.thread_suspend_func)
4639                 gc_callbacks.thread_suspend_func (info->runtime_data, context);
4640
4641         DEBUG (4, fprintf (gc_debug_file, "Posting suspend_ack_semaphore for suspend from %p %p\n", info, (gpointer)ARCH_GET_THREAD ()));
4642         /* notify the waiting thread */
4643         MONO_SEM_POST (suspend_ack_semaphore_ptr);
4644         info->stop_count = stop_count;
4645
4646         /* wait until we receive the restart signal */
4647         do {
4648                 info->signal = 0;
4649                 sigsuspend (&suspend_signal_mask);
4650         } while (info->signal != restart_signal_num);
4651
4652         DEBUG (4, fprintf (gc_debug_file, "Posting suspend_ack_semaphore for resume from %p %p\n", info, (gpointer)ARCH_GET_THREAD ()));
4653         /* notify the waiting thread */
4654         MONO_SEM_POST (suspend_ack_semaphore_ptr);
4655
4656         errno = old_errno;
4657 }
4658
4659 static void
4660 restart_handler (int sig)
4661 {
4662         SgenThreadInfo *info;
4663         int old_errno = errno;
4664
4665         info = mono_sgen_thread_info_lookup (pthread_self ());
4666         info->signal = restart_signal_num;
4667         DEBUG (4, fprintf (gc_debug_file, "Restart handler in %p %p\n", info, (gpointer)ARCH_GET_THREAD ()));
4668
4669         errno = old_errno;
4670 }
4671
4672 static void
4673 acquire_gc_locks (void)
4674 {
4675         LOCK_INTERRUPTION;
4676 }
4677
4678 static void
4679 release_gc_locks (void)
4680 {
4681         UNLOCK_INTERRUPTION;
4682 }
4683
4684 static TV_DECLARE (stop_world_time);
4685 static unsigned long max_pause_usec = 0;
4686
4687 /* LOCKING: assumes the GC lock is held */
4688 static int
4689 stop_world (int generation)
4690 {
4691         int count;
4692
4693         mono_profiler_gc_event (MONO_GC_EVENT_PRE_STOP_WORLD, generation);
4694         acquire_gc_locks ();
4695
4696         update_current_thread_stack (&count);
4697
4698         global_stop_count++;
4699         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 ()));
4700         TV_GETTIME (stop_world_time);
4701         count = mono_sgen_thread_handshake (suspend_signal_num);
4702         count -= restart_threads_until_none_in_managed_allocator ();
4703         g_assert (count >= 0);
4704         DEBUG (3, fprintf (gc_debug_file, "world stopped %d thread(s)\n", count));
4705         mono_profiler_gc_event (MONO_GC_EVENT_POST_STOP_WORLD, generation);
4706         return count;
4707 }
4708
4709 /* LOCKING: assumes the GC lock is held */
4710 static int
4711 restart_world (int generation)
4712 {
4713         int count, i;
4714         SgenThreadInfo *info;
4715         TV_DECLARE (end_sw);
4716         unsigned long usec;
4717
4718         /* notify the profiler of the leftovers */
4719         if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_GC_MOVES)) {
4720                 if (moved_objects_idx) {
4721                         mono_profiler_gc_moves (moved_objects, moved_objects_idx);
4722                         moved_objects_idx = 0;
4723                 }
4724         }
4725         mono_profiler_gc_event (MONO_GC_EVENT_PRE_START_WORLD, generation);
4726         for (i = 0; i < THREAD_HASH_SIZE; ++i) {
4727                 for (info = thread_table [i]; info; info = info->next) {
4728                         info->stack_start = NULL;
4729                         info->stopped_regs = NULL;
4730                 }
4731         }
4732
4733         release_gc_locks ();
4734
4735         count = mono_sgen_thread_handshake (restart_signal_num);
4736         TV_GETTIME (end_sw);
4737         usec = TV_ELAPSED (stop_world_time, end_sw);
4738         max_pause_usec = MAX (usec, max_pause_usec);
4739         DEBUG (2, fprintf (gc_debug_file, "restarted %d thread(s) (pause time: %d usec, max: %d)\n", count, (int)usec, (int)max_pause_usec));
4740         mono_profiler_gc_event (MONO_GC_EVENT_POST_START_WORLD, generation);
4741         return count;
4742 }
4743
4744 #endif /* USE_SIGNAL_BASED_START_STOP_WORLD */
4745
4746 void
4747 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
4748 {
4749         gc_callbacks = *callbacks;
4750 }
4751
4752 MonoGCCallbacks *
4753 mono_gc_get_gc_callbacks ()
4754 {
4755         return &gc_callbacks;
4756 }
4757
4758 /* Variables holding start/end nursery so it won't have to be passed at every call */
4759 static void *scan_area_arg_start, *scan_area_arg_end;
4760
4761 void
4762 mono_gc_conservatively_scan_area (void *start, void *end)
4763 {
4764         conservatively_pin_objects_from (start, end, scan_area_arg_start, scan_area_arg_end, PIN_TYPE_STACK);
4765 }
4766
4767 void*
4768 mono_gc_scan_object (void *obj)
4769 {
4770         g_assert_not_reached ();
4771         if (current_collection_generation == GENERATION_NURSERY)
4772                 major_collector.copy_object (&obj, &gray_queue);
4773         else
4774                 major_collector.copy_or_mark_object (&obj, &gray_queue);
4775         return obj;
4776 }
4777
4778 /*
4779  * Mark from thread stacks and registers.
4780  */
4781 static void
4782 scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise)
4783 {
4784         int i;
4785         SgenThreadInfo *info;
4786
4787         scan_area_arg_start = start_nursery;
4788         scan_area_arg_end = end_nursery;
4789
4790         for (i = 0; i < THREAD_HASH_SIZE; ++i) {
4791                 for (info = thread_table [i]; info; info = info->next) {
4792                         if (info->skip) {
4793                                 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));
4794                                 continue;
4795                         }
4796                         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));
4797                         if (gc_callbacks.thread_mark_func && !conservative_stack_mark)
4798                                 gc_callbacks.thread_mark_func (info->runtime_data, info->stack_start, info->stack_end, precise);
4799                         else if (!precise)
4800                                 conservatively_pin_objects_from (info->stack_start, info->stack_end, start_nursery, end_nursery, PIN_TYPE_STACK);
4801
4802                         if (!precise)
4803                                 conservatively_pin_objects_from (info->stopped_regs, info->stopped_regs + ARCH_NUM_REGS,
4804                                                 start_nursery, end_nursery, PIN_TYPE_STACK);
4805                 }
4806         }
4807 }
4808
4809 static void
4810 find_pinning_ref_from_thread (char *obj, size_t size)
4811 {
4812         int i;
4813         SgenThreadInfo *info;
4814         char *endobj = obj + size;
4815
4816         for (i = 0; i < THREAD_HASH_SIZE; ++i) {
4817                 for (info = thread_table [i]; info; info = info->next) {
4818                         char **start = (char**)info->stack_start;
4819                         if (info->skip)
4820                                 continue;
4821                         while (start < (char**)info->stack_end) {
4822                                 if (*start >= obj && *start < endobj) {
4823                                         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));
4824                                 }
4825                                 start++;
4826                         }
4827
4828                         /* FIXME: check info->stopped_regs */
4829                 }
4830         }
4831 }
4832
4833 static gboolean
4834 ptr_on_stack (void *ptr)
4835 {
4836         gpointer stack_start = &stack_start;
4837         SgenThreadInfo *info = mono_sgen_thread_info_lookup (ARCH_GET_THREAD ());
4838
4839         if (ptr >= stack_start && ptr < (gpointer)info->stack_end)
4840                 return TRUE;
4841         return FALSE;
4842 }
4843
4844 static mword*
4845 handle_remset (mword *p, void *start_nursery, void *end_nursery, gboolean global, GrayQueue *queue)
4846 {
4847         void **ptr;
4848         mword count;
4849         mword desc;
4850
4851         if (global)
4852                 HEAVY_STAT (++stat_global_remsets_processed);
4853         else
4854                 HEAVY_STAT (++stat_local_remsets_processed);
4855
4856         /* FIXME: exclude stack locations */
4857         switch ((*p) & REMSET_TYPE_MASK) {
4858         case REMSET_LOCATION:
4859                 ptr = (void**)(*p);
4860                 //__builtin_prefetch (ptr);
4861                 if (((void*)ptr < start_nursery || (void*)ptr >= end_nursery)) {
4862                         gpointer old = *ptr;
4863                         major_collector.copy_object (ptr, queue);
4864                         DEBUG (9, fprintf (gc_debug_file, "Overwrote remset at %p with %p\n", ptr, *ptr));
4865                         if (old)
4866                                 binary_protocol_ptr_update (ptr, old, *ptr, (gpointer)LOAD_VTABLE (*ptr), safe_object_get_size (*ptr));
4867                         if (!global && *ptr >= start_nursery && *ptr < end_nursery) {
4868                                 /*
4869                                  * If the object is pinned, each reference to it from nonpinned objects
4870                                  * becomes part of the global remset, which can grow very large.
4871                                  */
4872                                 DEBUG (9, fprintf (gc_debug_file, "Add to global remset because of pinning %p (%p %s)\n", ptr, *ptr, safe_name (*ptr)));
4873                                 mono_sgen_add_to_global_remset (ptr);
4874                         }
4875                 } else {
4876                         DEBUG (9, fprintf (gc_debug_file, "Skipping remset at %p holding %p\n", ptr, *ptr));
4877                 }
4878                 return p + 1;
4879         case REMSET_RANGE:
4880                 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
4881                 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
4882                         return p + 2;
4883                 count = p [1];
4884                 while (count-- > 0) {
4885                         major_collector.copy_object (ptr, queue);
4886                         DEBUG (9, fprintf (gc_debug_file, "Overwrote remset at %p with %p (count: %d)\n", ptr, *ptr, (int)count));
4887                         if (!global && *ptr >= start_nursery && *ptr < end_nursery)
4888                                 mono_sgen_add_to_global_remset (ptr);
4889                         ++ptr;
4890                 }
4891                 return p + 2;
4892         case REMSET_OBJECT:
4893                 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
4894                 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
4895                         return p + 1;
4896                 major_collector.minor_scan_object ((char*)ptr, queue);
4897                 return p + 1;
4898         case REMSET_VTYPE: {
4899                 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
4900                 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
4901                         return p + 3;
4902                 desc = p [1];
4903                 count = p [2];
4904                 while (count-- > 0)
4905                         ptr = (void**) major_collector.minor_scan_vtype ((char*)ptr, desc, start_nursery, end_nursery, queue);
4906                 return p + 3;
4907         }
4908         default:
4909                 g_assert_not_reached ();
4910         }
4911         return NULL;
4912 }
4913
4914 #ifdef HEAVY_STATISTICS
4915 static mword*
4916 collect_store_remsets (RememberedSet *remset, mword *bumper)
4917 {
4918         mword *p = remset->data;
4919         mword last = 0;
4920         mword last1 = 0;
4921         mword last2 = 0;
4922
4923         while (p < remset->store_next) {
4924                 switch ((*p) & REMSET_TYPE_MASK) {
4925                 case REMSET_LOCATION:
4926                         *bumper++ = *p;
4927                         if (*p == last)
4928                                 ++stat_saved_remsets_1;
4929                         last = *p;
4930                         if (*p == last1 || *p == last2) {
4931                                 ++stat_saved_remsets_2;
4932                         } else {
4933                                 last2 = last1;
4934                                 last1 = *p;
4935                         }
4936                         p += 1;
4937                         break;
4938                 case REMSET_RANGE:
4939                         p += 2;
4940                         break;
4941                 case REMSET_OBJECT:
4942                         p += 1;
4943                         break;
4944                 case REMSET_VTYPE:
4945                         p += 3;
4946                         break;
4947                 default:
4948                         g_assert_not_reached ();
4949                 }
4950         }
4951
4952         return bumper;
4953 }
4954
4955 static void
4956 remset_stats (void)
4957 {
4958         RememberedSet *remset;
4959         int size = 0;
4960         SgenThreadInfo *info;
4961         int i;
4962         mword *addresses, *bumper, *p, *r;
4963
4964         for (i = 0; i < THREAD_HASH_SIZE; ++i) {
4965                 for (info = thread_table [i]; info; info = info->next) {
4966                         for (remset = info->remset; remset; remset = remset->next)
4967                                 size += remset->store_next - remset->data;
4968                 }
4969         }
4970         for (remset = freed_thread_remsets; remset; remset = remset->next)
4971                 size += remset->store_next - remset->data;
4972         for (remset = global_remset; remset; remset = remset->next)
4973                 size += remset->store_next - remset->data;
4974
4975         bumper = addresses = mono_sgen_alloc_internal_dynamic (sizeof (mword) * size, INTERNAL_MEM_STATISTICS);
4976
4977         for (i = 0; i < THREAD_HASH_SIZE; ++i) {
4978                 for (info = thread_table [i]; info; info = info->next) {
4979                         for (remset = info->remset; remset; remset = remset->next)
4980                                 bumper = collect_store_remsets (remset, bumper);
4981                 }
4982         }
4983         for (remset = global_remset; remset; remset = remset->next)
4984                 bumper = collect_store_remsets (remset, bumper);
4985         for (remset = freed_thread_remsets; remset; remset = remset->next)
4986                 bumper = collect_store_remsets (remset, bumper);
4987
4988         g_assert (bumper <= addresses + size);
4989
4990         stat_store_remsets += bumper - addresses;
4991
4992         sort_addresses ((void**)addresses, bumper - addresses);
4993         p = addresses;
4994         r = addresses + 1;
4995         while (r < bumper) {
4996                 if (*r != *p)
4997                         *++p = *r;
4998                 ++r;
4999         }
5000
5001         stat_store_remsets_unique += p - addresses;
5002
5003         mono_sgen_free_internal_dynamic (addresses, sizeof (mword) * size, INTERNAL_MEM_STATISTICS);
5004 }
5005 #endif
5006
5007 static void
5008 clear_thread_store_remset_buffer (SgenThreadInfo *info)
5009 {
5010         *info->store_remset_buffer_index_addr = 0;
5011         memset (*info->store_remset_buffer_addr, 0, sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
5012 }
5013
5014 static size_t
5015 remset_byte_size (RememberedSet *remset)
5016 {
5017         return sizeof (RememberedSet) + (remset->end_set - remset->data) * sizeof (gpointer);
5018 }
5019
5020 static void
5021 scan_from_remsets (void *start_nursery, void *end_nursery, GrayQueue *queue)
5022 {
5023         int i;
5024         SgenThreadInfo *info;
5025         RememberedSet *remset;
5026         GenericStoreRememberedSet *store_remset;
5027         mword *p, *next_p, *store_pos;
5028
5029 #ifdef HEAVY_STATISTICS
5030         remset_stats ();
5031 #endif
5032
5033         /* the global one */
5034         for (remset = global_remset; remset; remset = remset->next) {
5035                 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));
5036                 store_pos = remset->data;
5037                 for (p = remset->data; p < remset->store_next; p = next_p) {
5038                         void **ptr = (void**)p [0];
5039
5040                         /*Ignore previously processed remset.*/
5041                         if (!global_remset_location_was_not_added (ptr)) {
5042                                 next_p = p + 1;
5043                                 continue;
5044                         }
5045
5046                         next_p = handle_remset (p, start_nursery, end_nursery, TRUE, queue);
5047
5048                         /* 
5049                          * Clear global remsets of locations which no longer point to the 
5050                          * nursery. Otherwise, they could grow indefinitely between major 
5051                          * collections.
5052                          *
5053                          * Since all global remsets are location remsets, we don't need to unmask the pointer.
5054                          */
5055                         if (ptr_in_nursery (*ptr)) {
5056                                 *store_pos ++ = p [0];
5057                                 HEAVY_STAT (++stat_global_remsets_readded);
5058                         }
5059                 }
5060
5061                 /* Truncate the remset */
5062                 remset->store_next = store_pos;
5063         }
5064
5065         /* the generic store ones */
5066         store_remset = generic_store_remsets;
5067         while (store_remset) {
5068                 GenericStoreRememberedSet *next = store_remset->next;
5069
5070                 for (i = 0; i < STORE_REMSET_BUFFER_SIZE - 1; ++i) {
5071                         gpointer addr = store_remset->data [i];
5072                         if (addr)
5073                                 handle_remset ((mword*)&addr, start_nursery, end_nursery, FALSE, queue);
5074                 }
5075
5076                 mono_sgen_free_internal (store_remset, INTERNAL_MEM_STORE_REMSET);
5077
5078                 store_remset = next;
5079         }
5080         generic_store_remsets = NULL;
5081
5082         /* the per-thread ones */
5083         for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5084                 for (info = thread_table [i]; info; info = info->next) {
5085                         RememberedSet *next;
5086                         int j;
5087                         for (remset = info->remset; remset; remset = next) {
5088                                 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));
5089                                 for (p = remset->data; p < remset->store_next;)
5090                                         p = handle_remset (p, start_nursery, end_nursery, FALSE, queue);
5091                                 remset->store_next = remset->data;
5092                                 next = remset->next;
5093                                 remset->next = NULL;
5094                                 if (remset != info->remset) {
5095                                         DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5096                                         mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
5097                                 }
5098                         }
5099                         for (j = 0; j < *info->store_remset_buffer_index_addr; ++j)
5100                                 handle_remset ((mword*)*info->store_remset_buffer_addr + j + 1, start_nursery, end_nursery, FALSE, queue);
5101                         clear_thread_store_remset_buffer (info);
5102                 }
5103         }
5104
5105         /* the freed thread ones */
5106         while (freed_thread_remsets) {
5107                 RememberedSet *next;
5108                 remset = freed_thread_remsets;
5109                 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));
5110                 for (p = remset->data; p < remset->store_next;)
5111                         p = handle_remset (p, start_nursery, end_nursery, FALSE, queue);
5112                 next = remset->next;
5113                 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5114                 mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
5115                 freed_thread_remsets = next;
5116         }
5117 }
5118
5119 /*
5120  * Clear the info in the remembered sets: we're doing a major collection, so
5121  * the per-thread ones are not needed and the global ones will be reconstructed
5122  * during the copy.
5123  */
5124 static void
5125 clear_remsets (void)
5126 {
5127         int i;
5128         SgenThreadInfo *info;
5129         RememberedSet *remset, *next;
5130
5131         /* the global list */
5132         for (remset = global_remset; remset; remset = next) {
5133                 remset->store_next = remset->data;
5134                 next = remset->next;
5135                 remset->next = NULL;
5136                 if (remset != global_remset) {
5137                         DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5138                         mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
5139                 }
5140         }
5141         /* the generic store ones */
5142         while (generic_store_remsets) {
5143                 GenericStoreRememberedSet *gs_next = generic_store_remsets->next;
5144                 mono_sgen_free_internal (generic_store_remsets, INTERNAL_MEM_STORE_REMSET);
5145                 generic_store_remsets = gs_next;
5146         }
5147         /* the per-thread ones */
5148         for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5149                 for (info = thread_table [i]; info; info = info->next) {
5150                         for (remset = info->remset; remset; remset = next) {
5151                                 remset->store_next = remset->data;
5152                                 next = remset->next;
5153                                 remset->next = NULL;
5154                                 if (remset != info->remset) {
5155                                         DEBUG (3, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5156                                         mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
5157                                 }
5158                         }
5159                         clear_thread_store_remset_buffer (info);
5160                 }
5161         }
5162
5163         /* the freed thread ones */
5164         while (freed_thread_remsets) {
5165                 next = freed_thread_remsets->next;
5166                 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", freed_thread_remsets->data));
5167                 mono_sgen_free_internal_dynamic (freed_thread_remsets, remset_byte_size (freed_thread_remsets), INTERNAL_MEM_REMSET);
5168                 freed_thread_remsets = next;
5169         }
5170 }
5171
5172 /*
5173  * Clear the thread local TLAB variables for all threads.
5174  */
5175 static void
5176 clear_tlabs (void)
5177 {
5178         SgenThreadInfo *info;
5179         int i;
5180
5181         for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5182                 for (info = thread_table [i]; info; info = info->next) {
5183                         /* A new TLAB will be allocated when the thread does its first allocation */
5184                         *info->tlab_start_addr = NULL;
5185                         *info->tlab_next_addr = NULL;
5186                         *info->tlab_temp_end_addr = NULL;
5187                         *info->tlab_real_end_addr = NULL;
5188                 }
5189         }
5190 }
5191
5192 /* LOCKING: assumes the GC lock is held */
5193 static SgenThreadInfo*
5194 gc_register_current_thread (void *addr)
5195 {
5196         int hash;
5197         SgenThreadInfo* info = malloc (sizeof (SgenThreadInfo));
5198 #ifndef HAVE_KW_THREAD
5199         SgenThreadInfo *__thread_info__ = info;
5200 #endif
5201
5202         if (!info)
5203                 return NULL;
5204
5205         memset (info, 0, sizeof (SgenThreadInfo));
5206 #ifndef HAVE_KW_THREAD
5207         info->tlab_start = info->tlab_next = info->tlab_temp_end = info->tlab_real_end = NULL;
5208
5209         g_assert (!pthread_getspecific (thread_info_key));
5210         pthread_setspecific (thread_info_key, info);
5211 #else
5212         thread_info = info;
5213 #endif
5214
5215         info->id = ARCH_GET_THREAD ();
5216         info->stop_count = -1;
5217         info->skip = 0;
5218         info->signal = 0;
5219         info->stack_start = NULL;
5220         info->tlab_start_addr = &TLAB_START;
5221         info->tlab_next_addr = &TLAB_NEXT;
5222         info->tlab_temp_end_addr = &TLAB_TEMP_END;
5223         info->tlab_real_end_addr = &TLAB_REAL_END;
5224         info->store_remset_buffer_addr = &STORE_REMSET_BUFFER;
5225         info->store_remset_buffer_index_addr = &STORE_REMSET_BUFFER_INDEX;
5226         info->stopped_ip = NULL;
5227         info->stopped_domain = NULL;
5228         info->stopped_regs = NULL;
5229
5230         binary_protocol_thread_register ((gpointer)info->id);
5231
5232 #ifdef HAVE_KW_THREAD
5233         tlab_next_addr = &tlab_next;
5234         store_remset_buffer_index_addr = &store_remset_buffer_index;
5235 #endif
5236
5237         /* try to get it with attributes first */
5238 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
5239         {
5240                 size_t size;
5241                 void *sstart;
5242                 pthread_attr_t attr;
5243                 pthread_getattr_np (pthread_self (), &attr);
5244                 pthread_attr_getstack (&attr, &sstart, &size);
5245                 info->stack_start_limit = sstart;
5246                 info->stack_end = (char*)sstart + size;
5247                 pthread_attr_destroy (&attr);
5248         }
5249 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
5250                  info->stack_end = (char*)pthread_get_stackaddr_np (pthread_self ());
5251                  info->stack_start_limit = (char*)info->stack_end - pthread_get_stacksize_np (pthread_self ());
5252 #else
5253         {
5254                 /* FIXME: we assume the stack grows down */
5255                 gsize stack_bottom = (gsize)addr;
5256                 stack_bottom += 4095;
5257                 stack_bottom &= ~4095;
5258                 info->stack_end = (char*)stack_bottom;
5259         }
5260 #endif
5261
5262 #ifdef HAVE_KW_THREAD
5263         stack_end = info->stack_end;
5264 #endif
5265
5266         /* hash into the table */
5267         hash = HASH_PTHREAD_T (info->id) % THREAD_HASH_SIZE;
5268         info->next = thread_table [hash];
5269         thread_table [hash] = info;
5270
5271         info->remset = alloc_remset (DEFAULT_REMSET_SIZE, info);
5272         pthread_setspecific (remembered_set_key, info->remset);
5273 #ifdef HAVE_KW_THREAD
5274         remembered_set = info->remset;
5275 #endif
5276
5277         STORE_REMSET_BUFFER = mono_sgen_alloc_internal (INTERNAL_MEM_STORE_REMSET);
5278         STORE_REMSET_BUFFER_INDEX = 0;
5279
5280         DEBUG (3, fprintf (gc_debug_file, "registered thread %p (%p) (hash: %d)\n", info, (gpointer)info->id, hash));
5281
5282         if (gc_callbacks.thread_attach_func)
5283                 info->runtime_data = gc_callbacks.thread_attach_func ();
5284
5285         return info;
5286 }
5287
5288 static void
5289 add_generic_store_remset_from_buffer (gpointer *buffer)
5290 {
5291         GenericStoreRememberedSet *remset = mono_sgen_alloc_internal (INTERNAL_MEM_STORE_REMSET);
5292         memcpy (remset->data, buffer + 1, sizeof (gpointer) * (STORE_REMSET_BUFFER_SIZE - 1));
5293         remset->next = generic_store_remsets;
5294         generic_store_remsets = remset;
5295 }
5296
5297 static void
5298 unregister_current_thread (void)
5299 {
5300         int hash;
5301         SgenThreadInfo *prev = NULL;
5302         SgenThreadInfo *p;
5303         RememberedSet *rset;
5304         ARCH_THREAD_TYPE id = ARCH_GET_THREAD ();
5305
5306         binary_protocol_thread_unregister ((gpointer)id);
5307
5308         hash = HASH_PTHREAD_T (id) % THREAD_HASH_SIZE;
5309         p = thread_table [hash];
5310         assert (p);
5311         DEBUG (3, fprintf (gc_debug_file, "unregister thread %p (%p)\n", p, (gpointer)p->id));
5312         while (!ARCH_THREAD_EQUALS (p->id, id)) {
5313                 prev = p;
5314                 p = p->next;
5315         }
5316         if (prev == NULL) {
5317                 thread_table [hash] = p->next;
5318         } else {
5319                 prev->next = p->next;
5320         }
5321         if (p->remset) {
5322                 if (freed_thread_remsets) {
5323                         for (rset = p->remset; rset->next; rset = rset->next)
5324                                 ;
5325                         rset->next = freed_thread_remsets;
5326                         freed_thread_remsets = p->remset;
5327                 } else {
5328                         freed_thread_remsets = p->remset;
5329                 }
5330         }
5331         if (*p->store_remset_buffer_index_addr)
5332                 add_generic_store_remset_from_buffer (*p->store_remset_buffer_addr);
5333         mono_sgen_free_internal (*p->store_remset_buffer_addr, INTERNAL_MEM_STORE_REMSET);
5334         free (p);
5335 }
5336
5337 static void
5338 unregister_thread (void *k)
5339 {
5340         g_assert (!mono_domain_get ());
5341         LOCK_GC;
5342         unregister_current_thread ();
5343         UNLOCK_GC;
5344 }
5345
5346 gboolean
5347 mono_gc_register_thread (void *baseptr)
5348 {
5349         SgenThreadInfo *info;
5350
5351         LOCK_GC;
5352         init_stats ();
5353         info = mono_sgen_thread_info_lookup (ARCH_GET_THREAD ());
5354         if (info == NULL)
5355                 info = gc_register_current_thread (baseptr);
5356         UNLOCK_GC;
5357         return info != NULL;
5358 }
5359
5360 #if USE_PTHREAD_INTERCEPT
5361
5362 typedef struct {
5363         void *(*start_routine) (void *);
5364         void *arg;
5365         int flags;
5366         MonoSemType registered;
5367 } SgenThreadStartInfo;
5368
5369 static void*
5370 gc_start_thread (void *arg)
5371 {
5372         SgenThreadStartInfo *start_info = arg;
5373         SgenThreadInfo* info;
5374         void *t_arg = start_info->arg;
5375         void *(*start_func) (void*) = start_info->start_routine;
5376         void *result;
5377         int post_result;
5378
5379         LOCK_GC;
5380         info = gc_register_current_thread (&result);
5381         UNLOCK_GC;
5382         post_result = MONO_SEM_POST (&(start_info->registered));
5383         g_assert (!post_result);
5384         result = start_func (t_arg);
5385         g_assert (!mono_domain_get ());
5386         /*
5387          * this is done by the pthread key dtor
5388         LOCK_GC;
5389         unregister_current_thread ();
5390         UNLOCK_GC;
5391         */
5392
5393         return result;
5394 }
5395
5396 int
5397 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
5398 {
5399         SgenThreadStartInfo *start_info;
5400         int result;
5401
5402         start_info = malloc (sizeof (SgenThreadStartInfo));
5403         if (!start_info)
5404                 return ENOMEM;
5405         MONO_SEM_INIT (&(start_info->registered), 0);
5406         start_info->arg = arg;
5407         start_info->start_routine = start_routine;
5408
5409         result = pthread_create (new_thread, attr, gc_start_thread, start_info);
5410         if (result == 0) {
5411                 while (MONO_SEM_WAIT (&(start_info->registered)) != 0) {
5412                         /*if (EINTR != errno) ABORT("sem_wait failed"); */
5413                 }
5414         }
5415         MONO_SEM_DESTROY (&(start_info->registered));
5416         free (start_info);
5417         return result;
5418 }
5419
5420 int
5421 mono_gc_pthread_join (pthread_t thread, void **retval)
5422 {
5423         return pthread_join (thread, retval);
5424 }
5425
5426 int
5427 mono_gc_pthread_detach (pthread_t thread)
5428 {
5429         return pthread_detach (thread);
5430 }
5431
5432 #endif /* USE_PTHREAD_INTERCEPT */
5433
5434 /*
5435  * ######################################################################
5436  * ########  Write barriers
5437  * ######################################################################
5438  */
5439
5440 /*
5441  * This causes the compile to extend the liveness of 'v' till the call to dummy_use
5442  */
5443 static void
5444 dummy_use (gpointer v) {
5445         __asm__ volatile ("" : "=r"(v) : "r"(v));
5446 }
5447
5448
5449 static RememberedSet*
5450 alloc_remset (int size, gpointer id) {
5451         RememberedSet* res = mono_sgen_alloc_internal_dynamic (sizeof (RememberedSet) + (size * sizeof (gpointer)), INTERNAL_MEM_REMSET);
5452         res->store_next = res->data;
5453         res->end_set = res->data + size;
5454         res->next = NULL;
5455         DEBUG (4, fprintf (gc_debug_file, "Allocated remset size %d at %p for %p\n", size, res->data, id));
5456         return res;
5457 }
5458
5459 /*
5460  * Note: the write barriers first do the needed GC work and then do the actual store:
5461  * this way the value is visible to the conservative GC scan after the write barrier
5462  * itself. If a GC interrupts the barrier in the middle, value will be kept alive by
5463  * the conservative scan, otherwise by the remembered set scan.
5464  */
5465 void
5466 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
5467 {
5468         HEAVY_STAT (++stat_wbarrier_set_field);
5469         if (ptr_in_nursery (field_ptr)) {
5470                 *(void**)field_ptr = value;
5471                 return;
5472         }
5473         DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", field_ptr));
5474         if (use_cardtable) {
5475                 *(void**)field_ptr = value;
5476                 if (ptr_in_nursery (value))
5477                         sgen_card_table_mark_address ((mword)field_ptr);
5478                 dummy_use (value);
5479         } else {
5480                 RememberedSet *rs;
5481                 TLAB_ACCESS_INIT;
5482
5483                 LOCK_GC;
5484                 rs = REMEMBERED_SET;
5485                 if (rs->store_next < rs->end_set) {
5486                         *(rs->store_next++) = (mword)field_ptr;
5487                         *(void**)field_ptr = value;
5488                         UNLOCK_GC;
5489                         return;
5490                 }
5491                 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
5492                 rs->next = REMEMBERED_SET;
5493                 REMEMBERED_SET = rs;
5494 #ifdef HAVE_KW_THREAD
5495                 mono_sgen_thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
5496 #endif
5497                 *(rs->store_next++) = (mword)field_ptr;
5498                 *(void**)field_ptr = value;
5499                 UNLOCK_GC;
5500         }
5501 }
5502
5503 void
5504 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
5505 {
5506         HEAVY_STAT (++stat_wbarrier_set_arrayref);
5507         if (ptr_in_nursery (slot_ptr)) {
5508                 *(void**)slot_ptr = value;
5509                 return;
5510         }
5511         DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", slot_ptr));
5512         if (use_cardtable) {
5513                 *(void**)slot_ptr = value;
5514                 if (ptr_in_nursery (value))
5515                         sgen_card_table_mark_address ((mword)slot_ptr);
5516                 dummy_use (value);
5517         } else {
5518                 RememberedSet *rs;
5519                 TLAB_ACCESS_INIT;
5520
5521                 LOCK_GC;
5522                 rs = REMEMBERED_SET;
5523                 if (rs->store_next < rs->end_set) {
5524                         *(rs->store_next++) = (mword)slot_ptr;
5525                         *(void**)slot_ptr = value;
5526                         UNLOCK_GC;
5527                         return;
5528                 }
5529                 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
5530                 rs->next = REMEMBERED_SET;
5531                 REMEMBERED_SET = rs;
5532 #ifdef HAVE_KW_THREAD
5533                 mono_sgen_thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
5534 #endif
5535                 *(rs->store_next++) = (mword)slot_ptr;
5536                 *(void**)slot_ptr = value;
5537                 UNLOCK_GC;
5538         }
5539 }
5540
5541 void
5542 mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
5543 {
5544         HEAVY_STAT (++stat_wbarrier_arrayref_copy);
5545         /*This check can be done without taking a lock since dest_ptr array is pinned*/
5546         if (ptr_in_nursery (dest_ptr) || count <= 0) {
5547                 memmove (dest_ptr, src_ptr, count * sizeof (gpointer));
5548                 return;
5549         }
5550
5551         if (use_cardtable) {
5552                 gpointer *dest = dest_ptr;
5553                 gpointer *src = src_ptr;
5554
5555                 /*overlapping that required backward copying*/
5556                 if (src < dest && (src + count) > dest) {
5557                         gpointer *start = dest;
5558                         dest += count - 1;
5559                         src += count - 1;
5560
5561                         for (; dest >= start; --src, --dest) {
5562                                 gpointer value = *src;
5563                                 *dest = value;
5564                                 if (ptr_in_nursery (value))
5565                                         sgen_card_table_mark_address ((mword)dest);
5566                                 dummy_use (value);
5567                         }
5568                 } else {
5569                         gpointer *end = dest + count;
5570                         for (; dest < end; ++src, ++dest) {
5571                                 gpointer value = *src;
5572                                 *dest = value;
5573                                 if (ptr_in_nursery (value))
5574                                         sgen_card_table_mark_address ((mword)dest);
5575                                 dummy_use (value);
5576                         }
5577                 }
5578         } else {
5579                 RememberedSet *rs;
5580                 TLAB_ACCESS_INIT;
5581                 LOCK_GC;
5582                 memmove (dest_ptr, src_ptr, count * sizeof (gpointer));
5583
5584                 rs = REMEMBERED_SET;
5585                 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p, %d\n", dest_ptr, count));
5586                 if (rs->store_next + 1 < rs->end_set) {
5587                         *(rs->store_next++) = (mword)dest_ptr | REMSET_RANGE;
5588                         *(rs->store_next++) = count;
5589                         UNLOCK_GC;
5590                         return;
5591                 }
5592                 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
5593                 rs->next = REMEMBERED_SET;
5594                 REMEMBERED_SET = rs;
5595 #ifdef HAVE_KW_THREAD
5596                 mono_sgen_thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
5597 #endif
5598                 *(rs->store_next++) = (mword)dest_ptr | REMSET_RANGE;
5599                 *(rs->store_next++) = count;
5600
5601                 UNLOCK_GC;
5602         }
5603 }
5604
5605 static char *found_obj;
5606
5607 static void
5608 find_object_for_ptr_callback (char *obj, size_t size, char *ptr)
5609 {
5610         if (ptr >= obj && ptr < obj + size) {
5611                 g_assert (!found_obj);
5612                 found_obj = obj;
5613         }
5614 }
5615
5616 /* for use in the debugger */
5617 char* find_object_for_ptr (char *ptr);
5618 char*
5619 find_object_for_ptr (char *ptr)
5620 {
5621         LOSObject *bigobj;
5622
5623         if (ptr >= nursery_section->data && ptr < nursery_section->end_data) {
5624                 found_obj = NULL;
5625                 mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
5626                                 (IterateObjectCallbackFunc)find_object_for_ptr_callback, ptr);
5627                 if (found_obj)
5628                         return found_obj;
5629         }
5630
5631         for (bigobj = los_object_list; bigobj; bigobj = bigobj->next) {
5632                 if (ptr >= bigobj->data && ptr < bigobj->data + bigobj->size)
5633                         return bigobj->data;
5634         }
5635
5636         /*
5637          * Very inefficient, but this is debugging code, supposed to
5638          * be called from gdb, so we don't care.
5639          */
5640         found_obj = NULL;
5641         major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)find_object_for_ptr_callback, ptr);
5642         return found_obj;
5643 }
5644
5645 static void
5646 evacuate_remset_buffer (void)
5647 {
5648         gpointer *buffer;
5649         TLAB_ACCESS_INIT;
5650
5651         buffer = STORE_REMSET_BUFFER;
5652
5653         add_generic_store_remset_from_buffer (buffer);
5654         memset (buffer, 0, sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
5655
5656         STORE_REMSET_BUFFER_INDEX = 0;
5657 }
5658
5659 void
5660 mono_gc_wbarrier_generic_nostore (gpointer ptr)
5661 {
5662         gpointer *buffer;
5663         int index;
5664         TLAB_ACCESS_INIT;
5665
5666         HEAVY_STAT (++stat_wbarrier_generic_store);
5667
5668 #ifdef XDOMAIN_CHECKS_IN_WBARRIER
5669         /* FIXME: ptr_in_heap must be called with the GC lock held */
5670         if (xdomain_checks && *(MonoObject**)ptr && ptr_in_heap (ptr)) {
5671                 char *start = find_object_for_ptr (ptr);
5672                 MonoObject *value = *(MonoObject**)ptr;
5673                 LOCK_GC;
5674                 g_assert (start);
5675                 if (start) {
5676                         MonoObject *obj = (MonoObject*)start;
5677                         if (obj->vtable->domain != value->vtable->domain)
5678                                 g_assert (is_xdomain_ref_allowed (ptr, start, obj->vtable->domain));
5679                 }
5680                 UNLOCK_GC;
5681         }
5682 #endif
5683
5684         if (*(gpointer*)ptr)
5685                 binary_protocol_wbarrier (ptr, *(gpointer*)ptr, (gpointer)LOAD_VTABLE (*(gpointer*)ptr));
5686
5687         if (ptr_in_nursery (ptr) || ptr_on_stack (ptr) || !ptr_in_nursery (*(gpointer*)ptr)) {
5688                 DEBUG (8, fprintf (gc_debug_file, "Skipping remset at %p\n", ptr));
5689                 return;
5690         }
5691
5692         if (use_cardtable) {
5693                 if (ptr_in_nursery(*(gpointer*)ptr))
5694                         sgen_card_table_mark_address ((mword)ptr);
5695                 return;
5696         }
5697
5698         LOCK_GC;
5699
5700         buffer = STORE_REMSET_BUFFER;
5701         index = STORE_REMSET_BUFFER_INDEX;
5702         /* This simple optimization eliminates a sizable portion of
5703            entries.  Comparing it to the last but one entry as well
5704            doesn't eliminate significantly more entries. */
5705         if (buffer [index] == ptr) {
5706                 UNLOCK_GC;
5707                 return;
5708         }
5709
5710         DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", ptr));
5711         HEAVY_STAT (++stat_wbarrier_generic_store_remset);
5712
5713         ++index;
5714         if (index >= STORE_REMSET_BUFFER_SIZE) {
5715                 evacuate_remset_buffer ();
5716                 index = STORE_REMSET_BUFFER_INDEX;
5717                 g_assert (index == 0);
5718                 ++index;
5719         }
5720         buffer [index] = ptr;
5721         STORE_REMSET_BUFFER_INDEX = index;
5722
5723         UNLOCK_GC;
5724 }
5725
5726 void
5727 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
5728 {
5729         DEBUG (8, fprintf (gc_debug_file, "Wbarrier store at %p to %p (%s)\n", ptr, value, value ? safe_name (value) : "null"));
5730         *(void**)ptr = value;
5731         if (ptr_in_nursery (value))
5732                 mono_gc_wbarrier_generic_nostore (ptr);
5733         dummy_use (value);
5734 }
5735
5736 void mono_gc_wbarrier_value_copy_bitmap (gpointer _dest, gpointer _src, int size, unsigned bitmap)
5737 {
5738         mword *dest = _dest;
5739         mword *src = _src;
5740
5741         while (size) {
5742                 if (bitmap & 0x1)
5743                         mono_gc_wbarrier_generic_store (dest, (MonoObject*)*src);
5744                 else
5745                         *dest = *src;
5746                 ++src;
5747                 ++dest;
5748                 size -= SIZEOF_VOID_P;
5749                 bitmap >>= 1;
5750         }
5751 }
5752
5753
5754 void
5755 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
5756 {
5757         RememberedSet *rs;
5758         size_t size = count * mono_class_value_size (klass, NULL);
5759         TLAB_ACCESS_INIT;
5760         HEAVY_STAT (++stat_wbarrier_value_copy);
5761         g_assert (klass->valuetype);
5762         LOCK_GC;
5763         memmove (dest, src, size);
5764         if (use_cardtable) {
5765                 sgen_card_table_mark_range ((mword)dest, size);
5766         } else {
5767                 rs = REMEMBERED_SET;
5768                 if (ptr_in_nursery (dest) || ptr_on_stack (dest) || !klass->has_references) {
5769                         UNLOCK_GC;
5770                         return;
5771                 }
5772                 g_assert (klass->gc_descr_inited);
5773                 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));
5774
5775                 if (rs->store_next + 3 < rs->end_set) {
5776                         *(rs->store_next++) = (mword)dest | REMSET_VTYPE;
5777                         *(rs->store_next++) = (mword)klass->gc_descr;
5778                         *(rs->store_next++) = (mword)count;
5779                         UNLOCK_GC;
5780                         return;
5781                 }
5782                 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
5783                 rs->next = REMEMBERED_SET;
5784                 REMEMBERED_SET = rs;
5785 #ifdef HAVE_KW_THREAD
5786                 mono_sgen_thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
5787 #endif
5788                 *(rs->store_next++) = (mword)dest | REMSET_VTYPE;
5789                 *(rs->store_next++) = (mword)klass->gc_descr;
5790                 *(rs->store_next++) = (mword)count;
5791         }
5792         UNLOCK_GC;
5793 }
5794
5795 /**
5796  * mono_gc_wbarrier_object_copy:
5797  *
5798  * Write barrier to call when obj is the result of a clone or copy of an object.
5799  */
5800 void
5801 mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
5802 {
5803         RememberedSet *rs;
5804         int size;
5805
5806         TLAB_ACCESS_INIT;
5807         HEAVY_STAT (++stat_wbarrier_object_copy);
5808         rs = REMEMBERED_SET;
5809         DEBUG (6, fprintf (gc_debug_file, "Adding object remset for %p\n", obj));
5810         size = mono_object_class (obj)->instance_size;
5811         LOCK_GC;
5812         /* do not copy the sync state */
5813         memcpy ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
5814                         size - sizeof (MonoObject));
5815         if (ptr_in_nursery (obj) || ptr_on_stack (obj)) {
5816                 UNLOCK_GC;
5817                 return;
5818         }
5819         if (rs->store_next < rs->end_set) {
5820                 *(rs->store_next++) = (mword)obj | REMSET_OBJECT;
5821                 UNLOCK_GC;
5822                 return;
5823         }
5824         rs = alloc_remset (rs->end_set - rs->data, (void*)1);
5825         rs->next = REMEMBERED_SET;
5826         REMEMBERED_SET = rs;
5827 #ifdef HAVE_KW_THREAD
5828         mono_sgen_thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
5829 #endif
5830         *(rs->store_next++) = (mword)obj | REMSET_OBJECT;
5831         UNLOCK_GC;
5832 }
5833
5834 /*
5835  * ######################################################################
5836  * ########  Collector debugging
5837  * ######################################################################
5838  */
5839
5840 const char*descriptor_types [] = {
5841         "run_length",
5842         "small_bitmap",
5843         "string",
5844         "complex",
5845         "vector",
5846         "array",
5847         "large_bitmap",
5848         "complex_arr"
5849 };
5850
5851 void
5852 describe_ptr (char *ptr)
5853 {
5854         MonoVTable *vtable;
5855         mword desc;
5856         int type;
5857
5858         if (ptr_in_nursery (ptr)) {
5859                 printf ("Pointer inside nursery.\n");
5860         } else {
5861                 if (major_collector.ptr_is_in_non_pinned_space (ptr)) {
5862                         printf ("Pointer inside oldspace.\n");
5863                 } else if (major_collector.obj_is_from_pinned_alloc (ptr)) {
5864                         printf ("Pointer is inside a pinned chunk.\n");
5865                 } else {
5866                         printf ("Pointer unknown.\n");
5867                         return;
5868                 }
5869         }
5870
5871         if (object_is_pinned (ptr))
5872                 printf ("Object is pinned.\n");
5873
5874         if (object_is_forwarded (ptr))
5875                 printf ("Object is forwared.\n");
5876
5877         // FIXME: Handle pointers to the inside of objects
5878         vtable = (MonoVTable*)LOAD_VTABLE (ptr);
5879
5880         printf ("VTable: %p\n", vtable);
5881         if (vtable == NULL) {
5882                 printf ("VTable is invalid (empty).\n");
5883                 return;
5884         }
5885         if (ptr_in_nursery (vtable)) {
5886                 printf ("VTable is invalid (points inside nursery).\n");
5887                 return;
5888         }
5889         printf ("Class: %s\n", vtable->klass->name);
5890
5891         desc = ((GCVTable*)vtable)->desc;
5892         printf ("Descriptor: %lx\n", (long)desc);
5893
5894         type = desc & 0x7;
5895         printf ("Descriptor type: %d (%s)\n", type, descriptor_types [type]);
5896 }
5897
5898 static mword*
5899 find_in_remset_loc (mword *p, char *addr, gboolean *found)
5900 {
5901         void **ptr;
5902         mword count, desc;
5903         size_t skip_size;
5904
5905         switch ((*p) & REMSET_TYPE_MASK) {
5906         case REMSET_LOCATION:
5907                 if (*p == (mword)addr)
5908                         *found = TRUE;
5909                 return p + 1;
5910         case REMSET_RANGE:
5911                 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
5912                 count = p [1];
5913                 if ((void**)addr >= ptr && (void**)addr < ptr + count)
5914                         *found = TRUE;
5915                 return p + 2;
5916         case REMSET_OBJECT:
5917                 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
5918                 count = safe_object_get_size ((MonoObject*)ptr); 
5919                 count = ALIGN_UP (count);
5920                 count /= sizeof (mword);
5921                 if ((void**)addr >= ptr && (void**)addr < ptr + count)
5922                         *found = TRUE;
5923                 return p + 1;
5924         case REMSET_VTYPE:
5925                 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
5926                 desc = p [1];
5927                 count = p [2];
5928
5929                 switch (desc & 0x7) {
5930                 case DESC_TYPE_RUN_LENGTH:
5931                         OBJ_RUN_LEN_SIZE (skip_size, desc, ptr);
5932                         break;
5933                 case DESC_TYPE_SMALL_BITMAP:
5934                         OBJ_BITMAP_SIZE (skip_size, desc, start);
5935                         break;
5936                 default:
5937                         // FIXME:
5938                         g_assert_not_reached ();
5939                 }
5940
5941                 /* The descriptor includes the size of MonoObject */
5942                 skip_size -= sizeof (MonoObject);
5943                 skip_size *= count;
5944                 if ((void**)addr >= ptr && (void**)addr < ptr + (skip_size / sizeof (gpointer)))
5945                         *found = TRUE;
5946
5947                 return p + 3;
5948         default:
5949                 g_assert_not_reached ();
5950         }
5951         return NULL;
5952 }
5953
5954 /*
5955  * Return whenever ADDR occurs in the remembered sets
5956  */
5957 static gboolean
5958 find_in_remsets (char *addr)
5959 {
5960         int i;
5961         SgenThreadInfo *info;
5962         RememberedSet *remset;
5963         GenericStoreRememberedSet *store_remset;
5964         mword *p;
5965         gboolean found = FALSE;
5966
5967         /* the global one */
5968         for (remset = global_remset; remset; remset = remset->next) {
5969                 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));
5970                 for (p = remset->data; p < remset->store_next;) {
5971                         p = find_in_remset_loc (p, addr, &found);
5972                         if (found)
5973                                 return TRUE;
5974                 }
5975         }
5976
5977         /* the generic store ones */
5978         for (store_remset = generic_store_remsets; store_remset; store_remset = store_remset->next) {
5979                 for (i = 0; i < STORE_REMSET_BUFFER_SIZE - 1; ++i) {
5980                         if (store_remset->data [i] == addr)
5981                                 return TRUE;
5982                 }
5983         }
5984
5985         /* the per-thread ones */
5986         for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5987                 for (info = thread_table [i]; info; info = info->next) {
5988                         int j;
5989                         for (remset = info->remset; remset; remset = remset->next) {
5990                                 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));
5991                                 for (p = remset->data; p < remset->store_next;) {
5992                                         p = find_in_remset_loc (p, addr, &found);
5993                                         if (found)
5994                                                 return TRUE;
5995                                 }
5996                         }
5997                         for (j = 0; j < *info->store_remset_buffer_index_addr; ++j) {
5998                                 if ((*info->store_remset_buffer_addr) [j + 1] == addr)
5999                                         return TRUE;
6000                         }
6001                 }
6002         }
6003
6004         /* the freed thread ones */
6005         for (remset = freed_thread_remsets; remset; remset = remset->next) {
6006                 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));
6007                 for (p = remset->data; p < remset->store_next;) {
6008                         p = find_in_remset_loc (p, addr, &found);
6009                         if (found)
6010                                 return TRUE;
6011                 }
6012         }
6013
6014         return FALSE;
6015 }
6016
6017 static gboolean missing_remsets;
6018
6019 /*
6020  * We let a missing remset slide if the target object is pinned,
6021  * because the store might have happened but the remset not yet added,
6022  * but in that case the target must be pinned.  We might theoretically
6023  * miss some missing remsets this way, but it's very unlikely.
6024  */
6025 #undef HANDLE_PTR
6026 #define HANDLE_PTR(ptr,obj)     do {    \
6027                 if (*(ptr) && (char*)*(ptr) >= nursery_start && (char*)*(ptr) < nursery_next) { \
6028                 if (!find_in_remsets ((char*)(ptr)) && (!use_cardtable || !sgen_card_table_address_is_marked ((mword)ptr))) { \
6029                 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); \
6030                 binary_protocol_missing_remset ((obj), (gpointer)LOAD_VTABLE ((obj)), (char*)(ptr) - (char*)(obj), *(ptr), (gpointer)LOAD_VTABLE(*(ptr)), object_is_pinned (*(ptr))); \
6031                 if (!object_is_pinned (*(ptr)))                         \
6032                         missing_remsets = TRUE;                         \
6033             } \
6034         } \
6035         } while (0)
6036
6037 /*
6038  * Check that each object reference which points into the nursery can
6039  * be found in the remembered sets.
6040  */
6041 static void
6042 check_consistency_callback (char *start, size_t size, void *dummy)
6043 {
6044         GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
6045         DEBUG (8, fprintf (gc_debug_file, "Scanning object %p, vtable: %p (%s)\n", start, vt, vt->klass->name));
6046
6047 #define SCAN_OBJECT_ACTION
6048 #include "sgen-scan-object.h"
6049 }
6050
6051 /*
6052  * Perform consistency check of the heap.
6053  *
6054  * Assumes the world is stopped.
6055  */
6056 static void
6057 check_consistency (void)
6058 {
6059         LOSObject *bigobj;
6060
6061         // Need to add more checks
6062
6063         missing_remsets = FALSE;
6064
6065         DEBUG (1, fprintf (gc_debug_file, "Begin heap consistency check...\n"));
6066
6067         // Check that oldspace->newspace pointers are registered with the collector
6068         major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)check_consistency_callback, NULL);
6069
6070         for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
6071                 check_consistency_callback (bigobj->data, bigobj->size, NULL);
6072
6073         DEBUG (1, fprintf (gc_debug_file, "Heap consistency check done.\n"));
6074
6075 #ifdef SGEN_BINARY_PROTOCOL
6076         if (!binary_protocol_file)
6077 #endif
6078                 g_assert (!missing_remsets);
6079 }
6080
6081
6082 #undef HANDLE_PTR
6083 #define HANDLE_PTR(ptr,obj)     do {                                    \
6084                 if (*(ptr) && !LOAD_VTABLE (*(ptr)))                                            \
6085                         g_error ("Could not load vtable for obj %p slot %d (size %d)", obj, (char*)ptr - (char*)obj, safe_object_get_size ((MonoObject*)obj));          \
6086         } while (0)
6087
6088 static void
6089 check_major_refs_callback (char *start, size_t size, void *dummy)
6090 {
6091 #define SCAN_OBJECT_ACTION
6092 #include "sgen-scan-object.h"
6093 }
6094
6095 static void
6096 check_major_refs (void)
6097 {
6098         LOSObject *bigobj;
6099
6100         major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)check_major_refs_callback, NULL);
6101
6102         for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
6103                 check_major_refs_callback (bigobj->data, bigobj->size, NULL);
6104 }
6105
6106 /* Check that the reference is valid */
6107 #undef HANDLE_PTR
6108 #define HANDLE_PTR(ptr,obj)     do {    \
6109                 if (*(ptr)) {   \
6110                         g_assert (safe_name (*(ptr)) != NULL);  \
6111                 }       \
6112         } while (0)
6113
6114 /*
6115  * check_object:
6116  *
6117  *   Perform consistency check on an object. Currently we only check that the
6118  * reference fields are valid.
6119  */
6120 void
6121 check_object (char *start)
6122 {
6123         if (!start)
6124                 return;
6125
6126 #include "sgen-scan-object.h"
6127 }
6128
6129 /*
6130  * ######################################################################
6131  * ########  Other mono public interface functions.
6132  * ######################################################################
6133  */
6134
6135 void
6136 mono_gc_collect (int generation)
6137 {
6138         LOCK_GC;
6139         if (generation > 1)
6140                 generation = 1;
6141         mono_profiler_gc_event (MONO_GC_EVENT_START, generation);
6142         stop_world (generation);
6143         if (generation == 0) {
6144                 collect_nursery (0);
6145         } else {
6146                 major_collection ("user request");
6147         }
6148         restart_world (generation);
6149         mono_profiler_gc_event (MONO_GC_EVENT_END, generation);
6150         UNLOCK_GC;
6151 }
6152
6153 int
6154 mono_gc_max_generation (void)
6155 {
6156         return 1;
6157 }
6158
6159 int
6160 mono_gc_collection_count (int generation)
6161 {
6162         if (generation == 0)
6163                 return num_minor_gcs;
6164         return num_major_gcs;
6165 }
6166
6167 int64_t
6168 mono_gc_get_used_size (void)
6169 {
6170         gint64 tot = 0;
6171         LOCK_GC;
6172         tot = los_memory_usage;
6173         tot += nursery_section->next_data - nursery_section->data;
6174         tot += major_collector.get_used_size ();
6175         /* FIXME: account for pinned objects */
6176         UNLOCK_GC;
6177         return tot;
6178 }
6179
6180 int64_t
6181 mono_gc_get_heap_size (void)
6182 {
6183         return total_alloc;
6184 }
6185
6186 void
6187 mono_gc_disable (void)
6188 {
6189         LOCK_GC;
6190         gc_disabled++;
6191         UNLOCK_GC;
6192 }
6193
6194 void
6195 mono_gc_enable (void)
6196 {
6197         LOCK_GC;
6198         gc_disabled--;
6199         UNLOCK_GC;
6200 }
6201
6202 int
6203 mono_gc_get_los_limit (void)
6204 {
6205         return MAX_SMALL_OBJ_SIZE;
6206 }
6207
6208 gboolean
6209 mono_object_is_alive (MonoObject* o)
6210 {
6211         return TRUE;
6212 }
6213
6214 int
6215 mono_gc_get_generation (MonoObject *obj)
6216 {
6217         if (ptr_in_nursery (obj))
6218                 return 0;
6219         return 1;
6220 }
6221
6222 void
6223 mono_gc_enable_events (void)
6224 {
6225 }
6226
6227 void
6228 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
6229 {
6230         LOCK_GC;
6231         mono_gc_register_disappearing_link (obj, link_addr, track);
6232         UNLOCK_GC;
6233 }
6234
6235 void
6236 mono_gc_weak_link_remove (void **link_addr)
6237 {
6238         LOCK_GC;
6239         mono_gc_register_disappearing_link (NULL, link_addr, FALSE);
6240         UNLOCK_GC;
6241 }
6242
6243 MonoObject*
6244 mono_gc_weak_link_get (void **link_addr)
6245 {
6246         if (!*link_addr)
6247                 return NULL;
6248         return (MonoObject*) REVEAL_POINTER (*link_addr);
6249 }
6250
6251 gboolean
6252 mono_gc_ephemeron_array_add (MonoObject *obj)
6253 {
6254         EphemeronLinkNode *node;
6255
6256         LOCK_GC;
6257
6258         node = mono_sgen_alloc_internal (INTERNAL_MEM_EPHEMERON_LINK);
6259         if (!node) {
6260                 UNLOCK_GC;
6261                 return FALSE;
6262         }
6263         node->array = (char*)obj;
6264         node->next = ephemeron_list;
6265         ephemeron_list = node;
6266
6267         DEBUG (5, fprintf (gc_debug_file, "Registered ephemeron array %p\n", obj));
6268
6269         UNLOCK_GC;
6270         return TRUE;
6271 }
6272
6273 void*
6274 mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
6275 {
6276         if (numbits == 0) {
6277                 return (void*)MAKE_ROOT_DESC (ROOT_DESC_BITMAP, 0);
6278         } else if (numbits < ((sizeof (*bitmap) * 8) - ROOT_DESC_TYPE_SHIFT)) {
6279                 return (void*)MAKE_ROOT_DESC (ROOT_DESC_BITMAP, bitmap [0]);
6280         } else {
6281                 mword complex = alloc_complex_descriptor (bitmap, numbits);
6282                 return (void*)MAKE_ROOT_DESC (ROOT_DESC_COMPLEX, complex);
6283         }
6284 }
6285
6286  static void *all_ref_root_descrs [32];
6287
6288 void*
6289 mono_gc_make_root_descr_all_refs (int numbits)
6290 {
6291         gsize *gc_bitmap;
6292         void *descr;
6293
6294         if (numbits < 32 && all_ref_root_descrs [numbits])
6295                 return all_ref_root_descrs [numbits];
6296
6297         gc_bitmap = g_malloc0 (ALIGN_TO (numbits, 8) + 1);
6298         memset (gc_bitmap, 0xff, numbits / 8);
6299         if (numbits % 8)
6300                 gc_bitmap [numbits / 8] = (1 << (numbits % 8)) - 1;
6301         descr = mono_gc_make_descr_from_bitmap (gc_bitmap, numbits);
6302         g_free (gc_bitmap);
6303
6304         if (numbits < 32)
6305                 all_ref_root_descrs [numbits] = descr;
6306
6307         return descr;
6308 }
6309
6310 void*
6311 mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
6312 {
6313         void *descr;
6314
6315         g_assert (user_descriptors_next < MAX_USER_DESCRIPTORS);
6316         descr = (void*)MAKE_ROOT_DESC (ROOT_DESC_USER, (mword)user_descriptors_next);
6317         user_descriptors [user_descriptors_next ++] = marker;
6318
6319         return descr;
6320 }
6321
6322 void*
6323 mono_gc_alloc_fixed (size_t size, void *descr)
6324 {
6325         /* FIXME: do a single allocation */
6326         void *res = calloc (1, size);
6327         if (!res)
6328                 return NULL;
6329         if (!mono_gc_register_root (res, size, descr)) {
6330                 free (res);
6331                 res = NULL;
6332         }
6333         return res;
6334 }
6335
6336 void
6337 mono_gc_free_fixed (void* addr)
6338 {
6339         mono_gc_deregister_root (addr);
6340         free (addr);
6341 }
6342
6343 void*
6344 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
6345 {
6346         void *result;
6347         LOCK_INTERRUPTION;
6348         result = func (data);
6349         UNLOCK_INTERRUPTION;
6350         return result;
6351 }
6352
6353 gboolean
6354 mono_gc_is_gc_thread (void)
6355 {
6356         gboolean result;
6357         LOCK_GC;
6358         result = mono_sgen_thread_info_lookup (ARCH_GET_THREAD ()) != NULL;
6359         UNLOCK_GC;
6360         return result;
6361 }
6362
6363 /* Tries to extract a number from the passed string, taking in to account m, k
6364  * and g suffixes */
6365 gboolean
6366 mono_sgen_parse_environment_string_extract_number (const char *str, glong *out)
6367 {
6368         char *endptr;
6369         int len = strlen (str), shift = 0;
6370         glong val;
6371         gboolean is_suffix = FALSE;
6372         char suffix;
6373
6374         switch (str [len - 1]) {
6375                 case 'g':
6376                 case 'G':
6377                         shift += 10;
6378                 case 'm':
6379                 case 'M':
6380                         shift += 10;
6381                 case 'k':
6382                 case 'K':
6383                         shift += 10;
6384                         is_suffix = TRUE;
6385                         suffix = str [len - 1];
6386                         break;
6387         }
6388
6389         errno = 0;
6390         val = strtol (str, &endptr, 10);
6391
6392         if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN))
6393                         || (errno != 0 && val == 0) || (endptr == str))
6394                 return FALSE;
6395
6396         if (is_suffix) {
6397                 if (*(endptr + 1)) /* Invalid string. */
6398                         return FALSE;
6399                 val <<= shift;
6400         }
6401
6402         *out = val;
6403         return TRUE;
6404 }
6405
6406 void
6407 mono_gc_base_init (void)
6408 {
6409         char *env;
6410         char **opts, **ptr;
6411         char *major_collector_opt = NULL;
6412         struct sigaction sinfo;
6413
6414         /* the gc_initialized guard seems to imply this method is
6415            idempotent, but LOCK_INIT(gc_mutex) might not be.  It's
6416            defined in sgen-gc.h as nothing, so there's no danger at
6417            present. */
6418         LOCK_INIT (gc_mutex);
6419         LOCK_GC;
6420         if (gc_initialized) {
6421                 UNLOCK_GC;
6422                 return;
6423         }
6424         pagesize = mono_pagesize ();
6425         gc_debug_file = stderr;
6426
6427         LOCK_INIT (interruption_mutex);
6428         LOCK_INIT (global_remset_mutex);
6429
6430         if ((env = getenv ("MONO_GC_PARAMS"))) {
6431                 opts = g_strsplit (env, ",", -1);
6432                 for (ptr = opts; *ptr; ++ptr) {
6433                         char *opt = *ptr;
6434                         if (g_str_has_prefix (opt, "major=")) {
6435                                 opt = strchr (opt, '=') + 1;
6436                                 major_collector_opt = g_strdup (opt);
6437                         }
6438                 }
6439         } else {
6440                 opts = NULL;
6441         }
6442
6443         init_stats ();
6444         mono_sgen_init_internal_allocator ();
6445
6446         mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_FRAGMENT, sizeof (Fragment));
6447         mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_SECTION, SGEN_SIZEOF_GC_MEM_SECTION);
6448         mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_FINALIZE_ENTRY, sizeof (FinalizeEntry));
6449         mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_DISLINK, sizeof (DisappearingLink));
6450         mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_ROOT_RECORD, sizeof (RootRecord));
6451         mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_GRAY_QUEUE, sizeof (GrayQueueSection));
6452         g_assert (sizeof (GenericStoreRememberedSet) == sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
6453         mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_STORE_REMSET, sizeof (GenericStoreRememberedSet));
6454         mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_EPHEMERON_LINK, sizeof (EphemeronLinkNode));
6455
6456         if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep")) {
6457                 mono_sgen_marksweep_init (&major_collector);
6458         } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-fixed")) {
6459                 mono_sgen_marksweep_fixed_init (&major_collector);
6460         } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-par")) {
6461                 mono_sgen_marksweep_par_init (&major_collector);
6462                 workers_init (mono_cpu_count ());
6463         } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-fixed-par")) {
6464                 mono_sgen_marksweep_fixed_par_init (&major_collector);
6465                 workers_init (mono_cpu_count ());
6466         } else if (!strcmp (major_collector_opt, "copying")) {
6467                 mono_sgen_copying_init (&major_collector);
6468         } else {
6469                 fprintf (stderr, "Unknown major collector `%s'.\n", major_collector_opt);
6470                 exit (1);
6471         }
6472
6473 #ifdef SGEN_HAVE_CARDTABLE
6474         use_cardtable = major_collector.supports_cardtable;
6475 #else
6476         use_cardtable = FALSE;
6477 #endif
6478
6479         if (opts) {
6480                 for (ptr = opts; *ptr; ++ptr) {
6481                         char *opt = *ptr;
6482                         if (g_str_has_prefix (opt, "major="))
6483                                 continue;
6484                         if (g_str_has_prefix (opt, "wbarrier=")) {
6485                                 opt = strchr (opt, '=') + 1;
6486                                 if (strcmp (opt, "remset") == 0) {
6487                                         use_cardtable = FALSE;
6488                                 } else if (strcmp (opt, "cardtable") == 0) {
6489                                         if (!use_cardtable) {
6490                                                 if (major_collector.supports_cardtable)
6491                                                         fprintf (stderr, "The cardtable write barrier is not supported on this platform.\n");
6492                                                 else
6493                                                         fprintf (stderr, "The major collector does not support the cardtable write barrier.\n");
6494                                                 exit (1);
6495                                         }
6496                                 }
6497                                 continue;
6498                         }
6499 #ifdef USER_CONFIG
6500                         if (g_str_has_prefix (opt, "nursery-size=")) {
6501                                 long val;
6502                                 opt = strchr (opt, '=') + 1;
6503                                 if (*opt && mono_sgen_parse_environment_string_extract_number (opt, &val)) {
6504                                         default_nursery_size = val;
6505 #ifdef SGEN_ALIGN_NURSERY
6506                                         if ((val & (val - 1))) {
6507                                                 fprintf (stderr, "The nursery size must be a power of two.\n");
6508                                                 exit (1);
6509                                         }
6510
6511                                         default_nursery_bits = 0;
6512                                         while (1 << (++ default_nursery_bits) != default_nursery_size)
6513                                                 ;
6514 #endif
6515                                 } else {
6516                                         fprintf (stderr, "nursery-size must be an integer.\n");
6517                                         exit (1);
6518                                 }
6519                                 continue;
6520                         }
6521 #endif
6522                         if (!(major_collector.handle_gc_param && major_collector.handle_gc_param (opt))) {
6523                                 fprintf (stderr, "MONO_GC_PARAMS must be a comma-delimited list of one or more of the following:\n");
6524                                 fprintf (stderr, "  nursery-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
6525                                 fprintf (stderr, "  major=COLLECTOR (where COLLECTOR is `marksweep', `marksweep-par' or `copying')\n");
6526                                 fprintf (stderr, "  wbarrier=WBARRIER (where WBARRIER is `remset' or `cardtable')\n");
6527                                 if (major_collector.print_gc_param_usage)
6528                                         major_collector.print_gc_param_usage ();
6529                                 exit (1);
6530                         }
6531                 }
6532                 g_strfreev (opts);
6533         }
6534
6535         if (major_collector_opt)
6536                 g_free (major_collector_opt);
6537
6538         nursery_size = DEFAULT_NURSERY_SIZE;
6539         minor_collection_allowance = MIN_MINOR_COLLECTION_ALLOWANCE;
6540
6541         alloc_nursery ();
6542
6543         if ((env = getenv ("MONO_GC_DEBUG"))) {
6544                 opts = g_strsplit (env, ",", -1);
6545                 for (ptr = opts; ptr && *ptr; ptr ++) {
6546                         char *opt = *ptr;
6547                         if (opt [0] >= '0' && opt [0] <= '9') {
6548                                 gc_debug_level = atoi (opt);
6549                                 opt++;
6550                                 if (opt [0] == ':')
6551                                         opt++;
6552                                 if (opt [0]) {
6553                                         char *rf = g_strdup_printf ("%s.%d", opt, getpid ());
6554                                         gc_debug_file = fopen (rf, "wb");
6555                                         if (!gc_debug_file)
6556                                                 gc_debug_file = stderr;
6557                                         g_free (rf);
6558                                 }
6559                         } else if (!strcmp (opt, "collect-before-allocs")) {
6560                                 collect_before_allocs = TRUE;
6561                         } else if (!strcmp (opt, "check-at-minor-collections")) {
6562                                 consistency_check_at_minor_collection = TRUE;
6563                                 nursery_clear_policy = CLEAR_AT_GC;
6564                         } else if (!strcmp (opt, "xdomain-checks")) {
6565                                 xdomain_checks = TRUE;
6566                         } else if (!strcmp (opt, "clear-at-gc")) {
6567                                 nursery_clear_policy = CLEAR_AT_GC;
6568                         } else if (!strcmp (opt, "conservative-stack-mark")) {
6569                                 conservative_stack_mark = TRUE;
6570                         } else if (!strcmp (opt, "check-scan-starts")) {
6571                                 do_scan_starts_check = TRUE;
6572                         } else if (g_str_has_prefix (opt, "heap-dump=")) {
6573                                 char *filename = strchr (opt, '=') + 1;
6574                                 nursery_clear_policy = CLEAR_AT_GC;
6575                                 heap_dump_file = fopen (filename, "w");
6576                                 if (heap_dump_file)
6577                                         fprintf (heap_dump_file, "<sgen-dump>\n");
6578 #ifdef SGEN_BINARY_PROTOCOL
6579                         } else if (g_str_has_prefix (opt, "binary-protocol=")) {
6580                                 char *filename = strchr (opt, '=') + 1;
6581                                 binary_protocol_file = fopen (filename, "w");
6582 #endif
6583                         } else {
6584                                 fprintf (stderr, "Invalid format for the MONO_GC_DEBUG env variable: '%s'\n", env);
6585                                 fprintf (stderr, "The format is: MONO_GC_DEBUG=[l[:filename]|<option>]+ where l is a debug level 0-9.\n");
6586                                 fprintf (stderr, "Valid options are: collect-before-allocs, check-at-minor-collections, xdomain-checks, clear-at-gc.\n");
6587                                 exit (1);
6588                         }
6589                 }
6590                 g_strfreev (opts);
6591         }
6592
6593         suspend_ack_semaphore_ptr = &suspend_ack_semaphore;
6594         MONO_SEM_INIT (&suspend_ack_semaphore, 0);
6595
6596         sigfillset (&sinfo.sa_mask);
6597         sinfo.sa_flags = SA_RESTART | SA_SIGINFO;
6598         sinfo.sa_sigaction = suspend_handler;
6599         if (sigaction (suspend_signal_num, &sinfo, NULL) != 0) {
6600                 g_error ("failed sigaction");
6601         }
6602
6603         sinfo.sa_handler = restart_handler;
6604         if (sigaction (restart_signal_num, &sinfo, NULL) != 0) {
6605                 g_error ("failed sigaction");
6606         }
6607
6608         sigfillset (&suspend_signal_mask);
6609         sigdelset (&suspend_signal_mask, restart_signal_num);
6610
6611         global_remset = alloc_remset (1024, NULL);
6612         global_remset->next = NULL;
6613
6614         pthread_key_create (&remembered_set_key, unregister_thread);
6615
6616 #ifndef HAVE_KW_THREAD
6617         pthread_key_create (&thread_info_key, NULL);
6618 #endif
6619
6620         if (use_cardtable)
6621                 card_table_init ();
6622
6623         gc_initialized = TRUE;
6624         UNLOCK_GC;
6625         mono_gc_register_thread (&sinfo);
6626 }
6627
6628 int
6629 mono_gc_get_suspend_signal (void)
6630 {
6631         return suspend_signal_num;
6632 }
6633
6634 enum {
6635         ATYPE_NORMAL,
6636         ATYPE_VECTOR,
6637         ATYPE_SMALL,
6638         ATYPE_NUM
6639 };
6640
6641 #ifdef HAVE_KW_THREAD
6642 #define EMIT_TLS_ACCESS(mb,dummy,offset)        do {    \
6643         mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX);   \
6644         mono_mb_emit_byte ((mb), CEE_MONO_TLS);         \
6645         mono_mb_emit_i4 ((mb), (offset));               \
6646         } while (0)
6647 #else
6648 #define EMIT_TLS_ACCESS(mb,member,dummy)        do {    \
6649         mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX);   \
6650         mono_mb_emit_byte ((mb), CEE_MONO_TLS);         \
6651         mono_mb_emit_i4 ((mb), thread_info_key);        \
6652         mono_mb_emit_icon ((mb), G_STRUCT_OFFSET (SgenThreadInfo, member));     \
6653         mono_mb_emit_byte ((mb), CEE_ADD);              \
6654         mono_mb_emit_byte ((mb), CEE_LDIND_I);          \
6655         } while (0)
6656 #endif
6657
6658 #ifdef MANAGED_ALLOCATION
6659 /* FIXME: Do this in the JIT, where specialized allocation sequences can be created
6660  * for each class. This is currently not easy to do, as it is hard to generate basic 
6661  * blocks + branches, but it is easy with the linear IL codebase.
6662  *
6663  * For this to work we'd need to solve the TLAB race, first.  Now we
6664  * require the allocator to be in a few known methods to make sure
6665  * that they are executed atomically via the restart mechanism.
6666  */
6667 static MonoMethod*
6668 create_allocator (int atype)
6669 {
6670         int p_var, size_var;
6671         guint32 slowpath_branch, max_size_branch;
6672         MonoMethodBuilder *mb;
6673         MonoMethod *res;
6674         MonoMethodSignature *csig;
6675         static gboolean registered = FALSE;
6676         int tlab_next_addr_var, new_next_var;
6677         int num_params, i;
6678         const char *name = NULL;
6679         AllocatorWrapperInfo *info;
6680
6681 #ifdef HAVE_KW_THREAD
6682         int tlab_next_addr_offset = -1;
6683         int tlab_temp_end_offset = -1;
6684
6685         MONO_THREAD_VAR_OFFSET (tlab_next_addr, tlab_next_addr_offset);
6686         MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
6687
6688         g_assert (tlab_next_addr_offset != -1);
6689         g_assert (tlab_temp_end_offset != -1);
6690 #endif
6691
6692         if (!registered) {
6693                 mono_register_jit_icall (mono_gc_alloc_obj, "mono_gc_alloc_obj", mono_create_icall_signature ("object ptr int"), FALSE);
6694                 mono_register_jit_icall (mono_gc_alloc_vector, "mono_gc_alloc_vector", mono_create_icall_signature ("object ptr int int"), FALSE);
6695                 registered = TRUE;
6696         }
6697
6698         if (atype == ATYPE_SMALL) {
6699                 num_params = 1;
6700                 name = "AllocSmall";
6701         } else if (atype == ATYPE_NORMAL) {
6702                 num_params = 1;
6703                 name = "Alloc";
6704         } else if (atype == ATYPE_VECTOR) {
6705                 num_params = 2;
6706                 name = "AllocVector";
6707         } else {
6708                 g_assert_not_reached ();
6709         }
6710
6711         csig = mono_metadata_signature_alloc (mono_defaults.corlib, num_params);
6712         csig->ret = &mono_defaults.object_class->byval_arg;
6713         for (i = 0; i < num_params; ++i)
6714                 csig->params [i] = &mono_defaults.int_class->byval_arg;
6715
6716         mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_ALLOC);
6717         size_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
6718         if (atype == ATYPE_NORMAL || atype == ATYPE_SMALL) {
6719                 /* size = vtable->klass->instance_size; */
6720                 mono_mb_emit_ldarg (mb, 0);
6721                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoVTable, klass));
6722                 mono_mb_emit_byte (mb, CEE_ADD);
6723                 mono_mb_emit_byte (mb, CEE_LDIND_I);
6724                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoClass, instance_size));
6725                 mono_mb_emit_byte (mb, CEE_ADD);
6726                 /* FIXME: assert instance_size stays a 4 byte integer */
6727                 mono_mb_emit_byte (mb, CEE_LDIND_U4);
6728                 mono_mb_emit_stloc (mb, size_var);
6729         } else if (atype == ATYPE_VECTOR) {
6730                 MonoExceptionClause *clause;
6731                 int pos, pos_leave;
6732                 MonoClass *oom_exc_class;
6733                 MonoMethod *ctor;
6734
6735                 /* n >  MONO_ARRAY_MAX_INDEX -> OverflowException */
6736                 mono_mb_emit_ldarg (mb, 1);
6737                 mono_mb_emit_icon (mb, MONO_ARRAY_MAX_INDEX);
6738                 pos = mono_mb_emit_short_branch (mb, CEE_BLE_UN_S);
6739                 mono_mb_emit_exception (mb, "OverflowException", NULL);
6740                 mono_mb_patch_short_branch (mb, pos);
6741
6742                 clause = mono_image_alloc0 (mono_defaults.corlib, sizeof (MonoExceptionClause));
6743                 clause->try_offset = mono_mb_get_label (mb);
6744
6745                 /* vtable->klass->sizes.element_size */
6746                 mono_mb_emit_ldarg (mb, 0);
6747                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoVTable, klass));
6748                 mono_mb_emit_byte (mb, CEE_ADD);
6749                 mono_mb_emit_byte (mb, CEE_LDIND_I);
6750                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoClass, sizes.element_size));
6751                 mono_mb_emit_byte (mb, CEE_ADD);
6752                 mono_mb_emit_byte (mb, CEE_LDIND_U4);
6753
6754                 /* * n */
6755                 mono_mb_emit_ldarg (mb, 1);
6756                 mono_mb_emit_byte (mb, CEE_MUL_OVF_UN);
6757                 /* + sizeof (MonoArray) */
6758                 mono_mb_emit_icon (mb, sizeof (MonoArray));
6759                 mono_mb_emit_byte (mb, CEE_ADD_OVF_UN);
6760                 mono_mb_emit_stloc (mb, size_var);
6761
6762                 pos_leave = mono_mb_emit_branch (mb, CEE_LEAVE);
6763
6764                 /* catch */
6765                 clause->flags = MONO_EXCEPTION_CLAUSE_NONE;
6766                 clause->try_len = mono_mb_get_pos (mb) - clause->try_offset;
6767                 clause->data.catch_class = mono_class_from_name (mono_defaults.corlib,
6768                                 "System", "OverflowException");
6769                 g_assert (clause->data.catch_class);
6770                 clause->handler_offset = mono_mb_get_label (mb);
6771
6772                 oom_exc_class = mono_class_from_name (mono_defaults.corlib,
6773                                 "System", "OutOfMemoryException");
6774                 g_assert (oom_exc_class);
6775                 ctor = mono_class_get_method_from_name (oom_exc_class, ".ctor", 0);
6776                 g_assert (ctor);
6777
6778                 mono_mb_emit_byte (mb, CEE_POP);
6779                 mono_mb_emit_op (mb, CEE_NEWOBJ, ctor);
6780                 mono_mb_emit_byte (mb, CEE_THROW);
6781
6782                 clause->handler_len = mono_mb_get_pos (mb) - clause->handler_offset;
6783                 mono_mb_set_clauses (mb, 1, clause);
6784                 mono_mb_patch_branch (mb, pos_leave);
6785                 /* end catch */
6786         } else {
6787                 g_assert_not_reached ();
6788         }
6789
6790         /* size += ALLOC_ALIGN - 1; */
6791         mono_mb_emit_ldloc (mb, size_var);
6792         mono_mb_emit_icon (mb, ALLOC_ALIGN - 1);
6793         mono_mb_emit_byte (mb, CEE_ADD);
6794         /* size &= ~(ALLOC_ALIGN - 1); */
6795         mono_mb_emit_icon (mb, ~(ALLOC_ALIGN - 1));
6796         mono_mb_emit_byte (mb, CEE_AND);
6797         mono_mb_emit_stloc (mb, size_var);
6798
6799         /* if (size > MAX_SMALL_OBJ_SIZE) goto slowpath */
6800         if (atype != ATYPE_SMALL) {
6801                 mono_mb_emit_ldloc (mb, size_var);
6802                 mono_mb_emit_icon (mb, MAX_SMALL_OBJ_SIZE);
6803                 max_size_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_S);
6804         }
6805
6806         /*
6807          * We need to modify tlab_next, but the JIT only supports reading, so we read
6808          * another tls var holding its address instead.
6809          */
6810
6811         /* tlab_next_addr (local) = tlab_next_addr (TLS var) */
6812         tlab_next_addr_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
6813         EMIT_TLS_ACCESS (mb, tlab_next_addr, tlab_next_addr_offset);
6814         mono_mb_emit_stloc (mb, tlab_next_addr_var);
6815
6816         /* p = (void**)tlab_next; */
6817         p_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
6818         mono_mb_emit_ldloc (mb, tlab_next_addr_var);
6819         mono_mb_emit_byte (mb, CEE_LDIND_I);
6820         mono_mb_emit_stloc (mb, p_var);
6821         
6822         /* new_next = (char*)p + size; */
6823         new_next_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
6824         mono_mb_emit_ldloc (mb, p_var);
6825         mono_mb_emit_ldloc (mb, size_var);
6826         mono_mb_emit_byte (mb, CEE_CONV_I);
6827         mono_mb_emit_byte (mb, CEE_ADD);
6828         mono_mb_emit_stloc (mb, new_next_var);
6829
6830         /* tlab_next = new_next */
6831         mono_mb_emit_ldloc (mb, tlab_next_addr_var);
6832         mono_mb_emit_ldloc (mb, new_next_var);
6833         mono_mb_emit_byte (mb, CEE_STIND_I);
6834
6835         /* if (G_LIKELY (new_next < tlab_temp_end)) */
6836         mono_mb_emit_ldloc (mb, new_next_var);
6837         EMIT_TLS_ACCESS (mb, tlab_temp_end, tlab_temp_end_offset);
6838         slowpath_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
6839
6840         /* Slowpath */
6841         if (atype != ATYPE_SMALL)
6842                 mono_mb_patch_short_branch (mb, max_size_branch);
6843
6844         mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
6845         mono_mb_emit_byte (mb, CEE_MONO_NOT_TAKEN);
6846
6847         /* FIXME: mono_gc_alloc_obj takes a 'size_t' as an argument, not an int32 */
6848         mono_mb_emit_ldarg (mb, 0);
6849         mono_mb_emit_ldloc (mb, size_var);
6850         if (atype == ATYPE_NORMAL || atype == ATYPE_SMALL) {
6851                 mono_mb_emit_icall (mb, mono_gc_alloc_obj);
6852         } else if (atype == ATYPE_VECTOR) {
6853                 mono_mb_emit_ldarg (mb, 1);
6854                 mono_mb_emit_icall (mb, mono_gc_alloc_vector);
6855         } else {
6856                 g_assert_not_reached ();
6857         }
6858         mono_mb_emit_byte (mb, CEE_RET);
6859
6860         /* Fastpath */
6861         mono_mb_patch_short_branch (mb, slowpath_branch);
6862
6863         /* FIXME: Memory barrier */
6864
6865         /* *p = vtable; */
6866         mono_mb_emit_ldloc (mb, p_var);
6867         mono_mb_emit_ldarg (mb, 0);
6868         mono_mb_emit_byte (mb, CEE_STIND_I);
6869
6870         if (atype == ATYPE_VECTOR) {
6871                 /* arr->max_length = max_length; */
6872                 mono_mb_emit_ldloc (mb, p_var);
6873                 mono_mb_emit_ldflda (mb, G_STRUCT_OFFSET (MonoArray, max_length));
6874                 mono_mb_emit_ldarg (mb, 1);
6875                 mono_mb_emit_byte (mb, CEE_STIND_I);
6876         }
6877
6878         /* return p */
6879         mono_mb_emit_ldloc (mb, p_var);
6880         mono_mb_emit_byte (mb, CEE_RET);
6881
6882         res = mono_mb_create_method (mb, csig, 8);
6883         mono_mb_free (mb);
6884         mono_method_get_header (res)->init_locals = FALSE;
6885
6886         info = mono_image_alloc0 (mono_defaults.corlib, sizeof (AllocatorWrapperInfo));
6887         info->gc_name = "sgen";
6888         info->alloc_type = atype;
6889         mono_marshal_set_wrapper_info (res, info);
6890
6891         return res;
6892 }
6893 #endif
6894
6895 const char *
6896 mono_gc_get_gc_name (void)
6897 {
6898         return "sgen";
6899 }
6900
6901 static MonoMethod* alloc_method_cache [ATYPE_NUM];
6902 static MonoMethod *write_barrier_method;
6903
6904 static gboolean
6905 is_ip_in_managed_allocator (MonoDomain *domain, gpointer ip)
6906 {
6907         MonoJitInfo *ji;
6908         MonoMethod *method;
6909         int i;
6910
6911         if (!mono_thread_internal_current ())
6912                 /* Happens during thread attach */
6913                 return FALSE;
6914
6915         if (!ip || !domain)
6916                 return FALSE;
6917         ji = mono_jit_info_table_find (domain, ip);
6918         if (!ji)
6919                 return FALSE;
6920         method = ji->method;
6921
6922         if (method == write_barrier_method)
6923                 return TRUE;
6924         for (i = 0; i < ATYPE_NUM; ++i)
6925                 if (method == alloc_method_cache [i])
6926                         return TRUE;
6927         return FALSE;
6928 }
6929
6930 /*
6931  * Generate an allocator method implementing the fast path of mono_gc_alloc_obj ().
6932  * The signature of the called method is:
6933  *      object allocate (MonoVTable *vtable)
6934  */
6935 MonoMethod*
6936 mono_gc_get_managed_allocator (MonoVTable *vtable, gboolean for_box)
6937 {
6938 #ifdef MANAGED_ALLOCATION
6939         MonoClass *klass = vtable->klass;
6940
6941 #ifdef HAVE_KW_THREAD
6942         int tlab_next_offset = -1;
6943         int tlab_temp_end_offset = -1;
6944         MONO_THREAD_VAR_OFFSET (tlab_next, tlab_next_offset);
6945         MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
6946
6947         if (tlab_next_offset == -1 || tlab_temp_end_offset == -1)
6948                 return NULL;
6949 #endif
6950
6951         if (!mono_runtime_has_tls_get ())
6952                 return NULL;
6953         if (klass->instance_size > tlab_size)
6954                 return NULL;
6955         if (klass->has_finalize || klass->marshalbyref || (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS))
6956                 return NULL;
6957         if (klass->rank)
6958                 return NULL;
6959         if (klass->byval_arg.type == MONO_TYPE_STRING)
6960                 return NULL;
6961         if (collect_before_allocs)
6962                 return NULL;
6963
6964         if (ALIGN_TO (klass->instance_size, ALLOC_ALIGN) < MAX_SMALL_OBJ_SIZE)
6965                 return mono_gc_get_managed_allocator_by_type (ATYPE_SMALL);
6966         else
6967                 return mono_gc_get_managed_allocator_by_type (ATYPE_NORMAL);
6968 #else
6969         return NULL;
6970 #endif
6971 }
6972
6973 MonoMethod*
6974 mono_gc_get_managed_array_allocator (MonoVTable *vtable, int rank)
6975 {
6976 #ifdef MANAGED_ALLOCATION
6977         MonoClass *klass = vtable->klass;
6978
6979 #ifdef HAVE_KW_THREAD
6980         int tlab_next_offset = -1;
6981         int tlab_temp_end_offset = -1;
6982         MONO_THREAD_VAR_OFFSET (tlab_next, tlab_next_offset);
6983         MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
6984
6985         if (tlab_next_offset == -1 || tlab_temp_end_offset == -1)
6986                 return NULL;
6987 #endif
6988
6989         if (rank != 1)
6990                 return NULL;
6991         if (!mono_runtime_has_tls_get ())
6992                 return NULL;
6993         if (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS)
6994                 return NULL;
6995         if (collect_before_allocs)
6996                 return NULL;
6997         g_assert (!klass->has_finalize && !klass->marshalbyref);
6998
6999         return mono_gc_get_managed_allocator_by_type (ATYPE_VECTOR);
7000 #else
7001         return NULL;
7002 #endif
7003 }
7004
7005 MonoMethod*
7006 mono_gc_get_managed_allocator_by_type (int atype)
7007 {
7008 #ifdef MANAGED_ALLOCATION
7009         MonoMethod *res;
7010
7011         if (!mono_runtime_has_tls_get ())
7012                 return NULL;
7013
7014         mono_loader_lock ();
7015         res = alloc_method_cache [atype];
7016         if (!res)
7017                 res = alloc_method_cache [atype] = create_allocator (atype);
7018         mono_loader_unlock ();
7019         return res;
7020 #else
7021         return NULL;
7022 #endif
7023 }
7024
7025 guint32
7026 mono_gc_get_managed_allocator_types (void)
7027 {
7028         return ATYPE_NUM;
7029 }
7030
7031
7032 MonoMethod*
7033 mono_gc_get_write_barrier (void)
7034 {
7035         MonoMethod *res;
7036         MonoMethodBuilder *mb;
7037         MonoMethodSignature *sig;
7038 #ifdef MANAGED_WBARRIER
7039         int label_no_wb_1, label_no_wb_2, label_no_wb_3, label_no_wb_4, label_need_wb, label_slow_path;
7040 #ifndef SGEN_ALIGN_NURSERY
7041         int label_continue_1, label_continue_2, label_no_wb_5;
7042         int dereferenced_var;
7043 #endif
7044         int buffer_var, buffer_index_var, dummy_var;
7045
7046 #ifdef HAVE_KW_THREAD
7047         int stack_end_offset = -1, store_remset_buffer_offset = -1;
7048         int store_remset_buffer_index_offset = -1, store_remset_buffer_index_addr_offset = -1;
7049
7050         MONO_THREAD_VAR_OFFSET (stack_end, stack_end_offset);
7051         g_assert (stack_end_offset != -1);
7052         MONO_THREAD_VAR_OFFSET (store_remset_buffer, store_remset_buffer_offset);
7053         g_assert (store_remset_buffer_offset != -1);
7054         MONO_THREAD_VAR_OFFSET (store_remset_buffer_index, store_remset_buffer_index_offset);
7055         g_assert (store_remset_buffer_index_offset != -1);
7056         MONO_THREAD_VAR_OFFSET (store_remset_buffer_index_addr, store_remset_buffer_index_addr_offset);
7057         g_assert (store_remset_buffer_index_addr_offset != -1);
7058 #endif
7059 #endif
7060
7061         g_assert (!use_cardtable);
7062
7063         // FIXME: Maybe create a separate version for ctors (the branch would be
7064         // correctly predicted more times)
7065         if (write_barrier_method)
7066                 return write_barrier_method;
7067
7068         /* Create the IL version of mono_gc_barrier_generic_store () */
7069         sig = mono_metadata_signature_alloc (mono_defaults.corlib, 1);
7070         sig->ret = &mono_defaults.void_class->byval_arg;
7071         sig->params [0] = &mono_defaults.int_class->byval_arg;
7072
7073         mb = mono_mb_new (mono_defaults.object_class, "wbarrier", MONO_WRAPPER_WRITE_BARRIER);
7074
7075 #ifdef MANAGED_WBARRIER
7076         if (mono_runtime_has_tls_get ()) {
7077 #ifdef SGEN_ALIGN_NURSERY
7078                 // if (ptr_in_nursery (ptr)) return;
7079                 /*
7080                  * Masking out the bits might be faster, but we would have to use 64 bit
7081                  * immediates, which might be slower.
7082                  */
7083                 mono_mb_emit_ldarg (mb, 0);
7084                 mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
7085                 mono_mb_emit_byte (mb, CEE_SHR_UN);
7086                 mono_mb_emit_icon (mb, (mword)nursery_start >> DEFAULT_NURSERY_BITS);
7087                 label_no_wb_1 = mono_mb_emit_branch (mb, CEE_BEQ);
7088
7089                 // if (!ptr_in_nursery (*ptr)) return;
7090                 mono_mb_emit_ldarg (mb, 0);
7091                 mono_mb_emit_byte (mb, CEE_LDIND_I);
7092                 mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
7093                 mono_mb_emit_byte (mb, CEE_SHR_UN);
7094                 mono_mb_emit_icon (mb, (mword)nursery_start >> DEFAULT_NURSERY_BITS);
7095                 label_no_wb_2 = mono_mb_emit_branch (mb, CEE_BNE_UN);
7096 #else
7097
7098                 // if (ptr < (nursery_start)) goto continue;
7099                 mono_mb_emit_ldarg (mb, 0);
7100                 mono_mb_emit_ptr (mb, (gpointer) nursery_start);
7101                 label_continue_1 = mono_mb_emit_branch (mb, CEE_BLT);
7102
7103                 // if (ptr >= nursery_real_end)) goto continue;
7104                 mono_mb_emit_ldarg (mb, 0);
7105                 mono_mb_emit_ptr (mb, (gpointer) nursery_real_end);
7106                 label_continue_2 = mono_mb_emit_branch (mb, CEE_BGE);
7107
7108                 // Otherwise return
7109                 label_no_wb_1 = mono_mb_emit_branch (mb, CEE_BR);
7110
7111                 // continue:
7112                 mono_mb_patch_branch (mb, label_continue_1);
7113                 mono_mb_patch_branch (mb, label_continue_2);
7114
7115                 // Dereference and store in local var
7116                 dereferenced_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7117                 mono_mb_emit_ldarg (mb, 0);
7118                 mono_mb_emit_byte (mb, CEE_LDIND_I);
7119                 mono_mb_emit_stloc (mb, dereferenced_var);
7120
7121                 // if (*ptr < nursery_start) return;
7122                 mono_mb_emit_ldloc (mb, dereferenced_var);
7123                 mono_mb_emit_ptr (mb, (gpointer) nursery_start);
7124                 label_no_wb_2 = mono_mb_emit_branch (mb, CEE_BLT);
7125
7126                 // if (*ptr >= nursery_end) return;
7127                 mono_mb_emit_ldloc (mb, dereferenced_var);
7128                 mono_mb_emit_ptr (mb, (gpointer) nursery_real_end);
7129                 label_no_wb_5 = mono_mb_emit_branch (mb, CEE_BGE);
7130
7131 #endif 
7132                 // if (ptr >= stack_end) goto need_wb;
7133                 mono_mb_emit_ldarg (mb, 0);
7134                 EMIT_TLS_ACCESS (mb, stack_end, stack_end_offset);
7135                 label_need_wb = mono_mb_emit_branch (mb, CEE_BGE_UN);
7136
7137                 // if (ptr >= stack_start) return;
7138                 dummy_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7139                 mono_mb_emit_ldarg (mb, 0);
7140                 mono_mb_emit_ldloc_addr (mb, dummy_var);
7141                 label_no_wb_3 = mono_mb_emit_branch (mb, CEE_BGE_UN);
7142
7143                 // need_wb:
7144                 mono_mb_patch_branch (mb, label_need_wb);
7145
7146                 // buffer = STORE_REMSET_BUFFER;
7147                 buffer_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7148                 EMIT_TLS_ACCESS (mb, store_remset_buffer, store_remset_buffer_offset);
7149                 mono_mb_emit_stloc (mb, buffer_var);
7150
7151                 // buffer_index = STORE_REMSET_BUFFER_INDEX;
7152                 buffer_index_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7153                 EMIT_TLS_ACCESS (mb, store_remset_buffer_index, store_remset_buffer_index_offset);
7154                 mono_mb_emit_stloc (mb, buffer_index_var);
7155
7156                 // if (buffer [buffer_index] == ptr) return;
7157                 mono_mb_emit_ldloc (mb, buffer_var);
7158                 mono_mb_emit_ldloc (mb, buffer_index_var);
7159                 g_assert (sizeof (gpointer) == 4 || sizeof (gpointer) == 8);
7160                 mono_mb_emit_icon (mb, sizeof (gpointer) == 4 ? 2 : 3);
7161                 mono_mb_emit_byte (mb, CEE_SHL);
7162                 mono_mb_emit_byte (mb, CEE_ADD);
7163                 mono_mb_emit_byte (mb, CEE_LDIND_I);
7164                 mono_mb_emit_ldarg (mb, 0);
7165                 label_no_wb_4 = mono_mb_emit_branch (mb, CEE_BEQ);
7166
7167                 // ++buffer_index;
7168                 mono_mb_emit_ldloc (mb, buffer_index_var);
7169                 mono_mb_emit_icon (mb, 1);
7170                 mono_mb_emit_byte (mb, CEE_ADD);
7171                 mono_mb_emit_stloc (mb, buffer_index_var);
7172
7173                 // if (buffer_index >= STORE_REMSET_BUFFER_SIZE) goto slow_path;
7174                 mono_mb_emit_ldloc (mb, buffer_index_var);
7175                 mono_mb_emit_icon (mb, STORE_REMSET_BUFFER_SIZE);
7176                 label_slow_path = mono_mb_emit_branch (mb, CEE_BGE);
7177
7178                 // buffer [buffer_index] = ptr;
7179                 mono_mb_emit_ldloc (mb, buffer_var);
7180                 mono_mb_emit_ldloc (mb, buffer_index_var);
7181                 g_assert (sizeof (gpointer) == 4 || sizeof (gpointer) == 8);
7182                 mono_mb_emit_icon (mb, sizeof (gpointer) == 4 ? 2 : 3);
7183                 mono_mb_emit_byte (mb, CEE_SHL);
7184                 mono_mb_emit_byte (mb, CEE_ADD);
7185                 mono_mb_emit_ldarg (mb, 0);
7186                 mono_mb_emit_byte (mb, CEE_STIND_I);
7187
7188                 // STORE_REMSET_BUFFER_INDEX = buffer_index;
7189                 EMIT_TLS_ACCESS (mb, store_remset_buffer_index_addr, store_remset_buffer_index_addr_offset);
7190                 mono_mb_emit_ldloc (mb, buffer_index_var);
7191                 mono_mb_emit_byte (mb, CEE_STIND_I);
7192
7193                 // return;
7194                 mono_mb_patch_branch (mb, label_no_wb_1);
7195                 mono_mb_patch_branch (mb, label_no_wb_2);
7196                 mono_mb_patch_branch (mb, label_no_wb_3);
7197                 mono_mb_patch_branch (mb, label_no_wb_4);
7198 #ifndef SGEN_ALIGN_NURSERY
7199                 mono_mb_patch_branch (mb, label_no_wb_5);
7200 #endif
7201                 mono_mb_emit_byte (mb, CEE_RET);
7202
7203                 // slow path
7204                 mono_mb_patch_branch (mb, label_slow_path);
7205         }
7206 #endif
7207
7208         mono_mb_emit_ldarg (mb, 0);
7209         mono_mb_emit_icall (mb, mono_gc_wbarrier_generic_nostore);
7210         mono_mb_emit_byte (mb, CEE_RET);
7211
7212         res = mono_mb_create_method (mb, sig, 16);
7213         mono_mb_free (mb);
7214
7215         mono_loader_lock ();
7216         if (write_barrier_method) {
7217                 /* Already created */
7218                 mono_free_method (res);
7219         } else {
7220                 /* double-checked locking */
7221                 mono_memory_barrier ();
7222                 write_barrier_method = res;
7223         }
7224         mono_loader_unlock ();
7225
7226         return write_barrier_method;
7227 }
7228
7229 char*
7230 mono_gc_get_description (void)
7231 {
7232         return g_strdup ("sgen");
7233 }
7234
7235 void
7236 mono_gc_set_desktop_mode (void)
7237 {
7238 }
7239
7240 gboolean
7241 mono_gc_is_moving (void)
7242 {
7243         return TRUE;
7244 }
7245
7246 gboolean
7247 mono_gc_is_disabled (void)
7248 {
7249         return FALSE;
7250 }
7251
7252 void
7253 mono_sgen_debug_printf (int level, const char *format, ...)
7254 {
7255         va_list ap;
7256
7257         if (level > gc_debug_level)
7258                 return;
7259
7260         va_start (ap, format);
7261         vfprintf (gc_debug_file, format, ap);
7262         va_end (ap);
7263 }
7264
7265 #endif /* HAVE_SGEN_GC */