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