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