3 * GC implementation using either the installed or included Boehm GC.
5 * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
6 * Copyright 2004-2011 Novell, Inc (http://www.novell.com)
7 * Copyright 2011-2012 Xamarin, Inc (http://www.xamarin.com)
8 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
15 #define GC_I_HIDE_POINTERS
16 #include <mono/metadata/gc-internals.h>
17 #include <mono/metadata/mono-gc.h>
18 #include <mono/metadata/profiler-private.h>
19 #include <mono/metadata/class-internals.h>
20 #include <mono/metadata/method-builder.h>
21 #include <mono/metadata/method-builder-ilgen.h>
22 #include <mono/metadata/method-builder-ilgen-internals.h>
23 #include <mono/metadata/opcodes.h>
24 #include <mono/metadata/domain-internals.h>
25 #include <mono/metadata/metadata-internals.h>
26 #include <mono/metadata/marshal.h>
27 #include <mono/metadata/runtime.h>
28 #include <mono/metadata/handle.h>
29 #include <mono/metadata/sgen-toggleref.h>
30 #include <mono/metadata/w32handle.h>
31 #include <mono/metadata/abi-details.h>
32 #include <mono/utils/atomic.h>
33 #include <mono/utils/mono-logger-internals.h>
34 #include <mono/utils/mono-memory-model.h>
35 #include <mono/utils/mono-time.h>
36 #include <mono/utils/mono-threads.h>
37 #include <mono/utils/dtrace.h>
38 #include <mono/utils/gc_wrapper.h>
39 #include <mono/utils/mono-os-mutex.h>
40 #include <mono/utils/mono-counters.h>
41 #include <mono/utils/mono-compiler.h>
42 #include <mono/utils/unlocked.h>
43 #include <mono/metadata/icall-decl.h>
49 #define THREAD_LOCAL_ALLOC 1
50 #include "private/pthread_support.h"
52 #if defined(HOST_DARWIN) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
53 void *pthread_get_stackaddr_np(pthread_t
);
56 #define GC_NO_DESCRIPTOR ((gpointer)(0 | GC_DS_LENGTH))
57 /*Boehm max heap cannot be smaller than 16MB*/
58 #define MIN_BOEHM_MAX_HEAP_SIZE_IN_MB 16
59 #define MIN_BOEHM_MAX_HEAP_SIZE (MIN_BOEHM_MAX_HEAP_SIZE_IN_MB << 20)
61 static gboolean gc_initialized
= FALSE
;
62 static gboolean gc_dont_gc_env
= FALSE
;
63 static mono_mutex_t mono_gc_lock
;
65 typedef void (*GC_push_other_roots_proc
)(void);
67 static GC_push_other_roots_proc default_push_other_roots
;
68 static GHashTable
*roots
;
71 mono_push_other_roots(void);
74 register_test_toggleref_callback (void);
76 #define BOEHM_GC_BIT_FINALIZER_AWARE 1
77 static MonoGCFinalizerCallbacks fin_callbacks
;
81 static mono_mutex_t handle_section
;
82 #define lock_handles(handles) mono_os_mutex_lock (&handle_section)
83 #define unlock_handles(handles) mono_os_mutex_unlock (&handle_section)
90 guint slot_hint
: 24; /* starting slot for search in bitmap */
91 /* 2^16 appdomains should be enough for everyone (though I know I'll regret this in 20 years) */
92 /* we alloc this only for weak refs, since we can get the domain directly in the other cases */
96 #define EMPTY_HANDLE_DATA(type) {NULL, NULL, 0, (type), 0, NULL}
98 /* weak and weak-track arrays will be allocated in malloc memory
100 static HandleData gc_handles
[] = {
101 EMPTY_HANDLE_DATA (HANDLE_WEAK
),
102 EMPTY_HANDLE_DATA (HANDLE_WEAK_TRACK
),
103 EMPTY_HANDLE_DATA (HANDLE_NORMAL
),
104 EMPTY_HANDLE_DATA (HANDLE_PINNED
)
108 mono_gc_warning (char *msg
, GC_word arg
)
110 mono_trace (G_LOG_LEVEL_WARNING
, MONO_TRACE_GC
, msg
, (unsigned long)arg
);
113 static void on_gc_notification (GC_EventType event
);
114 static void on_gc_heap_resize (size_t new_size
);
117 mono_gc_base_init (void)
124 mono_counters_init ();
127 mono_w32handle_init ();
131 * Handle the case when we are called from a thread different from the main thread,
133 * FIXME: Move this to libgc where it belongs.
135 * we used to do this only when running on valgrind,
136 * but it happens also in other setups.
138 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
143 pthread_getattr_np (pthread_self (), &attr
);
144 pthread_attr_getstack (&attr
, &sstart
, &size
);
145 pthread_attr_destroy (&attr
);
146 /*g_print ("stackbottom pth is: %p\n", (char*)sstart + size);*/
147 /* apparently with some linuxthreads implementations sstart can be NULL,
148 * fallback to the more imprecise method (bug# 78096).
151 GC_stackbottom
= (char*)sstart
+ size
;
154 gsize stack_bottom
= (gsize
)&dummy
;
155 stack_bottom
+= 4095;
156 stack_bottom
&= ~4095;
157 GC_stackbottom
= (char*)stack_bottom
;
160 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
161 GC_stackbottom
= (char*)pthread_get_stackaddr_np (pthread_self ());
162 #elif defined(__OpenBSD__)
163 # include <pthread_np.h>
168 rslt
= pthread_stackseg_np(pthread_self(), &ss
);
169 g_assert (rslt
== 0);
171 GC_stackbottom
= (char*)ss
.ss_sp
;
176 gsize stack_bottom
= (gsize
)&dummy
;
177 stack_bottom
+= 4095;
178 stack_bottom
&= ~4095;
179 /*g_print ("stackbottom is: %p\n", (char*)stack_bottom);*/
180 GC_stackbottom
= (char*)stack_bottom
;
184 roots
= g_hash_table_new (NULL
, NULL
);
185 default_push_other_roots
= GC_push_other_roots
;
186 GC_push_other_roots
= mono_push_other_roots
;
188 #if !defined(HOST_ANDROID)
189 /* If GC_no_dls is set to true, GC_find_limit is not called. This causes a seg fault on Android. */
193 if ((env
= g_getenv ("MONO_GC_DEBUG"))) {
194 char **opts
= g_strsplit (env
, ",", -1);
195 for (char **ptr
= opts
; ptr
&& *ptr
; ptr
++) {
197 if (!strcmp (opt
, "do-not-finalize")) {
198 mono_do_not_finalize
= 1;
199 } else if (!strcmp (opt
, "log-finalizers")) {
200 mono_log_finalizers
= 1;
207 /* cache value rather than calling during collection since g_hasenv may take locks and can deadlock */
208 gc_dont_gc_env
= g_hasenv ("GC_DONT_GC");
212 GC_set_warn_proc (mono_gc_warning
);
213 GC_finalize_on_demand
= 1;
214 GC_finalizer_notifier
= mono_gc_finalize_notify
;
216 GC_init_gcj_malloc (5, NULL
);
217 GC_allow_register_threads ();
219 if ((env
= g_getenv ("MONO_GC_PARAMS"))) {
220 char **ptr
, **opts
= g_strsplit (env
, ",", -1);
221 for (ptr
= opts
; *ptr
; ++ptr
) {
223 if (g_str_has_prefix (opt
, "max-heap-size=")) {
226 opt
= strchr (opt
, '=') + 1;
227 if (*opt
&& mono_gc_parse_environment_string_extract_number (opt
, &max_heap
)) {
228 if (max_heap
< MIN_BOEHM_MAX_HEAP_SIZE
) {
229 fprintf (stderr
, "max-heap-size must be at least %dMb.\n", MIN_BOEHM_MAX_HEAP_SIZE_IN_MB
);
232 GC_set_max_heap_size (max_heap
);
234 fprintf (stderr
, "max-heap-size must be an integer.\n");
238 } else if (g_str_has_prefix (opt
, "toggleref-test")) {
239 register_test_toggleref_callback ();
242 /* Could be a parameter for sgen */
244 fprintf (stderr, "MONO_GC_PARAMS must be a comma-delimited list of one or more of the following:\n");
245 fprintf (stderr, " max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
254 mono_thread_callbacks_init ();
255 mono_thread_info_init (sizeof (MonoThreadInfo
));
256 mono_os_mutex_init (&mono_gc_lock
);
257 mono_os_mutex_init_recursive (&handle_section
);
259 mono_thread_info_attach ();
261 GC_set_on_collection_event (on_gc_notification
);
262 GC_on_heap_resize
= on_gc_heap_resize
;
264 gc_initialized
= TRUE
;
268 mono_gc_base_cleanup (void)
270 GC_finalizer_notifier
= NULL
;
274 mono_gc_init_icalls (void)
280 * \param generation GC generation identifier
282 * Perform a garbage collection for the given generation, higher numbers
283 * mean usually older objects. Collecting a high-numbered generation
284 * implies collecting also the lower-numbered generations.
285 * The maximum value for \p generation can be retrieved with a call to
286 * \c mono_gc_max_generation, so this function is usually called as:
288 * <code>mono_gc_collect (mono_gc_max_generation ());</code>
291 mono_gc_collect (int generation
)
293 #ifndef DISABLE_PERFCOUNTERS
294 mono_atomic_inc_i32 (&mono_perfcounters
->gc_induced
);
300 * mono_gc_max_generation:
302 * Get the maximum generation number used by the current garbage
303 * collector. The value will be 0 for the Boehm collector, 1 or more
304 * for the generational collectors.
306 * Returns: the maximum generation number.
309 mono_gc_max_generation (void)
315 mono_gc_get_allocated_bytes_for_current_thread (void)
321 * mono_gc_get_generation:
322 * \param object a managed object
324 * Get the garbage collector's generation that \p object belongs to.
325 * Use this has a hint only.
327 * \returns a garbage collector generation number
330 mono_gc_get_generation (MonoObject
*object
)
336 * mono_gc_collection_count:
337 * \param generation a GC generation number
339 * Get how many times a garbage collection has been performed
340 * for the given \p generation number.
342 * \returns the number of garbage collections
345 mono_gc_collection_count (int generation
)
351 mono_gc_stop_world ()
353 g_assert ("mono_gc_stop_world is not supported in Boehm");
357 mono_gc_restart_world ()
359 g_assert ("mono_gc_restart_world is not supported in Boehm");
363 * mono_gc_add_memory_pressure:
364 * \param value amount of bytes
366 * Adjust the garbage collector's view of how many bytes of memory
367 * are indirectly referenced by managed objects (for example unmanaged
368 * memory holding image or other binary data).
369 * This is a hint only to the garbage collector algorithm.
370 * Note that negative amounts of p value will decrease the memory
374 mono_gc_add_memory_pressure (gint64 value
)
379 * mono_gc_get_used_size:
381 * Get the approximate amount of memory used by managed objects.
383 * Returns: the amount of memory used in bytes
386 mono_gc_get_used_size (void)
388 return GC_get_heap_size () - GC_get_free_bytes ();
392 * mono_gc_get_heap_size:
394 * Get the amount of memory used by the garbage collector.
396 * Returns: the size of the heap in bytes
399 mono_gc_get_heap_size (void)
401 return GC_get_heap_size ();
405 mono_gc_is_gc_thread (void)
407 return GC_thread_is_registered ();
411 mono_gc_thread_attach (MonoThreadInfo
* info
)
413 struct GC_stack_base sb
;
416 /* TODO: use GC_get_stack_base instead of baseptr. */
417 sb
.mem_base
= info
->stack_end
;
418 res
= GC_register_my_thread (&sb
);
419 if (res
== GC_UNIMPLEMENTED
)
420 return NULL
; /* Cannot happen with GC v7+. */
422 info
->handle_stack
= mono_handle_stack_alloc ();
428 mono_gc_thread_detach_with_lock (MonoThreadInfo
*p
)
430 MonoNativeThreadId tid
;
432 tid
= mono_thread_info_get_tid (p
);
434 if (p
->runtime_thread
)
435 mono_threads_add_joinable_thread ((gpointer
)tid
);
437 mono_handle_stack_free (p
->handle_stack
);
438 p
->handle_stack
= NULL
;
442 mono_gc_thread_in_critical_region (MonoThreadInfo
*info
)
448 mono_object_is_alive (MonoObject
* o
)
450 return GC_is_marked ((ptr_t
)o
);
454 mono_gc_walk_heap (int flags
, MonoGCReferences callback
, void *data
)
459 static gint64 gc_start_time
;
462 on_gc_notification (GC_EventType event
)
464 MonoProfilerGCEvent e
;
467 case GC_EVENT_PRE_STOP_WORLD
:
468 e
= MONO_GC_EVENT_PRE_STOP_WORLD
;
469 MONO_GC_WORLD_STOP_BEGIN ();
472 case GC_EVENT_POST_STOP_WORLD
:
473 e
= MONO_GC_EVENT_POST_STOP_WORLD
;
474 MONO_GC_WORLD_STOP_END ();
477 case GC_EVENT_PRE_START_WORLD
:
478 e
= MONO_GC_EVENT_PRE_START_WORLD
;
479 MONO_GC_WORLD_RESTART_BEGIN (1);
482 case GC_EVENT_POST_START_WORLD
:
483 e
= MONO_GC_EVENT_POST_START_WORLD
;
484 MONO_GC_WORLD_RESTART_END (1);
488 e
= MONO_GC_EVENT_START
;
490 #ifndef DISABLE_PERFCOUNTERS
491 if (mono_perfcounters
)
492 mono_atomic_inc_i32 (&mono_perfcounters
->gc_collections0
);
494 mono_atomic_inc_i32 (&mono_gc_stats
.major_gc_count
);
495 gc_start_time
= mono_100ns_ticks ();
499 e
= MONO_GC_EVENT_END
;
501 #if defined(ENABLE_DTRACE) && defined(__sun__)
502 /* This works around a dtrace -G problem on Solaris.
503 Limit its actual use to when the probe is enabled. */
504 if (MONO_GC_END_ENABLED ())
508 #ifndef DISABLE_PERFCOUNTERS
509 if (mono_perfcounters
) {
510 guint64 heap_size
= GC_get_heap_size ();
511 guint64 used_size
= heap_size
- GC_get_free_bytes ();
512 /* FIXME: change these to mono_atomic_store_i64 () */
513 UnlockedWrite64 (&mono_perfcounters
->gc_total_bytes
, used_size
);
514 UnlockedWrite64 (&mono_perfcounters
->gc_committed_bytes
, heap_size
);
515 UnlockedWrite64 (&mono_perfcounters
->gc_reserved_bytes
, heap_size
);
516 UnlockedWrite64 (&mono_perfcounters
->gc_gen0size
, heap_size
);
519 UnlockedAdd64 (&mono_gc_stats
.major_gc_time
, mono_100ns_ticks () - gc_start_time
);
520 mono_trace_message (MONO_TRACE_GC
, "gc took %" G_GINT64_FORMAT
" usecs", (mono_100ns_ticks () - gc_start_time
) / 10);
527 case GC_EVENT_MARK_START
:
528 case GC_EVENT_MARK_END
:
529 case GC_EVENT_RECLAIM_START
:
530 case GC_EVENT_RECLAIM_END
:
533 MONO_PROFILER_RAISE (gc_event
, (e
, 0, TRUE
));
538 case GC_EVENT_PRE_STOP_WORLD
:
539 mono_thread_info_suspend_lock ();
540 MONO_PROFILER_RAISE (gc_event
, (MONO_GC_EVENT_PRE_STOP_WORLD_LOCKED
, 0, TRUE
));
542 case GC_EVENT_POST_START_WORLD
:
543 mono_thread_info_suspend_unlock ();
544 MONO_PROFILER_RAISE (gc_event
, (MONO_GC_EVENT_POST_START_WORLD_UNLOCKED
, 0, TRUE
));
553 on_gc_heap_resize (size_t new_size
)
555 guint64 heap_size
= GC_get_heap_size ();
556 #ifndef DISABLE_PERFCOUNTERS
557 if (mono_perfcounters
) {
558 /* FIXME: change these to mono_atomic_store_i64 () */
559 UnlockedWrite64 (&mono_perfcounters
->gc_committed_bytes
, heap_size
);
560 UnlockedWrite64 (&mono_perfcounters
->gc_reserved_bytes
, heap_size
);
561 UnlockedWrite64 (&mono_perfcounters
->gc_gen0size
, heap_size
);
565 MONO_PROFILER_RAISE (gc_resize
, (new_size
));
574 register_root (gpointer arg
)
576 RootData
* root_data
= (RootData
*)arg
;
577 g_hash_table_insert (roots
, root_data
->start
, root_data
->end
);
582 mono_gc_register_root (char *start
, size_t size
, void *descr
, MonoGCRootSource source
, void *key
, const char *msg
)
585 root_data
.start
= start
;
586 /* Boehm root processing requires one byte past end of region to be scanned */
587 root_data
.end
= start
+ size
+ 1;
588 GC_call_with_alloc_lock (register_root
, &root_data
);
589 MONO_PROFILER_RAISE (gc_root_register
, ((const mono_byte
*) start
, size
, source
, key
, msg
));
594 mono_gc_register_root_wbarrier (char *start
, size_t size
, MonoGCDescriptor descr
, MonoGCRootSource source
, void *key
, const char *msg
)
596 return mono_gc_register_root (start
, size
, descr
, source
, key
, msg
);
600 deregister_root (gpointer arg
)
602 gboolean removed
= g_hash_table_remove (roots
, arg
);
608 mono_gc_deregister_root (char* addr
)
610 GC_call_with_alloc_lock (deregister_root
, addr
);
611 MONO_PROFILER_RAISE (gc_root_unregister
, ((const mono_byte
*) addr
));
615 push_root (gpointer key
, gpointer value
, gpointer user_data
)
617 GC_push_all (key
, value
);
621 push_handle_stack (HandleStack
* stack
)
623 HandleChunk
*cur
= stack
->bottom
;
624 HandleChunk
*last
= stack
->top
;
631 GC_push_all ((gpointer
)&cur
->elems
[0], (char*)(cur
->elems
+ cur
->size
) + 1);
639 mono_push_other_roots (void)
641 g_hash_table_foreach (roots
, push_root
, NULL
);
642 FOREACH_THREAD_EXCLUDE (info
, MONO_THREAD_INFO_FLAGS_NO_GC
) {
643 HandleStack
* stack
= info
->handle_stack
;
645 push_handle_stack (stack
);
647 if (default_push_other_roots
)
648 default_push_other_roots ();
652 mono_gc_weak_link_add (void **link_addr
, MonoObject
*obj
, gboolean track
)
654 /* libgc requires that we use HIDE_POINTER... */
655 *link_addr
= (void*)HIDE_POINTER (obj
);
657 GC_REGISTER_LONG_LINK (link_addr
, obj
);
659 GC_GENERAL_REGISTER_DISAPPEARING_LINK (link_addr
, obj
);
663 mono_gc_weak_link_remove (void **link_addr
, gboolean track
)
666 GC_unregister_long_link (link_addr
);
668 GC_unregister_disappearing_link (link_addr
);
673 reveal_link (gpointer link_addr
)
675 void **link_a
= (void **)link_addr
;
676 return REVEAL_POINTER (*link_a
);
680 mono_gc_weak_link_get (void **link_addr
)
682 MonoObject
*obj
= (MonoObject
*)GC_call_with_alloc_lock (reveal_link
, link_addr
);
683 if (obj
== (MonoObject
*) -1)
689 mono_gc_make_descr_for_string (gsize
*bitmap
, int numbits
)
691 return mono_gc_make_descr_from_bitmap (bitmap
, numbits
);
695 mono_gc_make_descr_for_object (gsize
*bitmap
, int numbits
, size_t obj_size
)
697 return mono_gc_make_descr_from_bitmap (bitmap
, numbits
);
701 mono_gc_make_descr_for_array (int vector
, gsize
*elem_bitmap
, int numbits
, size_t elem_size
)
703 /* libgc has no usable support for arrays... */
704 return GC_NO_DESCRIPTOR
;
708 mono_gc_make_descr_from_bitmap (gsize
*bitmap
, int numbits
)
710 /* It seems there are issues when the bitmap doesn't fit: play it safe */
712 return GC_NO_DESCRIPTOR
;
714 return (gpointer
)GC_make_descriptor ((GC_bitmap
)bitmap
, numbits
);
718 mono_gc_make_vector_descr (void)
724 mono_gc_make_root_descr_all_refs (int numbits
)
730 mono_gc_alloc_fixed (size_t size
, void *descr
, MonoGCRootSource source
, void *key
, const char *msg
)
732 void *start
= GC_MALLOC_UNCOLLECTABLE (size
);
733 MONO_PROFILER_RAISE (gc_root_register
, ((const mono_byte
*) start
, size
, source
, key
, msg
));
734 return (MonoObject
*)start
;
738 mono_gc_alloc_fixed_no_descriptor (size_t size
, MonoGCRootSource source
, void *key
, const char *msg
)
740 return mono_gc_alloc_fixed (size
, 0, source
, key
, msg
);
744 mono_gc_free_fixed (void* addr
)
746 MONO_PROFILER_RAISE (gc_root_unregister
, ((const mono_byte
*) addr
));
751 mono_gc_alloc_obj (MonoVTable
*vtable
, size_t size
)
755 if (!m_class_has_references (vtable
->klass
)) {
756 obj
= (MonoObject
*)GC_MALLOC_ATOMIC (size
);
757 if (G_UNLIKELY (!obj
))
760 obj
->vtable
= vtable
;
761 obj
->synchronisation
= NULL
;
763 memset (mono_object_get_data (obj
), 0, size
- MONO_ABI_SIZEOF (MonoObject
));
764 } else if (vtable
->gc_descr
!= GC_NO_DESCRIPTOR
) {
765 obj
= (MonoObject
*)GC_GCJ_MALLOC (size
, vtable
);
766 if (G_UNLIKELY (!obj
))
769 obj
= (MonoObject
*)GC_MALLOC (size
);
770 if (G_UNLIKELY (!obj
))
773 obj
->vtable
= vtable
;
776 if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
777 MONO_PROFILER_RAISE (gc_allocation
, (obj
));
783 mono_gc_alloc_vector (MonoVTable
*vtable
, size_t size
, uintptr_t max_length
)
787 if (!m_class_has_references (vtable
->klass
)) {
788 obj
= (MonoArray
*)GC_MALLOC_ATOMIC (size
);
789 if (G_UNLIKELY (!obj
))
792 obj
->obj
.vtable
= vtable
;
793 obj
->obj
.synchronisation
= NULL
;
795 memset (mono_object_get_data ((MonoObject
*)obj
), 0, size
- MONO_ABI_SIZEOF (MonoObject
));
796 } else if (vtable
->gc_descr
!= GC_NO_DESCRIPTOR
) {
797 obj
= (MonoArray
*)GC_GCJ_MALLOC (size
, vtable
);
798 if (G_UNLIKELY (!obj
))
801 obj
= (MonoArray
*)GC_MALLOC (size
);
802 if (G_UNLIKELY (!obj
))
805 obj
->obj
.vtable
= vtable
;
808 obj
->max_length
= max_length
;
810 if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
811 MONO_PROFILER_RAISE (gc_allocation
, (&obj
->obj
));
817 mono_gc_alloc_array (MonoVTable
*vtable
, size_t size
, uintptr_t max_length
, uintptr_t bounds_size
)
821 if (!m_class_has_references (vtable
->klass
)) {
822 obj
= (MonoArray
*)GC_MALLOC_ATOMIC (size
);
823 if (G_UNLIKELY (!obj
))
826 obj
->obj
.vtable
= vtable
;
827 obj
->obj
.synchronisation
= NULL
;
829 memset (mono_object_get_data ((MonoObject
*)obj
), 0, size
- MONO_ABI_SIZEOF (MonoObject
));
830 } else if (vtable
->gc_descr
!= GC_NO_DESCRIPTOR
) {
831 obj
= (MonoArray
*)GC_GCJ_MALLOC (size
, vtable
);
832 if (G_UNLIKELY (!obj
))
835 obj
= (MonoArray
*)GC_MALLOC (size
);
836 if (G_UNLIKELY (!obj
))
839 obj
->obj
.vtable
= vtable
;
842 obj
->max_length
= max_length
;
845 obj
->bounds
= (MonoArrayBounds
*) ((char *) obj
+ size
- bounds_size
);
847 if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
848 MONO_PROFILER_RAISE (gc_allocation
, (&obj
->obj
));
854 mono_gc_alloc_string (MonoVTable
*vtable
, size_t size
, gint32 len
)
856 MonoString
*obj
= (MonoString
*)GC_MALLOC_ATOMIC (size
);
857 if (G_UNLIKELY (!obj
))
860 obj
->object
.vtable
= vtable
;
861 obj
->object
.synchronisation
= NULL
;
863 obj
->chars
[len
] = 0;
865 if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
866 MONO_PROFILER_RAISE (gc_allocation
, (&obj
->object
));
872 mono_gc_alloc_mature (MonoVTable
*vtable
, size_t size
)
874 return mono_gc_alloc_obj (vtable
, size
);
878 mono_gc_alloc_pinned_obj (MonoVTable
*vtable
, size_t size
)
880 return mono_gc_alloc_obj (vtable
, size
);
884 mono_gc_invoke_finalizers (void)
886 /* There is a bug in GC_invoke_finalizer () in versions <= 6.2alpha4:
887 * the 'mem_freed' variable is not initialized when there are no
888 * objects to finalize, which leads to strange behavior later on.
889 * The check is necessary to work around that bug.
891 if (GC_should_invoke_finalizers ())
892 return GC_invoke_finalizers ();
897 mono_gc_pending_finalizers (void)
899 return GC_should_invoke_finalizers ();
903 mono_gc_wbarrier_set_field_internal (MonoObject
*obj
, gpointer field_ptr
, MonoObject
* value
)
905 *(void**)field_ptr
= value
;
909 mono_gc_wbarrier_set_arrayref_internal (MonoArray
*arr
, gpointer slot_ptr
, MonoObject
* value
)
911 *(void**)slot_ptr
= value
;
915 mono_gc_wbarrier_arrayref_copy_internal (gpointer dest_ptr
, gpointer src_ptr
, int count
)
917 mono_gc_memmove_aligned (dest_ptr
, src_ptr
, count
* sizeof (gpointer
));
921 mono_gc_wbarrier_generic_store_internal (gpointer ptr
, MonoObject
* value
)
923 *(void**)ptr
= value
;
927 mono_gc_wbarrier_generic_store_atomic_internal (gpointer ptr
, MonoObject
*value
)
929 mono_atomic_store_ptr ((volatile gpointer
*)ptr
, value
);
933 mono_gc_wbarrier_generic_nostore_internal (gpointer ptr
)
938 mono_gc_wbarrier_value_copy_internal (gpointer dest
, gpointer src
, int count
, MonoClass
*klass
)
940 mono_gc_memmove_atomic (dest
, src
, count
* mono_class_value_size (klass
, NULL
));
944 mono_gc_wbarrier_object_copy_internal (MonoObject
* obj
, MonoObject
*src
)
946 /* do not copy the sync state */
947 mono_gc_memmove_aligned (mono_object_get_data (obj
), (char*)src
+ MONO_ABI_SIZEOF (MonoObject
),
948 m_class_get_instance_size (mono_object_class (obj
)) - MONO_ABI_SIZEOF (MonoObject
));
952 mono_gc_clear_domain (MonoDomain
*domain
)
957 mono_gc_suspend_finalizers (void)
962 mono_gc_get_suspend_signal (void)
964 return GC_get_suspend_signal ();
968 mono_gc_get_restart_signal (void)
970 return GC_get_thr_restart_signal ();
973 #if defined(USE_COMPILER_TLS) && defined(__linux__) && (defined(__i386__) || defined(__x86_64__))
974 extern __thread
void* GC_thread_tls
;
975 #include "metadata-internals.h"
981 while (!(v
& (1 << i
)))
988 ATYPE_FREEPTR_FOR_BOX
,
996 create_allocator (int atype
, int tls_key
, gboolean slowpath
)
998 int index_var
, bytes_var
, my_fl_var
, my_entry_var
;
999 guint32 no_freelist_branch
, not_small_enough_branch
= 0;
1000 guint32 size_overflow_branch
= 0;
1001 MonoMethodBuilder
*mb
;
1003 MonoMethodSignature
*csig
;
1004 const char *name
= NULL
;
1007 g_assert_not_reached ();
1009 if (atype
== ATYPE_FREEPTR
) {
1010 name
= slowpath
? "SlowAllocPtrfree" : "AllocPtrfree";
1011 } else if (atype
== ATYPE_FREEPTR_FOR_BOX
) {
1012 name
= slowpath
? "SlowAllocPtrfreeBox" : "AllocPtrfreeBox";
1013 } else if (atype
== ATYPE_NORMAL
) {
1014 name
= slowpath
? "SlowAlloc" : "Alloc";
1015 } else if (atype
== ATYPE_GCJ
) {
1016 name
= slowpath
? "SlowAllocGcj" : "AllocGcj";
1017 } else if (atype
== ATYPE_STRING
) {
1018 name
= slowpath
? "SlowAllocString" : "AllocString";
1020 g_assert_not_reached ();
1023 csig
= mono_metadata_signature_alloc (mono_defaults
.corlib
, 2);
1025 if (atype
== ATYPE_STRING
) {
1026 csig
->ret
= m_class_get_byval_arg (mono_defaults
.string_class
);
1027 csig
->params
[0] = mono_get_int_type ();
1028 csig
->params
[1] = mono_get_int32_type ();
1030 csig
->ret
= mono_get_object_type ();
1031 csig
->params
[0] = mono_get_int_type ();
1032 csig
->params
[1] = mono_get_int32_type ();
1035 mb
= mono_mb_new (mono_defaults
.object_class
, name
, MONO_WRAPPER_ALLOC
);
1038 goto always_slowpath
;
1040 bytes_var
= mono_mb_add_local (mb
, mono_get_int32_type ());
1041 if (atype
== ATYPE_STRING
) {
1042 /* a string alloator method takes the args: (vtable, len) */
1043 /* bytes = (offsetof (MonoString, chars) + ((len + 1) * 2)); */
1044 mono_mb_emit_ldarg (mb
, 1);
1045 mono_mb_emit_icon (mb
, 1);
1046 mono_mb_emit_byte (mb
, MONO_CEE_ADD
);
1047 mono_mb_emit_icon (mb
, 1);
1048 mono_mb_emit_byte (mb
, MONO_CEE_SHL
);
1049 // sizeof (MonoString) might include padding
1050 mono_mb_emit_icon (mb
, G_STRUCT_OFFSET (MonoString
, chars
));
1051 mono_mb_emit_byte (mb
, MONO_CEE_ADD
);
1052 mono_mb_emit_stloc (mb
, bytes_var
);
1054 mono_mb_emit_ldarg (mb
, 1);
1055 mono_mb_emit_stloc (mb
, bytes_var
);
1058 /* this is needed for strings/arrays only as the other big types are never allocated with this method */
1059 if (atype
== ATYPE_STRING
) {
1060 /* check for size */
1061 /* if (!SMALL_ENOUGH (bytes)) jump slow_path;*/
1062 mono_mb_emit_ldloc (mb
, bytes_var
);
1063 mono_mb_emit_icon (mb
, (NFREELISTS
-1) * GRANULARITY
);
1064 not_small_enough_branch
= mono_mb_emit_short_branch (mb
, MONO_CEE_BGT_UN_S
);
1065 /* check for overflow */
1066 mono_mb_emit_ldloc (mb
, bytes_var
);
1067 mono_mb_emit_icon (mb
, sizeof (MonoString
));
1068 size_overflow_branch
= mono_mb_emit_short_branch (mb
, MONO_CEE_BLE_UN_S
);
1071 /* int index = INDEX_FROM_BYTES(bytes); */
1072 index_var
= mono_mb_add_local (mb
, mono_get_int32_type ());
1074 mono_mb_emit_ldloc (mb
, bytes_var
);
1075 mono_mb_emit_icon (mb
, GRANULARITY
- 1);
1076 mono_mb_emit_byte (mb
, MONO_CEE_ADD
);
1077 mono_mb_emit_icon (mb
, shift_amount (GRANULARITY
));
1078 mono_mb_emit_byte (mb
, MONO_CEE_SHR_UN
);
1079 mono_mb_emit_icon (mb
, shift_amount (sizeof (gpointer
)));
1080 mono_mb_emit_byte (mb
, MONO_CEE_SHL
);
1081 /* index var is already adjusted into bytes */
1082 mono_mb_emit_stloc (mb
, index_var
);
1084 my_fl_var
= mono_mb_add_local (mb
, mono_get_int_type ());
1085 my_entry_var
= mono_mb_add_local (mb
, mono_get_int_type ());
1086 /* my_fl = ((GC_thread)tsd) -> ptrfree_freelists + index; */
1087 mono_mb_emit_byte (mb
, MONO_CUSTOM_PREFIX
);
1088 mono_mb_emit_byte (mb
, 0x0D); /* CEE_MONO_TLS */
1089 mono_mb_emit_i4 (mb
, tls_key
);
1090 if (atype
== ATYPE_FREEPTR
|| atype
== ATYPE_FREEPTR_FOR_BOX
|| atype
== ATYPE_STRING
)
1091 mono_mb_emit_icon (mb
, G_STRUCT_OFFSET (struct GC_Thread_Rep
, tlfs
)
1092 + G_STRUCT_OFFSET (struct thread_local_freelists
,
1093 ptrfree_freelists
));
1094 else if (atype
== ATYPE_NORMAL
)
1095 mono_mb_emit_icon (mb
, G_STRUCT_OFFSET (struct GC_Thread_Rep
, tlfs
)
1096 + G_STRUCT_OFFSET (struct thread_local_freelists
,
1098 else if (atype
== ATYPE_GCJ
)
1099 mono_mb_emit_icon (mb
, G_STRUCT_OFFSET (struct GC_Thread_Rep
, tlfs
)
1100 + G_STRUCT_OFFSET (struct thread_local_freelists
,
1103 g_assert_not_reached ();
1104 mono_mb_emit_byte (mb
, MONO_CEE_ADD
);
1105 mono_mb_emit_ldloc (mb
, index_var
);
1106 mono_mb_emit_byte (mb
, MONO_CEE_ADD
);
1107 mono_mb_emit_stloc (mb
, my_fl_var
);
1109 /* my_entry = *my_fl; */
1110 mono_mb_emit_ldloc (mb
, my_fl_var
);
1111 mono_mb_emit_byte (mb
, MONO_CEE_LDIND_I
);
1112 mono_mb_emit_stloc (mb
, my_entry_var
);
1114 /* if (EXPECT((word)my_entry >= HBLKSIZE, 1)) { */
1115 mono_mb_emit_ldloc (mb
, my_entry_var
);
1116 mono_mb_emit_icon (mb
, HBLKSIZE
);
1117 no_freelist_branch
= mono_mb_emit_short_branch (mb
, MONO_CEE_BLT_UN_S
);
1119 /* ptr_t next = obj_link(my_entry); *my_fl = next; */
1120 mono_mb_emit_ldloc (mb
, my_fl_var
);
1121 mono_mb_emit_ldloc (mb
, my_entry_var
);
1122 mono_mb_emit_byte (mb
, MONO_CEE_LDIND_I
);
1123 mono_mb_emit_byte (mb
, MONO_CEE_STIND_I
);
1125 /* set the vtable and clear the words in the object */
1126 mono_mb_emit_ldloc (mb
, my_entry_var
);
1127 mono_mb_emit_ldarg (mb
, 0);
1128 mono_mb_emit_byte (mb
, MONO_CEE_STIND_I
);
1130 if (atype
== ATYPE_FREEPTR
) {
1131 int start_var
, end_var
, start_loop
;
1132 /* end = my_entry + bytes; start = my_entry + sizeof (gpointer);
1134 start_var
= mono_mb_add_local (mb
, mono_get_int_type ());
1135 end_var
= mono_mb_add_local (mb
, mono_get_int_type ());
1136 mono_mb_emit_ldloc (mb
, my_entry_var
);
1137 mono_mb_emit_ldloc (mb
, bytes_var
);
1138 mono_mb_emit_byte (mb
, MONO_CEE_ADD
);
1139 mono_mb_emit_stloc (mb
, end_var
);
1140 mono_mb_emit_ldloc (mb
, my_entry_var
);
1141 mono_mb_emit_icon (mb
, G_STRUCT_OFFSET (MonoObject
, synchronisation
));
1142 mono_mb_emit_byte (mb
, MONO_CEE_ADD
);
1143 mono_mb_emit_stloc (mb
, start_var
);
1147 * } while (start < end);
1149 start_loop
= mono_mb_get_label (mb
);
1150 mono_mb_emit_ldloc (mb
, start_var
);
1151 mono_mb_emit_icon (mb
, 0);
1152 mono_mb_emit_byte (mb
, MONO_CEE_STIND_I
);
1153 mono_mb_emit_ldloc (mb
, start_var
);
1154 mono_mb_emit_icon (mb
, sizeof (gpointer
));
1155 mono_mb_emit_byte (mb
, MONO_CEE_ADD
);
1156 mono_mb_emit_stloc (mb
, start_var
);
1158 mono_mb_emit_ldloc (mb
, start_var
);
1159 mono_mb_emit_ldloc (mb
, end_var
);
1160 mono_mb_emit_byte (mb
, MONO_CEE_BLT_UN_S
);
1161 mono_mb_emit_byte (mb
, start_loop
- (mono_mb_get_label (mb
) + 1));
1162 } else if (atype
== ATYPE_FREEPTR_FOR_BOX
|| atype
== ATYPE_STRING
) {
1163 /* need to clear just the sync pointer */
1164 mono_mb_emit_ldloc (mb
, my_entry_var
);
1165 mono_mb_emit_icon (mb
, G_STRUCT_OFFSET (MonoObject
, synchronisation
));
1166 mono_mb_emit_byte (mb
, MONO_CEE_ADD
);
1167 mono_mb_emit_icon (mb
, 0);
1168 mono_mb_emit_byte (mb
, MONO_CEE_STIND_I
);
1171 if (atype
== ATYPE_STRING
) {
1172 /* need to set length and clear the last char */
1173 /* s->length = len; */
1174 mono_mb_emit_ldloc (mb
, my_entry_var
);
1175 mono_mb_emit_icon (mb
, G_STRUCT_OFFSET (MonoString
, length
));
1176 mono_mb_emit_byte (mb
, MONO_CEE_ADD
);
1177 mono_mb_emit_ldarg (mb
, 1);
1178 mono_mb_emit_byte (mb
, MONO_CEE_STIND_I4
);
1179 /* s->chars [len] = 0; */
1180 mono_mb_emit_ldloc (mb
, my_entry_var
);
1181 mono_mb_emit_ldloc (mb
, bytes_var
);
1182 mono_mb_emit_icon (mb
, 2);
1183 mono_mb_emit_byte (mb
, MONO_CEE_SUB
);
1184 mono_mb_emit_byte (mb
, MONO_CEE_ADD
);
1185 mono_mb_emit_icon (mb
, 0);
1186 mono_mb_emit_byte (mb
, MONO_CEE_STIND_I2
);
1189 /* return my_entry; */
1190 mono_mb_emit_ldloc (mb
, my_entry_var
);
1191 mono_mb_emit_byte (mb
, MONO_CEE_RET
);
1193 mono_mb_patch_short_branch (mb
, no_freelist_branch
);
1194 if (not_small_enough_branch
> 0)
1195 mono_mb_patch_short_branch (mb
, not_small_enough_branch
);
1196 if (size_overflow_branch
> 0)
1197 mono_mb_patch_short_branch (mb
, size_overflow_branch
);
1199 /* the slow path: we just call back into the runtime */
1201 if (atype
== ATYPE_STRING
) {
1202 mono_mb_emit_ldarg (mb
, 1);
1203 mono_mb_emit_icall (mb
, ves_icall_string_alloc
);
1205 mono_mb_emit_ldarg (mb
, 0);
1206 mono_mb_emit_icall (mb
, ves_icall_object_new_specific
);
1209 mono_mb_emit_byte (mb
, MONO_CEE_RET
);
1211 info
= mono_wrapper_info_create (mb
, WRAPPER_SUBTYPE_NONE
);
1212 info
->d
.alloc
.gc_name
= "boehm";
1213 info
->d
.alloc
.alloc_type
= atype
;
1214 mb
->init_locals
= FALSE
;
1216 res
= mono_mb_create (mb
, csig
, 8, info
);
1222 static MonoMethod
* alloc_method_cache
[ATYPE_NUM
];
1223 static MonoMethod
* slowpath_alloc_method_cache
[ATYPE_NUM
];
1226 mono_gc_is_critical_method (MonoMethod
*method
)
1230 for (i
= 0; i
< ATYPE_NUM
; ++i
)
1231 if (method
== alloc_method_cache
[i
] || method
== slowpath_alloc_method_cache
[i
])
1238 * If possible, generate a managed method that can quickly allocate objects in class
1239 * @klass. The method will typically have an thread-local inline allocation sequence.
1240 * The signature of the called method is:
1241 * object allocate (MonoVTable *vtable)
1242 * The thread local alloc logic is taken from libgc/pthread_support.c.
1245 mono_gc_get_managed_allocator (MonoClass
*klass
, gboolean for_box
, gboolean known_instance_size
)
1250 * Tls implementation changed, we jump to tls native getters/setters.
1251 * Is boehm managed allocator ok with this ? Do we even care ?
1255 if (!SMALL_ENOUGH (m_class_get_instance_size (klass
)))
1257 if (mono_class_has_finalizer (klass
) || mono_class_is_marshalbyref (klass
))
1259 if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
1261 if (m_class_get_rank (klass
))
1263 if (mono_class_is_open_constructed_type (m_class_get_byval_arg (klass
)))
1265 if (m_class_get_byval_arg (klass
)->type
== MONO_TYPE_STRING
) {
1266 atype
= ATYPE_STRING
;
1267 } else if (!known_instance_size
) {
1269 } else if (!m_class_has_references (klass
)) {
1271 atype
= ATYPE_FREEPTR_FOR_BOX
;
1273 atype
= ATYPE_FREEPTR
;
1277 * disabled because we currently do a runtime choice anyway, to
1278 * deal with multiple appdomains.
1279 if (vtable->gc_descr != GC_NO_DESCRIPTOR)
1282 atype = ATYPE_NORMAL;
1285 return mono_gc_get_managed_allocator_by_type (atype
, MANAGED_ALLOCATOR_REGULAR
);
1289 mono_gc_get_managed_array_allocator (MonoClass
*klass
)
1295 * mono_gc_get_managed_allocator_by_type:
1297 * Return a managed allocator method corresponding to allocator type ATYPE.
1300 mono_gc_get_managed_allocator_by_type (int atype
, ManagedAllocatorVariant variant
)
1303 gboolean slowpath
= variant
!= MANAGED_ALLOCATOR_REGULAR
;
1304 MonoMethod
**cache
= slowpath
? slowpath_alloc_method_cache
: alloc_method_cache
;
1308 res
= cache
[atype
];
1312 res
= create_allocator (atype
, -1, slowpath
);
1313 mono_os_mutex_lock (&mono_gc_lock
);
1314 if (cache
[atype
]) {
1315 mono_free_method (res
);
1316 res
= cache
[atype
];
1318 mono_memory_barrier ();
1319 cache
[atype
] = res
;
1321 mono_os_mutex_unlock (&mono_gc_lock
);
1326 mono_gc_get_managed_allocator_types (void)
1332 mono_gc_get_write_barrier (void)
1334 g_assert_not_reached ();
1341 mono_gc_is_critical_method (MonoMethod
*method
)
1347 mono_gc_get_managed_allocator (MonoClass
*klass
, gboolean for_box
, gboolean known_instance_size
)
1353 mono_gc_get_managed_array_allocator (MonoClass
*klass
)
1359 mono_gc_get_managed_allocator_by_type (int atype
, ManagedAllocatorVariant variant
)
1365 mono_gc_get_managed_allocator_types (void)
1371 mono_gc_get_write_barrier (void)
1373 g_assert_not_reached ();
1380 mono_gc_get_specific_write_barrier (gboolean is_concurrent
)
1382 g_assert_not_reached ();
1387 mono_gc_get_aligned_size_for_allocator (int size
)
1393 mono_gc_get_gc_name (void)
1399 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func
, void *data
)
1401 return GC_call_with_alloc_lock (func
, data
);
1405 mono_gc_get_description (void)
1407 return g_strdup (DEFAULT_GC_NAME
);
1411 mono_gc_set_desktop_mode (void)
1417 mono_gc_is_moving (void)
1423 mono_gc_is_disabled (void)
1425 if (GC_dont_gc
|| gc_dont_gc_env
)
1432 mono_gc_wbarrier_range_copy (gpointer _dest
, gconstpointer _src
, int size
)
1434 g_assert_not_reached ();
1437 MonoRangeCopyFunction
1438 mono_gc_get_range_copy_func (void)
1440 return &mono_gc_wbarrier_range_copy
;
1444 mono_gc_get_card_table (int *shift_bits
, gpointer
*card_mask
)
1446 g_assert_not_reached ();
1451 mono_gc_get_target_card_table (int *shift_bits
, target_mgreg_t
*card_mask
)
1459 mono_gc_card_table_nursery_check (void)
1461 g_assert_not_reached ();
1466 mono_gc_get_nursery (int *shift_bits
, size_t *size
)
1472 mono_gc_precise_stack_mark_enabled (void)
1478 mono_gc_get_logfile (void)
1484 mono_gc_params_set (const char* options
)
1489 mono_gc_debug_set (const char* options
)
1494 mono_gc_conservatively_scan_area (void *start
, void *end
)
1496 g_assert_not_reached ();
1500 mono_gc_scan_object (void *obj
, void *gc_data
)
1502 g_assert_not_reached ();
1507 mono_gc_get_bitmap_for_descr (void *descr
, int *numbits
)
1509 g_assert_not_reached ();
1514 mono_gc_set_gc_callbacks (MonoGCCallbacks
*callbacks
)
1519 mono_gc_set_stack_end (void *stack_end
)
1524 mono_gc_skip_thread_changing (gboolean skip
)
1527 * Unlike SGen, Boehm doesn't respect our thread info flags. We need to
1528 * inform Boehm manually to skip/not skip the current thread.
1532 GC_start_blocking ();
1538 mono_gc_skip_thread_changed (gboolean skip
)
1543 mono_gc_register_for_finalization (MonoObject
*obj
, MonoFinalizationProc user_data
)
1548 /* This assertion is not valid when GC_DEBUG is defined */
1549 g_assert (GC_base (obj
) == (char*)obj
- offset
);
1552 GC_REGISTER_FINALIZER_NO_ORDER ((char*)obj
- offset
, user_data
, GUINT_TO_POINTER (offset
), NULL
, NULL
);
1557 mono_gc_pthread_create (pthread_t
*new_thread
, const pthread_attr_t
*attr
, void *(*start_routine
)(void *), void *arg
)
1559 /* it is being replaced by GC_pthread_create on some
1560 * platforms, see libgc/include/gc_pthread_redirects.h */
1561 return pthread_create (new_thread
, attr
, start_routine
, arg
);
1566 BOOL APIENTRY
mono_gc_dllmain (HMODULE module_handle
, DWORD reason
, LPVOID reserved
)
1568 return GC_DllMain (module_handle
, reason
, reserved
);
1573 mono_gc_get_vtable (MonoObject
*obj
)
1575 // No pointer tagging.
1580 mono_gc_get_vtable_bits (MonoClass
*klass
)
1582 if (fin_callbacks
.is_class_finalization_aware
) {
1583 if (fin_callbacks
.is_class_finalization_aware (klass
))
1584 return BOEHM_GC_BIT_FINALIZER_AWARE
;
1590 * mono_gc_register_altstack:
1592 * Register the dimensions of the normal stack and altstack with the collector.
1593 * Currently, STACK/STACK_SIZE is only used when the thread is suspended while it is on an altstack.
1596 mono_gc_register_altstack (gpointer stack
, gint32 stack_size
, gpointer altstack
, gint32 altstack_size
)
1598 GC_register_altstack (stack
, stack_size
, altstack
, altstack_size
);
1602 mono_gc_get_los_limit (void)
1608 mono_gc_set_string_length (MonoString
*str
, gint32 new_length
)
1610 mono_unichar2
*new_end
= str
->chars
+ new_length
;
1612 /* zero the discarded string. This null-delimits the string and allows
1613 * the space to be reclaimed by SGen. */
1615 memset (new_end
, 0, (str
->length
- new_length
+ 1) * sizeof (mono_unichar2
));
1616 str
->length
= new_length
;
1620 mono_gc_user_markers_supported (void)
1626 mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker
)
1628 g_assert_not_reached ();
1632 /* Toggleref support */
1635 mono_gc_toggleref_add (MonoObject
*object
, mono_bool strong_ref
)
1637 if (GC_toggleref_add ((GC_PTR
)object
, (int)strong_ref
) != GC_SUCCESS
)
1638 g_error ("GC_toggleref_add failed\n");
1642 mono_gc_toggleref_register_callback (MonoToggleRefStatus (*proccess_toggleref
) (MonoObject
*obj
))
1644 GC_set_toggleref_func ((GC_ToggleRefStatus (*) (GC_PTR obj
)) proccess_toggleref
);
1647 /* Test support code */
1649 static MonoToggleRefStatus
1650 test_toggleref_callback (MonoObject
*obj
)
1652 static MonoClassField
*mono_toggleref_test_field
;
1653 MonoToggleRefStatus status
= MONO_TOGGLE_REF_DROP
;
1655 if (!mono_toggleref_test_field
) {
1656 mono_toggleref_test_field
= mono_class_get_field_from_name_full (mono_object_class (obj
), "__test", NULL
);
1657 g_assert (mono_toggleref_test_field
);
1660 mono_field_get_value_internal (obj
, mono_toggleref_test_field
, &status
);
1661 printf ("toggleref-cb obj %d\n", status
);
1666 register_test_toggleref_callback (void)
1668 mono_gc_toggleref_register_callback (test_toggleref_callback
);
1672 is_finalization_aware (MonoObject
*obj
)
1674 MonoVTable
*vt
= obj
->vtable
;
1675 return (vt
->gc_bits
& BOEHM_GC_BIT_FINALIZER_AWARE
) == BOEHM_GC_BIT_FINALIZER_AWARE
;
1679 fin_notifier (MonoObject
*obj
)
1681 if (is_finalization_aware (obj
))
1682 fin_callbacks
.object_queued_for_finalization (obj
);
1686 mono_gc_register_finalizer_callbacks (MonoGCFinalizerCallbacks
*callbacks
)
1688 if (callbacks
->version
!= MONO_GC_FINALIZER_EXTENSION_VERSION
)
1689 g_error ("Invalid finalizer callback version. Expected %d but got %d\n", MONO_GC_FINALIZER_EXTENSION_VERSION
, callbacks
->version
);
1691 fin_callbacks
= *callbacks
;
1693 GC_set_await_finalize_proc ((void (*) (GC_PTR
))fin_notifier
);
1696 #define BITMAP_SIZE (sizeof (*((HandleData *)NULL)->bitmap) * CHAR_BIT)
1698 static inline gboolean
1699 slot_occupied (HandleData
*handles
, guint slot
) {
1700 return handles
->bitmap
[slot
/ BITMAP_SIZE
] & (1 << (slot
% BITMAP_SIZE
));
1704 vacate_slot (HandleData
*handles
, guint slot
) {
1705 handles
->bitmap
[slot
/ BITMAP_SIZE
] &= ~(1 << (slot
% BITMAP_SIZE
));
1709 occupy_slot (HandleData
*handles
, guint slot
) {
1710 handles
->bitmap
[slot
/ BITMAP_SIZE
] |= 1 << (slot
% BITMAP_SIZE
);
1714 find_first_unset (guint32 bitmap
)
1717 for (i
= 0; i
< 32; ++i
) {
1718 if (!(bitmap
& (1 << i
)))
1725 handle_data_alloc_entries (HandleData
*handles
)
1728 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles
->type
)) {
1729 handles
->entries
= (void **)g_malloc0 (sizeof (*handles
->entries
) * handles
->size
);
1730 handles
->domain_ids
= (guint16
*)g_malloc0 (sizeof (*handles
->domain_ids
) * handles
->size
);
1732 handles
->entries
= (void **)mono_gc_alloc_fixed (sizeof (*handles
->entries
) * handles
->size
, NULL
, MONO_ROOT_SOURCE_GC_HANDLE
, NULL
, "GC Handle Table (Boehm)");
1734 handles
->bitmap
= (guint32
*)g_malloc0 (handles
->size
/ CHAR_BIT
);
1738 handle_data_next_unset (HandleData
*handles
)
1741 for (slot
= handles
->slot_hint
; slot
< handles
->size
/ BITMAP_SIZE
; ++slot
) {
1742 if (handles
->bitmap
[slot
] == 0xffffffff)
1744 handles
->slot_hint
= slot
;
1745 return find_first_unset (handles
->bitmap
[slot
]);
1751 handle_data_first_unset (HandleData
*handles
)
1754 for (slot
= 0; slot
< handles
->slot_hint
; ++slot
) {
1755 if (handles
->bitmap
[slot
] == 0xffffffff)
1757 handles
->slot_hint
= slot
;
1758 return find_first_unset (handles
->bitmap
[slot
]);
1763 /* Returns the index of the current slot in the bitmap. */
1765 handle_data_grow (HandleData
*handles
, gboolean track
)
1767 guint32
*new_bitmap
;
1768 guint32 new_size
= handles
->size
* 2; /* always double: we memset to 0 based on this below */
1770 /* resize and copy the bitmap */
1771 new_bitmap
= (guint32
*)g_malloc0 (new_size
/ CHAR_BIT
);
1772 memcpy (new_bitmap
, handles
->bitmap
, handles
->size
/ CHAR_BIT
);
1773 g_free (handles
->bitmap
);
1774 handles
->bitmap
= new_bitmap
;
1776 /* resize and copy the entries */
1777 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles
->type
)) {
1779 guint16
*domain_ids
;
1781 domain_ids
= (guint16
*)g_malloc0 (sizeof (*handles
->domain_ids
) * new_size
);
1782 entries
= (void **)g_malloc0 (sizeof (*handles
->entries
) * new_size
);
1783 memcpy (domain_ids
, handles
->domain_ids
, sizeof (*handles
->domain_ids
) * handles
->size
);
1784 for (i
= 0; i
< handles
->size
; ++i
) {
1785 MonoObject
*obj
= mono_gc_weak_link_get (&(handles
->entries
[i
]));
1787 mono_gc_weak_link_add (&(entries
[i
]), obj
, track
);
1788 mono_gc_weak_link_remove (&(handles
->entries
[i
]), track
);
1790 g_assert (!handles
->entries
[i
]);
1793 g_free (handles
->entries
);
1794 g_free (handles
->domain_ids
);
1795 handles
->entries
= entries
;
1796 handles
->domain_ids
= domain_ids
;
1799 entries
= (void **)mono_gc_alloc_fixed (sizeof (*handles
->entries
) * new_size
, NULL
, MONO_ROOT_SOURCE_GC_HANDLE
, NULL
, "GC Handle Table (Boehm)");
1800 mono_gc_memmove_aligned (entries
, handles
->entries
, sizeof (*handles
->entries
) * handles
->size
);
1801 mono_gc_free_fixed (handles
->entries
);
1802 handles
->entries
= entries
;
1804 handles
->slot_hint
= handles
->size
/ BITMAP_SIZE
;
1805 handles
->size
= new_size
;
1809 alloc_handle (HandleData
*handles
, MonoObject
*obj
, gboolean track
)
1813 lock_handles (handles
);
1815 handle_data_alloc_entries (handles
);
1816 i
= handle_data_next_unset (handles
);
1817 if (i
== -1 && handles
->slot_hint
!= 0)
1818 i
= handle_data_first_unset (handles
);
1820 handle_data_grow (handles
, track
);
1823 slot
= handles
->slot_hint
* BITMAP_SIZE
+ i
;
1824 occupy_slot (handles
, slot
);
1825 handles
->entries
[slot
] = NULL
;
1826 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles
->type
)) {
1827 /*FIXME, what to use when obj == null?*/
1828 handles
->domain_ids
[slot
] = (obj
? mono_object_get_domain_internal (obj
) : mono_domain_get ())->domain_id
;
1830 mono_gc_weak_link_add (&(handles
->entries
[slot
]), obj
, track
);
1832 handles
->entries
[slot
] = obj
;
1835 #ifndef DISABLE_PERFCOUNTERS
1836 mono_atomic_inc_i32 (&mono_perfcounters
->gc_num_handles
);
1838 unlock_handles (handles
);
1839 res
= MONO_GC_HANDLE (slot
, handles
->type
);
1840 MONO_PROFILER_RAISE (gc_handle_created
, (res
, (MonoGCHandleType
)handles
->type
, obj
));
1845 * mono_gchandle_new_internal:
1846 * \param obj managed object to get a handle for
1847 * \param pinned whether the object should be pinned
1849 * This returns a handle that wraps the object, this is used to keep a
1850 * reference to a managed object from the unmanaged world and preventing the
1851 * object from being disposed.
1853 * If \p pinned is false the address of the object can not be obtained, if it is
1854 * true the address of the object can be obtained. This will also pin the
1855 * object so it will not be possible by a moving garbage collector to move the
1858 * \returns a handle that can be used to access the object from
1862 mono_gchandle_new_internal (MonoObject
*obj
, gboolean pinned
)
1864 return alloc_handle (&gc_handles
[pinned
? HANDLE_PINNED
: HANDLE_NORMAL
], obj
, FALSE
);
1868 * mono_gchandle_new_weakref_internal:
1869 * \param obj managed object to get a handle for
1870 * \param track_resurrection Determines how long to track the object, if this is set to TRUE, the object is tracked after finalization, if FALSE, the object is only tracked up until the point of finalization.
1872 * This returns a weak handle that wraps the object, this is used to
1873 * keep a reference to a managed object from the unmanaged world.
1874 * Unlike the \c mono_gchandle_new_internal the object can be reclaimed by the
1875 * garbage collector. In this case the value of the GCHandle will be
1878 * If \p track_resurrection is TRUE the object will be tracked through
1879 * finalization and if the object is resurrected during the execution
1880 * of the finalizer, then the returned weakref will continue to hold
1881 * a reference to the object. If \p track_resurrection is FALSE, then
1882 * the weak reference's target will become NULL as soon as the object
1883 * is passed on to the finalizer.
1885 * \returns a handle that can be used to access the object from
1889 mono_gchandle_new_weakref_internal (MonoObject
*obj
, gboolean track_resurrection
)
1891 return alloc_handle (&gc_handles
[track_resurrection
? HANDLE_WEAK_TRACK
: HANDLE_WEAK
], obj
, track_resurrection
);
1895 * mono_gchandle_get_target_internal:
1896 * \param gchandle a GCHandle's handle.
1898 * The handle was previously created by calling \c mono_gchandle_new_internal or
1899 * \c mono_gchandle_new_weakref.
1901 * \returns A pointer to the \c MonoObject* represented by the handle or
1902 * NULL for a collected object if using a weakref handle.
1905 mono_gchandle_get_target_internal (guint32 gchandle
)
1907 guint slot
= MONO_GC_HANDLE_SLOT (gchandle
);
1908 guint type
= MONO_GC_HANDLE_TYPE (gchandle
);
1909 HandleData
*handles
= &gc_handles
[type
];
1910 MonoObject
*obj
= NULL
;
1911 if (type
>= HANDLE_TYPE_MAX
)
1914 lock_handles (handles
);
1915 if (slot
< handles
->size
&& slot_occupied (handles
, slot
)) {
1916 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles
->type
)) {
1917 obj
= mono_gc_weak_link_get (&handles
->entries
[slot
]);
1919 obj
= (MonoObject
*)handles
->entries
[slot
];
1922 /* print a warning? */
1924 unlock_handles (handles
);
1925 /*g_print ("get target of entry %d of type %d: %p\n", slot, handles->type, obj);*/
1930 mono_gchandle_set_target (guint32 gchandle
, MonoObject
*obj
)
1932 guint slot
= MONO_GC_HANDLE_SLOT (gchandle
);
1933 guint type
= MONO_GC_HANDLE_TYPE (gchandle
);
1934 HandleData
*handles
= &gc_handles
[type
];
1935 MonoObject
*old_obj
= NULL
;
1937 g_assert (type
< HANDLE_TYPE_MAX
);
1938 lock_handles (handles
);
1939 if (slot
< handles
->size
&& slot_occupied (handles
, slot
)) {
1940 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles
->type
)) {
1941 old_obj
= (MonoObject
*)handles
->entries
[slot
];
1943 if (handles
->entries
[slot
])
1944 mono_gc_weak_link_remove (&handles
->entries
[slot
], handles
->type
== HANDLE_WEAK_TRACK
);
1946 mono_gc_weak_link_add (&handles
->entries
[slot
], obj
, handles
->type
== HANDLE_WEAK_TRACK
);
1947 /*FIXME, what to use when obj == null?*/
1948 handles
->domain_ids
[slot
] = (obj
? mono_object_get_domain_internal (obj
) : mono_domain_get ())->domain_id
;
1950 handles
->entries
[slot
] = obj
;
1953 /* print a warning? */
1955 /*g_print ("changed entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
1956 unlock_handles (handles
);
1960 mono_gc_is_null (void)
1966 * mono_gchandle_is_in_domain:
1967 * \param gchandle a GCHandle's handle.
1968 * \param domain An application domain.
1970 * Use this function to determine if the \p gchandle points to an
1971 * object allocated in the specified \p domain.
1973 * \returns TRUE if the object wrapped by the \p gchandle belongs to the specific \p domain.
1976 mono_gchandle_is_in_domain (guint32 gchandle
, MonoDomain
*domain
)
1978 guint slot
= MONO_GC_HANDLE_SLOT (gchandle
);
1979 guint type
= MONO_GC_HANDLE_TYPE (gchandle
);
1980 HandleData
*handles
= &gc_handles
[type
];
1981 gboolean result
= FALSE
;
1983 if (type
>= HANDLE_TYPE_MAX
)
1986 lock_handles (handles
);
1987 if (slot
< handles
->size
&& slot_occupied (handles
, slot
)) {
1988 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles
->type
)) {
1989 result
= domain
->domain_id
== handles
->domain_ids
[slot
];
1992 obj
= (MonoObject
*)handles
->entries
[slot
];
1996 result
= domain
== mono_object_domain (obj
);
1999 /* print a warning? */
2001 unlock_handles (handles
);
2006 * mono_gchandle_free_internal:
2007 * \param gchandle a GCHandle's handle.
2009 * Frees the \p gchandle handle. If there are no outstanding
2010 * references, the garbage collector can reclaim the memory of the
2014 mono_gchandle_free_internal (guint32 gchandle
)
2019 guint slot
= MONO_GC_HANDLE_SLOT (gchandle
);
2020 guint type
= MONO_GC_HANDLE_TYPE (gchandle
);
2021 HandleData
*handles
= &gc_handles
[type
];
2022 if (type
>= HANDLE_TYPE_MAX
)
2025 lock_handles (handles
);
2026 if (slot
< handles
->size
&& slot_occupied (handles
, slot
)) {
2027 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles
->type
)) {
2028 if (handles
->entries
[slot
])
2029 mono_gc_weak_link_remove (&handles
->entries
[slot
], handles
->type
== HANDLE_WEAK_TRACK
);
2031 handles
->entries
[slot
] = NULL
;
2033 vacate_slot (handles
, slot
);
2035 /* print a warning? */
2037 #ifndef DISABLE_PERFCOUNTERS
2038 mono_atomic_dec_i32 (&mono_perfcounters
->gc_num_handles
);
2040 /*g_print ("freed entry %d of type %d\n", slot, handles->type);*/
2041 unlock_handles (handles
);
2042 MONO_PROFILER_RAISE (gc_handle_deleted
, (gchandle
, (MonoGCHandleType
)handles
->type
));
2046 * mono_gchandle_free_domain:
2047 * \param domain domain that is unloading
2049 * Function used internally to cleanup any GC handle for objects belonging
2050 * to the specified domain during appdomain unload.
2053 mono_gchandle_free_domain (MonoDomain
*domain
)
2057 for (type
= HANDLE_TYPE_MIN
; type
< HANDLE_PINNED
; ++type
) {
2059 HandleData
*handles
= &gc_handles
[type
];
2060 lock_handles (handles
);
2061 for (slot
= 0; slot
< handles
->size
; ++slot
) {
2062 if (!slot_occupied (handles
, slot
))
2064 if (MONO_GC_HANDLE_TYPE_IS_WEAK (type
)) {
2065 if (domain
->domain_id
== handles
->domain_ids
[slot
]) {
2066 vacate_slot (handles
, slot
);
2067 if (handles
->entries
[slot
])
2068 mono_gc_weak_link_remove (&handles
->entries
[slot
], handles
->type
== HANDLE_WEAK_TRACK
);
2071 if (handles
->entries
[slot
] && mono_object_domain (handles
->entries
[slot
]) == domain
) {
2072 vacate_slot (handles
, slot
);
2073 handles
->entries
[slot
] = NULL
;
2077 unlock_handles (handles
);
2083 mono_gc_register_obj_with_weak_fields (void *obj
)
2085 g_error ("Weak fields not supported by boehm gc");
2089 mono_gc_ephemeron_array_add (MonoObject
*obj
)
2096 MONO_EMPTY_SOURCE_FILE (boehm_gc
);
2097 #endif /* no Boehm GC */