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