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/opcodes.h>
22 #include <mono/metadata/domain-internals.h>
23 #include <mono/metadata/metadata-internals.h>
24 #include <mono/metadata/marshal.h>
25 #include <mono/metadata/runtime.h>
26 #include <mono/metadata/handle.h>
27 #include <mono/metadata/sgen-toggleref.h>
28 #include <mono/metadata/w32handle.h>
29 #include <mono/utils/atomic.h>
30 #include <mono/utils/mono-logger-internals.h>
31 #include <mono/utils/mono-memory-model.h>
32 #include <mono/utils/mono-time.h>
33 #include <mono/utils/mono-threads.h>
34 #include <mono/utils/dtrace.h>
35 #include <mono/utils/gc_wrapper.h>
36 #include <mono/utils/mono-os-mutex.h>
37 #include <mono/utils/mono-counters.h>
38 #include <mono/utils/mono-compiler.h>
44 #define THREAD_LOCAL_ALLOC 1
45 #include "private/pthread_support.h"
47 #if defined(PLATFORM_MACOSX) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
48 void *pthread_get_stackaddr_np(pthread_t);
51 #define GC_NO_DESCRIPTOR ((gpointer)(0 | GC_DS_LENGTH))
52 /*Boehm max heap cannot be smaller than 16MB*/
53 #define MIN_BOEHM_MAX_HEAP_SIZE_IN_MB 16
54 #define MIN_BOEHM_MAX_HEAP_SIZE (MIN_BOEHM_MAX_HEAP_SIZE_IN_MB << 20)
56 static gboolean gc_initialized = FALSE;
57 static mono_mutex_t mono_gc_lock;
60 boehm_thread_register (MonoThreadInfo* info, void *baseptr);
62 boehm_thread_unregister (MonoThreadInfo *p);
64 boehm_thread_detach (MonoThreadInfo *p);
66 register_test_toggleref_callback (void);
68 #define BOEHM_GC_BIT_FINALIZER_AWARE 1
69 static MonoGCFinalizerCallbacks fin_callbacks;
73 static mono_mutex_t handle_section;
74 #define lock_handles(handles) mono_os_mutex_lock (&handle_section)
75 #define unlock_handles(handles) mono_os_mutex_unlock (&handle_section)
82 guint slot_hint : 24; /* starting slot for search in bitmap */
83 /* 2^16 appdomains should be enough for everyone (though I know I'll regret this in 20 years) */
84 /* we alloc this only for weak refs, since we can get the domain directly in the other cases */
88 #define EMPTY_HANDLE_DATA(type) {NULL, NULL, 0, (type), 0, NULL}
90 /* weak and weak-track arrays will be allocated in malloc memory
92 static HandleData gc_handles [] = {
93 EMPTY_HANDLE_DATA (HANDLE_WEAK),
94 EMPTY_HANDLE_DATA (HANDLE_WEAK_TRACK),
95 EMPTY_HANDLE_DATA (HANDLE_NORMAL),
96 EMPTY_HANDLE_DATA (HANDLE_PINNED)
100 mono_gc_warning (char *msg, GC_word arg)
102 mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_GC, msg, (unsigned long)arg);
105 static void on_gc_notification (GC_EventType event);
106 static void on_gc_heap_resize (size_t new_size);
109 mono_gc_base_init (void)
111 MonoThreadInfoCallbacks cb;
118 mono_counters_init ();
121 mono_w32handle_init ();
125 * Handle the case when we are called from a thread different from the main thread,
127 * FIXME: Move this to libgc where it belongs.
129 * we used to do this only when running on valgrind,
130 * but it happens also in other setups.
132 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
137 pthread_getattr_np (pthread_self (), &attr);
138 pthread_attr_getstack (&attr, &sstart, &size);
139 pthread_attr_destroy (&attr);
140 /*g_print ("stackbottom pth is: %p\n", (char*)sstart + size);*/
141 /* apparently with some linuxthreads implementations sstart can be NULL,
142 * fallback to the more imprecise method (bug# 78096).
145 GC_stackbottom = (char*)sstart + size;
148 gsize stack_bottom = (gsize)&dummy;
149 stack_bottom += 4095;
150 stack_bottom &= ~4095;
151 GC_stackbottom = (char*)stack_bottom;
154 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
155 GC_stackbottom = (char*)pthread_get_stackaddr_np (pthread_self ());
156 #elif defined(__OpenBSD__)
157 # include <pthread_np.h>
162 rslt = pthread_stackseg_np(pthread_self(), &ss);
163 g_assert (rslt == 0);
165 GC_stackbottom = (char*)ss.ss_sp;
170 gsize stack_bottom = (gsize)&dummy;
171 stack_bottom += 4095;
172 stack_bottom &= ~4095;
173 /*g_print ("stackbottom is: %p\n", (char*)stack_bottom);*/
174 GC_stackbottom = (char*)stack_bottom;
178 #if !defined(PLATFORM_ANDROID)
179 /* If GC_no_dls is set to true, GC_find_limit is not called. This causes a seg fault on Android. */
183 if ((env = g_getenv ("MONO_GC_DEBUG"))) {
184 char **opts = g_strsplit (env, ",", -1);
185 for (char **ptr = opts; ptr && *ptr; ptr ++) {
187 if (!strcmp (opt, "do-not-finalize")) {
188 mono_do_not_finalize = 1;
189 } else if (!strcmp (opt, "log-finalizers")) {
199 GC_set_warn_proc (mono_gc_warning);
200 GC_finalize_on_demand = 1;
201 GC_finalizer_notifier = mono_gc_finalize_notify;
203 GC_init_gcj_malloc (5, NULL);
204 GC_allow_register_threads ();
206 if ((env = g_getenv ("MONO_GC_PARAMS"))) {
207 char **ptr, **opts = g_strsplit (env, ",", -1);
208 for (ptr = opts; *ptr; ++ptr) {
210 if (g_str_has_prefix (opt, "max-heap-size=")) {
213 opt = strchr (opt, '=') + 1;
214 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &max_heap)) {
215 if (max_heap < MIN_BOEHM_MAX_HEAP_SIZE) {
216 fprintf (stderr, "max-heap-size must be at least %dMb.\n", MIN_BOEHM_MAX_HEAP_SIZE_IN_MB);
219 GC_set_max_heap_size (max_heap);
221 fprintf (stderr, "max-heap-size must be an integer.\n");
225 } else if (g_str_has_prefix (opt, "toggleref-test")) {
226 register_test_toggleref_callback ();
229 /* Could be a parameter for sgen */
231 fprintf (stderr, "MONO_GC_PARAMS must be a comma-delimited list of one or more of the following:\n");
232 fprintf (stderr, " max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
241 memset (&cb, 0, sizeof (cb));
242 cb.thread_register = boehm_thread_register;
243 cb.thread_unregister = boehm_thread_unregister;
244 cb.thread_detach = boehm_thread_detach;
245 cb.mono_method_is_critical = (gboolean (*)(void *))mono_runtime_is_critical_method;
247 mono_threads_init (&cb, sizeof (MonoThreadInfo));
248 mono_os_mutex_init (&mono_gc_lock);
249 mono_os_mutex_init_recursive (&handle_section);
251 mono_thread_info_attach (&dummy);
253 GC_set_on_collection_event (on_gc_notification);
254 GC_on_heap_resize = on_gc_heap_resize;
256 gc_initialized = TRUE;
260 mono_gc_base_cleanup (void)
262 GC_finalizer_notifier = NULL;
267 * \param generation GC generation identifier
269 * Perform a garbage collection for the given generation, higher numbers
270 * mean usually older objects. Collecting a high-numbered generation
271 * implies collecting also the lower-numbered generations.
272 * The maximum value for \p generation can be retrieved with a call to
273 * \c mono_gc_max_generation, so this function is usually called as:
275 * <code>mono_gc_collect (mono_gc_max_generation ());</code>
278 mono_gc_collect (int generation)
280 #ifndef DISABLE_PERFCOUNTERS
281 mono_perfcounters->gc_induced++;
287 * mono_gc_max_generation:
289 * Get the maximum generation number used by the current garbage
290 * collector. The value will be 0 for the Boehm collector, 1 or more
291 * for the generational collectors.
293 * Returns: the maximum generation number.
296 mono_gc_max_generation (void)
302 * mono_gc_get_generation:
303 * \param object a managed object
305 * Get the garbage collector's generation that \p object belongs to.
306 * Use this has a hint only.
308 * \returns a garbage collector generation number
311 mono_gc_get_generation (MonoObject *object)
317 * mono_gc_collection_count:
318 * \param generation a GC generation number
320 * Get how many times a garbage collection has been performed
321 * for the given \p generation number.
323 * \returns the number of garbage collections
326 mono_gc_collection_count (int generation)
332 * mono_gc_add_memory_pressure:
333 * \param value amount of bytes
335 * Adjust the garbage collector's view of how many bytes of memory
336 * are indirectly referenced by managed objects (for example unmanaged
337 * memory holding image or other binary data).
338 * This is a hint only to the garbage collector algorithm.
339 * Note that negative amounts of p value will decrease the memory
343 mono_gc_add_memory_pressure (gint64 value)
348 * mono_gc_get_used_size:
350 * Get the approximate amount of memory used by managed objects.
352 * Returns: the amount of memory used in bytes
355 mono_gc_get_used_size (void)
357 return GC_get_heap_size () - GC_get_free_bytes ();
361 * mono_gc_get_heap_size:
363 * Get the amount of memory used by the garbage collector.
365 * Returns: the size of the heap in bytes
368 mono_gc_get_heap_size (void)
370 return GC_get_heap_size ();
374 mono_gc_is_gc_thread (void)
376 return GC_thread_is_registered ();
380 mono_gc_register_thread (void *baseptr)
382 return mono_thread_info_attach (baseptr) != NULL;
386 boehm_thread_register (MonoThreadInfo* info, void *baseptr)
388 struct GC_stack_base sb;
391 /* TODO: use GC_get_stack_base instead of baseptr. */
392 sb.mem_base = baseptr;
393 res = GC_register_my_thread (&sb);
394 if (res == GC_UNIMPLEMENTED)
395 return NULL; /* Cannot happen with GC v7+. */
397 info->handle_stack = mono_handle_stack_alloc ();
403 boehm_thread_unregister (MonoThreadInfo *p)
405 MonoNativeThreadId tid;
407 tid = mono_thread_info_get_tid (p);
409 if (p->runtime_thread)
410 mono_threads_add_joinable_thread ((gpointer)tid);
414 boehm_thread_detach (MonoThreadInfo *p)
416 if (mono_thread_internal_current_is_attached ())
417 mono_thread_detach_internal (mono_thread_internal_current ());
421 mono_object_is_alive (MonoObject* o)
423 return GC_is_marked ((ptr_t)o);
427 mono_gc_walk_heap (int flags, MonoGCReferences callback, void *data)
432 static gint64 gc_start_time;
435 on_gc_notification (GC_EventType event)
437 MonoGCEvent e = (MonoGCEvent)event;
440 case MONO_GC_EVENT_PRE_STOP_WORLD:
441 MONO_GC_WORLD_STOP_BEGIN ();
444 case MONO_GC_EVENT_POST_STOP_WORLD:
445 MONO_GC_WORLD_STOP_END ();
448 case MONO_GC_EVENT_PRE_START_WORLD:
449 MONO_GC_WORLD_RESTART_BEGIN (1);
452 case MONO_GC_EVENT_POST_START_WORLD:
453 MONO_GC_WORLD_RESTART_END (1);
456 case MONO_GC_EVENT_START:
458 #ifndef DISABLE_PERFCOUNTERS
459 if (mono_perfcounters)
460 mono_perfcounters->gc_collections0++;
462 gc_stats.major_gc_count ++;
463 gc_start_time = mono_100ns_ticks ();
466 case MONO_GC_EVENT_END:
468 #if defined(ENABLE_DTRACE) && defined(__sun__)
469 /* This works around a dtrace -G problem on Solaris.
470 Limit its actual use to when the probe is enabled. */
471 if (MONO_GC_END_ENABLED ())
475 #ifndef DISABLE_PERFCOUNTERS
476 if (mono_perfcounters) {
477 guint64 heap_size = GC_get_heap_size ();
478 guint64 used_size = heap_size - GC_get_free_bytes ();
479 mono_perfcounters->gc_total_bytes = used_size;
480 mono_perfcounters->gc_committed_bytes = heap_size;
481 mono_perfcounters->gc_reserved_bytes = heap_size;
482 mono_perfcounters->gc_gen0size = heap_size;
485 gc_stats.major_gc_time += mono_100ns_ticks () - gc_start_time;
486 mono_trace_message (MONO_TRACE_GC, "gc took %d usecs", (mono_100ns_ticks () - gc_start_time) / 10);
492 mono_profiler_gc_event (e, 0);
495 case MONO_GC_EVENT_PRE_STOP_WORLD:
496 mono_thread_info_suspend_lock ();
497 mono_profiler_gc_event (MONO_GC_EVENT_PRE_STOP_WORLD_LOCKED, 0);
499 case MONO_GC_EVENT_POST_START_WORLD:
500 mono_thread_info_suspend_unlock ();
501 mono_profiler_gc_event (MONO_GC_EVENT_POST_START_WORLD_UNLOCKED, 0);
510 on_gc_heap_resize (size_t new_size)
512 guint64 heap_size = GC_get_heap_size ();
513 #ifndef DISABLE_PERFCOUNTERS
514 if (mono_perfcounters) {
515 mono_perfcounters->gc_committed_bytes = heap_size;
516 mono_perfcounters->gc_reserved_bytes = heap_size;
517 mono_perfcounters->gc_gen0size = heap_size;
520 mono_profiler_gc_heap_resize (new_size);
524 mono_gc_register_root (char *start, size_t size, void *descr, MonoGCRootSource source, const char *msg)
526 /* for some strange reason, they want one extra byte on the end */
527 GC_add_roots (start, start + size + 1);
533 mono_gc_register_root_wbarrier (char *start, size_t size, MonoGCDescriptor descr, MonoGCRootSource source, const char *msg)
535 return mono_gc_register_root (start, size, descr, source, msg);
539 mono_gc_deregister_root (char* addr)
542 /* FIXME: libgc doesn't define this work win32 for some reason */
543 /* FIXME: No size info */
544 GC_remove_roots (addr, addr + sizeof (gpointer) + 1);
549 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
551 /* libgc requires that we use HIDE_POINTER... */
552 *link_addr = (void*)HIDE_POINTER (obj);
554 GC_REGISTER_LONG_LINK (link_addr, obj);
556 GC_GENERAL_REGISTER_DISAPPEARING_LINK (link_addr, obj);
560 mono_gc_weak_link_remove (void **link_addr, gboolean track)
563 GC_unregister_long_link (link_addr);
565 GC_unregister_disappearing_link (link_addr);
570 reveal_link (gpointer link_addr)
572 void **link_a = (void **)link_addr;
573 return REVEAL_POINTER (*link_a);
577 mono_gc_weak_link_get (void **link_addr)
579 MonoObject *obj = (MonoObject *)GC_call_with_alloc_lock (reveal_link, link_addr);
580 if (obj == (MonoObject *) -1)
586 mono_gc_make_descr_for_string (gsize *bitmap, int numbits)
588 return mono_gc_make_descr_from_bitmap (bitmap, numbits);
592 mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size)
594 return mono_gc_make_descr_from_bitmap (bitmap, numbits);
598 mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size)
600 /* libgc has no usable support for arrays... */
601 return GC_NO_DESCRIPTOR;
605 mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
607 /* It seems there are issues when the bitmap doesn't fit: play it safe */
609 return GC_NO_DESCRIPTOR;
611 return (gpointer)GC_make_descriptor ((GC_bitmap)bitmap, numbits);
615 mono_gc_make_vector_descr (void)
621 mono_gc_make_root_descr_all_refs (int numbits)
627 mono_gc_alloc_fixed (size_t size, void *descr, MonoGCRootSource source, const char *msg)
629 return GC_MALLOC_UNCOLLECTABLE (size);
633 mono_gc_free_fixed (void* addr)
639 mono_gc_alloc_obj (MonoVTable *vtable, size_t size)
643 if (!vtable->klass->has_references) {
644 obj = (MonoObject *)GC_MALLOC_ATOMIC (size);
645 if (G_UNLIKELY (!obj))
648 obj->vtable = vtable;
649 obj->synchronisation = NULL;
651 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
652 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
653 obj = (MonoObject *)GC_GCJ_MALLOC (size, vtable);
654 if (G_UNLIKELY (!obj))
657 obj = (MonoObject *)GC_MALLOC (size);
658 if (G_UNLIKELY (!obj))
661 obj->vtable = vtable;
664 if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_ALLOCATIONS))
665 mono_profiler_allocation (obj);
671 mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length)
675 if (!vtable->klass->has_references) {
676 obj = (MonoArray *)GC_MALLOC_ATOMIC (size);
677 if (G_UNLIKELY (!obj))
680 obj->obj.vtable = vtable;
681 obj->obj.synchronisation = NULL;
683 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
684 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
685 obj = (MonoArray *)GC_GCJ_MALLOC (size, vtable);
686 if (G_UNLIKELY (!obj))
689 obj = (MonoArray *)GC_MALLOC (size);
690 if (G_UNLIKELY (!obj))
693 obj->obj.vtable = vtable;
696 obj->max_length = max_length;
698 if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_ALLOCATIONS))
699 mono_profiler_allocation (&obj->obj);
705 mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uintptr_t bounds_size)
709 if (!vtable->klass->has_references) {
710 obj = (MonoArray *)GC_MALLOC_ATOMIC (size);
711 if (G_UNLIKELY (!obj))
714 obj->obj.vtable = vtable;
715 obj->obj.synchronisation = NULL;
717 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
718 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
719 obj = (MonoArray *)GC_GCJ_MALLOC (size, vtable);
720 if (G_UNLIKELY (!obj))
723 obj = (MonoArray *)GC_MALLOC (size);
724 if (G_UNLIKELY (!obj))
727 obj->obj.vtable = vtable;
730 obj->max_length = max_length;
733 obj->bounds = (MonoArrayBounds *) ((char *) obj + size - bounds_size);
735 if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_ALLOCATIONS))
736 mono_profiler_allocation (&obj->obj);
742 mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len)
744 MonoString *obj = (MonoString *)GC_MALLOC_ATOMIC (size);
745 if (G_UNLIKELY (!obj))
748 obj->object.vtable = vtable;
749 obj->object.synchronisation = NULL;
751 obj->chars [len] = 0;
753 if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_ALLOCATIONS))
754 mono_profiler_allocation (&obj->object);
760 mono_gc_alloc_mature (MonoVTable *vtable, size_t size)
762 return mono_gc_alloc_obj (vtable, size);
766 mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size)
768 return mono_gc_alloc_obj (vtable, size);
772 mono_gc_invoke_finalizers (void)
774 /* There is a bug in GC_invoke_finalizer () in versions <= 6.2alpha4:
775 * the 'mem_freed' variable is not initialized when there are no
776 * objects to finalize, which leads to strange behavior later on.
777 * The check is necessary to work around that bug.
779 if (GC_should_invoke_finalizers ())
780 return GC_invoke_finalizers ();
785 mono_gc_pending_finalizers (void)
787 return GC_should_invoke_finalizers ();
791 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
793 *(void**)field_ptr = value;
797 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
799 *(void**)slot_ptr = value;
803 mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
805 mono_gc_memmove_aligned (dest_ptr, src_ptr, count * sizeof (gpointer));
809 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
811 *(void**)ptr = value;
815 mono_gc_wbarrier_generic_store_atomic (gpointer ptr, MonoObject *value)
817 InterlockedWritePointer ((volatile gpointer *)ptr, value);
821 mono_gc_wbarrier_generic_nostore (gpointer ptr)
826 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
828 mono_gc_memmove_atomic (dest, src, count * mono_class_value_size (klass, NULL));
832 mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
834 /* do not copy the sync state */
835 mono_gc_memmove_aligned ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
836 mono_object_class (obj)->instance_size - sizeof (MonoObject));
840 mono_gc_clear_domain (MonoDomain *domain)
845 mono_gc_suspend_finalizers (void)
850 mono_gc_get_suspend_signal (void)
852 return GC_get_suspend_signal ();
856 mono_gc_get_restart_signal (void)
858 return GC_get_thr_restart_signal ();
861 #if defined(USE_COMPILER_TLS) && defined(__linux__) && (defined(__i386__) || defined(__x86_64__))
862 extern __thread void* GC_thread_tls;
863 #include "metadata-internals.h"
869 while (!(v & (1 << i)))
876 ATYPE_FREEPTR_FOR_BOX,
884 create_allocator (int atype, int tls_key, gboolean slowpath)
886 int index_var, bytes_var, my_fl_var, my_entry_var;
887 guint32 no_freelist_branch, not_small_enough_branch = 0;
888 guint32 size_overflow_branch = 0;
889 MonoMethodBuilder *mb;
891 MonoMethodSignature *csig;
892 const char *name = NULL;
895 g_assert_not_reached ();
897 if (atype == ATYPE_FREEPTR) {
898 name = slowpath ? "SlowAllocPtrfree" : "AllocPtrfree";
899 } else if (atype == ATYPE_FREEPTR_FOR_BOX) {
900 name = slowpath ? "SlowAllocPtrfreeBox" : "AllocPtrfreeBox";
901 } else if (atype == ATYPE_NORMAL) {
902 name = slowpath ? "SlowAlloc" : "Alloc";
903 } else if (atype == ATYPE_GCJ) {
904 name = slowpath ? "SlowAllocGcj" : "AllocGcj";
905 } else if (atype == ATYPE_STRING) {
906 name = slowpath ? "SlowAllocString" : "AllocString";
908 g_assert_not_reached ();
911 csig = mono_metadata_signature_alloc (mono_defaults.corlib, 2);
913 if (atype == ATYPE_STRING) {
914 csig->ret = &mono_defaults.string_class->byval_arg;
915 csig->params [0] = &mono_defaults.int_class->byval_arg;
916 csig->params [1] = &mono_defaults.int32_class->byval_arg;
918 csig->ret = &mono_defaults.object_class->byval_arg;
919 csig->params [0] = &mono_defaults.int_class->byval_arg;
920 csig->params [1] = &mono_defaults.int32_class->byval_arg;
923 mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_ALLOC);
926 goto always_slowpath;
928 bytes_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
929 if (atype == ATYPE_STRING) {
930 /* a string alloator method takes the args: (vtable, len) */
931 /* bytes = (offsetof (MonoString, chars) + ((len + 1) * 2)); */
932 mono_mb_emit_ldarg (mb, 1);
933 mono_mb_emit_icon (mb, 1);
934 mono_mb_emit_byte (mb, MONO_CEE_ADD);
935 mono_mb_emit_icon (mb, 1);
936 mono_mb_emit_byte (mb, MONO_CEE_SHL);
937 // sizeof (MonoString) might include padding
938 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, chars));
939 mono_mb_emit_byte (mb, MONO_CEE_ADD);
940 mono_mb_emit_stloc (mb, bytes_var);
942 mono_mb_emit_ldarg (mb, 1);
943 mono_mb_emit_stloc (mb, bytes_var);
946 /* this is needed for strings/arrays only as the other big types are never allocated with this method */
947 if (atype == ATYPE_STRING) {
949 /* if (!SMALL_ENOUGH (bytes)) jump slow_path;*/
950 mono_mb_emit_ldloc (mb, bytes_var);
951 mono_mb_emit_icon (mb, (NFREELISTS-1) * GRANULARITY);
952 not_small_enough_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_UN_S);
953 /* check for overflow */
954 mono_mb_emit_ldloc (mb, bytes_var);
955 mono_mb_emit_icon (mb, sizeof (MonoString));
956 size_overflow_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLE_UN_S);
959 /* int index = INDEX_FROM_BYTES(bytes); */
960 index_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
962 mono_mb_emit_ldloc (mb, bytes_var);
963 mono_mb_emit_icon (mb, GRANULARITY - 1);
964 mono_mb_emit_byte (mb, MONO_CEE_ADD);
965 mono_mb_emit_icon (mb, shift_amount (GRANULARITY));
966 mono_mb_emit_byte (mb, MONO_CEE_SHR_UN);
967 mono_mb_emit_icon (mb, shift_amount (sizeof (gpointer)));
968 mono_mb_emit_byte (mb, MONO_CEE_SHL);
969 /* index var is already adjusted into bytes */
970 mono_mb_emit_stloc (mb, index_var);
972 my_fl_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
973 my_entry_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
974 /* my_fl = ((GC_thread)tsd) -> ptrfree_freelists + index; */
975 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
976 mono_mb_emit_byte (mb, 0x0D); /* CEE_MONO_TLS */
977 mono_mb_emit_i4 (mb, tls_key);
978 if (atype == ATYPE_FREEPTR || atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING)
979 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
980 + G_STRUCT_OFFSET (struct thread_local_freelists,
982 else if (atype == ATYPE_NORMAL)
983 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
984 + G_STRUCT_OFFSET (struct thread_local_freelists,
986 else if (atype == ATYPE_GCJ)
987 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
988 + G_STRUCT_OFFSET (struct thread_local_freelists,
991 g_assert_not_reached ();
992 mono_mb_emit_byte (mb, MONO_CEE_ADD);
993 mono_mb_emit_ldloc (mb, index_var);
994 mono_mb_emit_byte (mb, MONO_CEE_ADD);
995 mono_mb_emit_stloc (mb, my_fl_var);
997 /* my_entry = *my_fl; */
998 mono_mb_emit_ldloc (mb, my_fl_var);
999 mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
1000 mono_mb_emit_stloc (mb, my_entry_var);
1002 /* if (EXPECT((word)my_entry >= HBLKSIZE, 1)) { */
1003 mono_mb_emit_ldloc (mb, my_entry_var);
1004 mono_mb_emit_icon (mb, HBLKSIZE);
1005 no_freelist_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
1007 /* ptr_t next = obj_link(my_entry); *my_fl = next; */
1008 mono_mb_emit_ldloc (mb, my_fl_var);
1009 mono_mb_emit_ldloc (mb, my_entry_var);
1010 mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
1011 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1013 /* set the vtable and clear the words in the object */
1014 mono_mb_emit_ldloc (mb, my_entry_var);
1015 mono_mb_emit_ldarg (mb, 0);
1016 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1018 if (atype == ATYPE_FREEPTR) {
1019 int start_var, end_var, start_loop;
1020 /* end = my_entry + bytes; start = my_entry + sizeof (gpointer);
1022 start_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1023 end_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1024 mono_mb_emit_ldloc (mb, my_entry_var);
1025 mono_mb_emit_ldloc (mb, bytes_var);
1026 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1027 mono_mb_emit_stloc (mb, end_var);
1028 mono_mb_emit_ldloc (mb, my_entry_var);
1029 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
1030 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1031 mono_mb_emit_stloc (mb, start_var);
1035 * } while (start < end);
1037 start_loop = mono_mb_get_label (mb);
1038 mono_mb_emit_ldloc (mb, start_var);
1039 mono_mb_emit_icon (mb, 0);
1040 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1041 mono_mb_emit_ldloc (mb, start_var);
1042 mono_mb_emit_icon (mb, sizeof (gpointer));
1043 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1044 mono_mb_emit_stloc (mb, start_var);
1046 mono_mb_emit_ldloc (mb, start_var);
1047 mono_mb_emit_ldloc (mb, end_var);
1048 mono_mb_emit_byte (mb, MONO_CEE_BLT_UN_S);
1049 mono_mb_emit_byte (mb, start_loop - (mono_mb_get_label (mb) + 1));
1050 } else if (atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING) {
1051 /* need to clear just the sync pointer */
1052 mono_mb_emit_ldloc (mb, my_entry_var);
1053 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
1054 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1055 mono_mb_emit_icon (mb, 0);
1056 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1059 if (atype == ATYPE_STRING) {
1060 /* need to set length and clear the last char */
1061 /* s->length = len; */
1062 mono_mb_emit_ldloc (mb, my_entry_var);
1063 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, length));
1064 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1065 mono_mb_emit_ldarg (mb, 1);
1066 mono_mb_emit_byte (mb, MONO_CEE_STIND_I4);
1067 /* s->chars [len] = 0; */
1068 mono_mb_emit_ldloc (mb, my_entry_var);
1069 mono_mb_emit_ldloc (mb, bytes_var);
1070 mono_mb_emit_icon (mb, 2);
1071 mono_mb_emit_byte (mb, MONO_CEE_SUB);
1072 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1073 mono_mb_emit_icon (mb, 0);
1074 mono_mb_emit_byte (mb, MONO_CEE_STIND_I2);
1077 /* return my_entry; */
1078 mono_mb_emit_ldloc (mb, my_entry_var);
1079 mono_mb_emit_byte (mb, MONO_CEE_RET);
1081 mono_mb_patch_short_branch (mb, no_freelist_branch);
1082 if (not_small_enough_branch > 0)
1083 mono_mb_patch_short_branch (mb, not_small_enough_branch);
1084 if (size_overflow_branch > 0)
1085 mono_mb_patch_short_branch (mb, size_overflow_branch);
1087 /* the slow path: we just call back into the runtime */
1089 if (atype == ATYPE_STRING) {
1090 mono_mb_emit_ldarg (mb, 1);
1091 mono_mb_emit_icall (mb, ves_icall_string_alloc);
1093 mono_mb_emit_ldarg (mb, 0);
1094 mono_mb_emit_icall (mb, ves_icall_object_new_specific);
1097 mono_mb_emit_byte (mb, MONO_CEE_RET);
1099 info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE);
1100 info->d.alloc.gc_name = "boehm";
1101 info->d.alloc.alloc_type = atype;
1102 mb->init_locals = FALSE;
1104 res = mono_mb_create (mb, csig, 8, info);
1110 static MonoMethod* alloc_method_cache [ATYPE_NUM];
1111 static MonoMethod* slowpath_alloc_method_cache [ATYPE_NUM];
1114 mono_gc_is_critical_method (MonoMethod *method)
1118 for (i = 0; i < ATYPE_NUM; ++i)
1119 if (method == alloc_method_cache [i] || method == slowpath_alloc_method_cache [i])
1126 * If possible, generate a managed method that can quickly allocate objects in class
1127 * @klass. The method will typically have an thread-local inline allocation sequence.
1128 * The signature of the called method is:
1129 * object allocate (MonoVTable *vtable)
1130 * Some of the logic here is similar to mono_class_get_allocation_ftn () i object.c,
1132 * The thread local alloc logic is taken from libgc/pthread_support.c.
1136 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size)
1141 * Tls implementation changed, we jump to tls native getters/setters.
1142 * Is boehm managed allocator ok with this ? Do we even care ?
1146 if (!SMALL_ENOUGH (klass->instance_size))
1148 if (mono_class_has_finalizer (klass) || mono_class_is_marshalbyref (klass))
1150 if (mono_profiler_get_events () & (MONO_PROFILE_ALLOCATIONS | MONO_PROFILE_STATISTICAL))
1154 if (mono_class_is_open_constructed_type (&klass->byval_arg))
1156 if (klass->byval_arg.type == MONO_TYPE_STRING) {
1157 atype = ATYPE_STRING;
1158 } else if (!known_instance_size) {
1160 } else if (!klass->has_references) {
1162 atype = ATYPE_FREEPTR_FOR_BOX;
1164 atype = ATYPE_FREEPTR;
1168 * disabled because we currently do a runtime choice anyway, to
1169 * deal with multiple appdomains.
1170 if (vtable->gc_descr != GC_NO_DESCRIPTOR)
1173 atype = ATYPE_NORMAL;
1176 return mono_gc_get_managed_allocator_by_type (atype, MANAGED_ALLOCATOR_REGULAR);
1180 mono_gc_get_managed_array_allocator (MonoClass *klass)
1186 * mono_gc_get_managed_allocator_by_type:
1188 * Return a managed allocator method corresponding to allocator type ATYPE.
1191 mono_gc_get_managed_allocator_by_type (int atype, ManagedAllocatorVariant variant)
1194 gboolean slowpath = variant != MANAGED_ALLOCATOR_REGULAR;
1195 MonoMethod **cache = slowpath ? slowpath_alloc_method_cache : alloc_method_cache;
1199 res = cache [atype];
1203 res = create_allocator (atype, -1, slowpath);
1204 mono_os_mutex_lock (&mono_gc_lock);
1205 if (cache [atype]) {
1206 mono_free_method (res);
1207 res = cache [atype];
1209 mono_memory_barrier ();
1210 cache [atype] = res;
1212 mono_os_mutex_unlock (&mono_gc_lock);
1217 mono_gc_get_managed_allocator_types (void)
1223 mono_gc_get_write_barrier (void)
1225 g_assert_not_reached ();
1232 mono_gc_is_critical_method (MonoMethod *method)
1238 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size)
1244 mono_gc_get_managed_array_allocator (MonoClass *klass)
1250 mono_gc_get_managed_allocator_by_type (int atype, ManagedAllocatorVariant variant)
1256 mono_gc_get_managed_allocator_types (void)
1262 mono_gc_get_write_barrier (void)
1264 g_assert_not_reached ();
1271 mono_gc_get_specific_write_barrier (gboolean is_concurrent)
1273 g_assert_not_reached ();
1278 mono_gc_get_aligned_size_for_allocator (int size)
1284 mono_gc_get_gc_name (void)
1290 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
1292 return GC_call_with_alloc_lock (func, data);
1296 mono_gc_get_description (void)
1298 return g_strdup (DEFAULT_GC_NAME);
1302 mono_gc_set_desktop_mode (void)
1308 mono_gc_is_moving (void)
1314 mono_gc_is_disabled (void)
1316 if (GC_dont_gc || g_hasenv ("GC_DONT_GC"))
1323 mono_gc_wbarrier_range_copy (gpointer _dest, gpointer _src, int size)
1325 g_assert_not_reached ();
1329 mono_gc_get_range_copy_func (void)
1331 return &mono_gc_wbarrier_range_copy;
1335 mono_gc_get_card_table (int *shift_bits, gpointer *card_mask)
1337 g_assert_not_reached ();
1342 mono_gc_card_table_nursery_check (void)
1344 g_assert_not_reached ();
1349 mono_gc_get_nursery (int *shift_bits, size_t *size)
1355 mono_gc_precise_stack_mark_enabled (void)
1361 mono_gc_get_logfile (void)
1367 mono_gc_params_set (const char* options)
1372 mono_gc_debug_set (const char* options)
1377 mono_gc_conservatively_scan_area (void *start, void *end)
1379 g_assert_not_reached ();
1383 mono_gc_scan_object (void *obj, void *gc_data)
1385 g_assert_not_reached ();
1390 mono_gc_get_bitmap_for_descr (void *descr, int *numbits)
1392 g_assert_not_reached ();
1397 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
1402 mono_gc_set_stack_end (void *stack_end)
1406 void mono_gc_set_skip_thread (gboolean value)
1411 mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
1416 /* This assertion is not valid when GC_DEBUG is defined */
1417 g_assert (GC_base (obj) == (char*)obj - offset);
1420 GC_REGISTER_FINALIZER_NO_ORDER ((char*)obj - offset, (GC_finalization_proc)user_data, GUINT_TO_POINTER (offset), NULL, NULL);
1425 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
1427 /* it is being replaced by GC_pthread_create on some
1428 * platforms, see libgc/include/gc_pthread_redirects.h */
1429 return pthread_create (new_thread, attr, start_routine, arg);
1434 BOOL APIENTRY mono_gc_dllmain (HMODULE module_handle, DWORD reason, LPVOID reserved)
1436 return GC_DllMain (module_handle, reason, reserved);
1441 mono_gc_get_vtable_bits (MonoClass *klass)
1443 if (fin_callbacks.is_class_finalization_aware) {
1444 if (fin_callbacks.is_class_finalization_aware (klass))
1445 return BOEHM_GC_BIT_FINALIZER_AWARE;
1451 * mono_gc_register_altstack:
1453 * Register the dimensions of the normal stack and altstack with the collector.
1454 * Currently, STACK/STACK_SIZE is only used when the thread is suspended while it is on an altstack.
1457 mono_gc_register_altstack (gpointer stack, gint32 stack_size, gpointer altstack, gint32 altstack_size)
1459 GC_register_altstack (stack, stack_size, altstack, altstack_size);
1463 mono_gc_get_los_limit (void)
1469 mono_gc_set_string_length (MonoString *str, gint32 new_length)
1471 mono_unichar2 *new_end = str->chars + new_length;
1473 /* zero the discarded string. This null-delimits the string and allows
1474 * the space to be reclaimed by SGen. */
1476 memset (new_end, 0, (str->length - new_length + 1) * sizeof (mono_unichar2));
1477 str->length = new_length;
1481 mono_gc_user_markers_supported (void)
1487 mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
1489 g_assert_not_reached ();
1493 /* Toggleref support */
1496 mono_gc_toggleref_add (MonoObject *object, mono_bool strong_ref)
1498 if (GC_toggleref_add ((GC_PTR)object, (int)strong_ref) != GC_SUCCESS)
1499 g_error ("GC_toggleref_add failed\n");
1503 mono_gc_toggleref_register_callback (MonoToggleRefStatus (*proccess_toggleref) (MonoObject *obj))
1505 GC_set_toggleref_func ((GC_ToggleRefStatus (*) (GC_PTR obj)) proccess_toggleref);
1508 /* Test support code */
1510 static MonoToggleRefStatus
1511 test_toggleref_callback (MonoObject *obj)
1513 static MonoClassField *mono_toggleref_test_field;
1514 MonoToggleRefStatus status = MONO_TOGGLE_REF_DROP;
1516 if (!mono_toggleref_test_field) {
1517 mono_toggleref_test_field = mono_class_get_field_from_name (mono_object_get_class (obj), "__test");
1518 g_assert (mono_toggleref_test_field);
1521 mono_field_get_value (obj, mono_toggleref_test_field, &status);
1522 printf ("toggleref-cb obj %d\n", status);
1527 register_test_toggleref_callback (void)
1529 mono_gc_toggleref_register_callback (test_toggleref_callback);
1533 is_finalization_aware (MonoObject *obj)
1535 MonoVTable *vt = obj->vtable;
1536 return (vt->gc_bits & BOEHM_GC_BIT_FINALIZER_AWARE) == BOEHM_GC_BIT_FINALIZER_AWARE;
1540 fin_notifier (MonoObject *obj)
1542 if (is_finalization_aware (obj))
1543 fin_callbacks.object_queued_for_finalization (obj);
1547 mono_gc_register_finalizer_callbacks (MonoGCFinalizerCallbacks *callbacks)
1549 if (callbacks->version != MONO_GC_FINALIZER_EXTENSION_VERSION)
1550 g_error ("Invalid finalizer callback version. Expected %d but got %d\n", MONO_GC_FINALIZER_EXTENSION_VERSION, callbacks->version);
1552 fin_callbacks = *callbacks;
1554 GC_set_await_finalize_proc ((void (*) (GC_PTR))fin_notifier);
1557 #define BITMAP_SIZE (sizeof (*((HandleData *)NULL)->bitmap) * CHAR_BIT)
1559 static inline gboolean
1560 slot_occupied (HandleData *handles, guint slot) {
1561 return handles->bitmap [slot / BITMAP_SIZE] & (1 << (slot % BITMAP_SIZE));
1565 vacate_slot (HandleData *handles, guint slot) {
1566 handles->bitmap [slot / BITMAP_SIZE] &= ~(1 << (slot % BITMAP_SIZE));
1570 occupy_slot (HandleData *handles, guint slot) {
1571 handles->bitmap [slot / BITMAP_SIZE] |= 1 << (slot % BITMAP_SIZE);
1575 find_first_unset (guint32 bitmap)
1578 for (i = 0; i < 32; ++i) {
1579 if (!(bitmap & (1 << i)))
1586 handle_data_alloc_entries (HandleData *handles)
1589 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1590 handles->entries = (void **)g_malloc0 (sizeof (*handles->entries) * handles->size);
1591 handles->domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * handles->size);
1593 handles->entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * handles->size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
1595 handles->bitmap = (guint32 *)g_malloc0 (handles->size / CHAR_BIT);
1599 handle_data_next_unset (HandleData *handles)
1602 for (slot = handles->slot_hint; slot < handles->size / BITMAP_SIZE; ++slot) {
1603 if (handles->bitmap [slot] == 0xffffffff)
1605 handles->slot_hint = slot;
1606 return find_first_unset (handles->bitmap [slot]);
1612 handle_data_first_unset (HandleData *handles)
1615 for (slot = 0; slot < handles->slot_hint; ++slot) {
1616 if (handles->bitmap [slot] == 0xffffffff)
1618 handles->slot_hint = slot;
1619 return find_first_unset (handles->bitmap [slot]);
1624 /* Returns the index of the current slot in the bitmap. */
1626 handle_data_grow (HandleData *handles, gboolean track)
1628 guint32 *new_bitmap;
1629 guint32 new_size = handles->size * 2; /* always double: we memset to 0 based on this below */
1631 /* resize and copy the bitmap */
1632 new_bitmap = (guint32 *)g_malloc0 (new_size / CHAR_BIT);
1633 memcpy (new_bitmap, handles->bitmap, handles->size / CHAR_BIT);
1634 g_free (handles->bitmap);
1635 handles->bitmap = new_bitmap;
1637 /* resize and copy the entries */
1638 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1640 guint16 *domain_ids;
1642 domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * new_size);
1643 entries = (void **)g_malloc0 (sizeof (*handles->entries) * new_size);
1644 memcpy (domain_ids, handles->domain_ids, sizeof (*handles->domain_ids) * handles->size);
1645 for (i = 0; i < handles->size; ++i) {
1646 MonoObject *obj = mono_gc_weak_link_get (&(handles->entries [i]));
1648 mono_gc_weak_link_add (&(entries [i]), obj, track);
1649 mono_gc_weak_link_remove (&(handles->entries [i]), track);
1651 g_assert (!handles->entries [i]);
1654 g_free (handles->entries);
1655 g_free (handles->domain_ids);
1656 handles->entries = entries;
1657 handles->domain_ids = domain_ids;
1660 entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * new_size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
1661 mono_gc_memmove_aligned (entries, handles->entries, sizeof (*handles->entries) * handles->size);
1662 mono_gc_free_fixed (handles->entries);
1663 handles->entries = entries;
1665 handles->slot_hint = handles->size / BITMAP_SIZE;
1666 handles->size = new_size;
1670 alloc_handle (HandleData *handles, MonoObject *obj, gboolean track)
1674 lock_handles (handles);
1676 handle_data_alloc_entries (handles);
1677 i = handle_data_next_unset (handles);
1678 if (i == -1 && handles->slot_hint != 0)
1679 i = handle_data_first_unset (handles);
1681 handle_data_grow (handles, track);
1684 slot = handles->slot_hint * BITMAP_SIZE + i;
1685 occupy_slot (handles, slot);
1686 handles->entries [slot] = NULL;
1687 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1688 /*FIXME, what to use when obj == null?*/
1689 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
1691 mono_gc_weak_link_add (&(handles->entries [slot]), obj, track);
1693 handles->entries [slot] = obj;
1696 #ifndef DISABLE_PERFCOUNTERS
1697 mono_perfcounters->gc_num_handles++;
1699 unlock_handles (handles);
1700 res = MONO_GC_HANDLE (slot, handles->type);
1701 mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_CREATED, handles->type, res, obj);
1706 * mono_gchandle_new:
1707 * \param obj managed object to get a handle for
1708 * \param pinned whether the object should be pinned
1710 * This returns a handle that wraps the object, this is used to keep a
1711 * reference to a managed object from the unmanaged world and preventing the
1712 * object from being disposed.
1714 * If \p pinned is false the address of the object can not be obtained, if it is
1715 * true the address of the object can be obtained. This will also pin the
1716 * object so it will not be possible by a moving garbage collector to move the
1719 * \returns a handle that can be used to access the object from
1723 mono_gchandle_new (MonoObject *obj, gboolean pinned)
1725 return alloc_handle (&gc_handles [pinned? HANDLE_PINNED: HANDLE_NORMAL], obj, FALSE);
1729 * mono_gchandle_new_weakref:
1730 * \param obj managed object to get a handle for
1731 * \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.
1733 * This returns a weak handle that wraps the object, this is used to
1734 * keep a reference to a managed object from the unmanaged world.
1735 * Unlike the \c mono_gchandle_new the object can be reclaimed by the
1736 * garbage collector. In this case the value of the GCHandle will be
1739 * If \p track_resurrection is TRUE the object will be tracked through
1740 * finalization and if the object is resurrected during the execution
1741 * of the finalizer, then the returned weakref will continue to hold
1742 * a reference to the object. If \p track_resurrection is FALSE, then
1743 * the weak reference's target will become NULL as soon as the object
1744 * is passed on to the finalizer.
1746 * \returns a handle that can be used to access the object from
1750 mono_gchandle_new_weakref (MonoObject *obj, gboolean track_resurrection)
1752 return alloc_handle (&gc_handles [track_resurrection? HANDLE_WEAK_TRACK: HANDLE_WEAK], obj, track_resurrection);
1756 * mono_gchandle_get_target:
1757 * \param gchandle a GCHandle's handle.
1759 * The handle was previously created by calling \c mono_gchandle_new or
1760 * \c mono_gchandle_new_weakref.
1762 * \returns A pointer to the \c MonoObject* represented by the handle or
1763 * NULL for a collected object if using a weakref handle.
1766 mono_gchandle_get_target (guint32 gchandle)
1768 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1769 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1770 HandleData *handles = &gc_handles [type];
1771 MonoObject *obj = NULL;
1772 if (type >= HANDLE_TYPE_MAX)
1775 lock_handles (handles);
1776 if (slot < handles->size && slot_occupied (handles, slot)) {
1777 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1778 obj = mono_gc_weak_link_get (&handles->entries [slot]);
1780 obj = (MonoObject *)handles->entries [slot];
1783 /* print a warning? */
1785 unlock_handles (handles);
1786 /*g_print ("get target of entry %d of type %d: %p\n", slot, handles->type, obj);*/
1791 mono_gchandle_set_target (guint32 gchandle, MonoObject *obj)
1793 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1794 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1795 HandleData *handles = &gc_handles [type];
1796 MonoObject *old_obj = NULL;
1798 g_assert (type < HANDLE_TYPE_MAX);
1799 lock_handles (handles);
1800 if (slot < handles->size && slot_occupied (handles, slot)) {
1801 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1802 old_obj = (MonoObject *)handles->entries [slot];
1803 if (handles->entries [slot])
1804 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1806 mono_gc_weak_link_add (&handles->entries [slot], obj, handles->type == HANDLE_WEAK_TRACK);
1807 /*FIXME, what to use when obj == null?*/
1808 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
1810 handles->entries [slot] = obj;
1813 /* print a warning? */
1815 /*g_print ("changed entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
1816 unlock_handles (handles);
1820 mono_gc_is_null (void)
1826 * mono_gchandle_is_in_domain:
1827 * \param gchandle a GCHandle's handle.
1828 * \param domain An application domain.
1830 * Use this function to determine if the \p gchandle points to an
1831 * object allocated in the specified \p domain.
1833 * \returns TRUE if the object wrapped by the \p gchandle belongs to the specific \p domain.
1836 mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain)
1838 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1839 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1840 HandleData *handles = &gc_handles [type];
1841 gboolean result = FALSE;
1843 if (type >= HANDLE_TYPE_MAX)
1846 lock_handles (handles);
1847 if (slot < handles->size && slot_occupied (handles, slot)) {
1848 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1849 result = domain->domain_id == handles->domain_ids [slot];
1852 obj = (MonoObject *)handles->entries [slot];
1856 result = domain == mono_object_domain (obj);
1859 /* print a warning? */
1861 unlock_handles (handles);
1866 * mono_gchandle_free:
1867 * \param gchandle a GCHandle's handle.
1869 * Frees the \p gchandle handle. If there are no outstanding
1870 * references, the garbage collector can reclaim the memory of the
1874 mono_gchandle_free (guint32 gchandle)
1876 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1877 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1878 HandleData *handles = &gc_handles [type];
1879 if (type >= HANDLE_TYPE_MAX)
1882 lock_handles (handles);
1883 if (slot < handles->size && slot_occupied (handles, slot)) {
1884 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1885 if (handles->entries [slot])
1886 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1888 handles->entries [slot] = NULL;
1890 vacate_slot (handles, slot);
1892 /* print a warning? */
1894 #ifndef DISABLE_PERFCOUNTERS
1895 mono_perfcounters->gc_num_handles--;
1897 /*g_print ("freed entry %d of type %d\n", slot, handles->type);*/
1898 unlock_handles (handles);
1899 mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_DESTROYED, handles->type, gchandle, NULL);
1903 * mono_gchandle_free_domain:
1904 * \param domain domain that is unloading
1906 * Function used internally to cleanup any GC handle for objects belonging
1907 * to the specified domain during appdomain unload.
1910 mono_gchandle_free_domain (MonoDomain *domain)
1914 for (type = HANDLE_TYPE_MIN; type < HANDLE_PINNED; ++type) {
1916 HandleData *handles = &gc_handles [type];
1917 lock_handles (handles);
1918 for (slot = 0; slot < handles->size; ++slot) {
1919 if (!slot_occupied (handles, slot))
1921 if (MONO_GC_HANDLE_TYPE_IS_WEAK (type)) {
1922 if (domain->domain_id == handles->domain_ids [slot]) {
1923 vacate_slot (handles, slot);
1924 if (handles->entries [slot])
1925 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1928 if (handles->entries [slot] && mono_object_domain (handles->entries [slot]) == domain) {
1929 vacate_slot (handles, slot);
1930 handles->entries [slot] = NULL;
1934 unlock_handles (handles);
1940 MONO_EMPTY_SOURCE_FILE (boehm_gc);
1941 #endif /* no Boehm GC */