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