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)
13 #define GC_I_HIDE_POINTERS
14 #include <mono/metadata/gc-internals.h>
15 #include <mono/metadata/mono-gc.h>
16 #include <mono/metadata/profiler-private.h>
17 #include <mono/metadata/class-internals.h>
18 #include <mono/metadata/method-builder.h>
19 #include <mono/metadata/opcodes.h>
20 #include <mono/metadata/domain-internals.h>
21 #include <mono/metadata/metadata-internals.h>
22 #include <mono/metadata/marshal.h>
23 #include <mono/metadata/runtime.h>
24 #include <mono/metadata/sgen-toggleref.h>
25 #include <mono/utils/atomic.h>
26 #include <mono/utils/mono-logger-internals.h>
27 #include <mono/utils/mono-memory-model.h>
28 #include <mono/utils/mono-time.h>
29 #include <mono/utils/mono-threads.h>
30 #include <mono/utils/dtrace.h>
31 #include <mono/utils/gc_wrapper.h>
32 #include <mono/utils/mono-os-mutex.h>
33 #include <mono/utils/mono-counters.h>
39 #define THREAD_LOCAL_ALLOC 1
40 #include "private/pthread_support.h"
42 #if defined(PLATFORM_MACOSX) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
43 void *pthread_get_stackaddr_np(pthread_t);
46 #define GC_NO_DESCRIPTOR ((gpointer)(0 | GC_DS_LENGTH))
47 /*Boehm max heap cannot be smaller than 16MB*/
48 #define MIN_BOEHM_MAX_HEAP_SIZE_IN_MB 16
49 #define MIN_BOEHM_MAX_HEAP_SIZE (MIN_BOEHM_MAX_HEAP_SIZE_IN_MB << 20)
51 static gboolean gc_initialized = FALSE;
52 static mono_mutex_t mono_gc_lock;
55 boehm_thread_register (MonoThreadInfo* info, void *baseptr);
57 boehm_thread_unregister (MonoThreadInfo *p);
59 register_test_toggleref_callback (void);
61 #define BOEHM_GC_BIT_FINALIZER_AWARE 1
62 static MonoGCFinalizerCallbacks fin_callbacks;
66 static mono_mutex_t handle_section;
67 #define lock_handles(handles) mono_os_mutex_lock (&handle_section)
68 #define unlock_handles(handles) mono_os_mutex_unlock (&handle_section)
75 guint slot_hint : 24; /* starting slot for search in bitmap */
76 /* 2^16 appdomains should be enough for everyone (though I know I'll regret this in 20 years) */
77 /* we alloc this only for weak refs, since we can get the domain directly in the other cases */
81 #define EMPTY_HANDLE_DATA(type) {NULL, NULL, 0, (type), 0, NULL}
83 /* weak and weak-track arrays will be allocated in malloc memory
85 static HandleData gc_handles [] = {
86 EMPTY_HANDLE_DATA (HANDLE_WEAK),
87 EMPTY_HANDLE_DATA (HANDLE_WEAK_TRACK),
88 EMPTY_HANDLE_DATA (HANDLE_NORMAL),
89 EMPTY_HANDLE_DATA (HANDLE_PINNED)
93 mono_gc_warning (char *msg, GC_word arg)
95 mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_GC, msg, (unsigned long)arg);
99 mono_gc_base_init (void)
101 MonoThreadInfoCallbacks cb;
108 mono_counters_init ();
111 * Handle the case when we are called from a thread different from the main thread,
113 * FIXME: Move this to libgc where it belongs.
115 * we used to do this only when running on valgrind,
116 * but it happens also in other setups.
118 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) && !defined(__native_client__)
123 pthread_getattr_np (pthread_self (), &attr);
124 pthread_attr_getstack (&attr, &sstart, &size);
125 pthread_attr_destroy (&attr);
126 /*g_print ("stackbottom pth is: %p\n", (char*)sstart + size);*/
129 * The calculation above doesn't seem to work on ia64, also we need to set
130 * GC_register_stackbottom as well, but don't know how.
133 /* apparently with some linuxthreads implementations sstart can be NULL,
134 * fallback to the more imprecise method (bug# 78096).
137 GC_stackbottom = (char*)sstart + size;
140 gsize stack_bottom = (gsize)&dummy;
141 stack_bottom += 4095;
142 stack_bottom &= ~4095;
143 GC_stackbottom = (char*)stack_bottom;
147 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
148 GC_stackbottom = (char*)pthread_get_stackaddr_np (pthread_self ());
149 #elif defined(__OpenBSD__)
150 # include <pthread_np.h>
155 rslt = pthread_stackseg_np(pthread_self(), &ss);
156 g_assert (rslt == 0);
158 GC_stackbottom = (char*)ss.ss_sp;
160 #elif defined(__native_client__)
161 /* Do nothing, GC_stackbottom is set correctly in libgc */
165 gsize stack_bottom = (gsize)&dummy;
166 stack_bottom += 4095;
167 stack_bottom &= ~4095;
168 /*g_print ("stackbottom is: %p\n", (char*)stack_bottom);*/
169 GC_stackbottom = (char*)stack_bottom;
173 #if !defined(PLATFORM_ANDROID)
174 /* If GC_no_dls is set to true, GC_find_limit is not called. This causes a seg fault on Android. */
178 if ((env = g_getenv ("MONO_GC_DEBUG"))) {
179 char **opts = g_strsplit (env, ",", -1);
180 for (char **ptr = opts; ptr && *ptr; ptr ++) {
182 if (!strcmp (opt, "do-not-finalize")) {
183 mono_do_not_finalize = 1;
184 } else if (!strcmp (opt, "log-finalizers")) {
193 GC_set_warn_proc (mono_gc_warning);
194 GC_finalize_on_demand = 1;
195 GC_finalizer_notifier = mono_gc_finalize_notify;
197 GC_init_gcj_malloc (5, NULL);
198 GC_allow_register_threads ();
200 if ((env = g_getenv ("MONO_GC_PARAMS"))) {
201 char **ptr, **opts = g_strsplit (env, ",", -1);
202 for (ptr = opts; *ptr; ++ptr) {
204 if (g_str_has_prefix (opt, "max-heap-size=")) {
207 opt = strchr (opt, '=') + 1;
208 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &max_heap)) {
209 if (max_heap < MIN_BOEHM_MAX_HEAP_SIZE) {
210 fprintf (stderr, "max-heap-size must be at least %dMb.\n", MIN_BOEHM_MAX_HEAP_SIZE_IN_MB);
213 GC_set_max_heap_size (max_heap);
215 fprintf (stderr, "max-heap-size must be an integer.\n");
219 } else if (g_str_has_prefix (opt, "toggleref-test")) {
220 register_test_toggleref_callback ();
223 /* Could be a parameter for sgen */
225 fprintf (stderr, "MONO_GC_PARAMS must be a comma-delimited list of one or more of the following:\n");
226 fprintf (stderr, " max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
234 memset (&cb, 0, sizeof (cb));
235 cb.thread_register = boehm_thread_register;
236 cb.thread_unregister = boehm_thread_unregister;
237 cb.mono_method_is_critical = (gboolean (*)(void *))mono_runtime_is_critical_method;
239 mono_threads_init (&cb, sizeof (MonoThreadInfo));
240 mono_os_mutex_init (&mono_gc_lock);
241 mono_os_mutex_init_recursive (&handle_section);
243 mono_thread_info_attach (&dummy);
245 mono_gc_enable_events ();
247 MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_NORMAL].entries, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
248 MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_PINNED].entries, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
250 gc_initialized = TRUE;
254 mono_gc_base_cleanup (void)
256 GC_finalizer_notifier = NULL;
261 * @generation: GC generation identifier
263 * Perform a garbage collection for the given generation, higher numbers
264 * mean usually older objects. Collecting a high-numbered generation
265 * implies collecting also the lower-numbered generations.
266 * The maximum value for @generation can be retrieved with a call to
267 * mono_gc_max_generation(), so this function is usually called as:
269 * mono_gc_collect (mono_gc_max_generation ());
272 mono_gc_collect (int generation)
274 #ifndef DISABLE_PERFCOUNTERS
275 mono_perfcounters->gc_induced++;
281 * mono_gc_max_generation:
283 * Get the maximum generation number used by the current garbage
284 * collector. The value will be 0 for the Boehm collector, 1 or more
285 * for the generational collectors.
287 * Returns: the maximum generation number.
290 mono_gc_max_generation (void)
296 * mono_gc_get_generation:
297 * @object: a managed object
299 * Get the garbage collector's generation that @object belongs to.
300 * Use this has a hint only.
302 * Returns: a garbage collector generation number
305 mono_gc_get_generation (MonoObject *object)
311 * mono_gc_collection_count:
312 * @generation: a GC generation number
314 * Get how many times a garbage collection has been performed
315 * for the given @generation number.
317 * Returns: the number of garbage collections
320 mono_gc_collection_count (int generation)
326 * mono_gc_add_memory_pressure:
327 * @value: amount of bytes
329 * Adjust the garbage collector's view of how many bytes of memory
330 * are indirectly referenced by managed objects (for example unmanaged
331 * memory holding image or other binary data).
332 * This is a hint only to the garbage collector algorithm.
333 * Note that negative amounts of @value will decrease the memory
337 mono_gc_add_memory_pressure (gint64 value)
342 * mono_gc_get_used_size:
344 * Get the approximate amount of memory used by managed objects.
346 * Returns: the amount of memory used in bytes
349 mono_gc_get_used_size (void)
351 return GC_get_heap_size () - GC_get_free_bytes ();
355 * mono_gc_get_heap_size:
357 * Get the amount of memory used by the garbage collector.
359 * Returns: the size of the heap in bytes
362 mono_gc_get_heap_size (void)
364 return GC_get_heap_size ();
368 mono_gc_is_gc_thread (void)
370 return GC_thread_is_registered ();
374 mono_gc_register_thread (void *baseptr)
376 return mono_thread_info_attach (baseptr) != NULL;
380 boehm_thread_register (MonoThreadInfo* info, void *baseptr)
382 struct GC_stack_base sb;
385 /* TODO: use GC_get_stack_base instead of baseptr. */
386 sb.mem_base = baseptr;
387 res = GC_register_my_thread (&sb);
388 if (res == GC_UNIMPLEMENTED)
389 return NULL; /* Cannot happen with GC v7+. */
394 boehm_thread_unregister (MonoThreadInfo *p)
396 MonoNativeThreadId tid;
398 tid = mono_thread_info_get_tid (p);
400 if (p->runtime_thread)
401 mono_threads_add_joinable_thread ((gpointer)tid);
405 mono_object_is_alive (MonoObject* o)
407 return GC_is_marked ((ptr_t)o);
411 mono_gc_walk_heap (int flags, MonoGCReferences callback, void *data)
416 static gint64 gc_start_time;
419 on_gc_notification (GC_EventType event)
421 MonoGCEvent e = (MonoGCEvent)event;
424 case MONO_GC_EVENT_PRE_STOP_WORLD:
425 MONO_GC_WORLD_STOP_BEGIN ();
426 mono_thread_info_suspend_lock ();
429 case MONO_GC_EVENT_POST_STOP_WORLD:
430 MONO_GC_WORLD_STOP_END ();
433 case MONO_GC_EVENT_PRE_START_WORLD:
434 MONO_GC_WORLD_RESTART_BEGIN (1);
437 case MONO_GC_EVENT_POST_START_WORLD:
438 MONO_GC_WORLD_RESTART_END (1);
439 mono_thread_info_suspend_unlock ();
442 case MONO_GC_EVENT_START:
444 #ifndef DISABLE_PERFCOUNTERS
445 if (mono_perfcounters)
446 mono_perfcounters->gc_collections0++;
448 gc_stats.major_gc_count ++;
449 gc_start_time = mono_100ns_ticks ();
452 case MONO_GC_EVENT_END:
454 #if defined(ENABLE_DTRACE) && defined(__sun__)
455 /* This works around a dtrace -G problem on Solaris.
456 Limit its actual use to when the probe is enabled. */
457 if (MONO_GC_END_ENABLED ())
461 #ifndef DISABLE_PERFCOUNTERS
462 if (mono_perfcounters) {
463 guint64 heap_size = GC_get_heap_size ();
464 guint64 used_size = heap_size - GC_get_free_bytes ();
465 mono_perfcounters->gc_total_bytes = used_size;
466 mono_perfcounters->gc_committed_bytes = heap_size;
467 mono_perfcounters->gc_reserved_bytes = heap_size;
468 mono_perfcounters->gc_gen0size = heap_size;
471 gc_stats.major_gc_time += mono_100ns_ticks () - gc_start_time;
472 mono_trace_message (MONO_TRACE_GC, "gc took %d usecs", (mono_100ns_ticks () - gc_start_time) / 10);
478 mono_profiler_gc_event (e, 0);
482 on_gc_heap_resize (size_t new_size)
484 guint64 heap_size = GC_get_heap_size ();
485 #ifndef DISABLE_PERFCOUNTERS
486 if (mono_perfcounters) {
487 mono_perfcounters->gc_committed_bytes = heap_size;
488 mono_perfcounters->gc_reserved_bytes = heap_size;
489 mono_perfcounters->gc_gen0size = heap_size;
492 mono_profiler_gc_heap_resize (new_size);
496 mono_gc_enable_events (void)
498 GC_set_on_collection_event (on_gc_notification);
499 GC_on_heap_resize = on_gc_heap_resize;
502 static gboolean alloc_events = FALSE;
505 mono_gc_enable_alloc_events (void)
511 mono_gc_register_root (char *start, size_t size, void *descr, MonoGCRootSource source, const char *msg)
513 /* for some strange reason, they want one extra byte on the end */
514 GC_add_roots (start, start + size + 1);
520 mono_gc_deregister_root (char* addr)
523 /* FIXME: libgc doesn't define this work win32 for some reason */
524 /* FIXME: No size info */
525 GC_remove_roots (addr, addr + sizeof (gpointer) + 1);
530 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
532 /* libgc requires that we use HIDE_POINTER... */
533 *link_addr = (void*)HIDE_POINTER (obj);
535 GC_REGISTER_LONG_LINK (link_addr, obj);
537 GC_GENERAL_REGISTER_DISAPPEARING_LINK (link_addr, obj);
541 mono_gc_weak_link_remove (void **link_addr, gboolean track)
544 GC_unregister_long_link (link_addr);
546 GC_unregister_disappearing_link (link_addr);
551 reveal_link (gpointer link_addr)
553 void **link_a = (void **)link_addr;
554 return REVEAL_POINTER (*link_a);
558 mono_gc_weak_link_get (void **link_addr)
560 MonoObject *obj = (MonoObject *)GC_call_with_alloc_lock (reveal_link, link_addr);
561 if (obj == (MonoObject *) -1)
567 mono_gc_make_descr_for_string (gsize *bitmap, int numbits)
569 return mono_gc_make_descr_from_bitmap (bitmap, numbits);
573 mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size)
575 return mono_gc_make_descr_from_bitmap (bitmap, numbits);
579 mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size)
581 /* libgc has no usable support for arrays... */
582 return GC_NO_DESCRIPTOR;
586 mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
588 /* It seems there are issues when the bitmap doesn't fit: play it safe */
590 return GC_NO_DESCRIPTOR;
592 return (gpointer)GC_make_descriptor ((GC_bitmap)bitmap, numbits);
596 mono_gc_make_root_descr_all_refs (int numbits)
602 mono_gc_alloc_fixed (size_t size, void *descr, MonoGCRootSource source, const char *msg)
604 /* To help track down typed allocation bugs */
608 if (count == atoi (g_getenv ("COUNT2")))
610 if (count > atoi (g_getenv ("COUNT2")))
611 return GC_MALLOC (size);
615 return GC_MALLOC_EXPLICITLY_TYPED (size, (GC_descr)descr);
617 return GC_MALLOC (size);
621 mono_gc_free_fixed (void* addr)
626 mono_gc_alloc_obj (MonoVTable *vtable, size_t size)
630 if (!vtable->klass->has_references) {
631 obj = (MonoObject *)GC_MALLOC_ATOMIC (size);
632 if (G_UNLIKELY (!obj))
635 obj->vtable = vtable;
636 obj->synchronisation = NULL;
638 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
639 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
640 obj = (MonoObject *)GC_GCJ_MALLOC (size, vtable);
641 if (G_UNLIKELY (!obj))
644 obj = (MonoObject *)GC_MALLOC (size);
645 if (G_UNLIKELY (!obj))
648 obj->vtable = vtable;
651 if (G_UNLIKELY (alloc_events))
652 mono_profiler_allocation (obj);
658 mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length)
662 if (!vtable->klass->has_references) {
663 obj = (MonoArray *)GC_MALLOC_ATOMIC (size);
664 if (G_UNLIKELY (!obj))
667 obj->obj.vtable = vtable;
668 obj->obj.synchronisation = NULL;
670 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
671 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
672 obj = (MonoArray *)GC_GCJ_MALLOC (size, vtable);
673 if (G_UNLIKELY (!obj))
676 obj = (MonoArray *)GC_MALLOC (size);
677 if (G_UNLIKELY (!obj))
680 obj->obj.vtable = vtable;
683 obj->max_length = max_length;
685 if (G_UNLIKELY (alloc_events))
686 mono_profiler_allocation (&obj->obj);
692 mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uintptr_t bounds_size)
696 if (!vtable->klass->has_references) {
697 obj = (MonoArray *)GC_MALLOC_ATOMIC (size);
698 if (G_UNLIKELY (!obj))
701 obj->obj.vtable = vtable;
702 obj->obj.synchronisation = NULL;
704 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
705 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
706 obj = (MonoArray *)GC_GCJ_MALLOC (size, vtable);
707 if (G_UNLIKELY (!obj))
710 obj = (MonoArray *)GC_MALLOC (size);
711 if (G_UNLIKELY (!obj))
714 obj->obj.vtable = vtable;
717 obj->max_length = max_length;
720 obj->bounds = (MonoArrayBounds *) ((char *) obj + size - bounds_size);
722 if (G_UNLIKELY (alloc_events))
723 mono_profiler_allocation (&obj->obj);
729 mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len)
731 MonoString *obj = (MonoString *)GC_MALLOC_ATOMIC (size);
732 if (G_UNLIKELY (!obj))
735 obj->object.vtable = vtable;
736 obj->object.synchronisation = NULL;
738 obj->chars [len] = 0;
740 if (G_UNLIKELY (alloc_events))
741 mono_profiler_allocation (&obj->object);
747 mono_gc_alloc_mature (MonoVTable *vtable, size_t size)
749 return mono_gc_alloc_obj (vtable, size);
753 mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size)
755 return mono_gc_alloc_obj (vtable, size);
759 mono_gc_invoke_finalizers (void)
761 /* There is a bug in GC_invoke_finalizer () in versions <= 6.2alpha4:
762 * the 'mem_freed' variable is not initialized when there are no
763 * objects to finalize, which leads to strange behavior later on.
764 * The check is necessary to work around that bug.
766 if (GC_should_invoke_finalizers ())
767 return GC_invoke_finalizers ();
772 mono_gc_pending_finalizers (void)
774 return GC_should_invoke_finalizers ();
778 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
780 *(void**)field_ptr = value;
784 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
786 *(void**)slot_ptr = value;
790 mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
792 mono_gc_memmove_aligned (dest_ptr, src_ptr, count * sizeof (gpointer));
796 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
798 *(void**)ptr = value;
802 mono_gc_wbarrier_generic_store_atomic (gpointer ptr, MonoObject *value)
804 InterlockedWritePointer ((volatile gpointer *)ptr, value);
808 mono_gc_wbarrier_generic_nostore (gpointer ptr)
813 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
815 mono_gc_memmove_atomic (dest, src, count * mono_class_value_size (klass, NULL));
819 mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
821 /* do not copy the sync state */
822 mono_gc_memmove_aligned ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
823 mono_object_class (obj)->instance_size - sizeof (MonoObject));
827 mono_gc_clear_domain (MonoDomain *domain)
832 mono_gc_get_suspend_signal (void)
834 return GC_get_suspend_signal ();
838 mono_gc_get_restart_signal (void)
840 return GC_get_thr_restart_signal ();
843 #if defined(USE_COMPILER_TLS) && defined(__linux__) && (defined(__i386__) || defined(__x86_64__))
844 extern __thread MONO_TLS_FAST void* GC_thread_tls;
845 #include "metadata-internals.h"
851 while (!(v & (1 << i)))
858 ATYPE_FREEPTR_FOR_BOX,
866 create_allocator (int atype, int tls_key, gboolean slowpath)
868 int index_var, bytes_var, my_fl_var, my_entry_var;
869 guint32 no_freelist_branch, not_small_enough_branch = 0;
870 guint32 size_overflow_branch = 0;
871 MonoMethodBuilder *mb;
873 MonoMethodSignature *csig;
874 const char *name = NULL;
877 if (atype == ATYPE_FREEPTR) {
878 name = slowpath ? "SlowAllocPtrfree" : "AllocPtrfree";
879 } else if (atype == ATYPE_FREEPTR_FOR_BOX) {
880 name = slowpath ? "SlowAllocPtrfreeBox" : "AllocPtrfreeBox";
881 } else if (atype == ATYPE_NORMAL) {
882 name = slowpath ? "SlowAlloc" : "Alloc";
883 } else if (atype == ATYPE_GCJ) {
884 name = slowpath ? "SlowAllocGcj" : "AllocGcj";
885 } else if (atype == ATYPE_STRING) {
886 name = slowpath ? "SlowAllocString" : "AllocString";
888 g_assert_not_reached ();
891 csig = mono_metadata_signature_alloc (mono_defaults.corlib, 2);
893 if (atype == ATYPE_STRING) {
894 csig->ret = &mono_defaults.string_class->byval_arg;
895 csig->params [0] = &mono_defaults.int_class->byval_arg;
896 csig->params [1] = &mono_defaults.int32_class->byval_arg;
898 csig->ret = &mono_defaults.object_class->byval_arg;
899 csig->params [0] = &mono_defaults.int_class->byval_arg;
900 csig->params [1] = &mono_defaults.int32_class->byval_arg;
903 mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_ALLOC);
906 goto always_slowpath;
908 bytes_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
909 if (atype == ATYPE_STRING) {
910 /* a string alloator method takes the args: (vtable, len) */
911 /* bytes = (offsetof (MonoString, chars) + ((len + 1) * 2)); */
912 mono_mb_emit_ldarg (mb, 1);
913 mono_mb_emit_icon (mb, 1);
914 mono_mb_emit_byte (mb, MONO_CEE_ADD);
915 mono_mb_emit_icon (mb, 1);
916 mono_mb_emit_byte (mb, MONO_CEE_SHL);
917 // sizeof (MonoString) might include padding
918 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, chars));
919 mono_mb_emit_byte (mb, MONO_CEE_ADD);
920 mono_mb_emit_stloc (mb, bytes_var);
922 mono_mb_emit_ldarg (mb, 1);
923 mono_mb_emit_stloc (mb, bytes_var);
926 /* this is needed for strings/arrays only as the other big types are never allocated with this method */
927 if (atype == ATYPE_STRING) {
929 /* if (!SMALL_ENOUGH (bytes)) jump slow_path;*/
930 mono_mb_emit_ldloc (mb, bytes_var);
931 mono_mb_emit_icon (mb, (NFREELISTS-1) * GRANULARITY);
932 not_small_enough_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_UN_S);
933 /* check for overflow */
934 mono_mb_emit_ldloc (mb, bytes_var);
935 mono_mb_emit_icon (mb, sizeof (MonoString));
936 size_overflow_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLE_UN_S);
939 /* int index = INDEX_FROM_BYTES(bytes); */
940 index_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
942 mono_mb_emit_ldloc (mb, bytes_var);
943 mono_mb_emit_icon (mb, GRANULARITY - 1);
944 mono_mb_emit_byte (mb, MONO_CEE_ADD);
945 mono_mb_emit_icon (mb, shift_amount (GRANULARITY));
946 mono_mb_emit_byte (mb, MONO_CEE_SHR_UN);
947 mono_mb_emit_icon (mb, shift_amount (sizeof (gpointer)));
948 mono_mb_emit_byte (mb, MONO_CEE_SHL);
949 /* index var is already adjusted into bytes */
950 mono_mb_emit_stloc (mb, index_var);
952 my_fl_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
953 my_entry_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
954 /* my_fl = ((GC_thread)tsd) -> ptrfree_freelists + index; */
955 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
956 mono_mb_emit_byte (mb, 0x0D); /* CEE_MONO_TLS */
957 mono_mb_emit_i4 (mb, tls_key);
958 if (atype == ATYPE_FREEPTR || atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING)
959 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
960 + G_STRUCT_OFFSET (struct thread_local_freelists,
962 else if (atype == ATYPE_NORMAL)
963 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
964 + G_STRUCT_OFFSET (struct thread_local_freelists,
966 else if (atype == ATYPE_GCJ)
967 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
968 + G_STRUCT_OFFSET (struct thread_local_freelists,
971 g_assert_not_reached ();
972 mono_mb_emit_byte (mb, MONO_CEE_ADD);
973 mono_mb_emit_ldloc (mb, index_var);
974 mono_mb_emit_byte (mb, MONO_CEE_ADD);
975 mono_mb_emit_stloc (mb, my_fl_var);
977 /* my_entry = *my_fl; */
978 mono_mb_emit_ldloc (mb, my_fl_var);
979 mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
980 mono_mb_emit_stloc (mb, my_entry_var);
982 /* if (EXPECT((word)my_entry >= HBLKSIZE, 1)) { */
983 mono_mb_emit_ldloc (mb, my_entry_var);
984 mono_mb_emit_icon (mb, HBLKSIZE);
985 no_freelist_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
987 /* ptr_t next = obj_link(my_entry); *my_fl = next; */
988 mono_mb_emit_ldloc (mb, my_fl_var);
989 mono_mb_emit_ldloc (mb, my_entry_var);
990 mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
991 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
993 /* set the vtable and clear the words in the object */
994 mono_mb_emit_ldloc (mb, my_entry_var);
995 mono_mb_emit_ldarg (mb, 0);
996 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
998 if (atype == ATYPE_FREEPTR) {
999 int start_var, end_var, start_loop;
1000 /* end = my_entry + bytes; start = my_entry + sizeof (gpointer);
1002 start_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1003 end_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1004 mono_mb_emit_ldloc (mb, my_entry_var);
1005 mono_mb_emit_ldloc (mb, bytes_var);
1006 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1007 mono_mb_emit_stloc (mb, end_var);
1008 mono_mb_emit_ldloc (mb, my_entry_var);
1009 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
1010 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1011 mono_mb_emit_stloc (mb, start_var);
1015 * } while (start < end);
1017 start_loop = mono_mb_get_label (mb);
1018 mono_mb_emit_ldloc (mb, start_var);
1019 mono_mb_emit_icon (mb, 0);
1020 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1021 mono_mb_emit_ldloc (mb, start_var);
1022 mono_mb_emit_icon (mb, sizeof (gpointer));
1023 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1024 mono_mb_emit_stloc (mb, start_var);
1026 mono_mb_emit_ldloc (mb, start_var);
1027 mono_mb_emit_ldloc (mb, end_var);
1028 mono_mb_emit_byte (mb, MONO_CEE_BLT_UN_S);
1029 mono_mb_emit_byte (mb, start_loop - (mono_mb_get_label (mb) + 1));
1030 } else if (atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING) {
1031 /* need to clear just the sync pointer */
1032 mono_mb_emit_ldloc (mb, my_entry_var);
1033 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
1034 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1035 mono_mb_emit_icon (mb, 0);
1036 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1039 if (atype == ATYPE_STRING) {
1040 /* need to set length and clear the last char */
1041 /* s->length = len; */
1042 mono_mb_emit_ldloc (mb, my_entry_var);
1043 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, length));
1044 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1045 mono_mb_emit_ldarg (mb, 1);
1046 mono_mb_emit_byte (mb, MONO_CEE_STIND_I4);
1047 /* s->chars [len] = 0; */
1048 mono_mb_emit_ldloc (mb, my_entry_var);
1049 mono_mb_emit_ldloc (mb, bytes_var);
1050 mono_mb_emit_icon (mb, 2);
1051 mono_mb_emit_byte (mb, MONO_CEE_SUB);
1052 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1053 mono_mb_emit_icon (mb, 0);
1054 mono_mb_emit_byte (mb, MONO_CEE_STIND_I2);
1057 /* return my_entry; */
1058 mono_mb_emit_ldloc (mb, my_entry_var);
1059 mono_mb_emit_byte (mb, MONO_CEE_RET);
1061 mono_mb_patch_short_branch (mb, no_freelist_branch);
1062 if (not_small_enough_branch > 0)
1063 mono_mb_patch_short_branch (mb, not_small_enough_branch);
1064 if (size_overflow_branch > 0)
1065 mono_mb_patch_short_branch (mb, size_overflow_branch);
1067 /* the slow path: we just call back into the runtime */
1069 if (atype == ATYPE_STRING) {
1070 mono_mb_emit_ldarg (mb, 1);
1071 mono_mb_emit_icall (mb, ves_icall_string_alloc);
1073 mono_mb_emit_ldarg (mb, 0);
1074 mono_mb_emit_icall (mb, ves_icall_object_new_specific);
1077 mono_mb_emit_byte (mb, MONO_CEE_RET);
1079 info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE);
1080 info->d.alloc.gc_name = "boehm";
1081 info->d.alloc.alloc_type = atype;
1082 mb->init_locals = FALSE;
1084 res = mono_mb_create (mb, csig, 8, info);
1090 static MonoMethod* alloc_method_cache [ATYPE_NUM];
1091 static MonoMethod* slowpath_alloc_method_cache [ATYPE_NUM];
1093 static G_GNUC_UNUSED gboolean
1094 mono_gc_is_critical_method (MonoMethod *method)
1098 for (i = 0; i < ATYPE_NUM; ++i)
1099 if (method == alloc_method_cache [i] || method == slowpath_alloc_method_cache [i])
1106 * If possible, generate a managed method that can quickly allocate objects in class
1107 * @klass. The method will typically have an thread-local inline allocation sequence.
1108 * The signature of the called method is:
1109 * object allocate (MonoVTable *vtable)
1110 * Some of the logic here is similar to mono_class_get_allocation_ftn () i object.c,
1112 * The thread local alloc logic is taken from libgc/pthread_support.c.
1116 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size)
1120 MONO_THREAD_VAR_OFFSET (GC_thread_tls, offset);
1122 /*g_print ("thread tls: %d\n", offset);*/
1125 if (!SMALL_ENOUGH (klass->instance_size))
1127 if (mono_class_has_finalizer (klass) || mono_class_is_marshalbyref (klass) || (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS))
1131 if (mono_class_is_open_constructed_type (&klass->byval_arg))
1133 if (klass->byval_arg.type == MONO_TYPE_STRING) {
1134 atype = ATYPE_STRING;
1135 } else if (!known_instance_size) {
1137 } else if (!klass->has_references) {
1139 atype = ATYPE_FREEPTR_FOR_BOX;
1141 atype = ATYPE_FREEPTR;
1145 * disabled because we currently do a runtime choice anyway, to
1146 * deal with multiple appdomains.
1147 if (vtable->gc_descr != GC_NO_DESCRIPTOR)
1150 atype = ATYPE_NORMAL;
1153 return mono_gc_get_managed_allocator_by_type (atype, FALSE);
1157 mono_gc_get_managed_array_allocator (MonoClass *klass)
1163 * mono_gc_get_managed_allocator_by_type:
1165 * Return a managed allocator method corresponding to allocator type ATYPE.
1168 mono_gc_get_managed_allocator_by_type (int atype, gboolean slowpath)
1172 MonoMethod **cache = slowpath ? slowpath_alloc_method_cache : alloc_method_cache;
1173 MONO_THREAD_VAR_OFFSET (GC_thread_tls, offset);
1175 mono_tls_key_set_offset (TLS_KEY_BOEHM_GC_THREAD, offset);
1177 res = cache [atype];
1181 res = create_allocator (atype, TLS_KEY_BOEHM_GC_THREAD, slowpath);
1182 mono_os_mutex_lock (&mono_gc_lock);
1183 if (cache [atype]) {
1184 mono_free_method (res);
1185 res = cache [atype];
1187 mono_memory_barrier ();
1188 cache [atype] = res;
1190 mono_os_mutex_unlock (&mono_gc_lock);
1195 mono_gc_get_managed_allocator_types (void)
1201 mono_gc_get_write_barrier (void)
1203 g_assert_not_reached ();
1209 static G_GNUC_UNUSED gboolean
1210 mono_gc_is_critical_method (MonoMethod *method)
1216 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size)
1222 mono_gc_get_managed_array_allocator (MonoClass *klass)
1228 mono_gc_get_managed_allocator_by_type (int atype, gboolean slowpath)
1234 mono_gc_get_managed_allocator_types (void)
1240 mono_gc_get_write_barrier (void)
1242 g_assert_not_reached ();
1249 mono_gc_get_specific_write_barrier (gboolean is_concurrent)
1251 g_assert_not_reached ();
1256 mono_gc_get_aligned_size_for_allocator (int size)
1262 mono_gc_get_gc_name (void)
1268 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
1270 return GC_call_with_alloc_lock (func, data);
1274 mono_gc_get_description (void)
1276 return g_strdup (DEFAULT_GC_NAME);
1280 mono_gc_set_desktop_mode (void)
1286 mono_gc_is_moving (void)
1292 mono_gc_is_disabled (void)
1294 if (GC_dont_gc || g_getenv ("GC_DONT_GC"))
1301 mono_gc_wbarrier_value_copy_bitmap (gpointer _dest, gpointer _src, int size, unsigned bitmap)
1303 g_assert_not_reached ();
1308 mono_gc_get_card_table (int *shift_bits, gpointer *card_mask)
1310 g_assert_not_reached ();
1315 mono_gc_card_table_nursery_check (void)
1317 g_assert_not_reached ();
1322 mono_gc_get_nursery (int *shift_bits, size_t *size)
1328 mono_gc_set_current_thread_appdomain (MonoDomain *domain)
1333 mono_gc_precise_stack_mark_enabled (void)
1339 mono_gc_get_logfile (void)
1345 mono_gc_conservatively_scan_area (void *start, void *end)
1347 g_assert_not_reached ();
1351 mono_gc_scan_object (void *obj, void *gc_data)
1353 g_assert_not_reached ();
1358 mono_gc_get_bitmap_for_descr (void *descr, int *numbits)
1360 g_assert_not_reached ();
1365 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
1370 mono_gc_set_stack_end (void *stack_end)
1374 void mono_gc_set_skip_thread (gboolean value)
1379 mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
1384 /* This assertion is not valid when GC_DEBUG is defined */
1385 g_assert (GC_base (obj) == (char*)obj - offset);
1388 GC_REGISTER_FINALIZER_NO_ORDER ((char*)obj - offset, (GC_finalization_proc)user_data, GUINT_TO_POINTER (offset), NULL, NULL);
1393 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
1395 /* it is being replaced by GC_pthread_create on some
1396 * platforms, see libgc/include/gc_pthread_redirects.h */
1397 return pthread_create (new_thread, attr, start_routine, arg);
1402 BOOL APIENTRY mono_gc_dllmain (HMODULE module_handle, DWORD reason, LPVOID reserved)
1404 return GC_DllMain (module_handle, reason, reserved);
1409 mono_gc_get_vtable_bits (MonoClass *klass)
1411 if (fin_callbacks.is_class_finalization_aware) {
1412 if (fin_callbacks.is_class_finalization_aware (klass))
1413 return BOEHM_GC_BIT_FINALIZER_AWARE;
1419 * mono_gc_register_altstack:
1421 * Register the dimensions of the normal stack and altstack with the collector.
1422 * Currently, STACK/STACK_SIZE is only used when the thread is suspended while it is on an altstack.
1425 mono_gc_register_altstack (gpointer stack, gint32 stack_size, gpointer altstack, gint32 altstack_size)
1427 GC_register_altstack (stack, stack_size, altstack, altstack_size);
1431 mono_gc_get_los_limit (void)
1437 mono_gc_set_string_length (MonoString *str, gint32 new_length)
1439 mono_unichar2 *new_end = str->chars + new_length;
1441 /* zero the discarded string. This null-delimits the string and allows
1442 * the space to be reclaimed by SGen. */
1444 memset (new_end, 0, (str->length - new_length + 1) * sizeof (mono_unichar2));
1445 str->length = new_length;
1449 mono_gc_user_markers_supported (void)
1455 mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
1457 g_assert_not_reached ();
1461 /* Toggleref support */
1464 mono_gc_toggleref_add (MonoObject *object, mono_bool strong_ref)
1466 if (GC_toggleref_add ((GC_PTR)object, (int)strong_ref) != GC_SUCCESS)
1467 g_error ("GC_toggleref_add failed\n");
1471 mono_gc_toggleref_register_callback (MonoToggleRefStatus (*proccess_toggleref) (MonoObject *obj))
1473 GC_set_toggleref_func ((GC_ToggleRefStatus (*) (GC_PTR obj)) proccess_toggleref);
1476 /* Test support code */
1478 static MonoToggleRefStatus
1479 test_toggleref_callback (MonoObject *obj)
1481 static MonoClassField *mono_toggleref_test_field;
1482 MonoToggleRefStatus status = MONO_TOGGLE_REF_DROP;
1484 if (!mono_toggleref_test_field) {
1485 mono_toggleref_test_field = mono_class_get_field_from_name (mono_object_get_class (obj), "__test");
1486 g_assert (mono_toggleref_test_field);
1489 mono_field_get_value (obj, mono_toggleref_test_field, &status);
1490 printf ("toggleref-cb obj %d\n", status);
1495 register_test_toggleref_callback (void)
1497 mono_gc_toggleref_register_callback (test_toggleref_callback);
1501 is_finalization_aware (MonoObject *obj)
1503 MonoVTable *vt = obj->vtable;
1504 return (vt->gc_bits & BOEHM_GC_BIT_FINALIZER_AWARE) == BOEHM_GC_BIT_FINALIZER_AWARE;
1508 fin_notifier (MonoObject *obj)
1510 if (is_finalization_aware (obj))
1511 fin_callbacks.object_queued_for_finalization (obj);
1515 mono_gc_register_finalizer_callbacks (MonoGCFinalizerCallbacks *callbacks)
1517 if (callbacks->version != MONO_GC_FINALIZER_EXTENSION_VERSION)
1518 g_error ("Invalid finalizer callback version. Expected %d but got %d\n", MONO_GC_FINALIZER_EXTENSION_VERSION, callbacks->version);
1520 fin_callbacks = *callbacks;
1522 GC_set_await_finalize_proc ((void (*) (GC_PTR))fin_notifier);
1525 #define BITMAP_SIZE (sizeof (*((HandleData *)NULL)->bitmap) * CHAR_BIT)
1527 static inline gboolean
1528 slot_occupied (HandleData *handles, guint slot) {
1529 return handles->bitmap [slot / BITMAP_SIZE] & (1 << (slot % BITMAP_SIZE));
1533 vacate_slot (HandleData *handles, guint slot) {
1534 handles->bitmap [slot / BITMAP_SIZE] &= ~(1 << (slot % BITMAP_SIZE));
1538 occupy_slot (HandleData *handles, guint slot) {
1539 handles->bitmap [slot / BITMAP_SIZE] |= 1 << (slot % BITMAP_SIZE);
1543 find_first_unset (guint32 bitmap)
1546 for (i = 0; i < 32; ++i) {
1547 if (!(bitmap & (1 << i)))
1554 handle_data_alloc_entries (HandleData *handles)
1557 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1558 handles->entries = (void **)g_malloc0 (sizeof (*handles->entries) * handles->size);
1559 handles->domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * handles->size);
1561 handles->entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * handles->size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
1563 handles->bitmap = (guint32 *)g_malloc0 (handles->size / CHAR_BIT);
1567 handle_data_next_unset (HandleData *handles)
1570 for (slot = handles->slot_hint; slot < handles->size / BITMAP_SIZE; ++slot) {
1571 if (handles->bitmap [slot] == 0xffffffff)
1573 handles->slot_hint = slot;
1574 return find_first_unset (handles->bitmap [slot]);
1580 handle_data_first_unset (HandleData *handles)
1583 for (slot = 0; slot < handles->slot_hint; ++slot) {
1584 if (handles->bitmap [slot] == 0xffffffff)
1586 handles->slot_hint = slot;
1587 return find_first_unset (handles->bitmap [slot]);
1592 /* Returns the index of the current slot in the bitmap. */
1594 handle_data_grow (HandleData *handles, gboolean track)
1596 guint32 *new_bitmap;
1597 guint32 new_size = handles->size * 2; /* always double: we memset to 0 based on this below */
1599 /* resize and copy the bitmap */
1600 new_bitmap = (guint32 *)g_malloc0 (new_size / CHAR_BIT);
1601 memcpy (new_bitmap, handles->bitmap, handles->size / CHAR_BIT);
1602 g_free (handles->bitmap);
1603 handles->bitmap = new_bitmap;
1605 /* resize and copy the entries */
1606 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1608 guint16 *domain_ids;
1610 domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * new_size);
1611 entries = (void **)g_malloc0 (sizeof (*handles->entries) * new_size);
1612 memcpy (domain_ids, handles->domain_ids, sizeof (*handles->domain_ids) * handles->size);
1613 for (i = 0; i < handles->size; ++i) {
1614 MonoObject *obj = mono_gc_weak_link_get (&(handles->entries [i]));
1616 mono_gc_weak_link_add (&(entries [i]), obj, track);
1617 mono_gc_weak_link_remove (&(handles->entries [i]), track);
1619 g_assert (!handles->entries [i]);
1622 g_free (handles->entries);
1623 g_free (handles->domain_ids);
1624 handles->entries = entries;
1625 handles->domain_ids = domain_ids;
1628 entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * new_size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
1629 mono_gc_memmove_aligned (entries, handles->entries, sizeof (*handles->entries) * handles->size);
1630 mono_gc_free_fixed (handles->entries);
1631 handles->entries = entries;
1633 handles->slot_hint = handles->size / BITMAP_SIZE;
1634 handles->size = new_size;
1638 alloc_handle (HandleData *handles, MonoObject *obj, gboolean track)
1642 lock_handles (handles);
1644 handle_data_alloc_entries (handles);
1645 i = handle_data_next_unset (handles);
1646 if (i == -1 && handles->slot_hint != 0)
1647 i = handle_data_first_unset (handles);
1649 handle_data_grow (handles, track);
1652 slot = handles->slot_hint * BITMAP_SIZE + i;
1653 occupy_slot (handles, slot);
1654 handles->entries [slot] = NULL;
1655 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1656 /*FIXME, what to use when obj == null?*/
1657 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
1659 mono_gc_weak_link_add (&(handles->entries [slot]), obj, track);
1661 handles->entries [slot] = obj;
1664 #ifndef DISABLE_PERFCOUNTERS
1665 mono_perfcounters->gc_num_handles++;
1667 unlock_handles (handles);
1668 res = MONO_GC_HANDLE (slot, handles->type);
1669 mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_CREATED, handles->type, res, obj);
1674 * mono_gchandle_new:
1675 * @obj: managed object to get a handle for
1676 * @pinned: whether the object should be pinned
1678 * This returns a handle that wraps the object, this is used to keep a
1679 * reference to a managed object from the unmanaged world and preventing the
1680 * object from being disposed.
1682 * If @pinned is false the address of the object can not be obtained, if it is
1683 * true the address of the object can be obtained. This will also pin the
1684 * object so it will not be possible by a moving garbage collector to move the
1687 * Returns: a handle that can be used to access the object from
1691 mono_gchandle_new (MonoObject *obj, gboolean pinned)
1693 return alloc_handle (&gc_handles [pinned? HANDLE_PINNED: HANDLE_NORMAL], obj, FALSE);
1697 * mono_gchandle_new_weakref:
1698 * @obj: managed object to get a handle for
1699 * @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.
1701 * This returns a weak handle that wraps the object, this is used to
1702 * keep a reference to a managed object from the unmanaged world.
1703 * Unlike the mono_gchandle_new the object can be reclaimed by the
1704 * garbage collector. In this case the value of the GCHandle will be
1707 * If @track_resurrection is TRUE the object will be tracked through
1708 * finalization and if the object is resurrected during the execution
1709 * of the finalizer, then the returned weakref will continue to hold
1710 * a reference to the object. If @track_resurrection is FALSE, then
1711 * the weak reference's target will become NULL as soon as the object
1712 * is passed on to the finalizer.
1714 * Returns: a handle that can be used to access the object from
1718 mono_gchandle_new_weakref (MonoObject *obj, gboolean track_resurrection)
1720 return alloc_handle (&gc_handles [track_resurrection? HANDLE_WEAK_TRACK: HANDLE_WEAK], obj, track_resurrection);
1724 * mono_gchandle_get_target:
1725 * @gchandle: a GCHandle's handle.
1727 * The handle was previously created by calling `mono_gchandle_new` or
1728 * `mono_gchandle_new_weakref`.
1730 * Returns: A pointer to the `MonoObject*` represented by the handle or
1731 * NULL for a collected object if using a weakref handle.
1734 mono_gchandle_get_target (guint32 gchandle)
1736 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1737 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1738 HandleData *handles = &gc_handles [type];
1739 MonoObject *obj = NULL;
1740 if (type >= HANDLE_TYPE_MAX)
1743 lock_handles (handles);
1744 if (slot < handles->size && slot_occupied (handles, slot)) {
1745 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1746 obj = mono_gc_weak_link_get (&handles->entries [slot]);
1748 obj = (MonoObject *)handles->entries [slot];
1751 /* print a warning? */
1753 unlock_handles (handles);
1754 /*g_print ("get target of entry %d of type %d: %p\n", slot, handles->type, obj);*/
1759 mono_gchandle_set_target (guint32 gchandle, MonoObject *obj)
1761 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1762 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1763 HandleData *handles = &gc_handles [type];
1764 MonoObject *old_obj = NULL;
1766 g_assert (type < HANDLE_TYPE_MAX);
1767 lock_handles (handles);
1768 if (slot < handles->size && slot_occupied (handles, slot)) {
1769 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1770 old_obj = (MonoObject *)handles->entries [slot];
1771 if (handles->entries [slot])
1772 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1774 mono_gc_weak_link_add (&handles->entries [slot], obj, handles->type == HANDLE_WEAK_TRACK);
1775 /*FIXME, what to use when obj == null?*/
1776 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
1778 handles->entries [slot] = obj;
1781 /* print a warning? */
1783 /*g_print ("changed entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
1784 unlock_handles (handles);
1788 mono_gc_is_null (void)
1794 * mono_gchandle_is_in_domain:
1795 * @gchandle: a GCHandle's handle.
1796 * @domain: An application domain.
1798 * Use this function to determine if the @gchandle points to an
1799 * object allocated in the specified @domain.
1801 * Returns: TRUE if the object wrapped by the @gchandle belongs to the specific @domain.
1804 mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain)
1806 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1807 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1808 HandleData *handles = &gc_handles [type];
1809 gboolean result = FALSE;
1811 if (type >= HANDLE_TYPE_MAX)
1814 lock_handles (handles);
1815 if (slot < handles->size && slot_occupied (handles, slot)) {
1816 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1817 result = domain->domain_id == handles->domain_ids [slot];
1820 obj = (MonoObject *)handles->entries [slot];
1824 result = domain == mono_object_domain (obj);
1827 /* print a warning? */
1829 unlock_handles (handles);
1834 * mono_gchandle_free:
1835 * @gchandle: a GCHandle's handle.
1837 * Frees the @gchandle handle. If there are no outstanding
1838 * references, the garbage collector can reclaim the memory of the
1842 mono_gchandle_free (guint32 gchandle)
1844 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1845 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1846 HandleData *handles = &gc_handles [type];
1847 if (type >= HANDLE_TYPE_MAX)
1850 lock_handles (handles);
1851 if (slot < handles->size && slot_occupied (handles, slot)) {
1852 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1853 if (handles->entries [slot])
1854 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1856 handles->entries [slot] = NULL;
1858 vacate_slot (handles, slot);
1860 /* print a warning? */
1862 #ifndef DISABLE_PERFCOUNTERS
1863 mono_perfcounters->gc_num_handles--;
1865 /*g_print ("freed entry %d of type %d\n", slot, handles->type);*/
1866 unlock_handles (handles);
1867 mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_DESTROYED, handles->type, gchandle, NULL);
1871 * mono_gchandle_free_domain:
1872 * @domain: domain that is unloading
1874 * Function used internally to cleanup any GC handle for objects belonging
1875 * to the specified domain during appdomain unload.
1878 mono_gchandle_free_domain (MonoDomain *domain)
1882 for (type = HANDLE_TYPE_MIN; type < HANDLE_PINNED; ++type) {
1884 HandleData *handles = &gc_handles [type];
1885 lock_handles (handles);
1886 for (slot = 0; slot < handles->size; ++slot) {
1887 if (!slot_occupied (handles, slot))
1889 if (MONO_GC_HANDLE_TYPE_IS_WEAK (type)) {
1890 if (domain->domain_id == handles->domain_ids [slot]) {
1891 vacate_slot (handles, slot);
1892 if (handles->entries [slot])
1893 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1896 if (handles->entries [slot] && mono_object_domain (handles->entries [slot]) == domain) {
1897 vacate_slot (handles, slot);
1898 handles->entries [slot] = NULL;
1902 unlock_handles (handles);
1907 #endif /* no Boehm GC */