3 * GC implementation using either the installed or included Boehm GC.
5 * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
6 * Copyright 2004-2011 Novell, Inc (http://www.novell.com)
7 * Copyright 2011-2012 Xamarin, Inc (http://www.xamarin.com)
8 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
15 #define GC_I_HIDE_POINTERS
16 #include <mono/metadata/gc-internals.h>
17 #include <mono/metadata/mono-gc.h>
18 #include <mono/metadata/profiler-private.h>
19 #include <mono/metadata/class-internals.h>
20 #include <mono/metadata/method-builder.h>
21 #include <mono/metadata/opcodes.h>
22 #include <mono/metadata/domain-internals.h>
23 #include <mono/metadata/metadata-internals.h>
24 #include <mono/metadata/marshal.h>
25 #include <mono/metadata/runtime.h>
26 #include <mono/metadata/handle.h>
27 #include <mono/metadata/sgen-toggleref.h>
28 #include <mono/metadata/w32handle.h>
29 #include <mono/utils/atomic.h>
30 #include <mono/utils/mono-logger-internals.h>
31 #include <mono/utils/mono-memory-model.h>
32 #include <mono/utils/mono-time.h>
33 #include <mono/utils/mono-threads.h>
34 #include <mono/utils/dtrace.h>
35 #include <mono/utils/gc_wrapper.h>
36 #include <mono/utils/mono-os-mutex.h>
37 #include <mono/utils/mono-counters.h>
38 #include <mono/utils/mono-compiler.h>
44 #define THREAD_LOCAL_ALLOC 1
45 #include "private/pthread_support.h"
47 #if defined(PLATFORM_MACOSX) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
48 void *pthread_get_stackaddr_np(pthread_t);
51 #define GC_NO_DESCRIPTOR ((gpointer)(0 | GC_DS_LENGTH))
52 /*Boehm max heap cannot be smaller than 16MB*/
53 #define MIN_BOEHM_MAX_HEAP_SIZE_IN_MB 16
54 #define MIN_BOEHM_MAX_HEAP_SIZE (MIN_BOEHM_MAX_HEAP_SIZE_IN_MB << 20)
56 static gboolean gc_initialized = FALSE;
57 static mono_mutex_t mono_gc_lock;
59 typedef void (*GC_push_other_roots_proc)(void);
61 static GC_push_other_roots_proc default_push_other_roots;
62 static GHashTable *roots;
65 mono_push_other_roots(void);
68 register_test_toggleref_callback (void);
70 #define BOEHM_GC_BIT_FINALIZER_AWARE 1
71 static MonoGCFinalizerCallbacks fin_callbacks;
75 static mono_mutex_t handle_section;
76 #define lock_handles(handles) mono_os_mutex_lock (&handle_section)
77 #define unlock_handles(handles) mono_os_mutex_unlock (&handle_section)
84 guint slot_hint : 24; /* starting slot for search in bitmap */
85 /* 2^16 appdomains should be enough for everyone (though I know I'll regret this in 20 years) */
86 /* we alloc this only for weak refs, since we can get the domain directly in the other cases */
90 #define EMPTY_HANDLE_DATA(type) {NULL, NULL, 0, (type), 0, NULL}
92 /* weak and weak-track arrays will be allocated in malloc memory
94 static HandleData gc_handles [] = {
95 EMPTY_HANDLE_DATA (HANDLE_WEAK),
96 EMPTY_HANDLE_DATA (HANDLE_WEAK_TRACK),
97 EMPTY_HANDLE_DATA (HANDLE_NORMAL),
98 EMPTY_HANDLE_DATA (HANDLE_PINNED)
102 mono_gc_warning (char *msg, GC_word arg)
104 mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_GC, msg, (unsigned long)arg);
107 static void on_gc_notification (GC_EventType event);
108 static void on_gc_heap_resize (size_t new_size);
111 mono_gc_base_init (void)
118 mono_counters_init ();
121 mono_w32handle_init ();
125 * Handle the case when we are called from a thread different from the main thread,
127 * FIXME: Move this to libgc where it belongs.
129 * we used to do this only when running on valgrind,
130 * but it happens also in other setups.
132 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
137 pthread_getattr_np (pthread_self (), &attr);
138 pthread_attr_getstack (&attr, &sstart, &size);
139 pthread_attr_destroy (&attr);
140 /*g_print ("stackbottom pth is: %p\n", (char*)sstart + size);*/
141 /* apparently with some linuxthreads implementations sstart can be NULL,
142 * fallback to the more imprecise method (bug# 78096).
145 GC_stackbottom = (char*)sstart + size;
148 gsize stack_bottom = (gsize)&dummy;
149 stack_bottom += 4095;
150 stack_bottom &= ~4095;
151 GC_stackbottom = (char*)stack_bottom;
154 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
155 GC_stackbottom = (char*)pthread_get_stackaddr_np (pthread_self ());
156 #elif defined(__OpenBSD__)
157 # include <pthread_np.h>
162 rslt = pthread_stackseg_np(pthread_self(), &ss);
163 g_assert (rslt == 0);
165 GC_stackbottom = (char*)ss.ss_sp;
170 gsize stack_bottom = (gsize)&dummy;
171 stack_bottom += 4095;
172 stack_bottom &= ~4095;
173 /*g_print ("stackbottom is: %p\n", (char*)stack_bottom);*/
174 GC_stackbottom = (char*)stack_bottom;
178 roots = g_hash_table_new (NULL, NULL);
179 default_push_other_roots = GC_push_other_roots;
180 GC_push_other_roots = mono_push_other_roots;
182 #if !defined(PLATFORM_ANDROID)
183 /* If GC_no_dls is set to true, GC_find_limit is not called. This causes a seg fault on Android. */
187 if ((env = g_getenv ("MONO_GC_DEBUG"))) {
188 char **opts = g_strsplit (env, ",", -1);
189 for (char **ptr = opts; ptr && *ptr; ptr ++) {
191 if (!strcmp (opt, "do-not-finalize")) {
192 mono_do_not_finalize = 1;
193 } else if (!strcmp (opt, "log-finalizers")) {
203 GC_set_warn_proc (mono_gc_warning);
204 GC_finalize_on_demand = 1;
205 GC_finalizer_notifier = mono_gc_finalize_notify;
207 GC_init_gcj_malloc (5, NULL);
208 GC_allow_register_threads ();
210 if ((env = g_getenv ("MONO_GC_PARAMS"))) {
211 char **ptr, **opts = g_strsplit (env, ",", -1);
212 for (ptr = opts; *ptr; ++ptr) {
214 if (g_str_has_prefix (opt, "max-heap-size=")) {
217 opt = strchr (opt, '=') + 1;
218 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &max_heap)) {
219 if (max_heap < MIN_BOEHM_MAX_HEAP_SIZE) {
220 fprintf (stderr, "max-heap-size must be at least %dMb.\n", MIN_BOEHM_MAX_HEAP_SIZE_IN_MB);
223 GC_set_max_heap_size (max_heap);
225 fprintf (stderr, "max-heap-size must be an integer.\n");
229 } else if (g_str_has_prefix (opt, "toggleref-test")) {
230 register_test_toggleref_callback ();
233 /* Could be a parameter for sgen */
235 fprintf (stderr, "MONO_GC_PARAMS must be a comma-delimited list of one or more of the following:\n");
236 fprintf (stderr, " max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
245 mono_thread_callbacks_init ();
246 mono_thread_info_init (sizeof (MonoThreadInfo));
247 mono_os_mutex_init (&mono_gc_lock);
248 mono_os_mutex_init_recursive (&handle_section);
250 mono_thread_info_attach ();
252 GC_set_on_collection_event (on_gc_notification);
253 GC_on_heap_resize = on_gc_heap_resize;
255 gc_initialized = TRUE;
259 mono_gc_base_cleanup (void)
261 GC_finalizer_notifier = NULL;
266 * \param generation GC generation identifier
268 * Perform a garbage collection for the given generation, higher numbers
269 * mean usually older objects. Collecting a high-numbered generation
270 * implies collecting also the lower-numbered generations.
271 * The maximum value for \p generation can be retrieved with a call to
272 * \c mono_gc_max_generation, so this function is usually called as:
274 * <code>mono_gc_collect (mono_gc_max_generation ());</code>
277 mono_gc_collect (int generation)
279 #ifndef DISABLE_PERFCOUNTERS
280 mono_perfcounters->gc_induced++;
286 * mono_gc_max_generation:
288 * Get the maximum generation number used by the current garbage
289 * collector. The value will be 0 for the Boehm collector, 1 or more
290 * for the generational collectors.
292 * Returns: the maximum generation number.
295 mono_gc_max_generation (void)
301 * mono_gc_get_generation:
302 * \param object a managed object
304 * Get the garbage collector's generation that \p object belongs to.
305 * Use this has a hint only.
307 * \returns a garbage collector generation number
310 mono_gc_get_generation (MonoObject *object)
316 * mono_gc_collection_count:
317 * \param generation a GC generation number
319 * Get how many times a garbage collection has been performed
320 * for the given \p generation number.
322 * \returns the number of garbage collections
325 mono_gc_collection_count (int generation)
331 * mono_gc_add_memory_pressure:
332 * \param value amount of bytes
334 * Adjust the garbage collector's view of how many bytes of memory
335 * are indirectly referenced by managed objects (for example unmanaged
336 * memory holding image or other binary data).
337 * This is a hint only to the garbage collector algorithm.
338 * Note that negative amounts of p value will decrease the memory
342 mono_gc_add_memory_pressure (gint64 value)
347 * mono_gc_get_used_size:
349 * Get the approximate amount of memory used by managed objects.
351 * Returns: the amount of memory used in bytes
354 mono_gc_get_used_size (void)
356 return GC_get_heap_size () - GC_get_free_bytes ();
360 * mono_gc_get_heap_size:
362 * Get the amount of memory used by the garbage collector.
364 * Returns: the size of the heap in bytes
367 mono_gc_get_heap_size (void)
369 return GC_get_heap_size ();
373 mono_gc_is_gc_thread (void)
375 return GC_thread_is_registered ();
379 mono_gc_thread_attach (MonoThreadInfo* info)
381 struct GC_stack_base sb;
384 /* TODO: use GC_get_stack_base instead of baseptr. */
385 sb.mem_base = info->stack_end;
386 res = GC_register_my_thread (&sb);
387 if (res == GC_UNIMPLEMENTED)
388 return NULL; /* Cannot happen with GC v7+. */
390 info->handle_stack = mono_handle_stack_alloc ();
396 mono_gc_thread_detach_with_lock (MonoThreadInfo *p)
398 MonoNativeThreadId tid;
400 tid = mono_thread_info_get_tid (p);
402 if (p->runtime_thread)
403 mono_threads_add_joinable_thread ((gpointer)tid);
405 mono_handle_stack_free (p->handle_stack);
409 mono_gc_thread_in_critical_region (MonoThreadInfo *info)
415 mono_object_is_alive (MonoObject* o)
417 return GC_is_marked ((ptr_t)o);
421 mono_gc_walk_heap (int flags, MonoGCReferences callback, void *data)
426 static gint64 gc_start_time;
429 on_gc_notification (GC_EventType event)
431 MonoProfilerGCEvent e = (MonoProfilerGCEvent)event;
434 case MONO_GC_EVENT_PRE_STOP_WORLD:
435 MONO_GC_WORLD_STOP_BEGIN ();
438 case MONO_GC_EVENT_POST_STOP_WORLD:
439 MONO_GC_WORLD_STOP_END ();
442 case MONO_GC_EVENT_PRE_START_WORLD:
443 MONO_GC_WORLD_RESTART_BEGIN (1);
446 case MONO_GC_EVENT_POST_START_WORLD:
447 MONO_GC_WORLD_RESTART_END (1);
450 case MONO_GC_EVENT_START:
452 #ifndef DISABLE_PERFCOUNTERS
453 if (mono_perfcounters)
454 mono_perfcounters->gc_collections0++;
456 gc_stats.major_gc_count ++;
457 gc_start_time = mono_100ns_ticks ();
460 case MONO_GC_EVENT_END:
462 #if defined(ENABLE_DTRACE) && defined(__sun__)
463 /* This works around a dtrace -G problem on Solaris.
464 Limit its actual use to when the probe is enabled. */
465 if (MONO_GC_END_ENABLED ())
469 #ifndef DISABLE_PERFCOUNTERS
470 if (mono_perfcounters) {
471 guint64 heap_size = GC_get_heap_size ();
472 guint64 used_size = heap_size - GC_get_free_bytes ();
473 mono_perfcounters->gc_total_bytes = used_size;
474 mono_perfcounters->gc_committed_bytes = heap_size;
475 mono_perfcounters->gc_reserved_bytes = heap_size;
476 mono_perfcounters->gc_gen0size = heap_size;
479 gc_stats.major_gc_time += mono_100ns_ticks () - gc_start_time;
480 mono_trace_message (MONO_TRACE_GC, "gc took %" G_GINT64_FORMAT " usecs", (mono_100ns_ticks () - gc_start_time) / 10);
486 MONO_PROFILER_RAISE (gc_event, (e, 0));
489 case MONO_GC_EVENT_PRE_STOP_WORLD:
490 mono_thread_info_suspend_lock ();
491 MONO_PROFILER_RAISE (gc_event, (MONO_GC_EVENT_PRE_STOP_WORLD_LOCKED, 0));
493 case MONO_GC_EVENT_POST_START_WORLD:
494 mono_thread_info_suspend_unlock ();
495 MONO_PROFILER_RAISE (gc_event, (MONO_GC_EVENT_POST_START_WORLD_UNLOCKED, 0));
504 on_gc_heap_resize (size_t new_size)
506 guint64 heap_size = GC_get_heap_size ();
507 #ifndef DISABLE_PERFCOUNTERS
508 if (mono_perfcounters) {
509 mono_perfcounters->gc_committed_bytes = heap_size;
510 mono_perfcounters->gc_reserved_bytes = heap_size;
511 mono_perfcounters->gc_gen0size = heap_size;
515 MONO_PROFILER_RAISE (gc_resize, (new_size));
524 register_root (gpointer arg)
526 RootData* root_data = arg;
527 g_hash_table_insert (roots, root_data->start, root_data->end);
532 mono_gc_register_root (char *start, size_t size, void *descr, MonoGCRootSource source, const char *msg)
535 root_data.start = start;
536 /* Boehm root processing requires one byte past end of region to be scanned */
537 root_data.end = start + size + 1;
538 GC_call_with_alloc_lock (register_root, &root_data);
544 mono_gc_register_root_wbarrier (char *start, size_t size, MonoGCDescriptor descr, MonoGCRootSource source, const char *msg)
546 return mono_gc_register_root (start, size, descr, source, msg);
550 deregister_root (gpointer arg)
552 gboolean removed = g_hash_table_remove (roots, arg);
558 mono_gc_deregister_root (char* addr)
560 GC_call_with_alloc_lock (deregister_root, addr);
564 push_root (gpointer key, gpointer value, gpointer user_data)
566 GC_push_all (key, value);
570 mono_push_other_roots (void)
572 g_hash_table_foreach (roots, push_root, NULL);
573 if (default_push_other_roots)
574 default_push_other_roots ();
578 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
580 /* libgc requires that we use HIDE_POINTER... */
581 *link_addr = (void*)HIDE_POINTER (obj);
583 GC_REGISTER_LONG_LINK (link_addr, obj);
585 GC_GENERAL_REGISTER_DISAPPEARING_LINK (link_addr, obj);
589 mono_gc_weak_link_remove (void **link_addr, gboolean track)
592 GC_unregister_long_link (link_addr);
594 GC_unregister_disappearing_link (link_addr);
599 reveal_link (gpointer link_addr)
601 void **link_a = (void **)link_addr;
602 return REVEAL_POINTER (*link_a);
606 mono_gc_weak_link_get (void **link_addr)
608 MonoObject *obj = (MonoObject *)GC_call_with_alloc_lock (reveal_link, link_addr);
609 if (obj == (MonoObject *) -1)
615 mono_gc_make_descr_for_string (gsize *bitmap, int numbits)
617 return mono_gc_make_descr_from_bitmap (bitmap, numbits);
621 mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size)
623 return mono_gc_make_descr_from_bitmap (bitmap, numbits);
627 mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size)
629 /* libgc has no usable support for arrays... */
630 return GC_NO_DESCRIPTOR;
634 mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
636 /* It seems there are issues when the bitmap doesn't fit: play it safe */
638 return GC_NO_DESCRIPTOR;
640 return (gpointer)GC_make_descriptor ((GC_bitmap)bitmap, numbits);
644 mono_gc_make_vector_descr (void)
650 mono_gc_make_root_descr_all_refs (int numbits)
656 mono_gc_alloc_fixed (size_t size, void *descr, MonoGCRootSource source, const char *msg)
658 return GC_MALLOC_UNCOLLECTABLE (size);
662 mono_gc_free_fixed (void* addr)
668 mono_gc_alloc_obj (MonoVTable *vtable, size_t size)
672 if (!vtable->klass->has_references) {
673 obj = (MonoObject *)GC_MALLOC_ATOMIC (size);
674 if (G_UNLIKELY (!obj))
677 obj->vtable = vtable;
678 obj->synchronisation = NULL;
680 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
681 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
682 obj = (MonoObject *)GC_GCJ_MALLOC (size, vtable);
683 if (G_UNLIKELY (!obj))
686 obj = (MonoObject *)GC_MALLOC (size);
687 if (G_UNLIKELY (!obj))
690 obj->vtable = vtable;
693 if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
694 MONO_PROFILER_RAISE (gc_allocation, (obj));
700 mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length)
704 if (!vtable->klass->has_references) {
705 obj = (MonoArray *)GC_MALLOC_ATOMIC (size);
706 if (G_UNLIKELY (!obj))
709 obj->obj.vtable = vtable;
710 obj->obj.synchronisation = NULL;
712 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
713 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
714 obj = (MonoArray *)GC_GCJ_MALLOC (size, vtable);
715 if (G_UNLIKELY (!obj))
718 obj = (MonoArray *)GC_MALLOC (size);
719 if (G_UNLIKELY (!obj))
722 obj->obj.vtable = vtable;
725 obj->max_length = max_length;
727 if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
728 MONO_PROFILER_RAISE (gc_allocation, (&obj->obj));
734 mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uintptr_t bounds_size)
738 if (!vtable->klass->has_references) {
739 obj = (MonoArray *)GC_MALLOC_ATOMIC (size);
740 if (G_UNLIKELY (!obj))
743 obj->obj.vtable = vtable;
744 obj->obj.synchronisation = NULL;
746 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
747 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
748 obj = (MonoArray *)GC_GCJ_MALLOC (size, vtable);
749 if (G_UNLIKELY (!obj))
752 obj = (MonoArray *)GC_MALLOC (size);
753 if (G_UNLIKELY (!obj))
756 obj->obj.vtable = vtable;
759 obj->max_length = max_length;
762 obj->bounds = (MonoArrayBounds *) ((char *) obj + size - bounds_size);
764 if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
765 MONO_PROFILER_RAISE (gc_allocation, (&obj->obj));
771 mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len)
773 MonoString *obj = (MonoString *)GC_MALLOC_ATOMIC (size);
774 if (G_UNLIKELY (!obj))
777 obj->object.vtable = vtable;
778 obj->object.synchronisation = NULL;
780 obj->chars [len] = 0;
782 if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
783 MONO_PROFILER_RAISE (gc_allocation, (&obj->object));
789 mono_gc_alloc_mature (MonoVTable *vtable, size_t size)
791 return mono_gc_alloc_obj (vtable, size);
795 mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size)
797 return mono_gc_alloc_obj (vtable, size);
801 mono_gc_invoke_finalizers (void)
803 /* There is a bug in GC_invoke_finalizer () in versions <= 6.2alpha4:
804 * the 'mem_freed' variable is not initialized when there are no
805 * objects to finalize, which leads to strange behavior later on.
806 * The check is necessary to work around that bug.
808 if (GC_should_invoke_finalizers ())
809 return GC_invoke_finalizers ();
814 mono_gc_pending_finalizers (void)
816 return GC_should_invoke_finalizers ();
820 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
822 *(void**)field_ptr = value;
826 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
828 *(void**)slot_ptr = value;
832 mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
834 mono_gc_memmove_aligned (dest_ptr, src_ptr, count * sizeof (gpointer));
838 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
840 *(void**)ptr = value;
844 mono_gc_wbarrier_generic_store_atomic (gpointer ptr, MonoObject *value)
846 InterlockedWritePointer ((volatile gpointer *)ptr, value);
850 mono_gc_wbarrier_generic_nostore (gpointer ptr)
855 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
857 mono_gc_memmove_atomic (dest, src, count * mono_class_value_size (klass, NULL));
861 mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
863 /* do not copy the sync state */
864 mono_gc_memmove_aligned ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
865 mono_object_class (obj)->instance_size - sizeof (MonoObject));
869 mono_gc_clear_domain (MonoDomain *domain)
874 mono_gc_suspend_finalizers (void)
879 mono_gc_get_suspend_signal (void)
881 return GC_get_suspend_signal ();
885 mono_gc_get_restart_signal (void)
887 return GC_get_thr_restart_signal ();
890 #if defined(USE_COMPILER_TLS) && defined(__linux__) && (defined(__i386__) || defined(__x86_64__))
891 extern __thread void* GC_thread_tls;
892 #include "metadata-internals.h"
898 while (!(v & (1 << i)))
905 ATYPE_FREEPTR_FOR_BOX,
913 create_allocator (int atype, int tls_key, gboolean slowpath)
915 int index_var, bytes_var, my_fl_var, my_entry_var;
916 guint32 no_freelist_branch, not_small_enough_branch = 0;
917 guint32 size_overflow_branch = 0;
918 MonoMethodBuilder *mb;
920 MonoMethodSignature *csig;
921 const char *name = NULL;
924 g_assert_not_reached ();
926 if (atype == ATYPE_FREEPTR) {
927 name = slowpath ? "SlowAllocPtrfree" : "AllocPtrfree";
928 } else if (atype == ATYPE_FREEPTR_FOR_BOX) {
929 name = slowpath ? "SlowAllocPtrfreeBox" : "AllocPtrfreeBox";
930 } else if (atype == ATYPE_NORMAL) {
931 name = slowpath ? "SlowAlloc" : "Alloc";
932 } else if (atype == ATYPE_GCJ) {
933 name = slowpath ? "SlowAllocGcj" : "AllocGcj";
934 } else if (atype == ATYPE_STRING) {
935 name = slowpath ? "SlowAllocString" : "AllocString";
937 g_assert_not_reached ();
940 csig = mono_metadata_signature_alloc (mono_defaults.corlib, 2);
942 if (atype == ATYPE_STRING) {
943 csig->ret = &mono_defaults.string_class->byval_arg;
944 csig->params [0] = &mono_defaults.int_class->byval_arg;
945 csig->params [1] = &mono_defaults.int32_class->byval_arg;
947 csig->ret = &mono_defaults.object_class->byval_arg;
948 csig->params [0] = &mono_defaults.int_class->byval_arg;
949 csig->params [1] = &mono_defaults.int32_class->byval_arg;
952 mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_ALLOC);
955 goto always_slowpath;
957 bytes_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
958 if (atype == ATYPE_STRING) {
959 /* a string alloator method takes the args: (vtable, len) */
960 /* bytes = (offsetof (MonoString, chars) + ((len + 1) * 2)); */
961 mono_mb_emit_ldarg (mb, 1);
962 mono_mb_emit_icon (mb, 1);
963 mono_mb_emit_byte (mb, MONO_CEE_ADD);
964 mono_mb_emit_icon (mb, 1);
965 mono_mb_emit_byte (mb, MONO_CEE_SHL);
966 // sizeof (MonoString) might include padding
967 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, chars));
968 mono_mb_emit_byte (mb, MONO_CEE_ADD);
969 mono_mb_emit_stloc (mb, bytes_var);
971 mono_mb_emit_ldarg (mb, 1);
972 mono_mb_emit_stloc (mb, bytes_var);
975 /* this is needed for strings/arrays only as the other big types are never allocated with this method */
976 if (atype == ATYPE_STRING) {
978 /* if (!SMALL_ENOUGH (bytes)) jump slow_path;*/
979 mono_mb_emit_ldloc (mb, bytes_var);
980 mono_mb_emit_icon (mb, (NFREELISTS-1) * GRANULARITY);
981 not_small_enough_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_UN_S);
982 /* check for overflow */
983 mono_mb_emit_ldloc (mb, bytes_var);
984 mono_mb_emit_icon (mb, sizeof (MonoString));
985 size_overflow_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLE_UN_S);
988 /* int index = INDEX_FROM_BYTES(bytes); */
989 index_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
991 mono_mb_emit_ldloc (mb, bytes_var);
992 mono_mb_emit_icon (mb, GRANULARITY - 1);
993 mono_mb_emit_byte (mb, MONO_CEE_ADD);
994 mono_mb_emit_icon (mb, shift_amount (GRANULARITY));
995 mono_mb_emit_byte (mb, MONO_CEE_SHR_UN);
996 mono_mb_emit_icon (mb, shift_amount (sizeof (gpointer)));
997 mono_mb_emit_byte (mb, MONO_CEE_SHL);
998 /* index var is already adjusted into bytes */
999 mono_mb_emit_stloc (mb, index_var);
1001 my_fl_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1002 my_entry_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1003 /* my_fl = ((GC_thread)tsd) -> ptrfree_freelists + index; */
1004 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
1005 mono_mb_emit_byte (mb, 0x0D); /* CEE_MONO_TLS */
1006 mono_mb_emit_i4 (mb, tls_key);
1007 if (atype == ATYPE_FREEPTR || atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING)
1008 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
1009 + G_STRUCT_OFFSET (struct thread_local_freelists,
1010 ptrfree_freelists));
1011 else if (atype == ATYPE_NORMAL)
1012 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
1013 + G_STRUCT_OFFSET (struct thread_local_freelists,
1015 else if (atype == ATYPE_GCJ)
1016 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
1017 + G_STRUCT_OFFSET (struct thread_local_freelists,
1020 g_assert_not_reached ();
1021 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1022 mono_mb_emit_ldloc (mb, index_var);
1023 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1024 mono_mb_emit_stloc (mb, my_fl_var);
1026 /* my_entry = *my_fl; */
1027 mono_mb_emit_ldloc (mb, my_fl_var);
1028 mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
1029 mono_mb_emit_stloc (mb, my_entry_var);
1031 /* if (EXPECT((word)my_entry >= HBLKSIZE, 1)) { */
1032 mono_mb_emit_ldloc (mb, my_entry_var);
1033 mono_mb_emit_icon (mb, HBLKSIZE);
1034 no_freelist_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
1036 /* ptr_t next = obj_link(my_entry); *my_fl = next; */
1037 mono_mb_emit_ldloc (mb, my_fl_var);
1038 mono_mb_emit_ldloc (mb, my_entry_var);
1039 mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
1040 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1042 /* set the vtable and clear the words in the object */
1043 mono_mb_emit_ldloc (mb, my_entry_var);
1044 mono_mb_emit_ldarg (mb, 0);
1045 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1047 if (atype == ATYPE_FREEPTR) {
1048 int start_var, end_var, start_loop;
1049 /* end = my_entry + bytes; start = my_entry + sizeof (gpointer);
1051 start_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1052 end_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1053 mono_mb_emit_ldloc (mb, my_entry_var);
1054 mono_mb_emit_ldloc (mb, bytes_var);
1055 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1056 mono_mb_emit_stloc (mb, end_var);
1057 mono_mb_emit_ldloc (mb, my_entry_var);
1058 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
1059 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1060 mono_mb_emit_stloc (mb, start_var);
1064 * } while (start < end);
1066 start_loop = mono_mb_get_label (mb);
1067 mono_mb_emit_ldloc (mb, start_var);
1068 mono_mb_emit_icon (mb, 0);
1069 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1070 mono_mb_emit_ldloc (mb, start_var);
1071 mono_mb_emit_icon (mb, sizeof (gpointer));
1072 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1073 mono_mb_emit_stloc (mb, start_var);
1075 mono_mb_emit_ldloc (mb, start_var);
1076 mono_mb_emit_ldloc (mb, end_var);
1077 mono_mb_emit_byte (mb, MONO_CEE_BLT_UN_S);
1078 mono_mb_emit_byte (mb, start_loop - (mono_mb_get_label (mb) + 1));
1079 } else if (atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING) {
1080 /* need to clear just the sync pointer */
1081 mono_mb_emit_ldloc (mb, my_entry_var);
1082 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
1083 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1084 mono_mb_emit_icon (mb, 0);
1085 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1088 if (atype == ATYPE_STRING) {
1089 /* need to set length and clear the last char */
1090 /* s->length = len; */
1091 mono_mb_emit_ldloc (mb, my_entry_var);
1092 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, length));
1093 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1094 mono_mb_emit_ldarg (mb, 1);
1095 mono_mb_emit_byte (mb, MONO_CEE_STIND_I4);
1096 /* s->chars [len] = 0; */
1097 mono_mb_emit_ldloc (mb, my_entry_var);
1098 mono_mb_emit_ldloc (mb, bytes_var);
1099 mono_mb_emit_icon (mb, 2);
1100 mono_mb_emit_byte (mb, MONO_CEE_SUB);
1101 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1102 mono_mb_emit_icon (mb, 0);
1103 mono_mb_emit_byte (mb, MONO_CEE_STIND_I2);
1106 /* return my_entry; */
1107 mono_mb_emit_ldloc (mb, my_entry_var);
1108 mono_mb_emit_byte (mb, MONO_CEE_RET);
1110 mono_mb_patch_short_branch (mb, no_freelist_branch);
1111 if (not_small_enough_branch > 0)
1112 mono_mb_patch_short_branch (mb, not_small_enough_branch);
1113 if (size_overflow_branch > 0)
1114 mono_mb_patch_short_branch (mb, size_overflow_branch);
1116 /* the slow path: we just call back into the runtime */
1118 if (atype == ATYPE_STRING) {
1119 mono_mb_emit_ldarg (mb, 1);
1120 mono_mb_emit_icall (mb, ves_icall_string_alloc);
1122 mono_mb_emit_ldarg (mb, 0);
1123 mono_mb_emit_icall (mb, ves_icall_object_new_specific);
1126 mono_mb_emit_byte (mb, MONO_CEE_RET);
1128 info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE);
1129 info->d.alloc.gc_name = "boehm";
1130 info->d.alloc.alloc_type = atype;
1131 mb->init_locals = FALSE;
1133 res = mono_mb_create (mb, csig, 8, info);
1139 static MonoMethod* alloc_method_cache [ATYPE_NUM];
1140 static MonoMethod* slowpath_alloc_method_cache [ATYPE_NUM];
1143 mono_gc_is_critical_method (MonoMethod *method)
1147 for (i = 0; i < ATYPE_NUM; ++i)
1148 if (method == alloc_method_cache [i] || method == slowpath_alloc_method_cache [i])
1155 * If possible, generate a managed method that can quickly allocate objects in class
1156 * @klass. The method will typically have an thread-local inline allocation sequence.
1157 * The signature of the called method is:
1158 * object allocate (MonoVTable *vtable)
1159 * Some of the logic here is similar to mono_class_get_allocation_ftn () i object.c,
1161 * The thread local alloc logic is taken from libgc/pthread_support.c.
1165 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size)
1170 * Tls implementation changed, we jump to tls native getters/setters.
1171 * Is boehm managed allocator ok with this ? Do we even care ?
1175 if (!SMALL_ENOUGH (klass->instance_size))
1177 if (mono_class_has_finalizer (klass) || mono_class_is_marshalbyref (klass))
1179 if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
1183 if (mono_class_is_open_constructed_type (&klass->byval_arg))
1185 if (klass->byval_arg.type == MONO_TYPE_STRING) {
1186 atype = ATYPE_STRING;
1187 } else if (!known_instance_size) {
1189 } else if (!klass->has_references) {
1191 atype = ATYPE_FREEPTR_FOR_BOX;
1193 atype = ATYPE_FREEPTR;
1197 * disabled because we currently do a runtime choice anyway, to
1198 * deal with multiple appdomains.
1199 if (vtable->gc_descr != GC_NO_DESCRIPTOR)
1202 atype = ATYPE_NORMAL;
1205 return mono_gc_get_managed_allocator_by_type (atype, MANAGED_ALLOCATOR_REGULAR);
1209 mono_gc_get_managed_array_allocator (MonoClass *klass)
1215 * mono_gc_get_managed_allocator_by_type:
1217 * Return a managed allocator method corresponding to allocator type ATYPE.
1220 mono_gc_get_managed_allocator_by_type (int atype, ManagedAllocatorVariant variant)
1223 gboolean slowpath = variant != MANAGED_ALLOCATOR_REGULAR;
1224 MonoMethod **cache = slowpath ? slowpath_alloc_method_cache : alloc_method_cache;
1228 res = cache [atype];
1232 res = create_allocator (atype, -1, slowpath);
1233 mono_os_mutex_lock (&mono_gc_lock);
1234 if (cache [atype]) {
1235 mono_free_method (res);
1236 res = cache [atype];
1238 mono_memory_barrier ();
1239 cache [atype] = res;
1241 mono_os_mutex_unlock (&mono_gc_lock);
1246 mono_gc_get_managed_allocator_types (void)
1252 mono_gc_get_write_barrier (void)
1254 g_assert_not_reached ();
1261 mono_gc_is_critical_method (MonoMethod *method)
1267 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size)
1273 mono_gc_get_managed_array_allocator (MonoClass *klass)
1279 mono_gc_get_managed_allocator_by_type (int atype, ManagedAllocatorVariant variant)
1285 mono_gc_get_managed_allocator_types (void)
1291 mono_gc_get_write_barrier (void)
1293 g_assert_not_reached ();
1300 mono_gc_get_specific_write_barrier (gboolean is_concurrent)
1302 g_assert_not_reached ();
1307 mono_gc_get_aligned_size_for_allocator (int size)
1313 mono_gc_get_gc_name (void)
1319 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
1321 return GC_call_with_alloc_lock (func, data);
1325 mono_gc_get_description (void)
1327 return g_strdup (DEFAULT_GC_NAME);
1331 mono_gc_set_desktop_mode (void)
1337 mono_gc_is_moving (void)
1343 mono_gc_is_disabled (void)
1345 if (GC_dont_gc || g_hasenv ("GC_DONT_GC"))
1352 mono_gc_wbarrier_range_copy (gpointer _dest, gpointer _src, int size)
1354 g_assert_not_reached ();
1358 mono_gc_get_range_copy_func (void)
1360 return &mono_gc_wbarrier_range_copy;
1364 mono_gc_get_card_table (int *shift_bits, gpointer *card_mask)
1366 g_assert_not_reached ();
1371 mono_gc_card_table_nursery_check (void)
1373 g_assert_not_reached ();
1378 mono_gc_get_nursery (int *shift_bits, size_t *size)
1384 mono_gc_precise_stack_mark_enabled (void)
1390 mono_gc_get_logfile (void)
1396 mono_gc_params_set (const char* options)
1401 mono_gc_debug_set (const char* options)
1406 mono_gc_conservatively_scan_area (void *start, void *end)
1408 g_assert_not_reached ();
1412 mono_gc_scan_object (void *obj, void *gc_data)
1414 g_assert_not_reached ();
1419 mono_gc_get_bitmap_for_descr (void *descr, int *numbits)
1421 g_assert_not_reached ();
1426 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
1431 mono_gc_set_stack_end (void *stack_end)
1435 void mono_gc_set_skip_thread (gboolean value)
1440 mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
1445 /* This assertion is not valid when GC_DEBUG is defined */
1446 g_assert (GC_base (obj) == (char*)obj - offset);
1449 GC_REGISTER_FINALIZER_NO_ORDER ((char*)obj - offset, (GC_finalization_proc)user_data, GUINT_TO_POINTER (offset), NULL, NULL);
1454 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
1456 /* it is being replaced by GC_pthread_create on some
1457 * platforms, see libgc/include/gc_pthread_redirects.h */
1458 return pthread_create (new_thread, attr, start_routine, arg);
1463 BOOL APIENTRY mono_gc_dllmain (HMODULE module_handle, DWORD reason, LPVOID reserved)
1465 return GC_DllMain (module_handle, reason, reserved);
1470 mono_gc_get_vtable_bits (MonoClass *klass)
1472 if (fin_callbacks.is_class_finalization_aware) {
1473 if (fin_callbacks.is_class_finalization_aware (klass))
1474 return BOEHM_GC_BIT_FINALIZER_AWARE;
1480 * mono_gc_register_altstack:
1482 * Register the dimensions of the normal stack and altstack with the collector.
1483 * Currently, STACK/STACK_SIZE is only used when the thread is suspended while it is on an altstack.
1486 mono_gc_register_altstack (gpointer stack, gint32 stack_size, gpointer altstack, gint32 altstack_size)
1488 GC_register_altstack (stack, stack_size, altstack, altstack_size);
1492 mono_gc_get_los_limit (void)
1498 mono_gc_set_string_length (MonoString *str, gint32 new_length)
1500 mono_unichar2 *new_end = str->chars + new_length;
1502 /* zero the discarded string. This null-delimits the string and allows
1503 * the space to be reclaimed by SGen. */
1505 memset (new_end, 0, (str->length - new_length + 1) * sizeof (mono_unichar2));
1506 str->length = new_length;
1510 mono_gc_user_markers_supported (void)
1516 mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
1518 g_assert_not_reached ();
1522 /* Toggleref support */
1525 mono_gc_toggleref_add (MonoObject *object, mono_bool strong_ref)
1527 if (GC_toggleref_add ((GC_PTR)object, (int)strong_ref) != GC_SUCCESS)
1528 g_error ("GC_toggleref_add failed\n");
1532 mono_gc_toggleref_register_callback (MonoToggleRefStatus (*proccess_toggleref) (MonoObject *obj))
1534 GC_set_toggleref_func ((GC_ToggleRefStatus (*) (GC_PTR obj)) proccess_toggleref);
1537 /* Test support code */
1539 static MonoToggleRefStatus
1540 test_toggleref_callback (MonoObject *obj)
1542 static MonoClassField *mono_toggleref_test_field;
1543 MonoToggleRefStatus status = MONO_TOGGLE_REF_DROP;
1545 if (!mono_toggleref_test_field) {
1546 mono_toggleref_test_field = mono_class_get_field_from_name (mono_object_get_class (obj), "__test");
1547 g_assert (mono_toggleref_test_field);
1550 mono_field_get_value (obj, mono_toggleref_test_field, &status);
1551 printf ("toggleref-cb obj %d\n", status);
1556 register_test_toggleref_callback (void)
1558 mono_gc_toggleref_register_callback (test_toggleref_callback);
1562 is_finalization_aware (MonoObject *obj)
1564 MonoVTable *vt = obj->vtable;
1565 return (vt->gc_bits & BOEHM_GC_BIT_FINALIZER_AWARE) == BOEHM_GC_BIT_FINALIZER_AWARE;
1569 fin_notifier (MonoObject *obj)
1571 if (is_finalization_aware (obj))
1572 fin_callbacks.object_queued_for_finalization (obj);
1576 mono_gc_register_finalizer_callbacks (MonoGCFinalizerCallbacks *callbacks)
1578 if (callbacks->version != MONO_GC_FINALIZER_EXTENSION_VERSION)
1579 g_error ("Invalid finalizer callback version. Expected %d but got %d\n", MONO_GC_FINALIZER_EXTENSION_VERSION, callbacks->version);
1581 fin_callbacks = *callbacks;
1583 GC_set_await_finalize_proc ((void (*) (GC_PTR))fin_notifier);
1586 #define BITMAP_SIZE (sizeof (*((HandleData *)NULL)->bitmap) * CHAR_BIT)
1588 static inline gboolean
1589 slot_occupied (HandleData *handles, guint slot) {
1590 return handles->bitmap [slot / BITMAP_SIZE] & (1 << (slot % BITMAP_SIZE));
1594 vacate_slot (HandleData *handles, guint slot) {
1595 handles->bitmap [slot / BITMAP_SIZE] &= ~(1 << (slot % BITMAP_SIZE));
1599 occupy_slot (HandleData *handles, guint slot) {
1600 handles->bitmap [slot / BITMAP_SIZE] |= 1 << (slot % BITMAP_SIZE);
1604 find_first_unset (guint32 bitmap)
1607 for (i = 0; i < 32; ++i) {
1608 if (!(bitmap & (1 << i)))
1615 handle_data_alloc_entries (HandleData *handles)
1618 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1619 handles->entries = (void **)g_malloc0 (sizeof (*handles->entries) * handles->size);
1620 handles->domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * handles->size);
1622 handles->entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * handles->size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
1624 handles->bitmap = (guint32 *)g_malloc0 (handles->size / CHAR_BIT);
1628 handle_data_next_unset (HandleData *handles)
1631 for (slot = handles->slot_hint; slot < handles->size / BITMAP_SIZE; ++slot) {
1632 if (handles->bitmap [slot] == 0xffffffff)
1634 handles->slot_hint = slot;
1635 return find_first_unset (handles->bitmap [slot]);
1641 handle_data_first_unset (HandleData *handles)
1644 for (slot = 0; slot < handles->slot_hint; ++slot) {
1645 if (handles->bitmap [slot] == 0xffffffff)
1647 handles->slot_hint = slot;
1648 return find_first_unset (handles->bitmap [slot]);
1653 /* Returns the index of the current slot in the bitmap. */
1655 handle_data_grow (HandleData *handles, gboolean track)
1657 guint32 *new_bitmap;
1658 guint32 new_size = handles->size * 2; /* always double: we memset to 0 based on this below */
1660 /* resize and copy the bitmap */
1661 new_bitmap = (guint32 *)g_malloc0 (new_size / CHAR_BIT);
1662 memcpy (new_bitmap, handles->bitmap, handles->size / CHAR_BIT);
1663 g_free (handles->bitmap);
1664 handles->bitmap = new_bitmap;
1666 /* resize and copy the entries */
1667 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1669 guint16 *domain_ids;
1671 domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * new_size);
1672 entries = (void **)g_malloc0 (sizeof (*handles->entries) * new_size);
1673 memcpy (domain_ids, handles->domain_ids, sizeof (*handles->domain_ids) * handles->size);
1674 for (i = 0; i < handles->size; ++i) {
1675 MonoObject *obj = mono_gc_weak_link_get (&(handles->entries [i]));
1677 mono_gc_weak_link_add (&(entries [i]), obj, track);
1678 mono_gc_weak_link_remove (&(handles->entries [i]), track);
1680 g_assert (!handles->entries [i]);
1683 g_free (handles->entries);
1684 g_free (handles->domain_ids);
1685 handles->entries = entries;
1686 handles->domain_ids = domain_ids;
1689 entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * new_size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
1690 mono_gc_memmove_aligned (entries, handles->entries, sizeof (*handles->entries) * handles->size);
1691 mono_gc_free_fixed (handles->entries);
1692 handles->entries = entries;
1694 handles->slot_hint = handles->size / BITMAP_SIZE;
1695 handles->size = new_size;
1699 alloc_handle (HandleData *handles, MonoObject *obj, gboolean track)
1703 lock_handles (handles);
1705 handle_data_alloc_entries (handles);
1706 i = handle_data_next_unset (handles);
1707 if (i == -1 && handles->slot_hint != 0)
1708 i = handle_data_first_unset (handles);
1710 handle_data_grow (handles, track);
1713 slot = handles->slot_hint * BITMAP_SIZE + i;
1714 occupy_slot (handles, slot);
1715 handles->entries [slot] = NULL;
1716 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1717 /*FIXME, what to use when obj == null?*/
1718 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
1720 mono_gc_weak_link_add (&(handles->entries [slot]), obj, track);
1722 handles->entries [slot] = obj;
1725 #ifndef DISABLE_PERFCOUNTERS
1726 mono_perfcounters->gc_num_handles++;
1728 unlock_handles (handles);
1729 res = MONO_GC_HANDLE (slot, handles->type);
1730 MONO_PROFILER_RAISE (gc_handle_created, (res, handles->type, obj));
1735 * mono_gchandle_new:
1736 * \param obj managed object to get a handle for
1737 * \param pinned whether the object should be pinned
1739 * This returns a handle that wraps the object, this is used to keep a
1740 * reference to a managed object from the unmanaged world and preventing the
1741 * object from being disposed.
1743 * If \p pinned is false the address of the object can not be obtained, if it is
1744 * true the address of the object can be obtained. This will also pin the
1745 * object so it will not be possible by a moving garbage collector to move the
1748 * \returns a handle that can be used to access the object from
1752 mono_gchandle_new (MonoObject *obj, gboolean pinned)
1754 return alloc_handle (&gc_handles [pinned? HANDLE_PINNED: HANDLE_NORMAL], obj, FALSE);
1758 * mono_gchandle_new_weakref:
1759 * \param obj managed object to get a handle for
1760 * \param track_resurrection Determines how long to track the object, if this is set to TRUE, the object is tracked after finalization, if FALSE, the object is only tracked up until the point of finalization.
1762 * This returns a weak handle that wraps the object, this is used to
1763 * keep a reference to a managed object from the unmanaged world.
1764 * Unlike the \c mono_gchandle_new the object can be reclaimed by the
1765 * garbage collector. In this case the value of the GCHandle will be
1768 * If \p track_resurrection is TRUE the object will be tracked through
1769 * finalization and if the object is resurrected during the execution
1770 * of the finalizer, then the returned weakref will continue to hold
1771 * a reference to the object. If \p track_resurrection is FALSE, then
1772 * the weak reference's target will become NULL as soon as the object
1773 * is passed on to the finalizer.
1775 * \returns a handle that can be used to access the object from
1779 mono_gchandle_new_weakref (MonoObject *obj, gboolean track_resurrection)
1781 return alloc_handle (&gc_handles [track_resurrection? HANDLE_WEAK_TRACK: HANDLE_WEAK], obj, track_resurrection);
1785 * mono_gchandle_get_target:
1786 * \param gchandle a GCHandle's handle.
1788 * The handle was previously created by calling \c mono_gchandle_new or
1789 * \c mono_gchandle_new_weakref.
1791 * \returns A pointer to the \c MonoObject* represented by the handle or
1792 * NULL for a collected object if using a weakref handle.
1795 mono_gchandle_get_target (guint32 gchandle)
1797 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1798 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1799 HandleData *handles = &gc_handles [type];
1800 MonoObject *obj = NULL;
1801 if (type >= HANDLE_TYPE_MAX)
1804 lock_handles (handles);
1805 if (slot < handles->size && slot_occupied (handles, slot)) {
1806 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1807 obj = mono_gc_weak_link_get (&handles->entries [slot]);
1809 obj = (MonoObject *)handles->entries [slot];
1812 /* print a warning? */
1814 unlock_handles (handles);
1815 /*g_print ("get target of entry %d of type %d: %p\n", slot, handles->type, obj);*/
1820 mono_gchandle_set_target (guint32 gchandle, MonoObject *obj)
1822 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1823 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1824 HandleData *handles = &gc_handles [type];
1825 MonoObject *old_obj = NULL;
1827 g_assert (type < HANDLE_TYPE_MAX);
1828 lock_handles (handles);
1829 if (slot < handles->size && slot_occupied (handles, slot)) {
1830 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1831 old_obj = (MonoObject *)handles->entries [slot];
1832 if (handles->entries [slot])
1833 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1835 mono_gc_weak_link_add (&handles->entries [slot], obj, handles->type == HANDLE_WEAK_TRACK);
1836 /*FIXME, what to use when obj == null?*/
1837 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
1839 handles->entries [slot] = obj;
1842 /* print a warning? */
1844 /*g_print ("changed entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
1845 unlock_handles (handles);
1849 mono_gc_is_null (void)
1855 * mono_gchandle_is_in_domain:
1856 * \param gchandle a GCHandle's handle.
1857 * \param domain An application domain.
1859 * Use this function to determine if the \p gchandle points to an
1860 * object allocated in the specified \p domain.
1862 * \returns TRUE if the object wrapped by the \p gchandle belongs to the specific \p domain.
1865 mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain)
1867 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1868 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1869 HandleData *handles = &gc_handles [type];
1870 gboolean result = FALSE;
1872 if (type >= HANDLE_TYPE_MAX)
1875 lock_handles (handles);
1876 if (slot < handles->size && slot_occupied (handles, slot)) {
1877 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1878 result = domain->domain_id == handles->domain_ids [slot];
1881 obj = (MonoObject *)handles->entries [slot];
1885 result = domain == mono_object_domain (obj);
1888 /* print a warning? */
1890 unlock_handles (handles);
1895 * mono_gchandle_free:
1896 * \param gchandle a GCHandle's handle.
1898 * Frees the \p gchandle handle. If there are no outstanding
1899 * references, the garbage collector can reclaim the memory of the
1903 mono_gchandle_free (guint32 gchandle)
1905 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1906 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1907 HandleData *handles = &gc_handles [type];
1908 if (type >= HANDLE_TYPE_MAX)
1911 lock_handles (handles);
1912 if (slot < handles->size && slot_occupied (handles, slot)) {
1913 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1914 if (handles->entries [slot])
1915 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1917 handles->entries [slot] = NULL;
1919 vacate_slot (handles, slot);
1921 /* print a warning? */
1923 #ifndef DISABLE_PERFCOUNTERS
1924 mono_perfcounters->gc_num_handles--;
1926 /*g_print ("freed entry %d of type %d\n", slot, handles->type);*/
1927 unlock_handles (handles);
1928 MONO_PROFILER_RAISE (gc_handle_deleted, (gchandle, handles->type));
1932 * mono_gchandle_free_domain:
1933 * \param domain domain that is unloading
1935 * Function used internally to cleanup any GC handle for objects belonging
1936 * to the specified domain during appdomain unload.
1939 mono_gchandle_free_domain (MonoDomain *domain)
1943 for (type = HANDLE_TYPE_MIN; type < HANDLE_PINNED; ++type) {
1945 HandleData *handles = &gc_handles [type];
1946 lock_handles (handles);
1947 for (slot = 0; slot < handles->size; ++slot) {
1948 if (!slot_occupied (handles, slot))
1950 if (MONO_GC_HANDLE_TYPE_IS_WEAK (type)) {
1951 if (domain->domain_id == handles->domain_ids [slot]) {
1952 vacate_slot (handles, slot);
1953 if (handles->entries [slot])
1954 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1957 if (handles->entries [slot] && mono_object_domain (handles->entries [slot]) == domain) {
1958 vacate_slot (handles, slot);
1959 handles->entries [slot] = NULL;
1963 unlock_handles (handles);
1969 MONO_EMPTY_SOURCE_FILE (boehm_gc);
1970 #endif /* no Boehm GC */