2 * boehm-gc.c: GC implementation using either the installed or included Boehm GC.
4 * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
5 * Copyright 2004-2011 Novell, Inc (http://www.novell.com)
6 * Copyright 2011-2012 Xamarin, Inc (http://www.xamarin.com)
7 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
14 #define GC_I_HIDE_POINTERS
15 #include <mono/metadata/gc-internals.h>
16 #include <mono/metadata/mono-gc.h>
17 #include <mono/metadata/profiler-private.h>
18 #include <mono/metadata/class-internals.h>
19 #include <mono/metadata/method-builder.h>
20 #include <mono/metadata/opcodes.h>
21 #include <mono/metadata/domain-internals.h>
22 #include <mono/metadata/metadata-internals.h>
23 #include <mono/metadata/marshal.h>
24 #include <mono/metadata/runtime.h>
25 #include <mono/metadata/handle.h>
26 #include <mono/metadata/sgen-toggleref.h>
27 #include <mono/metadata/w32handle.h>
28 #include <mono/utils/atomic.h>
29 #include <mono/utils/mono-logger-internals.h>
30 #include <mono/utils/mono-memory-model.h>
31 #include <mono/utils/mono-time.h>
32 #include <mono/utils/mono-threads.h>
33 #include <mono/utils/dtrace.h>
34 #include <mono/utils/gc_wrapper.h>
35 #include <mono/utils/mono-os-mutex.h>
36 #include <mono/utils/mono-counters.h>
37 #include <mono/utils/mono-compiler.h>
43 #define THREAD_LOCAL_ALLOC 1
44 #include "private/pthread_support.h"
46 #if defined(PLATFORM_MACOSX) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
47 void *pthread_get_stackaddr_np(pthread_t);
50 #define GC_NO_DESCRIPTOR ((gpointer)(0 | GC_DS_LENGTH))
51 /*Boehm max heap cannot be smaller than 16MB*/
52 #define MIN_BOEHM_MAX_HEAP_SIZE_IN_MB 16
53 #define MIN_BOEHM_MAX_HEAP_SIZE (MIN_BOEHM_MAX_HEAP_SIZE_IN_MB << 20)
55 static gboolean gc_initialized = FALSE;
56 static mono_mutex_t mono_gc_lock;
59 boehm_thread_register (MonoThreadInfo* info, void *baseptr);
61 boehm_thread_unregister (MonoThreadInfo *p);
63 boehm_thread_detach (MonoThreadInfo *p);
65 register_test_toggleref_callback (void);
67 #define BOEHM_GC_BIT_FINALIZER_AWARE 1
68 static MonoGCFinalizerCallbacks fin_callbacks;
72 static mono_mutex_t handle_section;
73 #define lock_handles(handles) mono_os_mutex_lock (&handle_section)
74 #define unlock_handles(handles) mono_os_mutex_unlock (&handle_section)
81 guint slot_hint : 24; /* starting slot for search in bitmap */
82 /* 2^16 appdomains should be enough for everyone (though I know I'll regret this in 20 years) */
83 /* we alloc this only for weak refs, since we can get the domain directly in the other cases */
87 #define EMPTY_HANDLE_DATA(type) {NULL, NULL, 0, (type), 0, NULL}
89 /* weak and weak-track arrays will be allocated in malloc memory
91 static HandleData gc_handles [] = {
92 EMPTY_HANDLE_DATA (HANDLE_WEAK),
93 EMPTY_HANDLE_DATA (HANDLE_WEAK_TRACK),
94 EMPTY_HANDLE_DATA (HANDLE_NORMAL),
95 EMPTY_HANDLE_DATA (HANDLE_PINNED)
99 mono_gc_warning (char *msg, GC_word arg)
101 mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_GC, msg, (unsigned long)arg);
104 static void on_gc_notification (GC_EventType event);
105 static void on_gc_heap_resize (size_t new_size);
108 mono_gc_base_init (void)
110 MonoThreadInfoCallbacks cb;
117 mono_counters_init ();
120 mono_w32handle_init ();
124 * Handle the case when we are called from a thread different from the main thread,
126 * FIXME: Move this to libgc where it belongs.
128 * we used to do this only when running on valgrind,
129 * but it happens also in other setups.
131 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) && !defined(__native_client__)
136 pthread_getattr_np (pthread_self (), &attr);
137 pthread_attr_getstack (&attr, &sstart, &size);
138 pthread_attr_destroy (&attr);
139 /*g_print ("stackbottom pth is: %p\n", (char*)sstart + size);*/
142 * The calculation above doesn't seem to work on ia64, also we need to set
143 * GC_register_stackbottom as well, but don't know how.
146 /* apparently with some linuxthreads implementations sstart can be NULL,
147 * fallback to the more imprecise method (bug# 78096).
150 GC_stackbottom = (char*)sstart + size;
153 gsize stack_bottom = (gsize)&dummy;
154 stack_bottom += 4095;
155 stack_bottom &= ~4095;
156 GC_stackbottom = (char*)stack_bottom;
160 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
161 GC_stackbottom = (char*)pthread_get_stackaddr_np (pthread_self ());
162 #elif defined(__OpenBSD__)
163 # include <pthread_np.h>
168 rslt = pthread_stackseg_np(pthread_self(), &ss);
169 g_assert (rslt == 0);
171 GC_stackbottom = (char*)ss.ss_sp;
173 #elif defined(__native_client__)
174 /* Do nothing, GC_stackbottom is set correctly in libgc */
178 gsize stack_bottom = (gsize)&dummy;
179 stack_bottom += 4095;
180 stack_bottom &= ~4095;
181 /*g_print ("stackbottom is: %p\n", (char*)stack_bottom);*/
182 GC_stackbottom = (char*)stack_bottom;
186 #if !defined(PLATFORM_ANDROID)
187 /* If GC_no_dls is set to true, GC_find_limit is not called. This causes a seg fault on Android. */
191 if ((env = g_getenv ("MONO_GC_DEBUG"))) {
192 char **opts = g_strsplit (env, ",", -1);
193 for (char **ptr = opts; ptr && *ptr; ptr ++) {
195 if (!strcmp (opt, "do-not-finalize")) {
196 mono_do_not_finalize = 1;
197 } else if (!strcmp (opt, "log-finalizers")) {
206 GC_set_warn_proc (mono_gc_warning);
207 GC_finalize_on_demand = 1;
208 GC_finalizer_notifier = mono_gc_finalize_notify;
210 GC_init_gcj_malloc (5, NULL);
211 GC_allow_register_threads ();
213 if ((env = g_getenv ("MONO_GC_PARAMS"))) {
214 char **ptr, **opts = g_strsplit (env, ",", -1);
215 for (ptr = opts; *ptr; ++ptr) {
217 if (g_str_has_prefix (opt, "max-heap-size=")) {
220 opt = strchr (opt, '=') + 1;
221 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &max_heap)) {
222 if (max_heap < MIN_BOEHM_MAX_HEAP_SIZE) {
223 fprintf (stderr, "max-heap-size must be at least %dMb.\n", MIN_BOEHM_MAX_HEAP_SIZE_IN_MB);
226 GC_set_max_heap_size (max_heap);
228 fprintf (stderr, "max-heap-size must be an integer.\n");
232 } else if (g_str_has_prefix (opt, "toggleref-test")) {
233 register_test_toggleref_callback ();
236 /* Could be a parameter for sgen */
238 fprintf (stderr, "MONO_GC_PARAMS must be a comma-delimited list of one or more of the following:\n");
239 fprintf (stderr, " max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
247 memset (&cb, 0, sizeof (cb));
248 cb.thread_register = boehm_thread_register;
249 cb.thread_unregister = boehm_thread_unregister;
250 cb.thread_detach = boehm_thread_detach;
251 cb.mono_method_is_critical = (gboolean (*)(void *))mono_runtime_is_critical_method;
253 mono_threads_init (&cb, sizeof (MonoThreadInfo));
254 mono_os_mutex_init (&mono_gc_lock);
255 mono_os_mutex_init_recursive (&handle_section);
257 mono_thread_info_attach (&dummy);
259 GC_set_on_collection_event (on_gc_notification);
260 GC_on_heap_resize = on_gc_heap_resize;
262 gc_initialized = TRUE;
266 mono_gc_base_cleanup (void)
268 GC_finalizer_notifier = NULL;
273 * @generation: GC generation identifier
275 * Perform a garbage collection for the given generation, higher numbers
276 * mean usually older objects. Collecting a high-numbered generation
277 * implies collecting also the lower-numbered generations.
278 * The maximum value for @generation can be retrieved with a call to
279 * mono_gc_max_generation(), so this function is usually called as:
281 * mono_gc_collect (mono_gc_max_generation ());
284 mono_gc_collect (int generation)
286 #ifndef DISABLE_PERFCOUNTERS
287 mono_perfcounters->gc_induced++;
293 * mono_gc_max_generation:
295 * Get the maximum generation number used by the current garbage
296 * collector. The value will be 0 for the Boehm collector, 1 or more
297 * for the generational collectors.
299 * Returns: the maximum generation number.
302 mono_gc_max_generation (void)
308 * mono_gc_get_generation:
309 * @object: a managed object
311 * Get the garbage collector's generation that @object belongs to.
312 * Use this has a hint only.
314 * Returns: a garbage collector generation number
317 mono_gc_get_generation (MonoObject *object)
323 * mono_gc_collection_count:
324 * @generation: a GC generation number
326 * Get how many times a garbage collection has been performed
327 * for the given @generation number.
329 * Returns: the number of garbage collections
332 mono_gc_collection_count (int generation)
338 * mono_gc_add_memory_pressure:
339 * @value: amount of bytes
341 * Adjust the garbage collector's view of how many bytes of memory
342 * are indirectly referenced by managed objects (for example unmanaged
343 * memory holding image or other binary data).
344 * This is a hint only to the garbage collector algorithm.
345 * Note that negative amounts of @value will decrease the memory
349 mono_gc_add_memory_pressure (gint64 value)
354 * mono_gc_get_used_size:
356 * Get the approximate amount of memory used by managed objects.
358 * Returns: the amount of memory used in bytes
361 mono_gc_get_used_size (void)
363 return GC_get_heap_size () - GC_get_free_bytes ();
367 * mono_gc_get_heap_size:
369 * Get the amount of memory used by the garbage collector.
371 * Returns: the size of the heap in bytes
374 mono_gc_get_heap_size (void)
376 return GC_get_heap_size ();
380 mono_gc_is_gc_thread (void)
382 return GC_thread_is_registered ();
386 mono_gc_register_thread (void *baseptr)
388 return mono_thread_info_attach (baseptr) != NULL;
392 boehm_thread_register (MonoThreadInfo* info, void *baseptr)
394 struct GC_stack_base sb;
397 /* TODO: use GC_get_stack_base instead of baseptr. */
398 sb.mem_base = baseptr;
399 res = GC_register_my_thread (&sb);
400 if (res == GC_UNIMPLEMENTED)
401 return NULL; /* Cannot happen with GC v7+. */
403 info->handle_stack = mono_handle_stack_alloc ();
409 boehm_thread_unregister (MonoThreadInfo *p)
411 MonoNativeThreadId tid;
413 tid = mono_thread_info_get_tid (p);
415 if (p->runtime_thread)
416 mono_threads_add_joinable_thread ((gpointer)tid);
420 boehm_thread_detach (MonoThreadInfo *p)
422 if (mono_thread_internal_current_is_attached ())
423 mono_thread_detach_internal (mono_thread_internal_current ());
427 mono_object_is_alive (MonoObject* o)
429 return GC_is_marked ((ptr_t)o);
433 mono_gc_walk_heap (int flags, MonoGCReferences callback, void *data)
438 static gint64 gc_start_time;
441 on_gc_notification (GC_EventType event)
443 MonoGCEvent e = (MonoGCEvent)event;
446 case MONO_GC_EVENT_PRE_STOP_WORLD:
447 MONO_GC_WORLD_STOP_BEGIN ();
450 case MONO_GC_EVENT_POST_STOP_WORLD:
451 MONO_GC_WORLD_STOP_END ();
454 case MONO_GC_EVENT_PRE_START_WORLD:
455 MONO_GC_WORLD_RESTART_BEGIN (1);
458 case MONO_GC_EVENT_POST_START_WORLD:
459 MONO_GC_WORLD_RESTART_END (1);
462 case MONO_GC_EVENT_START:
464 #ifndef DISABLE_PERFCOUNTERS
465 if (mono_perfcounters)
466 mono_perfcounters->gc_collections0++;
468 gc_stats.major_gc_count ++;
469 gc_start_time = mono_100ns_ticks ();
472 case MONO_GC_EVENT_END:
474 #if defined(ENABLE_DTRACE) && defined(__sun__)
475 /* This works around a dtrace -G problem on Solaris.
476 Limit its actual use to when the probe is enabled. */
477 if (MONO_GC_END_ENABLED ())
481 #ifndef DISABLE_PERFCOUNTERS
482 if (mono_perfcounters) {
483 guint64 heap_size = GC_get_heap_size ();
484 guint64 used_size = heap_size - GC_get_free_bytes ();
485 mono_perfcounters->gc_total_bytes = used_size;
486 mono_perfcounters->gc_committed_bytes = heap_size;
487 mono_perfcounters->gc_reserved_bytes = heap_size;
488 mono_perfcounters->gc_gen0size = heap_size;
491 gc_stats.major_gc_time += mono_100ns_ticks () - gc_start_time;
492 mono_trace_message (MONO_TRACE_GC, "gc took %d usecs", (mono_100ns_ticks () - gc_start_time) / 10);
498 mono_profiler_gc_event (e, 0);
501 case MONO_GC_EVENT_PRE_STOP_WORLD:
502 mono_thread_info_suspend_lock ();
503 mono_profiler_gc_event (MONO_GC_EVENT_PRE_STOP_WORLD_LOCKED, 0);
505 case MONO_GC_EVENT_POST_START_WORLD:
506 mono_thread_info_suspend_unlock ();
507 mono_profiler_gc_event (MONO_GC_EVENT_POST_START_WORLD_UNLOCKED, 0);
516 on_gc_heap_resize (size_t new_size)
518 guint64 heap_size = GC_get_heap_size ();
519 #ifndef DISABLE_PERFCOUNTERS
520 if (mono_perfcounters) {
521 mono_perfcounters->gc_committed_bytes = heap_size;
522 mono_perfcounters->gc_reserved_bytes = heap_size;
523 mono_perfcounters->gc_gen0size = heap_size;
526 mono_profiler_gc_heap_resize (new_size);
530 mono_gc_register_root (char *start, size_t size, void *descr, MonoGCRootSource source, const char *msg)
532 /* for some strange reason, they want one extra byte on the end */
533 GC_add_roots (start, start + size + 1);
539 mono_gc_register_root_wbarrier (char *start, size_t size, MonoGCDescriptor descr, MonoGCRootSource source, const char *msg)
541 return mono_gc_register_root (start, size, descr, source, msg);
545 mono_gc_deregister_root (char* addr)
548 /* FIXME: libgc doesn't define this work win32 for some reason */
549 /* FIXME: No size info */
550 GC_remove_roots (addr, addr + sizeof (gpointer) + 1);
555 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
557 /* libgc requires that we use HIDE_POINTER... */
558 *link_addr = (void*)HIDE_POINTER (obj);
560 GC_REGISTER_LONG_LINK (link_addr, obj);
562 GC_GENERAL_REGISTER_DISAPPEARING_LINK (link_addr, obj);
566 mono_gc_weak_link_remove (void **link_addr, gboolean track)
569 GC_unregister_long_link (link_addr);
571 GC_unregister_disappearing_link (link_addr);
576 reveal_link (gpointer link_addr)
578 void **link_a = (void **)link_addr;
579 return REVEAL_POINTER (*link_a);
583 mono_gc_weak_link_get (void **link_addr)
585 MonoObject *obj = (MonoObject *)GC_call_with_alloc_lock (reveal_link, link_addr);
586 if (obj == (MonoObject *) -1)
592 mono_gc_make_descr_for_string (gsize *bitmap, int numbits)
594 return mono_gc_make_descr_from_bitmap (bitmap, numbits);
598 mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size)
600 return mono_gc_make_descr_from_bitmap (bitmap, numbits);
604 mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size)
606 /* libgc has no usable support for arrays... */
607 return GC_NO_DESCRIPTOR;
611 mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
613 /* It seems there are issues when the bitmap doesn't fit: play it safe */
615 return GC_NO_DESCRIPTOR;
617 return (gpointer)GC_make_descriptor ((GC_bitmap)bitmap, numbits);
621 mono_gc_make_vector_descr (void)
627 mono_gc_make_root_descr_all_refs (int numbits)
633 mono_gc_alloc_fixed (size_t size, void *descr, MonoGCRootSource source, const char *msg)
635 return GC_MALLOC_UNCOLLECTABLE (size);
639 mono_gc_free_fixed (void* addr)
645 mono_gc_alloc_obj (MonoVTable *vtable, size_t size)
649 if (!vtable->klass->has_references) {
650 obj = (MonoObject *)GC_MALLOC_ATOMIC (size);
651 if (G_UNLIKELY (!obj))
654 obj->vtable = vtable;
655 obj->synchronisation = NULL;
657 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
658 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
659 obj = (MonoObject *)GC_GCJ_MALLOC (size, vtable);
660 if (G_UNLIKELY (!obj))
663 obj = (MonoObject *)GC_MALLOC (size);
664 if (G_UNLIKELY (!obj))
667 obj->vtable = vtable;
670 if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_ALLOCATIONS))
671 mono_profiler_allocation (obj);
677 mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length)
681 if (!vtable->klass->has_references) {
682 obj = (MonoArray *)GC_MALLOC_ATOMIC (size);
683 if (G_UNLIKELY (!obj))
686 obj->obj.vtable = vtable;
687 obj->obj.synchronisation = NULL;
689 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
690 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
691 obj = (MonoArray *)GC_GCJ_MALLOC (size, vtable);
692 if (G_UNLIKELY (!obj))
695 obj = (MonoArray *)GC_MALLOC (size);
696 if (G_UNLIKELY (!obj))
699 obj->obj.vtable = vtable;
702 obj->max_length = max_length;
704 if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_ALLOCATIONS))
705 mono_profiler_allocation (&obj->obj);
711 mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uintptr_t bounds_size)
715 if (!vtable->klass->has_references) {
716 obj = (MonoArray *)GC_MALLOC_ATOMIC (size);
717 if (G_UNLIKELY (!obj))
720 obj->obj.vtable = vtable;
721 obj->obj.synchronisation = NULL;
723 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
724 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
725 obj = (MonoArray *)GC_GCJ_MALLOC (size, vtable);
726 if (G_UNLIKELY (!obj))
729 obj = (MonoArray *)GC_MALLOC (size);
730 if (G_UNLIKELY (!obj))
733 obj->obj.vtable = vtable;
736 obj->max_length = max_length;
739 obj->bounds = (MonoArrayBounds *) ((char *) obj + size - bounds_size);
741 if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_ALLOCATIONS))
742 mono_profiler_allocation (&obj->obj);
748 mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len)
750 MonoString *obj = (MonoString *)GC_MALLOC_ATOMIC (size);
751 if (G_UNLIKELY (!obj))
754 obj->object.vtable = vtable;
755 obj->object.synchronisation = NULL;
757 obj->chars [len] = 0;
759 if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_ALLOCATIONS))
760 mono_profiler_allocation (&obj->object);
766 mono_gc_alloc_mature (MonoVTable *vtable, size_t size)
768 return mono_gc_alloc_obj (vtable, size);
772 mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size)
774 return mono_gc_alloc_obj (vtable, size);
778 mono_gc_invoke_finalizers (void)
780 /* There is a bug in GC_invoke_finalizer () in versions <= 6.2alpha4:
781 * the 'mem_freed' variable is not initialized when there are no
782 * objects to finalize, which leads to strange behavior later on.
783 * The check is necessary to work around that bug.
785 if (GC_should_invoke_finalizers ())
786 return GC_invoke_finalizers ();
791 mono_gc_pending_finalizers (void)
793 return GC_should_invoke_finalizers ();
797 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
799 *(void**)field_ptr = value;
803 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
805 *(void**)slot_ptr = value;
809 mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
811 mono_gc_memmove_aligned (dest_ptr, src_ptr, count * sizeof (gpointer));
815 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
817 *(void**)ptr = value;
821 mono_gc_wbarrier_generic_store_atomic (gpointer ptr, MonoObject *value)
823 InterlockedWritePointer ((volatile gpointer *)ptr, value);
827 mono_gc_wbarrier_generic_nostore (gpointer ptr)
832 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
834 mono_gc_memmove_atomic (dest, src, count * mono_class_value_size (klass, NULL));
838 mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
840 /* do not copy the sync state */
841 mono_gc_memmove_aligned ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
842 mono_object_class (obj)->instance_size - sizeof (MonoObject));
846 mono_gc_clear_domain (MonoDomain *domain)
851 mono_gc_suspend_finalizers (void)
856 mono_gc_get_suspend_signal (void)
858 return GC_get_suspend_signal ();
862 mono_gc_get_restart_signal (void)
864 return GC_get_thr_restart_signal ();
867 #if defined(USE_COMPILER_TLS) && defined(__linux__) && (defined(__i386__) || defined(__x86_64__))
868 extern __thread void* GC_thread_tls;
869 #include "metadata-internals.h"
875 while (!(v & (1 << i)))
882 ATYPE_FREEPTR_FOR_BOX,
890 create_allocator (int atype, int tls_key, gboolean slowpath)
892 int index_var, bytes_var, my_fl_var, my_entry_var;
893 guint32 no_freelist_branch, not_small_enough_branch = 0;
894 guint32 size_overflow_branch = 0;
895 MonoMethodBuilder *mb;
897 MonoMethodSignature *csig;
898 const char *name = NULL;
901 g_assert_not_reached ();
903 if (atype == ATYPE_FREEPTR) {
904 name = slowpath ? "SlowAllocPtrfree" : "AllocPtrfree";
905 } else if (atype == ATYPE_FREEPTR_FOR_BOX) {
906 name = slowpath ? "SlowAllocPtrfreeBox" : "AllocPtrfreeBox";
907 } else if (atype == ATYPE_NORMAL) {
908 name = slowpath ? "SlowAlloc" : "Alloc";
909 } else if (atype == ATYPE_GCJ) {
910 name = slowpath ? "SlowAllocGcj" : "AllocGcj";
911 } else if (atype == ATYPE_STRING) {
912 name = slowpath ? "SlowAllocString" : "AllocString";
914 g_assert_not_reached ();
917 csig = mono_metadata_signature_alloc (mono_defaults.corlib, 2);
919 if (atype == ATYPE_STRING) {
920 csig->ret = &mono_defaults.string_class->byval_arg;
921 csig->params [0] = &mono_defaults.int_class->byval_arg;
922 csig->params [1] = &mono_defaults.int32_class->byval_arg;
924 csig->ret = &mono_defaults.object_class->byval_arg;
925 csig->params [0] = &mono_defaults.int_class->byval_arg;
926 csig->params [1] = &mono_defaults.int32_class->byval_arg;
929 mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_ALLOC);
932 goto always_slowpath;
934 bytes_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
935 if (atype == ATYPE_STRING) {
936 /* a string alloator method takes the args: (vtable, len) */
937 /* bytes = (offsetof (MonoString, chars) + ((len + 1) * 2)); */
938 mono_mb_emit_ldarg (mb, 1);
939 mono_mb_emit_icon (mb, 1);
940 mono_mb_emit_byte (mb, MONO_CEE_ADD);
941 mono_mb_emit_icon (mb, 1);
942 mono_mb_emit_byte (mb, MONO_CEE_SHL);
943 // sizeof (MonoString) might include padding
944 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, chars));
945 mono_mb_emit_byte (mb, MONO_CEE_ADD);
946 mono_mb_emit_stloc (mb, bytes_var);
948 mono_mb_emit_ldarg (mb, 1);
949 mono_mb_emit_stloc (mb, bytes_var);
952 /* this is needed for strings/arrays only as the other big types are never allocated with this method */
953 if (atype == ATYPE_STRING) {
955 /* if (!SMALL_ENOUGH (bytes)) jump slow_path;*/
956 mono_mb_emit_ldloc (mb, bytes_var);
957 mono_mb_emit_icon (mb, (NFREELISTS-1) * GRANULARITY);
958 not_small_enough_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_UN_S);
959 /* check for overflow */
960 mono_mb_emit_ldloc (mb, bytes_var);
961 mono_mb_emit_icon (mb, sizeof (MonoString));
962 size_overflow_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLE_UN_S);
965 /* int index = INDEX_FROM_BYTES(bytes); */
966 index_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
968 mono_mb_emit_ldloc (mb, bytes_var);
969 mono_mb_emit_icon (mb, GRANULARITY - 1);
970 mono_mb_emit_byte (mb, MONO_CEE_ADD);
971 mono_mb_emit_icon (mb, shift_amount (GRANULARITY));
972 mono_mb_emit_byte (mb, MONO_CEE_SHR_UN);
973 mono_mb_emit_icon (mb, shift_amount (sizeof (gpointer)));
974 mono_mb_emit_byte (mb, MONO_CEE_SHL);
975 /* index var is already adjusted into bytes */
976 mono_mb_emit_stloc (mb, index_var);
978 my_fl_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
979 my_entry_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
980 /* my_fl = ((GC_thread)tsd) -> ptrfree_freelists + index; */
981 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
982 mono_mb_emit_byte (mb, 0x0D); /* CEE_MONO_TLS */
983 mono_mb_emit_i4 (mb, tls_key);
984 if (atype == ATYPE_FREEPTR || atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING)
985 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
986 + G_STRUCT_OFFSET (struct thread_local_freelists,
988 else if (atype == ATYPE_NORMAL)
989 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
990 + G_STRUCT_OFFSET (struct thread_local_freelists,
992 else if (atype == ATYPE_GCJ)
993 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
994 + G_STRUCT_OFFSET (struct thread_local_freelists,
997 g_assert_not_reached ();
998 mono_mb_emit_byte (mb, MONO_CEE_ADD);
999 mono_mb_emit_ldloc (mb, index_var);
1000 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1001 mono_mb_emit_stloc (mb, my_fl_var);
1003 /* my_entry = *my_fl; */
1004 mono_mb_emit_ldloc (mb, my_fl_var);
1005 mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
1006 mono_mb_emit_stloc (mb, my_entry_var);
1008 /* if (EXPECT((word)my_entry >= HBLKSIZE, 1)) { */
1009 mono_mb_emit_ldloc (mb, my_entry_var);
1010 mono_mb_emit_icon (mb, HBLKSIZE);
1011 no_freelist_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
1013 /* ptr_t next = obj_link(my_entry); *my_fl = next; */
1014 mono_mb_emit_ldloc (mb, my_fl_var);
1015 mono_mb_emit_ldloc (mb, my_entry_var);
1016 mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
1017 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1019 /* set the vtable and clear the words in the object */
1020 mono_mb_emit_ldloc (mb, my_entry_var);
1021 mono_mb_emit_ldarg (mb, 0);
1022 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1024 if (atype == ATYPE_FREEPTR) {
1025 int start_var, end_var, start_loop;
1026 /* end = my_entry + bytes; start = my_entry + sizeof (gpointer);
1028 start_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1029 end_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1030 mono_mb_emit_ldloc (mb, my_entry_var);
1031 mono_mb_emit_ldloc (mb, bytes_var);
1032 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1033 mono_mb_emit_stloc (mb, end_var);
1034 mono_mb_emit_ldloc (mb, my_entry_var);
1035 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
1036 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1037 mono_mb_emit_stloc (mb, start_var);
1041 * } while (start < end);
1043 start_loop = mono_mb_get_label (mb);
1044 mono_mb_emit_ldloc (mb, start_var);
1045 mono_mb_emit_icon (mb, 0);
1046 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1047 mono_mb_emit_ldloc (mb, start_var);
1048 mono_mb_emit_icon (mb, sizeof (gpointer));
1049 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1050 mono_mb_emit_stloc (mb, start_var);
1052 mono_mb_emit_ldloc (mb, start_var);
1053 mono_mb_emit_ldloc (mb, end_var);
1054 mono_mb_emit_byte (mb, MONO_CEE_BLT_UN_S);
1055 mono_mb_emit_byte (mb, start_loop - (mono_mb_get_label (mb) + 1));
1056 } else if (atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING) {
1057 /* need to clear just the sync pointer */
1058 mono_mb_emit_ldloc (mb, my_entry_var);
1059 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
1060 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1061 mono_mb_emit_icon (mb, 0);
1062 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1065 if (atype == ATYPE_STRING) {
1066 /* need to set length and clear the last char */
1067 /* s->length = len; */
1068 mono_mb_emit_ldloc (mb, my_entry_var);
1069 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, length));
1070 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1071 mono_mb_emit_ldarg (mb, 1);
1072 mono_mb_emit_byte (mb, MONO_CEE_STIND_I4);
1073 /* s->chars [len] = 0; */
1074 mono_mb_emit_ldloc (mb, my_entry_var);
1075 mono_mb_emit_ldloc (mb, bytes_var);
1076 mono_mb_emit_icon (mb, 2);
1077 mono_mb_emit_byte (mb, MONO_CEE_SUB);
1078 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1079 mono_mb_emit_icon (mb, 0);
1080 mono_mb_emit_byte (mb, MONO_CEE_STIND_I2);
1083 /* return my_entry; */
1084 mono_mb_emit_ldloc (mb, my_entry_var);
1085 mono_mb_emit_byte (mb, MONO_CEE_RET);
1087 mono_mb_patch_short_branch (mb, no_freelist_branch);
1088 if (not_small_enough_branch > 0)
1089 mono_mb_patch_short_branch (mb, not_small_enough_branch);
1090 if (size_overflow_branch > 0)
1091 mono_mb_patch_short_branch (mb, size_overflow_branch);
1093 /* the slow path: we just call back into the runtime */
1095 if (atype == ATYPE_STRING) {
1096 mono_mb_emit_ldarg (mb, 1);
1097 mono_mb_emit_icall (mb, ves_icall_string_alloc);
1099 mono_mb_emit_ldarg (mb, 0);
1100 mono_mb_emit_icall (mb, ves_icall_object_new_specific);
1103 mono_mb_emit_byte (mb, MONO_CEE_RET);
1105 info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE);
1106 info->d.alloc.gc_name = "boehm";
1107 info->d.alloc.alloc_type = atype;
1108 mb->init_locals = FALSE;
1110 res = mono_mb_create (mb, csig, 8, info);
1116 static MonoMethod* alloc_method_cache [ATYPE_NUM];
1117 static MonoMethod* slowpath_alloc_method_cache [ATYPE_NUM];
1120 mono_gc_is_critical_method (MonoMethod *method)
1124 for (i = 0; i < ATYPE_NUM; ++i)
1125 if (method == alloc_method_cache [i] || method == slowpath_alloc_method_cache [i])
1132 * If possible, generate a managed method that can quickly allocate objects in class
1133 * @klass. The method will typically have an thread-local inline allocation sequence.
1134 * The signature of the called method is:
1135 * object allocate (MonoVTable *vtable)
1136 * Some of the logic here is similar to mono_class_get_allocation_ftn () i object.c,
1138 * The thread local alloc logic is taken from libgc/pthread_support.c.
1142 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size)
1147 * Tls implementation changed, we jump to tls native getters/setters.
1148 * Is boehm managed allocator ok with this ? Do we even care ?
1152 if (!SMALL_ENOUGH (klass->instance_size))
1154 if (mono_class_has_finalizer (klass) || mono_class_is_marshalbyref (klass))
1156 if (mono_profiler_get_events () & (MONO_PROFILE_ALLOCATIONS | MONO_PROFILE_STATISTICAL))
1160 if (mono_class_is_open_constructed_type (&klass->byval_arg))
1162 if (klass->byval_arg.type == MONO_TYPE_STRING) {
1163 atype = ATYPE_STRING;
1164 } else if (!known_instance_size) {
1166 } else if (!klass->has_references) {
1168 atype = ATYPE_FREEPTR_FOR_BOX;
1170 atype = ATYPE_FREEPTR;
1174 * disabled because we currently do a runtime choice anyway, to
1175 * deal with multiple appdomains.
1176 if (vtable->gc_descr != GC_NO_DESCRIPTOR)
1179 atype = ATYPE_NORMAL;
1182 return mono_gc_get_managed_allocator_by_type (atype, MANAGED_ALLOCATOR_REGULAR);
1186 mono_gc_get_managed_array_allocator (MonoClass *klass)
1192 * mono_gc_get_managed_allocator_by_type:
1194 * Return a managed allocator method corresponding to allocator type ATYPE.
1197 mono_gc_get_managed_allocator_by_type (int atype, ManagedAllocatorVariant variant)
1200 gboolean slowpath = variant != MANAGED_ALLOCATOR_REGULAR;
1201 MonoMethod **cache = slowpath ? slowpath_alloc_method_cache : alloc_method_cache;
1205 res = cache [atype];
1209 res = create_allocator (atype, -1, slowpath);
1210 mono_os_mutex_lock (&mono_gc_lock);
1211 if (cache [atype]) {
1212 mono_free_method (res);
1213 res = cache [atype];
1215 mono_memory_barrier ();
1216 cache [atype] = res;
1218 mono_os_mutex_unlock (&mono_gc_lock);
1223 mono_gc_get_managed_allocator_types (void)
1229 mono_gc_get_write_barrier (void)
1231 g_assert_not_reached ();
1238 mono_gc_is_critical_method (MonoMethod *method)
1244 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size)
1250 mono_gc_get_managed_array_allocator (MonoClass *klass)
1256 mono_gc_get_managed_allocator_by_type (int atype, ManagedAllocatorVariant variant)
1262 mono_gc_get_managed_allocator_types (void)
1268 mono_gc_get_write_barrier (void)
1270 g_assert_not_reached ();
1277 mono_gc_get_specific_write_barrier (gboolean is_concurrent)
1279 g_assert_not_reached ();
1284 mono_gc_get_aligned_size_for_allocator (int size)
1290 mono_gc_get_gc_name (void)
1296 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
1298 return GC_call_with_alloc_lock (func, data);
1302 mono_gc_get_description (void)
1304 return g_strdup (DEFAULT_GC_NAME);
1308 mono_gc_set_desktop_mode (void)
1314 mono_gc_is_moving (void)
1320 mono_gc_is_disabled (void)
1322 if (GC_dont_gc || g_getenv ("GC_DONT_GC"))
1329 mono_gc_wbarrier_value_copy_bitmap (gpointer _dest, gpointer _src, int size, unsigned bitmap)
1331 g_assert_not_reached ();
1336 mono_gc_get_card_table (int *shift_bits, gpointer *card_mask)
1338 g_assert_not_reached ();
1343 mono_gc_card_table_nursery_check (void)
1345 g_assert_not_reached ();
1350 mono_gc_get_nursery (int *shift_bits, size_t *size)
1356 mono_gc_precise_stack_mark_enabled (void)
1362 mono_gc_get_logfile (void)
1368 mono_gc_params_set (const char* options)
1373 mono_gc_debug_set (const char* options)
1378 mono_gc_conservatively_scan_area (void *start, void *end)
1380 g_assert_not_reached ();
1384 mono_gc_scan_object (void *obj, void *gc_data)
1386 g_assert_not_reached ();
1391 mono_gc_get_bitmap_for_descr (void *descr, int *numbits)
1393 g_assert_not_reached ();
1398 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
1403 mono_gc_set_stack_end (void *stack_end)
1407 void mono_gc_set_skip_thread (gboolean value)
1412 mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
1417 /* This assertion is not valid when GC_DEBUG is defined */
1418 g_assert (GC_base (obj) == (char*)obj - offset);
1421 GC_REGISTER_FINALIZER_NO_ORDER ((char*)obj - offset, (GC_finalization_proc)user_data, GUINT_TO_POINTER (offset), NULL, NULL);
1426 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
1428 /* it is being replaced by GC_pthread_create on some
1429 * platforms, see libgc/include/gc_pthread_redirects.h */
1430 return pthread_create (new_thread, attr, start_routine, arg);
1435 BOOL APIENTRY mono_gc_dllmain (HMODULE module_handle, DWORD reason, LPVOID reserved)
1437 return GC_DllMain (module_handle, reason, reserved);
1442 mono_gc_get_vtable_bits (MonoClass *klass)
1444 if (fin_callbacks.is_class_finalization_aware) {
1445 if (fin_callbacks.is_class_finalization_aware (klass))
1446 return BOEHM_GC_BIT_FINALIZER_AWARE;
1452 * mono_gc_register_altstack:
1454 * Register the dimensions of the normal stack and altstack with the collector.
1455 * Currently, STACK/STACK_SIZE is only used when the thread is suspended while it is on an altstack.
1458 mono_gc_register_altstack (gpointer stack, gint32 stack_size, gpointer altstack, gint32 altstack_size)
1460 GC_register_altstack (stack, stack_size, altstack, altstack_size);
1464 mono_gc_get_los_limit (void)
1470 mono_gc_set_string_length (MonoString *str, gint32 new_length)
1472 mono_unichar2 *new_end = str->chars + new_length;
1474 /* zero the discarded string. This null-delimits the string and allows
1475 * the space to be reclaimed by SGen. */
1477 memset (new_end, 0, (str->length - new_length + 1) * sizeof (mono_unichar2));
1478 str->length = new_length;
1482 mono_gc_user_markers_supported (void)
1488 mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
1490 g_assert_not_reached ();
1494 /* Toggleref support */
1497 mono_gc_toggleref_add (MonoObject *object, mono_bool strong_ref)
1499 if (GC_toggleref_add ((GC_PTR)object, (int)strong_ref) != GC_SUCCESS)
1500 g_error ("GC_toggleref_add failed\n");
1504 mono_gc_toggleref_register_callback (MonoToggleRefStatus (*proccess_toggleref) (MonoObject *obj))
1506 GC_set_toggleref_func ((GC_ToggleRefStatus (*) (GC_PTR obj)) proccess_toggleref);
1509 /* Test support code */
1511 static MonoToggleRefStatus
1512 test_toggleref_callback (MonoObject *obj)
1514 static MonoClassField *mono_toggleref_test_field;
1515 MonoToggleRefStatus status = MONO_TOGGLE_REF_DROP;
1517 if (!mono_toggleref_test_field) {
1518 mono_toggleref_test_field = mono_class_get_field_from_name (mono_object_get_class (obj), "__test");
1519 g_assert (mono_toggleref_test_field);
1522 mono_field_get_value (obj, mono_toggleref_test_field, &status);
1523 printf ("toggleref-cb obj %d\n", status);
1528 register_test_toggleref_callback (void)
1530 mono_gc_toggleref_register_callback (test_toggleref_callback);
1534 is_finalization_aware (MonoObject *obj)
1536 MonoVTable *vt = obj->vtable;
1537 return (vt->gc_bits & BOEHM_GC_BIT_FINALIZER_AWARE) == BOEHM_GC_BIT_FINALIZER_AWARE;
1541 fin_notifier (MonoObject *obj)
1543 if (is_finalization_aware (obj))
1544 fin_callbacks.object_queued_for_finalization (obj);
1548 mono_gc_register_finalizer_callbacks (MonoGCFinalizerCallbacks *callbacks)
1550 if (callbacks->version != MONO_GC_FINALIZER_EXTENSION_VERSION)
1551 g_error ("Invalid finalizer callback version. Expected %d but got %d\n", MONO_GC_FINALIZER_EXTENSION_VERSION, callbacks->version);
1553 fin_callbacks = *callbacks;
1555 GC_set_await_finalize_proc ((void (*) (GC_PTR))fin_notifier);
1558 #define BITMAP_SIZE (sizeof (*((HandleData *)NULL)->bitmap) * CHAR_BIT)
1560 static inline gboolean
1561 slot_occupied (HandleData *handles, guint slot) {
1562 return handles->bitmap [slot / BITMAP_SIZE] & (1 << (slot % BITMAP_SIZE));
1566 vacate_slot (HandleData *handles, guint slot) {
1567 handles->bitmap [slot / BITMAP_SIZE] &= ~(1 << (slot % BITMAP_SIZE));
1571 occupy_slot (HandleData *handles, guint slot) {
1572 handles->bitmap [slot / BITMAP_SIZE] |= 1 << (slot % BITMAP_SIZE);
1576 find_first_unset (guint32 bitmap)
1579 for (i = 0; i < 32; ++i) {
1580 if (!(bitmap & (1 << i)))
1587 handle_data_alloc_entries (HandleData *handles)
1590 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1591 handles->entries = (void **)g_malloc0 (sizeof (*handles->entries) * handles->size);
1592 handles->domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * handles->size);
1594 handles->entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * handles->size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
1596 handles->bitmap = (guint32 *)g_malloc0 (handles->size / CHAR_BIT);
1600 handle_data_next_unset (HandleData *handles)
1603 for (slot = handles->slot_hint; slot < handles->size / BITMAP_SIZE; ++slot) {
1604 if (handles->bitmap [slot] == 0xffffffff)
1606 handles->slot_hint = slot;
1607 return find_first_unset (handles->bitmap [slot]);
1613 handle_data_first_unset (HandleData *handles)
1616 for (slot = 0; slot < handles->slot_hint; ++slot) {
1617 if (handles->bitmap [slot] == 0xffffffff)
1619 handles->slot_hint = slot;
1620 return find_first_unset (handles->bitmap [slot]);
1625 /* Returns the index of the current slot in the bitmap. */
1627 handle_data_grow (HandleData *handles, gboolean track)
1629 guint32 *new_bitmap;
1630 guint32 new_size = handles->size * 2; /* always double: we memset to 0 based on this below */
1632 /* resize and copy the bitmap */
1633 new_bitmap = (guint32 *)g_malloc0 (new_size / CHAR_BIT);
1634 memcpy (new_bitmap, handles->bitmap, handles->size / CHAR_BIT);
1635 g_free (handles->bitmap);
1636 handles->bitmap = new_bitmap;
1638 /* resize and copy the entries */
1639 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1641 guint16 *domain_ids;
1643 domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * new_size);
1644 entries = (void **)g_malloc0 (sizeof (*handles->entries) * new_size);
1645 memcpy (domain_ids, handles->domain_ids, sizeof (*handles->domain_ids) * handles->size);
1646 for (i = 0; i < handles->size; ++i) {
1647 MonoObject *obj = mono_gc_weak_link_get (&(handles->entries [i]));
1649 mono_gc_weak_link_add (&(entries [i]), obj, track);
1650 mono_gc_weak_link_remove (&(handles->entries [i]), track);
1652 g_assert (!handles->entries [i]);
1655 g_free (handles->entries);
1656 g_free (handles->domain_ids);
1657 handles->entries = entries;
1658 handles->domain_ids = domain_ids;
1661 entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * new_size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
1662 mono_gc_memmove_aligned (entries, handles->entries, sizeof (*handles->entries) * handles->size);
1663 mono_gc_free_fixed (handles->entries);
1664 handles->entries = entries;
1666 handles->slot_hint = handles->size / BITMAP_SIZE;
1667 handles->size = new_size;
1671 alloc_handle (HandleData *handles, MonoObject *obj, gboolean track)
1675 lock_handles (handles);
1677 handle_data_alloc_entries (handles);
1678 i = handle_data_next_unset (handles);
1679 if (i == -1 && handles->slot_hint != 0)
1680 i = handle_data_first_unset (handles);
1682 handle_data_grow (handles, track);
1685 slot = handles->slot_hint * BITMAP_SIZE + i;
1686 occupy_slot (handles, slot);
1687 handles->entries [slot] = NULL;
1688 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1689 /*FIXME, what to use when obj == null?*/
1690 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
1692 mono_gc_weak_link_add (&(handles->entries [slot]), obj, track);
1694 handles->entries [slot] = obj;
1697 #ifndef DISABLE_PERFCOUNTERS
1698 mono_perfcounters->gc_num_handles++;
1700 unlock_handles (handles);
1701 res = MONO_GC_HANDLE (slot, handles->type);
1702 mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_CREATED, handles->type, res, obj);
1707 * mono_gchandle_new:
1708 * @obj: managed object to get a handle for
1709 * @pinned: whether the object should be pinned
1711 * This returns a handle that wraps the object, this is used to keep a
1712 * reference to a managed object from the unmanaged world and preventing the
1713 * object from being disposed.
1715 * If @pinned is false the address of the object can not be obtained, if it is
1716 * true the address of the object can be obtained. This will also pin the
1717 * object so it will not be possible by a moving garbage collector to move the
1720 * Returns: a handle that can be used to access the object from
1724 mono_gchandle_new (MonoObject *obj, gboolean pinned)
1726 return alloc_handle (&gc_handles [pinned? HANDLE_PINNED: HANDLE_NORMAL], obj, FALSE);
1730 * mono_gchandle_new_weakref:
1731 * @obj: managed object to get a handle for
1732 * @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.
1734 * This returns a weak handle that wraps the object, this is used to
1735 * keep a reference to a managed object from the unmanaged world.
1736 * Unlike the mono_gchandle_new the object can be reclaimed by the
1737 * garbage collector. In this case the value of the GCHandle will be
1740 * If @track_resurrection is TRUE the object will be tracked through
1741 * finalization and if the object is resurrected during the execution
1742 * of the finalizer, then the returned weakref will continue to hold
1743 * a reference to the object. If @track_resurrection is FALSE, then
1744 * the weak reference's target will become NULL as soon as the object
1745 * is passed on to the finalizer.
1747 * Returns: a handle that can be used to access the object from
1751 mono_gchandle_new_weakref (MonoObject *obj, gboolean track_resurrection)
1753 return alloc_handle (&gc_handles [track_resurrection? HANDLE_WEAK_TRACK: HANDLE_WEAK], obj, track_resurrection);
1757 * mono_gchandle_get_target:
1758 * @gchandle: a GCHandle's handle.
1760 * The handle was previously created by calling `mono_gchandle_new` or
1761 * `mono_gchandle_new_weakref`.
1763 * Returns: A pointer to the `MonoObject*` represented by the handle or
1764 * NULL for a collected object if using a weakref handle.
1767 mono_gchandle_get_target (guint32 gchandle)
1769 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1770 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1771 HandleData *handles = &gc_handles [type];
1772 MonoObject *obj = NULL;
1773 if (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 obj = mono_gc_weak_link_get (&handles->entries [slot]);
1781 obj = (MonoObject *)handles->entries [slot];
1784 /* print a warning? */
1786 unlock_handles (handles);
1787 /*g_print ("get target of entry %d of type %d: %p\n", slot, handles->type, obj);*/
1792 mono_gchandle_set_target (guint32 gchandle, MonoObject *obj)
1794 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1795 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1796 HandleData *handles = &gc_handles [type];
1797 MonoObject *old_obj = NULL;
1799 g_assert (type < HANDLE_TYPE_MAX);
1800 lock_handles (handles);
1801 if (slot < handles->size && slot_occupied (handles, slot)) {
1802 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1803 old_obj = (MonoObject *)handles->entries [slot];
1804 if (handles->entries [slot])
1805 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1807 mono_gc_weak_link_add (&handles->entries [slot], obj, handles->type == HANDLE_WEAK_TRACK);
1808 /*FIXME, what to use when obj == null?*/
1809 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
1811 handles->entries [slot] = obj;
1814 /* print a warning? */
1816 /*g_print ("changed entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
1817 unlock_handles (handles);
1821 mono_gc_is_null (void)
1827 * mono_gchandle_is_in_domain:
1828 * @gchandle: a GCHandle's handle.
1829 * @domain: An application domain.
1831 * Use this function to determine if the @gchandle points to an
1832 * object allocated in the specified @domain.
1834 * Returns: TRUE if the object wrapped by the @gchandle belongs to the specific @domain.
1837 mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain)
1839 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1840 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1841 HandleData *handles = &gc_handles [type];
1842 gboolean result = FALSE;
1844 if (type >= HANDLE_TYPE_MAX)
1847 lock_handles (handles);
1848 if (slot < handles->size && slot_occupied (handles, slot)) {
1849 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1850 result = domain->domain_id == handles->domain_ids [slot];
1853 obj = (MonoObject *)handles->entries [slot];
1857 result = domain == mono_object_domain (obj);
1860 /* print a warning? */
1862 unlock_handles (handles);
1867 * mono_gchandle_free:
1868 * @gchandle: a GCHandle's handle.
1870 * Frees the @gchandle handle. If there are no outstanding
1871 * references, the garbage collector can reclaim the memory of the
1875 mono_gchandle_free (guint32 gchandle)
1877 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1878 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1879 HandleData *handles = &gc_handles [type];
1880 if (type >= HANDLE_TYPE_MAX)
1883 lock_handles (handles);
1884 if (slot < handles->size && slot_occupied (handles, slot)) {
1885 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1886 if (handles->entries [slot])
1887 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1889 handles->entries [slot] = NULL;
1891 vacate_slot (handles, slot);
1893 /* print a warning? */
1895 #ifndef DISABLE_PERFCOUNTERS
1896 mono_perfcounters->gc_num_handles--;
1898 /*g_print ("freed entry %d of type %d\n", slot, handles->type);*/
1899 unlock_handles (handles);
1900 mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_DESTROYED, handles->type, gchandle, NULL);
1904 * mono_gchandle_free_domain:
1905 * @domain: domain that is unloading
1907 * Function used internally to cleanup any GC handle for objects belonging
1908 * to the specified domain during appdomain unload.
1911 mono_gchandle_free_domain (MonoDomain *domain)
1915 for (type = HANDLE_TYPE_MIN; type < HANDLE_PINNED; ++type) {
1917 HandleData *handles = &gc_handles [type];
1918 lock_handles (handles);
1919 for (slot = 0; slot < handles->size; ++slot) {
1920 if (!slot_occupied (handles, slot))
1922 if (MONO_GC_HANDLE_TYPE_IS_WEAK (type)) {
1923 if (domain->domain_id == handles->domain_ids [slot]) {
1924 vacate_slot (handles, slot);
1925 if (handles->entries [slot])
1926 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1929 if (handles->entries [slot] && mono_object_domain (handles->entries [slot]) == domain) {
1930 vacate_slot (handles, slot);
1931 handles->entries [slot] = NULL;
1935 unlock_handles (handles);
1941 MONO_EMPTY_SOURCE_FILE (boehm_gc);
1942 #endif /* no Boehm GC */