When calculating allowance, don't blindly cast from double to uint as it might not...
[mono-project.git] / mono / metadata / sgen-gc.c
blobbac056b91bd74135ef673d04c1908ee0669eb156
1 /*
2 * sgen-gc.c: Simple generational GC.
4 * Author:
5 * Paolo Molaro (lupus@ximian.com)
6 * Rodrigo Kumpera (kumpera@gmail.com)
8 * Copyright 2005-2011 Novell, Inc (http://www.novell.com)
9 * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
11 * Thread start/stop adapted from Boehm's GC:
12 * Copyright (c) 1994 by Xerox Corporation. All rights reserved.
13 * Copyright (c) 1996 by Silicon Graphics. All rights reserved.
14 * Copyright (c) 1998 by Fergus Henderson. All rights reserved.
15 * Copyright (c) 2000-2004 by Hewlett-Packard Company. All rights reserved.
17 * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
18 * OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
20 * Permission is hereby granted to use or copy this program
21 * for any purpose, provided the above notices are retained on all copies.
22 * Permission to modify the code and to distribute modified code is granted,
23 * provided the above notices are retained, and a notice that the code was
24 * modified is included with the above copyright notice.
27 * Copyright 2001-2003 Ximian, Inc
28 * Copyright 2003-2010 Novell, Inc.
29 * Copyright 2011 Xamarin, Inc.
31 * Permission is hereby granted, free of charge, to any person obtaining
32 * a copy of this software and associated documentation files (the
33 * "Software"), to deal in the Software without restriction, including
34 * without limitation the rights to use, copy, modify, merge, publish,
35 * distribute, sublicense, and/or sell copies of the Software, and to
36 * permit persons to whom the Software is furnished to do so, subject to
37 * the following conditions:
39 * The above copyright notice and this permission notice shall be
40 * included in all copies or substantial portions of the Software.
42 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
43 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
44 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
45 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
46 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
47 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
48 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
51 * Important: allocation provides always zeroed memory, having to do
52 * a memset after allocation is deadly for performance.
53 * Memory usage at startup is currently as follows:
54 * 64 KB pinned space
55 * 64 KB internal space
56 * size of nursery
57 * We should provide a small memory config with half the sizes
59 * We currently try to make as few mono assumptions as possible:
60 * 1) 2-word header with no GC pointers in it (first vtable, second to store the
61 * forwarding ptr)
62 * 2) gc descriptor is the second word in the vtable (first word in the class)
63 * 3) 8 byte alignment is the minimum and enough (not true for special structures (SIMD), FIXME)
64 * 4) there is a function to get an object's size and the number of
65 * elements in an array.
66 * 5) we know the special way bounds are allocated for complex arrays
67 * 6) we know about proxies and how to treat them when domains are unloaded
69 * Always try to keep stack usage to a minimum: no recursive behaviour
70 * and no large stack allocs.
72 * General description.
73 * Objects are initially allocated in a nursery using a fast bump-pointer technique.
74 * When the nursery is full we start a nursery collection: this is performed with a
75 * copying GC.
76 * When the old generation is full we start a copying GC of the old generation as well:
77 * this will be changed to mark&sweep with copying when fragmentation becomes to severe
78 * in the future. Maybe we'll even do both during the same collection like IMMIX.
80 * The things that complicate this description are:
81 * *) pinned objects: we can't move them so we need to keep track of them
82 * *) no precise info of the thread stacks and registers: we need to be able to
83 * quickly find the objects that may be referenced conservatively and pin them
84 * (this makes the first issues more important)
85 * *) large objects are too expensive to be dealt with using copying GC: we handle them
86 * with mark/sweep during major collections
87 * *) some objects need to not move even if they are small (interned strings, Type handles):
88 * we use mark/sweep for them, too: they are not allocated in the nursery, but inside
89 * PinnedChunks regions
93 * TODO:
95 *) we could have a function pointer in MonoClass to implement
96 customized write barriers for value types
98 *) investigate the stuff needed to advance a thread to a GC-safe
99 point (single-stepping, read from unmapped memory etc) and implement it.
100 This would enable us to inline allocations and write barriers, for example,
101 or at least parts of them, like the write barrier checks.
102 We may need this also for handling precise info on stacks, even simple things
103 as having uninitialized data on the stack and having to wait for the prolog
104 to zero it. Not an issue for the last frame that we scan conservatively.
105 We could always not trust the value in the slots anyway.
107 *) modify the jit to save info about references in stack locations:
108 this can be done just for locals as a start, so that at least
109 part of the stack is handled precisely.
111 *) test/fix endianess issues
113 *) Implement a card table as the write barrier instead of remembered
114 sets? Card tables are not easy to implement with our current
115 memory layout. We have several different kinds of major heap
116 objects: Small objects in regular blocks, small objects in pinned
117 chunks and LOS objects. If we just have a pointer we have no way
118 to tell which kind of object it points into, therefore we cannot
119 know where its card table is. The least we have to do to make
120 this happen is to get rid of write barriers for indirect stores.
121 (See next item)
123 *) Get rid of write barriers for indirect stores. We can do this by
124 telling the GC to wbarrier-register an object once we do an ldloca
125 or ldelema on it, and to unregister it once it's not used anymore
126 (it can only travel downwards on the stack). The problem with
127 unregistering is that it needs to happen eventually no matter
128 what, even if exceptions are thrown, the thread aborts, etc.
129 Rodrigo suggested that we could do only the registering part and
130 let the collector find out (pessimistically) when it's safe to
131 unregister, namely when the stack pointer of the thread that
132 registered the object is higher than it was when the registering
133 happened. This might make for a good first implementation to get
134 some data on performance.
136 *) Some sort of blacklist support? Blacklists is a concept from the
137 Boehm GC: if during a conservative scan we find pointers to an
138 area which we might use as heap, we mark that area as unusable, so
139 pointer retention by random pinning pointers is reduced.
141 *) experiment with max small object size (very small right now - 2kb,
142 because it's tied to the max freelist size)
144 *) add an option to mmap the whole heap in one chunk: it makes for many
145 simplifications in the checks (put the nursery at the top and just use a single
146 check for inclusion/exclusion): the issue this has is that on 32 bit systems it's
147 not flexible (too much of the address space may be used by default or we can't
148 increase the heap as needed) and we'd need a race-free mechanism to return memory
149 back to the system (mprotect(PROT_NONE) will still keep the memory allocated if it
150 was written to, munmap is needed, but the following mmap may not find the same segment
151 free...)
153 *) memzero the major fragments after restarting the world and optionally a smaller
154 chunk at a time
156 *) investigate having fragment zeroing threads
158 *) separate locks for finalization and other minor stuff to reduce
159 lock contention
161 *) try a different copying order to improve memory locality
163 *) a thread abort after a store but before the write barrier will
164 prevent the write barrier from executing
166 *) specialized dynamically generated markers/copiers
168 *) Dynamically adjust TLAB size to the number of threads. If we have
169 too many threads that do allocation, we might need smaller TLABs,
170 and we might get better performance with larger TLABs if we only
171 have a handful of threads. We could sum up the space left in all
172 assigned TLABs and if that's more than some percentage of the
173 nursery size, reduce the TLAB size.
175 *) Explore placing unreachable objects on unused nursery memory.
176 Instead of memset'ng a region to zero, place an int[] covering it.
177 A good place to start is add_nursery_frag. The tricky thing here is
178 placing those objects atomically outside of a collection.
180 *) Allocation should use asymmetric Dekker synchronization:
181 http://blogs.oracle.com/dave/resource/Asymmetric-Dekker-Synchronization.txt
182 This should help weak consistency archs.
184 #include "config.h"
185 #ifdef HAVE_SGEN_GC
187 #include <unistd.h>
188 #include <stdio.h>
189 #include <string.h>
190 #include <semaphore.h>
191 #include <signal.h>
192 #include <errno.h>
193 #include <assert.h>
194 #ifdef __MACH__
195 #undef _XOPEN_SOURCE
196 #endif
197 #include <pthread.h>
198 #ifdef __MACH__
199 #define _XOPEN_SOURCE
200 #endif
201 #include "metadata/metadata-internals.h"
202 #include "metadata/class-internals.h"
203 #include "metadata/gc-internal.h"
204 #include "metadata/object-internals.h"
205 #include "metadata/threads.h"
206 #include "metadata/sgen-gc.h"
207 #include "metadata/sgen-cardtable.h"
208 #include "metadata/sgen-protocol.h"
209 #include "metadata/sgen-archdep.h"
210 #include "metadata/sgen-bridge.h"
211 #include "metadata/mono-gc.h"
212 #include "metadata/method-builder.h"
213 #include "metadata/profiler-private.h"
214 #include "metadata/monitor.h"
215 #include "metadata/threadpool-internals.h"
216 #include "metadata/mempool-internals.h"
217 #include "metadata/marshal.h"
218 #include "utils/mono-mmap.h"
219 #include "utils/mono-time.h"
220 #include "utils/mono-semaphore.h"
221 #include "utils/mono-counters.h"
222 #include "utils/mono-proclib.h"
223 #include "utils/mono-logger-internal.h"
224 #include "utils/mono-memory-model.h"
226 #include <mono/utils/memcheck.h>
228 #if defined(__MACH__)
229 #include "utils/mach-support.h"
230 #endif
232 #define OPDEF(a,b,c,d,e,f,g,h,i,j) \
233 a = i,
235 enum {
236 #include "mono/cil/opcode.def"
237 CEE_LAST
240 #undef OPDEF
242 #undef pthread_create
243 #undef pthread_join
244 #undef pthread_detach
247 * ######################################################################
248 * ######## Types and constants used by the GC.
249 * ######################################################################
252 static int gc_initialized = 0;
253 /* If set, check if we need to do something every X allocations */
254 static gboolean has_per_allocation_action;
255 /* If set, do a heap check every X allocation */
256 static guint32 verify_before_allocs = 0;
257 /* If set, do a minor collection before every X allocation */
258 static guint32 collect_before_allocs = 0;
259 /* If set, do a whole heap check before each collection */
260 static gboolean whole_heap_check_before_collection = FALSE;
261 /* If set, do a heap consistency check before each minor collection */
262 static gboolean consistency_check_at_minor_collection = FALSE;
263 /* If set, check that there are no references to the domain left at domain unload */
264 static gboolean xdomain_checks = FALSE;
265 /* If not null, dump the heap after each collection into this file */
266 static FILE *heap_dump_file = NULL;
267 /* If set, mark stacks conservatively, even if precise marking is possible */
268 gboolean conservative_stack_mark = FALSE;
269 /* If set, do a plausibility check on the scan_starts before and after
270 each collection */
271 static gboolean do_scan_starts_check = FALSE;
272 static gboolean disable_minor_collections = FALSE;
273 static gboolean disable_major_collections = FALSE;
275 #ifdef HEAVY_STATISTICS
276 static long long stat_objects_alloced = 0;
277 static long long stat_bytes_alloced = 0;
278 long long stat_objects_alloced_degraded = 0;
279 long long stat_bytes_alloced_degraded = 0;
280 static long long stat_bytes_alloced_los = 0;
282 long long stat_copy_object_called_nursery = 0;
283 long long stat_objects_copied_nursery = 0;
284 long long stat_copy_object_called_major = 0;
285 long long stat_objects_copied_major = 0;
287 long long stat_scan_object_called_nursery = 0;
288 long long stat_scan_object_called_major = 0;
290 long long stat_nursery_copy_object_failed_from_space = 0;
291 long long stat_nursery_copy_object_failed_forwarded = 0;
292 long long stat_nursery_copy_object_failed_pinned = 0;
294 static long long stat_store_remsets = 0;
295 static long long stat_store_remsets_unique = 0;
296 static long long stat_saved_remsets_1 = 0;
297 static long long stat_saved_remsets_2 = 0;
298 static long long stat_local_remsets_processed = 0;
299 static long long stat_global_remsets_added = 0;
300 static long long stat_global_remsets_readded = 0;
301 static long long stat_global_remsets_processed = 0;
302 static long long stat_global_remsets_discarded = 0;
304 static long long stat_wasted_fragments_used = 0;
305 static long long stat_wasted_fragments_bytes = 0;
307 static int stat_wbarrier_set_field = 0;
308 static int stat_wbarrier_set_arrayref = 0;
309 static int stat_wbarrier_arrayref_copy = 0;
310 static int stat_wbarrier_generic_store = 0;
311 static int stat_wbarrier_generic_store_remset = 0;
312 static int stat_wbarrier_set_root = 0;
313 static int stat_wbarrier_value_copy = 0;
314 static int stat_wbarrier_object_copy = 0;
315 #endif
317 static long long stat_pinned_objects = 0;
319 static long long time_minor_pre_collection_fragment_clear = 0;
320 static long long time_minor_pinning = 0;
321 static long long time_minor_scan_remsets = 0;
322 static long long time_minor_scan_card_table = 0;
323 static long long time_minor_scan_pinned = 0;
324 static long long time_minor_scan_registered_roots = 0;
325 static long long time_minor_scan_thread_data = 0;
326 static long long time_minor_finish_gray_stack = 0;
327 static long long time_minor_fragment_creation = 0;
329 static long long time_major_pre_collection_fragment_clear = 0;
330 static long long time_major_pinning = 0;
331 static long long time_major_scan_pinned = 0;
332 static long long time_major_scan_registered_roots = 0;
333 static long long time_major_scan_thread_data = 0;
334 static long long time_major_scan_alloc_pinned = 0;
335 static long long time_major_scan_finalized = 0;
336 static long long time_major_scan_big_objects = 0;
337 static long long time_major_finish_gray_stack = 0;
338 static long long time_major_free_bigobjs = 0;
339 static long long time_major_los_sweep = 0;
340 static long long time_major_sweep = 0;
341 static long long time_major_fragment_creation = 0;
343 #define DEBUG(level,a) do {if (G_UNLIKELY ((level) <= SGEN_MAX_DEBUG_LEVEL && (level) <= gc_debug_level)) a;} while (0)
345 int gc_debug_level = 0;
346 FILE* gc_debug_file;
347 static gboolean debug_print_allowance = FALSE;
350 void
351 mono_gc_flush_info (void)
353 fflush (gc_debug_file);
358 * Define this to allow the user to change the nursery size by
359 * specifying its value in the MONO_GC_PARAMS environmental
360 * variable. See mono_gc_base_init for details.
362 #define USER_CONFIG 1
364 #define TV_DECLARE SGEN_TV_DECLARE
365 #define TV_GETTIME SGEN_TV_GETTIME
366 #define TV_ELAPSED SGEN_TV_ELAPSED
367 #define TV_ELAPSED_MS SGEN_TV_ELAPSED_MS
369 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
371 /* The method used to clear the nursery */
372 /* Clearing at nursery collections is the safest, but has bad interactions with caches.
373 * Clearing at TLAB creation is much faster, but more complex and it might expose hard
374 * to find bugs.
376 typedef enum {
377 CLEAR_AT_GC,
378 CLEAR_AT_TLAB_CREATION
379 } NurseryClearPolicy;
381 static NurseryClearPolicy nursery_clear_policy = CLEAR_AT_TLAB_CREATION;
384 * The young generation is divided into fragments. This is because
385 * we can hand one fragments to a thread for lock-less fast alloc and
386 * because the young generation ends up fragmented anyway by pinned objects.
387 * Once a collection is done, a list of fragments is created. When doing
388 * thread local alloc we use smallish nurseries so we allow new threads to
389 * allocate memory from gen0 without triggering a collection. Threads that
390 * are found to allocate lots of memory are given bigger fragments. This
391 * should make the finalizer thread use little nursery memory after a while.
392 * We should start assigning threads very small fragments: if there are many
393 * threads the nursery will be full of reserved space that the threads may not
394 * use at all, slowing down allocation speed.
395 * Thread local allocation is done from areas of memory Hotspot calls Thread Local
396 * Allocation Buffers (TLABs).
398 typedef struct _Fragment Fragment;
400 struct _Fragment {
401 Fragment *next;
402 char *fragment_start;
403 char *fragment_limit; /* the current soft limit for allocation */
404 char *fragment_end;
407 /* the runtime can register areas of memory as roots: we keep two lists of roots,
408 * a pinned root set for conservatively scanned roots and a normal one for
409 * precisely scanned roots (currently implemented as a single list).
411 typedef struct _RootRecord RootRecord;
412 struct _RootRecord {
413 RootRecord *next;
414 char *start_root;
415 char *end_root;
416 mword root_desc;
420 * We're never actually using the first element. It's always set to
421 * NULL to simplify the elimination of consecutive duplicate
422 * entries.
424 #define STORE_REMSET_BUFFER_SIZE 1024
426 typedef struct _GenericStoreRememberedSet GenericStoreRememberedSet;
427 struct _GenericStoreRememberedSet {
428 GenericStoreRememberedSet *next;
429 /* We need one entry less because the first entry of store
430 remset buffers is always a dummy and we don't copy it. */
431 gpointer data [STORE_REMSET_BUFFER_SIZE - 1];
434 /* we have 4 possible values in the low 2 bits */
435 enum {
436 REMSET_LOCATION, /* just a pointer to the exact location */
437 REMSET_RANGE, /* range of pointer fields */
438 REMSET_OBJECT, /* mark all the object for scanning */
439 REMSET_VTYPE, /* a valuetype array described by a MonoClass pointer and a count */
440 REMSET_TYPE_MASK = 0x3
443 #ifdef HAVE_KW_THREAD
444 static __thread RememberedSet *remembered_set MONO_TLS_FAST;
445 #endif
446 static pthread_key_t remembered_set_key;
447 static RememberedSet *global_remset;
448 static RememberedSet *freed_thread_remsets;
449 static GenericStoreRememberedSet *generic_store_remsets = NULL;
451 /*A two slots cache for recently inserted remsets */
452 static gpointer global_remset_cache [2];
454 /* FIXME: later choose a size that takes into account the RememberedSet struct
455 * and doesn't waste any alloc paddin space.
457 #define DEFAULT_REMSET_SIZE 1024
458 static RememberedSet* alloc_remset (int size, gpointer id);
460 #define object_is_forwarded SGEN_OBJECT_IS_FORWARDED
461 #define object_is_pinned SGEN_OBJECT_IS_PINNED
462 #define pin_object SGEN_PIN_OBJECT
463 #define unpin_object SGEN_UNPIN_OBJECT
465 #define ptr_in_nursery(p) (SGEN_PTR_IN_NURSERY ((p), DEFAULT_NURSERY_BITS, nursery_start, nursery_real_end))
467 #define LOAD_VTABLE SGEN_LOAD_VTABLE
469 static const char*
470 safe_name (void* obj)
472 MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (obj);
473 return vt->klass->name;
476 #define safe_object_get_size mono_sgen_safe_object_get_size
478 const char*
479 mono_sgen_safe_name (void* obj)
481 return safe_name (obj);
485 * ######################################################################
486 * ######## Global data.
487 * ######################################################################
489 static LOCK_DECLARE (gc_mutex);
490 static int gc_disabled = 0;
491 static int num_minor_gcs = 0;
492 static int num_major_gcs = 0;
494 static gboolean use_cardtable;
496 #ifdef USER_CONFIG
498 /* good sizes are 512KB-1MB: larger ones increase a lot memzeroing time */
499 #define DEFAULT_NURSERY_SIZE (default_nursery_size)
500 static int default_nursery_size = (1 << 22);
501 #ifdef SGEN_ALIGN_NURSERY
502 /* The number of trailing 0 bits in DEFAULT_NURSERY_SIZE */
503 #define DEFAULT_NURSERY_BITS (default_nursery_bits)
504 static int default_nursery_bits = 22;
505 #endif
507 #else
509 #define DEFAULT_NURSERY_SIZE (4*1024*1024)
510 #ifdef SGEN_ALIGN_NURSERY
511 #define DEFAULT_NURSERY_BITS 22
512 #endif
514 #endif
516 #ifndef SGEN_ALIGN_NURSERY
517 #define DEFAULT_NURSERY_BITS -1
518 #endif
520 #define MIN_MINOR_COLLECTION_ALLOWANCE (DEFAULT_NURSERY_SIZE * 4)
522 #define SCAN_START_SIZE SGEN_SCAN_START_SIZE
524 /* the minimum size of a fragment that we consider useful for allocation */
525 #define FRAGMENT_MIN_SIZE (512)
527 static mword pagesize = 4096;
528 static mword nursery_size;
529 static int degraded_mode = 0;
531 static mword total_alloc = 0;
532 /* use this to tune when to do a major/minor collection */
533 static mword memory_pressure = 0;
534 static mword minor_collection_allowance;
535 static int minor_collection_sections_alloced = 0;
538 /* GC Logging stats */
539 static int last_major_num_sections = 0;
540 static int last_los_memory_usage = 0;
541 static gboolean major_collection_hapenned = FALSE;
543 static GCMemSection *nursery_section = NULL;
544 static mword lowest_heap_address = ~(mword)0;
545 static mword highest_heap_address = 0;
547 static LOCK_DECLARE (interruption_mutex);
548 static LOCK_DECLARE (global_remset_mutex);
549 static LOCK_DECLARE (pin_queue_mutex);
551 #define LOCK_GLOBAL_REMSET pthread_mutex_lock (&global_remset_mutex)
552 #define UNLOCK_GLOBAL_REMSET pthread_mutex_unlock (&global_remset_mutex)
554 #define LOCK_PIN_QUEUE pthread_mutex_lock (&pin_queue_mutex)
555 #define UNLOCK_PIN_QUEUE pthread_mutex_unlock (&pin_queue_mutex)
557 typedef struct _FinalizeEntry FinalizeEntry;
558 struct _FinalizeEntry {
559 FinalizeEntry *next;
560 void *object;
563 typedef struct _FinalizeEntryHashTable FinalizeEntryHashTable;
564 struct _FinalizeEntryHashTable {
565 FinalizeEntry **table;
566 mword size;
567 int num_registered;
570 typedef struct _DisappearingLink DisappearingLink;
571 struct _DisappearingLink {
572 DisappearingLink *next;
573 void **link;
576 typedef struct _DisappearingLinkHashTable DisappearingLinkHashTable;
577 struct _DisappearingLinkHashTable {
578 DisappearingLink **table;
579 mword size;
580 int num_links;
583 typedef struct _EphemeronLinkNode EphemeronLinkNode;
585 struct _EphemeronLinkNode {
586 EphemeronLinkNode *next;
587 char *array;
590 typedef struct {
591 void *key;
592 void *value;
593 } Ephemeron;
595 int current_collection_generation = -1;
598 * The link pointer is hidden by negating each bit. We use the lowest
599 * bit of the link (before negation) to store whether it needs
600 * resurrection tracking.
602 #define HIDE_POINTER(p,t) ((gpointer)(~((gulong)(p)|((t)?1:0))))
603 #define REVEAL_POINTER(p) ((gpointer)((~(gulong)(p))&~3L))
605 #define DISLINK_OBJECT(d) (REVEAL_POINTER (*(d)->link))
606 #define DISLINK_TRACK(d) ((~(gulong)(*(d)->link)) & 1)
609 * The finalizable hash has the object as the key, the
610 * disappearing_link hash, has the link address as key.
612 static FinalizeEntryHashTable minor_finalizable_hash;
613 static FinalizeEntryHashTable major_finalizable_hash;
614 /* objects that are ready to be finalized */
615 static FinalizeEntry *fin_ready_list = NULL;
616 static FinalizeEntry *critical_fin_list = NULL;
618 static DisappearingLinkHashTable minor_disappearing_link_hash;
619 static DisappearingLinkHashTable major_disappearing_link_hash;
621 static EphemeronLinkNode *ephemeron_list;
623 static int num_ready_finalizers = 0;
624 static int no_finalize = 0;
626 enum {
627 ROOT_TYPE_NORMAL = 0, /* "normal" roots */
628 ROOT_TYPE_PINNED = 1, /* roots without a GC descriptor */
629 ROOT_TYPE_WBARRIER = 2, /* roots with a write barrier */
630 ROOT_TYPE_NUM
633 /* registered roots: the key to the hash is the root start address */
635 * Different kinds of roots are kept separate to speed up pin_from_roots () for example.
637 static RootRecord **roots_hash [ROOT_TYPE_NUM] = { NULL, NULL };
638 static int roots_hash_size [ROOT_TYPE_NUM] = { 0, 0, 0 };
639 static mword roots_size = 0; /* amount of memory in the root set */
640 static int num_roots_entries [ROOT_TYPE_NUM] = { 0, 0, 0 };
642 #define GC_ROOT_NUM 32
643 typedef struct {
644 int count;
645 void *objects [GC_ROOT_NUM];
646 int root_types [GC_ROOT_NUM];
647 uintptr_t extra_info [GC_ROOT_NUM];
648 } GCRootReport;
650 static void
651 notify_gc_roots (GCRootReport *report)
653 if (!report->count)
654 return;
655 mono_profiler_gc_roots (report->count, report->objects, report->root_types, report->extra_info);
656 report->count = 0;
659 static void
660 add_profile_gc_root (GCRootReport *report, void *object, int rtype, uintptr_t extra_info)
662 if (report->count == GC_ROOT_NUM)
663 notify_gc_roots (report);
664 report->objects [report->count] = object;
665 report->root_types [report->count] = rtype;
666 report->extra_info [report->count++] = (uintptr_t)((MonoVTable*)LOAD_VTABLE (object))->klass;
670 * The current allocation cursors
671 * We allocate objects in the nursery.
672 * The nursery is the area between nursery_start and nursery_real_end.
673 * Allocation is done from a Thread Local Allocation Buffer (TLAB). TLABs are allocated
674 * from nursery fragments.
675 * tlab_next is the pointer to the space inside the TLAB where the next object will
676 * be allocated.
677 * tlab_temp_end is the pointer to the end of the temporary space reserved for
678 * the allocation: it allows us to set the scan starts at reasonable intervals.
679 * tlab_real_end points to the end of the TLAB.
680 * nursery_frag_real_end points to the end of the currently used nursery fragment.
681 * nursery_first_pinned_start points to the start of the first pinned object in the nursery
682 * nursery_last_pinned_end points to the end of the last pinned object in the nursery
683 * At the next allocation, the area of the nursery where objects can be present is
684 * between MIN(nursery_first_pinned_start, first_fragment_start) and
685 * MAX(nursery_last_pinned_end, nursery_frag_real_end)
687 static char *nursery_start = NULL;
689 #ifdef HAVE_KW_THREAD
690 #define TLAB_ACCESS_INIT
691 #define TLAB_START tlab_start
692 #define TLAB_NEXT tlab_next
693 #define TLAB_TEMP_END tlab_temp_end
694 #define TLAB_REAL_END tlab_real_end
695 #define REMEMBERED_SET remembered_set
696 #define STORE_REMSET_BUFFER store_remset_buffer
697 #define STORE_REMSET_BUFFER_INDEX store_remset_buffer_index
698 #define IN_CRITICAL_REGION thread_info->in_critical_region
699 #else
700 static pthread_key_t thread_info_key;
701 #define TLAB_ACCESS_INIT SgenThreadInfo *__thread_info__ = pthread_getspecific (thread_info_key)
702 #define TLAB_START (__thread_info__->tlab_start)
703 #define TLAB_NEXT (__thread_info__->tlab_next)
704 #define TLAB_TEMP_END (__thread_info__->tlab_temp_end)
705 #define TLAB_REAL_END (__thread_info__->tlab_real_end)
706 #define REMEMBERED_SET (__thread_info__->remset)
707 #define STORE_REMSET_BUFFER (__thread_info__->store_remset_buffer)
708 #define STORE_REMSET_BUFFER_INDEX (__thread_info__->store_remset_buffer_index)
709 #define IN_CRITICAL_REGION (__thread_info__->in_critical_region)
710 #endif
712 #ifndef DISABLE_CRITICAL_REGION
714 /* Enter must be visible before anything is done in the critical region. */
715 #define ENTER_CRITICAL_REGION do { mono_atomic_store_acquire (&IN_CRITICAL_REGION, 1); } while (0)
717 /* Exit must make sure all critical regions stores are visible before it signal the end of the region.
718 * We don't need to emit a full barrier since we
720 #define EXIT_CRITICAL_REGION do { mono_atomic_store_release (&IN_CRITICAL_REGION, 0); } while (0)
723 #endif
726 * FIXME: What is faster, a TLS variable pointing to a structure, or separate TLS
727 * variables for next+temp_end ?
729 #ifdef HAVE_KW_THREAD
730 static __thread SgenThreadInfo *thread_info;
731 static __thread char *tlab_start;
732 static __thread char *tlab_next;
733 static __thread char *tlab_temp_end;
734 static __thread char *tlab_real_end;
735 static __thread gpointer *store_remset_buffer;
736 static __thread long store_remset_buffer_index;
737 /* Used by the managed allocator/wbarrier */
738 static __thread char **tlab_next_addr;
739 static __thread char *stack_end;
740 static __thread long *store_remset_buffer_index_addr;
741 #endif
742 static char *nursery_next = NULL;
743 static char *nursery_frag_real_end = NULL;
744 static char *nursery_real_end = NULL;
745 static char *nursery_last_pinned_end = NULL;
747 /* The size of a TLAB */
748 /* The bigger the value, the less often we have to go to the slow path to allocate a new
749 * one, but the more space is wasted by threads not allocating much memory.
750 * FIXME: Tune this.
751 * FIXME: Make this self-tuning for each thread.
753 static guint32 tlab_size = (1024 * 4);
755 /*How much space is tolerable to be wasted from the current fragment when allocating a new TLAB*/
756 #define MAX_NURSERY_TLAB_WASTE 512
758 /* fragments that are free and ready to be used for allocation */
759 static Fragment *nursery_fragments = NULL;
760 /* freeelist of fragment structures */
761 static Fragment *fragment_freelist = NULL;
763 #define MAX_SMALL_OBJ_SIZE SGEN_MAX_SMALL_OBJ_SIZE
765 /* Functions supplied by the runtime to be called by the GC */
766 static MonoGCCallbacks gc_callbacks;
768 #define ALLOC_ALIGN SGEN_ALLOC_ALIGN
769 #define ALLOC_ALIGN_BITS SGEN_ALLOC_ALIGN_BITS
771 #define ALIGN_UP SGEN_ALIGN_UP
773 #define MOVED_OBJECTS_NUM 64
774 static void *moved_objects [MOVED_OBJECTS_NUM];
775 static int moved_objects_idx = 0;
777 /* Vtable of the objects used to fill out nursery fragments before a collection */
778 static MonoVTable *array_fill_vtable;
781 * ######################################################################
782 * ######## Heap size accounting
783 * ######################################################################
785 /*heap limits*/
786 static mword max_heap_size = ((mword)0)- ((mword)1);
787 static mword soft_heap_limit = ((mword)0) - ((mword)1);
788 static mword allocated_heap;
790 /*Object was pinned during the current collection*/
791 static mword objects_pinned;
793 void
794 mono_sgen_release_space (mword size, int space)
796 allocated_heap -= size;
799 static size_t
800 available_free_space (void)
802 return max_heap_size - MIN (allocated_heap, max_heap_size);
805 gboolean
806 mono_sgen_try_alloc_space (mword size, int space)
808 if (available_free_space () < size)
809 return FALSE;
811 allocated_heap += size;
812 return TRUE;
815 static void
816 init_heap_size_limits (glong max_heap, glong soft_limit)
818 if (soft_limit)
819 soft_heap_limit = soft_limit;
821 if (max_heap == 0)
822 return;
824 if (max_heap < soft_limit) {
825 fprintf (stderr, "max-heap-size must be at least as large as soft-heap-limit.\n");
826 exit (1);
829 if (max_heap < nursery_size * 4) {
830 fprintf (stderr, "max-heap-size must be at least 4 times larger than nursery size.\n");
831 exit (1);
833 max_heap_size = max_heap - nursery_size;
837 * ######################################################################
838 * ######## Macros and function declarations.
839 * ######################################################################
842 inline static void*
843 align_pointer (void *ptr)
845 mword p = (mword)ptr;
846 p += sizeof (gpointer) - 1;
847 p &= ~ (sizeof (gpointer) - 1);
848 return (void*)p;
851 typedef SgenGrayQueue GrayQueue;
853 typedef char* (*ScanObjectFunc) (char*, GrayQueue*);
856 static inline MonoObject*
857 finalize_entry_get_object (FinalizeEntry *entry)
859 return (MonoObject*)(((mword)entry->object) & ~(mword)0x1);
862 static inline gboolean
863 finalize_entry_get_bridge_bit (FinalizeEntry *entry)
865 return (((mword)entry->object) & 0x1);
868 static inline void
869 finalize_entry_set_object (FinalizeEntry *entry, MonoObject *object, gboolean bridge_bit)
871 entry->object = (MonoObject*)((mword)object | (mword)bridge_bit);
874 /* forward declarations */
875 static int stop_world (int generation);
876 static int restart_world (int generation);
877 static void scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise);
878 static void scan_from_remsets (void *start_nursery, void *end_nursery, GrayQueue *queue);
879 static void scan_from_registered_roots (CopyOrMarkObjectFunc copy_func, char *addr_start, char *addr_end, int root_type, GrayQueue *queue);
880 static void scan_finalizer_entries (CopyOrMarkObjectFunc copy_func, FinalizeEntry *list, GrayQueue *queue);
881 static void report_finalizer_roots (void);
882 static void report_registered_roots (void);
883 static void find_pinning_ref_from_thread (char *obj, size_t size);
884 static void update_current_thread_stack (void *start);
885 static void collect_bridge_objects (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, GrayQueue *queue);
886 static void finalize_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, GrayQueue *queue);
887 static void add_or_remove_disappearing_link (MonoObject *obj, void **link, gboolean track, int generation);
888 static void null_link_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, gboolean before_finalization, GrayQueue *queue);
889 static void null_links_for_domain (MonoDomain *domain, int generation);
890 static void remove_finalizers_for_domain (MonoDomain *domain, int generation);
891 static gboolean search_fragment_for_size (size_t size);
892 static int search_fragment_for_size_range (size_t desired_size, size_t minimum_size);
893 static void clear_nursery_fragments (char *next);
894 static void pin_from_roots (void *start_nursery, void *end_nursery);
895 static int pin_objects_from_addresses (GCMemSection *section, void **start, void **end, void *start_nursery, void *end_nursery, GrayQueue *queue);
896 static void optimize_pin_queue (int start_slot);
897 static void clear_remsets (void);
898 static void clear_tlabs (void);
899 static void sort_addresses (void **array, int size);
900 static void drain_gray_stack (GrayQueue *queue);
901 static void finish_gray_stack (char *start_addr, char *end_addr, int generation, GrayQueue *queue);
902 static gboolean need_major_collection (mword space_needed);
903 static void major_collection (const char *reason);
905 static void mono_gc_register_disappearing_link (MonoObject *obj, void **link, gboolean track);
907 void describe_ptr (char *ptr);
908 void check_object (char *start);
910 static void check_consistency (void);
911 static void check_major_refs (void);
912 static void check_scan_starts (void);
913 static void check_for_xdomain_refs (void);
914 static void dump_heap (const char *type, int num, const char *reason);
915 static void verify_whole_heap (void);
916 static void whole_heap_check (void);
918 void mono_gc_scan_for_specific_ref (MonoObject *key);
920 static void init_stats (void);
922 static int mark_ephemerons_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, GrayQueue *queue);
923 static void clear_unreachable_ephemerons (CopyOrMarkObjectFunc copy_func, char *start, char *end, GrayQueue *queue);
924 static void null_ephemerons_for_domain (MonoDomain *domain);
926 SgenMajorCollector major_collector;
928 #include "sgen-pinning.c"
929 #include "sgen-pinning-stats.c"
930 #include "sgen-gray.c"
931 #include "sgen-workers.c"
932 #include "sgen-cardtable.c"
934 /* Root bitmap descriptors are simpler: the lower three bits describe the type
935 * and we either have 30/62 bitmap bits or nibble-based run-length,
936 * or a complex descriptor, or a user defined marker function.
938 enum {
939 ROOT_DESC_CONSERVATIVE, /* 0, so matches NULL value */
940 ROOT_DESC_BITMAP,
941 ROOT_DESC_RUN_LEN,
942 ROOT_DESC_COMPLEX,
943 ROOT_DESC_USER,
944 ROOT_DESC_TYPE_MASK = 0x7,
945 ROOT_DESC_TYPE_SHIFT = 3,
948 #define MAKE_ROOT_DESC(type,val) ((type) | ((val) << ROOT_DESC_TYPE_SHIFT))
950 #define MAX_USER_DESCRIPTORS 16
952 static gsize* complex_descriptors = NULL;
953 static int complex_descriptors_size = 0;
954 static int complex_descriptors_next = 0;
955 static MonoGCRootMarkFunc user_descriptors [MAX_USER_DESCRIPTORS];
956 static int user_descriptors_next = 0;
958 static int
959 alloc_complex_descriptor (gsize *bitmap, int numbits)
961 int nwords, res, i;
963 numbits = ALIGN_TO (numbits, GC_BITS_PER_WORD);
964 nwords = numbits / GC_BITS_PER_WORD + 1;
966 LOCK_GC;
967 res = complex_descriptors_next;
968 /* linear search, so we don't have duplicates with domain load/unload
969 * this should not be performance critical or we'd have bigger issues
970 * (the number and size of complex descriptors should be small).
972 for (i = 0; i < complex_descriptors_next; ) {
973 if (complex_descriptors [i] == nwords) {
974 int j, found = TRUE;
975 for (j = 0; j < nwords - 1; ++j) {
976 if (complex_descriptors [i + 1 + j] != bitmap [j]) {
977 found = FALSE;
978 break;
981 if (found) {
982 UNLOCK_GC;
983 return i;
986 i += complex_descriptors [i];
988 if (complex_descriptors_next + nwords > complex_descriptors_size) {
989 int new_size = complex_descriptors_size * 2 + nwords;
990 complex_descriptors = g_realloc (complex_descriptors, new_size * sizeof (gsize));
991 complex_descriptors_size = new_size;
993 DEBUG (6, fprintf (gc_debug_file, "Complex descriptor %d, size: %d (total desc memory: %d)\n", res, nwords, complex_descriptors_size));
994 complex_descriptors_next += nwords;
995 complex_descriptors [res] = nwords;
996 for (i = 0; i < nwords - 1; ++i) {
997 complex_descriptors [res + 1 + i] = bitmap [i];
998 DEBUG (6, fprintf (gc_debug_file, "\tvalue: %p\n", (void*)complex_descriptors [res + 1 + i]));
1000 UNLOCK_GC;
1001 return res;
1004 gsize*
1005 mono_sgen_get_complex_descriptor (mword desc)
1007 return complex_descriptors + (desc >> LOW_TYPE_BITS);
1011 * Descriptor builders.
1013 void*
1014 mono_gc_make_descr_for_string (gsize *bitmap, int numbits)
1016 return (void*) DESC_TYPE_RUN_LENGTH;
1019 void*
1020 mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size)
1022 int first_set = -1, num_set = 0, last_set = -1, i;
1023 mword desc = 0;
1024 size_t stored_size = obj_size;
1025 for (i = 0; i < numbits; ++i) {
1026 if (bitmap [i / GC_BITS_PER_WORD] & ((gsize)1 << (i % GC_BITS_PER_WORD))) {
1027 if (first_set < 0)
1028 first_set = i;
1029 last_set = i;
1030 num_set++;
1034 * We don't encode the size of types that don't contain
1035 * references because they might not be aligned, i.e. the
1036 * bottom two bits might be set, which would clash with the
1037 * bits we need to encode the descriptor type. Since we don't
1038 * use the encoded size to skip objects, other than for
1039 * processing remsets, in which case only the positions of
1040 * references are relevant, this is not a problem.
1042 if (first_set < 0)
1043 return (void*)DESC_TYPE_RUN_LENGTH;
1044 g_assert (!(stored_size & 0x3));
1045 if (stored_size <= MAX_SMALL_OBJ_SIZE) {
1046 /* check run-length encoding first: one byte offset, one byte number of pointers
1047 * on 64 bit archs, we can have 3 runs, just one on 32.
1048 * It may be better to use nibbles.
1050 if (first_set < 0) {
1051 desc = DESC_TYPE_RUN_LENGTH | (stored_size << 1);
1052 DEBUG (6, fprintf (gc_debug_file, "Ptrfree descriptor %p, size: %zd\n", (void*)desc, stored_size));
1053 return (void*) desc;
1054 } else if (first_set < 256 && num_set < 256 && (first_set + num_set == last_set + 1)) {
1055 desc = DESC_TYPE_RUN_LENGTH | (stored_size << 1) | (first_set << 16) | (num_set << 24);
1056 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));
1057 return (void*) desc;
1059 /* we know the 2-word header is ptr-free */
1060 if (last_set < SMALL_BITMAP_SIZE + OBJECT_HEADER_WORDS) {
1061 desc = DESC_TYPE_SMALL_BITMAP | (stored_size << 1) | ((*bitmap >> OBJECT_HEADER_WORDS) << SMALL_BITMAP_SHIFT);
1062 DEBUG (6, fprintf (gc_debug_file, "Smallbitmap descriptor %p, size: %zd, last set: %d\n", (void*)desc, stored_size, last_set));
1063 return (void*) desc;
1066 /* we know the 2-word header is ptr-free */
1067 if (last_set < LARGE_BITMAP_SIZE + OBJECT_HEADER_WORDS) {
1068 desc = DESC_TYPE_LARGE_BITMAP | ((*bitmap >> OBJECT_HEADER_WORDS) << LOW_TYPE_BITS);
1069 DEBUG (6, fprintf (gc_debug_file, "Largebitmap descriptor %p, size: %zd, last set: %d\n", (void*)desc, stored_size, last_set));
1070 return (void*) desc;
1072 /* it's a complex object ... */
1073 desc = DESC_TYPE_COMPLEX | (alloc_complex_descriptor (bitmap, last_set + 1) << LOW_TYPE_BITS);
1074 return (void*) desc;
1077 /* If the array holds references, numbits == 1 and the first bit is set in elem_bitmap */
1078 void*
1079 mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size)
1081 int first_set = -1, num_set = 0, last_set = -1, i;
1082 mword desc = vector? DESC_TYPE_VECTOR: DESC_TYPE_ARRAY;
1083 for (i = 0; i < numbits; ++i) {
1084 if (elem_bitmap [i / GC_BITS_PER_WORD] & ((gsize)1 << (i % GC_BITS_PER_WORD))) {
1085 if (first_set < 0)
1086 first_set = i;
1087 last_set = i;
1088 num_set++;
1091 /* See comment at the definition of DESC_TYPE_RUN_LENGTH. */
1092 if (first_set < 0)
1093 return (void*)DESC_TYPE_RUN_LENGTH;
1094 if (elem_size <= MAX_ELEMENT_SIZE) {
1095 desc |= elem_size << VECTOR_ELSIZE_SHIFT;
1096 if (!num_set) {
1097 return (void*)(desc | VECTOR_SUBTYPE_PTRFREE);
1099 /* Note: we also handle structs with just ref fields */
1100 if (num_set * sizeof (gpointer) == elem_size) {
1101 return (void*)(desc | VECTOR_SUBTYPE_REFS | ((gssize)(-1) << 16));
1103 /* FIXME: try run-len first */
1104 /* Note: we can't skip the object header here, because it's not present */
1105 if (last_set <= SMALL_BITMAP_SIZE) {
1106 return (void*)(desc | VECTOR_SUBTYPE_BITMAP | (*elem_bitmap << 16));
1109 /* it's am array of complex structs ... */
1110 desc = DESC_TYPE_COMPLEX_ARR;
1111 desc |= alloc_complex_descriptor (elem_bitmap, last_set + 1) << LOW_TYPE_BITS;
1112 return (void*) desc;
1115 /* Return the bitmap encoded by a descriptor */
1116 gsize*
1117 mono_gc_get_bitmap_for_descr (void *descr, int *numbits)
1119 mword d = (mword)descr;
1120 gsize *bitmap;
1122 switch (d & 0x7) {
1123 case DESC_TYPE_RUN_LENGTH: {
1124 int first_set = (d >> 16) & 0xff;
1125 int num_set = (d >> 24) & 0xff;
1126 int i;
1128 bitmap = g_new0 (gsize, (first_set + num_set + 7) / 8);
1130 for (i = first_set; i < first_set + num_set; ++i)
1131 bitmap [i / GC_BITS_PER_WORD] |= ((gsize)1 << (i % GC_BITS_PER_WORD));
1133 *numbits = first_set + num_set;
1135 return bitmap;
1137 case DESC_TYPE_SMALL_BITMAP:
1138 bitmap = g_new0 (gsize, 1);
1140 bitmap [0] = (d >> SMALL_BITMAP_SHIFT) << OBJECT_HEADER_WORDS;
1142 *numbits = GC_BITS_PER_WORD;
1144 return bitmap;
1145 default:
1146 g_assert_not_reached ();
1150 static gboolean
1151 is_xdomain_ref_allowed (gpointer *ptr, char *obj, MonoDomain *domain)
1153 MonoObject *o = (MonoObject*)(obj);
1154 MonoObject *ref = (MonoObject*)*(ptr);
1155 int offset = (char*)(ptr) - (char*)o;
1157 if (o->vtable->klass == mono_defaults.thread_class && offset == G_STRUCT_OFFSET (MonoThread, internal_thread))
1158 return TRUE;
1159 if (o->vtable->klass == mono_defaults.internal_thread_class && offset == G_STRUCT_OFFSET (MonoInternalThread, current_appcontext))
1160 return TRUE;
1161 if (mono_class_has_parent_fast (o->vtable->klass, mono_defaults.real_proxy_class) &&
1162 offset == G_STRUCT_OFFSET (MonoRealProxy, unwrapped_server))
1163 return TRUE;
1164 /* Thread.cached_culture_info */
1165 if (!strcmp (ref->vtable->klass->name_space, "System.Globalization") &&
1166 !strcmp (ref->vtable->klass->name, "CultureInfo") &&
1167 !strcmp(o->vtable->klass->name_space, "System") &&
1168 !strcmp(o->vtable->klass->name, "Object[]"))
1169 return TRUE;
1171 * 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
1172 * at System.IO.MemoryStream..ctor (byte[]) [0x00017] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.IO/MemoryStream.cs:81
1173 * at (wrapper remoting-invoke-with-check) System.IO.MemoryStream..ctor (byte[]) <IL 0x00020, 0xffffffff>
1174 * at System.Runtime.Remoting.Messaging.CADMethodCallMessage.GetArguments () [0x0000d] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.Runtime.Remoting.Messaging/CADMessages.cs:327
1175 * 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
1176 * 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
1177 * at (wrapper remoting-invoke-with-check) System.AppDomain.ProcessMessageInDomain (byte[],System.Runtime.Remoting.Messaging.CADMethodCallMessage,byte[]&,System.Runtime.Remoting.Messaging.CADMethodReturnMessage&) <IL 0x0003d, 0xffffffff>
1178 * 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
1179 * at (wrapper runtime-invoke) object.runtime_invoke_CrossAppDomainSink/ProcessMessageRes_object_object (object,intptr,intptr,intptr) <IL 0x0004c, 0xffffffff>
1181 if (!strcmp (ref->vtable->klass->name_space, "System") &&
1182 !strcmp (ref->vtable->klass->name, "Byte[]") &&
1183 !strcmp (o->vtable->klass->name_space, "System.IO") &&
1184 !strcmp (o->vtable->klass->name, "MemoryStream"))
1185 return TRUE;
1186 /* append_job() in threadpool.c */
1187 if (!strcmp (ref->vtable->klass->name_space, "System.Runtime.Remoting.Messaging") &&
1188 !strcmp (ref->vtable->klass->name, "AsyncResult") &&
1189 !strcmp (o->vtable->klass->name_space, "System") &&
1190 !strcmp (o->vtable->klass->name, "Object[]") &&
1191 mono_thread_pool_is_queue_array ((MonoArray*) o))
1192 return TRUE;
1193 return FALSE;
1196 static void
1197 check_reference_for_xdomain (gpointer *ptr, char *obj, MonoDomain *domain)
1199 MonoObject *o = (MonoObject*)(obj);
1200 MonoObject *ref = (MonoObject*)*(ptr);
1201 int offset = (char*)(ptr) - (char*)o;
1202 MonoClass *class;
1203 MonoClassField *field;
1204 char *str;
1206 if (!ref || ref->vtable->domain == domain)
1207 return;
1208 if (is_xdomain_ref_allowed (ptr, obj, domain))
1209 return;
1211 field = NULL;
1212 for (class = o->vtable->klass; class; class = class->parent) {
1213 int i;
1215 for (i = 0; i < class->field.count; ++i) {
1216 if (class->fields[i].offset == offset) {
1217 field = &class->fields[i];
1218 break;
1221 if (field)
1222 break;
1225 if (ref->vtable->klass == mono_defaults.string_class)
1226 str = mono_string_to_utf8 ((MonoString*)ref);
1227 else
1228 str = NULL;
1229 g_print ("xdomain reference in %p (%s.%s) at offset %d (%s) to %p (%s.%s) (%s) - pointed to by:\n",
1230 o, o->vtable->klass->name_space, o->vtable->klass->name,
1231 offset, field ? field->name : "",
1232 ref, ref->vtable->klass->name_space, ref->vtable->klass->name, str ? str : "");
1233 mono_gc_scan_for_specific_ref (o);
1234 if (str)
1235 g_free (str);
1238 #undef HANDLE_PTR
1239 #define HANDLE_PTR(ptr,obj) check_reference_for_xdomain ((ptr), (obj), domain)
1241 static void
1242 scan_object_for_xdomain_refs (char *start, mword size, void *data)
1244 MonoDomain *domain = ((MonoObject*)start)->vtable->domain;
1246 #include "sgen-scan-object.h"
1249 #undef HANDLE_PTR
1250 #define HANDLE_PTR(ptr,obj) do { \
1251 if ((MonoObject*)*(ptr) == key) { \
1252 g_print ("found ref to %p in object %p (%s) at offset %td\n", \
1253 key, (obj), safe_name ((obj)), ((char*)(ptr) - (char*)(obj))); \
1255 } while (0)
1257 static void
1258 scan_object_for_specific_ref (char *start, MonoObject *key)
1260 char *forwarded;
1262 if ((forwarded = SGEN_OBJECT_IS_FORWARDED (start)))
1263 start = forwarded;
1265 #include "sgen-scan-object.h"
1268 void
1269 mono_sgen_scan_area_with_callback (char *start, char *end, IterateObjectCallbackFunc callback, void *data, gboolean allow_flags)
1271 while (start < end) {
1272 size_t size;
1273 char *obj;
1275 if (!*(void**)start) {
1276 start += sizeof (void*); /* should be ALLOC_ALIGN, really */
1277 continue;
1280 if (allow_flags) {
1281 if (!(obj = SGEN_OBJECT_IS_FORWARDED (start)))
1282 obj = start;
1283 } else {
1284 obj = start;
1287 size = ALIGN_UP (safe_object_get_size ((MonoObject*)obj));
1289 callback (obj, size, data);
1291 start += size;
1295 static void
1296 scan_object_for_specific_ref_callback (char *obj, size_t size, MonoObject *key)
1298 scan_object_for_specific_ref (obj, key);
1301 static void
1302 check_root_obj_specific_ref (RootRecord *root, MonoObject *key, MonoObject *obj)
1304 if (key != obj)
1305 return;
1306 g_print ("found ref to %p in root record %p\n", key, root);
1309 static MonoObject *check_key = NULL;
1310 static RootRecord *check_root = NULL;
1312 static void
1313 check_root_obj_specific_ref_from_marker (void **obj)
1315 check_root_obj_specific_ref (check_root, check_key, *obj);
1318 static void
1319 scan_roots_for_specific_ref (MonoObject *key, int root_type)
1321 int i;
1322 RootRecord *root;
1323 check_key = key;
1324 for (i = 0; i < roots_hash_size [root_type]; ++i) {
1325 for (root = roots_hash [root_type][i]; root; root = root->next) {
1326 void **start_root = (void**)root->start_root;
1327 mword desc = root->root_desc;
1329 check_root = root;
1331 switch (desc & ROOT_DESC_TYPE_MASK) {
1332 case ROOT_DESC_BITMAP:
1333 desc >>= ROOT_DESC_TYPE_SHIFT;
1334 while (desc) {
1335 if (desc & 1)
1336 check_root_obj_specific_ref (root, key, *start_root);
1337 desc >>= 1;
1338 start_root++;
1340 return;
1341 case ROOT_DESC_COMPLEX: {
1342 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
1343 int bwords = (*bitmap_data) - 1;
1344 void **start_run = start_root;
1345 bitmap_data++;
1346 while (bwords-- > 0) {
1347 gsize bmap = *bitmap_data++;
1348 void **objptr = start_run;
1349 while (bmap) {
1350 if (bmap & 1)
1351 check_root_obj_specific_ref (root, key, *objptr);
1352 bmap >>= 1;
1353 ++objptr;
1355 start_run += GC_BITS_PER_WORD;
1357 break;
1359 case ROOT_DESC_USER: {
1360 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
1361 marker (start_root, check_root_obj_specific_ref_from_marker);
1362 break;
1364 case ROOT_DESC_RUN_LEN:
1365 g_assert_not_reached ();
1366 default:
1367 g_assert_not_reached ();
1371 check_key = NULL;
1372 check_root = NULL;
1375 void
1376 mono_gc_scan_for_specific_ref (MonoObject *key)
1378 RootRecord *root;
1379 int i;
1381 mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1382 (IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key, TRUE);
1384 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
1386 mono_sgen_los_iterate_objects ((IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
1388 scan_roots_for_specific_ref (key, ROOT_TYPE_NORMAL);
1389 scan_roots_for_specific_ref (key, ROOT_TYPE_WBARRIER);
1391 for (i = 0; i < roots_hash_size [ROOT_TYPE_PINNED]; ++i) {
1392 for (root = roots_hash [ROOT_TYPE_PINNED][i]; root; root = root->next) {
1393 void **ptr = (void**)root->start_root;
1395 while (ptr < (void**)root->end_root) {
1396 check_root_obj_specific_ref (root, *ptr, key);
1397 ++ptr;
1403 static void
1404 clear_current_nursery_fragment (char *next)
1406 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
1407 g_assert (next <= nursery_frag_real_end);
1408 DEBUG (4, fprintf (gc_debug_file, "Clear nursery frag %p-%p\n", next, nursery_frag_real_end));
1409 memset (next, 0, nursery_frag_real_end - next);
1413 /* Clear all remaining nursery fragments */
1414 static void
1415 clear_nursery_fragments (char *next)
1417 Fragment *frag;
1418 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
1419 clear_current_nursery_fragment (next);
1420 for (frag = nursery_fragments; frag; frag = frag->next) {
1421 DEBUG (4, fprintf (gc_debug_file, "Clear nursery frag %p-%p\n", frag->fragment_start, frag->fragment_end));
1422 memset (frag->fragment_start, 0, frag->fragment_end - frag->fragment_start);
1427 static gboolean
1428 need_remove_object_for_domain (char *start, MonoDomain *domain)
1430 if (mono_object_domain (start) == domain) {
1431 DEBUG (4, fprintf (gc_debug_file, "Need to cleanup object %p\n", start));
1432 binary_protocol_cleanup (start, (gpointer)LOAD_VTABLE (start), safe_object_get_size ((MonoObject*)start));
1433 return TRUE;
1435 return FALSE;
1438 static void
1439 process_object_for_domain_clearing (char *start, MonoDomain *domain)
1441 GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
1442 if (vt->klass == mono_defaults.internal_thread_class)
1443 g_assert (mono_object_domain (start) == mono_get_root_domain ());
1444 /* The object could be a proxy for an object in the domain
1445 we're deleting. */
1446 if (mono_class_has_parent_fast (vt->klass, mono_defaults.real_proxy_class)) {
1447 MonoObject *server = ((MonoRealProxy*)start)->unwrapped_server;
1449 /* The server could already have been zeroed out, so
1450 we need to check for that, too. */
1451 if (server && (!LOAD_VTABLE (server) || mono_object_domain (server) == domain)) {
1452 DEBUG (4, fprintf (gc_debug_file, "Cleaning up remote pointer in %p to object %p\n",
1453 start, server));
1454 ((MonoRealProxy*)start)->unwrapped_server = NULL;
1459 static MonoDomain *check_domain = NULL;
1461 static void
1462 check_obj_not_in_domain (void **o)
1464 g_assert (((MonoObject*)(*o))->vtable->domain != check_domain);
1467 static void
1468 scan_for_registered_roots_in_domain (MonoDomain *domain, int root_type)
1470 int i;
1471 RootRecord *root;
1472 check_domain = domain;
1473 for (i = 0; i < roots_hash_size [root_type]; ++i) {
1474 for (root = roots_hash [root_type][i]; root; root = root->next) {
1475 void **start_root = (void**)root->start_root;
1476 mword desc = root->root_desc;
1478 /* The MonoDomain struct is allowed to hold
1479 references to objects in its own domain. */
1480 if (start_root == (void**)domain)
1481 continue;
1483 switch (desc & ROOT_DESC_TYPE_MASK) {
1484 case ROOT_DESC_BITMAP:
1485 desc >>= ROOT_DESC_TYPE_SHIFT;
1486 while (desc) {
1487 if ((desc & 1) && *start_root)
1488 check_obj_not_in_domain (*start_root);
1489 desc >>= 1;
1490 start_root++;
1492 break;
1493 case ROOT_DESC_COMPLEX: {
1494 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
1495 int bwords = (*bitmap_data) - 1;
1496 void **start_run = start_root;
1497 bitmap_data++;
1498 while (bwords-- > 0) {
1499 gsize bmap = *bitmap_data++;
1500 void **objptr = start_run;
1501 while (bmap) {
1502 if ((bmap & 1) && *objptr)
1503 check_obj_not_in_domain (*objptr);
1504 bmap >>= 1;
1505 ++objptr;
1507 start_run += GC_BITS_PER_WORD;
1509 break;
1511 case ROOT_DESC_USER: {
1512 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
1513 marker (start_root, check_obj_not_in_domain);
1514 break;
1516 case ROOT_DESC_RUN_LEN:
1517 g_assert_not_reached ();
1518 default:
1519 g_assert_not_reached ();
1523 check_domain = NULL;
1526 static void
1527 check_for_xdomain_refs (void)
1529 LOSObject *bigobj;
1531 mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1532 (IterateObjectCallbackFunc)scan_object_for_xdomain_refs, NULL, FALSE);
1534 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)scan_object_for_xdomain_refs, NULL);
1536 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1537 scan_object_for_xdomain_refs (bigobj->data, bigobj->size, NULL);
1540 static gboolean
1541 clear_domain_process_object (char *obj, MonoDomain *domain)
1543 gboolean remove;
1545 process_object_for_domain_clearing (obj, domain);
1546 remove = need_remove_object_for_domain (obj, domain);
1548 if (remove && ((MonoObject*)obj)->synchronisation) {
1549 void **dislink = mono_monitor_get_object_monitor_weak_link ((MonoObject*)obj);
1550 if (dislink)
1551 mono_gc_register_disappearing_link (NULL, dislink, FALSE);
1554 return remove;
1557 static void
1558 clear_domain_process_minor_object_callback (char *obj, size_t size, MonoDomain *domain)
1560 if (clear_domain_process_object (obj, domain))
1561 memset (obj, 0, size);
1564 static void
1565 clear_domain_process_major_object_callback (char *obj, size_t size, MonoDomain *domain)
1567 clear_domain_process_object (obj, domain);
1570 static void
1571 clear_domain_free_major_non_pinned_object_callback (char *obj, size_t size, MonoDomain *domain)
1573 if (need_remove_object_for_domain (obj, domain))
1574 major_collector.free_non_pinned_object (obj, size);
1577 static void
1578 clear_domain_free_major_pinned_object_callback (char *obj, size_t size, MonoDomain *domain)
1580 if (need_remove_object_for_domain (obj, domain))
1581 major_collector.free_pinned_object (obj, size);
1585 * When appdomains are unloaded we can easily remove objects that have finalizers,
1586 * but all the others could still be present in random places on the heap.
1587 * We need a sweep to get rid of them even though it's going to be costly
1588 * with big heaps.
1589 * The reason we need to remove them is because we access the vtable and class
1590 * structures to know the object size and the reference bitmap: once the domain is
1591 * unloaded the point to random memory.
1593 void
1594 mono_gc_clear_domain (MonoDomain * domain)
1596 LOSObject *bigobj, *prev;
1597 int i;
1599 LOCK_GC;
1601 clear_nursery_fragments (nursery_next);
1603 if (xdomain_checks && domain != mono_get_root_domain ()) {
1604 scan_for_registered_roots_in_domain (domain, ROOT_TYPE_NORMAL);
1605 scan_for_registered_roots_in_domain (domain, ROOT_TYPE_WBARRIER);
1606 check_for_xdomain_refs ();
1609 /*Ephemerons and dislinks must be processed before LOS since they might end up pointing
1610 to memory returned to the OS.*/
1611 null_ephemerons_for_domain (domain);
1613 for (i = GENERATION_NURSERY; i < GENERATION_MAX; ++i)
1614 null_links_for_domain (domain, i);
1616 for (i = GENERATION_NURSERY; i < GENERATION_MAX; ++i)
1617 remove_finalizers_for_domain (domain, i);
1619 mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
1620 (IterateObjectCallbackFunc)clear_domain_process_minor_object_callback, domain, FALSE);
1622 /* We need two passes over major and large objects because
1623 freeing such objects might give their memory back to the OS
1624 (in the case of large objects) or obliterate its vtable
1625 (pinned objects with major-copying or pinned and non-pinned
1626 objects with major-mark&sweep), but we might need to
1627 dereference a pointer from an object to another object if
1628 the first object is a proxy. */
1629 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)clear_domain_process_major_object_callback, domain);
1630 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1631 clear_domain_process_object (bigobj->data, domain);
1633 prev = NULL;
1634 for (bigobj = los_object_list; bigobj;) {
1635 if (need_remove_object_for_domain (bigobj->data, domain)) {
1636 LOSObject *to_free = bigobj;
1637 if (prev)
1638 prev->next = bigobj->next;
1639 else
1640 los_object_list = bigobj->next;
1641 bigobj = bigobj->next;
1642 DEBUG (4, fprintf (gc_debug_file, "Freeing large object %p\n",
1643 bigobj->data));
1644 mono_sgen_los_free_object (to_free);
1645 continue;
1647 prev = bigobj;
1648 bigobj = bigobj->next;
1650 major_collector.iterate_objects (TRUE, FALSE, (IterateObjectCallbackFunc)clear_domain_free_major_non_pinned_object_callback, domain);
1651 major_collector.iterate_objects (FALSE, TRUE, (IterateObjectCallbackFunc)clear_domain_free_major_pinned_object_callback, domain);
1653 UNLOCK_GC;
1656 static void
1657 global_remset_cache_clear (void)
1659 memset (global_remset_cache, 0, sizeof (global_remset_cache));
1663 * Tries to check if a given remset location was already added to the global remset.
1664 * It can
1666 * A 2 entry, LRU cache of recently saw location remsets.
1668 * It's hand-coded instead of done using loops to reduce the number of memory references on cache hit.
1670 * Returns TRUE is the element was added..
1672 static gboolean
1673 global_remset_location_was_not_added (gpointer ptr)
1676 gpointer first = global_remset_cache [0], second;
1677 if (first == ptr) {
1678 HEAVY_STAT (++stat_global_remsets_discarded);
1679 return FALSE;
1682 second = global_remset_cache [1];
1684 if (second == ptr) {
1685 /*Move the second to the front*/
1686 global_remset_cache [0] = second;
1687 global_remset_cache [1] = first;
1689 HEAVY_STAT (++stat_global_remsets_discarded);
1690 return FALSE;
1693 global_remset_cache [0] = second;
1694 global_remset_cache [1] = ptr;
1695 return TRUE;
1699 * mono_sgen_add_to_global_remset:
1701 * The global remset contains locations which point into newspace after
1702 * a minor collection. This can happen if the objects they point to are pinned.
1704 * LOCKING: If called from a parallel collector, the global remset
1705 * lock must be held. For serial collectors that is not necessary.
1707 void
1708 mono_sgen_add_to_global_remset (gpointer ptr)
1710 RememberedSet *rs;
1711 gboolean lock;
1713 if (use_cardtable) {
1714 sgen_card_table_mark_address ((mword)ptr);
1715 return;
1718 g_assert (!ptr_in_nursery (ptr) && ptr_in_nursery (*(gpointer*)ptr));
1720 lock = (current_collection_generation == GENERATION_OLD && major_collector.is_parallel);
1721 if (lock)
1722 LOCK_GLOBAL_REMSET;
1724 if (!global_remset_location_was_not_added (ptr))
1725 goto done;
1727 DEBUG (8, fprintf (gc_debug_file, "Adding global remset for %p\n", ptr));
1728 binary_protocol_global_remset (ptr, *(gpointer*)ptr, (gpointer)LOAD_VTABLE (*(gpointer*)ptr));
1730 HEAVY_STAT (++stat_global_remsets_added);
1733 * FIXME: If an object remains pinned, we need to add it at every minor collection.
1734 * To avoid uncontrolled growth of the global remset, only add each pointer once.
1736 if (global_remset->store_next + 3 < global_remset->end_set) {
1737 *(global_remset->store_next++) = (mword)ptr;
1738 goto done;
1740 rs = alloc_remset (global_remset->end_set - global_remset->data, NULL);
1741 rs->next = global_remset;
1742 global_remset = rs;
1743 *(global_remset->store_next++) = (mword)ptr;
1746 int global_rs_size = 0;
1748 for (rs = global_remset; rs; rs = rs->next) {
1749 global_rs_size += rs->store_next - rs->data;
1751 DEBUG (4, fprintf (gc_debug_file, "Global remset now has size %d\n", global_rs_size));
1754 done:
1755 if (lock)
1756 UNLOCK_GLOBAL_REMSET;
1760 * drain_gray_stack:
1762 * Scan objects in the gray stack until the stack is empty. This should be called
1763 * frequently after each object is copied, to achieve better locality and cache
1764 * usage.
1766 static void
1767 drain_gray_stack (GrayQueue *queue)
1769 char *obj;
1771 if (current_collection_generation == GENERATION_NURSERY) {
1772 for (;;) {
1773 GRAY_OBJECT_DEQUEUE (queue, obj);
1774 if (!obj)
1775 break;
1776 DEBUG (9, fprintf (gc_debug_file, "Precise gray object scan %p (%s)\n", obj, safe_name (obj)));
1777 major_collector.minor_scan_object (obj, queue);
1779 } else {
1780 if (major_collector.is_parallel && queue == &workers_distribute_gray_queue)
1781 return;
1783 for (;;) {
1784 GRAY_OBJECT_DEQUEUE (queue, obj);
1785 if (!obj)
1786 break;
1787 DEBUG (9, fprintf (gc_debug_file, "Precise gray object scan %p (%s)\n", obj, safe_name (obj)));
1788 major_collector.major_scan_object (obj, queue);
1794 * Addresses from start to end are already sorted. This function finds
1795 * the object header for each address and pins the object. The
1796 * addresses must be inside the passed section. The (start of the)
1797 * address array is overwritten with the addresses of the actually
1798 * pinned objects. Return the number of pinned objects.
1800 static int
1801 pin_objects_from_addresses (GCMemSection *section, void **start, void **end, void *start_nursery, void *end_nursery, GrayQueue *queue)
1803 void *last = NULL;
1804 int count = 0;
1805 void *search_start;
1806 void *last_obj = NULL;
1807 size_t last_obj_size = 0;
1808 void *addr;
1809 int idx;
1810 void **definitely_pinned = start;
1811 Fragment *frag;
1814 * The code below starts the search from an entry in scan_starts, which might point into a nursery
1815 * fragment containing random data. Clearing the nursery fragments takes a lot of time, and searching
1816 * though them too, so lay arrays at each location inside a fragment where a search can start:
1817 * - scan_locations[i]
1818 * - start_nursery
1819 * - the start of each fragment (the last_obj + last_obj case)
1820 * The third encompasses the first two, since scan_locations [i] can't point inside a nursery fragment.
1822 for (frag = nursery_fragments; frag; frag = frag->next) {
1823 MonoArray *o;
1825 g_assert (frag->fragment_end - frag->fragment_start >= sizeof (MonoArray));
1826 o = (MonoArray*)frag->fragment_start;
1827 memset (o, 0, sizeof (MonoArray));
1828 g_assert (array_fill_vtable);
1829 o->obj.vtable = array_fill_vtable;
1830 /* Mark this as not a real object */
1831 o->obj.synchronisation = GINT_TO_POINTER (-1);
1832 o->max_length = (frag->fragment_end - frag->fragment_start) - sizeof (MonoArray);
1833 g_assert (frag->fragment_start + safe_object_get_size ((MonoObject*)o) == frag->fragment_end);
1836 while (start < end) {
1837 addr = *start;
1838 /* the range check should be reduntant */
1839 if (addr != last && addr >= start_nursery && addr < end_nursery) {
1840 DEBUG (5, fprintf (gc_debug_file, "Considering pinning addr %p\n", addr));
1841 /* multiple pointers to the same object */
1842 if (addr >= last_obj && (char*)addr < (char*)last_obj + last_obj_size) {
1843 start++;
1844 continue;
1846 idx = ((char*)addr - (char*)section->data) / SCAN_START_SIZE;
1847 g_assert (idx < section->num_scan_start);
1848 search_start = (void*)section->scan_starts [idx];
1849 if (!search_start || search_start > addr) {
1850 while (idx) {
1851 --idx;
1852 search_start = section->scan_starts [idx];
1853 if (search_start && search_start <= addr)
1854 break;
1856 if (!search_start || search_start > addr)
1857 search_start = start_nursery;
1859 if (search_start < last_obj)
1860 search_start = (char*)last_obj + last_obj_size;
1861 /* now addr should be in an object a short distance from search_start
1862 * Note that search_start must point to zeroed mem or point to an object.
1865 do {
1866 if (!*(void**)search_start) {
1867 /* Consistency check */
1869 for (frag = nursery_fragments; frag; frag = frag->next) {
1870 if (search_start >= frag->fragment_start && search_start < frag->fragment_end)
1871 g_assert_not_reached ();
1875 search_start = (void*)ALIGN_UP ((mword)search_start + sizeof (gpointer));
1876 continue;
1878 last_obj = search_start;
1879 last_obj_size = ALIGN_UP (safe_object_get_size ((MonoObject*)search_start));
1881 if (((MonoObject*)last_obj)->synchronisation == GINT_TO_POINTER (-1)) {
1882 /* Marks the beginning of a nursery fragment, skip */
1883 } else {
1884 DEBUG (8, fprintf (gc_debug_file, "Pinned try match %p (%s), size %zd\n", last_obj, safe_name (last_obj), last_obj_size));
1885 if (addr >= search_start && (char*)addr < (char*)last_obj + last_obj_size) {
1886 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));
1887 binary_protocol_pin (search_start, (gpointer)LOAD_VTABLE (search_start), safe_object_get_size (search_start));
1888 pin_object (search_start);
1889 GRAY_OBJECT_ENQUEUE (queue, search_start);
1890 if (heap_dump_file)
1891 mono_sgen_pin_stats_register_object (search_start, last_obj_size);
1892 definitely_pinned [count] = search_start;
1893 count++;
1894 break;
1897 /* skip to the next object */
1898 search_start = (void*)((char*)search_start + last_obj_size);
1899 } while (search_start <= addr);
1900 /* we either pinned the correct object or we ignored the addr because
1901 * it points to unused zeroed memory.
1903 last = addr;
1905 start++;
1907 //printf ("effective pinned: %d (at the end: %d)\n", count, (char*)end_nursery - (char*)last);
1908 if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS) {
1909 GCRootReport report;
1910 report.count = 0;
1911 for (idx = 0; idx < count; ++idx)
1912 add_profile_gc_root (&report, definitely_pinned [idx], MONO_PROFILE_GC_ROOT_PINNING | MONO_PROFILE_GC_ROOT_MISC, 0);
1913 notify_gc_roots (&report);
1915 stat_pinned_objects += count;
1916 return count;
1919 void
1920 mono_sgen_pin_objects_in_section (GCMemSection *section, GrayQueue *queue)
1922 int num_entries = section->pin_queue_num_entries;
1923 if (num_entries) {
1924 void **start = section->pin_queue_start;
1925 int reduced_to;
1926 reduced_to = pin_objects_from_addresses (section, start, start + num_entries,
1927 section->data, section->next_data, queue);
1928 section->pin_queue_num_entries = reduced_to;
1929 if (!reduced_to)
1930 section->pin_queue_start = NULL;
1935 void
1936 mono_sgen_pin_object (void *object, GrayQueue *queue)
1938 if (major_collector.is_parallel) {
1939 LOCK_PIN_QUEUE;
1940 /*object arrives pinned*/
1941 pin_stage_ptr (object);
1942 ++objects_pinned ;
1943 UNLOCK_PIN_QUEUE;
1944 } else {
1945 SGEN_PIN_OBJECT (object);
1946 pin_stage_ptr (object);
1947 ++objects_pinned;
1949 GRAY_OBJECT_ENQUEUE (queue, object);
1952 /* Sort the addresses in array in increasing order.
1953 * Done using a by-the book heap sort. Which has decent and stable performance, is pretty cache efficient.
1955 static void
1956 sort_addresses (void **array, int size)
1958 int i;
1959 void *tmp;
1961 for (i = 1; i < size; ++i) {
1962 int child = i;
1963 while (child > 0) {
1964 int parent = (child - 1) / 2;
1966 if (array [parent] >= array [child])
1967 break;
1969 tmp = array [parent];
1970 array [parent] = array [child];
1971 array [child] = tmp;
1973 child = parent;
1977 for (i = size - 1; i > 0; --i) {
1978 int end, root;
1979 tmp = array [i];
1980 array [i] = array [0];
1981 array [0] = tmp;
1983 end = i - 1;
1984 root = 0;
1986 while (root * 2 + 1 <= end) {
1987 int child = root * 2 + 1;
1989 if (child < end && array [child] < array [child + 1])
1990 ++child;
1991 if (array [root] >= array [child])
1992 break;
1994 tmp = array [root];
1995 array [root] = array [child];
1996 array [child] = tmp;
1998 root = child;
2003 static G_GNUC_UNUSED void
2004 print_nursery_gaps (void* start_nursery, void *end_nursery)
2006 int i;
2007 gpointer first = start_nursery;
2008 gpointer next;
2009 for (i = 0; i < next_pin_slot; ++i) {
2010 next = pin_queue [i];
2011 fprintf (gc_debug_file, "Nursery range: %p-%p, size: %td\n", first, next, (char*)next-(char*)first);
2012 first = next;
2014 next = end_nursery;
2015 fprintf (gc_debug_file, "Nursery range: %p-%p, size: %td\n", first, next, (char*)next-(char*)first);
2018 /* reduce the info in the pin queue, removing duplicate pointers and sorting them */
2019 static void
2020 optimize_pin_queue (int start_slot)
2022 void **start, **cur, **end;
2023 /* sort and uniq pin_queue: we just sort and we let the rest discard multiple values */
2024 /* it may be better to keep ranges of pinned memory instead of individually pinning objects */
2025 DEBUG (5, fprintf (gc_debug_file, "Sorting pin queue, size: %d\n", next_pin_slot));
2026 if ((next_pin_slot - start_slot) > 1)
2027 sort_addresses (pin_queue + start_slot, next_pin_slot - start_slot);
2028 start = cur = pin_queue + start_slot;
2029 end = pin_queue + next_pin_slot;
2030 while (cur < end) {
2031 *start = *cur++;
2032 while (*start == *cur && cur < end)
2033 cur++;
2034 start++;
2036 next_pin_slot = start - pin_queue;
2037 DEBUG (5, fprintf (gc_debug_file, "Pin queue reduced to size: %d\n", next_pin_slot));
2038 //DEBUG (6, print_nursery_gaps (start_nursery, end_nursery));
2043 * Scan the memory between start and end and queue values which could be pointers
2044 * to the area between start_nursery and end_nursery for later consideration.
2045 * Typically used for thread stacks.
2047 static void
2048 conservatively_pin_objects_from (void **start, void **end, void *start_nursery, void *end_nursery, int pin_type)
2050 int count = 0;
2051 while (start < end) {
2052 if (*start >= start_nursery && *start < end_nursery) {
2054 * *start can point to the middle of an object
2055 * note: should we handle pointing at the end of an object?
2056 * pinning in C# code disallows pointing at the end of an object
2057 * but there is some small chance that an optimizing C compiler
2058 * may keep the only reference to an object by pointing
2059 * at the end of it. We ignore this small chance for now.
2060 * Pointers to the end of an object are indistinguishable
2061 * from pointers to the start of the next object in memory
2062 * so if we allow that we'd need to pin two objects...
2063 * We queue the pointer in an array, the
2064 * array will then be sorted and uniqued. This way
2065 * we can coalesce several pinning pointers and it should
2066 * be faster since we'd do a memory scan with increasing
2067 * addresses. Note: we can align the address to the allocation
2068 * alignment, so the unique process is more effective.
2070 mword addr = (mword)*start;
2071 addr &= ~(ALLOC_ALIGN - 1);
2072 if (addr >= (mword)start_nursery && addr < (mword)end_nursery)
2073 pin_stage_ptr ((void*)addr);
2074 if (heap_dump_file)
2075 pin_stats_register_address ((char*)addr, pin_type);
2076 DEBUG (6, if (count) fprintf (gc_debug_file, "Pinning address %p from %p\n", (void*)addr, start));
2077 count++;
2079 start++;
2081 DEBUG (7, if (count) fprintf (gc_debug_file, "found %d potential pinned heap pointers\n", count));
2085 * Debugging function: find in the conservative roots where @obj is being pinned.
2087 static G_GNUC_UNUSED void
2088 find_pinning_reference (char *obj, size_t size)
2090 RootRecord *root;
2091 int i;
2092 char *endobj = obj + size;
2093 for (i = 0; i < roots_hash_size [0]; ++i) {
2094 for (root = roots_hash [0][i]; root; root = root->next) {
2095 /* if desc is non-null it has precise info */
2096 if (!root->root_desc) {
2097 char ** start = (char**)root->start_root;
2098 while (start < (char**)root->end_root) {
2099 if (*start >= obj && *start < endobj) {
2100 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));
2102 start++;
2107 find_pinning_ref_from_thread (obj, size);
2111 * The first thing we do in a collection is to identify pinned objects.
2112 * This function considers all the areas of memory that need to be
2113 * conservatively scanned.
2115 static void
2116 pin_from_roots (void *start_nursery, void *end_nursery)
2118 RootRecord *root;
2119 int i;
2120 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]));
2121 /* objects pinned from the API are inside these roots */
2122 for (i = 0; i < roots_hash_size [ROOT_TYPE_PINNED]; ++i) {
2123 for (root = roots_hash [ROOT_TYPE_PINNED][i]; root; root = root->next) {
2124 DEBUG (6, fprintf (gc_debug_file, "Pinned roots %p-%p\n", root->start_root, root->end_root));
2125 conservatively_pin_objects_from ((void**)root->start_root, (void**)root->end_root, start_nursery, end_nursery, PIN_TYPE_OTHER);
2128 /* now deal with the thread stacks
2129 * in the future we should be able to conservatively scan only:
2130 * *) the cpu registers
2131 * *) the unmanaged stack frames
2132 * *) the _last_ managed stack frame
2133 * *) pointers slots in managed frames
2135 scan_thread_data (start_nursery, end_nursery, FALSE);
2137 evacuate_pin_staging_area ();
2140 static CopyOrMarkObjectFunc user_copy_or_mark_func;
2141 static GrayQueue *user_copy_or_mark_queue;
2143 static void
2144 single_arg_user_copy_or_mark (void **obj)
2146 user_copy_or_mark_func (obj, user_copy_or_mark_queue);
2150 * The memory area from start_root to end_root contains pointers to objects.
2151 * Their position is precisely described by @desc (this means that the pointer
2152 * can be either NULL or the pointer to the start of an object).
2153 * This functions copies them to to_space updates them.
2155 * This function is not thread-safe!
2157 static void
2158 precisely_scan_objects_from (CopyOrMarkObjectFunc copy_func, void** start_root, void** end_root, char* n_start, char *n_end, mword desc, GrayQueue *queue)
2160 switch (desc & ROOT_DESC_TYPE_MASK) {
2161 case ROOT_DESC_BITMAP:
2162 desc >>= ROOT_DESC_TYPE_SHIFT;
2163 while (desc) {
2164 if ((desc & 1) && *start_root) {
2165 copy_func (start_root, queue);
2166 DEBUG (9, fprintf (gc_debug_file, "Overwrote root at %p with %p\n", start_root, *start_root));
2167 drain_gray_stack (queue);
2169 desc >>= 1;
2170 start_root++;
2172 return;
2173 case ROOT_DESC_COMPLEX: {
2174 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
2175 int bwords = (*bitmap_data) - 1;
2176 void **start_run = start_root;
2177 bitmap_data++;
2178 while (bwords-- > 0) {
2179 gsize bmap = *bitmap_data++;
2180 void **objptr = start_run;
2181 while (bmap) {
2182 if ((bmap & 1) && *objptr) {
2183 copy_func (objptr, queue);
2184 DEBUG (9, fprintf (gc_debug_file, "Overwrote root at %p with %p\n", objptr, *objptr));
2185 drain_gray_stack (queue);
2187 bmap >>= 1;
2188 ++objptr;
2190 start_run += GC_BITS_PER_WORD;
2192 break;
2194 case ROOT_DESC_USER: {
2195 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
2196 user_copy_or_mark_func = copy_func;
2197 user_copy_or_mark_queue = queue;
2198 marker (start_root, single_arg_user_copy_or_mark);
2199 user_copy_or_mark_func = NULL;
2200 user_copy_or_mark_queue = NULL;
2201 break;
2203 case ROOT_DESC_RUN_LEN:
2204 g_assert_not_reached ();
2205 default:
2206 g_assert_not_reached ();
2210 static void
2211 reset_heap_boundaries (void)
2213 lowest_heap_address = ~(mword)0;
2214 highest_heap_address = 0;
2217 void
2218 mono_sgen_update_heap_boundaries (mword low, mword high)
2220 mword old;
2222 do {
2223 old = lowest_heap_address;
2224 if (low >= old)
2225 break;
2226 } while (SGEN_CAS_PTR ((gpointer*)&lowest_heap_address, (gpointer)low, (gpointer)old) != (gpointer)old);
2228 do {
2229 old = highest_heap_address;
2230 if (high <= old)
2231 break;
2232 } while (SGEN_CAS_PTR ((gpointer*)&highest_heap_address, (gpointer)high, (gpointer)old) != (gpointer)old);
2235 static Fragment*
2236 alloc_fragment (void)
2238 Fragment *frag = fragment_freelist;
2239 if (frag) {
2240 fragment_freelist = frag->next;
2241 frag->next = NULL;
2242 return frag;
2244 frag = mono_sgen_alloc_internal (INTERNAL_MEM_FRAGMENT);
2245 frag->next = NULL;
2246 return frag;
2249 /* size must be a power of 2 */
2250 void*
2251 mono_sgen_alloc_os_memory_aligned (mword size, mword alignment, gboolean activate)
2253 /* Allocate twice the memory to be able to put the block on an aligned address */
2254 char *mem = mono_sgen_alloc_os_memory (size + alignment, activate);
2255 char *aligned;
2257 g_assert (mem);
2259 aligned = (char*)((mword)(mem + (alignment - 1)) & ~(alignment - 1));
2260 g_assert (aligned >= mem && aligned + size <= mem + size + alignment && !((mword)aligned & (alignment - 1)));
2262 if (aligned > mem)
2263 mono_sgen_free_os_memory (mem, aligned - mem);
2264 if (aligned + size < mem + size + alignment)
2265 mono_sgen_free_os_memory (aligned + size, (mem + size + alignment) - (aligned + size));
2267 return aligned;
2271 * Allocate and setup the data structures needed to be able to allocate objects
2272 * in the nursery. The nursery is stored in nursery_section.
2274 static void
2275 alloc_nursery (void)
2277 GCMemSection *section;
2278 char *data;
2279 int scan_starts;
2280 Fragment *frag;
2281 int alloc_size;
2283 if (nursery_section)
2284 return;
2285 DEBUG (2, fprintf (gc_debug_file, "Allocating nursery size: %lu\n", (unsigned long)nursery_size));
2286 /* later we will alloc a larger area for the nursery but only activate
2287 * what we need. The rest will be used as expansion if we have too many pinned
2288 * objects in the existing nursery.
2290 /* FIXME: handle OOM */
2291 section = mono_sgen_alloc_internal (INTERNAL_MEM_SECTION);
2293 g_assert (nursery_size == DEFAULT_NURSERY_SIZE);
2294 alloc_size = nursery_size;
2295 #ifdef SGEN_ALIGN_NURSERY
2296 data = major_collector.alloc_heap (alloc_size, alloc_size, DEFAULT_NURSERY_BITS);
2297 #else
2298 data = major_collector.alloc_heap (alloc_size, 0, DEFAULT_NURSERY_BITS);
2299 #endif
2300 nursery_start = data;
2301 nursery_real_end = nursery_start + nursery_size;
2302 mono_sgen_update_heap_boundaries ((mword)nursery_start, (mword)nursery_real_end);
2303 nursery_next = nursery_start;
2304 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));
2305 section->data = section->next_data = data;
2306 section->size = alloc_size;
2307 section->end_data = nursery_real_end;
2308 scan_starts = (alloc_size + SCAN_START_SIZE - 1) / SCAN_START_SIZE;
2309 section->scan_starts = mono_sgen_alloc_internal_dynamic (sizeof (char*) * scan_starts, INTERNAL_MEM_SCAN_STARTS);
2310 section->num_scan_start = scan_starts;
2311 section->block.role = MEMORY_ROLE_GEN0;
2312 section->block.next = NULL;
2314 nursery_section = section;
2316 /* Setup the single first large fragment */
2317 frag = alloc_fragment ();
2318 frag->fragment_start = nursery_start;
2319 frag->fragment_limit = nursery_start;
2320 frag->fragment_end = nursery_real_end;
2321 nursery_frag_real_end = nursery_real_end;
2322 /* FIXME: frag here is lost */
2325 void*
2326 mono_gc_get_nursery (int *shift_bits, size_t *size)
2328 *size = nursery_size;
2329 #ifdef SGEN_ALIGN_NURSERY
2330 *shift_bits = DEFAULT_NURSERY_BITS;
2331 #else
2332 *shift_bits = -1;
2333 #endif
2334 return nursery_start;
2337 static void
2338 report_finalizer_roots_list (FinalizeEntry *list)
2340 GCRootReport report;
2341 FinalizeEntry *fin;
2343 report.count = 0;
2344 for (fin = list; fin; fin = fin->next) {
2345 MonoObject *object = finalize_entry_get_object (fin);
2346 if (!object)
2347 continue;
2348 add_profile_gc_root (&report, object, MONO_PROFILE_GC_ROOT_FINALIZER, 0);
2350 notify_gc_roots (&report);
2353 static void
2354 report_finalizer_roots (void)
2356 report_finalizer_roots_list (fin_ready_list);
2357 report_finalizer_roots_list (critical_fin_list);
2360 static GCRootReport *root_report;
2362 static void
2363 single_arg_report_root (void **obj)
2365 if (*obj)
2366 add_profile_gc_root (root_report, *obj, MONO_PROFILE_GC_ROOT_OTHER, 0);
2369 static void
2370 precisely_report_roots_from (GCRootReport *report, void** start_root, void** end_root, mword desc)
2372 switch (desc & ROOT_DESC_TYPE_MASK) {
2373 case ROOT_DESC_BITMAP:
2374 desc >>= ROOT_DESC_TYPE_SHIFT;
2375 while (desc) {
2376 if ((desc & 1) && *start_root) {
2377 add_profile_gc_root (report, *start_root, MONO_PROFILE_GC_ROOT_OTHER, 0);
2379 desc >>= 1;
2380 start_root++;
2382 return;
2383 case ROOT_DESC_COMPLEX: {
2384 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
2385 int bwords = (*bitmap_data) - 1;
2386 void **start_run = start_root;
2387 bitmap_data++;
2388 while (bwords-- > 0) {
2389 gsize bmap = *bitmap_data++;
2390 void **objptr = start_run;
2391 while (bmap) {
2392 if ((bmap & 1) && *objptr) {
2393 add_profile_gc_root (report, *objptr, MONO_PROFILE_GC_ROOT_OTHER, 0);
2395 bmap >>= 1;
2396 ++objptr;
2398 start_run += GC_BITS_PER_WORD;
2400 break;
2402 case ROOT_DESC_USER: {
2403 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
2404 root_report = report;
2405 marker (start_root, single_arg_report_root);
2406 break;
2408 case ROOT_DESC_RUN_LEN:
2409 g_assert_not_reached ();
2410 default:
2411 g_assert_not_reached ();
2415 static void
2416 report_registered_roots_by_type (int root_type)
2418 GCRootReport report;
2419 int i;
2420 RootRecord *root;
2421 report.count = 0;
2422 for (i = 0; i < roots_hash_size [root_type]; ++i) {
2423 for (root = roots_hash [root_type][i]; root; root = root->next) {
2424 DEBUG (6, fprintf (gc_debug_file, "Precise root scan %p-%p (desc: %p)\n", root->start_root, root->end_root, (void*)root->root_desc));
2425 precisely_report_roots_from (&report, (void**)root->start_root, (void**)root->end_root, root->root_desc);
2428 notify_gc_roots (&report);
2431 static void
2432 report_registered_roots (void)
2434 report_registered_roots_by_type (ROOT_TYPE_NORMAL);
2435 report_registered_roots_by_type (ROOT_TYPE_WBARRIER);
2438 static void
2439 scan_finalizer_entries (CopyOrMarkObjectFunc copy_func, FinalizeEntry *list, GrayQueue *queue)
2441 FinalizeEntry *fin;
2443 for (fin = list; fin; fin = fin->next) {
2444 void *object = finalize_entry_get_object (fin);
2445 gboolean bridge_bit = finalize_entry_get_bridge_bit (fin);
2446 if (!object)
2447 continue;
2448 DEBUG (5, fprintf (gc_debug_file, "Scan of fin ready object: %p (%s)\n", object, safe_name (object)));
2449 copy_func (&object, queue);
2451 finalize_entry_set_object (fin, object, bridge_bit);
2455 static mword fragment_total = 0;
2457 * We found a fragment of free memory in the nursery: memzero it and if
2458 * it is big enough, add it to the list of fragments that can be used for
2459 * allocation.
2461 static void
2462 add_nursery_frag (size_t frag_size, char* frag_start, char* frag_end)
2464 Fragment *fragment;
2465 DEBUG (4, fprintf (gc_debug_file, "Found empty fragment: %p-%p, size: %zd\n", frag_start, frag_end, frag_size));
2466 binary_protocol_empty (frag_start, frag_size);
2467 /* Not worth dealing with smaller fragments: need to tune */
2468 if (frag_size >= FRAGMENT_MIN_SIZE) {
2469 /* memsetting just the first chunk start is bound to provide better cache locality */
2470 if (nursery_clear_policy == CLEAR_AT_GC)
2471 memset (frag_start, 0, frag_size);
2473 fragment = alloc_fragment ();
2474 fragment->fragment_start = frag_start;
2475 fragment->fragment_limit = frag_start;
2476 fragment->fragment_end = frag_end;
2477 fragment->next = nursery_fragments;
2478 nursery_fragments = fragment;
2479 fragment_total += frag_size;
2480 } else {
2481 /* Clear unused fragments, pinning depends on this */
2482 /*TODO place an int[] here instead of the memset if size justify it*/
2483 memset (frag_start, 0, frag_size);
2487 static const char*
2488 generation_name (int generation)
2490 switch (generation) {
2491 case GENERATION_NURSERY: return "nursery";
2492 case GENERATION_OLD: return "old";
2493 default: g_assert_not_reached ();
2497 static DisappearingLinkHashTable*
2498 get_dislink_hash_table (int generation)
2500 switch (generation) {
2501 case GENERATION_NURSERY: return &minor_disappearing_link_hash;
2502 case GENERATION_OLD: return &major_disappearing_link_hash;
2503 default: g_assert_not_reached ();
2507 static FinalizeEntryHashTable*
2508 get_finalize_entry_hash_table (int generation)
2510 switch (generation) {
2511 case GENERATION_NURSERY: return &minor_finalizable_hash;
2512 case GENERATION_OLD: return &major_finalizable_hash;
2513 default: g_assert_not_reached ();
2517 static MonoObject **finalized_array = NULL;
2518 static int finalized_array_capacity = 0;
2519 static int finalized_array_entries = 0;
2521 static void
2522 bridge_register_finalized_object (MonoObject *object)
2524 if (!finalized_array)
2525 return;
2527 if (finalized_array_entries >= finalized_array_capacity) {
2528 MonoObject **new_array;
2529 g_assert (finalized_array_entries == finalized_array_capacity);
2530 finalized_array_capacity *= 2;
2531 new_array = mono_sgen_alloc_internal_dynamic (sizeof (MonoObject*) * finalized_array_capacity, INTERNAL_MEM_BRIDGE_DATA);
2532 memcpy (new_array, finalized_array, sizeof (MonoObject*) * finalized_array_entries);
2533 mono_sgen_free_internal_dynamic (finalized_array, sizeof (MonoObject*) * finalized_array_entries, INTERNAL_MEM_BRIDGE_DATA);
2534 finalized_array = new_array;
2536 finalized_array [finalized_array_entries++] = object;
2539 static void
2540 stw_bridge_process (void)
2542 mono_sgen_bridge_processing_stw_step ();
2545 static void
2546 bridge_process (void)
2548 mono_sgen_bridge_processing_finish ();
2551 static void
2552 finish_gray_stack (char *start_addr, char *end_addr, int generation, GrayQueue *queue)
2554 TV_DECLARE (atv);
2555 TV_DECLARE (btv);
2556 int fin_ready;
2557 int done_with_ephemerons, ephemeron_rounds = 0;
2558 int num_loops;
2559 CopyOrMarkObjectFunc copy_func = current_collection_generation == GENERATION_NURSERY ? major_collector.copy_object : major_collector.copy_or_mark_object;
2562 * We copied all the reachable objects. Now it's the time to copy
2563 * the objects that were not referenced by the roots, but by the copied objects.
2564 * we built a stack of objects pointed to by gray_start: they are
2565 * additional roots and we may add more items as we go.
2566 * We loop until gray_start == gray_objects which means no more objects have
2567 * been added. Note this is iterative: no recursion is involved.
2568 * We need to walk the LO list as well in search of marked big objects
2569 * (use a flag since this is needed only on major collections). We need to loop
2570 * here as well, so keep a counter of marked LO (increasing it in copy_object).
2571 * To achieve better cache locality and cache usage, we drain the gray stack
2572 * frequently, after each object is copied, and just finish the work here.
2574 drain_gray_stack (queue);
2575 TV_GETTIME (atv);
2576 DEBUG (2, fprintf (gc_debug_file, "%s generation done\n", generation_name (generation)));
2579 Reset bridge data, we might have lingering data from a previous collection if this is a major
2580 collection trigged by minor overflow.
2582 We must reset the gathered bridges since their original block might be evacuated due to major
2583 fragmentation in the meanwhile and the bridge code should not have to deal with that.
2585 mono_sgen_bridge_reset_data ();
2588 * Walk the ephemeron tables marking all values with reachable keys. This must be completely done
2589 * before processing finalizable objects or non-tracking weak hamdle to avoid finalizing/clearing
2590 * objects that are in fact reachable.
2592 done_with_ephemerons = 0;
2593 do {
2594 done_with_ephemerons = mark_ephemerons_in_range (copy_func, start_addr, end_addr, queue);
2595 drain_gray_stack (queue);
2596 ++ephemeron_rounds;
2597 } while (!done_with_ephemerons);
2599 mono_sgen_scan_togglerefs (copy_func, start_addr, end_addr, queue);
2600 if (generation == GENERATION_OLD)
2601 mono_sgen_scan_togglerefs (copy_func, nursery_start, nursery_real_end, queue);
2603 if (mono_sgen_need_bridge_processing ()) {
2604 if (finalized_array == NULL) {
2605 finalized_array_capacity = 32;
2606 finalized_array = mono_sgen_alloc_internal_dynamic (sizeof (MonoObject*) * finalized_array_capacity, INTERNAL_MEM_BRIDGE_DATA);
2608 finalized_array_entries = 0;
2610 collect_bridge_objects (copy_func, start_addr, end_addr, generation, queue);
2611 if (generation == GENERATION_OLD)
2612 collect_bridge_objects (copy_func, nursery_start, nursery_real_end, GENERATION_NURSERY, queue);
2614 if (finalized_array_entries > 0) {
2615 mono_sgen_bridge_processing_register_objects (finalized_array_entries, finalized_array);
2616 finalized_array_entries = 0;
2621 Make sure we drain the gray stack before processing disappearing links and finalizers.
2622 If we don't make sure it is empty we might wrongly see a live object as dead.
2624 drain_gray_stack (queue);
2627 We must clear weak links that don't track resurrection before processing object ready for
2628 finalization so they can be cleared before that.
2630 null_link_in_range (copy_func, start_addr, end_addr, generation, TRUE, queue);
2631 if (generation == GENERATION_OLD)
2632 null_link_in_range (copy_func, start_addr, end_addr, GENERATION_NURSERY, TRUE, queue);
2635 /* walk the finalization queue and move also the objects that need to be
2636 * finalized: use the finalized objects as new roots so the objects they depend
2637 * on are also not reclaimed. As with the roots above, only objects in the nursery
2638 * are marked/copied.
2639 * We need a loop here, since objects ready for finalizers may reference other objects
2640 * that are fin-ready. Speedup with a flag?
2642 num_loops = 0;
2643 do {
2644 fin_ready = num_ready_finalizers;
2645 finalize_in_range (copy_func, start_addr, end_addr, generation, queue);
2646 if (generation == GENERATION_OLD)
2647 finalize_in_range (copy_func, nursery_start, nursery_real_end, GENERATION_NURSERY, queue);
2649 if (fin_ready != num_ready_finalizers)
2650 ++num_loops;
2652 /* drain the new stack that might have been created */
2653 DEBUG (6, fprintf (gc_debug_file, "Precise scan of gray area post fin\n"));
2654 drain_gray_stack (queue);
2655 } while (fin_ready != num_ready_finalizers);
2657 if (mono_sgen_need_bridge_processing ())
2658 g_assert (num_loops <= 1);
2661 * This must be done again after processing finalizable objects since CWL slots are cleared only after the key is finalized.
2663 done_with_ephemerons = 0;
2664 do {
2665 done_with_ephemerons = mark_ephemerons_in_range (copy_func, start_addr, end_addr, queue);
2666 drain_gray_stack (queue);
2667 ++ephemeron_rounds;
2668 } while (!done_with_ephemerons);
2671 * Clear ephemeron pairs with unreachable keys.
2672 * We pass the copy func so we can figure out if an array was promoted or not.
2674 clear_unreachable_ephemerons (copy_func, start_addr, end_addr, queue);
2676 TV_GETTIME (btv);
2677 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));
2680 * handle disappearing links
2681 * Note we do this after checking the finalization queue because if an object
2682 * survives (at least long enough to be finalized) we don't clear the link.
2683 * This also deals with a possible issue with the monitor reclamation: with the Boehm
2684 * GC a finalized object my lose the monitor because it is cleared before the finalizer is
2685 * called.
2687 g_assert (gray_object_queue_is_empty (queue));
2688 for (;;) {
2689 null_link_in_range (copy_func, start_addr, end_addr, generation, FALSE, queue);
2690 if (generation == GENERATION_OLD)
2691 null_link_in_range (copy_func, start_addr, end_addr, GENERATION_NURSERY, FALSE, queue);
2692 if (gray_object_queue_is_empty (queue))
2693 break;
2694 drain_gray_stack (queue);
2697 g_assert (gray_object_queue_is_empty (queue));
2700 void
2701 mono_sgen_check_section_scan_starts (GCMemSection *section)
2703 int i;
2704 for (i = 0; i < section->num_scan_start; ++i) {
2705 if (section->scan_starts [i]) {
2706 guint size = safe_object_get_size ((MonoObject*) section->scan_starts [i]);
2707 g_assert (size >= sizeof (MonoObject) && size <= MAX_SMALL_OBJ_SIZE);
2712 static void
2713 check_scan_starts (void)
2715 if (!do_scan_starts_check)
2716 return;
2717 mono_sgen_check_section_scan_starts (nursery_section);
2718 major_collector.check_scan_starts ();
2721 static int last_num_pinned = 0;
2723 static void
2724 build_nursery_fragments (void **start, int num_entries)
2726 char *frag_start, *frag_end;
2727 size_t frag_size;
2728 int i;
2730 while (nursery_fragments) {
2731 Fragment *next = nursery_fragments->next;
2732 nursery_fragments->next = fragment_freelist;
2733 fragment_freelist = nursery_fragments;
2734 nursery_fragments = next;
2736 frag_start = nursery_start;
2737 fragment_total = 0;
2738 /* clear scan starts */
2739 memset (nursery_section->scan_starts, 0, nursery_section->num_scan_start * sizeof (gpointer));
2740 for (i = 0; i < num_entries; ++i) {
2741 frag_end = start [i];
2742 /* remove the pin bit from pinned objects */
2743 unpin_object (frag_end);
2744 nursery_section->scan_starts [((char*)frag_end - (char*)nursery_section->data)/SCAN_START_SIZE] = frag_end;
2745 frag_size = frag_end - frag_start;
2746 if (frag_size)
2747 add_nursery_frag (frag_size, frag_start, frag_end);
2748 frag_size = ALIGN_UP (safe_object_get_size ((MonoObject*)start [i]));
2749 frag_start = (char*)start [i] + frag_size;
2751 nursery_last_pinned_end = frag_start;
2752 frag_end = nursery_real_end;
2753 frag_size = frag_end - frag_start;
2754 if (frag_size)
2755 add_nursery_frag (frag_size, frag_start, frag_end);
2756 if (!nursery_fragments) {
2757 DEBUG (1, fprintf (gc_debug_file, "Nursery fully pinned (%d)\n", num_entries));
2758 for (i = 0; i < num_entries; ++i) {
2759 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])));
2761 degraded_mode = 1;
2764 nursery_next = nursery_frag_real_end = NULL;
2766 /* Clear TLABs for all threads */
2767 clear_tlabs ();
2770 static void
2771 scan_from_registered_roots (CopyOrMarkObjectFunc copy_func, char *addr_start, char *addr_end, int root_type, GrayQueue *queue)
2773 int i;
2774 RootRecord *root;
2775 for (i = 0; i < roots_hash_size [root_type]; ++i) {
2776 for (root = roots_hash [root_type][i]; root; root = root->next) {
2777 DEBUG (6, fprintf (gc_debug_file, "Precise root scan %p-%p (desc: %p)\n", root->start_root, root->end_root, (void*)root->root_desc));
2778 precisely_scan_objects_from (copy_func, (void**)root->start_root, (void**)root->end_root, addr_start, addr_end, root->root_desc, queue);
2783 void
2784 mono_sgen_dump_occupied (char *start, char *end, char *section_start)
2786 fprintf (heap_dump_file, "<occupied offset=\"%td\" size=\"%td\"/>\n", start - section_start, end - start);
2789 void
2790 mono_sgen_dump_section (GCMemSection *section, const char *type)
2792 char *start = section->data;
2793 char *end = section->data + section->size;
2794 char *occ_start = NULL;
2795 GCVTable *vt;
2796 char *old_start = NULL; /* just for debugging */
2798 fprintf (heap_dump_file, "<section type=\"%s\" size=\"%lu\">\n", type, (unsigned long)section->size);
2800 while (start < end) {
2801 guint size;
2802 MonoClass *class;
2804 if (!*(void**)start) {
2805 if (occ_start) {
2806 mono_sgen_dump_occupied (occ_start, start, section->data);
2807 occ_start = NULL;
2809 start += sizeof (void*); /* should be ALLOC_ALIGN, really */
2810 continue;
2812 g_assert (start < section->next_data);
2814 if (!occ_start)
2815 occ_start = start;
2817 vt = (GCVTable*)LOAD_VTABLE (start);
2818 class = vt->klass;
2820 size = ALIGN_UP (safe_object_get_size ((MonoObject*) start));
2823 fprintf (heap_dump_file, "<object offset=\"%d\" class=\"%s.%s\" size=\"%d\"/>\n",
2824 start - section->data,
2825 vt->klass->name_space, vt->klass->name,
2826 size);
2829 old_start = start;
2830 start += size;
2832 if (occ_start)
2833 mono_sgen_dump_occupied (occ_start, start, section->data);
2835 fprintf (heap_dump_file, "</section>\n");
2838 static void
2839 dump_object (MonoObject *obj, gboolean dump_location)
2841 static char class_name [1024];
2843 MonoClass *class = mono_object_class (obj);
2844 int i, j;
2847 * Python's XML parser is too stupid to parse angle brackets
2848 * in strings, so we just ignore them;
2850 i = j = 0;
2851 while (class->name [i] && j < sizeof (class_name) - 1) {
2852 if (!strchr ("<>\"", class->name [i]))
2853 class_name [j++] = class->name [i];
2854 ++i;
2856 g_assert (j < sizeof (class_name));
2857 class_name [j] = 0;
2859 fprintf (heap_dump_file, "<object class=\"%s.%s\" size=\"%d\"",
2860 class->name_space, class_name,
2861 safe_object_get_size (obj));
2862 if (dump_location) {
2863 const char *location;
2864 if (ptr_in_nursery (obj))
2865 location = "nursery";
2866 else if (safe_object_get_size (obj) <= MAX_SMALL_OBJ_SIZE)
2867 location = "major";
2868 else
2869 location = "LOS";
2870 fprintf (heap_dump_file, " location=\"%s\"", location);
2872 fprintf (heap_dump_file, "/>\n");
2875 static void
2876 dump_heap (const char *type, int num, const char *reason)
2878 ObjectList *list;
2879 LOSObject *bigobj;
2881 fprintf (heap_dump_file, "<collection type=\"%s\" num=\"%d\"", type, num);
2882 if (reason)
2883 fprintf (heap_dump_file, " reason=\"%s\"", reason);
2884 fprintf (heap_dump_file, ">\n");
2885 fprintf (heap_dump_file, "<other-mem-usage type=\"mempools\" size=\"%ld\"/>\n", mono_mempool_get_bytes_allocated ());
2886 mono_sgen_dump_internal_mem_usage (heap_dump_file);
2887 fprintf (heap_dump_file, "<pinned type=\"stack\" bytes=\"%zu\"/>\n", pinned_byte_counts [PIN_TYPE_STACK]);
2888 /* fprintf (heap_dump_file, "<pinned type=\"static-data\" bytes=\"%d\"/>\n", pinned_byte_counts [PIN_TYPE_STATIC_DATA]); */
2889 fprintf (heap_dump_file, "<pinned type=\"other\" bytes=\"%zu\"/>\n", pinned_byte_counts [PIN_TYPE_OTHER]);
2891 fprintf (heap_dump_file, "<pinned-objects>\n");
2892 for (list = pinned_objects; list; list = list->next)
2893 dump_object (list->obj, TRUE);
2894 fprintf (heap_dump_file, "</pinned-objects>\n");
2896 mono_sgen_dump_section (nursery_section, "nursery");
2898 major_collector.dump_heap (heap_dump_file);
2900 fprintf (heap_dump_file, "<los>\n");
2901 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
2902 dump_object ((MonoObject*)bigobj->data, FALSE);
2903 fprintf (heap_dump_file, "</los>\n");
2905 fprintf (heap_dump_file, "</collection>\n");
2908 void
2909 mono_sgen_register_moved_object (void *obj, void *destination)
2911 g_assert (mono_profiler_events & MONO_PROFILE_GC_MOVES);
2913 /* FIXME: handle this for parallel collector */
2914 g_assert (!major_collector.is_parallel);
2916 if (moved_objects_idx == MOVED_OBJECTS_NUM) {
2917 mono_profiler_gc_moves (moved_objects, moved_objects_idx);
2918 moved_objects_idx = 0;
2920 moved_objects [moved_objects_idx++] = obj;
2921 moved_objects [moved_objects_idx++] = destination;
2924 static void
2925 init_stats (void)
2927 static gboolean inited = FALSE;
2929 if (inited)
2930 return;
2932 mono_counters_register ("Minor fragment clear", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_pre_collection_fragment_clear);
2933 mono_counters_register ("Minor pinning", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_pinning);
2934 mono_counters_register ("Minor scan remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_remsets);
2935 mono_counters_register ("Minor scan cardtables", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_card_table);
2936 mono_counters_register ("Minor scan pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_pinned);
2937 mono_counters_register ("Minor scan registered roots", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_registered_roots);
2938 mono_counters_register ("Minor scan thread data", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_thread_data);
2939 mono_counters_register ("Minor finish gray stack", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_finish_gray_stack);
2940 mono_counters_register ("Minor fragment creation", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_fragment_creation);
2942 mono_counters_register ("Major fragment clear", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_pre_collection_fragment_clear);
2943 mono_counters_register ("Major pinning", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_pinning);
2944 mono_counters_register ("Major scan pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_pinned);
2945 mono_counters_register ("Major scan registered roots", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_registered_roots);
2946 mono_counters_register ("Major scan thread data", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_thread_data);
2947 mono_counters_register ("Major scan alloc_pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_alloc_pinned);
2948 mono_counters_register ("Major scan finalized", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_finalized);
2949 mono_counters_register ("Major scan big objects", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_big_objects);
2950 mono_counters_register ("Major finish gray stack", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_finish_gray_stack);
2951 mono_counters_register ("Major free big objects", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_free_bigobjs);
2952 mono_counters_register ("Major LOS sweep", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_los_sweep);
2953 mono_counters_register ("Major sweep", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_sweep);
2954 mono_counters_register ("Major fragment creation", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_fragment_creation);
2956 mono_counters_register ("Number of pinned objects", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_pinned_objects);
2958 #ifdef HEAVY_STATISTICS
2959 mono_counters_register ("WBarrier set field", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_field);
2960 mono_counters_register ("WBarrier set arrayref", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_arrayref);
2961 mono_counters_register ("WBarrier arrayref copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_arrayref_copy);
2962 mono_counters_register ("WBarrier generic store called", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_generic_store);
2963 mono_counters_register ("WBarrier generic store stored", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_generic_store_remset);
2964 mono_counters_register ("WBarrier set root", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_root);
2965 mono_counters_register ("WBarrier value copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_value_copy);
2966 mono_counters_register ("WBarrier object copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_object_copy);
2968 mono_counters_register ("# objects allocated", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_alloced);
2969 mono_counters_register ("bytes allocated", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_bytes_alloced);
2970 mono_counters_register ("# objects allocated degraded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_alloced_degraded);
2971 mono_counters_register ("bytes allocated degraded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_bytes_alloced_degraded);
2972 mono_counters_register ("bytes allocated in LOS", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_bytes_alloced_los);
2974 mono_counters_register ("# copy_object() called (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_copy_object_called_nursery);
2975 mono_counters_register ("# objects copied (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_copied_nursery);
2976 mono_counters_register ("# copy_object() called (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_copy_object_called_major);
2977 mono_counters_register ("# objects copied (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_copied_major);
2979 mono_counters_register ("# scan_object() called (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_scan_object_called_nursery);
2980 mono_counters_register ("# scan_object() called (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_scan_object_called_major);
2982 mono_counters_register ("# nursery copy_object() failed from space", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_from_space);
2983 mono_counters_register ("# nursery copy_object() failed forwarded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_forwarded);
2984 mono_counters_register ("# nursery copy_object() failed pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_nursery_copy_object_failed_pinned);
2986 mono_counters_register ("# wasted fragments used", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_wasted_fragments_used);
2987 mono_counters_register ("bytes in wasted fragments", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_wasted_fragments_bytes);
2989 mono_counters_register ("Store remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_store_remsets);
2990 mono_counters_register ("Unique store remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_store_remsets_unique);
2991 mono_counters_register ("Saved remsets 1", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_saved_remsets_1);
2992 mono_counters_register ("Saved remsets 2", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_saved_remsets_2);
2993 mono_counters_register ("Non-global remsets processed", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_local_remsets_processed);
2994 mono_counters_register ("Global remsets added", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_added);
2995 mono_counters_register ("Global remsets re-added", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_readded);
2996 mono_counters_register ("Global remsets processed", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_processed);
2997 mono_counters_register ("Global remsets discarded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_discarded);
2998 #endif
3000 inited = TRUE;
3003 static gboolean need_calculate_minor_collection_allowance;
3005 static int last_collection_old_num_major_sections;
3006 static mword last_collection_los_memory_usage = 0;
3007 static mword last_collection_old_los_memory_usage;
3008 static mword last_collection_los_memory_alloced;
3010 static void
3011 reset_minor_collection_allowance (void)
3013 need_calculate_minor_collection_allowance = TRUE;
3016 static mword
3017 double_to_mword_with_saturation (double value)
3019 if (value >= (double)MWORD_MAX_VALUE)
3020 return MWORD_MAX_VALUE;
3021 return (mword)value;
3024 static void
3025 try_calculate_minor_collection_allowance (gboolean overwrite)
3027 int num_major_sections, num_major_sections_saved, save_target, allowance_target;
3028 mword los_memory_saved, new_major, new_heap_size;
3030 if (overwrite)
3031 g_assert (need_calculate_minor_collection_allowance);
3033 if (!need_calculate_minor_collection_allowance)
3034 return;
3036 if (!*major_collector.have_swept) {
3037 if (overwrite)
3038 minor_collection_allowance = MIN_MINOR_COLLECTION_ALLOWANCE;
3039 return;
3042 num_major_sections = major_collector.get_num_major_sections ();
3044 num_major_sections_saved = MAX (last_collection_old_num_major_sections - num_major_sections, 0);
3045 los_memory_saved = MAX (last_collection_old_los_memory_usage - last_collection_los_memory_usage, 1);
3047 new_major = num_major_sections * major_collector.section_size;
3048 new_heap_size = new_major + last_collection_los_memory_usage;
3051 * FIXME: Why is save_target half the major memory plus half the
3052 * LOS memory saved? Shouldn't it be half the major memory
3053 * saved plus half the LOS memory saved? Or half the whole heap
3054 * size?
3056 save_target = (new_major + los_memory_saved) / 2;
3059 * We aim to allow the allocation of as many sections as is
3060 * necessary to reclaim save_target sections in the next
3061 * collection. We assume the collection pattern won't change.
3062 * In the last cycle, we had num_major_sections_saved for
3063 * minor_collection_sections_alloced. Assuming things won't
3064 * change, this must be the same ratio as save_target for
3065 * allowance_target, i.e.
3067 * num_major_sections_saved save_target
3068 * --------------------------------- == ----------------
3069 * minor_collection_sections_alloced allowance_target
3071 * hence:
3073 allowance_target = double_to_mword_with_saturation ((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));
3075 minor_collection_allowance = MAX (MIN (allowance_target, num_major_sections * major_collector.section_size + los_memory_usage), MIN_MINOR_COLLECTION_ALLOWANCE);
3077 if (new_heap_size + minor_collection_allowance > soft_heap_limit) {
3078 if (new_heap_size > soft_heap_limit)
3079 minor_collection_allowance = MIN_MINOR_COLLECTION_ALLOWANCE;
3080 else
3081 minor_collection_allowance = MAX (soft_heap_limit - new_heap_size, MIN_MINOR_COLLECTION_ALLOWANCE);
3084 if (debug_print_allowance) {
3085 mword old_major = last_collection_old_num_major_sections * major_collector.section_size;
3087 fprintf (gc_debug_file, "Before collection: %ld bytes (%ld major, %ld LOS)\n",
3088 old_major + last_collection_old_los_memory_usage, old_major, last_collection_old_los_memory_usage);
3089 fprintf (gc_debug_file, "After collection: %ld bytes (%ld major, %ld LOS)\n",
3090 new_heap_size, new_major, last_collection_los_memory_usage);
3091 fprintf (gc_debug_file, "Allowance: %ld bytes\n", minor_collection_allowance);
3094 if (major_collector.have_computed_minor_collection_allowance)
3095 major_collector.have_computed_minor_collection_allowance ();
3097 need_calculate_minor_collection_allowance = FALSE;
3100 static gboolean
3101 need_major_collection (mword space_needed)
3103 mword los_alloced = los_memory_usage - MIN (last_collection_los_memory_usage, los_memory_usage);
3104 return (space_needed > available_free_space ()) ||
3105 minor_collection_sections_alloced * major_collector.section_size + los_alloced > minor_collection_allowance;
3108 gboolean
3109 mono_sgen_need_major_collection (mword space_needed)
3111 return need_major_collection (space_needed);
3115 * Collect objects in the nursery. Returns whether to trigger a major
3116 * collection.
3118 static gboolean
3119 collect_nursery (size_t requested_size)
3121 gboolean needs_major;
3122 size_t max_garbage_amount;
3123 char *orig_nursery_next;
3124 TV_DECLARE (all_atv);
3125 TV_DECLARE (all_btv);
3126 TV_DECLARE (atv);
3127 TV_DECLARE (btv);
3129 if (disable_minor_collections)
3130 return TRUE;
3132 mono_perfcounters->gc_collections0++;
3134 current_collection_generation = GENERATION_NURSERY;
3136 binary_protocol_collection (GENERATION_NURSERY);
3137 check_scan_starts ();
3139 degraded_mode = 0;
3140 objects_pinned = 0;
3141 orig_nursery_next = nursery_next;
3142 nursery_next = MAX (nursery_next, nursery_last_pinned_end);
3143 /* FIXME: optimize later to use the higher address where an object can be present */
3144 nursery_next = MAX (nursery_next, nursery_real_end);
3146 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)));
3147 max_garbage_amount = nursery_next - nursery_start;
3148 g_assert (nursery_section->size >= max_garbage_amount);
3150 /* world must be stopped already */
3151 TV_GETTIME (all_atv);
3152 atv = all_atv;
3154 /* Pinning no longer depends on clearing all nursery fragments */
3155 clear_current_nursery_fragment (orig_nursery_next);
3157 TV_GETTIME (btv);
3158 time_minor_pre_collection_fragment_clear += TV_ELAPSED_MS (atv, btv);
3160 if (xdomain_checks)
3161 check_for_xdomain_refs ();
3163 nursery_section->next_data = nursery_next;
3165 major_collector.start_nursery_collection ();
3167 try_calculate_minor_collection_allowance (FALSE);
3169 gray_object_queue_init (&gray_queue, mono_sgen_get_unmanaged_allocator ());
3171 num_minor_gcs++;
3172 mono_stats.minor_gc_count ++;
3174 global_remset_cache_clear ();
3176 /* pin from pinned handles */
3177 init_pinning ();
3178 mono_profiler_gc_event (MONO_GC_EVENT_MARK_START, 0);
3179 pin_from_roots (nursery_start, nursery_next);
3180 /* identify pinned objects */
3181 optimize_pin_queue (0);
3182 next_pin_slot = pin_objects_from_addresses (nursery_section, pin_queue, pin_queue + next_pin_slot, nursery_start, nursery_next, &gray_queue);
3183 nursery_section->pin_queue_start = pin_queue;
3184 nursery_section->pin_queue_num_entries = next_pin_slot;
3185 TV_GETTIME (atv);
3186 time_minor_pinning += TV_ELAPSED_MS (btv, atv);
3187 DEBUG (2, fprintf (gc_debug_file, "Finding pinned pointers: %d in %d usecs\n", next_pin_slot, TV_ELAPSED (btv, atv)));
3188 DEBUG (4, fprintf (gc_debug_file, "Start scan with %d pinned objects\n", next_pin_slot));
3190 if (whole_heap_check_before_collection)
3191 whole_heap_check ();
3192 if (consistency_check_at_minor_collection)
3193 check_consistency ();
3196 * walk all the roots and copy the young objects to the old generation,
3197 * starting from to_space
3200 scan_from_remsets (nursery_start, nursery_next, &gray_queue);
3201 /* we don't have complete write barrier yet, so we scan all the old generation sections */
3202 TV_GETTIME (btv);
3203 time_minor_scan_remsets += TV_ELAPSED_MS (atv, btv);
3204 DEBUG (2, fprintf (gc_debug_file, "Old generation scan: %d usecs\n", TV_ELAPSED (atv, btv)));
3206 if (use_cardtable) {
3207 atv = btv;
3208 card_tables_collect_stats (TRUE);
3209 scan_from_card_tables (nursery_start, nursery_next, &gray_queue);
3210 TV_GETTIME (btv);
3211 time_minor_scan_card_table += TV_ELAPSED_MS (atv, btv);
3214 drain_gray_stack (&gray_queue);
3216 if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
3217 report_registered_roots ();
3218 if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
3219 report_finalizer_roots ();
3220 TV_GETTIME (atv);
3221 time_minor_scan_pinned += TV_ELAPSED_MS (btv, atv);
3222 /* registered roots, this includes static fields */
3223 scan_from_registered_roots (major_collector.copy_object, nursery_start, nursery_next, ROOT_TYPE_NORMAL, &gray_queue);
3224 scan_from_registered_roots (major_collector.copy_object, nursery_start, nursery_next, ROOT_TYPE_WBARRIER, &gray_queue);
3225 TV_GETTIME (btv);
3226 time_minor_scan_registered_roots += TV_ELAPSED_MS (atv, btv);
3227 /* thread data */
3228 scan_thread_data (nursery_start, nursery_next, TRUE);
3229 TV_GETTIME (atv);
3230 time_minor_scan_thread_data += TV_ELAPSED_MS (btv, atv);
3231 btv = atv;
3233 finish_gray_stack (nursery_start, nursery_next, GENERATION_NURSERY, &gray_queue);
3234 TV_GETTIME (atv);
3235 time_minor_finish_gray_stack += TV_ELAPSED_MS (btv, atv);
3236 mono_profiler_gc_event (MONO_GC_EVENT_MARK_END, 0);
3238 if (objects_pinned) {
3239 evacuate_pin_staging_area ();
3240 optimize_pin_queue (0);
3241 nursery_section->pin_queue_start = pin_queue;
3242 nursery_section->pin_queue_num_entries = next_pin_slot;
3245 /* walk the pin_queue, build up the fragment list of free memory, unmark
3246 * pinned objects as we go, memzero() the empty fragments so they are ready for the
3247 * next allocations.
3249 mono_profiler_gc_event (MONO_GC_EVENT_RECLAIM_START, 0);
3250 build_nursery_fragments (pin_queue, next_pin_slot);
3251 mono_profiler_gc_event (MONO_GC_EVENT_RECLAIM_END, 0);
3252 TV_GETTIME (btv);
3253 time_minor_fragment_creation += TV_ELAPSED_MS (atv, btv);
3254 DEBUG (2, fprintf (gc_debug_file, "Fragment creation: %d usecs, %lu bytes available\n", TV_ELAPSED (atv, btv), (unsigned long)fragment_total));
3256 if (consistency_check_at_minor_collection)
3257 check_major_refs ();
3259 major_collector.finish_nursery_collection ();
3261 TV_GETTIME (all_btv);
3262 mono_stats.minor_gc_time_usecs += TV_ELAPSED (all_atv, all_btv);
3264 if (heap_dump_file)
3265 dump_heap ("minor", num_minor_gcs - 1, NULL);
3267 /* prepare the pin queue for the next collection */
3268 last_num_pinned = next_pin_slot;
3269 next_pin_slot = 0;
3270 if (fin_ready_list || critical_fin_list) {
3271 DEBUG (4, fprintf (gc_debug_file, "Finalizer-thread wakeup: ready %d\n", num_ready_finalizers));
3272 mono_gc_finalize_notify ();
3274 pin_stats_reset ();
3276 g_assert (gray_object_queue_is_empty (&gray_queue));
3278 if (use_cardtable)
3279 card_tables_collect_stats (FALSE);
3281 check_scan_starts ();
3283 binary_protocol_flush_buffers (FALSE);
3285 /*objects are late pinned because of lack of memory, so a major is a good call*/
3286 needs_major = need_major_collection (0) || objects_pinned;
3287 current_collection_generation = -1;
3288 objects_pinned = 0;
3290 return needs_major;
3293 static void
3294 major_do_collection (const char *reason)
3296 LOSObject *bigobj, *prevbo;
3297 TV_DECLARE (all_atv);
3298 TV_DECLARE (all_btv);
3299 TV_DECLARE (atv);
3300 TV_DECLARE (btv);
3301 /* FIXME: only use these values for the precise scan
3302 * note that to_space pointers should be excluded anyway...
3304 char *heap_start = NULL;
3305 char *heap_end = (char*)-1;
3306 int old_next_pin_slot;
3308 mono_perfcounters->gc_collections1++;
3310 last_collection_old_num_major_sections = major_collector.get_num_major_sections ();
3313 * A domain could have been freed, resulting in
3314 * los_memory_usage being less than last_collection_los_memory_usage.
3316 last_collection_los_memory_alloced = los_memory_usage - MIN (last_collection_los_memory_usage, los_memory_usage);
3317 last_collection_old_los_memory_usage = los_memory_usage;
3318 objects_pinned = 0;
3320 //count_ref_nonref_objs ();
3321 //consistency_check ();
3323 binary_protocol_collection (GENERATION_OLD);
3324 check_scan_starts ();
3325 gray_object_queue_init (&gray_queue, mono_sgen_get_unmanaged_allocator ());
3326 if (major_collector.is_parallel)
3327 gray_object_queue_init (&workers_distribute_gray_queue, mono_sgen_get_unmanaged_allocator ());
3329 degraded_mode = 0;
3330 DEBUG (1, fprintf (gc_debug_file, "Start major collection %d\n", num_major_gcs));
3331 num_major_gcs++;
3332 mono_stats.major_gc_count ++;
3334 /* world must be stopped already */
3335 TV_GETTIME (all_atv);
3336 atv = all_atv;
3338 /* Pinning depends on this */
3339 clear_nursery_fragments (nursery_next);
3341 if (whole_heap_check_before_collection)
3342 whole_heap_check ();
3344 TV_GETTIME (btv);
3345 time_major_pre_collection_fragment_clear += TV_ELAPSED_MS (atv, btv);
3347 nursery_section->next_data = nursery_real_end;
3348 /* we should also coalesce scanning from sections close to each other
3349 * and deal with pointers outside of the sections later.
3352 if (major_collector.start_major_collection)
3353 major_collector.start_major_collection ();
3355 *major_collector.have_swept = FALSE;
3356 reset_minor_collection_allowance ();
3358 if (xdomain_checks)
3359 check_for_xdomain_refs ();
3361 /* The remsets are not useful for a major collection */
3362 clear_remsets ();
3363 global_remset_cache_clear ();
3364 if (use_cardtable)
3365 card_table_clear ();
3367 TV_GETTIME (atv);
3368 init_pinning ();
3369 DEBUG (6, fprintf (gc_debug_file, "Collecting pinned addresses\n"));
3370 pin_from_roots ((void*)lowest_heap_address, (void*)highest_heap_address);
3371 optimize_pin_queue (0);
3374 * pin_queue now contains all candidate pointers, sorted and
3375 * uniqued. We must do two passes now to figure out which
3376 * objects are pinned.
3378 * The first is to find within the pin_queue the area for each
3379 * section. This requires that the pin_queue be sorted. We
3380 * also process the LOS objects and pinned chunks here.
3382 * The second, destructive, pass is to reduce the section
3383 * areas to pointers to the actually pinned objects.
3385 DEBUG (6, fprintf (gc_debug_file, "Pinning from sections\n"));
3386 /* first pass for the sections */
3387 mono_sgen_find_section_pin_queue_start_end (nursery_section);
3388 major_collector.find_pin_queue_start_ends (WORKERS_DISTRIBUTE_GRAY_QUEUE);
3389 /* identify possible pointers to the insize of large objects */
3390 DEBUG (6, fprintf (gc_debug_file, "Pinning from large objects\n"));
3391 for (bigobj = los_object_list; bigobj; bigobj = bigobj->next) {
3392 int dummy;
3393 gboolean profile_roots = mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS;
3394 GCRootReport report;
3395 report.count = 0;
3396 if (mono_sgen_find_optimized_pin_queue_area (bigobj->data, (char*)bigobj->data + bigobj->size, &dummy)) {
3397 pin_object (bigobj->data);
3398 /* FIXME: only enqueue if object has references */
3399 GRAY_OBJECT_ENQUEUE (WORKERS_DISTRIBUTE_GRAY_QUEUE, bigobj->data);
3400 if (heap_dump_file)
3401 mono_sgen_pin_stats_register_object ((char*) bigobj->data, safe_object_get_size ((MonoObject*) bigobj->data));
3402 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));
3404 if (profile_roots)
3405 add_profile_gc_root (&report, bigobj->data, MONO_PROFILE_GC_ROOT_PINNING | MONO_PROFILE_GC_ROOT_MISC, 0);
3407 if (profile_roots)
3408 notify_gc_roots (&report);
3410 /* second pass for the sections */
3411 mono_sgen_pin_objects_in_section (nursery_section, WORKERS_DISTRIBUTE_GRAY_QUEUE);
3412 major_collector.pin_objects (WORKERS_DISTRIBUTE_GRAY_QUEUE);
3413 old_next_pin_slot = next_pin_slot;
3415 TV_GETTIME (btv);
3416 time_major_pinning += TV_ELAPSED_MS (atv, btv);
3417 DEBUG (2, fprintf (gc_debug_file, "Finding pinned pointers: %d in %d usecs\n", next_pin_slot, TV_ELAPSED (atv, btv)));
3418 DEBUG (4, fprintf (gc_debug_file, "Start scan with %d pinned objects\n", next_pin_slot));
3420 major_collector.init_to_space ();
3422 workers_start_all_workers (1);
3424 if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
3425 report_registered_roots ();
3426 TV_GETTIME (atv);
3427 time_major_scan_pinned += TV_ELAPSED_MS (btv, atv);
3429 /* registered roots, this includes static fields */
3430 scan_from_registered_roots (major_collector.copy_or_mark_object, heap_start, heap_end, ROOT_TYPE_NORMAL, WORKERS_DISTRIBUTE_GRAY_QUEUE);
3431 scan_from_registered_roots (major_collector.copy_or_mark_object, heap_start, heap_end, ROOT_TYPE_WBARRIER, WORKERS_DISTRIBUTE_GRAY_QUEUE);
3432 TV_GETTIME (btv);
3433 time_major_scan_registered_roots += TV_ELAPSED_MS (atv, btv);
3435 /* Threads */
3436 /* FIXME: This is the wrong place for this, because it does
3437 pinning */
3438 scan_thread_data (heap_start, heap_end, TRUE);
3439 TV_GETTIME (atv);
3440 time_major_scan_thread_data += TV_ELAPSED_MS (btv, atv);
3442 TV_GETTIME (btv);
3443 time_major_scan_alloc_pinned += TV_ELAPSED_MS (atv, btv);
3445 if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
3446 report_finalizer_roots ();
3447 /* scan the list of objects ready for finalization */
3448 scan_finalizer_entries (major_collector.copy_or_mark_object, fin_ready_list, WORKERS_DISTRIBUTE_GRAY_QUEUE);
3449 scan_finalizer_entries (major_collector.copy_or_mark_object, critical_fin_list, WORKERS_DISTRIBUTE_GRAY_QUEUE);
3450 TV_GETTIME (atv);
3451 time_major_scan_finalized += TV_ELAPSED_MS (btv, atv);
3452 DEBUG (2, fprintf (gc_debug_file, "Root scan: %d usecs\n", TV_ELAPSED (btv, atv)));
3454 TV_GETTIME (btv);
3455 time_major_scan_big_objects += TV_ELAPSED_MS (atv, btv);
3457 if (major_collector.is_parallel) {
3458 while (!gray_object_queue_is_empty (WORKERS_DISTRIBUTE_GRAY_QUEUE)) {
3459 workers_distribute_gray_queue_sections ();
3460 usleep (2000);
3463 workers_change_num_working (-1);
3464 workers_join ();
3466 if (major_collector.is_parallel)
3467 g_assert (gray_object_queue_is_empty (&gray_queue));
3469 /* all the objects in the heap */
3470 finish_gray_stack (heap_start, heap_end, GENERATION_OLD, &gray_queue);
3471 TV_GETTIME (atv);
3472 time_major_finish_gray_stack += TV_ELAPSED_MS (btv, atv);
3474 if (objects_pinned) {
3475 /*This is slow, but we just OOM'd*/
3476 mono_sgen_pin_queue_clear_discarded_entries (nursery_section, old_next_pin_slot);
3477 evacuate_pin_staging_area ();
3478 optimize_pin_queue (0);
3479 mono_sgen_find_section_pin_queue_start_end (nursery_section);
3480 objects_pinned = 0;
3483 reset_heap_boundaries ();
3484 mono_sgen_update_heap_boundaries ((mword)nursery_start, (mword)nursery_real_end);
3486 /* sweep the big objects list */
3487 prevbo = NULL;
3488 for (bigobj = los_object_list; bigobj;) {
3489 if (object_is_pinned (bigobj->data)) {
3490 unpin_object (bigobj->data);
3491 mono_sgen_update_heap_boundaries ((mword)bigobj->data, (mword)bigobj->data + bigobj->size);
3492 } else {
3493 LOSObject *to_free;
3494 /* not referenced anywhere, so we can free it */
3495 if (prevbo)
3496 prevbo->next = bigobj->next;
3497 else
3498 los_object_list = bigobj->next;
3499 to_free = bigobj;
3500 bigobj = bigobj->next;
3501 mono_sgen_los_free_object (to_free);
3502 continue;
3504 prevbo = bigobj;
3505 bigobj = bigobj->next;
3508 TV_GETTIME (btv);
3509 time_major_free_bigobjs += TV_ELAPSED_MS (atv, btv);
3511 mono_sgen_los_sweep ();
3513 TV_GETTIME (atv);
3514 time_major_los_sweep += TV_ELAPSED_MS (btv, atv);
3516 major_collector.sweep ();
3518 TV_GETTIME (btv);
3519 time_major_sweep += TV_ELAPSED_MS (atv, btv);
3521 /* walk the pin_queue, build up the fragment list of free memory, unmark
3522 * pinned objects as we go, memzero() the empty fragments so they are ready for the
3523 * next allocations.
3525 build_nursery_fragments (nursery_section->pin_queue_start, nursery_section->pin_queue_num_entries);
3527 TV_GETTIME (atv);
3528 time_major_fragment_creation += TV_ELAPSED_MS (btv, atv);
3530 TV_GETTIME (all_btv);
3531 mono_stats.major_gc_time_usecs += TV_ELAPSED (all_atv, all_btv);
3533 if (heap_dump_file)
3534 dump_heap ("major", num_major_gcs - 1, reason);
3536 /* prepare the pin queue for the next collection */
3537 next_pin_slot = 0;
3538 if (fin_ready_list || critical_fin_list) {
3539 DEBUG (4, fprintf (gc_debug_file, "Finalizer-thread wakeup: ready %d\n", num_ready_finalizers));
3540 mono_gc_finalize_notify ();
3542 pin_stats_reset ();
3544 g_assert (gray_object_queue_is_empty (&gray_queue));
3546 try_calculate_minor_collection_allowance (TRUE);
3548 minor_collection_sections_alloced = 0;
3549 last_collection_los_memory_usage = los_memory_usage;
3551 major_collector.finish_major_collection ();
3553 check_scan_starts ();
3555 binary_protocol_flush_buffers (FALSE);
3557 //consistency_check ();
3560 static void
3561 major_collection (const char *reason)
3563 if (disable_major_collections) {
3564 collect_nursery (0);
3565 return;
3568 major_collection_hapenned = TRUE;
3569 current_collection_generation = GENERATION_OLD;
3570 major_do_collection (reason);
3571 current_collection_generation = -1;
3574 void
3575 sgen_collect_major_no_lock (const char *reason)
3577 mono_profiler_gc_event (MONO_GC_EVENT_START, 1);
3578 stop_world (1);
3579 major_collection (reason);
3580 restart_world (1);
3581 mono_profiler_gc_event (MONO_GC_EVENT_END, 1);
3585 * When deciding if it's better to collect or to expand, keep track
3586 * of how much garbage was reclaimed with the last collection: if it's too
3587 * little, expand.
3588 * This is called when we could not allocate a small object.
3590 static void __attribute__((noinline))
3591 minor_collect_or_expand_inner (size_t size)
3593 int do_minor_collection = 1;
3595 g_assert (nursery_section);
3596 if (do_minor_collection) {
3597 mono_profiler_gc_event (MONO_GC_EVENT_START, 0);
3598 stop_world (0);
3599 if (collect_nursery (size)) {
3600 mono_profiler_gc_event (MONO_GC_EVENT_START, 1);
3601 major_collection ("minor overflow");
3602 /* keep events symmetric */
3603 mono_profiler_gc_event (MONO_GC_EVENT_END, 1);
3605 DEBUG (2, fprintf (gc_debug_file, "Heap size: %lu, LOS size: %lu\n", (unsigned long)total_alloc, (unsigned long)los_memory_usage));
3606 restart_world (0);
3607 /* this also sets the proper pointers for the next allocation */
3608 if (!search_fragment_for_size (size)) {
3609 int i;
3610 /* TypeBuilder and MonoMethod are killing mcs with fragmentation */
3611 DEBUG (1, fprintf (gc_debug_file, "nursery collection didn't find enough room for %zd alloc (%d pinned)\n", size, last_num_pinned));
3612 for (i = 0; i < last_num_pinned; ++i) {
3613 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])));
3615 degraded_mode = 1;
3617 mono_profiler_gc_event (MONO_GC_EVENT_END, 0);
3619 //report_internal_mem_usage ();
3623 * ######################################################################
3624 * ######## Memory allocation from the OS
3625 * ######################################################################
3626 * This section of code deals with getting memory from the OS and
3627 * allocating memory for GC-internal data structures.
3628 * Internal memory can be handled with a freelist for small objects.
3632 * Debug reporting.
3634 G_GNUC_UNUSED static void
3635 report_internal_mem_usage (void)
3637 printf ("Internal memory usage:\n");
3638 mono_sgen_report_internal_mem_usage ();
3639 printf ("Pinned memory usage:\n");
3640 major_collector.report_pinned_memory_usage ();
3644 * Allocate a big chunk of memory from the OS (usually 64KB to several megabytes).
3645 * This must not require any lock.
3647 void*
3648 mono_sgen_alloc_os_memory (size_t size, int activate)
3650 void *ptr;
3651 unsigned long prot_flags = activate? MONO_MMAP_READ|MONO_MMAP_WRITE: MONO_MMAP_NONE;
3653 prot_flags |= MONO_MMAP_PRIVATE | MONO_MMAP_ANON;
3654 size += pagesize - 1;
3655 size &= ~(pagesize - 1);
3656 ptr = mono_valloc (0, size, prot_flags);
3657 /* FIXME: CAS */
3658 total_alloc += size;
3659 return ptr;
3663 * Free the memory returned by mono_sgen_alloc_os_memory (), returning it to the OS.
3665 void
3666 mono_sgen_free_os_memory (void *addr, size_t size)
3668 mono_vfree (addr, size);
3670 size += pagesize - 1;
3671 size &= ~(pagesize - 1);
3672 /* FIXME: CAS */
3673 total_alloc -= size;
3677 * ######################################################################
3678 * ######## Object allocation
3679 * ######################################################################
3680 * This section of code deals with allocating memory for objects.
3681 * There are several ways:
3682 * *) allocate large objects
3683 * *) allocate normal objects
3684 * *) fast lock-free allocation
3685 * *) allocation of pinned objects
3688 static void
3689 setup_fragment (Fragment *frag, Fragment *prev, size_t size)
3691 /* remove from the list */
3692 if (prev)
3693 prev->next = frag->next;
3694 else
3695 nursery_fragments = frag->next;
3696 nursery_next = frag->fragment_start;
3697 nursery_frag_real_end = frag->fragment_end;
3699 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));
3700 frag->next = fragment_freelist;
3701 fragment_freelist = frag;
3704 /* check if we have a suitable fragment in nursery_fragments to be able to allocate
3705 * an object of size @size
3706 * Return FALSE if not found (which means we need a collection)
3708 static gboolean
3709 search_fragment_for_size (size_t size)
3711 Fragment *frag, *prev;
3712 DEBUG (4, fprintf (gc_debug_file, "Searching nursery fragment %p, size: %zd\n", nursery_frag_real_end, size));
3714 if (nursery_frag_real_end > nursery_next && nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
3715 /* Clear the remaining space, pinning depends on this */
3716 memset (nursery_next, 0, nursery_frag_real_end - nursery_next);
3719 prev = NULL;
3720 for (frag = nursery_fragments; frag; frag = frag->next) {
3721 if (size <= (frag->fragment_end - frag->fragment_start)) {
3722 setup_fragment (frag, prev, size);
3723 return TRUE;
3725 prev = frag;
3727 return FALSE;
3731 * Same as search_fragment_for_size but if search for @desired_size fails, try to satisfy @minimum_size.
3732 * This improves nursery usage.
3734 static int
3735 search_fragment_for_size_range (size_t desired_size, size_t minimum_size)
3737 Fragment *frag, *prev, *min_prev;
3738 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));
3740 if (nursery_frag_real_end > nursery_next && nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
3741 /* Clear the remaining space, pinning depends on this */
3742 memset (nursery_next, 0, nursery_frag_real_end - nursery_next);
3745 min_prev = GINT_TO_POINTER (-1);
3746 prev = NULL;
3748 for (frag = nursery_fragments; frag; frag = frag->next) {
3749 int frag_size = frag->fragment_end - frag->fragment_start;
3750 if (desired_size <= frag_size) {
3751 setup_fragment (frag, prev, desired_size);
3752 return desired_size;
3754 if (minimum_size <= frag_size)
3755 min_prev = prev;
3757 prev = frag;
3760 if (min_prev != GINT_TO_POINTER (-1)) {
3761 int frag_size;
3762 if (min_prev)
3763 frag = min_prev->next;
3764 else
3765 frag = nursery_fragments;
3767 frag_size = frag->fragment_end - frag->fragment_start;
3768 HEAVY_STAT (++stat_wasted_fragments_used);
3769 HEAVY_STAT (stat_wasted_fragments_bytes += frag_size);
3771 setup_fragment (frag, min_prev, minimum_size);
3772 return frag_size;
3775 return 0;
3778 static void*
3779 alloc_degraded (MonoVTable *vtable, size_t size)
3781 if (need_major_collection (0)) {
3782 mono_profiler_gc_event (MONO_GC_EVENT_START, 1);
3783 stop_world (1);
3784 major_collection ("degraded overflow");
3785 restart_world (1);
3786 mono_profiler_gc_event (MONO_GC_EVENT_END, 1);
3789 return major_collector.alloc_degraded (vtable, size);
3793 * Provide a variant that takes just the vtable for small fixed-size objects.
3794 * The aligned size is already computed and stored in vt->gc_descr.
3795 * Note: every SCAN_START_SIZE or so we are given the chance to do some special
3796 * processing. We can keep track of where objects start, for example,
3797 * so when we scan the thread stacks for pinned objects, we can start
3798 * a search for the pinned object in SCAN_START_SIZE chunks.
3800 static void*
3801 mono_gc_alloc_obj_nolock (MonoVTable *vtable, size_t size)
3803 /* FIXME: handle OOM */
3804 void **p;
3805 char *new_next;
3806 TLAB_ACCESS_INIT;
3808 HEAVY_STAT (++stat_objects_alloced);
3809 if (size <= MAX_SMALL_OBJ_SIZE)
3810 HEAVY_STAT (stat_bytes_alloced += size);
3811 else
3812 HEAVY_STAT (stat_bytes_alloced_los += size);
3814 size = ALIGN_UP (size);
3816 g_assert (vtable->gc_descr);
3818 if (G_UNLIKELY (has_per_allocation_action)) {
3819 static int alloc_count;
3820 int current_alloc = InterlockedIncrement (&alloc_count);
3822 if (collect_before_allocs) {
3823 if (((current_alloc % collect_before_allocs) == 0) && nursery_section) {
3824 mono_profiler_gc_event (MONO_GC_EVENT_START, 0);
3825 stop_world (0);
3826 collect_nursery (0);
3827 restart_world (0);
3828 mono_profiler_gc_event (MONO_GC_EVENT_END, 0);
3829 if (!degraded_mode && !search_fragment_for_size (size) && size <= MAX_SMALL_OBJ_SIZE) {
3830 // FIXME:
3831 g_assert_not_reached ();
3834 } else if (verify_before_allocs) {
3835 if ((current_alloc % verify_before_allocs) == 0)
3836 verify_whole_heap ();
3841 * We must already have the lock here instead of after the
3842 * fast path because we might be interrupted in the fast path
3843 * (after confirming that new_next < TLAB_TEMP_END) by the GC,
3844 * and we'll end up allocating an object in a fragment which
3845 * no longer belongs to us.
3847 * The managed allocator does not do this, but it's treated
3848 * specially by the world-stopping code.
3851 if (size > MAX_SMALL_OBJ_SIZE) {
3852 p = mono_sgen_los_alloc_large_inner (vtable, size);
3853 } else {
3854 /* tlab_next and tlab_temp_end are TLS vars so accessing them might be expensive */
3856 p = (void**)TLAB_NEXT;
3857 /* FIXME: handle overflow */
3858 new_next = (char*)p + size;
3859 TLAB_NEXT = new_next;
3861 if (G_LIKELY (new_next < TLAB_TEMP_END)) {
3862 /* Fast path */
3865 * FIXME: We might need a memory barrier here so the change to tlab_next is
3866 * visible before the vtable store.
3869 DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
3870 binary_protocol_alloc (p , vtable, size);
3871 g_assert (*p == NULL);
3872 mono_atomic_store_seq (p, vtable);
3874 g_assert (TLAB_NEXT == new_next);
3876 return p;
3879 /* Slow path */
3881 /* there are two cases: the object is too big or we run out of space in the TLAB */
3882 /* we also reach here when the thread does its first allocation after a minor
3883 * collection, since the tlab_ variables are initialized to NULL.
3884 * there can be another case (from ORP), if we cooperate with the runtime a bit:
3885 * objects that need finalizers can have the high bit set in their size
3886 * so the above check fails and we can readily add the object to the queue.
3887 * This avoids taking again the GC lock when registering, but this is moot when
3888 * doing thread-local allocation, so it may not be a good idea.
3890 g_assert (TLAB_NEXT == new_next);
3891 if (TLAB_NEXT >= TLAB_REAL_END) {
3893 * Run out of space in the TLAB. When this happens, some amount of space
3894 * remains in the TLAB, but not enough to satisfy the current allocation
3895 * request. Currently, we retire the TLAB in all cases, later we could
3896 * keep it if the remaining space is above a treshold, and satisfy the
3897 * allocation directly from the nursery.
3899 TLAB_NEXT -= size;
3900 /* when running in degraded mode, we continue allocing that way
3901 * for a while, to decrease the number of useless nursery collections.
3903 if (degraded_mode && degraded_mode < DEFAULT_NURSERY_SIZE) {
3904 p = alloc_degraded (vtable, size);
3905 binary_protocol_alloc_degraded (p, vtable, size);
3906 return p;
3909 /*FIXME This codepath is current deadcode since tlab_size > MAX_SMALL_OBJ_SIZE*/
3910 if (size > tlab_size) {
3911 /* Allocate directly from the nursery */
3912 if (nursery_next + size >= nursery_frag_real_end) {
3913 if (!search_fragment_for_size (size)) {
3914 minor_collect_or_expand_inner (size);
3915 if (degraded_mode) {
3916 p = alloc_degraded (vtable, size);
3917 binary_protocol_alloc_degraded (p, vtable, size);
3918 return p;
3923 p = (void*)nursery_next;
3924 nursery_next += size;
3925 if (nursery_next > nursery_frag_real_end) {
3926 // no space left
3927 g_assert (0);
3930 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
3931 memset (p, 0, size);
3933 } else {
3934 int alloc_size = tlab_size;
3935 int available_in_nursery = nursery_frag_real_end - nursery_next;
3936 if (TLAB_START)
3937 DEBUG (3, fprintf (gc_debug_file, "Retire TLAB: %p-%p [%ld]\n", TLAB_START, TLAB_REAL_END, (long)(TLAB_REAL_END - TLAB_NEXT - size)));
3939 if (alloc_size >= available_in_nursery) {
3940 if (available_in_nursery > MAX_NURSERY_TLAB_WASTE && available_in_nursery > size) {
3941 alloc_size = available_in_nursery;
3942 } else {
3943 alloc_size = search_fragment_for_size_range (tlab_size, size);
3944 if (!alloc_size) {
3945 alloc_size = tlab_size;
3946 minor_collect_or_expand_inner (tlab_size);
3947 if (degraded_mode) {
3948 p = alloc_degraded (vtable, size);
3949 binary_protocol_alloc_degraded (p, vtable, size);
3950 return p;
3956 /* Allocate a new TLAB from the current nursery fragment */
3957 TLAB_START = nursery_next;
3958 nursery_next += alloc_size;
3959 TLAB_NEXT = TLAB_START;
3960 TLAB_REAL_END = TLAB_START + alloc_size;
3961 TLAB_TEMP_END = TLAB_START + MIN (SCAN_START_SIZE, alloc_size);
3963 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
3964 memset (TLAB_START, 0, alloc_size);
3967 /* Allocate from the TLAB */
3968 p = (void*)TLAB_NEXT;
3969 TLAB_NEXT += size;
3970 g_assert (TLAB_NEXT <= TLAB_REAL_END);
3972 nursery_section->scan_starts [((char*)p - (char*)nursery_section->data)/SCAN_START_SIZE] = (char*)p;
3974 } else {
3975 /* Reached tlab_temp_end */
3977 /* record the scan start so we can find pinned objects more easily */
3978 nursery_section->scan_starts [((char*)p - (char*)nursery_section->data)/SCAN_START_SIZE] = (char*)p;
3979 /* we just bump tlab_temp_end as well */
3980 TLAB_TEMP_END = MIN (TLAB_REAL_END, TLAB_NEXT + SCAN_START_SIZE);
3981 DEBUG (5, fprintf (gc_debug_file, "Expanding local alloc: %p-%p\n", TLAB_NEXT, TLAB_TEMP_END));
3985 if (G_LIKELY (p)) {
3986 DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
3987 binary_protocol_alloc (p, vtable, size);
3988 mono_atomic_store_seq (p, vtable);
3991 return p;
3994 static void*
3995 mono_gc_try_alloc_obj_nolock (MonoVTable *vtable, size_t size)
3997 void **p;
3998 char *new_next;
3999 TLAB_ACCESS_INIT;
4001 size = ALIGN_UP (size);
4003 g_assert (vtable->gc_descr);
4004 if (size <= MAX_SMALL_OBJ_SIZE) {
4005 /* tlab_next and tlab_temp_end are TLS vars so accessing them might be expensive */
4007 p = (void**)TLAB_NEXT;
4008 /* FIXME: handle overflow */
4009 new_next = (char*)p + size;
4010 TLAB_NEXT = new_next;
4012 if (G_LIKELY (new_next < TLAB_TEMP_END)) {
4013 /* Fast path */
4016 * FIXME: We might need a memory barrier here so the change to tlab_next is
4017 * visible before the vtable store.
4020 HEAVY_STAT (++stat_objects_alloced);
4021 HEAVY_STAT (stat_bytes_alloced += size);
4023 DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
4024 binary_protocol_alloc (p, vtable, size);
4025 g_assert (*p == NULL);
4026 mono_atomic_store_seq (p, vtable);
4028 return p;
4031 return NULL;
4034 void*
4035 mono_gc_alloc_obj (MonoVTable *vtable, size_t size)
4037 void *res;
4038 #ifndef DISABLE_CRITICAL_REGION
4039 TLAB_ACCESS_INIT;
4040 ENTER_CRITICAL_REGION;
4041 res = mono_gc_try_alloc_obj_nolock (vtable, size);
4042 if (res) {
4043 EXIT_CRITICAL_REGION;
4044 return res;
4046 EXIT_CRITICAL_REGION;
4047 #endif
4048 LOCK_GC;
4049 res = mono_gc_alloc_obj_nolock (vtable, size);
4050 UNLOCK_GC;
4051 if (G_UNLIKELY (!res))
4052 return mono_gc_out_of_memory (size);
4053 return res;
4056 void*
4057 mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length)
4059 MonoArray *arr;
4060 #ifndef DISABLE_CRITICAL_REGION
4061 TLAB_ACCESS_INIT;
4062 ENTER_CRITICAL_REGION;
4063 arr = mono_gc_try_alloc_obj_nolock (vtable, size);
4064 if (arr) {
4065 /*This doesn't require fencing since EXIT_CRITICAL_REGION already does it for us*/
4066 arr->max_length = max_length;
4067 EXIT_CRITICAL_REGION;
4068 return arr;
4070 EXIT_CRITICAL_REGION;
4071 #endif
4073 LOCK_GC;
4075 arr = mono_gc_alloc_obj_nolock (vtable, size);
4076 if (G_UNLIKELY (!arr)) {
4077 UNLOCK_GC;
4078 return mono_gc_out_of_memory (size);
4081 arr->max_length = max_length;
4083 UNLOCK_GC;
4085 return arr;
4088 void*
4089 mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uintptr_t bounds_size)
4091 MonoArray *arr;
4092 MonoArrayBounds *bounds;
4094 LOCK_GC;
4096 arr = mono_gc_alloc_obj_nolock (vtable, size);
4097 if (G_UNLIKELY (!arr)) {
4098 UNLOCK_GC;
4099 return mono_gc_out_of_memory (size);
4102 arr->max_length = max_length;
4104 bounds = (MonoArrayBounds*)((char*)arr + size - bounds_size);
4105 arr->bounds = bounds;
4107 UNLOCK_GC;
4109 return arr;
4112 void*
4113 mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len)
4115 MonoString *str;
4116 #ifndef DISABLE_CRITICAL_REGION
4117 TLAB_ACCESS_INIT;
4118 ENTER_CRITICAL_REGION;
4119 str = mono_gc_try_alloc_obj_nolock (vtable, size);
4120 if (str) {
4121 /*This doesn't require fencing since EXIT_CRITICAL_REGION already does it for us*/
4122 str->length = len;
4123 EXIT_CRITICAL_REGION;
4124 return str;
4126 EXIT_CRITICAL_REGION;
4127 #endif
4129 LOCK_GC;
4131 str = mono_gc_alloc_obj_nolock (vtable, size);
4132 if (G_UNLIKELY (!str)) {
4133 UNLOCK_GC;
4134 return mono_gc_out_of_memory (size);
4137 str->length = len;
4139 UNLOCK_GC;
4141 return str;
4145 * To be used for interned strings and possibly MonoThread, reflection handles.
4146 * We may want to explicitly free these objects.
4148 void*
4149 mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size)
4151 void **p;
4152 size = ALIGN_UP (size);
4153 LOCK_GC;
4155 if (size > MAX_SMALL_OBJ_SIZE) {
4156 /* large objects are always pinned anyway */
4157 p = mono_sgen_los_alloc_large_inner (vtable, size);
4158 } else {
4159 DEBUG (9, g_assert (vtable->klass->inited));
4160 p = major_collector.alloc_small_pinned_obj (size, SGEN_VTABLE_HAS_REFERENCES (vtable));
4162 if (G_LIKELY (p)) {
4163 DEBUG (6, fprintf (gc_debug_file, "Allocated pinned object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
4164 binary_protocol_alloc_pinned (p, vtable, size);
4165 mono_atomic_store_seq (p, vtable);
4167 UNLOCK_GC;
4168 return p;
4171 void*
4172 mono_gc_alloc_mature (MonoVTable *vtable)
4174 void **res;
4175 size_t size = ALIGN_UP (vtable->klass->instance_size);
4176 LOCK_GC;
4177 res = alloc_degraded (vtable, size);
4178 mono_atomic_store_seq (res, vtable);
4179 UNLOCK_GC;
4180 if (G_UNLIKELY (vtable->klass->has_finalize))
4181 mono_object_register_finalizer ((MonoObject*)res);
4183 return res;
4187 * ######################################################################
4188 * ######## Finalization support
4189 * ######################################################################
4193 * this is valid for the nursery: if the object has been forwarded it means it's
4194 * still refrenced from a root. If it is pinned it's still alive as well.
4195 * Return TRUE if @obj is ready to be finalized.
4197 #define object_is_fin_ready(obj) (!object_is_pinned (obj) && !object_is_forwarded (obj))
4200 gboolean
4201 mono_sgen_gc_is_object_ready_for_finalization (void *object)
4203 return !major_collector.is_object_live (object) && object_is_fin_ready (object);
4206 static gboolean
4207 is_critical_finalizer (FinalizeEntry *entry)
4209 MonoObject *obj;
4210 MonoClass *class;
4212 if (!mono_defaults.critical_finalizer_object)
4213 return FALSE;
4215 obj = finalize_entry_get_object (entry);
4216 class = ((MonoVTable*)LOAD_VTABLE (obj))->klass;
4218 return mono_class_has_parent_fast (class, mono_defaults.critical_finalizer_object);
4221 static void
4222 queue_finalization_entry (FinalizeEntry *entry) {
4223 if (is_critical_finalizer (entry)) {
4224 entry->next = critical_fin_list;
4225 critical_fin_list = entry;
4226 } else {
4227 entry->next = fin_ready_list;
4228 fin_ready_list = entry;
4232 /* LOCKING: requires that the GC lock is held */
4233 static void
4234 rehash_fin_table (FinalizeEntryHashTable *hash_table)
4236 FinalizeEntry **finalizable_hash = hash_table->table;
4237 mword finalizable_hash_size = hash_table->size;
4238 int i;
4239 unsigned int hash;
4240 FinalizeEntry **new_hash;
4241 FinalizeEntry *entry, *next;
4242 int new_size = g_spaced_primes_closest (hash_table->num_registered);
4244 new_hash = mono_sgen_alloc_internal_dynamic (new_size * sizeof (FinalizeEntry*), INTERNAL_MEM_FIN_TABLE);
4245 for (i = 0; i < finalizable_hash_size; ++i) {
4246 for (entry = finalizable_hash [i]; entry; entry = next) {
4247 hash = mono_object_hash (finalize_entry_get_object (entry)) % new_size;
4248 next = entry->next;
4249 entry->next = new_hash [hash];
4250 new_hash [hash] = entry;
4253 mono_sgen_free_internal_dynamic (finalizable_hash, finalizable_hash_size * sizeof (FinalizeEntry*), INTERNAL_MEM_FIN_TABLE);
4254 hash_table->table = new_hash;
4255 hash_table->size = new_size;
4258 /* LOCKING: requires that the GC lock is held */
4259 static void
4260 rehash_fin_table_if_necessary (FinalizeEntryHashTable *hash_table)
4262 if (hash_table->num_registered >= hash_table->size * 2)
4263 rehash_fin_table (hash_table);
4268 /* LOCKING: requires that the GC lock is held */
4269 void
4270 mono_sgen_mark_bridge_object (MonoObject *obj)
4272 FinalizeEntryHashTable *hash_table = get_finalize_entry_hash_table (ptr_in_nursery (obj) ? GENERATION_NURSERY : GENERATION_OLD);
4273 FinalizeEntry **finalizable_hash = hash_table->table;
4274 FinalizeEntry *entry;
4275 unsigned int hash;
4277 hash = mono_object_hash (obj);
4278 hash %= hash_table->size;
4280 for (entry = finalizable_hash [hash]; entry; entry = entry->next) {
4281 if (finalize_entry_get_object (entry) == obj)
4282 finalize_entry_set_object (entry, obj, TRUE);
4286 /* LOCKING: requires that the GC lock is held */
4287 static void
4288 collect_bridge_objects (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, GrayQueue *queue)
4290 FinalizeEntryHashTable *hash_table = get_finalize_entry_hash_table (generation);
4291 FinalizeEntry *entry, *prev;
4292 int i;
4293 FinalizeEntry **finalizable_hash = hash_table->table;
4294 mword finalizable_hash_size = hash_table->size;
4296 if (no_finalize)
4297 return;
4299 for (i = 0; i < finalizable_hash_size; ++i) {
4300 prev = NULL;
4301 for (entry = finalizable_hash [i]; entry;) {
4302 MonoObject *object = finalize_entry_get_object (entry);
4303 gboolean ignore_obj = finalize_entry_get_bridge_bit (entry);
4304 char *copy;
4306 /* Bridge code told us to ignore this one */
4307 if (ignore_obj)
4308 goto next_step;
4310 /* Object is a bridge object and major heap says it's dead */
4311 if (!((char*)object >= start && (char*)object < end && !major_collector.is_object_live ((char*)object)))
4312 goto next_step;
4314 /* Nursery says the object is dead. */
4315 if (!object_is_fin_ready (object))
4316 goto next_step;
4318 if (!mono_sgen_is_bridge_object (object))
4319 goto next_step;
4321 copy = (char*)object;
4322 copy_func ((void**)&copy, queue);
4324 bridge_register_finalized_object ((MonoObject*)copy);
4326 if (hash_table == &minor_finalizable_hash && !ptr_in_nursery (copy)) {
4327 FinalizeEntry *next = entry->next;
4328 unsigned int major_hash;
4329 /* remove from the list */
4330 if (prev)
4331 prev->next = entry->next;
4332 else
4333 finalizable_hash [i] = entry->next;
4334 hash_table->num_registered--;
4336 finalize_entry_set_object (entry, (MonoObject*)copy, ignore_obj);
4338 /* insert it into the major hash */
4339 rehash_fin_table_if_necessary (&major_finalizable_hash);
4340 major_hash = mono_object_hash ((MonoObject*) copy) %
4341 major_finalizable_hash.size;
4342 entry->next = major_finalizable_hash.table [major_hash];
4343 major_finalizable_hash.table [major_hash] = entry;
4344 major_finalizable_hash.num_registered++;
4346 entry = next;
4347 continue;
4348 } else {
4349 /* update pointer */
4350 finalize_entry_set_object (entry, (MonoObject*)copy, ignore_obj);
4352 next_step:
4353 prev = entry;
4354 entry = entry->next;
4359 /* LOCKING: requires that the GC lock is held */
4360 static void
4361 finalize_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, GrayQueue *queue)
4363 FinalizeEntryHashTable *hash_table = get_finalize_entry_hash_table (generation);
4364 FinalizeEntry *entry, *prev;
4365 int i;
4366 FinalizeEntry **finalizable_hash = hash_table->table;
4367 mword finalizable_hash_size = hash_table->size;
4369 if (no_finalize)
4370 return;
4371 for (i = 0; i < finalizable_hash_size; ++i) {
4372 prev = NULL;
4373 for (entry = finalizable_hash [i]; entry;) {
4374 MonoObject *object = finalize_entry_get_object (entry);
4375 gboolean bridge_bit = finalize_entry_get_bridge_bit (entry);
4377 if ((char*)object >= start && (char*)object < end && !major_collector.is_object_live ((char*)object)) {
4378 gboolean is_fin_ready = object_is_fin_ready (object);
4379 char *copy = (char*)object;
4380 copy_func ((void**)&copy, queue);
4381 if (is_fin_ready) {
4382 char *from;
4383 FinalizeEntry *next;
4384 /* Make it survive */
4385 from = (char*)object;
4386 finalize_entry_set_object (entry, (MonoObject*)copy, bridge_bit);
4387 /* remove and put in fin_ready_list */
4388 if (prev)
4389 prev->next = entry->next;
4390 else
4391 finalizable_hash [i] = entry->next;
4392 next = entry->next;
4393 num_ready_finalizers++;
4394 hash_table->num_registered--;
4395 queue_finalization_entry (entry);
4396 DEBUG (5, fprintf (gc_debug_file, "Queueing object for finalization: %p (%s) (was at %p) (%d/%d)\n", object, safe_name (object), from, num_ready_finalizers, hash_table->num_registered));
4397 entry = next;
4398 continue;
4399 } else {
4400 char *from = (char*)object;
4401 if (hash_table == &minor_finalizable_hash && !ptr_in_nursery (copy)) {
4402 FinalizeEntry *next = entry->next;
4403 unsigned int major_hash;
4404 /* remove from the list */
4405 if (prev)
4406 prev->next = entry->next;
4407 else
4408 finalizable_hash [i] = entry->next;
4409 hash_table->num_registered--;
4411 finalize_entry_set_object (entry, (MonoObject*)copy, bridge_bit);
4413 /* insert it into the major hash */
4414 rehash_fin_table_if_necessary (&major_finalizable_hash);
4415 major_hash = mono_object_hash ((MonoObject*) copy) %
4416 major_finalizable_hash.size;
4417 entry->next = major_finalizable_hash.table [major_hash];
4418 major_finalizable_hash.table [major_hash] = entry;
4419 major_finalizable_hash.num_registered++;
4421 DEBUG (5, fprintf (gc_debug_file, "Promoting finalization of object %p (%s) (was at %p) to major table\n", copy, safe_name (copy), from));
4423 entry = next;
4424 continue;
4425 } else {
4426 /* update pointer */
4427 DEBUG (5, fprintf (gc_debug_file, "Updating object for finalization: %p (%s) (was at %p)\n", object, safe_name (object), from));
4428 finalize_entry_set_object (entry, (MonoObject*)copy, bridge_bit);
4432 prev = entry;
4433 entry = entry->next;
4438 static int
4439 object_is_reachable (char *object, char *start, char *end)
4441 /*This happens for non nursery objects during minor collections. We just treat all objects as alive.*/
4442 if (object < start || object >= end)
4443 return TRUE;
4444 return !object_is_fin_ready (object) || major_collector.is_object_live (object);
4447 gboolean
4448 mono_sgen_object_is_live (void *obj)
4450 if (ptr_in_nursery (obj))
4451 return object_is_pinned (obj);
4452 if (current_collection_generation == GENERATION_NURSERY)
4453 return FALSE;
4454 return major_collector.is_object_live (obj);
4457 /* LOCKING: requires that the GC lock is held */
4458 static void
4459 null_ephemerons_for_domain (MonoDomain *domain)
4461 EphemeronLinkNode *current = ephemeron_list, *prev = NULL;
4463 while (current) {
4464 MonoObject *object = (MonoObject*)current->array;
4466 if (object && !object->vtable) {
4467 EphemeronLinkNode *tmp = current;
4469 if (prev)
4470 prev->next = current->next;
4471 else
4472 ephemeron_list = current->next;
4474 current = current->next;
4475 mono_sgen_free_internal (tmp, INTERNAL_MEM_EPHEMERON_LINK);
4476 } else {
4477 prev = current;
4478 current = current->next;
4483 /* LOCKING: requires that the GC lock is held */
4484 static void
4485 clear_unreachable_ephemerons (CopyOrMarkObjectFunc copy_func, char *start, char *end, GrayQueue *queue)
4487 int was_in_nursery, was_promoted;
4488 EphemeronLinkNode *current = ephemeron_list, *prev = NULL;
4489 MonoArray *array;
4490 Ephemeron *cur, *array_end;
4491 char *tombstone;
4493 while (current) {
4494 char *object = current->array;
4496 if (!object_is_reachable (object, start, end)) {
4497 EphemeronLinkNode *tmp = current;
4499 DEBUG (5, fprintf (gc_debug_file, "Dead Ephemeron array at %p\n", object));
4501 if (prev)
4502 prev->next = current->next;
4503 else
4504 ephemeron_list = current->next;
4506 current = current->next;
4507 mono_sgen_free_internal (tmp, INTERNAL_MEM_EPHEMERON_LINK);
4509 continue;
4512 was_in_nursery = ptr_in_nursery (object);
4513 copy_func ((void**)&object, queue);
4514 current->array = object;
4516 /*The array was promoted, add global remsets for key/values left behind in nursery.*/
4517 was_promoted = was_in_nursery && !ptr_in_nursery (object);
4519 DEBUG (5, fprintf (gc_debug_file, "Clearing unreachable entries for ephemeron array at %p\n", object));
4521 array = (MonoArray*)object;
4522 cur = mono_array_addr (array, Ephemeron, 0);
4523 array_end = cur + mono_array_length_fast (array);
4524 tombstone = (char*)((MonoVTable*)LOAD_VTABLE (object))->domain->ephemeron_tombstone;
4526 for (; cur < array_end; ++cur) {
4527 char *key = (char*)cur->key;
4529 if (!key || key == tombstone)
4530 continue;
4532 DEBUG (5, fprintf (gc_debug_file, "[%td] key %p (%s) value %p (%s)\n", cur - mono_array_addr (array, Ephemeron, 0),
4533 key, object_is_reachable (key, start, end) ? "reachable" : "unreachable",
4534 cur->value, cur->value && object_is_reachable (cur->value, start, end) ? "reachable" : "unreachable"));
4536 if (!object_is_reachable (key, start, end)) {
4537 cur->key = tombstone;
4538 cur->value = NULL;
4539 continue;
4542 if (was_promoted) {
4543 if (ptr_in_nursery (key)) {/*key was not promoted*/
4544 DEBUG (5, fprintf (gc_debug_file, "\tAdded remset to key %p\n", key));
4545 mono_sgen_add_to_global_remset (&cur->key);
4547 if (ptr_in_nursery (cur->value)) {/*value was not promoted*/
4548 DEBUG (5, fprintf (gc_debug_file, "\tAdded remset to value %p\n", cur->value));
4549 mono_sgen_add_to_global_remset (&cur->value);
4553 prev = current;
4554 current = current->next;
4558 /* LOCKING: requires that the GC lock is held */
4559 static int
4560 mark_ephemerons_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, GrayQueue *queue)
4562 int nothing_marked = 1;
4563 EphemeronLinkNode *current = ephemeron_list;
4564 MonoArray *array;
4565 Ephemeron *cur, *array_end;
4566 char *tombstone;
4568 for (current = ephemeron_list; current; current = current->next) {
4569 char *object = current->array;
4570 DEBUG (5, fprintf (gc_debug_file, "Ephemeron array at %p\n", object));
4573 For now we process all ephemerons during all collections.
4574 Ideally we should use remset information to partially scan those
4575 arrays.
4576 We already emit write barriers for Ephemeron fields, it's
4577 just that we don't process them.
4579 /*if (object < start || object >= end)
4580 continue;*/
4582 /*It has to be alive*/
4583 if (!object_is_reachable (object, start, end)) {
4584 DEBUG (5, fprintf (gc_debug_file, "\tnot reachable\n"));
4585 continue;
4588 copy_func ((void**)&object, queue);
4590 array = (MonoArray*)object;
4591 cur = mono_array_addr (array, Ephemeron, 0);
4592 array_end = cur + mono_array_length_fast (array);
4593 tombstone = (char*)((MonoVTable*)LOAD_VTABLE (object))->domain->ephemeron_tombstone;
4595 for (; cur < array_end; ++cur) {
4596 char *key = cur->key;
4598 if (!key || key == tombstone)
4599 continue;
4601 DEBUG (5, fprintf (gc_debug_file, "[%td] key %p (%s) value %p (%s)\n", cur - mono_array_addr (array, Ephemeron, 0),
4602 key, object_is_reachable (key, start, end) ? "reachable" : "unreachable",
4603 cur->value, cur->value && object_is_reachable (cur->value, start, end) ? "reachable" : "unreachable"));
4605 if (object_is_reachable (key, start, end)) {
4606 char *value = cur->value;
4608 copy_func ((void**)&cur->key, queue);
4609 if (value) {
4610 if (!object_is_reachable (value, start, end))
4611 nothing_marked = 0;
4612 copy_func ((void**)&cur->value, queue);
4618 DEBUG (5, fprintf (gc_debug_file, "Ephemeron run finished. Is it done %d\n", nothing_marked));
4619 return nothing_marked;
4622 /* LOCKING: requires that the GC lock is held */
4623 static void
4624 null_link_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, gboolean before_finalization, GrayQueue *queue)
4626 DisappearingLinkHashTable *hash = get_dislink_hash_table (generation);
4627 DisappearingLink **disappearing_link_hash = hash->table;
4628 int disappearing_link_hash_size = hash->size;
4629 DisappearingLink *entry, *prev;
4630 int i;
4631 if (!hash->num_links)
4632 return;
4633 for (i = 0; i < disappearing_link_hash_size; ++i) {
4634 prev = NULL;
4635 for (entry = disappearing_link_hash [i]; entry;) {
4636 char *object;
4637 gboolean track = DISLINK_TRACK (entry);
4640 * Tracked references are processed after
4641 * finalization handling whereas standard weak
4642 * references are processed before. If an
4643 * object is still not marked after finalization
4644 * handling it means that it either doesn't have
4645 * a finalizer or the finalizer has already run,
4646 * so we must null a tracking reference.
4648 if (track == before_finalization) {
4649 prev = entry;
4650 entry = entry->next;
4651 continue;
4654 object = DISLINK_OBJECT (entry);
4656 if (object >= start && object < end && !major_collector.is_object_live (object)) {
4657 if (object_is_fin_ready (object)) {
4658 void **p = entry->link;
4659 DisappearingLink *old;
4660 *p = NULL;
4661 /* remove from list */
4662 if (prev)
4663 prev->next = entry->next;
4664 else
4665 disappearing_link_hash [i] = entry->next;
4666 DEBUG (5, fprintf (gc_debug_file, "Dislink nullified at %p to GCed object %p\n", p, object));
4667 old = entry->next;
4668 mono_sgen_free_internal (entry, INTERNAL_MEM_DISLINK);
4669 entry = old;
4670 hash->num_links--;
4671 continue;
4672 } else {
4673 char *copy = object;
4674 copy_func ((void**)&copy, queue);
4676 /* Update pointer if it's moved. If the object
4677 * has been moved out of the nursery, we need to
4678 * remove the link from the minor hash table to
4679 * the major one.
4681 * FIXME: what if an object is moved earlier?
4684 if (hash == &minor_disappearing_link_hash && !ptr_in_nursery (copy)) {
4685 void **link = entry->link;
4686 DisappearingLink *old;
4687 /* remove from list */
4688 if (prev)
4689 prev->next = entry->next;
4690 else
4691 disappearing_link_hash [i] = entry->next;
4692 old = entry->next;
4693 mono_sgen_free_internal (entry, INTERNAL_MEM_DISLINK);
4694 entry = old;
4695 hash->num_links--;
4697 add_or_remove_disappearing_link ((MonoObject*)copy, link,
4698 track, GENERATION_OLD);
4700 DEBUG (5, fprintf (gc_debug_file, "Upgraded dislink at %p to major because object %p moved to %p\n", link, object, copy));
4702 continue;
4703 } else {
4704 *entry->link = HIDE_POINTER (copy, track);
4705 DEBUG (5, fprintf (gc_debug_file, "Updated dislink at %p to %p\n", entry->link, DISLINK_OBJECT (entry)));
4709 prev = entry;
4710 entry = entry->next;
4715 /* LOCKING: requires that the GC lock is held */
4716 static void
4717 null_links_for_domain (MonoDomain *domain, int generation)
4719 DisappearingLinkHashTable *hash = get_dislink_hash_table (generation);
4720 DisappearingLink **disappearing_link_hash = hash->table;
4721 int disappearing_link_hash_size = hash->size;
4722 DisappearingLink *entry, *prev;
4723 int i;
4724 for (i = 0; i < disappearing_link_hash_size; ++i) {
4725 prev = NULL;
4726 for (entry = disappearing_link_hash [i]; entry; ) {
4727 char *object = DISLINK_OBJECT (entry);
4728 if (object && !((MonoObject*)object)->vtable) {
4729 DisappearingLink *next = entry->next;
4731 if (prev)
4732 prev->next = next;
4733 else
4734 disappearing_link_hash [i] = next;
4736 if (*(entry->link)) {
4737 *(entry->link) = NULL;
4738 g_warning ("Disappearing link %p not freed", entry->link);
4739 } else {
4740 mono_sgen_free_internal (entry, INTERNAL_MEM_DISLINK);
4743 entry = next;
4744 continue;
4746 prev = entry;
4747 entry = entry->next;
4752 static void
4753 remove_finalizers_for_domain (MonoDomain *domain, int generation)
4755 int i;
4756 FinalizeEntryHashTable *hash_table = get_finalize_entry_hash_table (generation);
4757 mword finalizable_hash_size = hash_table->size;
4758 FinalizeEntry **table = hash_table->table;
4760 for (i = 0; i < finalizable_hash_size; ++i) {
4761 FinalizeEntry *entry, *prev = NULL;
4763 for (entry = table [i]; entry;) {
4764 MonoObject *object = finalize_entry_get_object (entry);
4765 if (mono_object_domain (object) == domain) {
4766 FinalizeEntry *next = entry->next;
4768 if (prev)
4769 prev->next = next;
4770 else
4771 table [i] = next;
4772 hash_table->num_registered--;
4774 DEBUG (5, fprintf (gc_debug_file, "Collecting object for finalization: %p (%s) (%d/%d)\n", object, safe_name (object), num_ready_finalizers, hash_table->num_registered));
4775 mono_sgen_free_internal (entry, INTERNAL_MEM_FINALIZE_ENTRY);
4776 entry = next;
4777 continue;
4779 prev = entry;
4780 entry = entry->next;
4786 /* LOCKING: requires that the GC lock is held */
4787 static int
4788 finalizers_for_domain (MonoDomain *domain, MonoObject **out_array, int out_size,
4789 FinalizeEntryHashTable *hash_table)
4791 FinalizeEntry **finalizable_hash = hash_table->table;
4792 mword finalizable_hash_size = hash_table->size;
4793 FinalizeEntry *entry, *prev;
4794 int i, count;
4796 if (no_finalize || !out_size || !out_array)
4797 return 0;
4798 count = 0;
4799 for (i = 0; i < finalizable_hash_size; ++i) {
4800 prev = NULL;
4801 for (entry = finalizable_hash [i]; entry;) {
4802 MonoObject *object = finalize_entry_get_object (entry);
4803 if (mono_object_domain (object) == domain) {
4804 FinalizeEntry *next;
4805 /* remove and put in out_array */
4806 if (prev)
4807 prev->next = entry->next;
4808 else
4809 finalizable_hash [i] = entry->next;
4810 next = entry->next;
4811 hash_table->num_registered--;
4812 out_array [count ++] = object;
4813 DEBUG (5, fprintf (gc_debug_file, "Collecting object for finalization: %p (%s) (%d/%d)\n", object, safe_name (object), num_ready_finalizers, hash_table->num_registered));
4814 entry = next;
4815 if (count == out_size)
4816 return count;
4817 continue;
4819 prev = entry;
4820 entry = entry->next;
4823 return count;
4827 * mono_gc_finalizers_for_domain:
4828 * @domain: the unloading appdomain
4829 * @out_array: output array
4830 * @out_size: size of output array
4832 * Store inside @out_array up to @out_size objects that belong to the unloading
4833 * appdomain @domain. Returns the number of stored items. Can be called repeteadly
4834 * until it returns 0.
4835 * The items are removed from the finalizer data structure, so the caller is supposed
4836 * to finalize them.
4837 * @out_array should be on the stack to allow the GC to know the objects are still alive.
4840 mono_gc_finalizers_for_domain (MonoDomain *domain, MonoObject **out_array, int out_size)
4842 int result;
4844 LOCK_GC;
4845 result = finalizers_for_domain (domain, out_array, out_size, &minor_finalizable_hash);
4846 if (result < out_size) {
4847 result += finalizers_for_domain (domain, out_array + result, out_size - result,
4848 &major_finalizable_hash);
4850 UNLOCK_GC;
4852 return result;
4855 static void
4856 register_for_finalization (MonoObject *obj, void *user_data, int generation)
4858 FinalizeEntryHashTable *hash_table = get_finalize_entry_hash_table (generation);
4859 FinalizeEntry **finalizable_hash;
4860 mword finalizable_hash_size;
4861 FinalizeEntry *entry, *prev;
4862 unsigned int hash;
4863 if (no_finalize)
4864 return;
4865 g_assert (user_data == NULL || user_data == mono_gc_run_finalize);
4866 hash = mono_object_hash (obj);
4867 LOCK_GC;
4868 rehash_fin_table_if_necessary (hash_table);
4869 finalizable_hash = hash_table->table;
4870 finalizable_hash_size = hash_table->size;
4871 hash %= finalizable_hash_size;
4872 prev = NULL;
4873 for (entry = finalizable_hash [hash]; entry; entry = entry->next) {
4874 if (finalize_entry_get_object (entry) == obj) {
4875 if (!user_data) {
4876 /* remove from the list */
4877 if (prev)
4878 prev->next = entry->next;
4879 else
4880 finalizable_hash [hash] = entry->next;
4881 hash_table->num_registered--;
4882 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));
4883 mono_sgen_free_internal (entry, INTERNAL_MEM_FINALIZE_ENTRY);
4885 UNLOCK_GC;
4886 return;
4888 prev = entry;
4890 if (!user_data) {
4891 /* request to deregister, but already out of the list */
4892 UNLOCK_GC;
4893 return;
4895 entry = mono_sgen_alloc_internal (INTERNAL_MEM_FINALIZE_ENTRY);
4896 finalize_entry_set_object (entry, obj, FALSE);
4897 entry->next = finalizable_hash [hash];
4898 finalizable_hash [hash] = entry;
4899 hash_table->num_registered++;
4900 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)));
4901 UNLOCK_GC;
4904 void
4905 mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
4907 if (ptr_in_nursery (obj))
4908 register_for_finalization (obj, user_data, GENERATION_NURSERY);
4909 else
4910 register_for_finalization (obj, user_data, GENERATION_OLD);
4913 static void
4914 rehash_dislink (DisappearingLinkHashTable *hash_table)
4916 DisappearingLink **disappearing_link_hash = hash_table->table;
4917 int disappearing_link_hash_size = hash_table->size;
4918 int i;
4919 unsigned int hash;
4920 DisappearingLink **new_hash;
4921 DisappearingLink *entry, *next;
4922 int new_size = g_spaced_primes_closest (hash_table->num_links);
4924 new_hash = mono_sgen_alloc_internal_dynamic (new_size * sizeof (DisappearingLink*), INTERNAL_MEM_DISLINK_TABLE);
4925 for (i = 0; i < disappearing_link_hash_size; ++i) {
4926 for (entry = disappearing_link_hash [i]; entry; entry = next) {
4927 hash = mono_aligned_addr_hash (entry->link) % new_size;
4928 next = entry->next;
4929 entry->next = new_hash [hash];
4930 new_hash [hash] = entry;
4933 mono_sgen_free_internal_dynamic (disappearing_link_hash,
4934 disappearing_link_hash_size * sizeof (DisappearingLink*), INTERNAL_MEM_DISLINK_TABLE);
4935 hash_table->table = new_hash;
4936 hash_table->size = new_size;
4939 /* LOCKING: assumes the GC lock is held */
4940 static void
4941 add_or_remove_disappearing_link (MonoObject *obj, void **link, gboolean track, int generation)
4943 DisappearingLinkHashTable *hash_table = get_dislink_hash_table (generation);
4944 DisappearingLink *entry, *prev;
4945 unsigned int hash;
4946 DisappearingLink **disappearing_link_hash = hash_table->table;
4947 int disappearing_link_hash_size = hash_table->size;
4949 if (hash_table->num_links >= disappearing_link_hash_size * 2) {
4950 rehash_dislink (hash_table);
4951 disappearing_link_hash = hash_table->table;
4952 disappearing_link_hash_size = hash_table->size;
4954 /* FIXME: add check that link is not in the heap */
4955 hash = mono_aligned_addr_hash (link) % disappearing_link_hash_size;
4956 entry = disappearing_link_hash [hash];
4957 prev = NULL;
4958 for (; entry; entry = entry->next) {
4959 /* link already added */
4960 if (link == entry->link) {
4961 /* NULL obj means remove */
4962 if (obj == NULL) {
4963 if (prev)
4964 prev->next = entry->next;
4965 else
4966 disappearing_link_hash [hash] = entry->next;
4967 hash_table->num_links--;
4968 DEBUG (5, fprintf (gc_debug_file, "Removed dislink %p (%d) from %s table\n", entry, hash_table->num_links, generation_name (generation)));
4969 mono_sgen_free_internal (entry, INTERNAL_MEM_DISLINK);
4970 *link = NULL;
4971 } else {
4972 *link = HIDE_POINTER (obj, track); /* we allow the change of object */
4974 return;
4976 prev = entry;
4978 if (obj == NULL)
4979 return;
4980 entry = mono_sgen_alloc_internal (INTERNAL_MEM_DISLINK);
4981 *link = HIDE_POINTER (obj, track);
4982 entry->link = link;
4983 entry->next = disappearing_link_hash [hash];
4984 disappearing_link_hash [hash] = entry;
4985 hash_table->num_links++;
4986 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)));
4989 /* LOCKING: assumes the GC lock is held */
4990 static void
4991 mono_gc_register_disappearing_link (MonoObject *obj, void **link, gboolean track)
4993 add_or_remove_disappearing_link (NULL, link, FALSE, GENERATION_NURSERY);
4994 add_or_remove_disappearing_link (NULL, link, FALSE, GENERATION_OLD);
4995 if (obj) {
4996 if (ptr_in_nursery (obj))
4997 add_or_remove_disappearing_link (obj, link, track, GENERATION_NURSERY);
4998 else
4999 add_or_remove_disappearing_link (obj, link, track, GENERATION_OLD);
5004 mono_gc_invoke_finalizers (void)
5006 FinalizeEntry *entry = NULL;
5007 gboolean entry_is_critical = FALSE;
5008 int count = 0;
5009 void *obj;
5010 /* FIXME: batch to reduce lock contention */
5011 while (fin_ready_list || critical_fin_list) {
5012 LOCK_GC;
5014 if (entry) {
5015 FinalizeEntry **list = entry_is_critical ? &critical_fin_list : &fin_ready_list;
5017 /* We have finalized entry in the last
5018 interation, now we need to remove it from
5019 the list. */
5020 if (*list == entry)
5021 *list = entry->next;
5022 else {
5023 FinalizeEntry *e = *list;
5024 while (e->next != entry)
5025 e = e->next;
5026 e->next = entry->next;
5028 mono_sgen_free_internal (entry, INTERNAL_MEM_FINALIZE_ENTRY);
5029 entry = NULL;
5032 /* Now look for the first non-null entry. */
5033 for (entry = fin_ready_list; entry && !finalize_entry_get_object (entry); entry = entry->next)
5035 if (entry) {
5036 entry_is_critical = FALSE;
5037 } else {
5038 entry_is_critical = TRUE;
5039 for (entry = critical_fin_list; entry && !finalize_entry_get_object (entry); entry = entry->next)
5043 if (entry) {
5044 obj = finalize_entry_get_object (entry);
5045 g_assert (obj);
5046 num_ready_finalizers--;
5047 finalize_entry_set_object (entry, NULL, FALSE);
5048 DEBUG (7, fprintf (gc_debug_file, "Finalizing object %p (%s)\n", obj, safe_name (obj)));
5051 UNLOCK_GC;
5053 if (!entry)
5054 break;
5056 g_assert (finalize_entry_get_object (entry) == NULL);
5057 count++;
5058 /* the object is on the stack so it is pinned */
5059 /*g_print ("Calling finalizer for object: %p (%s)\n", entry->object, safe_name (entry->object));*/
5060 mono_gc_run_finalize (obj, NULL);
5062 g_assert (!entry);
5063 return count;
5066 gboolean
5067 mono_gc_pending_finalizers (void)
5069 return fin_ready_list || critical_fin_list;
5072 /* Negative value to remove */
5073 void
5074 mono_gc_add_memory_pressure (gint64 value)
5076 /* FIXME: Use interlocked functions */
5077 LOCK_GC;
5078 memory_pressure += value;
5079 UNLOCK_GC;
5082 void
5083 mono_sgen_register_major_sections_alloced (int num_sections)
5085 minor_collection_sections_alloced += num_sections;
5088 mword
5089 mono_sgen_get_minor_collection_allowance (void)
5091 return minor_collection_allowance;
5095 * ######################################################################
5096 * ######## registered roots support
5097 * ######################################################################
5100 static void
5101 rehash_roots (gboolean pinned)
5103 int i;
5104 unsigned int hash;
5105 RootRecord **new_hash;
5106 RootRecord *entry, *next;
5107 int new_size;
5109 new_size = g_spaced_primes_closest (num_roots_entries [pinned]);
5110 new_hash = mono_sgen_alloc_internal_dynamic (new_size * sizeof (RootRecord*), INTERNAL_MEM_ROOTS_TABLE);
5111 for (i = 0; i < roots_hash_size [pinned]; ++i) {
5112 for (entry = roots_hash [pinned][i]; entry; entry = next) {
5113 hash = mono_aligned_addr_hash (entry->start_root) % new_size;
5114 next = entry->next;
5115 entry->next = new_hash [hash];
5116 new_hash [hash] = entry;
5119 mono_sgen_free_internal_dynamic (roots_hash [pinned], roots_hash_size [pinned] * sizeof (RootRecord*), INTERNAL_MEM_ROOTS_TABLE);
5120 roots_hash [pinned] = new_hash;
5121 roots_hash_size [pinned] = new_size;
5124 static RootRecord*
5125 find_root (int root_type, char *start, guint32 addr_hash)
5127 RootRecord *new_root;
5129 guint32 hash = addr_hash % roots_hash_size [root_type];
5130 for (new_root = roots_hash [root_type][hash]; new_root; new_root = new_root->next) {
5131 /* we allow changing the size and the descriptor (for thread statics etc) */
5132 if (new_root->start_root == start) {
5133 return new_root;
5137 return NULL;
5141 * We do not coalesce roots.
5143 static int
5144 mono_gc_register_root_inner (char *start, size_t size, void *descr, int root_type)
5146 RootRecord *new_root;
5147 unsigned int hash, addr_hash = mono_aligned_addr_hash (start);
5148 int i;
5149 LOCK_GC;
5150 for (i = 0; i < ROOT_TYPE_NUM; ++i) {
5151 if (num_roots_entries [i] >= roots_hash_size [i] * 2)
5152 rehash_roots (i);
5154 for (i = 0; i < ROOT_TYPE_NUM; ++i) {
5155 new_root = find_root (i, start, addr_hash);
5156 /* we allow changing the size and the descriptor (for thread statics etc) */
5157 if (new_root) {
5158 size_t old_size = new_root->end_root - new_root->start_root;
5159 new_root->end_root = new_root->start_root + size;
5160 g_assert (((new_root->root_desc != 0) && (descr != NULL)) ||
5161 ((new_root->root_desc == 0) && (descr == NULL)));
5162 new_root->root_desc = (mword)descr;
5163 roots_size += size;
5164 roots_size -= old_size;
5165 UNLOCK_GC;
5166 return TRUE;
5169 new_root = mono_sgen_alloc_internal (INTERNAL_MEM_ROOT_RECORD);
5170 if (new_root) {
5171 new_root->start_root = start;
5172 new_root->end_root = new_root->start_root + size;
5173 new_root->root_desc = (mword)descr;
5174 roots_size += size;
5175 hash = addr_hash % roots_hash_size [root_type];
5176 num_roots_entries [root_type]++;
5177 new_root->next = roots_hash [root_type] [hash];
5178 roots_hash [root_type][hash] = new_root;
5179 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));
5180 } else {
5181 UNLOCK_GC;
5182 return FALSE;
5184 UNLOCK_GC;
5185 return TRUE;
5189 mono_gc_register_root (char *start, size_t size, void *descr)
5191 return mono_gc_register_root_inner (start, size, descr, descr ? ROOT_TYPE_NORMAL : ROOT_TYPE_PINNED);
5195 mono_gc_register_root_wbarrier (char *start, size_t size, void *descr)
5197 return mono_gc_register_root_inner (start, size, descr, ROOT_TYPE_WBARRIER);
5200 void
5201 mono_gc_deregister_root (char* addr)
5203 RootRecord *tmp, *prev;
5204 unsigned int hash, addr_hash = mono_aligned_addr_hash (addr);
5205 int root_type;
5207 LOCK_GC;
5208 for (root_type = 0; root_type < ROOT_TYPE_NUM; ++root_type) {
5209 hash = addr_hash % roots_hash_size [root_type];
5210 tmp = roots_hash [root_type][hash];
5211 prev = NULL;
5212 while (tmp) {
5213 if (tmp->start_root == (char*)addr) {
5214 if (prev)
5215 prev->next = tmp->next;
5216 else
5217 roots_hash [root_type][hash] = tmp->next;
5218 roots_size -= (tmp->end_root - tmp->start_root);
5219 num_roots_entries [root_type]--;
5220 DEBUG (3, fprintf (gc_debug_file, "Removed root %p for range: %p-%p\n", tmp, tmp->start_root, tmp->end_root));
5221 mono_sgen_free_internal (tmp, INTERNAL_MEM_ROOT_RECORD);
5222 break;
5224 prev = tmp;
5225 tmp = tmp->next;
5228 UNLOCK_GC;
5232 * ######################################################################
5233 * ######## Thread handling (stop/start code)
5234 * ######################################################################
5237 /* FIXME: handle large/small config */
5238 #define HASH_PTHREAD_T(id) (((unsigned int)(id) >> 4) * 2654435761u)
5240 static SgenThreadInfo* thread_table [THREAD_HASH_SIZE];
5242 #if USE_SIGNAL_BASED_START_STOP_WORLD
5244 static MonoSemType suspend_ack_semaphore;
5245 static MonoSemType *suspend_ack_semaphore_ptr;
5246 static unsigned int global_stop_count = 0;
5248 static sigset_t suspend_signal_mask;
5249 static mword cur_thread_regs [ARCH_NUM_REGS] = {0};
5251 /* LOCKING: assumes the GC lock is held */
5252 SgenThreadInfo**
5253 mono_sgen_get_thread_table (void)
5255 return thread_table;
5258 SgenThreadInfo*
5259 mono_sgen_thread_info_lookup (ARCH_THREAD_TYPE id)
5261 unsigned int hash = HASH_PTHREAD_T (id) % THREAD_HASH_SIZE;
5262 SgenThreadInfo *info;
5264 info = thread_table [hash];
5265 while (info && !ARCH_THREAD_EQUALS (info->id, id)) {
5266 info = info->next;
5268 return info;
5271 static void
5272 update_current_thread_stack (void *start)
5274 void *ptr = cur_thread_regs;
5275 SgenThreadInfo *info = mono_sgen_thread_info_lookup (ARCH_GET_THREAD ());
5277 info->stack_start = align_pointer (&ptr);
5278 g_assert (info->stack_start >= info->stack_start_limit && info->stack_start < info->stack_end);
5279 ARCH_STORE_REGS (ptr);
5280 info->stopped_regs = ptr;
5281 if (gc_callbacks.thread_suspend_func)
5282 gc_callbacks.thread_suspend_func (info->runtime_data, NULL);
5286 * Define this and use the "xdomain-checks" MONO_GC_DEBUG option to
5287 * have cross-domain checks in the write barrier.
5289 //#define XDOMAIN_CHECKS_IN_WBARRIER
5291 #ifndef SGEN_BINARY_PROTOCOL
5292 #ifndef HEAVY_STATISTICS
5293 #define MANAGED_ALLOCATION
5294 #ifndef XDOMAIN_CHECKS_IN_WBARRIER
5295 #define MANAGED_WBARRIER
5296 #endif
5297 #endif
5298 #endif
5300 static gboolean
5301 is_ip_in_managed_allocator (MonoDomain *domain, gpointer ip);
5303 void
5304 mono_sgen_wait_for_suspend_ack (int count)
5306 int i, result;
5308 for (i = 0; i < count; ++i) {
5309 while ((result = MONO_SEM_WAIT (suspend_ack_semaphore_ptr)) != 0) {
5310 if (errno != EINTR) {
5311 g_error ("MONO_SEM_WAIT FAILED with %d errno %d (%s)", result, errno, strerror (errno));
5317 static int
5318 restart_threads_until_none_in_managed_allocator (void)
5320 SgenThreadInfo *info;
5321 int i, num_threads_died = 0;
5322 int sleep_duration = -1;
5324 for (;;) {
5325 int restart_count = 0, restarted_count = 0;
5326 /* restart all threads that stopped in the
5327 allocator */
5328 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5329 for (info = thread_table [i]; info; info = info->next) {
5330 gboolean result;
5332 if (info->skip || info->gc_disabled)
5333 continue;
5334 if (!info->stack_start || info->in_critical_region ||
5335 is_ip_in_managed_allocator (info->stopped_domain, info->stopped_ip)) {
5336 binary_protocol_thread_restart ((gpointer)info->id);
5337 #if defined(__MACH__) && MONO_MACH_ARCH_SUPPORTED
5338 result = thread_resume (pthread_mach_thread_np (info->id)) == KERN_SUCCESS;
5339 #else
5340 result = mono_sgen_pthread_kill (info, restart_signal_num) == 0;
5341 #endif
5342 if (result) {
5343 ++restart_count;
5344 } else {
5345 info->skip = 1;
5347 } else {
5348 /* we set the stopped_ip to
5349 NULL for threads which
5350 we're not restarting so
5351 that we can easily identify
5352 the others */
5353 info->stopped_ip = NULL;
5354 info->stopped_domain = NULL;
5358 /* if no threads were restarted, we're done */
5359 if (restart_count == 0)
5360 break;
5362 #if defined(__MACH__) && MONO_MACH_ARCH_SUPPORTED
5363 /* mach thread_resume is synchronous so we dont need to wait for them */
5364 #else
5365 /* wait for the threads to signal their restart */
5366 mono_sgen_wait_for_suspend_ack (restart_count);
5367 #endif
5369 if (sleep_duration < 0) {
5370 sched_yield ();
5371 sleep_duration = 0;
5372 } else {
5373 g_usleep (sleep_duration);
5374 sleep_duration += 10;
5377 /* stop them again */
5378 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5379 for (info = thread_table [i]; info; info = info->next) {
5380 gboolean result;
5381 if (info->skip || info->stopped_ip == NULL)
5382 continue;
5383 #if defined(__MACH__) && MONO_MACH_ARCH_SUPPORTED
5384 result = mono_sgen_suspend_thread (info);
5385 #else
5386 result = mono_sgen_pthread_kill (info, suspend_signal_num) == 0;
5387 #endif
5388 if (result) {
5389 ++restarted_count;
5390 } else {
5391 info->skip = 1;
5395 /* some threads might have died */
5396 num_threads_died += restart_count - restarted_count;
5397 #if defined(__MACH__) && MONO_MACH_ARCH_SUPPORTED
5398 /* mach thread_resume is synchronous so we dont need to wait for them */
5399 #else
5400 /* wait for the threads to signal their suspension
5401 again */
5402 mono_sgen_wait_for_suspend_ack (restarted_count);
5403 #endif
5406 return num_threads_died;
5409 /* LOCKING: assumes the GC lock is held (by the stopping thread) */
5410 static void
5411 suspend_handler (int sig, siginfo_t *siginfo, void *context)
5413 SgenThreadInfo *info;
5414 pthread_t id;
5415 int stop_count;
5416 int old_errno = errno;
5417 gpointer regs [ARCH_NUM_REGS];
5418 gpointer stack_start;
5420 id = pthread_self ();
5421 info = mono_sgen_thread_info_lookup (id);
5422 info->stopped_domain = mono_domain_get ();
5423 info->stopped_ip = (gpointer) ARCH_SIGCTX_IP (context);
5424 stop_count = global_stop_count;
5425 /* duplicate signal */
5426 if (0 && info->stop_count == stop_count) {
5427 errno = old_errno;
5428 return;
5430 #ifdef HAVE_KW_THREAD
5431 /* update the remset info in the thread data structure */
5432 info->remset = remembered_set;
5433 #endif
5434 stack_start = (char*) ARCH_SIGCTX_SP (context) - REDZONE_SIZE;
5435 /* If stack_start is not within the limits, then don't set it
5436 in info and we will be restarted. */
5437 if (stack_start >= info->stack_start_limit && info->stack_start <= info->stack_end) {
5438 info->stack_start = stack_start;
5440 ARCH_COPY_SIGCTX_REGS (regs, context);
5441 info->stopped_regs = regs;
5442 } else {
5443 g_assert (!info->stack_start);
5446 /* Notify the JIT */
5447 if (gc_callbacks.thread_suspend_func)
5448 gc_callbacks.thread_suspend_func (info->runtime_data, context);
5450 DEBUG (4, fprintf (gc_debug_file, "Posting suspend_ack_semaphore for suspend from %p %p\n", info, (gpointer)ARCH_GET_THREAD ()));
5451 /* notify the waiting thread */
5452 MONO_SEM_POST (suspend_ack_semaphore_ptr);
5453 info->stop_count = stop_count;
5455 /* wait until we receive the restart signal */
5456 do {
5457 info->signal = 0;
5458 sigsuspend (&suspend_signal_mask);
5459 } while (info->signal != restart_signal_num);
5461 DEBUG (4, fprintf (gc_debug_file, "Posting suspend_ack_semaphore for resume from %p %p\n", info, (gpointer)ARCH_GET_THREAD ()));
5462 /* notify the waiting thread */
5463 MONO_SEM_POST (suspend_ack_semaphore_ptr);
5465 errno = old_errno;
5468 static void
5469 restart_handler (int sig)
5471 SgenThreadInfo *info;
5472 int old_errno = errno;
5474 info = mono_sgen_thread_info_lookup (pthread_self ());
5475 info->signal = restart_signal_num;
5476 DEBUG (4, fprintf (gc_debug_file, "Restart handler in %p %p\n", info, (gpointer)ARCH_GET_THREAD ()));
5478 errno = old_errno;
5481 static void
5482 acquire_gc_locks (void)
5484 LOCK_INTERRUPTION;
5487 static void
5488 release_gc_locks (void)
5490 UNLOCK_INTERRUPTION;
5493 static TV_DECLARE (stop_world_time);
5494 static unsigned long max_pause_usec = 0;
5496 /* LOCKING: assumes the GC lock is held */
5497 static int
5498 stop_world (int generation)
5500 int count, dead;
5502 /*XXX this is the right stop, thought might not be the nicest place to put it*/
5503 mono_sgen_process_togglerefs ();
5505 mono_profiler_gc_event (MONO_GC_EVENT_PRE_STOP_WORLD, generation);
5506 acquire_gc_locks ();
5508 update_current_thread_stack (&count);
5510 global_stop_count++;
5511 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 ()));
5512 TV_GETTIME (stop_world_time);
5513 count = mono_sgen_thread_handshake (suspend_signal_num);
5514 dead = restart_threads_until_none_in_managed_allocator ();
5515 if (count < dead)
5516 g_error ("More threads have died (%d) that been initialy suspended %d", dead, count);
5517 count -= dead;
5519 DEBUG (3, fprintf (gc_debug_file, "world stopped %d thread(s)\n", count));
5520 mono_profiler_gc_event (MONO_GC_EVENT_POST_STOP_WORLD, generation);
5522 last_major_num_sections = major_collector.get_num_major_sections ();
5523 last_los_memory_usage = los_memory_usage;
5524 major_collection_hapenned = FALSE;
5525 return count;
5528 /* LOCKING: assumes the GC lock is held */
5529 static int
5530 restart_world (int generation)
5532 int count, i, num_major_sections;
5533 SgenThreadInfo *info;
5534 TV_DECLARE (end_sw);
5535 TV_DECLARE (end_bridge);
5536 unsigned long usec, bridge_usec;
5538 /* notify the profiler of the leftovers */
5539 if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_GC_MOVES)) {
5540 if (moved_objects_idx) {
5541 mono_profiler_gc_moves (moved_objects, moved_objects_idx);
5542 moved_objects_idx = 0;
5545 mono_profiler_gc_event (MONO_GC_EVENT_PRE_START_WORLD, generation);
5546 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5547 for (info = thread_table [i]; info; info = info->next) {
5548 info->stack_start = NULL;
5549 info->stopped_regs = NULL;
5553 stw_bridge_process ();
5554 release_gc_locks ();
5556 count = mono_sgen_thread_handshake (restart_signal_num);
5557 TV_GETTIME (end_sw);
5558 usec = TV_ELAPSED (stop_world_time, end_sw);
5559 max_pause_usec = MAX (usec, max_pause_usec);
5560 DEBUG (2, fprintf (gc_debug_file, "restarted %d thread(s) (pause time: %d usec, max: %d)\n", count, (int)usec, (int)max_pause_usec));
5561 mono_profiler_gc_event (MONO_GC_EVENT_POST_START_WORLD, generation);
5563 bridge_process ();
5565 TV_GETTIME (end_bridge);
5566 bridge_usec = TV_ELAPSED (end_sw, end_bridge);
5568 num_major_sections = major_collector.get_num_major_sections ();
5569 if (major_collection_hapenned)
5570 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_MAJOR: %s pause %.2fms, bridge %.2fms major %dK/%dK los %dK/%dK",
5571 generation ? "" : "(minor overflow)",
5572 (int)usec / 1000.0f, (int)bridge_usec / 1000.0f,
5573 major_collector.section_size * num_major_sections / 1024,
5574 major_collector.section_size * last_major_num_sections / 1024,
5575 los_memory_usage / 1024,
5576 last_los_memory_usage / 1024);
5577 else
5578 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_MINOR: pause %.2fms, bridge %.2fms promoted %dK major %dK los %dK",
5579 (int)usec / 1000.0f, (int)bridge_usec / 1000.0f,
5580 (num_major_sections - last_major_num_sections) * major_collector.section_size / 1024,
5581 major_collector.section_size * num_major_sections / 1024,
5582 los_memory_usage / 1024);
5584 return count;
5587 #endif /* USE_SIGNAL_BASED_START_STOP_WORLD */
5590 mono_sgen_get_current_collection_generation (void)
5592 return current_collection_generation;
5595 void
5596 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
5598 gc_callbacks = *callbacks;
5601 MonoGCCallbacks *
5602 mono_gc_get_gc_callbacks ()
5604 return &gc_callbacks;
5607 /* Variables holding start/end nursery so it won't have to be passed at every call */
5608 static void *scan_area_arg_start, *scan_area_arg_end;
5610 void
5611 mono_gc_conservatively_scan_area (void *start, void *end)
5613 conservatively_pin_objects_from (start, end, scan_area_arg_start, scan_area_arg_end, PIN_TYPE_STACK);
5616 void*
5617 mono_gc_scan_object (void *obj)
5619 if (current_collection_generation == GENERATION_NURSERY)
5620 major_collector.copy_object (&obj, &gray_queue);
5621 else
5622 major_collector.copy_or_mark_object (&obj, &gray_queue);
5623 return obj;
5627 * Mark from thread stacks and registers.
5629 static void
5630 scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise)
5632 int i;
5633 SgenThreadInfo *info;
5635 scan_area_arg_start = start_nursery;
5636 scan_area_arg_end = end_nursery;
5638 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5639 for (info = thread_table [i]; info; info = info->next) {
5640 if (info->skip) {
5641 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));
5642 continue;
5644 if (info->gc_disabled) {
5645 DEBUG (3, fprintf (gc_debug_file, "GC disabled for thread %p, range: %p-%p, size: %td\n", info, info->stack_start, info->stack_end, (char*)info->stack_end - (char*)info->stack_start));
5646 continue;
5648 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));
5649 if (gc_callbacks.thread_mark_func && !conservative_stack_mark)
5650 gc_callbacks.thread_mark_func (info->runtime_data, info->stack_start, info->stack_end, precise);
5651 else if (!precise)
5652 conservatively_pin_objects_from (info->stack_start, info->stack_end, start_nursery, end_nursery, PIN_TYPE_STACK);
5654 if (!precise)
5655 conservatively_pin_objects_from (info->stopped_regs, info->stopped_regs + ARCH_NUM_REGS,
5656 start_nursery, end_nursery, PIN_TYPE_STACK);
5661 static void
5662 find_pinning_ref_from_thread (char *obj, size_t size)
5664 int i, j;
5665 SgenThreadInfo *info;
5666 char *endobj = obj + size;
5668 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5669 for (info = thread_table [i]; info; info = info->next) {
5670 char **start = (char**)info->stack_start;
5671 if (info->skip)
5672 continue;
5673 while (start < (char**)info->stack_end) {
5674 if (*start >= obj && *start < endobj) {
5675 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));
5677 start++;
5680 for (j = 0; j < ARCH_NUM_REGS; ++j) {
5681 mword w = (mword)info->stopped_regs [j];
5683 if (w >= (mword)obj && w < (mword)obj + size)
5684 DEBUG (0, fprintf (gc_debug_file, "Object %p referenced in saved reg %d of thread %p (id %p)\n", obj, j, info, (gpointer)info->id));
5690 static gboolean
5691 ptr_on_stack (void *ptr)
5693 gpointer stack_start = &stack_start;
5694 SgenThreadInfo *info = mono_sgen_thread_info_lookup (ARCH_GET_THREAD ());
5696 if (ptr >= stack_start && ptr < (gpointer)info->stack_end)
5697 return TRUE;
5698 return FALSE;
5701 static mword*
5702 handle_remset (mword *p, void *start_nursery, void *end_nursery, gboolean global, GrayQueue *queue)
5704 void **ptr;
5705 mword count;
5706 mword desc;
5708 if (global)
5709 HEAVY_STAT (++stat_global_remsets_processed);
5710 else
5711 HEAVY_STAT (++stat_local_remsets_processed);
5713 /* FIXME: exclude stack locations */
5714 switch ((*p) & REMSET_TYPE_MASK) {
5715 case REMSET_LOCATION:
5716 ptr = (void**)(*p);
5717 //__builtin_prefetch (ptr);
5718 if (((void*)ptr < start_nursery || (void*)ptr >= end_nursery)) {
5719 gpointer old = *ptr;
5720 major_collector.copy_object (ptr, queue);
5721 DEBUG (9, fprintf (gc_debug_file, "Overwrote remset at %p with %p\n", ptr, *ptr));
5722 if (old)
5723 binary_protocol_ptr_update (ptr, old, *ptr, (gpointer)LOAD_VTABLE (*ptr), safe_object_get_size (*ptr));
5724 if (!global && *ptr >= start_nursery && *ptr < end_nursery) {
5726 * If the object is pinned, each reference to it from nonpinned objects
5727 * becomes part of the global remset, which can grow very large.
5729 DEBUG (9, fprintf (gc_debug_file, "Add to global remset because of pinning %p (%p %s)\n", ptr, *ptr, safe_name (*ptr)));
5730 mono_sgen_add_to_global_remset (ptr);
5732 } else {
5733 DEBUG (9, fprintf (gc_debug_file, "Skipping remset at %p holding %p\n", ptr, *ptr));
5735 return p + 1;
5736 case REMSET_RANGE:
5737 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
5738 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
5739 return p + 2;
5740 count = p [1];
5741 while (count-- > 0) {
5742 major_collector.copy_object (ptr, queue);
5743 DEBUG (9, fprintf (gc_debug_file, "Overwrote remset at %p with %p (count: %d)\n", ptr, *ptr, (int)count));
5744 if (!global && *ptr >= start_nursery && *ptr < end_nursery)
5745 mono_sgen_add_to_global_remset (ptr);
5746 ++ptr;
5748 return p + 2;
5749 case REMSET_OBJECT:
5750 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
5751 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
5752 return p + 1;
5753 major_collector.minor_scan_object ((char*)ptr, queue);
5754 return p + 1;
5755 case REMSET_VTYPE: {
5756 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
5757 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
5758 return p + 3;
5759 count = p [2];
5760 while (count-- > 0)
5761 ptr = (void**) major_collector.minor_scan_vtype ((char*)ptr, (MonoClass*)p [1], start_nursery, end_nursery, queue);
5762 return p + 3;
5764 default:
5765 g_assert_not_reached ();
5767 return NULL;
5770 #ifdef HEAVY_STATISTICS
5771 static mword*
5772 collect_store_remsets (RememberedSet *remset, mword *bumper)
5774 mword *p = remset->data;
5775 mword last = 0;
5776 mword last1 = 0;
5777 mword last2 = 0;
5779 while (p < remset->store_next) {
5780 switch ((*p) & REMSET_TYPE_MASK) {
5781 case REMSET_LOCATION:
5782 *bumper++ = *p;
5783 if (*p == last)
5784 ++stat_saved_remsets_1;
5785 last = *p;
5786 if (*p == last1 || *p == last2) {
5787 ++stat_saved_remsets_2;
5788 } else {
5789 last2 = last1;
5790 last1 = *p;
5792 p += 1;
5793 break;
5794 case REMSET_RANGE:
5795 p += 2;
5796 break;
5797 case REMSET_OBJECT:
5798 p += 1;
5799 break;
5800 case REMSET_VTYPE:
5801 p += 3;
5802 break;
5803 default:
5804 g_assert_not_reached ();
5808 return bumper;
5811 static void
5812 remset_stats (void)
5814 RememberedSet *remset;
5815 int size = 0;
5816 SgenThreadInfo *info;
5817 int i;
5818 mword *addresses, *bumper, *p, *r;
5820 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5821 for (info = thread_table [i]; info; info = info->next) {
5822 for (remset = info->remset; remset; remset = remset->next)
5823 size += remset->store_next - remset->data;
5826 for (remset = freed_thread_remsets; remset; remset = remset->next)
5827 size += remset->store_next - remset->data;
5828 for (remset = global_remset; remset; remset = remset->next)
5829 size += remset->store_next - remset->data;
5831 bumper = addresses = mono_sgen_alloc_internal_dynamic (sizeof (mword) * size, INTERNAL_MEM_STATISTICS);
5833 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5834 for (info = thread_table [i]; info; info = info->next) {
5835 for (remset = info->remset; remset; remset = remset->next)
5836 bumper = collect_store_remsets (remset, bumper);
5839 for (remset = global_remset; remset; remset = remset->next)
5840 bumper = collect_store_remsets (remset, bumper);
5841 for (remset = freed_thread_remsets; remset; remset = remset->next)
5842 bumper = collect_store_remsets (remset, bumper);
5844 g_assert (bumper <= addresses + size);
5846 stat_store_remsets += bumper - addresses;
5848 sort_addresses ((void**)addresses, bumper - addresses);
5849 p = addresses;
5850 r = addresses + 1;
5851 while (r < bumper) {
5852 if (*r != *p)
5853 *++p = *r;
5854 ++r;
5857 stat_store_remsets_unique += p - addresses;
5859 mono_sgen_free_internal_dynamic (addresses, sizeof (mword) * size, INTERNAL_MEM_STATISTICS);
5861 #endif
5863 static void
5864 clear_thread_store_remset_buffer (SgenThreadInfo *info)
5866 *info->store_remset_buffer_index_addr = 0;
5867 memset (*info->store_remset_buffer_addr, 0, sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
5870 static size_t
5871 remset_byte_size (RememberedSet *remset)
5873 return sizeof (RememberedSet) + (remset->end_set - remset->data) * sizeof (gpointer);
5876 static void
5877 scan_from_remsets (void *start_nursery, void *end_nursery, GrayQueue *queue)
5879 int i;
5880 SgenThreadInfo *info;
5881 RememberedSet *remset;
5882 GenericStoreRememberedSet *store_remset;
5883 mword *p, *next_p, *store_pos;
5885 #ifdef HEAVY_STATISTICS
5886 remset_stats ();
5887 #endif
5889 /* the global one */
5890 for (remset = global_remset; remset; remset = remset->next) {
5891 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));
5892 store_pos = remset->data;
5893 for (p = remset->data; p < remset->store_next; p = next_p) {
5894 void **ptr = (void**)p [0];
5896 /*Ignore previously processed remset.*/
5897 if (!global_remset_location_was_not_added (ptr)) {
5898 next_p = p + 1;
5899 continue;
5902 next_p = handle_remset (p, start_nursery, end_nursery, TRUE, queue);
5905 * Clear global remsets of locations which no longer point to the
5906 * nursery. Otherwise, they could grow indefinitely between major
5907 * collections.
5909 * Since all global remsets are location remsets, we don't need to unmask the pointer.
5911 if (ptr_in_nursery (*ptr)) {
5912 *store_pos ++ = p [0];
5913 HEAVY_STAT (++stat_global_remsets_readded);
5917 /* Truncate the remset */
5918 remset->store_next = store_pos;
5921 /* the generic store ones */
5922 store_remset = generic_store_remsets;
5923 while (store_remset) {
5924 GenericStoreRememberedSet *next = store_remset->next;
5926 for (i = 0; i < STORE_REMSET_BUFFER_SIZE - 1; ++i) {
5927 gpointer addr = store_remset->data [i];
5928 if (addr)
5929 handle_remset ((mword*)&addr, start_nursery, end_nursery, FALSE, queue);
5932 mono_sgen_free_internal (store_remset, INTERNAL_MEM_STORE_REMSET);
5934 store_remset = next;
5936 generic_store_remsets = NULL;
5938 /* the per-thread ones */
5939 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5940 for (info = thread_table [i]; info; info = info->next) {
5941 RememberedSet *next;
5942 int j;
5943 for (remset = info->remset; remset; remset = next) {
5944 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));
5945 for (p = remset->data; p < remset->store_next;)
5946 p = handle_remset (p, start_nursery, end_nursery, FALSE, queue);
5947 remset->store_next = remset->data;
5948 next = remset->next;
5949 remset->next = NULL;
5950 if (remset != info->remset) {
5951 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5952 mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
5955 for (j = 0; j < *info->store_remset_buffer_index_addr; ++j)
5956 handle_remset ((mword*)*info->store_remset_buffer_addr + j + 1, start_nursery, end_nursery, FALSE, queue);
5957 clear_thread_store_remset_buffer (info);
5961 /* the freed thread ones */
5962 while (freed_thread_remsets) {
5963 RememberedSet *next;
5964 remset = freed_thread_remsets;
5965 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));
5966 for (p = remset->data; p < remset->store_next;)
5967 p = handle_remset (p, start_nursery, end_nursery, FALSE, queue);
5968 next = remset->next;
5969 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5970 mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
5971 freed_thread_remsets = next;
5976 * Clear the info in the remembered sets: we're doing a major collection, so
5977 * the per-thread ones are not needed and the global ones will be reconstructed
5978 * during the copy.
5980 static void
5981 clear_remsets (void)
5983 int i;
5984 SgenThreadInfo *info;
5985 RememberedSet *remset, *next;
5987 /* the global list */
5988 for (remset = global_remset; remset; remset = next) {
5989 remset->store_next = remset->data;
5990 next = remset->next;
5991 remset->next = NULL;
5992 if (remset != global_remset) {
5993 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
5994 mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
5997 /* the generic store ones */
5998 while (generic_store_remsets) {
5999 GenericStoreRememberedSet *gs_next = generic_store_remsets->next;
6000 mono_sgen_free_internal (generic_store_remsets, INTERNAL_MEM_STORE_REMSET);
6001 generic_store_remsets = gs_next;
6003 /* the per-thread ones */
6004 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
6005 for (info = thread_table [i]; info; info = info->next) {
6006 for (remset = info->remset; remset; remset = next) {
6007 remset->store_next = remset->data;
6008 next = remset->next;
6009 remset->next = NULL;
6010 if (remset != info->remset) {
6011 DEBUG (3, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
6012 mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
6015 clear_thread_store_remset_buffer (info);
6019 /* the freed thread ones */
6020 while (freed_thread_remsets) {
6021 next = freed_thread_remsets->next;
6022 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", freed_thread_remsets->data));
6023 mono_sgen_free_internal_dynamic (freed_thread_remsets, remset_byte_size (freed_thread_remsets), INTERNAL_MEM_REMSET);
6024 freed_thread_remsets = next;
6029 * Clear the thread local TLAB variables for all threads.
6031 static void
6032 clear_tlabs (void)
6034 SgenThreadInfo *info;
6035 int i;
6037 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
6038 for (info = thread_table [i]; info; info = info->next) {
6039 /* A new TLAB will be allocated when the thread does its first allocation */
6040 *info->tlab_start_addr = NULL;
6041 *info->tlab_next_addr = NULL;
6042 *info->tlab_temp_end_addr = NULL;
6043 *info->tlab_real_end_addr = NULL;
6048 /* LOCKING: assumes the GC lock is held */
6049 static SgenThreadInfo*
6050 gc_register_current_thread (void *addr)
6052 int hash;
6053 SgenThreadInfo* info = malloc (sizeof (SgenThreadInfo));
6054 #ifndef HAVE_KW_THREAD
6055 SgenThreadInfo *__thread_info__ = info;
6056 #endif
6058 if (!info)
6059 return NULL;
6061 memset (info, 0, sizeof (SgenThreadInfo));
6062 #ifndef HAVE_KW_THREAD
6063 info->tlab_start = info->tlab_next = info->tlab_temp_end = info->tlab_real_end = NULL;
6065 g_assert (!pthread_getspecific (thread_info_key));
6066 pthread_setspecific (thread_info_key, info);
6067 #else
6068 thread_info = info;
6069 #endif
6071 info->id = ARCH_GET_THREAD ();
6072 info->stop_count = -1;
6073 info->skip = 0;
6074 info->signal = 0;
6075 info->stack_start = NULL;
6076 info->tlab_start_addr = &TLAB_START;
6077 info->tlab_next_addr = &TLAB_NEXT;
6078 info->tlab_temp_end_addr = &TLAB_TEMP_END;
6079 info->tlab_real_end_addr = &TLAB_REAL_END;
6080 info->store_remset_buffer_addr = &STORE_REMSET_BUFFER;
6081 info->store_remset_buffer_index_addr = &STORE_REMSET_BUFFER_INDEX;
6082 info->stopped_ip = NULL;
6083 info->stopped_domain = NULL;
6084 info->stopped_regs = NULL;
6086 binary_protocol_thread_register ((gpointer)info->id);
6088 #ifdef HAVE_KW_THREAD
6089 tlab_next_addr = &tlab_next;
6090 store_remset_buffer_index_addr = &store_remset_buffer_index;
6091 #endif
6093 #if defined(__MACH__)
6094 info->mach_port = mach_thread_self ();
6095 #endif
6097 #if defined(PLATFORM_ANDROID)
6098 info->android_tid = (gpointer) gettid ();
6099 #endif
6101 /* try to get it with attributes first */
6102 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
6104 size_t size;
6105 void *sstart;
6106 pthread_attr_t attr;
6107 pthread_getattr_np (pthread_self (), &attr);
6108 pthread_attr_getstack (&attr, &sstart, &size);
6109 info->stack_start_limit = sstart;
6110 info->stack_end = (char*)sstart + size;
6111 pthread_attr_destroy (&attr);
6113 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
6114 info->stack_end = (char*)pthread_get_stackaddr_np (pthread_self ());
6115 info->stack_start_limit = (char*)info->stack_end - pthread_get_stacksize_np (pthread_self ());
6116 #else
6118 /* FIXME: we assume the stack grows down */
6119 gsize stack_bottom = (gsize)addr;
6120 stack_bottom += 4095;
6121 stack_bottom &= ~4095;
6122 info->stack_end = (char*)stack_bottom;
6124 #endif
6126 #ifdef HAVE_KW_THREAD
6127 stack_end = info->stack_end;
6128 #endif
6130 /* hash into the table */
6131 hash = HASH_PTHREAD_T (info->id) % THREAD_HASH_SIZE;
6132 info->next = thread_table [hash];
6133 thread_table [hash] = info;
6135 info->remset = alloc_remset (DEFAULT_REMSET_SIZE, info);
6136 pthread_setspecific (remembered_set_key, info->remset);
6137 #ifdef HAVE_KW_THREAD
6138 remembered_set = info->remset;
6139 #endif
6141 STORE_REMSET_BUFFER = mono_sgen_alloc_internal (INTERNAL_MEM_STORE_REMSET);
6142 STORE_REMSET_BUFFER_INDEX = 0;
6144 DEBUG (3, fprintf (gc_debug_file, "registered thread %p (%p) (hash: %d)\n", info, (gpointer)info->id, hash));
6146 if (gc_callbacks.thread_attach_func)
6147 info->runtime_data = gc_callbacks.thread_attach_func ();
6149 return info;
6152 static void
6153 add_generic_store_remset_from_buffer (gpointer *buffer)
6155 GenericStoreRememberedSet *remset = mono_sgen_alloc_internal (INTERNAL_MEM_STORE_REMSET);
6156 memcpy (remset->data, buffer + 1, sizeof (gpointer) * (STORE_REMSET_BUFFER_SIZE - 1));
6157 remset->next = generic_store_remsets;
6158 generic_store_remsets = remset;
6161 static void
6162 unregister_current_thread (void)
6164 int hash;
6165 SgenThreadInfo *prev = NULL;
6166 SgenThreadInfo *p;
6167 RememberedSet *rset;
6168 ARCH_THREAD_TYPE id = ARCH_GET_THREAD ();
6170 binary_protocol_thread_unregister ((gpointer)id);
6172 hash = HASH_PTHREAD_T (id) % THREAD_HASH_SIZE;
6173 p = thread_table [hash];
6174 assert (p);
6175 DEBUG (3, fprintf (gc_debug_file, "unregister thread %p (%p)\n", p, (gpointer)p->id));
6176 while (!ARCH_THREAD_EQUALS (p->id, id)) {
6177 prev = p;
6178 p = p->next;
6180 if (prev == NULL) {
6181 thread_table [hash] = p->next;
6182 } else {
6183 prev->next = p->next;
6186 #if defined(__MACH__)
6187 mach_port_deallocate (current_task (), p->mach_port);
6188 #endif
6190 if (gc_callbacks.thread_detach_func) {
6191 gc_callbacks.thread_detach_func (p->runtime_data);
6192 p->runtime_data = NULL;
6195 if (p->remset) {
6196 if (freed_thread_remsets) {
6197 for (rset = p->remset; rset->next; rset = rset->next)
6199 rset->next = freed_thread_remsets;
6200 freed_thread_remsets = p->remset;
6201 } else {
6202 freed_thread_remsets = p->remset;
6205 if (*p->store_remset_buffer_index_addr)
6206 add_generic_store_remset_from_buffer (*p->store_remset_buffer_addr);
6207 mono_sgen_free_internal (*p->store_remset_buffer_addr, INTERNAL_MEM_STORE_REMSET);
6208 free (p);
6211 static void
6212 unregister_thread (void *k)
6214 /* If a delegate is passed to native code and invoked on a thread we dont
6215 * know about, the jit will register it with mono_jit_thead_attach, but
6216 * we have no way of knowing when that thread goes away. SGen has a TSD
6217 * so we assume that if the domain is still registered, we can detach
6218 * the thread
6220 if (mono_domain_get ())
6221 mono_thread_detach (mono_thread_current ());
6223 LOCK_GC;
6224 unregister_current_thread ();
6225 UNLOCK_GC;
6228 gboolean
6229 mono_gc_register_thread (void *baseptr)
6231 SgenThreadInfo *info;
6233 LOCK_GC;
6234 init_stats ();
6235 info = mono_sgen_thread_info_lookup (ARCH_GET_THREAD ());
6236 if (info == NULL) {
6237 info = gc_register_current_thread (baseptr);
6238 } else {
6239 /* The main thread might get registered before callbacks are set */
6240 if (gc_callbacks.thread_attach_func && !info->runtime_data)
6241 info->runtime_data = gc_callbacks.thread_attach_func ();
6243 UNLOCK_GC;
6245 /* Need a better place to initialize this */
6246 if (!array_fill_vtable && mono_get_root_domain ()) {
6247 array_fill_vtable = mono_class_vtable (mono_get_root_domain (), mono_array_class_get (mono_defaults.byte_class, 1));
6250 return info != NULL;
6254 * mono_gc_set_stack_end:
6256 * Set the end of the current threads stack to STACK_END. The stack space between
6257 * STACK_END and the real end of the threads stack will not be scanned during collections.
6259 void
6260 mono_gc_set_stack_end (void *stack_end)
6262 SgenThreadInfo *info;
6264 LOCK_GC;
6265 info = mono_sgen_thread_info_lookup (ARCH_GET_THREAD ());
6266 if (info) {
6267 g_assert (stack_end < info->stack_end);
6268 info->stack_end = stack_end;
6270 UNLOCK_GC;
6273 #if USE_PTHREAD_INTERCEPT
6275 typedef struct {
6276 void *(*start_routine) (void *);
6277 void *arg;
6278 int flags;
6279 MonoSemType registered;
6280 } SgenThreadStartInfo;
6282 static void*
6283 gc_start_thread (void *arg)
6285 SgenThreadStartInfo *start_info = arg;
6286 SgenThreadInfo* info;
6287 void *t_arg = start_info->arg;
6288 void *(*start_func) (void*) = start_info->start_routine;
6289 void *result;
6290 int post_result;
6292 LOCK_GC;
6293 info = gc_register_current_thread (&result);
6294 UNLOCK_GC;
6295 post_result = MONO_SEM_POST (&(start_info->registered));
6296 g_assert (!post_result);
6297 result = start_func (t_arg);
6298 g_assert (!mono_domain_get ());
6300 * this is done by the pthread key dtor
6301 LOCK_GC;
6302 unregister_current_thread ();
6303 UNLOCK_GC;
6306 return result;
6310 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
6312 SgenThreadStartInfo *start_info;
6313 int result;
6315 start_info = malloc (sizeof (SgenThreadStartInfo));
6316 if (!start_info)
6317 return ENOMEM;
6318 MONO_SEM_INIT (&(start_info->registered), 0);
6319 start_info->arg = arg;
6320 start_info->start_routine = start_routine;
6322 result = pthread_create (new_thread, attr, gc_start_thread, start_info);
6323 if (result == 0) {
6324 while (MONO_SEM_WAIT (&(start_info->registered)) != 0) {
6325 /*if (EINTR != errno) ABORT("sem_wait failed"); */
6328 MONO_SEM_DESTROY (&(start_info->registered));
6329 free (start_info);
6330 return result;
6334 mono_gc_pthread_join (pthread_t thread, void **retval)
6336 return pthread_join (thread, retval);
6340 mono_gc_pthread_detach (pthread_t thread)
6342 return pthread_detach (thread);
6345 #endif /* USE_PTHREAD_INTERCEPT */
6348 * ######################################################################
6349 * ######## Write barriers
6350 * ######################################################################
6354 * This causes the compile to extend the liveness of 'v' till the call to dummy_use
6356 static void
6357 dummy_use (gpointer v) {
6358 __asm__ volatile ("" : "=r"(v) : "r"(v));
6362 static RememberedSet*
6363 alloc_remset (int size, gpointer id) {
6364 RememberedSet* res = mono_sgen_alloc_internal_dynamic (sizeof (RememberedSet) + (size * sizeof (gpointer)), INTERNAL_MEM_REMSET);
6365 res->store_next = res->data;
6366 res->end_set = res->data + size;
6367 res->next = NULL;
6368 DEBUG (4, fprintf (gc_debug_file, "Allocated remset size %d at %p for %p\n", size, res->data, id));
6369 return res;
6373 * Note: the write barriers first do the needed GC work and then do the actual store:
6374 * this way the value is visible to the conservative GC scan after the write barrier
6375 * itself. If a GC interrupts the barrier in the middle, value will be kept alive by
6376 * the conservative scan, otherwise by the remembered set scan.
6378 void
6379 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
6381 HEAVY_STAT (++stat_wbarrier_set_field);
6382 if (ptr_in_nursery (field_ptr)) {
6383 *(void**)field_ptr = value;
6384 return;
6386 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", field_ptr));
6387 if (use_cardtable) {
6388 *(void**)field_ptr = value;
6389 if (ptr_in_nursery (value))
6390 sgen_card_table_mark_address ((mword)field_ptr);
6391 dummy_use (value);
6392 } else {
6393 RememberedSet *rs;
6394 TLAB_ACCESS_INIT;
6396 LOCK_GC;
6397 rs = REMEMBERED_SET;
6398 if (rs->store_next < rs->end_set) {
6399 *(rs->store_next++) = (mword)field_ptr;
6400 *(void**)field_ptr = value;
6401 UNLOCK_GC;
6402 return;
6404 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6405 rs->next = REMEMBERED_SET;
6406 REMEMBERED_SET = rs;
6407 #ifdef HAVE_KW_THREAD
6408 mono_sgen_thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
6409 #endif
6410 *(rs->store_next++) = (mword)field_ptr;
6411 *(void**)field_ptr = value;
6412 UNLOCK_GC;
6416 void
6417 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
6419 HEAVY_STAT (++stat_wbarrier_set_arrayref);
6420 if (ptr_in_nursery (slot_ptr)) {
6421 *(void**)slot_ptr = value;
6422 return;
6424 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", slot_ptr));
6425 if (use_cardtable) {
6426 *(void**)slot_ptr = value;
6427 if (ptr_in_nursery (value))
6428 sgen_card_table_mark_address ((mword)slot_ptr);
6429 dummy_use (value);
6430 } else {
6431 RememberedSet *rs;
6432 TLAB_ACCESS_INIT;
6434 LOCK_GC;
6435 rs = REMEMBERED_SET;
6436 if (rs->store_next < rs->end_set) {
6437 *(rs->store_next++) = (mword)slot_ptr;
6438 *(void**)slot_ptr = value;
6439 UNLOCK_GC;
6440 return;
6442 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6443 rs->next = REMEMBERED_SET;
6444 REMEMBERED_SET = rs;
6445 #ifdef HAVE_KW_THREAD
6446 mono_sgen_thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
6447 #endif
6448 *(rs->store_next++) = (mword)slot_ptr;
6449 *(void**)slot_ptr = value;
6450 UNLOCK_GC;
6454 void
6455 mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
6457 HEAVY_STAT (++stat_wbarrier_arrayref_copy);
6458 /*This check can be done without taking a lock since dest_ptr array is pinned*/
6459 if (ptr_in_nursery (dest_ptr) || count <= 0) {
6460 mono_gc_memmove (dest_ptr, src_ptr, count * sizeof (gpointer));
6461 return;
6464 if (use_cardtable) {
6465 gpointer *dest = dest_ptr;
6466 gpointer *src = src_ptr;
6468 /*overlapping that required backward copying*/
6469 if (src < dest && (src + count) > dest) {
6470 gpointer *start = dest;
6471 dest += count - 1;
6472 src += count - 1;
6474 for (; dest >= start; --src, --dest) {
6475 gpointer value = *src;
6476 *dest = value;
6477 if (ptr_in_nursery (value))
6478 sgen_card_table_mark_address ((mword)dest);
6479 dummy_use (value);
6481 } else {
6482 gpointer *end = dest + count;
6483 for (; dest < end; ++src, ++dest) {
6484 gpointer value = *src;
6485 *dest = value;
6486 if (ptr_in_nursery (value))
6487 sgen_card_table_mark_address ((mword)dest);
6488 dummy_use (value);
6491 } else {
6492 RememberedSet *rs;
6493 TLAB_ACCESS_INIT;
6494 LOCK_GC;
6495 mono_gc_memmove (dest_ptr, src_ptr, count * sizeof (gpointer));
6497 rs = REMEMBERED_SET;
6498 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p, %d\n", dest_ptr, count));
6499 if (rs->store_next + 1 < rs->end_set) {
6500 *(rs->store_next++) = (mword)dest_ptr | REMSET_RANGE;
6501 *(rs->store_next++) = count;
6502 UNLOCK_GC;
6503 return;
6505 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6506 rs->next = REMEMBERED_SET;
6507 REMEMBERED_SET = rs;
6508 #ifdef HAVE_KW_THREAD
6509 mono_sgen_thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
6510 #endif
6511 *(rs->store_next++) = (mword)dest_ptr | REMSET_RANGE;
6512 *(rs->store_next++) = count;
6514 UNLOCK_GC;
6518 static char *found_obj;
6520 static void
6521 find_object_for_ptr_callback (char *obj, size_t size, void *user_data)
6523 char *ptr = user_data;
6525 if (ptr >= obj && ptr < obj + size) {
6526 g_assert (!found_obj);
6527 found_obj = obj;
6531 /* for use in the debugger */
6532 char* find_object_for_ptr (char *ptr);
6533 char*
6534 find_object_for_ptr (char *ptr)
6536 if (ptr >= nursery_section->data && ptr < nursery_section->end_data) {
6537 found_obj = NULL;
6538 mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
6539 find_object_for_ptr_callback, ptr, TRUE);
6540 if (found_obj)
6541 return found_obj;
6544 found_obj = NULL;
6545 mono_sgen_los_iterate_objects (find_object_for_ptr_callback, ptr);
6546 if (found_obj)
6547 return found_obj;
6550 * Very inefficient, but this is debugging code, supposed to
6551 * be called from gdb, so we don't care.
6553 found_obj = NULL;
6554 major_collector.iterate_objects (TRUE, TRUE, find_object_for_ptr_callback, ptr);
6555 return found_obj;
6558 static void
6559 evacuate_remset_buffer (void)
6561 gpointer *buffer;
6562 TLAB_ACCESS_INIT;
6564 buffer = STORE_REMSET_BUFFER;
6566 add_generic_store_remset_from_buffer (buffer);
6567 memset (buffer, 0, sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
6569 STORE_REMSET_BUFFER_INDEX = 0;
6572 void
6573 mono_gc_wbarrier_generic_nostore (gpointer ptr)
6575 gpointer *buffer;
6576 int index;
6577 TLAB_ACCESS_INIT;
6579 HEAVY_STAT (++stat_wbarrier_generic_store);
6581 #ifdef XDOMAIN_CHECKS_IN_WBARRIER
6582 /* FIXME: ptr_in_heap must be called with the GC lock held */
6583 if (xdomain_checks && *(MonoObject**)ptr && ptr_in_heap (ptr)) {
6584 char *start = find_object_for_ptr (ptr);
6585 MonoObject *value = *(MonoObject**)ptr;
6586 LOCK_GC;
6587 g_assert (start);
6588 if (start) {
6589 MonoObject *obj = (MonoObject*)start;
6590 if (obj->vtable->domain != value->vtable->domain)
6591 g_assert (is_xdomain_ref_allowed (ptr, start, obj->vtable->domain));
6593 UNLOCK_GC;
6595 #endif
6597 if (*(gpointer*)ptr)
6598 binary_protocol_wbarrier (ptr, *(gpointer*)ptr, (gpointer)LOAD_VTABLE (*(gpointer*)ptr));
6600 if (ptr_in_nursery (ptr) || ptr_on_stack (ptr) || !ptr_in_nursery (*(gpointer*)ptr)) {
6601 DEBUG (8, fprintf (gc_debug_file, "Skipping remset at %p\n", ptr));
6602 return;
6605 if (use_cardtable) {
6606 if (ptr_in_nursery(*(gpointer*)ptr))
6607 sgen_card_table_mark_address ((mword)ptr);
6608 return;
6611 LOCK_GC;
6613 buffer = STORE_REMSET_BUFFER;
6614 index = STORE_REMSET_BUFFER_INDEX;
6615 /* This simple optimization eliminates a sizable portion of
6616 entries. Comparing it to the last but one entry as well
6617 doesn't eliminate significantly more entries. */
6618 if (buffer [index] == ptr) {
6619 UNLOCK_GC;
6620 return;
6623 DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", ptr));
6624 HEAVY_STAT (++stat_wbarrier_generic_store_remset);
6626 ++index;
6627 if (index >= STORE_REMSET_BUFFER_SIZE) {
6628 evacuate_remset_buffer ();
6629 index = STORE_REMSET_BUFFER_INDEX;
6630 g_assert (index == 0);
6631 ++index;
6633 buffer [index] = ptr;
6634 STORE_REMSET_BUFFER_INDEX = index;
6636 UNLOCK_GC;
6639 void
6640 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
6642 DEBUG (8, fprintf (gc_debug_file, "Wbarrier store at %p to %p (%s)\n", ptr, value, value ? safe_name (value) : "null"));
6643 *(void**)ptr = value;
6644 if (ptr_in_nursery (value))
6645 mono_gc_wbarrier_generic_nostore (ptr);
6646 dummy_use (value);
6649 void mono_gc_wbarrier_value_copy_bitmap (gpointer _dest, gpointer _src, int size, unsigned bitmap)
6651 mword *dest = _dest;
6652 mword *src = _src;
6654 while (size) {
6655 if (bitmap & 0x1)
6656 mono_gc_wbarrier_generic_store (dest, (MonoObject*)*src);
6657 else
6658 *dest = *src;
6659 ++src;
6660 ++dest;
6661 size -= SIZEOF_VOID_P;
6662 bitmap >>= 1;
6667 void
6668 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
6670 RememberedSet *rs;
6671 size_t size = count * mono_class_value_size (klass, NULL);
6672 TLAB_ACCESS_INIT;
6673 HEAVY_STAT (++stat_wbarrier_value_copy);
6674 g_assert (klass->valuetype);
6675 LOCK_GC;
6676 mono_gc_memmove (dest, src, size);
6677 if (use_cardtable) {
6678 sgen_card_table_mark_range ((mword)dest, size);
6679 } else {
6680 rs = REMEMBERED_SET;
6681 if (ptr_in_nursery (dest) || ptr_on_stack (dest) || !SGEN_CLASS_HAS_REFERENCES (klass)) {
6682 UNLOCK_GC;
6683 return;
6685 g_assert (klass->gc_descr_inited);
6686 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));
6688 if (rs->store_next + 3 < rs->end_set) {
6689 *(rs->store_next++) = (mword)dest | REMSET_VTYPE;
6690 *(rs->store_next++) = (mword)klass;
6691 *(rs->store_next++) = (mword)count;
6692 UNLOCK_GC;
6693 return;
6695 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6696 rs->next = REMEMBERED_SET;
6697 REMEMBERED_SET = rs;
6698 #ifdef HAVE_KW_THREAD
6699 mono_sgen_thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
6700 #endif
6701 *(rs->store_next++) = (mword)dest | REMSET_VTYPE;
6702 *(rs->store_next++) = (mword)klass;
6703 *(rs->store_next++) = (mword)count;
6705 UNLOCK_GC;
6709 * mono_gc_wbarrier_object_copy:
6711 * Write barrier to call when obj is the result of a clone or copy of an object.
6713 void
6714 mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
6716 RememberedSet *rs;
6717 int size;
6719 TLAB_ACCESS_INIT;
6720 HEAVY_STAT (++stat_wbarrier_object_copy);
6721 rs = REMEMBERED_SET;
6722 DEBUG (6, fprintf (gc_debug_file, "Adding object remset for %p\n", obj));
6723 size = mono_object_class (obj)->instance_size;
6724 LOCK_GC;
6725 /* do not copy the sync state */
6726 mono_gc_memmove ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
6727 size - sizeof (MonoObject));
6728 if (ptr_in_nursery (obj) || ptr_on_stack (obj)) {
6729 UNLOCK_GC;
6730 return;
6732 if (rs->store_next < rs->end_set) {
6733 *(rs->store_next++) = (mword)obj | REMSET_OBJECT;
6734 UNLOCK_GC;
6735 return;
6737 rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6738 rs->next = REMEMBERED_SET;
6739 REMEMBERED_SET = rs;
6740 #ifdef HAVE_KW_THREAD
6741 mono_sgen_thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
6742 #endif
6743 *(rs->store_next++) = (mword)obj | REMSET_OBJECT;
6744 UNLOCK_GC;
6748 * ######################################################################
6749 * ######## Collector debugging
6750 * ######################################################################
6753 const char*descriptor_types [] = {
6754 "run_length",
6755 "small_bitmap",
6756 "string",
6757 "complex",
6758 "vector",
6759 "array",
6760 "large_bitmap",
6761 "complex_arr"
6764 void
6765 describe_ptr (char *ptr)
6767 MonoVTable *vtable;
6768 mword desc;
6769 int type;
6770 char *start;
6772 if (ptr_in_nursery (ptr)) {
6773 printf ("Pointer inside nursery.\n");
6774 } else {
6775 if (mono_sgen_ptr_is_in_los (ptr, &start)) {
6776 if (ptr == start)
6777 printf ("Pointer is the start of object %p in LOS space.\n", start);
6778 else
6779 printf ("Pointer is at offset 0x%x of object %p in LOS space.\n", (int)(ptr - start), start);
6780 ptr = start;
6781 } else if (major_collector.ptr_is_in_non_pinned_space (ptr)) {
6782 printf ("Pointer inside oldspace.\n");
6783 } else if (major_collector.obj_is_from_pinned_alloc (ptr)) {
6784 printf ("Pointer is inside a pinned chunk.\n");
6785 } else {
6786 printf ("Pointer unknown.\n");
6787 return;
6791 if (object_is_pinned (ptr))
6792 printf ("Object is pinned.\n");
6794 if (object_is_forwarded (ptr))
6795 printf ("Object is forwared.\n");
6797 // FIXME: Handle pointers to the inside of objects
6798 vtable = (MonoVTable*)LOAD_VTABLE (ptr);
6800 printf ("VTable: %p\n", vtable);
6801 if (vtable == NULL) {
6802 printf ("VTable is invalid (empty).\n");
6803 return;
6805 if (ptr_in_nursery (vtable)) {
6806 printf ("VTable is invalid (points inside nursery).\n");
6807 return;
6809 printf ("Class: %s\n", vtable->klass->name);
6811 desc = ((GCVTable*)vtable)->desc;
6812 printf ("Descriptor: %lx\n", (long)desc);
6814 type = desc & 0x7;
6815 printf ("Descriptor type: %d (%s)\n", type, descriptor_types [type]);
6818 static mword*
6819 find_in_remset_loc (mword *p, char *addr, gboolean *found)
6821 void **ptr;
6822 mword count, desc;
6823 size_t skip_size;
6825 switch ((*p) & REMSET_TYPE_MASK) {
6826 case REMSET_LOCATION:
6827 if (*p == (mword)addr)
6828 *found = TRUE;
6829 return p + 1;
6830 case REMSET_RANGE:
6831 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
6832 count = p [1];
6833 if ((void**)addr >= ptr && (void**)addr < ptr + count)
6834 *found = TRUE;
6835 return p + 2;
6836 case REMSET_OBJECT:
6837 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
6838 count = safe_object_get_size ((MonoObject*)ptr);
6839 count = ALIGN_UP (count);
6840 count /= sizeof (mword);
6841 if ((void**)addr >= ptr && (void**)addr < ptr + count)
6842 *found = TRUE;
6843 return p + 1;
6844 case REMSET_VTYPE:
6845 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
6846 desc = (mword)((MonoClass*)p [1])->gc_descr;
6847 count = p [2];
6849 switch (desc & 0x7) {
6850 case DESC_TYPE_RUN_LENGTH:
6851 OBJ_RUN_LEN_SIZE (skip_size, desc, ptr);
6852 break;
6853 case DESC_TYPE_SMALL_BITMAP:
6854 OBJ_BITMAP_SIZE (skip_size, desc, start);
6855 break;
6856 default:
6857 // FIXME:
6858 g_assert_not_reached ();
6861 /* The descriptor includes the size of MonoObject */
6862 skip_size -= sizeof (MonoObject);
6863 skip_size *= count;
6864 if ((void**)addr >= ptr && (void**)addr < ptr + (skip_size / sizeof (gpointer)))
6865 *found = TRUE;
6867 return p + 3;
6868 default:
6869 g_assert_not_reached ();
6871 return NULL;
6875 * Return whenever ADDR occurs in the remembered sets
6877 static gboolean
6878 find_in_remsets (char *addr)
6880 int i;
6881 SgenThreadInfo *info;
6882 RememberedSet *remset;
6883 GenericStoreRememberedSet *store_remset;
6884 mword *p;
6885 gboolean found = FALSE;
6887 /* the global one */
6888 for (remset = global_remset; remset; remset = remset->next) {
6889 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));
6890 for (p = remset->data; p < remset->store_next;) {
6891 p = find_in_remset_loc (p, addr, &found);
6892 if (found)
6893 return TRUE;
6897 /* the generic store ones */
6898 for (store_remset = generic_store_remsets; store_remset; store_remset = store_remset->next) {
6899 for (i = 0; i < STORE_REMSET_BUFFER_SIZE - 1; ++i) {
6900 if (store_remset->data [i] == addr)
6901 return TRUE;
6905 /* the per-thread ones */
6906 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
6907 for (info = thread_table [i]; info; info = info->next) {
6908 int j;
6909 for (remset = info->remset; remset; remset = remset->next) {
6910 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));
6911 for (p = remset->data; p < remset->store_next;) {
6912 p = find_in_remset_loc (p, addr, &found);
6913 if (found)
6914 return TRUE;
6917 for (j = 0; j < *info->store_remset_buffer_index_addr; ++j) {
6918 if ((*info->store_remset_buffer_addr) [j + 1] == addr)
6919 return TRUE;
6924 /* the freed thread ones */
6925 for (remset = freed_thread_remsets; remset; remset = remset->next) {
6926 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));
6927 for (p = remset->data; p < remset->store_next;) {
6928 p = find_in_remset_loc (p, addr, &found);
6929 if (found)
6930 return TRUE;
6934 return FALSE;
6937 static gboolean missing_remsets;
6940 * We let a missing remset slide if the target object is pinned,
6941 * because the store might have happened but the remset not yet added,
6942 * but in that case the target must be pinned. We might theoretically
6943 * miss some missing remsets this way, but it's very unlikely.
6945 #undef HANDLE_PTR
6946 #define HANDLE_PTR(ptr,obj) do { \
6947 if (*(ptr) && (char*)*(ptr) >= nursery_start && (char*)*(ptr) < nursery_next) { \
6948 if (!find_in_remsets ((char*)(ptr)) && (!use_cardtable || !sgen_card_table_address_is_marked ((mword)ptr))) { \
6949 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); \
6950 binary_protocol_missing_remset ((obj), (gpointer)LOAD_VTABLE ((obj)), (char*)(ptr) - (char*)(obj), *(ptr), (gpointer)LOAD_VTABLE(*(ptr)), object_is_pinned (*(ptr))); \
6951 if (!object_is_pinned (*(ptr))) \
6952 missing_remsets = TRUE; \
6955 } while (0)
6958 * Check that each object reference which points into the nursery can
6959 * be found in the remembered sets.
6961 static void
6962 check_consistency_callback (char *start, size_t size, void *dummy)
6964 GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
6965 DEBUG (8, fprintf (gc_debug_file, "Scanning object %p, vtable: %p (%s)\n", start, vt, vt->klass->name));
6967 #define SCAN_OBJECT_ACTION
6968 #include "sgen-scan-object.h"
6972 * Perform consistency check of the heap.
6974 * Assumes the world is stopped.
6976 static void
6977 check_consistency (void)
6979 // Need to add more checks
6981 missing_remsets = FALSE;
6983 DEBUG (1, fprintf (gc_debug_file, "Begin heap consistency check...\n"));
6985 // Check that oldspace->newspace pointers are registered with the collector
6986 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)check_consistency_callback, NULL);
6988 mono_sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_consistency_callback, NULL);
6990 DEBUG (1, fprintf (gc_debug_file, "Heap consistency check done.\n"));
6992 #ifdef SGEN_BINARY_PROTOCOL
6993 if (!binary_protocol_file)
6994 #endif
6995 g_assert (!missing_remsets);
6999 #undef HANDLE_PTR
7000 #define HANDLE_PTR(ptr,obj) do { \
7001 if (*(ptr) && !LOAD_VTABLE (*(ptr))) \
7002 g_error ("Could not load vtable for obj %p slot %d (size %d)", obj, (char*)ptr - (char*)obj, safe_object_get_size ((MonoObject*)obj)); \
7003 } while (0)
7005 static void
7006 check_major_refs_callback (char *start, size_t size, void *dummy)
7008 #define SCAN_OBJECT_ACTION
7009 #include "sgen-scan-object.h"
7012 static void
7013 check_major_refs (void)
7015 major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)check_major_refs_callback, NULL);
7016 mono_sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_major_refs_callback, NULL);
7019 static char **valid_nursery_objects;
7020 static int valid_nursery_object_count;
7021 static gboolean broken_heap;
7023 static void
7024 setup_mono_sgen_scan_area_with_callback (char *object, size_t size, void *data)
7026 valid_nursery_objects [valid_nursery_object_count++] = object;
7029 static int
7030 compare_pointers (const void *a, const void *b) {
7031 if (a == b)
7032 return 0;
7033 if (a < b)
7034 return -1;
7035 return 1;
7038 static gboolean
7039 find_object_in_nursery_dump (char *object)
7041 int first = 0, last = valid_nursery_object_count;
7042 while (first < last) {
7043 int middle = first + ((last - first) >> 1);
7044 if (object == valid_nursery_objects [middle])
7045 return TRUE;
7047 if (object < valid_nursery_objects [middle])
7048 last = middle;
7049 else
7050 first = middle + 1;
7052 g_assert (first == last);
7053 return FALSE;
7056 static void
7057 describe_nursery_ptr (char *ptr)
7059 int i;
7061 fprintf (gc_debug_file, "nursery-ptr ");
7062 for (i = 0; i < valid_nursery_object_count; ++i) {
7063 if (valid_nursery_objects [i] >= ptr)
7064 break;
7067 if (i >= valid_nursery_object_count || valid_nursery_objects [i] + safe_object_get_size (valid_nursery_objects [i]) < ptr) {
7068 fprintf (gc_debug_file, "(unalloc'd-memory)");
7069 } else {
7070 char *obj = valid_nursery_objects [i];
7071 MonoVTable *vtable = (MonoVTable*)LOAD_VTABLE (obj);
7072 int size = safe_object_get_size (obj);
7074 if (obj == ptr)
7075 fprintf (gc_debug_file, "(object %s.%s size %d)",
7076 vtable->klass->name_space, vtable->klass->name, size);
7077 else
7078 fprintf (gc_debug_file, "(interior-ptr offset %td of %p (%s.%s) size %d)",
7079 ptr - obj, obj,
7080 vtable->klass->name_space, vtable->klass->name, size);
7084 static gboolean
7085 is_valid_object_pointer (char *object)
7087 if (ptr_in_nursery (object))
7088 return find_object_in_nursery_dump (object);
7090 if (major_collector.is_valid_object (object))
7091 return TRUE;
7093 if (mono_sgen_los_is_valid_object (object))
7094 return TRUE;
7096 return FALSE;
7099 static void
7100 describe_pointer (char *ptr)
7102 if (ptr_in_nursery (ptr)) {
7103 describe_nursery_ptr (ptr);
7104 } else if (major_collector.describe_pointer (ptr)) {
7105 //Nothing really
7106 } else if (!mono_sgen_los_describe_pointer (ptr)) {
7107 fprintf (gc_debug_file, "non-heap-ptr");
7111 static void
7112 bad_pointer_spew (char *obj, char **slot)
7114 char *ptr = *slot;
7115 MonoVTable *vtable = (MonoVTable*)LOAD_VTABLE (obj);
7117 fprintf (gc_debug_file, "Invalid object pointer %p [", ptr);
7118 describe_pointer (ptr);
7119 fprintf (gc_debug_file, "] at offset %td in object %p (%s.%s).\n",
7120 (char*)slot - obj,
7121 obj, vtable->klass->name_space, vtable->klass->name);
7122 broken_heap = TRUE;
7125 static void
7126 missing_remset_spew (char *obj, char **slot)
7128 char *ptr = *slot;
7129 MonoVTable *vtable = (MonoVTable*)LOAD_VTABLE (obj);
7131 fprintf (gc_debug_file, "Oldspace->newspace reference %p at offset %td in object %p (%s.%s) not found in remsets.\n",
7132 ptr, (char*)slot - obj, obj,
7133 vtable->klass->name_space, vtable->klass->name);
7135 broken_heap = TRUE;
7139 FIXME Flag missing remsets due to pinning as non fatal
7141 #undef HANDLE_PTR
7142 #define HANDLE_PTR(ptr,obj) do { \
7143 if (*(char**)ptr) { \
7144 if (!is_valid_object_pointer (*(char**)ptr)) { \
7145 bad_pointer_spew ((char*)obj, (char**)ptr); \
7146 } else if (!ptr_in_nursery (obj) && ptr_in_nursery ((char*)*ptr)) { \
7147 if (!find_in_remsets ((char*)(ptr)) && (!use_cardtable || !sgen_card_table_address_is_marked ((mword)ptr))) \
7148 missing_remset_spew ((char*)obj, (char**)ptr); \
7151 } while (0)
7154 static void
7155 verify_object_pointers_callback (char *start, size_t size, void *dummy)
7157 #define SCAN_OBJECT_ACTION
7158 #include "sgen-scan-object.h"
7161 static void
7162 whole_heap_check (void)
7164 /*setup valid_nursery_objects*/
7165 if (!valid_nursery_objects)
7166 valid_nursery_objects = mono_sgen_alloc_os_memory (DEFAULT_NURSERY_SIZE, TRUE);
7167 valid_nursery_object_count = 0;
7168 mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data, setup_mono_sgen_scan_area_with_callback, NULL, FALSE);
7170 broken_heap = FALSE;
7171 mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data, verify_object_pointers_callback, NULL, FALSE);
7172 major_collector.iterate_objects (TRUE, TRUE, verify_object_pointers_callback, NULL);
7173 mono_sgen_los_iterate_objects (verify_object_pointers_callback, NULL);
7175 g_assert (!broken_heap);
7178 FIXME:
7179 -This heap checker is racy regarding inlined write barriers and other JIT tricks that
7180 depend on OP_DUMMY_USE.
7182 static void
7183 verify_whole_heap (void)
7185 stop_world (0);
7186 clear_nursery_fragments (nursery_next);
7187 whole_heap_check ();
7188 restart_world (0);
7191 /* Check that the reference is valid */
7192 #undef HANDLE_PTR
7193 #define HANDLE_PTR(ptr,obj) do { \
7194 if (*(ptr)) { \
7195 g_assert (safe_name (*(ptr)) != NULL); \
7197 } while (0)
7200 * check_object:
7202 * Perform consistency check on an object. Currently we only check that the
7203 * reference fields are valid.
7205 void
7206 check_object (char *start)
7208 if (!start)
7209 return;
7211 #include "sgen-scan-object.h"
7216 * ######################################################################
7217 * ######## Other mono public interface functions.
7218 * ######################################################################
7221 #define REFS_SIZE 128
7222 typedef struct {
7223 void *data;
7224 MonoGCReferences callback;
7225 int flags;
7226 int count;
7227 int called;
7228 MonoObject *refs [REFS_SIZE];
7229 uintptr_t offsets [REFS_SIZE];
7230 } HeapWalkInfo;
7232 #undef HANDLE_PTR
7233 #define HANDLE_PTR(ptr,obj) do { \
7234 if (*(ptr)) { \
7235 if (hwi->count == REFS_SIZE) { \
7236 hwi->callback ((MonoObject*)start, mono_object_class (start), hwi->called? 0: size, hwi->count, hwi->refs, hwi->offsets, hwi->data); \
7237 hwi->count = 0; \
7238 hwi->called = 1; \
7240 hwi->offsets [hwi->count] = (char*)(ptr)-(char*)start; \
7241 hwi->refs [hwi->count++] = *(ptr); \
7243 } while (0)
7245 static void
7246 collect_references (HeapWalkInfo *hwi, char *start, size_t size)
7248 #include "sgen-scan-object.h"
7251 static void
7252 walk_references (char *start, size_t size, void *data)
7254 HeapWalkInfo *hwi = data;
7255 hwi->called = 0;
7256 hwi->count = 0;
7257 collect_references (hwi, start, size);
7258 if (hwi->count || !hwi->called)
7259 hwi->callback ((MonoObject*)start, mono_object_class (start), hwi->called? 0: size, hwi->count, hwi->refs, hwi->offsets, hwi->data);
7263 * mono_gc_walk_heap:
7264 * @flags: flags for future use
7265 * @callback: a function pointer called for each object in the heap
7266 * @data: a user data pointer that is passed to callback
7268 * This function can be used to iterate over all the live objects in the heap:
7269 * for each object, @callback is invoked, providing info about the object's
7270 * location in memory, its class, its size and the objects it references.
7271 * For each referenced object it's offset from the object address is
7272 * reported in the offsets array.
7273 * The object references may be buffered, so the callback may be invoked
7274 * multiple times for the same object: in all but the first call, the size
7275 * argument will be zero.
7276 * Note that this function can be only called in the #MONO_GC_EVENT_PRE_START_WORLD
7277 * profiler event handler.
7279 * Returns: a non-zero value if the GC doesn't support heap walking
7282 mono_gc_walk_heap (int flags, MonoGCReferences callback, void *data)
7284 HeapWalkInfo hwi;
7286 hwi.flags = flags;
7287 hwi.callback = callback;
7288 hwi.data = data;
7290 clear_nursery_fragments (nursery_next);
7291 mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data, walk_references, &hwi, FALSE);
7293 major_collector.iterate_objects (TRUE, TRUE, walk_references, &hwi);
7294 mono_sgen_los_iterate_objects (walk_references, &hwi);
7296 return 0;
7299 void
7300 mono_gc_collect (int generation)
7302 LOCK_GC;
7303 if (generation > 1)
7304 generation = 1;
7305 mono_profiler_gc_event (MONO_GC_EVENT_START, generation);
7306 stop_world (generation);
7307 if (generation == 0) {
7308 collect_nursery (0);
7309 } else {
7310 major_collection ("user request");
7312 restart_world (generation);
7313 mono_profiler_gc_event (MONO_GC_EVENT_END, generation);
7314 UNLOCK_GC;
7318 mono_gc_max_generation (void)
7320 return 1;
7324 mono_gc_collection_count (int generation)
7326 if (generation == 0)
7327 return num_minor_gcs;
7328 return num_major_gcs;
7331 int64_t
7332 mono_gc_get_used_size (void)
7334 gint64 tot = 0;
7335 LOCK_GC;
7336 tot = los_memory_usage;
7337 tot += nursery_section->next_data - nursery_section->data;
7338 tot += major_collector.get_used_size ();
7339 /* FIXME: account for pinned objects */
7340 UNLOCK_GC;
7341 return tot;
7344 int64_t
7345 mono_gc_get_heap_size (void)
7347 return total_alloc;
7350 void
7351 mono_gc_disable (void)
7353 LOCK_GC;
7354 gc_disabled++;
7355 UNLOCK_GC;
7358 void
7359 mono_gc_enable (void)
7361 LOCK_GC;
7362 gc_disabled--;
7363 UNLOCK_GC;
7367 mono_gc_get_los_limit (void)
7369 return MAX_SMALL_OBJ_SIZE;
7372 gboolean
7373 mono_object_is_alive (MonoObject* o)
7375 return TRUE;
7379 mono_gc_get_generation (MonoObject *obj)
7381 if (ptr_in_nursery (obj))
7382 return 0;
7383 return 1;
7386 void
7387 mono_gc_enable_events (void)
7391 void
7392 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
7394 LOCK_GC;
7395 mono_gc_register_disappearing_link (obj, link_addr, track);
7396 UNLOCK_GC;
7399 void
7400 mono_gc_weak_link_remove (void **link_addr)
7402 LOCK_GC;
7403 mono_gc_register_disappearing_link (NULL, link_addr, FALSE);
7404 UNLOCK_GC;
7407 MonoObject*
7408 mono_gc_weak_link_get (void **link_addr)
7410 if (!*link_addr)
7411 return NULL;
7412 return (MonoObject*) REVEAL_POINTER (*link_addr);
7415 gboolean
7416 mono_gc_ephemeron_array_add (MonoObject *obj)
7418 EphemeronLinkNode *node;
7420 LOCK_GC;
7422 node = mono_sgen_alloc_internal (INTERNAL_MEM_EPHEMERON_LINK);
7423 if (!node) {
7424 UNLOCK_GC;
7425 return FALSE;
7427 node->array = (char*)obj;
7428 node->next = ephemeron_list;
7429 ephemeron_list = node;
7431 DEBUG (5, fprintf (gc_debug_file, "Registered ephemeron array %p\n", obj));
7433 UNLOCK_GC;
7434 return TRUE;
7437 void*
7438 mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
7440 if (numbits == 0) {
7441 return (void*)MAKE_ROOT_DESC (ROOT_DESC_BITMAP, 0);
7442 } else if (numbits < ((sizeof (*bitmap) * 8) - ROOT_DESC_TYPE_SHIFT)) {
7443 return (void*)MAKE_ROOT_DESC (ROOT_DESC_BITMAP, bitmap [0]);
7444 } else {
7445 mword complex = alloc_complex_descriptor (bitmap, numbits);
7446 return (void*)MAKE_ROOT_DESC (ROOT_DESC_COMPLEX, complex);
7450 static void *all_ref_root_descrs [32];
7452 void*
7453 mono_gc_make_root_descr_all_refs (int numbits)
7455 gsize *gc_bitmap;
7456 void *descr;
7458 if (numbits < 32 && all_ref_root_descrs [numbits])
7459 return all_ref_root_descrs [numbits];
7461 gc_bitmap = g_malloc0 (ALIGN_TO (numbits, 8) + 1);
7462 memset (gc_bitmap, 0xff, numbits / 8);
7463 if (numbits % 8)
7464 gc_bitmap [numbits / 8] = (1 << (numbits % 8)) - 1;
7465 descr = mono_gc_make_descr_from_bitmap (gc_bitmap, numbits);
7466 g_free (gc_bitmap);
7468 if (numbits < 32)
7469 all_ref_root_descrs [numbits] = descr;
7471 return descr;
7474 void*
7475 mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
7477 void *descr;
7479 g_assert (user_descriptors_next < MAX_USER_DESCRIPTORS);
7480 descr = (void*)MAKE_ROOT_DESC (ROOT_DESC_USER, (mword)user_descriptors_next);
7481 user_descriptors [user_descriptors_next ++] = marker;
7483 return descr;
7486 void*
7487 mono_gc_alloc_fixed (size_t size, void *descr)
7489 /* FIXME: do a single allocation */
7490 void *res = calloc (1, size);
7491 if (!res)
7492 return NULL;
7493 if (!mono_gc_register_root (res, size, descr)) {
7494 free (res);
7495 res = NULL;
7497 return res;
7500 void
7501 mono_gc_free_fixed (void* addr)
7503 mono_gc_deregister_root (addr);
7504 free (addr);
7507 void*
7508 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
7510 void *result;
7511 LOCK_INTERRUPTION;
7512 result = func (data);
7513 UNLOCK_INTERRUPTION;
7514 return result;
7517 gboolean
7518 mono_gc_is_gc_thread (void)
7520 gboolean result;
7521 LOCK_GC;
7522 result = mono_sgen_thread_info_lookup (ARCH_GET_THREAD ()) != NULL;
7523 UNLOCK_GC;
7524 return result;
7527 void
7528 mono_gc_base_init (void)
7530 char *env;
7531 char **opts, **ptr;
7532 char *major_collector_opt = NULL;
7533 struct sigaction sinfo;
7534 glong max_heap = 0;
7536 glong soft_limit = 0;
7538 /* the gc_initialized guard seems to imply this method is
7539 idempotent, but LOCK_INIT(gc_mutex) might not be. It's
7540 defined in sgen-gc.h as nothing, so there's no danger at
7541 present. */
7542 LOCK_INIT (gc_mutex);
7543 LOCK_GC;
7544 if (gc_initialized) {
7545 UNLOCK_GC;
7546 return;
7548 pagesize = mono_pagesize ();
7549 gc_debug_file = stdout;
7551 LOCK_INIT (interruption_mutex);
7552 LOCK_INIT (global_remset_mutex);
7553 LOCK_INIT (pin_queue_mutex);
7555 if ((env = getenv ("MONO_GC_PARAMS"))) {
7556 opts = g_strsplit (env, ",", -1);
7557 for (ptr = opts; *ptr; ++ptr) {
7558 char *opt = *ptr;
7559 if (g_str_has_prefix (opt, "major=")) {
7560 opt = strchr (opt, '=') + 1;
7561 major_collector_opt = g_strdup (opt);
7564 } else {
7565 opts = NULL;
7568 init_stats ();
7569 mono_sgen_init_internal_allocator ();
7571 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_FRAGMENT, sizeof (Fragment));
7572 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_SECTION, SGEN_SIZEOF_GC_MEM_SECTION);
7573 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_FINALIZE_ENTRY, sizeof (FinalizeEntry));
7574 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_DISLINK, sizeof (DisappearingLink));
7575 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_ROOT_RECORD, sizeof (RootRecord));
7576 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_GRAY_QUEUE, sizeof (GrayQueueSection));
7577 g_assert (sizeof (GenericStoreRememberedSet) == sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
7578 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_STORE_REMSET, sizeof (GenericStoreRememberedSet));
7579 mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_EPHEMERON_LINK, sizeof (EphemeronLinkNode));
7581 if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep")) {
7582 mono_sgen_marksweep_init (&major_collector);
7583 } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-fixed")) {
7584 mono_sgen_marksweep_fixed_init (&major_collector);
7585 } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-par")) {
7586 mono_sgen_marksweep_par_init (&major_collector);
7587 workers_init (mono_cpu_count ());
7588 } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-fixed-par")) {
7589 mono_sgen_marksweep_fixed_par_init (&major_collector);
7590 workers_init (mono_cpu_count ());
7591 } else if (!strcmp (major_collector_opt, "copying")) {
7592 mono_sgen_copying_init (&major_collector);
7593 } else {
7594 fprintf (stderr, "Unknown major collector `%s'.\n", major_collector_opt);
7595 exit (1);
7598 #ifdef SGEN_HAVE_CARDTABLE
7599 use_cardtable = major_collector.supports_cardtable;
7600 #else
7601 use_cardtable = FALSE;
7602 #endif
7604 /* Keep this the default for now */
7605 conservative_stack_mark = TRUE;
7607 if (opts) {
7608 for (ptr = opts; *ptr; ++ptr) {
7609 char *opt = *ptr;
7610 if (g_str_has_prefix (opt, "major="))
7611 continue;
7612 if (g_str_has_prefix (opt, "wbarrier=")) {
7613 opt = strchr (opt, '=') + 1;
7614 if (strcmp (opt, "remset") == 0) {
7615 use_cardtable = FALSE;
7616 } else if (strcmp (opt, "cardtable") == 0) {
7617 if (!use_cardtable) {
7618 if (major_collector.supports_cardtable)
7619 fprintf (stderr, "The cardtable write barrier is not supported on this platform.\n");
7620 else
7621 fprintf (stderr, "The major collector does not support the cardtable write barrier.\n");
7622 exit (1);
7625 continue;
7627 if (g_str_has_prefix (opt, "max-heap-size=")) {
7628 opt = strchr (opt, '=') + 1;
7629 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &max_heap)) {
7630 if ((max_heap & (mono_pagesize () - 1))) {
7631 fprintf (stderr, "max-heap-size size must be a multiple of %d.\n", mono_pagesize ());
7632 exit (1);
7634 } else {
7635 fprintf (stderr, "max-heap-size must be an integer.\n");
7636 exit (1);
7638 continue;
7640 if (g_str_has_prefix (opt, "soft-heap-limit=")) {
7641 opt = strchr (opt, '=') + 1;
7642 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &soft_limit)) {
7643 if (soft_limit <= 0) {
7644 fprintf (stderr, "soft-heap-limit must be positive.\n");
7645 exit (1);
7647 } else {
7648 fprintf (stderr, "soft-heap-limit must be an integer.\n");
7649 exit (1);
7651 continue;
7653 if (g_str_has_prefix (opt, "stack-mark=")) {
7654 opt = strchr (opt, '=') + 1;
7655 if (!strcmp (opt, "precise")) {
7656 conservative_stack_mark = FALSE;
7657 } else if (!strcmp (opt, "conservative")) {
7658 conservative_stack_mark = TRUE;
7659 } else {
7660 fprintf (stderr, "Invalid value '%s' for stack-mark= option, possible values are: 'precise', 'conservative'.\n", opt);
7661 exit (1);
7663 continue;
7665 #ifdef USER_CONFIG
7666 if (g_str_has_prefix (opt, "nursery-size=")) {
7667 long val;
7668 opt = strchr (opt, '=') + 1;
7669 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &val)) {
7670 default_nursery_size = val;
7671 #ifdef SGEN_ALIGN_NURSERY
7672 if ((val & (val - 1))) {
7673 fprintf (stderr, "The nursery size must be a power of two.\n");
7674 exit (1);
7677 default_nursery_bits = 0;
7678 while (1 << (++ default_nursery_bits) != default_nursery_size)
7680 #endif
7681 } else {
7682 fprintf (stderr, "nursery-size must be an integer.\n");
7683 exit (1);
7685 continue;
7687 #endif
7688 if (!(major_collector.handle_gc_param && major_collector.handle_gc_param (opt))) {
7689 fprintf (stderr, "MONO_GC_PARAMS must be a comma-delimited list of one or more of the following:\n");
7690 fprintf (stderr, " max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
7691 fprintf (stderr, " soft-heap-limit=n (where N is an integer, possibly with a k, m or a g suffix)\n");
7692 fprintf (stderr, " nursery-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
7693 fprintf (stderr, " major=COLLECTOR (where COLLECTOR is `marksweep', `marksweep-par' or `copying')\n");
7694 fprintf (stderr, " wbarrier=WBARRIER (where WBARRIER is `remset' or `cardtable')\n");
7695 fprintf (stderr, " stack-mark=MARK-METHOD (where MARK-METHOD is 'precise' or 'conservative')\n");
7696 if (major_collector.print_gc_param_usage)
7697 major_collector.print_gc_param_usage ();
7698 exit (1);
7701 g_strfreev (opts);
7704 if (major_collector_opt)
7705 g_free (major_collector_opt);
7707 nursery_size = DEFAULT_NURSERY_SIZE;
7708 minor_collection_allowance = MIN_MINOR_COLLECTION_ALLOWANCE;
7709 init_heap_size_limits (max_heap, soft_limit);
7711 alloc_nursery ();
7713 if ((env = getenv ("MONO_GC_DEBUG"))) {
7714 opts = g_strsplit (env, ",", -1);
7715 for (ptr = opts; ptr && *ptr; ptr ++) {
7716 char *opt = *ptr;
7717 if (opt [0] >= '0' && opt [0] <= '9') {
7718 gc_debug_level = atoi (opt);
7719 opt++;
7720 if (opt [0] == ':')
7721 opt++;
7722 if (opt [0]) {
7723 char *rf = g_strdup_printf ("%s.%d", opt, getpid ());
7724 gc_debug_file = fopen (rf, "wb");
7725 if (!gc_debug_file)
7726 gc_debug_file = stderr;
7727 g_free (rf);
7729 } else if (!strcmp (opt, "print-allowance")) {
7730 debug_print_allowance = TRUE;
7731 } else if (!strcmp (opt, "verify-before-allocs")) {
7732 verify_before_allocs = 1;
7733 has_per_allocation_action = TRUE;
7734 } else if (g_str_has_prefix (opt, "verify-before-allocs=")) {
7735 char *arg = strchr (opt, '=') + 1;
7736 verify_before_allocs = atoi (arg);
7737 has_per_allocation_action = TRUE;
7738 } else if (!strcmp (opt, "collect-before-allocs")) {
7739 collect_before_allocs = 1;
7740 has_per_allocation_action = TRUE;
7741 } else if (g_str_has_prefix (opt, "collect-before-allocs=")) {
7742 char *arg = strchr (opt, '=') + 1;
7743 has_per_allocation_action = TRUE;
7744 collect_before_allocs = atoi (arg);
7745 } else if (!strcmp (opt, "verify-before-collections")) {
7746 whole_heap_check_before_collection = TRUE;
7747 } else if (!strcmp (opt, "check-at-minor-collections")) {
7748 consistency_check_at_minor_collection = TRUE;
7749 nursery_clear_policy = CLEAR_AT_GC;
7750 } else if (!strcmp (opt, "xdomain-checks")) {
7751 xdomain_checks = TRUE;
7752 } else if (!strcmp (opt, "clear-at-gc")) {
7753 nursery_clear_policy = CLEAR_AT_GC;
7754 } else if (!strcmp (opt, "clear-nursery-at-gc")) {
7755 nursery_clear_policy = CLEAR_AT_GC;
7756 } else if (!strcmp (opt, "check-scan-starts")) {
7757 do_scan_starts_check = TRUE;
7758 } else if (!strcmp (opt, "disable-minor")) {
7759 disable_minor_collections = TRUE;
7760 } else if (!strcmp (opt, "disable-major")) {
7761 disable_major_collections = TRUE;
7762 } else if (g_str_has_prefix (opt, "heap-dump=")) {
7763 char *filename = strchr (opt, '=') + 1;
7764 nursery_clear_policy = CLEAR_AT_GC;
7765 heap_dump_file = fopen (filename, "w");
7766 if (heap_dump_file)
7767 fprintf (heap_dump_file, "<sgen-dump>\n");
7768 #ifdef SGEN_BINARY_PROTOCOL
7769 } else if (g_str_has_prefix (opt, "binary-protocol=")) {
7770 char *filename = strchr (opt, '=') + 1;
7771 binary_protocol_file = fopen (filename, "w");
7772 #endif
7773 } else {
7774 fprintf (stderr, "Invalid format for the MONO_GC_DEBUG env variable: '%s'\n", env);
7775 fprintf (stderr, "The format is: MONO_GC_DEBUG=[l[:filename]|<option>]+ where l is a debug level 0-9.\n");
7776 fprintf (stderr, "Valid options are:\n");
7777 fprintf (stderr, " collect-before-allocs[=<n>]\n");
7778 fprintf (stderr, " verify-before-allocs[=<n>]\n");
7779 fprintf (stderr, " check-at-minor-collections\n");
7780 fprintf (stderr, " verify-before-collections\n");
7781 fprintf (stderr, " disable-minor\n");
7782 fprintf (stderr, " disable-major\n");
7783 fprintf (stderr, " xdomain-checks\n");
7784 fprintf (stderr, " clear-at-gc\n");
7785 exit (1);
7788 g_strfreev (opts);
7791 suspend_ack_semaphore_ptr = &suspend_ack_semaphore;
7792 MONO_SEM_INIT (&suspend_ack_semaphore, 0);
7794 sigfillset (&sinfo.sa_mask);
7795 sinfo.sa_flags = SA_RESTART | SA_SIGINFO;
7796 sinfo.sa_sigaction = suspend_handler;
7797 if (sigaction (suspend_signal_num, &sinfo, NULL) != 0) {
7798 g_error ("failed sigaction");
7801 sinfo.sa_handler = restart_handler;
7802 if (sigaction (restart_signal_num, &sinfo, NULL) != 0) {
7803 g_error ("failed sigaction");
7806 sigfillset (&suspend_signal_mask);
7807 sigdelset (&suspend_signal_mask, restart_signal_num);
7809 global_remset = alloc_remset (1024, NULL);
7810 global_remset->next = NULL;
7812 pthread_key_create (&remembered_set_key, unregister_thread);
7814 #ifndef HAVE_KW_THREAD
7815 pthread_key_create (&thread_info_key, NULL);
7816 #endif
7818 if (use_cardtable)
7819 card_table_init ();
7821 gc_initialized = TRUE;
7822 UNLOCK_GC;
7823 mono_gc_register_thread (&sinfo);
7827 mono_gc_get_suspend_signal (void)
7829 return suspend_signal_num;
7832 enum {
7833 ATYPE_NORMAL,
7834 ATYPE_VECTOR,
7835 ATYPE_SMALL,
7836 ATYPE_NUM
7839 #ifdef HAVE_KW_THREAD
7840 #define EMIT_TLS_ACCESS(mb,dummy,offset) do { \
7841 mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX); \
7842 mono_mb_emit_byte ((mb), CEE_MONO_TLS); \
7843 mono_mb_emit_i4 ((mb), (offset)); \
7844 } while (0)
7845 #else
7846 #define EMIT_TLS_ACCESS(mb,member,dummy) do { \
7847 mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX); \
7848 mono_mb_emit_byte ((mb), CEE_MONO_TLS); \
7849 mono_mb_emit_i4 ((mb), thread_info_key); \
7850 mono_mb_emit_icon ((mb), G_STRUCT_OFFSET (SgenThreadInfo, member)); \
7851 mono_mb_emit_byte ((mb), CEE_ADD); \
7852 mono_mb_emit_byte ((mb), CEE_LDIND_I); \
7853 } while (0)
7854 #endif
7856 #ifdef MANAGED_ALLOCATION
7857 /* FIXME: Do this in the JIT, where specialized allocation sequences can be created
7858 * for each class. This is currently not easy to do, as it is hard to generate basic
7859 * blocks + branches, but it is easy with the linear IL codebase.
7861 * For this to work we'd need to solve the TLAB race, first. Now we
7862 * require the allocator to be in a few known methods to make sure
7863 * that they are executed atomically via the restart mechanism.
7865 static MonoMethod*
7866 create_allocator (int atype)
7868 int p_var, size_var;
7869 guint32 slowpath_branch, max_size_branch;
7870 MonoMethodBuilder *mb;
7871 MonoMethod *res;
7872 MonoMethodSignature *csig;
7873 static gboolean registered = FALSE;
7874 int tlab_next_addr_var, new_next_var;
7875 int num_params, i;
7876 const char *name = NULL;
7877 AllocatorWrapperInfo *info;
7879 #ifdef HAVE_KW_THREAD
7880 int tlab_next_addr_offset = -1;
7881 int tlab_temp_end_offset = -1;
7883 MONO_THREAD_VAR_OFFSET (tlab_next_addr, tlab_next_addr_offset);
7884 MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
7886 g_assert (tlab_next_addr_offset != -1);
7887 g_assert (tlab_temp_end_offset != -1);
7888 #endif
7890 if (!registered) {
7891 mono_register_jit_icall (mono_gc_alloc_obj, "mono_gc_alloc_obj", mono_create_icall_signature ("object ptr int"), FALSE);
7892 mono_register_jit_icall (mono_gc_alloc_vector, "mono_gc_alloc_vector", mono_create_icall_signature ("object ptr int int"), FALSE);
7893 registered = TRUE;
7896 if (atype == ATYPE_SMALL) {
7897 num_params = 1;
7898 name = "AllocSmall";
7899 } else if (atype == ATYPE_NORMAL) {
7900 num_params = 1;
7901 name = "Alloc";
7902 } else if (atype == ATYPE_VECTOR) {
7903 num_params = 2;
7904 name = "AllocVector";
7905 } else {
7906 g_assert_not_reached ();
7909 csig = mono_metadata_signature_alloc (mono_defaults.corlib, num_params);
7910 csig->ret = &mono_defaults.object_class->byval_arg;
7911 for (i = 0; i < num_params; ++i)
7912 csig->params [i] = &mono_defaults.int_class->byval_arg;
7914 mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_ALLOC);
7915 size_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
7916 if (atype == ATYPE_NORMAL || atype == ATYPE_SMALL) {
7917 /* size = vtable->klass->instance_size; */
7918 mono_mb_emit_ldarg (mb, 0);
7919 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoVTable, klass));
7920 mono_mb_emit_byte (mb, CEE_ADD);
7921 mono_mb_emit_byte (mb, CEE_LDIND_I);
7922 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoClass, instance_size));
7923 mono_mb_emit_byte (mb, CEE_ADD);
7924 /* FIXME: assert instance_size stays a 4 byte integer */
7925 mono_mb_emit_byte (mb, CEE_LDIND_U4);
7926 mono_mb_emit_stloc (mb, size_var);
7927 } else if (atype == ATYPE_VECTOR) {
7928 MonoExceptionClause *clause;
7929 int pos, pos_leave;
7930 MonoClass *oom_exc_class;
7931 MonoMethod *ctor;
7933 /* n > MONO_ARRAY_MAX_INDEX -> OverflowException */
7934 mono_mb_emit_ldarg (mb, 1);
7935 mono_mb_emit_icon (mb, MONO_ARRAY_MAX_INDEX);
7936 pos = mono_mb_emit_short_branch (mb, CEE_BLE_UN_S);
7937 mono_mb_emit_exception (mb, "OverflowException", NULL);
7938 mono_mb_patch_short_branch (mb, pos);
7940 clause = mono_image_alloc0 (mono_defaults.corlib, sizeof (MonoExceptionClause));
7941 clause->try_offset = mono_mb_get_label (mb);
7943 /* vtable->klass->sizes.element_size */
7944 mono_mb_emit_ldarg (mb, 0);
7945 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoVTable, klass));
7946 mono_mb_emit_byte (mb, CEE_ADD);
7947 mono_mb_emit_byte (mb, CEE_LDIND_I);
7948 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoClass, sizes.element_size));
7949 mono_mb_emit_byte (mb, CEE_ADD);
7950 mono_mb_emit_byte (mb, CEE_LDIND_U4);
7952 /* * n */
7953 mono_mb_emit_ldarg (mb, 1);
7954 mono_mb_emit_byte (mb, CEE_MUL_OVF_UN);
7955 /* + sizeof (MonoArray) */
7956 mono_mb_emit_icon (mb, sizeof (MonoArray));
7957 mono_mb_emit_byte (mb, CEE_ADD_OVF_UN);
7958 mono_mb_emit_stloc (mb, size_var);
7960 pos_leave = mono_mb_emit_branch (mb, CEE_LEAVE);
7962 /* catch */
7963 clause->flags = MONO_EXCEPTION_CLAUSE_NONE;
7964 clause->try_len = mono_mb_get_pos (mb) - clause->try_offset;
7965 clause->data.catch_class = mono_class_from_name (mono_defaults.corlib,
7966 "System", "OverflowException");
7967 g_assert (clause->data.catch_class);
7968 clause->handler_offset = mono_mb_get_label (mb);
7970 oom_exc_class = mono_class_from_name (mono_defaults.corlib,
7971 "System", "OutOfMemoryException");
7972 g_assert (oom_exc_class);
7973 ctor = mono_class_get_method_from_name (oom_exc_class, ".ctor", 0);
7974 g_assert (ctor);
7976 mono_mb_emit_byte (mb, CEE_POP);
7977 mono_mb_emit_op (mb, CEE_NEWOBJ, ctor);
7978 mono_mb_emit_byte (mb, CEE_THROW);
7980 clause->handler_len = mono_mb_get_pos (mb) - clause->handler_offset;
7981 mono_mb_set_clauses (mb, 1, clause);
7982 mono_mb_patch_branch (mb, pos_leave);
7983 /* end catch */
7984 } else {
7985 g_assert_not_reached ();
7988 /* size += ALLOC_ALIGN - 1; */
7989 mono_mb_emit_ldloc (mb, size_var);
7990 mono_mb_emit_icon (mb, ALLOC_ALIGN - 1);
7991 mono_mb_emit_byte (mb, CEE_ADD);
7992 /* size &= ~(ALLOC_ALIGN - 1); */
7993 mono_mb_emit_icon (mb, ~(ALLOC_ALIGN - 1));
7994 mono_mb_emit_byte (mb, CEE_AND);
7995 mono_mb_emit_stloc (mb, size_var);
7997 /* if (size > MAX_SMALL_OBJ_SIZE) goto slowpath */
7998 if (atype != ATYPE_SMALL) {
7999 mono_mb_emit_ldloc (mb, size_var);
8000 mono_mb_emit_icon (mb, MAX_SMALL_OBJ_SIZE);
8001 max_size_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_S);
8005 * We need to modify tlab_next, but the JIT only supports reading, so we read
8006 * another tls var holding its address instead.
8009 /* tlab_next_addr (local) = tlab_next_addr (TLS var) */
8010 tlab_next_addr_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
8011 EMIT_TLS_ACCESS (mb, tlab_next_addr, tlab_next_addr_offset);
8012 mono_mb_emit_stloc (mb, tlab_next_addr_var);
8014 /* p = (void**)tlab_next; */
8015 p_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
8016 mono_mb_emit_ldloc (mb, tlab_next_addr_var);
8017 mono_mb_emit_byte (mb, CEE_LDIND_I);
8018 mono_mb_emit_stloc (mb, p_var);
8020 /* new_next = (char*)p + size; */
8021 new_next_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
8022 mono_mb_emit_ldloc (mb, p_var);
8023 mono_mb_emit_ldloc (mb, size_var);
8024 mono_mb_emit_byte (mb, CEE_CONV_I);
8025 mono_mb_emit_byte (mb, CEE_ADD);
8026 mono_mb_emit_stloc (mb, new_next_var);
8028 /* tlab_next = new_next */
8029 mono_mb_emit_ldloc (mb, tlab_next_addr_var);
8030 mono_mb_emit_ldloc (mb, new_next_var);
8031 mono_mb_emit_byte (mb, CEE_STIND_I);
8033 /* if (G_LIKELY (new_next < tlab_temp_end)) */
8034 mono_mb_emit_ldloc (mb, new_next_var);
8035 EMIT_TLS_ACCESS (mb, tlab_temp_end, tlab_temp_end_offset);
8036 slowpath_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
8038 /* Slowpath */
8039 if (atype != ATYPE_SMALL)
8040 mono_mb_patch_short_branch (mb, max_size_branch);
8042 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
8043 mono_mb_emit_byte (mb, CEE_MONO_NOT_TAKEN);
8045 /* FIXME: mono_gc_alloc_obj takes a 'size_t' as an argument, not an int32 */
8046 mono_mb_emit_ldarg (mb, 0);
8047 mono_mb_emit_ldloc (mb, size_var);
8048 if (atype == ATYPE_NORMAL || atype == ATYPE_SMALL) {
8049 mono_mb_emit_icall (mb, mono_gc_alloc_obj);
8050 } else if (atype == ATYPE_VECTOR) {
8051 mono_mb_emit_ldarg (mb, 1);
8052 mono_mb_emit_icall (mb, mono_gc_alloc_vector);
8053 } else {
8054 g_assert_not_reached ();
8056 mono_mb_emit_byte (mb, CEE_RET);
8058 /* Fastpath */
8059 mono_mb_patch_short_branch (mb, slowpath_branch);
8061 /* FIXME: Memory barrier */
8063 /* *p = vtable; */
8064 mono_mb_emit_ldloc (mb, p_var);
8065 mono_mb_emit_ldarg (mb, 0);
8066 mono_mb_emit_byte (mb, CEE_STIND_I);
8068 if (atype == ATYPE_VECTOR) {
8069 /* arr->max_length = max_length; */
8070 mono_mb_emit_ldloc (mb, p_var);
8071 mono_mb_emit_ldflda (mb, G_STRUCT_OFFSET (MonoArray, max_length));
8072 mono_mb_emit_ldarg (mb, 1);
8073 mono_mb_emit_byte (mb, CEE_STIND_I);
8076 /* return p */
8077 mono_mb_emit_ldloc (mb, p_var);
8078 mono_mb_emit_byte (mb, CEE_RET);
8080 res = mono_mb_create_method (mb, csig, 8);
8081 mono_mb_free (mb);
8082 mono_method_get_header (res)->init_locals = FALSE;
8084 info = mono_image_alloc0 (mono_defaults.corlib, sizeof (AllocatorWrapperInfo));
8085 info->gc_name = "sgen";
8086 info->alloc_type = atype;
8087 mono_marshal_set_wrapper_info (res, info);
8089 return res;
8091 #endif
8093 const char *
8094 mono_gc_get_gc_name (void)
8096 return "sgen";
8099 static MonoMethod* alloc_method_cache [ATYPE_NUM];
8100 static MonoMethod *write_barrier_method;
8102 static gboolean
8103 is_ip_in_managed_allocator (MonoDomain *domain, gpointer ip)
8105 MonoJitInfo *ji;
8106 MonoMethod *method;
8107 int i;
8109 if (!mono_thread_internal_current ())
8110 /* Happens during thread attach */
8111 return FALSE;
8113 if (!ip || !domain)
8114 return FALSE;
8115 ji = mono_jit_info_table_find (domain, ip);
8116 if (!ji)
8117 return FALSE;
8118 method = ji->method;
8120 if (method == write_barrier_method)
8121 return TRUE;
8122 for (i = 0; i < ATYPE_NUM; ++i)
8123 if (method == alloc_method_cache [i])
8124 return TRUE;
8125 return FALSE;
8129 * Generate an allocator method implementing the fast path of mono_gc_alloc_obj ().
8130 * The signature of the called method is:
8131 * object allocate (MonoVTable *vtable)
8133 MonoMethod*
8134 mono_gc_get_managed_allocator (MonoVTable *vtable, gboolean for_box)
8136 #ifdef MANAGED_ALLOCATION
8137 MonoClass *klass = vtable->klass;
8139 #ifdef HAVE_KW_THREAD
8140 int tlab_next_offset = -1;
8141 int tlab_temp_end_offset = -1;
8142 MONO_THREAD_VAR_OFFSET (tlab_next, tlab_next_offset);
8143 MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
8145 if (tlab_next_offset == -1 || tlab_temp_end_offset == -1)
8146 return NULL;
8147 #endif
8149 if (!mono_runtime_has_tls_get ())
8150 return NULL;
8151 if (klass->instance_size > tlab_size)
8152 return NULL;
8153 if (klass->has_finalize || klass->marshalbyref || (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS))
8154 return NULL;
8155 if (klass->rank)
8156 return NULL;
8157 if (klass->byval_arg.type == MONO_TYPE_STRING)
8158 return NULL;
8159 if (has_per_allocation_action)
8160 return NULL;
8162 if (ALIGN_TO (klass->instance_size, ALLOC_ALIGN) < MAX_SMALL_OBJ_SIZE)
8163 return mono_gc_get_managed_allocator_by_type (ATYPE_SMALL);
8164 else
8165 return mono_gc_get_managed_allocator_by_type (ATYPE_NORMAL);
8166 #else
8167 return NULL;
8168 #endif
8171 MonoMethod*
8172 mono_gc_get_managed_array_allocator (MonoVTable *vtable, int rank)
8174 #ifdef MANAGED_ALLOCATION
8175 MonoClass *klass = vtable->klass;
8177 #ifdef HAVE_KW_THREAD
8178 int tlab_next_offset = -1;
8179 int tlab_temp_end_offset = -1;
8180 MONO_THREAD_VAR_OFFSET (tlab_next, tlab_next_offset);
8181 MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
8183 if (tlab_next_offset == -1 || tlab_temp_end_offset == -1)
8184 return NULL;
8185 #endif
8187 if (rank != 1)
8188 return NULL;
8189 if (!mono_runtime_has_tls_get ())
8190 return NULL;
8191 if (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS)
8192 return NULL;
8193 if (collect_before_allocs)
8194 return NULL;
8195 g_assert (!mono_class_has_finalizer (klass) && !klass->marshalbyref);
8197 return mono_gc_get_managed_allocator_by_type (ATYPE_VECTOR);
8198 #else
8199 return NULL;
8200 #endif
8203 MonoMethod*
8204 mono_gc_get_managed_allocator_by_type (int atype)
8206 #ifdef MANAGED_ALLOCATION
8207 MonoMethod *res;
8209 if (!mono_runtime_has_tls_get ())
8210 return NULL;
8212 mono_loader_lock ();
8213 res = alloc_method_cache [atype];
8214 if (!res)
8215 res = alloc_method_cache [atype] = create_allocator (atype);
8216 mono_loader_unlock ();
8217 return res;
8218 #else
8219 return NULL;
8220 #endif
8223 guint32
8224 mono_gc_get_managed_allocator_types (void)
8226 return ATYPE_NUM;
8230 MonoMethod*
8231 mono_gc_get_write_barrier (void)
8233 MonoMethod *res;
8234 MonoMethodBuilder *mb;
8235 MonoMethodSignature *sig;
8236 #ifdef MANAGED_WBARRIER
8237 int label_no_wb_1, label_no_wb_2, label_no_wb_3, label_no_wb_4, label_need_wb, label_slow_path;
8238 #ifndef SGEN_ALIGN_NURSERY
8239 int label_continue_1, label_continue_2, label_no_wb_5;
8240 int dereferenced_var;
8241 #endif
8242 int buffer_var, buffer_index_var, dummy_var;
8244 #ifdef HAVE_KW_THREAD
8245 int stack_end_offset = -1, store_remset_buffer_offset = -1;
8246 int store_remset_buffer_index_offset = -1, store_remset_buffer_index_addr_offset = -1;
8248 MONO_THREAD_VAR_OFFSET (stack_end, stack_end_offset);
8249 g_assert (stack_end_offset != -1);
8250 MONO_THREAD_VAR_OFFSET (store_remset_buffer, store_remset_buffer_offset);
8251 g_assert (store_remset_buffer_offset != -1);
8252 MONO_THREAD_VAR_OFFSET (store_remset_buffer_index, store_remset_buffer_index_offset);
8253 g_assert (store_remset_buffer_index_offset != -1);
8254 MONO_THREAD_VAR_OFFSET (store_remset_buffer_index_addr, store_remset_buffer_index_addr_offset);
8255 g_assert (store_remset_buffer_index_addr_offset != -1);
8256 #endif
8257 #endif
8259 g_assert (!use_cardtable);
8261 // FIXME: Maybe create a separate version for ctors (the branch would be
8262 // correctly predicted more times)
8263 if (write_barrier_method)
8264 return write_barrier_method;
8266 /* Create the IL version of mono_gc_barrier_generic_store () */
8267 sig = mono_metadata_signature_alloc (mono_defaults.corlib, 1);
8268 sig->ret = &mono_defaults.void_class->byval_arg;
8269 sig->params [0] = &mono_defaults.int_class->byval_arg;
8271 mb = mono_mb_new (mono_defaults.object_class, "wbarrier", MONO_WRAPPER_WRITE_BARRIER);
8273 #ifdef MANAGED_WBARRIER
8274 if (mono_runtime_has_tls_get ()) {
8275 #ifdef SGEN_ALIGN_NURSERY
8276 // if (ptr_in_nursery (ptr)) return;
8278 * Masking out the bits might be faster, but we would have to use 64 bit
8279 * immediates, which might be slower.
8281 mono_mb_emit_ldarg (mb, 0);
8282 mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
8283 mono_mb_emit_byte (mb, CEE_SHR_UN);
8284 mono_mb_emit_icon (mb, (mword)nursery_start >> DEFAULT_NURSERY_BITS);
8285 label_no_wb_1 = mono_mb_emit_branch (mb, CEE_BEQ);
8287 // if (!ptr_in_nursery (*ptr)) return;
8288 mono_mb_emit_ldarg (mb, 0);
8289 mono_mb_emit_byte (mb, CEE_LDIND_I);
8290 mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
8291 mono_mb_emit_byte (mb, CEE_SHR_UN);
8292 mono_mb_emit_icon (mb, (mword)nursery_start >> DEFAULT_NURSERY_BITS);
8293 label_no_wb_2 = mono_mb_emit_branch (mb, CEE_BNE_UN);
8294 #else
8296 // if (ptr < (nursery_start)) goto continue;
8297 mono_mb_emit_ldarg (mb, 0);
8298 mono_mb_emit_ptr (mb, (gpointer) nursery_start);
8299 label_continue_1 = mono_mb_emit_branch (mb, CEE_BLT);
8301 // if (ptr >= nursery_real_end)) goto continue;
8302 mono_mb_emit_ldarg (mb, 0);
8303 mono_mb_emit_ptr (mb, (gpointer) nursery_real_end);
8304 label_continue_2 = mono_mb_emit_branch (mb, CEE_BGE);
8306 // Otherwise return
8307 label_no_wb_1 = mono_mb_emit_branch (mb, CEE_BR);
8309 // continue:
8310 mono_mb_patch_branch (mb, label_continue_1);
8311 mono_mb_patch_branch (mb, label_continue_2);
8313 // Dereference and store in local var
8314 dereferenced_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
8315 mono_mb_emit_ldarg (mb, 0);
8316 mono_mb_emit_byte (mb, CEE_LDIND_I);
8317 mono_mb_emit_stloc (mb, dereferenced_var);
8319 // if (*ptr < nursery_start) return;
8320 mono_mb_emit_ldloc (mb, dereferenced_var);
8321 mono_mb_emit_ptr (mb, (gpointer) nursery_start);
8322 label_no_wb_2 = mono_mb_emit_branch (mb, CEE_BLT);
8324 // if (*ptr >= nursery_end) return;
8325 mono_mb_emit_ldloc (mb, dereferenced_var);
8326 mono_mb_emit_ptr (mb, (gpointer) nursery_real_end);
8327 label_no_wb_5 = mono_mb_emit_branch (mb, CEE_BGE);
8329 #endif
8330 // if (ptr >= stack_end) goto need_wb;
8331 mono_mb_emit_ldarg (mb, 0);
8332 EMIT_TLS_ACCESS (mb, stack_end, stack_end_offset);
8333 label_need_wb = mono_mb_emit_branch (mb, CEE_BGE_UN);
8335 // if (ptr >= stack_start) return;
8336 dummy_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
8337 mono_mb_emit_ldarg (mb, 0);
8338 mono_mb_emit_ldloc_addr (mb, dummy_var);
8339 label_no_wb_3 = mono_mb_emit_branch (mb, CEE_BGE_UN);
8341 // need_wb:
8342 mono_mb_patch_branch (mb, label_need_wb);
8344 // buffer = STORE_REMSET_BUFFER;
8345 buffer_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
8346 EMIT_TLS_ACCESS (mb, store_remset_buffer, store_remset_buffer_offset);
8347 mono_mb_emit_stloc (mb, buffer_var);
8349 // buffer_index = STORE_REMSET_BUFFER_INDEX;
8350 buffer_index_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
8351 EMIT_TLS_ACCESS (mb, store_remset_buffer_index, store_remset_buffer_index_offset);
8352 mono_mb_emit_stloc (mb, buffer_index_var);
8354 // if (buffer [buffer_index] == ptr) return;
8355 mono_mb_emit_ldloc (mb, buffer_var);
8356 mono_mb_emit_ldloc (mb, buffer_index_var);
8357 g_assert (sizeof (gpointer) == 4 || sizeof (gpointer) == 8);
8358 mono_mb_emit_icon (mb, sizeof (gpointer) == 4 ? 2 : 3);
8359 mono_mb_emit_byte (mb, CEE_SHL);
8360 mono_mb_emit_byte (mb, CEE_ADD);
8361 mono_mb_emit_byte (mb, CEE_LDIND_I);
8362 mono_mb_emit_ldarg (mb, 0);
8363 label_no_wb_4 = mono_mb_emit_branch (mb, CEE_BEQ);
8365 // ++buffer_index;
8366 mono_mb_emit_ldloc (mb, buffer_index_var);
8367 mono_mb_emit_icon (mb, 1);
8368 mono_mb_emit_byte (mb, CEE_ADD);
8369 mono_mb_emit_stloc (mb, buffer_index_var);
8371 // if (buffer_index >= STORE_REMSET_BUFFER_SIZE) goto slow_path;
8372 mono_mb_emit_ldloc (mb, buffer_index_var);
8373 mono_mb_emit_icon (mb, STORE_REMSET_BUFFER_SIZE);
8374 label_slow_path = mono_mb_emit_branch (mb, CEE_BGE);
8376 // buffer [buffer_index] = ptr;
8377 mono_mb_emit_ldloc (mb, buffer_var);
8378 mono_mb_emit_ldloc (mb, buffer_index_var);
8379 g_assert (sizeof (gpointer) == 4 || sizeof (gpointer) == 8);
8380 mono_mb_emit_icon (mb, sizeof (gpointer) == 4 ? 2 : 3);
8381 mono_mb_emit_byte (mb, CEE_SHL);
8382 mono_mb_emit_byte (mb, CEE_ADD);
8383 mono_mb_emit_ldarg (mb, 0);
8384 mono_mb_emit_byte (mb, CEE_STIND_I);
8386 // STORE_REMSET_BUFFER_INDEX = buffer_index;
8387 EMIT_TLS_ACCESS (mb, store_remset_buffer_index_addr, store_remset_buffer_index_addr_offset);
8388 mono_mb_emit_ldloc (mb, buffer_index_var);
8389 mono_mb_emit_byte (mb, CEE_STIND_I);
8391 // return;
8392 mono_mb_patch_branch (mb, label_no_wb_1);
8393 mono_mb_patch_branch (mb, label_no_wb_2);
8394 mono_mb_patch_branch (mb, label_no_wb_3);
8395 mono_mb_patch_branch (mb, label_no_wb_4);
8396 #ifndef SGEN_ALIGN_NURSERY
8397 mono_mb_patch_branch (mb, label_no_wb_5);
8398 #endif
8399 mono_mb_emit_byte (mb, CEE_RET);
8401 // slow path
8402 mono_mb_patch_branch (mb, label_slow_path);
8404 #endif
8406 mono_mb_emit_ldarg (mb, 0);
8407 mono_mb_emit_icall (mb, mono_gc_wbarrier_generic_nostore);
8408 mono_mb_emit_byte (mb, CEE_RET);
8410 res = mono_mb_create_method (mb, sig, 16);
8411 mono_mb_free (mb);
8413 mono_loader_lock ();
8414 if (write_barrier_method) {
8415 /* Already created */
8416 mono_free_method (res);
8417 } else {
8418 /* double-checked locking */
8419 mono_memory_barrier ();
8420 write_barrier_method = res;
8422 mono_loader_unlock ();
8424 return write_barrier_method;
8427 char*
8428 mono_gc_get_description (void)
8430 return g_strdup ("sgen");
8433 void
8434 mono_gc_set_desktop_mode (void)
8438 gboolean
8439 mono_gc_is_moving (void)
8441 return TRUE;
8444 gboolean
8445 mono_gc_is_disabled (void)
8447 return FALSE;
8450 void
8451 mono_sgen_debug_printf (int level, const char *format, ...)
8453 va_list ap;
8455 if (level > gc_debug_level)
8456 return;
8458 va_start (ap, format);
8459 vfprintf (gc_debug_file, format, ap);
8460 va_end (ap);
8463 FILE*
8464 mono_sgen_get_logfile (void)
8466 return gc_debug_file;
8469 void
8470 mono_sgen_gc_lock (void)
8472 LOCK_GC;
8475 void
8476 mono_sgen_gc_unlock (void)
8478 UNLOCK_GC;
8482 void mono_gc_set_skip_thread (gboolean skip)
8484 SgenThreadInfo *info = mono_sgen_thread_info_lookup (ARCH_GET_THREAD ());
8486 LOCK_GC;
8487 info->gc_disabled = skip;
8488 UNLOCK_GC;
8491 guint
8492 mono_gc_get_vtable_bits (MonoClass *class)
8494 if (mono_sgen_need_bridge_processing () && mono_sgen_is_bridge_class (class))
8495 return SGEN_GC_BIT_BRIDGE_OBJECT;
8496 return 0;
8499 #endif /* HAVE_SGEN_GC */