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/sgen-toggleref.h>
26 #include <mono/utils/atomic.h>
27 #include <mono/utils/mono-logger-internals.h>
28 #include <mono/utils/mono-memory-model.h>
29 #include <mono/utils/mono-time.h>
30 #include <mono/utils/mono-threads.h>
31 #include <mono/utils/dtrace.h>
32 #include <mono/utils/gc_wrapper.h>
33 #include <mono/utils/mono-os-mutex.h>
34 #include <mono/utils/mono-counters.h>
40 #define THREAD_LOCAL_ALLOC 1
41 #include "private/pthread_support.h"
43 #if defined(PLATFORM_MACOSX) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
44 void *pthread_get_stackaddr_np(pthread_t);
47 #define GC_NO_DESCRIPTOR ((gpointer)(0 | GC_DS_LENGTH))
48 /*Boehm max heap cannot be smaller than 16MB*/
49 #define MIN_BOEHM_MAX_HEAP_SIZE_IN_MB 16
50 #define MIN_BOEHM_MAX_HEAP_SIZE (MIN_BOEHM_MAX_HEAP_SIZE_IN_MB << 20)
52 static gboolean gc_initialized = FALSE;
53 static mono_mutex_t mono_gc_lock;
56 boehm_thread_register (MonoThreadInfo* info, void *baseptr);
58 boehm_thread_unregister (MonoThreadInfo *p);
60 register_test_toggleref_callback (void);
62 #define BOEHM_GC_BIT_FINALIZER_AWARE 1
63 static MonoGCFinalizerCallbacks fin_callbacks;
67 static mono_mutex_t handle_section;
68 #define lock_handles(handles) mono_os_mutex_lock (&handle_section)
69 #define unlock_handles(handles) mono_os_mutex_unlock (&handle_section)
76 guint slot_hint : 24; /* starting slot for search in bitmap */
77 /* 2^16 appdomains should be enough for everyone (though I know I'll regret this in 20 years) */
78 /* we alloc this only for weak refs, since we can get the domain directly in the other cases */
82 #define EMPTY_HANDLE_DATA(type) {NULL, NULL, 0, (type), 0, NULL}
84 /* weak and weak-track arrays will be allocated in malloc memory
86 static HandleData gc_handles [] = {
87 EMPTY_HANDLE_DATA (HANDLE_WEAK),
88 EMPTY_HANDLE_DATA (HANDLE_WEAK_TRACK),
89 EMPTY_HANDLE_DATA (HANDLE_NORMAL),
90 EMPTY_HANDLE_DATA (HANDLE_PINNED)
94 mono_gc_warning (char *msg, GC_word arg)
96 mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_GC, msg, (unsigned long)arg);
100 mono_gc_base_init (void)
102 MonoThreadInfoCallbacks cb;
109 mono_counters_init ();
112 * Handle the case when we are called from a thread different from the main thread,
114 * FIXME: Move this to libgc where it belongs.
116 * we used to do this only when running on valgrind,
117 * but it happens also in other setups.
119 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) && !defined(__native_client__)
124 pthread_getattr_np (pthread_self (), &attr);
125 pthread_attr_getstack (&attr, &sstart, &size);
126 pthread_attr_destroy (&attr);
127 /*g_print ("stackbottom pth is: %p\n", (char*)sstart + size);*/
130 * The calculation above doesn't seem to work on ia64, also we need to set
131 * GC_register_stackbottom as well, but don't know how.
134 /* apparently with some linuxthreads implementations sstart can be NULL,
135 * fallback to the more imprecise method (bug# 78096).
138 GC_stackbottom = (char*)sstart + size;
141 gsize stack_bottom = (gsize)&dummy;
142 stack_bottom += 4095;
143 stack_bottom &= ~4095;
144 GC_stackbottom = (char*)stack_bottom;
148 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
149 GC_stackbottom = (char*)pthread_get_stackaddr_np (pthread_self ());
150 #elif defined(__OpenBSD__)
151 # include <pthread_np.h>
156 rslt = pthread_stackseg_np(pthread_self(), &ss);
157 g_assert (rslt == 0);
159 GC_stackbottom = (char*)ss.ss_sp;
161 #elif defined(__native_client__)
162 /* Do nothing, GC_stackbottom is set correctly in libgc */
166 gsize stack_bottom = (gsize)&dummy;
167 stack_bottom += 4095;
168 stack_bottom &= ~4095;
169 /*g_print ("stackbottom is: %p\n", (char*)stack_bottom);*/
170 GC_stackbottom = (char*)stack_bottom;
174 #if !defined(PLATFORM_ANDROID)
175 /* If GC_no_dls is set to true, GC_find_limit is not called. This causes a seg fault on Android. */
179 if ((env = g_getenv ("MONO_GC_DEBUG"))) {
180 char **opts = g_strsplit (env, ",", -1);
181 for (char **ptr = opts; ptr && *ptr; ptr ++) {
183 if (!strcmp (opt, "do-not-finalize")) {
184 mono_do_not_finalize = 1;
185 } else if (!strcmp (opt, "log-finalizers")) {
194 GC_set_warn_proc (mono_gc_warning);
195 GC_finalize_on_demand = 1;
196 GC_finalizer_notifier = mono_gc_finalize_notify;
198 GC_init_gcj_malloc (5, NULL);
199 GC_allow_register_threads ();
201 if ((env = g_getenv ("MONO_GC_PARAMS"))) {
202 char **ptr, **opts = g_strsplit (env, ",", -1);
203 for (ptr = opts; *ptr; ++ptr) {
205 if (g_str_has_prefix (opt, "max-heap-size=")) {
208 opt = strchr (opt, '=') + 1;
209 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &max_heap)) {
210 if (max_heap < MIN_BOEHM_MAX_HEAP_SIZE) {
211 fprintf (stderr, "max-heap-size must be at least %dMb.\n", MIN_BOEHM_MAX_HEAP_SIZE_IN_MB);
214 GC_set_max_heap_size (max_heap);
216 fprintf (stderr, "max-heap-size must be an integer.\n");
220 } else if (g_str_has_prefix (opt, "toggleref-test")) {
221 register_test_toggleref_callback ();
224 /* Could be a parameter for sgen */
226 fprintf (stderr, "MONO_GC_PARAMS must be a comma-delimited list of one or more of the following:\n");
227 fprintf (stderr, " max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
235 memset (&cb, 0, sizeof (cb));
236 cb.thread_register = boehm_thread_register;
237 cb.thread_unregister = boehm_thread_unregister;
238 cb.mono_method_is_critical = (gboolean (*)(void *))mono_runtime_is_critical_method;
240 mono_threads_init (&cb, sizeof (MonoThreadInfo));
241 mono_os_mutex_init (&mono_gc_lock);
242 mono_os_mutex_init_recursive (&handle_section);
244 mono_thread_info_attach (&dummy);
246 mono_gc_enable_events ();
248 MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_NORMAL].entries, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
249 MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_PINNED].entries, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
251 gc_initialized = TRUE;
255 mono_gc_base_cleanup (void)
257 GC_finalizer_notifier = NULL;
262 * @generation: GC generation identifier
264 * Perform a garbage collection for the given generation, higher numbers
265 * mean usually older objects. Collecting a high-numbered generation
266 * implies collecting also the lower-numbered generations.
267 * The maximum value for @generation can be retrieved with a call to
268 * mono_gc_max_generation(), so this function is usually called as:
270 * mono_gc_collect (mono_gc_max_generation ());
273 mono_gc_collect (int generation)
275 #ifndef DISABLE_PERFCOUNTERS
276 mono_perfcounters->gc_induced++;
282 * mono_gc_max_generation:
284 * Get the maximum generation number used by the current garbage
285 * collector. The value will be 0 for the Boehm collector, 1 or more
286 * for the generational collectors.
288 * Returns: the maximum generation number.
291 mono_gc_max_generation (void)
297 * mono_gc_get_generation:
298 * @object: a managed object
300 * Get the garbage collector's generation that @object belongs to.
301 * Use this has a hint only.
303 * Returns: a garbage collector generation number
306 mono_gc_get_generation (MonoObject *object)
312 * mono_gc_collection_count:
313 * @generation: a GC generation number
315 * Get how many times a garbage collection has been performed
316 * for the given @generation number.
318 * Returns: the number of garbage collections
321 mono_gc_collection_count (int generation)
327 * mono_gc_add_memory_pressure:
328 * @value: amount of bytes
330 * Adjust the garbage collector's view of how many bytes of memory
331 * are indirectly referenced by managed objects (for example unmanaged
332 * memory holding image or other binary data).
333 * This is a hint only to the garbage collector algorithm.
334 * Note that negative amounts of @value will decrease the memory
338 mono_gc_add_memory_pressure (gint64 value)
343 * mono_gc_get_used_size:
345 * Get the approximate amount of memory used by managed objects.
347 * Returns: the amount of memory used in bytes
350 mono_gc_get_used_size (void)
352 return GC_get_heap_size () - GC_get_free_bytes ();
356 * mono_gc_get_heap_size:
358 * Get the amount of memory used by the garbage collector.
360 * Returns: the size of the heap in bytes
363 mono_gc_get_heap_size (void)
365 return GC_get_heap_size ();
369 mono_gc_is_gc_thread (void)
371 return GC_thread_is_registered ();
375 mono_gc_register_thread (void *baseptr)
377 return mono_thread_info_attach (baseptr) != NULL;
381 boehm_thread_register (MonoThreadInfo* info, void *baseptr)
383 struct GC_stack_base sb;
386 /* TODO: use GC_get_stack_base instead of baseptr. */
387 sb.mem_base = baseptr;
388 res = GC_register_my_thread (&sb);
389 if (res == GC_UNIMPLEMENTED)
390 return NULL; /* Cannot happen with GC v7+. */
395 boehm_thread_unregister (MonoThreadInfo *p)
397 MonoNativeThreadId tid;
399 tid = mono_thread_info_get_tid (p);
401 if (p->runtime_thread)
402 mono_threads_add_joinable_thread ((gpointer)tid);
406 mono_object_is_alive (MonoObject* o)
408 return GC_is_marked ((ptr_t)o);
412 mono_gc_walk_heap (int flags, MonoGCReferences callback, void *data)
417 static gint64 gc_start_time;
420 on_gc_notification (GC_EventType event)
422 MonoGCEvent e = (MonoGCEvent)event;
425 case MONO_GC_EVENT_PRE_STOP_WORLD:
426 MONO_GC_WORLD_STOP_BEGIN ();
427 mono_thread_info_suspend_lock ();
430 case MONO_GC_EVENT_POST_STOP_WORLD:
431 MONO_GC_WORLD_STOP_END ();
434 case MONO_GC_EVENT_PRE_START_WORLD:
435 MONO_GC_WORLD_RESTART_BEGIN (1);
438 case MONO_GC_EVENT_POST_START_WORLD:
439 MONO_GC_WORLD_RESTART_END (1);
440 mono_thread_info_suspend_unlock ();
443 case MONO_GC_EVENT_START:
445 #ifndef DISABLE_PERFCOUNTERS
446 if (mono_perfcounters)
447 mono_perfcounters->gc_collections0++;
449 gc_stats.major_gc_count ++;
450 gc_start_time = mono_100ns_ticks ();
453 case MONO_GC_EVENT_END:
455 #if defined(ENABLE_DTRACE) && defined(__sun__)
456 /* This works around a dtrace -G problem on Solaris.
457 Limit its actual use to when the probe is enabled. */
458 if (MONO_GC_END_ENABLED ())
462 #ifndef DISABLE_PERFCOUNTERS
463 if (mono_perfcounters) {
464 guint64 heap_size = GC_get_heap_size ();
465 guint64 used_size = heap_size - GC_get_free_bytes ();
466 mono_perfcounters->gc_total_bytes = used_size;
467 mono_perfcounters->gc_committed_bytes = heap_size;
468 mono_perfcounters->gc_reserved_bytes = heap_size;
469 mono_perfcounters->gc_gen0size = heap_size;
472 gc_stats.major_gc_time += mono_100ns_ticks () - gc_start_time;
473 mono_trace_message (MONO_TRACE_GC, "gc took %d usecs", (mono_100ns_ticks () - gc_start_time) / 10);
479 mono_profiler_gc_event (e, 0);
483 on_gc_heap_resize (size_t new_size)
485 guint64 heap_size = GC_get_heap_size ();
486 #ifndef DISABLE_PERFCOUNTERS
487 if (mono_perfcounters) {
488 mono_perfcounters->gc_committed_bytes = heap_size;
489 mono_perfcounters->gc_reserved_bytes = heap_size;
490 mono_perfcounters->gc_gen0size = heap_size;
493 mono_profiler_gc_heap_resize (new_size);
497 mono_gc_enable_events (void)
499 GC_set_on_collection_event (on_gc_notification);
500 GC_on_heap_resize = on_gc_heap_resize;
503 static gboolean alloc_events = FALSE;
506 mono_gc_enable_alloc_events (void)
512 mono_gc_register_root (char *start, size_t size, void *descr, MonoGCRootSource source, const char *msg)
514 /* for some strange reason, they want one extra byte on the end */
515 GC_add_roots (start, start + size + 1);
521 mono_gc_deregister_root (char* addr)
524 /* FIXME: libgc doesn't define this work win32 for some reason */
525 /* FIXME: No size info */
526 GC_remove_roots (addr, addr + sizeof (gpointer) + 1);
531 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
533 /* libgc requires that we use HIDE_POINTER... */
534 *link_addr = (void*)HIDE_POINTER (obj);
536 GC_REGISTER_LONG_LINK (link_addr, obj);
538 GC_GENERAL_REGISTER_DISAPPEARING_LINK (link_addr, obj);
542 mono_gc_weak_link_remove (void **link_addr, gboolean track)
545 GC_unregister_long_link (link_addr);
547 GC_unregister_disappearing_link (link_addr);
552 reveal_link (gpointer link_addr)
554 void **link_a = (void **)link_addr;
555 return REVEAL_POINTER (*link_a);
559 mono_gc_weak_link_get (void **link_addr)
561 MonoObject *obj = (MonoObject *)GC_call_with_alloc_lock (reveal_link, link_addr);
562 if (obj == (MonoObject *) -1)
568 mono_gc_make_descr_for_string (gsize *bitmap, int numbits)
570 return mono_gc_make_descr_from_bitmap (bitmap, numbits);
574 mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size)
576 return mono_gc_make_descr_from_bitmap (bitmap, numbits);
580 mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size)
582 /* libgc has no usable support for arrays... */
583 return GC_NO_DESCRIPTOR;
587 mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
589 /* It seems there are issues when the bitmap doesn't fit: play it safe */
591 return GC_NO_DESCRIPTOR;
593 return (gpointer)GC_make_descriptor ((GC_bitmap)bitmap, numbits);
597 mono_gc_make_root_descr_all_refs (int numbits)
603 mono_gc_alloc_fixed (size_t size, void *descr, MonoGCRootSource source, const char *msg)
605 /* To help track down typed allocation bugs */
609 if (count == atoi (g_getenv ("COUNT2")))
611 if (count > atoi (g_getenv ("COUNT2")))
612 return GC_MALLOC (size);
616 return GC_MALLOC_EXPLICITLY_TYPED (size, (GC_descr)descr);
618 return GC_MALLOC (size);
622 mono_gc_free_fixed (void* addr)
627 mono_gc_alloc_obj (MonoVTable *vtable, size_t size)
631 if (!vtable->klass->has_references) {
632 obj = (MonoObject *)GC_MALLOC_ATOMIC (size);
633 if (G_UNLIKELY (!obj))
636 obj->vtable = vtable;
637 obj->synchronisation = NULL;
639 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
640 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
641 obj = (MonoObject *)GC_GCJ_MALLOC (size, vtable);
642 if (G_UNLIKELY (!obj))
645 obj = (MonoObject *)GC_MALLOC (size);
646 if (G_UNLIKELY (!obj))
649 obj->vtable = vtable;
652 if (G_UNLIKELY (alloc_events))
653 mono_profiler_allocation (obj);
659 mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length)
663 if (!vtable->klass->has_references) {
664 obj = (MonoArray *)GC_MALLOC_ATOMIC (size);
665 if (G_UNLIKELY (!obj))
668 obj->obj.vtable = vtable;
669 obj->obj.synchronisation = NULL;
671 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
672 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
673 obj = (MonoArray *)GC_GCJ_MALLOC (size, vtable);
674 if (G_UNLIKELY (!obj))
677 obj = (MonoArray *)GC_MALLOC (size);
678 if (G_UNLIKELY (!obj))
681 obj->obj.vtable = vtable;
684 obj->max_length = max_length;
686 if (G_UNLIKELY (alloc_events))
687 mono_profiler_allocation (&obj->obj);
693 mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uintptr_t bounds_size)
697 if (!vtable->klass->has_references) {
698 obj = (MonoArray *)GC_MALLOC_ATOMIC (size);
699 if (G_UNLIKELY (!obj))
702 obj->obj.vtable = vtable;
703 obj->obj.synchronisation = NULL;
705 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
706 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
707 obj = (MonoArray *)GC_GCJ_MALLOC (size, vtable);
708 if (G_UNLIKELY (!obj))
711 obj = (MonoArray *)GC_MALLOC (size);
712 if (G_UNLIKELY (!obj))
715 obj->obj.vtable = vtable;
718 obj->max_length = max_length;
721 obj->bounds = (MonoArrayBounds *) ((char *) obj + size - bounds_size);
723 if (G_UNLIKELY (alloc_events))
724 mono_profiler_allocation (&obj->obj);
730 mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len)
732 MonoString *obj = (MonoString *)GC_MALLOC_ATOMIC (size);
733 if (G_UNLIKELY (!obj))
736 obj->object.vtable = vtable;
737 obj->object.synchronisation = NULL;
739 obj->chars [len] = 0;
741 if (G_UNLIKELY (alloc_events))
742 mono_profiler_allocation (&obj->object);
748 mono_gc_alloc_mature (MonoVTable *vtable, size_t size)
750 return mono_gc_alloc_obj (vtable, size);
754 mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size)
756 return mono_gc_alloc_obj (vtable, size);
760 mono_gc_invoke_finalizers (void)
762 /* There is a bug in GC_invoke_finalizer () in versions <= 6.2alpha4:
763 * the 'mem_freed' variable is not initialized when there are no
764 * objects to finalize, which leads to strange behavior later on.
765 * The check is necessary to work around that bug.
767 if (GC_should_invoke_finalizers ())
768 return GC_invoke_finalizers ();
773 mono_gc_pending_finalizers (void)
775 return GC_should_invoke_finalizers ();
779 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
781 *(void**)field_ptr = value;
785 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
787 *(void**)slot_ptr = value;
791 mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
793 mono_gc_memmove_aligned (dest_ptr, src_ptr, count * sizeof (gpointer));
797 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
799 *(void**)ptr = value;
803 mono_gc_wbarrier_generic_store_atomic (gpointer ptr, MonoObject *value)
805 InterlockedWritePointer ((volatile gpointer *)ptr, value);
809 mono_gc_wbarrier_generic_nostore (gpointer ptr)
814 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
816 mono_gc_memmove_atomic (dest, src, count * mono_class_value_size (klass, NULL));
820 mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
822 /* do not copy the sync state */
823 mono_gc_memmove_aligned ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
824 mono_object_class (obj)->instance_size - sizeof (MonoObject));
828 mono_gc_clear_domain (MonoDomain *domain)
833 mono_gc_suspend_finalizers (void)
838 mono_gc_get_suspend_signal (void)
840 return GC_get_suspend_signal ();
844 mono_gc_get_restart_signal (void)
846 return GC_get_thr_restart_signal ();
849 #if defined(USE_COMPILER_TLS) && defined(__linux__) && (defined(__i386__) || defined(__x86_64__))
850 extern __thread MONO_TLS_FAST void* GC_thread_tls;
851 #include "metadata-internals.h"
857 while (!(v & (1 << i)))
864 ATYPE_FREEPTR_FOR_BOX,
872 create_allocator (int atype, int tls_key, gboolean slowpath)
874 int index_var, bytes_var, my_fl_var, my_entry_var;
875 guint32 no_freelist_branch, not_small_enough_branch = 0;
876 guint32 size_overflow_branch = 0;
877 MonoMethodBuilder *mb;
879 MonoMethodSignature *csig;
880 const char *name = NULL;
883 if (atype == ATYPE_FREEPTR) {
884 name = slowpath ? "SlowAllocPtrfree" : "AllocPtrfree";
885 } else if (atype == ATYPE_FREEPTR_FOR_BOX) {
886 name = slowpath ? "SlowAllocPtrfreeBox" : "AllocPtrfreeBox";
887 } else if (atype == ATYPE_NORMAL) {
888 name = slowpath ? "SlowAlloc" : "Alloc";
889 } else if (atype == ATYPE_GCJ) {
890 name = slowpath ? "SlowAllocGcj" : "AllocGcj";
891 } else if (atype == ATYPE_STRING) {
892 name = slowpath ? "SlowAllocString" : "AllocString";
894 g_assert_not_reached ();
897 csig = mono_metadata_signature_alloc (mono_defaults.corlib, 2);
899 if (atype == ATYPE_STRING) {
900 csig->ret = &mono_defaults.string_class->byval_arg;
901 csig->params [0] = &mono_defaults.int_class->byval_arg;
902 csig->params [1] = &mono_defaults.int32_class->byval_arg;
904 csig->ret = &mono_defaults.object_class->byval_arg;
905 csig->params [0] = &mono_defaults.int_class->byval_arg;
906 csig->params [1] = &mono_defaults.int32_class->byval_arg;
909 mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_ALLOC);
912 goto always_slowpath;
914 bytes_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
915 if (atype == ATYPE_STRING) {
916 /* a string alloator method takes the args: (vtable, len) */
917 /* bytes = (offsetof (MonoString, chars) + ((len + 1) * 2)); */
918 mono_mb_emit_ldarg (mb, 1);
919 mono_mb_emit_icon (mb, 1);
920 mono_mb_emit_byte (mb, MONO_CEE_ADD);
921 mono_mb_emit_icon (mb, 1);
922 mono_mb_emit_byte (mb, MONO_CEE_SHL);
923 // sizeof (MonoString) might include padding
924 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, chars));
925 mono_mb_emit_byte (mb, MONO_CEE_ADD);
926 mono_mb_emit_stloc (mb, bytes_var);
928 mono_mb_emit_ldarg (mb, 1);
929 mono_mb_emit_stloc (mb, bytes_var);
932 /* this is needed for strings/arrays only as the other big types are never allocated with this method */
933 if (atype == ATYPE_STRING) {
935 /* if (!SMALL_ENOUGH (bytes)) jump slow_path;*/
936 mono_mb_emit_ldloc (mb, bytes_var);
937 mono_mb_emit_icon (mb, (NFREELISTS-1) * GRANULARITY);
938 not_small_enough_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_UN_S);
939 /* check for overflow */
940 mono_mb_emit_ldloc (mb, bytes_var);
941 mono_mb_emit_icon (mb, sizeof (MonoString));
942 size_overflow_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLE_UN_S);
945 /* int index = INDEX_FROM_BYTES(bytes); */
946 index_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
948 mono_mb_emit_ldloc (mb, bytes_var);
949 mono_mb_emit_icon (mb, GRANULARITY - 1);
950 mono_mb_emit_byte (mb, MONO_CEE_ADD);
951 mono_mb_emit_icon (mb, shift_amount (GRANULARITY));
952 mono_mb_emit_byte (mb, MONO_CEE_SHR_UN);
953 mono_mb_emit_icon (mb, shift_amount (sizeof (gpointer)));
954 mono_mb_emit_byte (mb, MONO_CEE_SHL);
955 /* index var is already adjusted into bytes */
956 mono_mb_emit_stloc (mb, index_var);
958 my_fl_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
959 my_entry_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
960 /* my_fl = ((GC_thread)tsd) -> ptrfree_freelists + index; */
961 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
962 mono_mb_emit_byte (mb, 0x0D); /* CEE_MONO_TLS */
963 mono_mb_emit_i4 (mb, tls_key);
964 if (atype == ATYPE_FREEPTR || atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING)
965 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
966 + G_STRUCT_OFFSET (struct thread_local_freelists,
968 else if (atype == ATYPE_NORMAL)
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_GCJ)
973 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
974 + G_STRUCT_OFFSET (struct thread_local_freelists,
977 g_assert_not_reached ();
978 mono_mb_emit_byte (mb, MONO_CEE_ADD);
979 mono_mb_emit_ldloc (mb, index_var);
980 mono_mb_emit_byte (mb, MONO_CEE_ADD);
981 mono_mb_emit_stloc (mb, my_fl_var);
983 /* my_entry = *my_fl; */
984 mono_mb_emit_ldloc (mb, my_fl_var);
985 mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
986 mono_mb_emit_stloc (mb, my_entry_var);
988 /* if (EXPECT((word)my_entry >= HBLKSIZE, 1)) { */
989 mono_mb_emit_ldloc (mb, my_entry_var);
990 mono_mb_emit_icon (mb, HBLKSIZE);
991 no_freelist_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
993 /* ptr_t next = obj_link(my_entry); *my_fl = next; */
994 mono_mb_emit_ldloc (mb, my_fl_var);
995 mono_mb_emit_ldloc (mb, my_entry_var);
996 mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
997 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
999 /* set the vtable and clear the words in the object */
1000 mono_mb_emit_ldloc (mb, my_entry_var);
1001 mono_mb_emit_ldarg (mb, 0);
1002 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1004 if (atype == ATYPE_FREEPTR) {
1005 int start_var, end_var, start_loop;
1006 /* end = my_entry + bytes; start = my_entry + sizeof (gpointer);
1008 start_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1009 end_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1010 mono_mb_emit_ldloc (mb, my_entry_var);
1011 mono_mb_emit_ldloc (mb, bytes_var);
1012 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1013 mono_mb_emit_stloc (mb, end_var);
1014 mono_mb_emit_ldloc (mb, my_entry_var);
1015 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
1016 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1017 mono_mb_emit_stloc (mb, start_var);
1021 * } while (start < end);
1023 start_loop = mono_mb_get_label (mb);
1024 mono_mb_emit_ldloc (mb, start_var);
1025 mono_mb_emit_icon (mb, 0);
1026 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1027 mono_mb_emit_ldloc (mb, start_var);
1028 mono_mb_emit_icon (mb, sizeof (gpointer));
1029 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1030 mono_mb_emit_stloc (mb, start_var);
1032 mono_mb_emit_ldloc (mb, start_var);
1033 mono_mb_emit_ldloc (mb, end_var);
1034 mono_mb_emit_byte (mb, MONO_CEE_BLT_UN_S);
1035 mono_mb_emit_byte (mb, start_loop - (mono_mb_get_label (mb) + 1));
1036 } else if (atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING) {
1037 /* need to clear just the sync pointer */
1038 mono_mb_emit_ldloc (mb, my_entry_var);
1039 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
1040 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1041 mono_mb_emit_icon (mb, 0);
1042 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1045 if (atype == ATYPE_STRING) {
1046 /* need to set length and clear the last char */
1047 /* s->length = len; */
1048 mono_mb_emit_ldloc (mb, my_entry_var);
1049 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, length));
1050 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1051 mono_mb_emit_ldarg (mb, 1);
1052 mono_mb_emit_byte (mb, MONO_CEE_STIND_I4);
1053 /* s->chars [len] = 0; */
1054 mono_mb_emit_ldloc (mb, my_entry_var);
1055 mono_mb_emit_ldloc (mb, bytes_var);
1056 mono_mb_emit_icon (mb, 2);
1057 mono_mb_emit_byte (mb, MONO_CEE_SUB);
1058 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1059 mono_mb_emit_icon (mb, 0);
1060 mono_mb_emit_byte (mb, MONO_CEE_STIND_I2);
1063 /* return my_entry; */
1064 mono_mb_emit_ldloc (mb, my_entry_var);
1065 mono_mb_emit_byte (mb, MONO_CEE_RET);
1067 mono_mb_patch_short_branch (mb, no_freelist_branch);
1068 if (not_small_enough_branch > 0)
1069 mono_mb_patch_short_branch (mb, not_small_enough_branch);
1070 if (size_overflow_branch > 0)
1071 mono_mb_patch_short_branch (mb, size_overflow_branch);
1073 /* the slow path: we just call back into the runtime */
1075 if (atype == ATYPE_STRING) {
1076 mono_mb_emit_ldarg (mb, 1);
1077 mono_mb_emit_icall (mb, ves_icall_string_alloc);
1079 mono_mb_emit_ldarg (mb, 0);
1080 mono_mb_emit_icall (mb, ves_icall_object_new_specific);
1083 mono_mb_emit_byte (mb, MONO_CEE_RET);
1085 info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE);
1086 info->d.alloc.gc_name = "boehm";
1087 info->d.alloc.alloc_type = atype;
1088 mb->init_locals = FALSE;
1090 res = mono_mb_create (mb, csig, 8, info);
1096 static MonoMethod* alloc_method_cache [ATYPE_NUM];
1097 static MonoMethod* slowpath_alloc_method_cache [ATYPE_NUM];
1099 static G_GNUC_UNUSED gboolean
1100 mono_gc_is_critical_method (MonoMethod *method)
1104 for (i = 0; i < ATYPE_NUM; ++i)
1105 if (method == alloc_method_cache [i] || method == slowpath_alloc_method_cache [i])
1112 * If possible, generate a managed method that can quickly allocate objects in class
1113 * @klass. The method will typically have an thread-local inline allocation sequence.
1114 * The signature of the called method is:
1115 * object allocate (MonoVTable *vtable)
1116 * Some of the logic here is similar to mono_class_get_allocation_ftn () i object.c,
1118 * The thread local alloc logic is taken from libgc/pthread_support.c.
1122 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size)
1126 MONO_THREAD_VAR_OFFSET (GC_thread_tls, offset);
1128 /*g_print ("thread tls: %d\n", offset);*/
1131 if (!SMALL_ENOUGH (klass->instance_size))
1133 if (mono_class_has_finalizer (klass) || mono_class_is_marshalbyref (klass))
1135 if (mono_profiler_get_events () & (MONO_PROFILE_ALLOCATIONS | MONO_PROFILE_STATISTICAL))
1139 if (mono_class_is_open_constructed_type (&klass->byval_arg))
1141 if (klass->byval_arg.type == MONO_TYPE_STRING) {
1142 atype = ATYPE_STRING;
1143 } else if (!known_instance_size) {
1145 } else if (!klass->has_references) {
1147 atype = ATYPE_FREEPTR_FOR_BOX;
1149 atype = ATYPE_FREEPTR;
1153 * disabled because we currently do a runtime choice anyway, to
1154 * deal with multiple appdomains.
1155 if (vtable->gc_descr != GC_NO_DESCRIPTOR)
1158 atype = ATYPE_NORMAL;
1161 return mono_gc_get_managed_allocator_by_type (atype, MANAGED_ALLOCATOR_REGULAR);
1165 mono_gc_get_managed_array_allocator (MonoClass *klass)
1171 * mono_gc_get_managed_allocator_by_type:
1173 * Return a managed allocator method corresponding to allocator type ATYPE.
1176 mono_gc_get_managed_allocator_by_type (int atype, ManagedAllocatorVariant variant)
1180 gboolean slowpath = variant != MANAGED_ALLOCATOR_REGULAR;
1181 MonoMethod **cache = slowpath ? slowpath_alloc_method_cache : alloc_method_cache;
1182 MONO_THREAD_VAR_OFFSET (GC_thread_tls, offset);
1184 mono_tls_key_set_offset (TLS_KEY_BOEHM_GC_THREAD, offset);
1186 res = cache [atype];
1190 res = create_allocator (atype, TLS_KEY_BOEHM_GC_THREAD, slowpath);
1191 mono_os_mutex_lock (&mono_gc_lock);
1192 if (cache [atype]) {
1193 mono_free_method (res);
1194 res = cache [atype];
1196 mono_memory_barrier ();
1197 cache [atype] = res;
1199 mono_os_mutex_unlock (&mono_gc_lock);
1204 mono_gc_get_managed_allocator_types (void)
1210 mono_gc_get_write_barrier (void)
1212 g_assert_not_reached ();
1218 static G_GNUC_UNUSED gboolean
1219 mono_gc_is_critical_method (MonoMethod *method)
1225 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size)
1231 mono_gc_get_managed_array_allocator (MonoClass *klass)
1237 mono_gc_get_managed_allocator_by_type (int atype, ManagedAllocatorVariant variant)
1243 mono_gc_get_managed_allocator_types (void)
1249 mono_gc_get_write_barrier (void)
1251 g_assert_not_reached ();
1258 mono_gc_get_specific_write_barrier (gboolean is_concurrent)
1260 g_assert_not_reached ();
1265 mono_gc_get_aligned_size_for_allocator (int size)
1271 mono_gc_get_gc_name (void)
1277 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
1279 return GC_call_with_alloc_lock (func, data);
1283 mono_gc_get_description (void)
1285 return g_strdup (DEFAULT_GC_NAME);
1289 mono_gc_set_desktop_mode (void)
1295 mono_gc_is_moving (void)
1301 mono_gc_is_disabled (void)
1303 if (GC_dont_gc || g_getenv ("GC_DONT_GC"))
1310 mono_gc_wbarrier_value_copy_bitmap (gpointer _dest, gpointer _src, int size, unsigned bitmap)
1312 g_assert_not_reached ();
1317 mono_gc_get_card_table (int *shift_bits, gpointer *card_mask)
1319 g_assert_not_reached ();
1324 mono_gc_card_table_nursery_check (void)
1326 g_assert_not_reached ();
1331 mono_gc_get_nursery (int *shift_bits, size_t *size)
1337 mono_gc_set_current_thread_appdomain (MonoDomain *domain)
1342 mono_gc_precise_stack_mark_enabled (void)
1348 mono_gc_get_logfile (void)
1354 mono_gc_conservatively_scan_area (void *start, void *end)
1356 g_assert_not_reached ();
1360 mono_gc_scan_object (void *obj, void *gc_data)
1362 g_assert_not_reached ();
1367 mono_gc_get_bitmap_for_descr (void *descr, int *numbits)
1369 g_assert_not_reached ();
1374 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
1379 mono_gc_set_stack_end (void *stack_end)
1383 void mono_gc_set_skip_thread (gboolean value)
1388 mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
1393 /* This assertion is not valid when GC_DEBUG is defined */
1394 g_assert (GC_base (obj) == (char*)obj - offset);
1397 GC_REGISTER_FINALIZER_NO_ORDER ((char*)obj - offset, (GC_finalization_proc)user_data, GUINT_TO_POINTER (offset), NULL, NULL);
1402 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
1404 /* it is being replaced by GC_pthread_create on some
1405 * platforms, see libgc/include/gc_pthread_redirects.h */
1406 return pthread_create (new_thread, attr, start_routine, arg);
1411 BOOL APIENTRY mono_gc_dllmain (HMODULE module_handle, DWORD reason, LPVOID reserved)
1413 return GC_DllMain (module_handle, reason, reserved);
1418 mono_gc_get_vtable_bits (MonoClass *klass)
1420 if (fin_callbacks.is_class_finalization_aware) {
1421 if (fin_callbacks.is_class_finalization_aware (klass))
1422 return BOEHM_GC_BIT_FINALIZER_AWARE;
1428 * mono_gc_register_altstack:
1430 * Register the dimensions of the normal stack and altstack with the collector.
1431 * Currently, STACK/STACK_SIZE is only used when the thread is suspended while it is on an altstack.
1434 mono_gc_register_altstack (gpointer stack, gint32 stack_size, gpointer altstack, gint32 altstack_size)
1436 GC_register_altstack (stack, stack_size, altstack, altstack_size);
1440 mono_gc_get_los_limit (void)
1446 mono_gc_set_string_length (MonoString *str, gint32 new_length)
1448 mono_unichar2 *new_end = str->chars + new_length;
1450 /* zero the discarded string. This null-delimits the string and allows
1451 * the space to be reclaimed by SGen. */
1453 memset (new_end, 0, (str->length - new_length + 1) * sizeof (mono_unichar2));
1454 str->length = new_length;
1458 mono_gc_user_markers_supported (void)
1464 mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
1466 g_assert_not_reached ();
1470 /* Toggleref support */
1473 mono_gc_toggleref_add (MonoObject *object, mono_bool strong_ref)
1475 if (GC_toggleref_add ((GC_PTR)object, (int)strong_ref) != GC_SUCCESS)
1476 g_error ("GC_toggleref_add failed\n");
1480 mono_gc_toggleref_register_callback (MonoToggleRefStatus (*proccess_toggleref) (MonoObject *obj))
1482 GC_set_toggleref_func ((GC_ToggleRefStatus (*) (GC_PTR obj)) proccess_toggleref);
1485 /* Test support code */
1487 static MonoToggleRefStatus
1488 test_toggleref_callback (MonoObject *obj)
1490 static MonoClassField *mono_toggleref_test_field;
1491 MonoToggleRefStatus status = MONO_TOGGLE_REF_DROP;
1493 if (!mono_toggleref_test_field) {
1494 mono_toggleref_test_field = mono_class_get_field_from_name (mono_object_get_class (obj), "__test");
1495 g_assert (mono_toggleref_test_field);
1498 mono_field_get_value (obj, mono_toggleref_test_field, &status);
1499 printf ("toggleref-cb obj %d\n", status);
1504 register_test_toggleref_callback (void)
1506 mono_gc_toggleref_register_callback (test_toggleref_callback);
1510 is_finalization_aware (MonoObject *obj)
1512 MonoVTable *vt = obj->vtable;
1513 return (vt->gc_bits & BOEHM_GC_BIT_FINALIZER_AWARE) == BOEHM_GC_BIT_FINALIZER_AWARE;
1517 fin_notifier (MonoObject *obj)
1519 if (is_finalization_aware (obj))
1520 fin_callbacks.object_queued_for_finalization (obj);
1524 mono_gc_register_finalizer_callbacks (MonoGCFinalizerCallbacks *callbacks)
1526 if (callbacks->version != MONO_GC_FINALIZER_EXTENSION_VERSION)
1527 g_error ("Invalid finalizer callback version. Expected %d but got %d\n", MONO_GC_FINALIZER_EXTENSION_VERSION, callbacks->version);
1529 fin_callbacks = *callbacks;
1531 GC_set_await_finalize_proc ((void (*) (GC_PTR))fin_notifier);
1534 #define BITMAP_SIZE (sizeof (*((HandleData *)NULL)->bitmap) * CHAR_BIT)
1536 static inline gboolean
1537 slot_occupied (HandleData *handles, guint slot) {
1538 return handles->bitmap [slot / BITMAP_SIZE] & (1 << (slot % BITMAP_SIZE));
1542 vacate_slot (HandleData *handles, guint slot) {
1543 handles->bitmap [slot / BITMAP_SIZE] &= ~(1 << (slot % BITMAP_SIZE));
1547 occupy_slot (HandleData *handles, guint slot) {
1548 handles->bitmap [slot / BITMAP_SIZE] |= 1 << (slot % BITMAP_SIZE);
1552 find_first_unset (guint32 bitmap)
1555 for (i = 0; i < 32; ++i) {
1556 if (!(bitmap & (1 << i)))
1563 handle_data_alloc_entries (HandleData *handles)
1566 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1567 handles->entries = (void **)g_malloc0 (sizeof (*handles->entries) * handles->size);
1568 handles->domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * handles->size);
1570 handles->entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * handles->size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
1572 handles->bitmap = (guint32 *)g_malloc0 (handles->size / CHAR_BIT);
1576 handle_data_next_unset (HandleData *handles)
1579 for (slot = handles->slot_hint; slot < handles->size / BITMAP_SIZE; ++slot) {
1580 if (handles->bitmap [slot] == 0xffffffff)
1582 handles->slot_hint = slot;
1583 return find_first_unset (handles->bitmap [slot]);
1589 handle_data_first_unset (HandleData *handles)
1592 for (slot = 0; slot < handles->slot_hint; ++slot) {
1593 if (handles->bitmap [slot] == 0xffffffff)
1595 handles->slot_hint = slot;
1596 return find_first_unset (handles->bitmap [slot]);
1601 /* Returns the index of the current slot in the bitmap. */
1603 handle_data_grow (HandleData *handles, gboolean track)
1605 guint32 *new_bitmap;
1606 guint32 new_size = handles->size * 2; /* always double: we memset to 0 based on this below */
1608 /* resize and copy the bitmap */
1609 new_bitmap = (guint32 *)g_malloc0 (new_size / CHAR_BIT);
1610 memcpy (new_bitmap, handles->bitmap, handles->size / CHAR_BIT);
1611 g_free (handles->bitmap);
1612 handles->bitmap = new_bitmap;
1614 /* resize and copy the entries */
1615 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1617 guint16 *domain_ids;
1619 domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * new_size);
1620 entries = (void **)g_malloc0 (sizeof (*handles->entries) * new_size);
1621 memcpy (domain_ids, handles->domain_ids, sizeof (*handles->domain_ids) * handles->size);
1622 for (i = 0; i < handles->size; ++i) {
1623 MonoObject *obj = mono_gc_weak_link_get (&(handles->entries [i]));
1625 mono_gc_weak_link_add (&(entries [i]), obj, track);
1626 mono_gc_weak_link_remove (&(handles->entries [i]), track);
1628 g_assert (!handles->entries [i]);
1631 g_free (handles->entries);
1632 g_free (handles->domain_ids);
1633 handles->entries = entries;
1634 handles->domain_ids = domain_ids;
1637 entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * new_size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
1638 mono_gc_memmove_aligned (entries, handles->entries, sizeof (*handles->entries) * handles->size);
1639 mono_gc_free_fixed (handles->entries);
1640 handles->entries = entries;
1642 handles->slot_hint = handles->size / BITMAP_SIZE;
1643 handles->size = new_size;
1647 alloc_handle (HandleData *handles, MonoObject *obj, gboolean track)
1651 lock_handles (handles);
1653 handle_data_alloc_entries (handles);
1654 i = handle_data_next_unset (handles);
1655 if (i == -1 && handles->slot_hint != 0)
1656 i = handle_data_first_unset (handles);
1658 handle_data_grow (handles, track);
1661 slot = handles->slot_hint * BITMAP_SIZE + i;
1662 occupy_slot (handles, slot);
1663 handles->entries [slot] = NULL;
1664 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1665 /*FIXME, what to use when obj == null?*/
1666 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
1668 mono_gc_weak_link_add (&(handles->entries [slot]), obj, track);
1670 handles->entries [slot] = obj;
1673 #ifndef DISABLE_PERFCOUNTERS
1674 mono_perfcounters->gc_num_handles++;
1676 unlock_handles (handles);
1677 res = MONO_GC_HANDLE (slot, handles->type);
1678 mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_CREATED, handles->type, res, obj);
1683 * mono_gchandle_new:
1684 * @obj: managed object to get a handle for
1685 * @pinned: whether the object should be pinned
1687 * This returns a handle that wraps the object, this is used to keep a
1688 * reference to a managed object from the unmanaged world and preventing the
1689 * object from being disposed.
1691 * If @pinned is false the address of the object can not be obtained, if it is
1692 * true the address of the object can be obtained. This will also pin the
1693 * object so it will not be possible by a moving garbage collector to move the
1696 * Returns: a handle that can be used to access the object from
1700 mono_gchandle_new (MonoObject *obj, gboolean pinned)
1702 return alloc_handle (&gc_handles [pinned? HANDLE_PINNED: HANDLE_NORMAL], obj, FALSE);
1706 * mono_gchandle_new_weakref:
1707 * @obj: managed object to get a handle for
1708 * @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.
1710 * This returns a weak handle that wraps the object, this is used to
1711 * keep a reference to a managed object from the unmanaged world.
1712 * Unlike the mono_gchandle_new the object can be reclaimed by the
1713 * garbage collector. In this case the value of the GCHandle will be
1716 * If @track_resurrection is TRUE the object will be tracked through
1717 * finalization and if the object is resurrected during the execution
1718 * of the finalizer, then the returned weakref will continue to hold
1719 * a reference to the object. If @track_resurrection is FALSE, then
1720 * the weak reference's target will become NULL as soon as the object
1721 * is passed on to the finalizer.
1723 * Returns: a handle that can be used to access the object from
1727 mono_gchandle_new_weakref (MonoObject *obj, gboolean track_resurrection)
1729 return alloc_handle (&gc_handles [track_resurrection? HANDLE_WEAK_TRACK: HANDLE_WEAK], obj, track_resurrection);
1733 * mono_gchandle_get_target:
1734 * @gchandle: a GCHandle's handle.
1736 * The handle was previously created by calling `mono_gchandle_new` or
1737 * `mono_gchandle_new_weakref`.
1739 * Returns: A pointer to the `MonoObject*` represented by the handle or
1740 * NULL for a collected object if using a weakref handle.
1743 mono_gchandle_get_target (guint32 gchandle)
1745 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1746 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1747 HandleData *handles = &gc_handles [type];
1748 MonoObject *obj = NULL;
1749 if (type >= HANDLE_TYPE_MAX)
1752 lock_handles (handles);
1753 if (slot < handles->size && slot_occupied (handles, slot)) {
1754 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1755 obj = mono_gc_weak_link_get (&handles->entries [slot]);
1757 obj = (MonoObject *)handles->entries [slot];
1760 /* print a warning? */
1762 unlock_handles (handles);
1763 /*g_print ("get target of entry %d of type %d: %p\n", slot, handles->type, obj);*/
1768 mono_gchandle_set_target (guint32 gchandle, MonoObject *obj)
1770 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1771 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1772 HandleData *handles = &gc_handles [type];
1773 MonoObject *old_obj = NULL;
1775 g_assert (type < HANDLE_TYPE_MAX);
1776 lock_handles (handles);
1777 if (slot < handles->size && slot_occupied (handles, slot)) {
1778 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1779 old_obj = (MonoObject *)handles->entries [slot];
1780 if (handles->entries [slot])
1781 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1783 mono_gc_weak_link_add (&handles->entries [slot], obj, handles->type == HANDLE_WEAK_TRACK);
1784 /*FIXME, what to use when obj == null?*/
1785 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
1787 handles->entries [slot] = obj;
1790 /* print a warning? */
1792 /*g_print ("changed entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
1793 unlock_handles (handles);
1797 mono_gc_is_null (void)
1803 * mono_gchandle_is_in_domain:
1804 * @gchandle: a GCHandle's handle.
1805 * @domain: An application domain.
1807 * Use this function to determine if the @gchandle points to an
1808 * object allocated in the specified @domain.
1810 * Returns: TRUE if the object wrapped by the @gchandle belongs to the specific @domain.
1813 mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain)
1815 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1816 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1817 HandleData *handles = &gc_handles [type];
1818 gboolean result = FALSE;
1820 if (type >= HANDLE_TYPE_MAX)
1823 lock_handles (handles);
1824 if (slot < handles->size && slot_occupied (handles, slot)) {
1825 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1826 result = domain->domain_id == handles->domain_ids [slot];
1829 obj = (MonoObject *)handles->entries [slot];
1833 result = domain == mono_object_domain (obj);
1836 /* print a warning? */
1838 unlock_handles (handles);
1843 * mono_gchandle_free:
1844 * @gchandle: a GCHandle's handle.
1846 * Frees the @gchandle handle. If there are no outstanding
1847 * references, the garbage collector can reclaim the memory of the
1851 mono_gchandle_free (guint32 gchandle)
1853 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1854 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1855 HandleData *handles = &gc_handles [type];
1856 if (type >= HANDLE_TYPE_MAX)
1859 lock_handles (handles);
1860 if (slot < handles->size && slot_occupied (handles, slot)) {
1861 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1862 if (handles->entries [slot])
1863 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1865 handles->entries [slot] = NULL;
1867 vacate_slot (handles, slot);
1869 /* print a warning? */
1871 #ifndef DISABLE_PERFCOUNTERS
1872 mono_perfcounters->gc_num_handles--;
1874 /*g_print ("freed entry %d of type %d\n", slot, handles->type);*/
1875 unlock_handles (handles);
1876 mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_DESTROYED, handles->type, gchandle, NULL);
1880 * mono_gchandle_free_domain:
1881 * @domain: domain that is unloading
1883 * Function used internally to cleanup any GC handle for objects belonging
1884 * to the specified domain during appdomain unload.
1887 mono_gchandle_free_domain (MonoDomain *domain)
1891 for (type = HANDLE_TYPE_MIN; type < HANDLE_PINNED; ++type) {
1893 HandleData *handles = &gc_handles [type];
1894 lock_handles (handles);
1895 for (slot = 0; slot < handles->size; ++slot) {
1896 if (!slot_occupied (handles, slot))
1898 if (MONO_GC_HANDLE_TYPE_IS_WEAK (type)) {
1899 if (domain->domain_id == handles->domain_ids [slot]) {
1900 vacate_slot (handles, slot);
1901 if (handles->entries [slot])
1902 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1905 if (handles->entries [slot] && mono_object_domain (handles->entries [slot]) == domain) {
1906 vacate_slot (handles, slot);
1907 handles->entries [slot] = NULL;
1911 unlock_handles (handles);
1916 #endif /* no Boehm GC */