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