2 * boehm-gc.c: GC implementation using either the installed or included Boehm GC.
4 * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
5 * Copyright 2004-2011 Novell, Inc (http://www.novell.com)
6 * Copyright 2011-2012 Xamarin, Inc (http://www.xamarin.com)
7 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
14 #define GC_I_HIDE_POINTERS
15 #include <mono/metadata/gc-internals.h>
16 #include <mono/metadata/mono-gc.h>
17 #include <mono/metadata/profiler-private.h>
18 #include <mono/metadata/class-internals.h>
19 #include <mono/metadata/method-builder.h>
20 #include <mono/metadata/opcodes.h>
21 #include <mono/metadata/domain-internals.h>
22 #include <mono/metadata/metadata-internals.h>
23 #include <mono/metadata/marshal.h>
24 #include <mono/metadata/runtime.h>
25 #include <mono/metadata/handle.h>
26 #include <mono/metadata/sgen-toggleref.h>
27 #include <mono/utils/atomic.h>
28 #include <mono/utils/mono-logger-internals.h>
29 #include <mono/utils/mono-memory-model.h>
30 #include <mono/utils/mono-time.h>
31 #include <mono/utils/mono-threads.h>
32 #include <mono/utils/dtrace.h>
33 #include <mono/utils/gc_wrapper.h>
34 #include <mono/utils/mono-os-mutex.h>
35 #include <mono/utils/mono-counters.h>
41 #define THREAD_LOCAL_ALLOC 1
42 #include "private/pthread_support.h"
44 #if defined(PLATFORM_MACOSX) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
45 void *pthread_get_stackaddr_np(pthread_t);
48 #define GC_NO_DESCRIPTOR ((gpointer)(0 | GC_DS_LENGTH))
49 /*Boehm max heap cannot be smaller than 16MB*/
50 #define MIN_BOEHM_MAX_HEAP_SIZE_IN_MB 16
51 #define MIN_BOEHM_MAX_HEAP_SIZE (MIN_BOEHM_MAX_HEAP_SIZE_IN_MB << 20)
53 static gboolean gc_initialized = FALSE;
54 static mono_mutex_t mono_gc_lock;
57 boehm_thread_register (MonoThreadInfo* info, void *baseptr);
59 boehm_thread_unregister (MonoThreadInfo *p);
61 register_test_toggleref_callback (void);
63 #define BOEHM_GC_BIT_FINALIZER_AWARE 1
64 static MonoGCFinalizerCallbacks fin_callbacks;
68 static mono_mutex_t handle_section;
69 #define lock_handles(handles) mono_os_mutex_lock (&handle_section)
70 #define unlock_handles(handles) mono_os_mutex_unlock (&handle_section)
77 guint slot_hint : 24; /* starting slot for search in bitmap */
78 /* 2^16 appdomains should be enough for everyone (though I know I'll regret this in 20 years) */
79 /* we alloc this only for weak refs, since we can get the domain directly in the other cases */
83 #define EMPTY_HANDLE_DATA(type) {NULL, NULL, 0, (type), 0, NULL}
85 /* weak and weak-track arrays will be allocated in malloc memory
87 static HandleData gc_handles [] = {
88 EMPTY_HANDLE_DATA (HANDLE_WEAK),
89 EMPTY_HANDLE_DATA (HANDLE_WEAK_TRACK),
90 EMPTY_HANDLE_DATA (HANDLE_NORMAL),
91 EMPTY_HANDLE_DATA (HANDLE_PINNED)
95 mono_gc_warning (char *msg, GC_word arg)
97 mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_GC, msg, (unsigned long)arg);
101 mono_gc_base_init (void)
103 MonoThreadInfoCallbacks cb;
110 mono_counters_init ();
113 * Handle the case when we are called from a thread different from the main thread,
115 * FIXME: Move this to libgc where it belongs.
117 * we used to do this only when running on valgrind,
118 * but it happens also in other setups.
120 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) && !defined(__native_client__)
125 pthread_getattr_np (pthread_self (), &attr);
126 pthread_attr_getstack (&attr, &sstart, &size);
127 pthread_attr_destroy (&attr);
128 /*g_print ("stackbottom pth is: %p\n", (char*)sstart + size);*/
131 * The calculation above doesn't seem to work on ia64, also we need to set
132 * GC_register_stackbottom as well, but don't know how.
135 /* apparently with some linuxthreads implementations sstart can be NULL,
136 * fallback to the more imprecise method (bug# 78096).
139 GC_stackbottom = (char*)sstart + size;
142 gsize stack_bottom = (gsize)&dummy;
143 stack_bottom += 4095;
144 stack_bottom &= ~4095;
145 GC_stackbottom = (char*)stack_bottom;
149 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
150 GC_stackbottom = (char*)pthread_get_stackaddr_np (pthread_self ());
151 #elif defined(__OpenBSD__)
152 # include <pthread_np.h>
157 rslt = pthread_stackseg_np(pthread_self(), &ss);
158 g_assert (rslt == 0);
160 GC_stackbottom = (char*)ss.ss_sp;
162 #elif defined(__native_client__)
163 /* Do nothing, GC_stackbottom is set correctly in libgc */
167 gsize stack_bottom = (gsize)&dummy;
168 stack_bottom += 4095;
169 stack_bottom &= ~4095;
170 /*g_print ("stackbottom is: %p\n", (char*)stack_bottom);*/
171 GC_stackbottom = (char*)stack_bottom;
175 #if !defined(PLATFORM_ANDROID)
176 /* If GC_no_dls is set to true, GC_find_limit is not called. This causes a seg fault on Android. */
180 if ((env = g_getenv ("MONO_GC_DEBUG"))) {
181 char **opts = g_strsplit (env, ",", -1);
182 for (char **ptr = opts; ptr && *ptr; ptr ++) {
184 if (!strcmp (opt, "do-not-finalize")) {
185 mono_do_not_finalize = 1;
186 } else if (!strcmp (opt, "log-finalizers")) {
195 GC_set_warn_proc (mono_gc_warning);
196 GC_finalize_on_demand = 1;
197 GC_finalizer_notifier = mono_gc_finalize_notify;
199 GC_init_gcj_malloc (5, NULL);
200 GC_allow_register_threads ();
202 if ((env = g_getenv ("MONO_GC_PARAMS"))) {
203 char **ptr, **opts = g_strsplit (env, ",", -1);
204 for (ptr = opts; *ptr; ++ptr) {
206 if (g_str_has_prefix (opt, "max-heap-size=")) {
209 opt = strchr (opt, '=') + 1;
210 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &max_heap)) {
211 if (max_heap < MIN_BOEHM_MAX_HEAP_SIZE) {
212 fprintf (stderr, "max-heap-size must be at least %dMb.\n", MIN_BOEHM_MAX_HEAP_SIZE_IN_MB);
215 GC_set_max_heap_size (max_heap);
217 fprintf (stderr, "max-heap-size must be an integer.\n");
221 } else if (g_str_has_prefix (opt, "toggleref-test")) {
222 register_test_toggleref_callback ();
225 /* Could be a parameter for sgen */
227 fprintf (stderr, "MONO_GC_PARAMS must be a comma-delimited list of one or more of the following:\n");
228 fprintf (stderr, " max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
236 memset (&cb, 0, sizeof (cb));
237 cb.thread_register = boehm_thread_register;
238 cb.thread_unregister = boehm_thread_unregister;
239 cb.mono_method_is_critical = (gboolean (*)(void *))mono_runtime_is_critical_method;
241 mono_threads_init (&cb, sizeof (MonoThreadInfo));
242 mono_os_mutex_init (&mono_gc_lock);
243 mono_os_mutex_init_recursive (&handle_section);
245 mono_thread_info_attach (&dummy);
247 mono_gc_enable_events ();
249 MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_NORMAL].entries, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
250 MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_PINNED].entries, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
252 gc_initialized = TRUE;
256 mono_gc_base_cleanup (void)
258 GC_finalizer_notifier = NULL;
263 * @generation: GC generation identifier
265 * Perform a garbage collection for the given generation, higher numbers
266 * mean usually older objects. Collecting a high-numbered generation
267 * implies collecting also the lower-numbered generations.
268 * The maximum value for @generation can be retrieved with a call to
269 * mono_gc_max_generation(), so this function is usually called as:
271 * mono_gc_collect (mono_gc_max_generation ());
274 mono_gc_collect (int generation)
276 #ifndef DISABLE_PERFCOUNTERS
277 mono_perfcounters->gc_induced++;
283 * mono_gc_max_generation:
285 * Get the maximum generation number used by the current garbage
286 * collector. The value will be 0 for the Boehm collector, 1 or more
287 * for the generational collectors.
289 * Returns: the maximum generation number.
292 mono_gc_max_generation (void)
298 * mono_gc_get_generation:
299 * @object: a managed object
301 * Get the garbage collector's generation that @object belongs to.
302 * Use this has a hint only.
304 * Returns: a garbage collector generation number
307 mono_gc_get_generation (MonoObject *object)
313 * mono_gc_collection_count:
314 * @generation: a GC generation number
316 * Get how many times a garbage collection has been performed
317 * for the given @generation number.
319 * Returns: the number of garbage collections
322 mono_gc_collection_count (int generation)
328 * mono_gc_add_memory_pressure:
329 * @value: amount of bytes
331 * Adjust the garbage collector's view of how many bytes of memory
332 * are indirectly referenced by managed objects (for example unmanaged
333 * memory holding image or other binary data).
334 * This is a hint only to the garbage collector algorithm.
335 * Note that negative amounts of @value will decrease the memory
339 mono_gc_add_memory_pressure (gint64 value)
344 * mono_gc_get_used_size:
346 * Get the approximate amount of memory used by managed objects.
348 * Returns: the amount of memory used in bytes
351 mono_gc_get_used_size (void)
353 return GC_get_heap_size () - GC_get_free_bytes ();
357 * mono_gc_get_heap_size:
359 * Get the amount of memory used by the garbage collector.
361 * Returns: the size of the heap in bytes
364 mono_gc_get_heap_size (void)
366 return GC_get_heap_size ();
370 mono_gc_is_gc_thread (void)
372 return GC_thread_is_registered ();
376 mono_gc_register_thread (void *baseptr)
378 return mono_thread_info_attach (baseptr) != NULL;
382 boehm_thread_register (MonoThreadInfo* info, void *baseptr)
384 struct GC_stack_base sb;
387 /* TODO: use GC_get_stack_base instead of baseptr. */
388 sb.mem_base = baseptr;
389 res = GC_register_my_thread (&sb);
390 if (res == GC_UNIMPLEMENTED)
391 return NULL; /* Cannot happen with GC v7+. */
393 info->handle_stack = mono_handle_stack_alloc ();
399 boehm_thread_unregister (MonoThreadInfo *p)
401 MonoNativeThreadId tid;
403 tid = mono_thread_info_get_tid (p);
405 if (p->runtime_thread)
406 mono_threads_add_joinable_thread ((gpointer)tid);
410 mono_object_is_alive (MonoObject* o)
412 return GC_is_marked ((ptr_t)o);
416 mono_gc_walk_heap (int flags, MonoGCReferences callback, void *data)
421 static gint64 gc_start_time;
424 on_gc_notification (GC_EventType event)
426 MonoGCEvent e = (MonoGCEvent)event;
429 case MONO_GC_EVENT_PRE_STOP_WORLD:
430 MONO_GC_WORLD_STOP_BEGIN ();
431 mono_thread_info_suspend_lock ();
434 case MONO_GC_EVENT_POST_STOP_WORLD:
435 MONO_GC_WORLD_STOP_END ();
438 case MONO_GC_EVENT_PRE_START_WORLD:
439 MONO_GC_WORLD_RESTART_BEGIN (1);
442 case MONO_GC_EVENT_POST_START_WORLD:
443 MONO_GC_WORLD_RESTART_END (1);
444 mono_thread_info_suspend_unlock ();
447 case MONO_GC_EVENT_START:
449 #ifndef DISABLE_PERFCOUNTERS
450 if (mono_perfcounters)
451 mono_perfcounters->gc_collections0++;
453 gc_stats.major_gc_count ++;
454 gc_start_time = mono_100ns_ticks ();
457 case MONO_GC_EVENT_END:
459 #if defined(ENABLE_DTRACE) && defined(__sun__)
460 /* This works around a dtrace -G problem on Solaris.
461 Limit its actual use to when the probe is enabled. */
462 if (MONO_GC_END_ENABLED ())
466 #ifndef DISABLE_PERFCOUNTERS
467 if (mono_perfcounters) {
468 guint64 heap_size = GC_get_heap_size ();
469 guint64 used_size = heap_size - GC_get_free_bytes ();
470 mono_perfcounters->gc_total_bytes = used_size;
471 mono_perfcounters->gc_committed_bytes = heap_size;
472 mono_perfcounters->gc_reserved_bytes = heap_size;
473 mono_perfcounters->gc_gen0size = heap_size;
476 gc_stats.major_gc_time += mono_100ns_ticks () - gc_start_time;
477 mono_trace_message (MONO_TRACE_GC, "gc took %d usecs", (mono_100ns_ticks () - gc_start_time) / 10);
483 mono_profiler_gc_event (e, 0);
487 on_gc_heap_resize (size_t new_size)
489 guint64 heap_size = GC_get_heap_size ();
490 #ifndef DISABLE_PERFCOUNTERS
491 if (mono_perfcounters) {
492 mono_perfcounters->gc_committed_bytes = heap_size;
493 mono_perfcounters->gc_reserved_bytes = heap_size;
494 mono_perfcounters->gc_gen0size = heap_size;
497 mono_profiler_gc_heap_resize (new_size);
501 mono_gc_enable_events (void)
503 GC_set_on_collection_event (on_gc_notification);
504 GC_on_heap_resize = on_gc_heap_resize;
507 static gboolean alloc_events = FALSE;
510 mono_gc_enable_alloc_events (void)
516 mono_gc_register_root (char *start, size_t size, void *descr, MonoGCRootSource source, const char *msg)
518 /* for some strange reason, they want one extra byte on the end */
519 GC_add_roots (start, start + size + 1);
525 mono_gc_deregister_root (char* addr)
528 /* FIXME: libgc doesn't define this work win32 for some reason */
529 /* FIXME: No size info */
530 GC_remove_roots (addr, addr + sizeof (gpointer) + 1);
535 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
537 /* libgc requires that we use HIDE_POINTER... */
538 *link_addr = (void*)HIDE_POINTER (obj);
540 GC_REGISTER_LONG_LINK (link_addr, obj);
542 GC_GENERAL_REGISTER_DISAPPEARING_LINK (link_addr, obj);
546 mono_gc_weak_link_remove (void **link_addr, gboolean track)
549 GC_unregister_long_link (link_addr);
551 GC_unregister_disappearing_link (link_addr);
556 reveal_link (gpointer link_addr)
558 void **link_a = (void **)link_addr;
559 return REVEAL_POINTER (*link_a);
563 mono_gc_weak_link_get (void **link_addr)
565 MonoObject *obj = (MonoObject *)GC_call_with_alloc_lock (reveal_link, link_addr);
566 if (obj == (MonoObject *) -1)
572 mono_gc_make_descr_for_string (gsize *bitmap, int numbits)
574 return mono_gc_make_descr_from_bitmap (bitmap, numbits);
578 mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size)
580 return mono_gc_make_descr_from_bitmap (bitmap, numbits);
584 mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size)
586 /* libgc has no usable support for arrays... */
587 return GC_NO_DESCRIPTOR;
591 mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
593 /* It seems there are issues when the bitmap doesn't fit: play it safe */
595 return GC_NO_DESCRIPTOR;
597 return (gpointer)GC_make_descriptor ((GC_bitmap)bitmap, numbits);
601 mono_gc_make_root_descr_all_refs (int numbits)
607 mono_gc_alloc_fixed (size_t size, void *descr, MonoGCRootSource source, const char *msg)
609 /* To help track down typed allocation bugs */
613 if (count == atoi (g_getenv ("COUNT2")))
615 if (count > atoi (g_getenv ("COUNT2")))
616 return GC_MALLOC (size);
620 return GC_MALLOC_EXPLICITLY_TYPED (size, (GC_descr)descr);
622 return GC_MALLOC (size);
626 mono_gc_free_fixed (void* addr)
631 mono_gc_alloc_obj (MonoVTable *vtable, size_t size)
635 if (!vtable->klass->has_references) {
636 obj = (MonoObject *)GC_MALLOC_ATOMIC (size);
637 if (G_UNLIKELY (!obj))
640 obj->vtable = vtable;
641 obj->synchronisation = NULL;
643 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
644 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
645 obj = (MonoObject *)GC_GCJ_MALLOC (size, vtable);
646 if (G_UNLIKELY (!obj))
649 obj = (MonoObject *)GC_MALLOC (size);
650 if (G_UNLIKELY (!obj))
653 obj->vtable = vtable;
656 if (G_UNLIKELY (alloc_events))
657 mono_profiler_allocation (obj);
663 mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length)
667 if (!vtable->klass->has_references) {
668 obj = (MonoArray *)GC_MALLOC_ATOMIC (size);
669 if (G_UNLIKELY (!obj))
672 obj->obj.vtable = vtable;
673 obj->obj.synchronisation = NULL;
675 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
676 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
677 obj = (MonoArray *)GC_GCJ_MALLOC (size, vtable);
678 if (G_UNLIKELY (!obj))
681 obj = (MonoArray *)GC_MALLOC (size);
682 if (G_UNLIKELY (!obj))
685 obj->obj.vtable = vtable;
688 obj->max_length = max_length;
690 if (G_UNLIKELY (alloc_events))
691 mono_profiler_allocation (&obj->obj);
697 mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uintptr_t bounds_size)
701 if (!vtable->klass->has_references) {
702 obj = (MonoArray *)GC_MALLOC_ATOMIC (size);
703 if (G_UNLIKELY (!obj))
706 obj->obj.vtable = vtable;
707 obj->obj.synchronisation = NULL;
709 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
710 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
711 obj = (MonoArray *)GC_GCJ_MALLOC (size, vtable);
712 if (G_UNLIKELY (!obj))
715 obj = (MonoArray *)GC_MALLOC (size);
716 if (G_UNLIKELY (!obj))
719 obj->obj.vtable = vtable;
722 obj->max_length = max_length;
725 obj->bounds = (MonoArrayBounds *) ((char *) obj + size - bounds_size);
727 if (G_UNLIKELY (alloc_events))
728 mono_profiler_allocation (&obj->obj);
734 mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len)
736 MonoString *obj = (MonoString *)GC_MALLOC_ATOMIC (size);
737 if (G_UNLIKELY (!obj))
740 obj->object.vtable = vtable;
741 obj->object.synchronisation = NULL;
743 obj->chars [len] = 0;
745 if (G_UNLIKELY (alloc_events))
746 mono_profiler_allocation (&obj->object);
752 mono_gc_alloc_mature (MonoVTable *vtable, size_t size)
754 return mono_gc_alloc_obj (vtable, size);
758 mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size)
760 return mono_gc_alloc_obj (vtable, size);
764 mono_gc_invoke_finalizers (void)
766 /* There is a bug in GC_invoke_finalizer () in versions <= 6.2alpha4:
767 * the 'mem_freed' variable is not initialized when there are no
768 * objects to finalize, which leads to strange behavior later on.
769 * The check is necessary to work around that bug.
771 if (GC_should_invoke_finalizers ())
772 return GC_invoke_finalizers ();
777 mono_gc_pending_finalizers (void)
779 return GC_should_invoke_finalizers ();
783 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
785 *(void**)field_ptr = value;
789 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
791 *(void**)slot_ptr = value;
795 mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
797 mono_gc_memmove_aligned (dest_ptr, src_ptr, count * sizeof (gpointer));
801 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
803 *(void**)ptr = value;
807 mono_gc_wbarrier_generic_store_atomic (gpointer ptr, MonoObject *value)
809 InterlockedWritePointer ((volatile gpointer *)ptr, value);
813 mono_gc_wbarrier_generic_nostore (gpointer ptr)
818 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
820 mono_gc_memmove_atomic (dest, src, count * mono_class_value_size (klass, NULL));
824 mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
826 /* do not copy the sync state */
827 mono_gc_memmove_aligned ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
828 mono_object_class (obj)->instance_size - sizeof (MonoObject));
832 mono_gc_clear_domain (MonoDomain *domain)
837 mono_gc_suspend_finalizers (void)
842 mono_gc_get_suspend_signal (void)
844 return GC_get_suspend_signal ();
848 mono_gc_get_restart_signal (void)
850 return GC_get_thr_restart_signal ();
853 #if defined(USE_COMPILER_TLS) && defined(__linux__) && (defined(__i386__) || defined(__x86_64__))
854 extern __thread MONO_TLS_FAST void* GC_thread_tls;
855 #include "metadata-internals.h"
861 while (!(v & (1 << i)))
868 ATYPE_FREEPTR_FOR_BOX,
876 create_allocator (int atype, int tls_key, gboolean slowpath)
878 int index_var, bytes_var, my_fl_var, my_entry_var;
879 guint32 no_freelist_branch, not_small_enough_branch = 0;
880 guint32 size_overflow_branch = 0;
881 MonoMethodBuilder *mb;
883 MonoMethodSignature *csig;
884 const char *name = NULL;
887 if (atype == ATYPE_FREEPTR) {
888 name = slowpath ? "SlowAllocPtrfree" : "AllocPtrfree";
889 } else if (atype == ATYPE_FREEPTR_FOR_BOX) {
890 name = slowpath ? "SlowAllocPtrfreeBox" : "AllocPtrfreeBox";
891 } else if (atype == ATYPE_NORMAL) {
892 name = slowpath ? "SlowAlloc" : "Alloc";
893 } else if (atype == ATYPE_GCJ) {
894 name = slowpath ? "SlowAllocGcj" : "AllocGcj";
895 } else if (atype == ATYPE_STRING) {
896 name = slowpath ? "SlowAllocString" : "AllocString";
898 g_assert_not_reached ();
901 csig = mono_metadata_signature_alloc (mono_defaults.corlib, 2);
903 if (atype == ATYPE_STRING) {
904 csig->ret = &mono_defaults.string_class->byval_arg;
905 csig->params [0] = &mono_defaults.int_class->byval_arg;
906 csig->params [1] = &mono_defaults.int32_class->byval_arg;
908 csig->ret = &mono_defaults.object_class->byval_arg;
909 csig->params [0] = &mono_defaults.int_class->byval_arg;
910 csig->params [1] = &mono_defaults.int32_class->byval_arg;
913 mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_ALLOC);
916 goto always_slowpath;
918 bytes_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
919 if (atype == ATYPE_STRING) {
920 /* a string alloator method takes the args: (vtable, len) */
921 /* bytes = (offsetof (MonoString, chars) + ((len + 1) * 2)); */
922 mono_mb_emit_ldarg (mb, 1);
923 mono_mb_emit_icon (mb, 1);
924 mono_mb_emit_byte (mb, MONO_CEE_ADD);
925 mono_mb_emit_icon (mb, 1);
926 mono_mb_emit_byte (mb, MONO_CEE_SHL);
927 // sizeof (MonoString) might include padding
928 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, chars));
929 mono_mb_emit_byte (mb, MONO_CEE_ADD);
930 mono_mb_emit_stloc (mb, bytes_var);
932 mono_mb_emit_ldarg (mb, 1);
933 mono_mb_emit_stloc (mb, bytes_var);
936 /* this is needed for strings/arrays only as the other big types are never allocated with this method */
937 if (atype == ATYPE_STRING) {
939 /* if (!SMALL_ENOUGH (bytes)) jump slow_path;*/
940 mono_mb_emit_ldloc (mb, bytes_var);
941 mono_mb_emit_icon (mb, (NFREELISTS-1) * GRANULARITY);
942 not_small_enough_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_UN_S);
943 /* check for overflow */
944 mono_mb_emit_ldloc (mb, bytes_var);
945 mono_mb_emit_icon (mb, sizeof (MonoString));
946 size_overflow_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLE_UN_S);
949 /* int index = INDEX_FROM_BYTES(bytes); */
950 index_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
952 mono_mb_emit_ldloc (mb, bytes_var);
953 mono_mb_emit_icon (mb, GRANULARITY - 1);
954 mono_mb_emit_byte (mb, MONO_CEE_ADD);
955 mono_mb_emit_icon (mb, shift_amount (GRANULARITY));
956 mono_mb_emit_byte (mb, MONO_CEE_SHR_UN);
957 mono_mb_emit_icon (mb, shift_amount (sizeof (gpointer)));
958 mono_mb_emit_byte (mb, MONO_CEE_SHL);
959 /* index var is already adjusted into bytes */
960 mono_mb_emit_stloc (mb, index_var);
962 my_fl_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
963 my_entry_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
964 /* my_fl = ((GC_thread)tsd) -> ptrfree_freelists + index; */
965 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
966 mono_mb_emit_byte (mb, 0x0D); /* CEE_MONO_TLS */
967 mono_mb_emit_i4 (mb, tls_key);
968 if (atype == ATYPE_FREEPTR || atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING)
969 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
970 + G_STRUCT_OFFSET (struct thread_local_freelists,
972 else if (atype == ATYPE_NORMAL)
973 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
974 + G_STRUCT_OFFSET (struct thread_local_freelists,
976 else if (atype == ATYPE_GCJ)
977 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
978 + G_STRUCT_OFFSET (struct thread_local_freelists,
981 g_assert_not_reached ();
982 mono_mb_emit_byte (mb, MONO_CEE_ADD);
983 mono_mb_emit_ldloc (mb, index_var);
984 mono_mb_emit_byte (mb, MONO_CEE_ADD);
985 mono_mb_emit_stloc (mb, my_fl_var);
987 /* my_entry = *my_fl; */
988 mono_mb_emit_ldloc (mb, my_fl_var);
989 mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
990 mono_mb_emit_stloc (mb, my_entry_var);
992 /* if (EXPECT((word)my_entry >= HBLKSIZE, 1)) { */
993 mono_mb_emit_ldloc (mb, my_entry_var);
994 mono_mb_emit_icon (mb, HBLKSIZE);
995 no_freelist_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
997 /* ptr_t next = obj_link(my_entry); *my_fl = next; */
998 mono_mb_emit_ldloc (mb, my_fl_var);
999 mono_mb_emit_ldloc (mb, my_entry_var);
1000 mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
1001 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1003 /* set the vtable and clear the words in the object */
1004 mono_mb_emit_ldloc (mb, my_entry_var);
1005 mono_mb_emit_ldarg (mb, 0);
1006 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1008 if (atype == ATYPE_FREEPTR) {
1009 int start_var, end_var, start_loop;
1010 /* end = my_entry + bytes; start = my_entry + sizeof (gpointer);
1012 start_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1013 end_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1014 mono_mb_emit_ldloc (mb, my_entry_var);
1015 mono_mb_emit_ldloc (mb, bytes_var);
1016 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1017 mono_mb_emit_stloc (mb, end_var);
1018 mono_mb_emit_ldloc (mb, my_entry_var);
1019 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
1020 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1021 mono_mb_emit_stloc (mb, start_var);
1025 * } while (start < end);
1027 start_loop = mono_mb_get_label (mb);
1028 mono_mb_emit_ldloc (mb, start_var);
1029 mono_mb_emit_icon (mb, 0);
1030 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1031 mono_mb_emit_ldloc (mb, start_var);
1032 mono_mb_emit_icon (mb, sizeof (gpointer));
1033 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1034 mono_mb_emit_stloc (mb, start_var);
1036 mono_mb_emit_ldloc (mb, start_var);
1037 mono_mb_emit_ldloc (mb, end_var);
1038 mono_mb_emit_byte (mb, MONO_CEE_BLT_UN_S);
1039 mono_mb_emit_byte (mb, start_loop - (mono_mb_get_label (mb) + 1));
1040 } else if (atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING) {
1041 /* need to clear just the sync pointer */
1042 mono_mb_emit_ldloc (mb, my_entry_var);
1043 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
1044 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1045 mono_mb_emit_icon (mb, 0);
1046 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1049 if (atype == ATYPE_STRING) {
1050 /* need to set length and clear the last char */
1051 /* s->length = len; */
1052 mono_mb_emit_ldloc (mb, my_entry_var);
1053 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, length));
1054 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1055 mono_mb_emit_ldarg (mb, 1);
1056 mono_mb_emit_byte (mb, MONO_CEE_STIND_I4);
1057 /* s->chars [len] = 0; */
1058 mono_mb_emit_ldloc (mb, my_entry_var);
1059 mono_mb_emit_ldloc (mb, bytes_var);
1060 mono_mb_emit_icon (mb, 2);
1061 mono_mb_emit_byte (mb, MONO_CEE_SUB);
1062 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1063 mono_mb_emit_icon (mb, 0);
1064 mono_mb_emit_byte (mb, MONO_CEE_STIND_I2);
1067 /* return my_entry; */
1068 mono_mb_emit_ldloc (mb, my_entry_var);
1069 mono_mb_emit_byte (mb, MONO_CEE_RET);
1071 mono_mb_patch_short_branch (mb, no_freelist_branch);
1072 if (not_small_enough_branch > 0)
1073 mono_mb_patch_short_branch (mb, not_small_enough_branch);
1074 if (size_overflow_branch > 0)
1075 mono_mb_patch_short_branch (mb, size_overflow_branch);
1077 /* the slow path: we just call back into the runtime */
1079 if (atype == ATYPE_STRING) {
1080 mono_mb_emit_ldarg (mb, 1);
1081 mono_mb_emit_icall (mb, ves_icall_string_alloc);
1083 mono_mb_emit_ldarg (mb, 0);
1084 mono_mb_emit_icall (mb, ves_icall_object_new_specific);
1087 mono_mb_emit_byte (mb, MONO_CEE_RET);
1089 info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE);
1090 info->d.alloc.gc_name = "boehm";
1091 info->d.alloc.alloc_type = atype;
1092 mb->init_locals = FALSE;
1094 res = mono_mb_create (mb, csig, 8, info);
1100 static MonoMethod* alloc_method_cache [ATYPE_NUM];
1101 static MonoMethod* slowpath_alloc_method_cache [ATYPE_NUM];
1103 static G_GNUC_UNUSED gboolean
1104 mono_gc_is_critical_method (MonoMethod *method)
1108 for (i = 0; i < ATYPE_NUM; ++i)
1109 if (method == alloc_method_cache [i] || method == slowpath_alloc_method_cache [i])
1116 * If possible, generate a managed method that can quickly allocate objects in class
1117 * @klass. The method will typically have an thread-local inline allocation sequence.
1118 * The signature of the called method is:
1119 * object allocate (MonoVTable *vtable)
1120 * Some of the logic here is similar to mono_class_get_allocation_ftn () i object.c,
1122 * The thread local alloc logic is taken from libgc/pthread_support.c.
1126 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size)
1130 MONO_THREAD_VAR_OFFSET (GC_thread_tls, offset);
1132 /*g_print ("thread tls: %d\n", offset);*/
1135 if (!SMALL_ENOUGH (klass->instance_size))
1137 if (mono_class_has_finalizer (klass) || mono_class_is_marshalbyref (klass))
1139 if (mono_profiler_get_events () & (MONO_PROFILE_ALLOCATIONS | MONO_PROFILE_STATISTICAL))
1143 if (mono_class_is_open_constructed_type (&klass->byval_arg))
1145 if (klass->byval_arg.type == MONO_TYPE_STRING) {
1146 atype = ATYPE_STRING;
1147 } else if (!known_instance_size) {
1149 } else if (!klass->has_references) {
1151 atype = ATYPE_FREEPTR_FOR_BOX;
1153 atype = ATYPE_FREEPTR;
1157 * disabled because we currently do a runtime choice anyway, to
1158 * deal with multiple appdomains.
1159 if (vtable->gc_descr != GC_NO_DESCRIPTOR)
1162 atype = ATYPE_NORMAL;
1165 return mono_gc_get_managed_allocator_by_type (atype, MANAGED_ALLOCATOR_REGULAR);
1169 mono_gc_get_managed_array_allocator (MonoClass *klass)
1175 * mono_gc_get_managed_allocator_by_type:
1177 * Return a managed allocator method corresponding to allocator type ATYPE.
1180 mono_gc_get_managed_allocator_by_type (int atype, ManagedAllocatorVariant variant)
1184 gboolean slowpath = variant != MANAGED_ALLOCATOR_REGULAR;
1185 MonoMethod **cache = slowpath ? slowpath_alloc_method_cache : alloc_method_cache;
1186 MONO_THREAD_VAR_OFFSET (GC_thread_tls, offset);
1188 mono_tls_key_set_offset (TLS_KEY_BOEHM_GC_THREAD, offset);
1190 res = cache [atype];
1194 res = create_allocator (atype, TLS_KEY_BOEHM_GC_THREAD, slowpath);
1195 mono_os_mutex_lock (&mono_gc_lock);
1196 if (cache [atype]) {
1197 mono_free_method (res);
1198 res = cache [atype];
1200 mono_memory_barrier ();
1201 cache [atype] = res;
1203 mono_os_mutex_unlock (&mono_gc_lock);
1208 mono_gc_get_managed_allocator_types (void)
1214 mono_gc_get_write_barrier (void)
1216 g_assert_not_reached ();
1222 static G_GNUC_UNUSED gboolean
1223 mono_gc_is_critical_method (MonoMethod *method)
1229 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size)
1235 mono_gc_get_managed_array_allocator (MonoClass *klass)
1241 mono_gc_get_managed_allocator_by_type (int atype, ManagedAllocatorVariant variant)
1247 mono_gc_get_managed_allocator_types (void)
1253 mono_gc_get_write_barrier (void)
1255 g_assert_not_reached ();
1262 mono_gc_get_specific_write_barrier (gboolean is_concurrent)
1264 g_assert_not_reached ();
1269 mono_gc_get_aligned_size_for_allocator (int size)
1275 mono_gc_get_gc_name (void)
1281 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
1283 return GC_call_with_alloc_lock (func, data);
1287 mono_gc_get_description (void)
1289 return g_strdup (DEFAULT_GC_NAME);
1293 mono_gc_set_desktop_mode (void)
1299 mono_gc_is_moving (void)
1305 mono_gc_is_disabled (void)
1307 if (GC_dont_gc || g_getenv ("GC_DONT_GC"))
1314 mono_gc_wbarrier_value_copy_bitmap (gpointer _dest, gpointer _src, int size, unsigned bitmap)
1316 g_assert_not_reached ();
1321 mono_gc_get_card_table (int *shift_bits, gpointer *card_mask)
1323 g_assert_not_reached ();
1328 mono_gc_card_table_nursery_check (void)
1330 g_assert_not_reached ();
1335 mono_gc_get_nursery (int *shift_bits, size_t *size)
1341 mono_gc_set_current_thread_appdomain (MonoDomain *domain)
1346 mono_gc_precise_stack_mark_enabled (void)
1352 mono_gc_get_logfile (void)
1358 mono_gc_conservatively_scan_area (void *start, void *end)
1360 g_assert_not_reached ();
1364 mono_gc_scan_object (void *obj, void *gc_data)
1366 g_assert_not_reached ();
1371 mono_gc_get_bitmap_for_descr (void *descr, int *numbits)
1373 g_assert_not_reached ();
1378 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
1383 mono_gc_set_stack_end (void *stack_end)
1387 void mono_gc_set_skip_thread (gboolean value)
1392 mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
1397 /* This assertion is not valid when GC_DEBUG is defined */
1398 g_assert (GC_base (obj) == (char*)obj - offset);
1401 GC_REGISTER_FINALIZER_NO_ORDER ((char*)obj - offset, (GC_finalization_proc)user_data, GUINT_TO_POINTER (offset), NULL, NULL);
1406 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
1408 /* it is being replaced by GC_pthread_create on some
1409 * platforms, see libgc/include/gc_pthread_redirects.h */
1410 return pthread_create (new_thread, attr, start_routine, arg);
1415 BOOL APIENTRY mono_gc_dllmain (HMODULE module_handle, DWORD reason, LPVOID reserved)
1417 return GC_DllMain (module_handle, reason, reserved);
1422 mono_gc_get_vtable_bits (MonoClass *klass)
1424 if (fin_callbacks.is_class_finalization_aware) {
1425 if (fin_callbacks.is_class_finalization_aware (klass))
1426 return BOEHM_GC_BIT_FINALIZER_AWARE;
1432 * mono_gc_register_altstack:
1434 * Register the dimensions of the normal stack and altstack with the collector.
1435 * Currently, STACK/STACK_SIZE is only used when the thread is suspended while it is on an altstack.
1438 mono_gc_register_altstack (gpointer stack, gint32 stack_size, gpointer altstack, gint32 altstack_size)
1440 GC_register_altstack (stack, stack_size, altstack, altstack_size);
1444 mono_gc_get_los_limit (void)
1450 mono_gc_set_string_length (MonoString *str, gint32 new_length)
1452 mono_unichar2 *new_end = str->chars + new_length;
1454 /* zero the discarded string. This null-delimits the string and allows
1455 * the space to be reclaimed by SGen. */
1457 memset (new_end, 0, (str->length - new_length + 1) * sizeof (mono_unichar2));
1458 str->length = new_length;
1462 mono_gc_user_markers_supported (void)
1468 mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
1470 g_assert_not_reached ();
1474 /* Toggleref support */
1477 mono_gc_toggleref_add (MonoObject *object, mono_bool strong_ref)
1479 if (GC_toggleref_add ((GC_PTR)object, (int)strong_ref) != GC_SUCCESS)
1480 g_error ("GC_toggleref_add failed\n");
1484 mono_gc_toggleref_register_callback (MonoToggleRefStatus (*proccess_toggleref) (MonoObject *obj))
1486 GC_set_toggleref_func ((GC_ToggleRefStatus (*) (GC_PTR obj)) proccess_toggleref);
1489 /* Test support code */
1491 static MonoToggleRefStatus
1492 test_toggleref_callback (MonoObject *obj)
1494 static MonoClassField *mono_toggleref_test_field;
1495 MonoToggleRefStatus status = MONO_TOGGLE_REF_DROP;
1497 if (!mono_toggleref_test_field) {
1498 mono_toggleref_test_field = mono_class_get_field_from_name (mono_object_get_class (obj), "__test");
1499 g_assert (mono_toggleref_test_field);
1502 mono_field_get_value (obj, mono_toggleref_test_field, &status);
1503 printf ("toggleref-cb obj %d\n", status);
1508 register_test_toggleref_callback (void)
1510 mono_gc_toggleref_register_callback (test_toggleref_callback);
1514 is_finalization_aware (MonoObject *obj)
1516 MonoVTable *vt = obj->vtable;
1517 return (vt->gc_bits & BOEHM_GC_BIT_FINALIZER_AWARE) == BOEHM_GC_BIT_FINALIZER_AWARE;
1521 fin_notifier (MonoObject *obj)
1523 if (is_finalization_aware (obj))
1524 fin_callbacks.object_queued_for_finalization (obj);
1528 mono_gc_register_finalizer_callbacks (MonoGCFinalizerCallbacks *callbacks)
1530 if (callbacks->version != MONO_GC_FINALIZER_EXTENSION_VERSION)
1531 g_error ("Invalid finalizer callback version. Expected %d but got %d\n", MONO_GC_FINALIZER_EXTENSION_VERSION, callbacks->version);
1533 fin_callbacks = *callbacks;
1535 GC_set_await_finalize_proc ((void (*) (GC_PTR))fin_notifier);
1538 #define BITMAP_SIZE (sizeof (*((HandleData *)NULL)->bitmap) * CHAR_BIT)
1540 static inline gboolean
1541 slot_occupied (HandleData *handles, guint slot) {
1542 return handles->bitmap [slot / BITMAP_SIZE] & (1 << (slot % BITMAP_SIZE));
1546 vacate_slot (HandleData *handles, guint slot) {
1547 handles->bitmap [slot / BITMAP_SIZE] &= ~(1 << (slot % BITMAP_SIZE));
1551 occupy_slot (HandleData *handles, guint slot) {
1552 handles->bitmap [slot / BITMAP_SIZE] |= 1 << (slot % BITMAP_SIZE);
1556 find_first_unset (guint32 bitmap)
1559 for (i = 0; i < 32; ++i) {
1560 if (!(bitmap & (1 << i)))
1567 handle_data_alloc_entries (HandleData *handles)
1570 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1571 handles->entries = (void **)g_malloc0 (sizeof (*handles->entries) * handles->size);
1572 handles->domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * handles->size);
1574 handles->entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * handles->size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
1576 handles->bitmap = (guint32 *)g_malloc0 (handles->size / CHAR_BIT);
1580 handle_data_next_unset (HandleData *handles)
1583 for (slot = handles->slot_hint; slot < handles->size / BITMAP_SIZE; ++slot) {
1584 if (handles->bitmap [slot] == 0xffffffff)
1586 handles->slot_hint = slot;
1587 return find_first_unset (handles->bitmap [slot]);
1593 handle_data_first_unset (HandleData *handles)
1596 for (slot = 0; slot < handles->slot_hint; ++slot) {
1597 if (handles->bitmap [slot] == 0xffffffff)
1599 handles->slot_hint = slot;
1600 return find_first_unset (handles->bitmap [slot]);
1605 /* Returns the index of the current slot in the bitmap. */
1607 handle_data_grow (HandleData *handles, gboolean track)
1609 guint32 *new_bitmap;
1610 guint32 new_size = handles->size * 2; /* always double: we memset to 0 based on this below */
1612 /* resize and copy the bitmap */
1613 new_bitmap = (guint32 *)g_malloc0 (new_size / CHAR_BIT);
1614 memcpy (new_bitmap, handles->bitmap, handles->size / CHAR_BIT);
1615 g_free (handles->bitmap);
1616 handles->bitmap = new_bitmap;
1618 /* resize and copy the entries */
1619 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1621 guint16 *domain_ids;
1623 domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * new_size);
1624 entries = (void **)g_malloc0 (sizeof (*handles->entries) * new_size);
1625 memcpy (domain_ids, handles->domain_ids, sizeof (*handles->domain_ids) * handles->size);
1626 for (i = 0; i < handles->size; ++i) {
1627 MonoObject *obj = mono_gc_weak_link_get (&(handles->entries [i]));
1629 mono_gc_weak_link_add (&(entries [i]), obj, track);
1630 mono_gc_weak_link_remove (&(handles->entries [i]), track);
1632 g_assert (!handles->entries [i]);
1635 g_free (handles->entries);
1636 g_free (handles->domain_ids);
1637 handles->entries = entries;
1638 handles->domain_ids = domain_ids;
1641 entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * new_size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
1642 mono_gc_memmove_aligned (entries, handles->entries, sizeof (*handles->entries) * handles->size);
1643 mono_gc_free_fixed (handles->entries);
1644 handles->entries = entries;
1646 handles->slot_hint = handles->size / BITMAP_SIZE;
1647 handles->size = new_size;
1651 alloc_handle (HandleData *handles, MonoObject *obj, gboolean track)
1655 lock_handles (handles);
1657 handle_data_alloc_entries (handles);
1658 i = handle_data_next_unset (handles);
1659 if (i == -1 && handles->slot_hint != 0)
1660 i = handle_data_first_unset (handles);
1662 handle_data_grow (handles, track);
1665 slot = handles->slot_hint * BITMAP_SIZE + i;
1666 occupy_slot (handles, slot);
1667 handles->entries [slot] = NULL;
1668 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1669 /*FIXME, what to use when obj == null?*/
1670 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
1672 mono_gc_weak_link_add (&(handles->entries [slot]), obj, track);
1674 handles->entries [slot] = obj;
1677 #ifndef DISABLE_PERFCOUNTERS
1678 mono_perfcounters->gc_num_handles++;
1680 unlock_handles (handles);
1681 res = MONO_GC_HANDLE (slot, handles->type);
1682 mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_CREATED, handles->type, res, obj);
1687 * mono_gchandle_new:
1688 * @obj: managed object to get a handle for
1689 * @pinned: whether the object should be pinned
1691 * This returns a handle that wraps the object, this is used to keep a
1692 * reference to a managed object from the unmanaged world and preventing the
1693 * object from being disposed.
1695 * If @pinned is false the address of the object can not be obtained, if it is
1696 * true the address of the object can be obtained. This will also pin the
1697 * object so it will not be possible by a moving garbage collector to move the
1700 * Returns: a handle that can be used to access the object from
1704 mono_gchandle_new (MonoObject *obj, gboolean pinned)
1706 return alloc_handle (&gc_handles [pinned? HANDLE_PINNED: HANDLE_NORMAL], obj, FALSE);
1710 * mono_gchandle_new_weakref:
1711 * @obj: managed object to get a handle for
1712 * @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.
1714 * This returns a weak handle that wraps the object, this is used to
1715 * keep a reference to a managed object from the unmanaged world.
1716 * Unlike the mono_gchandle_new the object can be reclaimed by the
1717 * garbage collector. In this case the value of the GCHandle will be
1720 * If @track_resurrection is TRUE the object will be tracked through
1721 * finalization and if the object is resurrected during the execution
1722 * of the finalizer, then the returned weakref will continue to hold
1723 * a reference to the object. If @track_resurrection is FALSE, then
1724 * the weak reference's target will become NULL as soon as the object
1725 * is passed on to the finalizer.
1727 * Returns: a handle that can be used to access the object from
1731 mono_gchandle_new_weakref (MonoObject *obj, gboolean track_resurrection)
1733 return alloc_handle (&gc_handles [track_resurrection? HANDLE_WEAK_TRACK: HANDLE_WEAK], obj, track_resurrection);
1737 * mono_gchandle_get_target:
1738 * @gchandle: a GCHandle's handle.
1740 * The handle was previously created by calling `mono_gchandle_new` or
1741 * `mono_gchandle_new_weakref`.
1743 * Returns: A pointer to the `MonoObject*` represented by the handle or
1744 * NULL for a collected object if using a weakref handle.
1747 mono_gchandle_get_target (guint32 gchandle)
1749 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1750 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1751 HandleData *handles = &gc_handles [type];
1752 MonoObject *obj = NULL;
1753 if (type >= HANDLE_TYPE_MAX)
1756 lock_handles (handles);
1757 if (slot < handles->size && slot_occupied (handles, slot)) {
1758 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1759 obj = mono_gc_weak_link_get (&handles->entries [slot]);
1761 obj = (MonoObject *)handles->entries [slot];
1764 /* print a warning? */
1766 unlock_handles (handles);
1767 /*g_print ("get target of entry %d of type %d: %p\n", slot, handles->type, obj);*/
1772 mono_gchandle_set_target (guint32 gchandle, MonoObject *obj)
1774 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1775 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1776 HandleData *handles = &gc_handles [type];
1777 MonoObject *old_obj = NULL;
1779 g_assert (type < HANDLE_TYPE_MAX);
1780 lock_handles (handles);
1781 if (slot < handles->size && slot_occupied (handles, slot)) {
1782 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1783 old_obj = (MonoObject *)handles->entries [slot];
1784 if (handles->entries [slot])
1785 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1787 mono_gc_weak_link_add (&handles->entries [slot], obj, handles->type == HANDLE_WEAK_TRACK);
1788 /*FIXME, what to use when obj == null?*/
1789 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
1791 handles->entries [slot] = obj;
1794 /* print a warning? */
1796 /*g_print ("changed entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
1797 unlock_handles (handles);
1801 mono_gc_is_null (void)
1807 * mono_gchandle_is_in_domain:
1808 * @gchandle: a GCHandle's handle.
1809 * @domain: An application domain.
1811 * Use this function to determine if the @gchandle points to an
1812 * object allocated in the specified @domain.
1814 * Returns: TRUE if the object wrapped by the @gchandle belongs to the specific @domain.
1817 mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain)
1819 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1820 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1821 HandleData *handles = &gc_handles [type];
1822 gboolean result = FALSE;
1824 if (type >= HANDLE_TYPE_MAX)
1827 lock_handles (handles);
1828 if (slot < handles->size && slot_occupied (handles, slot)) {
1829 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1830 result = domain->domain_id == handles->domain_ids [slot];
1833 obj = (MonoObject *)handles->entries [slot];
1837 result = domain == mono_object_domain (obj);
1840 /* print a warning? */
1842 unlock_handles (handles);
1847 * mono_gchandle_free:
1848 * @gchandle: a GCHandle's handle.
1850 * Frees the @gchandle handle. If there are no outstanding
1851 * references, the garbage collector can reclaim the memory of the
1855 mono_gchandle_free (guint32 gchandle)
1857 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1858 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1859 HandleData *handles = &gc_handles [type];
1860 if (type >= HANDLE_TYPE_MAX)
1863 lock_handles (handles);
1864 if (slot < handles->size && slot_occupied (handles, slot)) {
1865 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1866 if (handles->entries [slot])
1867 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1869 handles->entries [slot] = NULL;
1871 vacate_slot (handles, slot);
1873 /* print a warning? */
1875 #ifndef DISABLE_PERFCOUNTERS
1876 mono_perfcounters->gc_num_handles--;
1878 /*g_print ("freed entry %d of type %d\n", slot, handles->type);*/
1879 unlock_handles (handles);
1880 mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_DESTROYED, handles->type, gchandle, NULL);
1884 * mono_gchandle_free_domain:
1885 * @domain: domain that is unloading
1887 * Function used internally to cleanup any GC handle for objects belonging
1888 * to the specified domain during appdomain unload.
1891 mono_gchandle_free_domain (MonoDomain *domain)
1895 for (type = HANDLE_TYPE_MIN; type < HANDLE_PINNED; ++type) {
1897 HandleData *handles = &gc_handles [type];
1898 lock_handles (handles);
1899 for (slot = 0; slot < handles->size; ++slot) {
1900 if (!slot_occupied (handles, slot))
1902 if (MONO_GC_HANDLE_TYPE_IS_WEAK (type)) {
1903 if (domain->domain_id == handles->domain_ids [slot]) {
1904 vacate_slot (handles, slot);
1905 if (handles->entries [slot])
1906 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1909 if (handles->entries [slot] && mono_object_domain (handles->entries [slot]) == domain) {
1910 vacate_slot (handles, slot);
1911 handles->entries [slot] = NULL;
1915 unlock_handles (handles);
1920 #endif /* no Boehm GC */