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