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>
39 #include <mono/utils/unlocked.h>
45 #define THREAD_LOCAL_ALLOC 1
46 #include "private/pthread_support.h"
48 #if defined(HOST_DARWIN) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
49 void *pthread_get_stackaddr_np(pthread_t);
52 #define GC_NO_DESCRIPTOR ((gpointer)(0 | GC_DS_LENGTH))
53 /*Boehm max heap cannot be smaller than 16MB*/
54 #define MIN_BOEHM_MAX_HEAP_SIZE_IN_MB 16
55 #define MIN_BOEHM_MAX_HEAP_SIZE (MIN_BOEHM_MAX_HEAP_SIZE_IN_MB << 20)
57 static gboolean gc_initialized = FALSE;
58 static mono_mutex_t mono_gc_lock;
60 typedef void (*GC_push_other_roots_proc)(void);
62 static GC_push_other_roots_proc default_push_other_roots;
63 static GHashTable *roots;
66 mono_push_other_roots(void);
69 register_test_toggleref_callback (void);
71 #define BOEHM_GC_BIT_FINALIZER_AWARE 1
72 static MonoGCFinalizerCallbacks fin_callbacks;
76 static mono_mutex_t handle_section;
77 #define lock_handles(handles) mono_os_mutex_lock (&handle_section)
78 #define unlock_handles(handles) mono_os_mutex_unlock (&handle_section)
85 guint slot_hint : 24; /* starting slot for search in bitmap */
86 /* 2^16 appdomains should be enough for everyone (though I know I'll regret this in 20 years) */
87 /* we alloc this only for weak refs, since we can get the domain directly in the other cases */
91 #define EMPTY_HANDLE_DATA(type) {NULL, NULL, 0, (type), 0, NULL}
93 /* weak and weak-track arrays will be allocated in malloc memory
95 static HandleData gc_handles [] = {
96 EMPTY_HANDLE_DATA (HANDLE_WEAK),
97 EMPTY_HANDLE_DATA (HANDLE_WEAK_TRACK),
98 EMPTY_HANDLE_DATA (HANDLE_NORMAL),
99 EMPTY_HANDLE_DATA (HANDLE_PINNED)
103 mono_gc_warning (char *msg, GC_word arg)
105 mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_GC, msg, (unsigned long)arg);
108 static void on_gc_notification (GC_EventType event);
109 static void on_gc_heap_resize (size_t new_size);
112 mono_gc_base_init (void)
119 mono_counters_init ();
122 mono_w32handle_init ();
126 * Handle the case when we are called from a thread different from the main thread,
128 * FIXME: Move this to libgc where it belongs.
130 * we used to do this only when running on valgrind,
131 * but it happens also in other setups.
133 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
138 pthread_getattr_np (pthread_self (), &attr);
139 pthread_attr_getstack (&attr, &sstart, &size);
140 pthread_attr_destroy (&attr);
141 /*g_print ("stackbottom pth is: %p\n", (char*)sstart + size);*/
142 /* apparently with some linuxthreads implementations sstart can be NULL,
143 * fallback to the more imprecise method (bug# 78096).
146 GC_stackbottom = (char*)sstart + size;
149 gsize stack_bottom = (gsize)&dummy;
150 stack_bottom += 4095;
151 stack_bottom &= ~4095;
152 GC_stackbottom = (char*)stack_bottom;
155 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
156 GC_stackbottom = (char*)pthread_get_stackaddr_np (pthread_self ());
157 #elif defined(__OpenBSD__)
158 # include <pthread_np.h>
163 rslt = pthread_stackseg_np(pthread_self(), &ss);
164 g_assert (rslt == 0);
166 GC_stackbottom = (char*)ss.ss_sp;
171 gsize stack_bottom = (gsize)&dummy;
172 stack_bottom += 4095;
173 stack_bottom &= ~4095;
174 /*g_print ("stackbottom is: %p\n", (char*)stack_bottom);*/
175 GC_stackbottom = (char*)stack_bottom;
179 roots = g_hash_table_new (NULL, NULL);
180 default_push_other_roots = GC_push_other_roots;
181 GC_push_other_roots = mono_push_other_roots;
183 #if !defined(HOST_ANDROID)
184 /* If GC_no_dls is set to true, GC_find_limit is not called. This causes a seg fault on Android. */
188 if ((env = g_getenv ("MONO_GC_DEBUG"))) {
189 char **opts = g_strsplit (env, ",", -1);
190 for (char **ptr = opts; ptr && *ptr; ptr ++) {
192 if (!strcmp (opt, "do-not-finalize")) {
193 mono_do_not_finalize = 1;
194 } else if (!strcmp (opt, "log-finalizers")) {
204 GC_set_warn_proc (mono_gc_warning);
205 GC_finalize_on_demand = 1;
206 GC_finalizer_notifier = mono_gc_finalize_notify;
208 GC_init_gcj_malloc (5, NULL);
209 GC_allow_register_threads ();
211 if ((env = g_getenv ("MONO_GC_PARAMS"))) {
212 char **ptr, **opts = g_strsplit (env, ",", -1);
213 for (ptr = opts; *ptr; ++ptr) {
215 if (g_str_has_prefix (opt, "max-heap-size=")) {
218 opt = strchr (opt, '=') + 1;
219 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &max_heap)) {
220 if (max_heap < MIN_BOEHM_MAX_HEAP_SIZE) {
221 fprintf (stderr, "max-heap-size must be at least %dMb.\n", MIN_BOEHM_MAX_HEAP_SIZE_IN_MB);
224 GC_set_max_heap_size (max_heap);
226 fprintf (stderr, "max-heap-size must be an integer.\n");
230 } else if (g_str_has_prefix (opt, "toggleref-test")) {
231 register_test_toggleref_callback ();
234 /* Could be a parameter for sgen */
236 fprintf (stderr, "MONO_GC_PARAMS must be a comma-delimited list of one or more of the following:\n");
237 fprintf (stderr, " max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
246 mono_thread_callbacks_init ();
247 mono_thread_info_init (sizeof (MonoThreadInfo));
248 mono_os_mutex_init (&mono_gc_lock);
249 mono_os_mutex_init_recursive (&handle_section);
251 mono_thread_info_attach ();
253 GC_set_on_collection_event (on_gc_notification);
254 GC_on_heap_resize = on_gc_heap_resize;
256 gc_initialized = TRUE;
260 mono_gc_base_cleanup (void)
262 GC_finalizer_notifier = NULL;
267 * \param generation GC generation identifier
269 * Perform a garbage collection for the given generation, higher numbers
270 * mean usually older objects. Collecting a high-numbered generation
271 * implies collecting also the lower-numbered generations.
272 * The maximum value for \p generation can be retrieved with a call to
273 * \c mono_gc_max_generation, so this function is usually called as:
275 * <code>mono_gc_collect (mono_gc_max_generation ());</code>
278 mono_gc_collect (int generation)
280 #ifndef DISABLE_PERFCOUNTERS
281 mono_perfcounters->gc_induced++;
287 * mono_gc_max_generation:
289 * Get the maximum generation number used by the current garbage
290 * collector. The value will be 0 for the Boehm collector, 1 or more
291 * for the generational collectors.
293 * Returns: the maximum generation number.
296 mono_gc_max_generation (void)
302 * mono_gc_get_generation:
303 * \param object a managed object
305 * Get the garbage collector's generation that \p object belongs to.
306 * Use this has a hint only.
308 * \returns a garbage collector generation number
311 mono_gc_get_generation (MonoObject *object)
317 * mono_gc_collection_count:
318 * \param generation a GC generation number
320 * Get how many times a garbage collection has been performed
321 * for the given \p generation number.
323 * \returns the number of garbage collections
326 mono_gc_collection_count (int generation)
332 * mono_gc_add_memory_pressure:
333 * \param value amount of bytes
335 * Adjust the garbage collector's view of how many bytes of memory
336 * are indirectly referenced by managed objects (for example unmanaged
337 * memory holding image or other binary data).
338 * This is a hint only to the garbage collector algorithm.
339 * Note that negative amounts of p value will decrease the memory
343 mono_gc_add_memory_pressure (gint64 value)
348 * mono_gc_get_used_size:
350 * Get the approximate amount of memory used by managed objects.
352 * Returns: the amount of memory used in bytes
355 mono_gc_get_used_size (void)
357 return GC_get_heap_size () - GC_get_free_bytes ();
361 * mono_gc_get_heap_size:
363 * Get the amount of memory used by the garbage collector.
365 * Returns: the size of the heap in bytes
368 mono_gc_get_heap_size (void)
370 return GC_get_heap_size ();
374 mono_gc_is_gc_thread (void)
376 return GC_thread_is_registered ();
380 mono_gc_thread_attach (MonoThreadInfo* info)
382 struct GC_stack_base sb;
385 /* TODO: use GC_get_stack_base instead of baseptr. */
386 sb.mem_base = info->stack_end;
387 res = GC_register_my_thread (&sb);
388 if (res == GC_UNIMPLEMENTED)
389 return NULL; /* Cannot happen with GC v7+. */
391 info->handle_stack = mono_handle_stack_alloc ();
397 mono_gc_thread_detach_with_lock (MonoThreadInfo *p)
399 MonoNativeThreadId tid;
401 tid = mono_thread_info_get_tid (p);
403 if (p->runtime_thread)
404 mono_threads_add_joinable_thread ((gpointer)tid);
406 mono_handle_stack_free (p->handle_stack);
410 mono_gc_thread_in_critical_region (MonoThreadInfo *info)
416 mono_object_is_alive (MonoObject* o)
418 return GC_is_marked ((ptr_t)o);
422 mono_gc_walk_heap (int flags, MonoGCReferences callback, void *data)
427 static gint64 gc_start_time;
430 on_gc_notification (GC_EventType event)
432 MonoProfilerGCEvent e;
435 case GC_EVENT_PRE_STOP_WORLD:
436 e = MONO_GC_EVENT_PRE_STOP_WORLD;
437 MONO_GC_WORLD_STOP_BEGIN ();
440 case GC_EVENT_POST_STOP_WORLD:
441 e = MONO_GC_EVENT_POST_STOP_WORLD;
442 MONO_GC_WORLD_STOP_END ();
445 case GC_EVENT_PRE_START_WORLD:
446 e = MONO_GC_EVENT_PRE_START_WORLD;
447 MONO_GC_WORLD_RESTART_BEGIN (1);
450 case GC_EVENT_POST_START_WORLD:
451 e = MONO_GC_EVENT_POST_START_WORLD;
452 MONO_GC_WORLD_RESTART_END (1);
456 e = MONO_GC_EVENT_START;
458 #ifndef DISABLE_PERFCOUNTERS
459 if (mono_perfcounters)
460 mono_perfcounters->gc_collections0++;
462 InterlockedIncrement (&gc_stats.major_gc_count);
463 gc_start_time = mono_100ns_ticks ();
467 e = MONO_GC_EVENT_END;
469 #if defined(ENABLE_DTRACE) && defined(__sun__)
470 /* This works around a dtrace -G problem on Solaris.
471 Limit its actual use to when the probe is enabled. */
472 if (MONO_GC_END_ENABLED ())
476 #ifndef DISABLE_PERFCOUNTERS
477 if (mono_perfcounters) {
478 guint64 heap_size = GC_get_heap_size ();
479 guint64 used_size = heap_size - GC_get_free_bytes ();
480 mono_perfcounters->gc_total_bytes = used_size;
481 mono_perfcounters->gc_committed_bytes = heap_size;
482 mono_perfcounters->gc_reserved_bytes = heap_size;
483 mono_perfcounters->gc_gen0size = heap_size;
486 UnlockedAdd64 (&gc_stats.major_gc_time, mono_100ns_ticks () - gc_start_time);
487 mono_trace_message (MONO_TRACE_GC, "gc took %" G_GINT64_FORMAT " usecs", (mono_100ns_ticks () - gc_start_time) / 10);
494 case GC_EVENT_MARK_START:
495 case GC_EVENT_MARK_END:
496 case GC_EVENT_RECLAIM_START:
497 case GC_EVENT_RECLAIM_END:
500 MONO_PROFILER_RAISE (gc_event, (e, 0));
505 case GC_EVENT_PRE_STOP_WORLD:
506 mono_thread_info_suspend_lock ();
507 MONO_PROFILER_RAISE (gc_event, (MONO_GC_EVENT_PRE_STOP_WORLD_LOCKED, 0));
509 case GC_EVENT_POST_START_WORLD:
510 mono_thread_info_suspend_unlock ();
511 MONO_PROFILER_RAISE (gc_event, (MONO_GC_EVENT_POST_START_WORLD_UNLOCKED, 0));
520 on_gc_heap_resize (size_t new_size)
522 guint64 heap_size = GC_get_heap_size ();
523 #ifndef DISABLE_PERFCOUNTERS
524 if (mono_perfcounters) {
525 mono_perfcounters->gc_committed_bytes = heap_size;
526 mono_perfcounters->gc_reserved_bytes = heap_size;
527 mono_perfcounters->gc_gen0size = heap_size;
531 MONO_PROFILER_RAISE (gc_resize, (new_size));
540 register_root (gpointer arg)
542 RootData* root_data = arg;
543 g_hash_table_insert (roots, root_data->start, root_data->end);
548 mono_gc_register_root (char *start, size_t size, void *descr, MonoGCRootSource source, const char *msg)
551 root_data.start = start;
552 /* Boehm root processing requires one byte past end of region to be scanned */
553 root_data.end = start + size + 1;
554 GC_call_with_alloc_lock (register_root, &root_data);
560 mono_gc_register_root_wbarrier (char *start, size_t size, MonoGCDescriptor descr, MonoGCRootSource source, const char *msg)
562 return mono_gc_register_root (start, size, descr, source, msg);
566 deregister_root (gpointer arg)
568 gboolean removed = g_hash_table_remove (roots, arg);
574 mono_gc_deregister_root (char* addr)
576 GC_call_with_alloc_lock (deregister_root, addr);
580 push_root (gpointer key, gpointer value, gpointer user_data)
582 GC_push_all (key, value);
586 mono_push_other_roots (void)
588 g_hash_table_foreach (roots, push_root, NULL);
589 if (default_push_other_roots)
590 default_push_other_roots ();
594 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
596 /* libgc requires that we use HIDE_POINTER... */
597 *link_addr = (void*)HIDE_POINTER (obj);
599 GC_REGISTER_LONG_LINK (link_addr, obj);
601 GC_GENERAL_REGISTER_DISAPPEARING_LINK (link_addr, obj);
605 mono_gc_weak_link_remove (void **link_addr, gboolean track)
608 GC_unregister_long_link (link_addr);
610 GC_unregister_disappearing_link (link_addr);
615 reveal_link (gpointer link_addr)
617 void **link_a = (void **)link_addr;
618 return REVEAL_POINTER (*link_a);
622 mono_gc_weak_link_get (void **link_addr)
624 MonoObject *obj = (MonoObject *)GC_call_with_alloc_lock (reveal_link, link_addr);
625 if (obj == (MonoObject *) -1)
631 mono_gc_make_descr_for_string (gsize *bitmap, int numbits)
633 return mono_gc_make_descr_from_bitmap (bitmap, numbits);
637 mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size)
639 return mono_gc_make_descr_from_bitmap (bitmap, numbits);
643 mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size)
645 /* libgc has no usable support for arrays... */
646 return GC_NO_DESCRIPTOR;
650 mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
652 /* It seems there are issues when the bitmap doesn't fit: play it safe */
654 return GC_NO_DESCRIPTOR;
656 return (gpointer)GC_make_descriptor ((GC_bitmap)bitmap, numbits);
660 mono_gc_make_vector_descr (void)
666 mono_gc_make_root_descr_all_refs (int numbits)
672 mono_gc_alloc_fixed (size_t size, void *descr, MonoGCRootSource source, const char *msg)
674 return GC_MALLOC_UNCOLLECTABLE (size);
678 mono_gc_free_fixed (void* addr)
684 mono_gc_alloc_obj (MonoVTable *vtable, size_t size)
688 if (!vtable->klass->has_references) {
689 obj = (MonoObject *)GC_MALLOC_ATOMIC (size);
690 if (G_UNLIKELY (!obj))
693 obj->vtable = vtable;
694 obj->synchronisation = NULL;
696 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
697 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
698 obj = (MonoObject *)GC_GCJ_MALLOC (size, vtable);
699 if (G_UNLIKELY (!obj))
702 obj = (MonoObject *)GC_MALLOC (size);
703 if (G_UNLIKELY (!obj))
706 obj->vtable = vtable;
709 if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
710 MONO_PROFILER_RAISE (gc_allocation, (obj));
716 mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length)
720 if (!vtable->klass->has_references) {
721 obj = (MonoArray *)GC_MALLOC_ATOMIC (size);
722 if (G_UNLIKELY (!obj))
725 obj->obj.vtable = vtable;
726 obj->obj.synchronisation = NULL;
728 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
729 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
730 obj = (MonoArray *)GC_GCJ_MALLOC (size, vtable);
731 if (G_UNLIKELY (!obj))
734 obj = (MonoArray *)GC_MALLOC (size);
735 if (G_UNLIKELY (!obj))
738 obj->obj.vtable = vtable;
741 obj->max_length = max_length;
743 if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
744 MONO_PROFILER_RAISE (gc_allocation, (&obj->obj));
750 mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uintptr_t bounds_size)
754 if (!vtable->klass->has_references) {
755 obj = (MonoArray *)GC_MALLOC_ATOMIC (size);
756 if (G_UNLIKELY (!obj))
759 obj->obj.vtable = vtable;
760 obj->obj.synchronisation = NULL;
762 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
763 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
764 obj = (MonoArray *)GC_GCJ_MALLOC (size, vtable);
765 if (G_UNLIKELY (!obj))
768 obj = (MonoArray *)GC_MALLOC (size);
769 if (G_UNLIKELY (!obj))
772 obj->obj.vtable = vtable;
775 obj->max_length = max_length;
778 obj->bounds = (MonoArrayBounds *) ((char *) obj + size - bounds_size);
780 if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
781 MONO_PROFILER_RAISE (gc_allocation, (&obj->obj));
787 mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len)
789 MonoString *obj = (MonoString *)GC_MALLOC_ATOMIC (size);
790 if (G_UNLIKELY (!obj))
793 obj->object.vtable = vtable;
794 obj->object.synchronisation = NULL;
796 obj->chars [len] = 0;
798 if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
799 MONO_PROFILER_RAISE (gc_allocation, (&obj->object));
805 mono_gc_alloc_mature (MonoVTable *vtable, size_t size)
807 return mono_gc_alloc_obj (vtable, size);
811 mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size)
813 return mono_gc_alloc_obj (vtable, size);
817 mono_gc_invoke_finalizers (void)
819 /* There is a bug in GC_invoke_finalizer () in versions <= 6.2alpha4:
820 * the 'mem_freed' variable is not initialized when there are no
821 * objects to finalize, which leads to strange behavior later on.
822 * The check is necessary to work around that bug.
824 if (GC_should_invoke_finalizers ())
825 return GC_invoke_finalizers ();
830 mono_gc_pending_finalizers (void)
832 return GC_should_invoke_finalizers ();
836 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
838 *(void**)field_ptr = value;
842 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
844 *(void**)slot_ptr = value;
848 mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
850 mono_gc_memmove_aligned (dest_ptr, src_ptr, count * sizeof (gpointer));
854 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
856 *(void**)ptr = value;
860 mono_gc_wbarrier_generic_store_atomic (gpointer ptr, MonoObject *value)
862 InterlockedWritePointer ((volatile gpointer *)ptr, value);
866 mono_gc_wbarrier_generic_nostore (gpointer ptr)
871 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
873 mono_gc_memmove_atomic (dest, src, count * mono_class_value_size (klass, NULL));
877 mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
879 /* do not copy the sync state */
880 mono_gc_memmove_aligned ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
881 mono_object_class (obj)->instance_size - sizeof (MonoObject));
885 mono_gc_clear_domain (MonoDomain *domain)
890 mono_gc_suspend_finalizers (void)
895 mono_gc_get_suspend_signal (void)
897 return GC_get_suspend_signal ();
901 mono_gc_get_restart_signal (void)
903 return GC_get_thr_restart_signal ();
906 #if defined(USE_COMPILER_TLS) && defined(__linux__) && (defined(__i386__) || defined(__x86_64__))
907 extern __thread void* GC_thread_tls;
908 #include "metadata-internals.h"
914 while (!(v & (1 << i)))
921 ATYPE_FREEPTR_FOR_BOX,
929 create_allocator (int atype, int tls_key, gboolean slowpath)
931 int index_var, bytes_var, my_fl_var, my_entry_var;
932 guint32 no_freelist_branch, not_small_enough_branch = 0;
933 guint32 size_overflow_branch = 0;
934 MonoMethodBuilder *mb;
936 MonoMethodSignature *csig;
937 const char *name = NULL;
940 g_assert_not_reached ();
942 if (atype == ATYPE_FREEPTR) {
943 name = slowpath ? "SlowAllocPtrfree" : "AllocPtrfree";
944 } else if (atype == ATYPE_FREEPTR_FOR_BOX) {
945 name = slowpath ? "SlowAllocPtrfreeBox" : "AllocPtrfreeBox";
946 } else if (atype == ATYPE_NORMAL) {
947 name = slowpath ? "SlowAlloc" : "Alloc";
948 } else if (atype == ATYPE_GCJ) {
949 name = slowpath ? "SlowAllocGcj" : "AllocGcj";
950 } else if (atype == ATYPE_STRING) {
951 name = slowpath ? "SlowAllocString" : "AllocString";
953 g_assert_not_reached ();
956 csig = mono_metadata_signature_alloc (mono_defaults.corlib, 2);
958 if (atype == ATYPE_STRING) {
959 csig->ret = &mono_defaults.string_class->byval_arg;
960 csig->params [0] = &mono_defaults.int_class->byval_arg;
961 csig->params [1] = &mono_defaults.int32_class->byval_arg;
963 csig->ret = &mono_defaults.object_class->byval_arg;
964 csig->params [0] = &mono_defaults.int_class->byval_arg;
965 csig->params [1] = &mono_defaults.int32_class->byval_arg;
968 mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_ALLOC);
971 goto always_slowpath;
973 bytes_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
974 if (atype == ATYPE_STRING) {
975 /* a string alloator method takes the args: (vtable, len) */
976 /* bytes = (offsetof (MonoString, chars) + ((len + 1) * 2)); */
977 mono_mb_emit_ldarg (mb, 1);
978 mono_mb_emit_icon (mb, 1);
979 mono_mb_emit_byte (mb, MONO_CEE_ADD);
980 mono_mb_emit_icon (mb, 1);
981 mono_mb_emit_byte (mb, MONO_CEE_SHL);
982 // sizeof (MonoString) might include padding
983 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, chars));
984 mono_mb_emit_byte (mb, MONO_CEE_ADD);
985 mono_mb_emit_stloc (mb, bytes_var);
987 mono_mb_emit_ldarg (mb, 1);
988 mono_mb_emit_stloc (mb, bytes_var);
991 /* this is needed for strings/arrays only as the other big types are never allocated with this method */
992 if (atype == ATYPE_STRING) {
994 /* if (!SMALL_ENOUGH (bytes)) jump slow_path;*/
995 mono_mb_emit_ldloc (mb, bytes_var);
996 mono_mb_emit_icon (mb, (NFREELISTS-1) * GRANULARITY);
997 not_small_enough_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_UN_S);
998 /* check for overflow */
999 mono_mb_emit_ldloc (mb, bytes_var);
1000 mono_mb_emit_icon (mb, sizeof (MonoString));
1001 size_overflow_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLE_UN_S);
1004 /* int index = INDEX_FROM_BYTES(bytes); */
1005 index_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
1007 mono_mb_emit_ldloc (mb, bytes_var);
1008 mono_mb_emit_icon (mb, GRANULARITY - 1);
1009 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1010 mono_mb_emit_icon (mb, shift_amount (GRANULARITY));
1011 mono_mb_emit_byte (mb, MONO_CEE_SHR_UN);
1012 mono_mb_emit_icon (mb, shift_amount (sizeof (gpointer)));
1013 mono_mb_emit_byte (mb, MONO_CEE_SHL);
1014 /* index var is already adjusted into bytes */
1015 mono_mb_emit_stloc (mb, index_var);
1017 my_fl_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1018 my_entry_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1019 /* my_fl = ((GC_thread)tsd) -> ptrfree_freelists + index; */
1020 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
1021 mono_mb_emit_byte (mb, 0x0D); /* CEE_MONO_TLS */
1022 mono_mb_emit_i4 (mb, tls_key);
1023 if (atype == ATYPE_FREEPTR || atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING)
1024 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
1025 + G_STRUCT_OFFSET (struct thread_local_freelists,
1026 ptrfree_freelists));
1027 else if (atype == ATYPE_NORMAL)
1028 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
1029 + G_STRUCT_OFFSET (struct thread_local_freelists,
1031 else if (atype == ATYPE_GCJ)
1032 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
1033 + G_STRUCT_OFFSET (struct thread_local_freelists,
1036 g_assert_not_reached ();
1037 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1038 mono_mb_emit_ldloc (mb, index_var);
1039 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1040 mono_mb_emit_stloc (mb, my_fl_var);
1042 /* my_entry = *my_fl; */
1043 mono_mb_emit_ldloc (mb, my_fl_var);
1044 mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
1045 mono_mb_emit_stloc (mb, my_entry_var);
1047 /* if (EXPECT((word)my_entry >= HBLKSIZE, 1)) { */
1048 mono_mb_emit_ldloc (mb, my_entry_var);
1049 mono_mb_emit_icon (mb, HBLKSIZE);
1050 no_freelist_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
1052 /* ptr_t next = obj_link(my_entry); *my_fl = next; */
1053 mono_mb_emit_ldloc (mb, my_fl_var);
1054 mono_mb_emit_ldloc (mb, my_entry_var);
1055 mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
1056 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1058 /* set the vtable and clear the words in the object */
1059 mono_mb_emit_ldloc (mb, my_entry_var);
1060 mono_mb_emit_ldarg (mb, 0);
1061 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1063 if (atype == ATYPE_FREEPTR) {
1064 int start_var, end_var, start_loop;
1065 /* end = my_entry + bytes; start = my_entry + sizeof (gpointer);
1067 start_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1068 end_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1069 mono_mb_emit_ldloc (mb, my_entry_var);
1070 mono_mb_emit_ldloc (mb, bytes_var);
1071 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1072 mono_mb_emit_stloc (mb, end_var);
1073 mono_mb_emit_ldloc (mb, my_entry_var);
1074 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
1075 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1076 mono_mb_emit_stloc (mb, start_var);
1080 * } while (start < end);
1082 start_loop = mono_mb_get_label (mb);
1083 mono_mb_emit_ldloc (mb, start_var);
1084 mono_mb_emit_icon (mb, 0);
1085 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1086 mono_mb_emit_ldloc (mb, start_var);
1087 mono_mb_emit_icon (mb, sizeof (gpointer));
1088 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1089 mono_mb_emit_stloc (mb, start_var);
1091 mono_mb_emit_ldloc (mb, start_var);
1092 mono_mb_emit_ldloc (mb, end_var);
1093 mono_mb_emit_byte (mb, MONO_CEE_BLT_UN_S);
1094 mono_mb_emit_byte (mb, start_loop - (mono_mb_get_label (mb) + 1));
1095 } else if (atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING) {
1096 /* need to clear just the sync pointer */
1097 mono_mb_emit_ldloc (mb, my_entry_var);
1098 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
1099 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1100 mono_mb_emit_icon (mb, 0);
1101 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1104 if (atype == ATYPE_STRING) {
1105 /* need to set length and clear the last char */
1106 /* s->length = len; */
1107 mono_mb_emit_ldloc (mb, my_entry_var);
1108 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, length));
1109 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1110 mono_mb_emit_ldarg (mb, 1);
1111 mono_mb_emit_byte (mb, MONO_CEE_STIND_I4);
1112 /* s->chars [len] = 0; */
1113 mono_mb_emit_ldloc (mb, my_entry_var);
1114 mono_mb_emit_ldloc (mb, bytes_var);
1115 mono_mb_emit_icon (mb, 2);
1116 mono_mb_emit_byte (mb, MONO_CEE_SUB);
1117 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1118 mono_mb_emit_icon (mb, 0);
1119 mono_mb_emit_byte (mb, MONO_CEE_STIND_I2);
1122 /* return my_entry; */
1123 mono_mb_emit_ldloc (mb, my_entry_var);
1124 mono_mb_emit_byte (mb, MONO_CEE_RET);
1126 mono_mb_patch_short_branch (mb, no_freelist_branch);
1127 if (not_small_enough_branch > 0)
1128 mono_mb_patch_short_branch (mb, not_small_enough_branch);
1129 if (size_overflow_branch > 0)
1130 mono_mb_patch_short_branch (mb, size_overflow_branch);
1132 /* the slow path: we just call back into the runtime */
1134 if (atype == ATYPE_STRING) {
1135 mono_mb_emit_ldarg (mb, 1);
1136 mono_mb_emit_icall (mb, ves_icall_string_alloc);
1138 mono_mb_emit_ldarg (mb, 0);
1139 mono_mb_emit_icall (mb, ves_icall_object_new_specific);
1142 mono_mb_emit_byte (mb, MONO_CEE_RET);
1144 info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE);
1145 info->d.alloc.gc_name = "boehm";
1146 info->d.alloc.alloc_type = atype;
1147 mb->init_locals = FALSE;
1149 res = mono_mb_create (mb, csig, 8, info);
1155 static MonoMethod* alloc_method_cache [ATYPE_NUM];
1156 static MonoMethod* slowpath_alloc_method_cache [ATYPE_NUM];
1159 mono_gc_is_critical_method (MonoMethod *method)
1163 for (i = 0; i < ATYPE_NUM; ++i)
1164 if (method == alloc_method_cache [i] || method == slowpath_alloc_method_cache [i])
1171 * If possible, generate a managed method that can quickly allocate objects in class
1172 * @klass. The method will typically have an thread-local inline allocation sequence.
1173 * The signature of the called method is:
1174 * object allocate (MonoVTable *vtable)
1175 * The thread local alloc logic is taken from libgc/pthread_support.c.
1178 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size)
1183 * Tls implementation changed, we jump to tls native getters/setters.
1184 * Is boehm managed allocator ok with this ? Do we even care ?
1188 if (!SMALL_ENOUGH (klass->instance_size))
1190 if (mono_class_has_finalizer (klass) || mono_class_is_marshalbyref (klass))
1192 if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
1196 if (mono_class_is_open_constructed_type (&klass->byval_arg))
1198 if (klass->byval_arg.type == MONO_TYPE_STRING) {
1199 atype = ATYPE_STRING;
1200 } else if (!known_instance_size) {
1202 } else if (!klass->has_references) {
1204 atype = ATYPE_FREEPTR_FOR_BOX;
1206 atype = ATYPE_FREEPTR;
1210 * disabled because we currently do a runtime choice anyway, to
1211 * deal with multiple appdomains.
1212 if (vtable->gc_descr != GC_NO_DESCRIPTOR)
1215 atype = ATYPE_NORMAL;
1218 return mono_gc_get_managed_allocator_by_type (atype, MANAGED_ALLOCATOR_REGULAR);
1222 mono_gc_get_managed_array_allocator (MonoClass *klass)
1228 * mono_gc_get_managed_allocator_by_type:
1230 * Return a managed allocator method corresponding to allocator type ATYPE.
1233 mono_gc_get_managed_allocator_by_type (int atype, ManagedAllocatorVariant variant)
1236 gboolean slowpath = variant != MANAGED_ALLOCATOR_REGULAR;
1237 MonoMethod **cache = slowpath ? slowpath_alloc_method_cache : alloc_method_cache;
1241 res = cache [atype];
1245 res = create_allocator (atype, -1, slowpath);
1246 mono_os_mutex_lock (&mono_gc_lock);
1247 if (cache [atype]) {
1248 mono_free_method (res);
1249 res = cache [atype];
1251 mono_memory_barrier ();
1252 cache [atype] = res;
1254 mono_os_mutex_unlock (&mono_gc_lock);
1259 mono_gc_get_managed_allocator_types (void)
1265 mono_gc_get_write_barrier (void)
1267 g_assert_not_reached ();
1274 mono_gc_is_critical_method (MonoMethod *method)
1280 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size)
1286 mono_gc_get_managed_array_allocator (MonoClass *klass)
1292 mono_gc_get_managed_allocator_by_type (int atype, ManagedAllocatorVariant variant)
1298 mono_gc_get_managed_allocator_types (void)
1304 mono_gc_get_write_barrier (void)
1306 g_assert_not_reached ();
1313 mono_gc_get_specific_write_barrier (gboolean is_concurrent)
1315 g_assert_not_reached ();
1320 mono_gc_get_aligned_size_for_allocator (int size)
1326 mono_gc_get_gc_name (void)
1332 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
1334 return GC_call_with_alloc_lock (func, data);
1338 mono_gc_get_description (void)
1340 return g_strdup (DEFAULT_GC_NAME);
1344 mono_gc_set_desktop_mode (void)
1350 mono_gc_is_moving (void)
1356 mono_gc_is_disabled (void)
1358 if (GC_dont_gc || g_hasenv ("GC_DONT_GC"))
1365 mono_gc_wbarrier_range_copy (gpointer _dest, gpointer _src, int size)
1367 g_assert_not_reached ();
1371 mono_gc_get_range_copy_func (void)
1373 return &mono_gc_wbarrier_range_copy;
1377 mono_gc_get_card_table (int *shift_bits, gpointer *card_mask)
1379 g_assert_not_reached ();
1384 mono_gc_card_table_nursery_check (void)
1386 g_assert_not_reached ();
1391 mono_gc_get_nursery (int *shift_bits, size_t *size)
1397 mono_gc_precise_stack_mark_enabled (void)
1403 mono_gc_get_logfile (void)
1409 mono_gc_params_set (const char* options)
1414 mono_gc_debug_set (const char* options)
1419 mono_gc_conservatively_scan_area (void *start, void *end)
1421 g_assert_not_reached ();
1425 mono_gc_scan_object (void *obj, void *gc_data)
1427 g_assert_not_reached ();
1432 mono_gc_get_bitmap_for_descr (void *descr, int *numbits)
1434 g_assert_not_reached ();
1439 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
1444 mono_gc_set_stack_end (void *stack_end)
1448 void mono_gc_set_skip_thread (gboolean value)
1453 mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
1458 /* This assertion is not valid when GC_DEBUG is defined */
1459 g_assert (GC_base (obj) == (char*)obj - offset);
1462 GC_REGISTER_FINALIZER_NO_ORDER ((char*)obj - offset, (GC_finalization_proc)user_data, GUINT_TO_POINTER (offset), NULL, NULL);
1467 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
1469 /* it is being replaced by GC_pthread_create on some
1470 * platforms, see libgc/include/gc_pthread_redirects.h */
1471 return pthread_create (new_thread, attr, start_routine, arg);
1476 BOOL APIENTRY mono_gc_dllmain (HMODULE module_handle, DWORD reason, LPVOID reserved)
1478 return GC_DllMain (module_handle, reason, reserved);
1483 mono_gc_get_vtable_bits (MonoClass *klass)
1485 if (fin_callbacks.is_class_finalization_aware) {
1486 if (fin_callbacks.is_class_finalization_aware (klass))
1487 return BOEHM_GC_BIT_FINALIZER_AWARE;
1493 * mono_gc_register_altstack:
1495 * Register the dimensions of the normal stack and altstack with the collector.
1496 * Currently, STACK/STACK_SIZE is only used when the thread is suspended while it is on an altstack.
1499 mono_gc_register_altstack (gpointer stack, gint32 stack_size, gpointer altstack, gint32 altstack_size)
1501 GC_register_altstack (stack, stack_size, altstack, altstack_size);
1505 mono_gc_get_los_limit (void)
1511 mono_gc_set_string_length (MonoString *str, gint32 new_length)
1513 mono_unichar2 *new_end = str->chars + new_length;
1515 /* zero the discarded string. This null-delimits the string and allows
1516 * the space to be reclaimed by SGen. */
1518 memset (new_end, 0, (str->length - new_length + 1) * sizeof (mono_unichar2));
1519 str->length = new_length;
1523 mono_gc_user_markers_supported (void)
1529 mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
1531 g_assert_not_reached ();
1535 /* Toggleref support */
1538 mono_gc_toggleref_add (MonoObject *object, mono_bool strong_ref)
1540 if (GC_toggleref_add ((GC_PTR)object, (int)strong_ref) != GC_SUCCESS)
1541 g_error ("GC_toggleref_add failed\n");
1545 mono_gc_toggleref_register_callback (MonoToggleRefStatus (*proccess_toggleref) (MonoObject *obj))
1547 GC_set_toggleref_func ((GC_ToggleRefStatus (*) (GC_PTR obj)) proccess_toggleref);
1550 /* Test support code */
1552 static MonoToggleRefStatus
1553 test_toggleref_callback (MonoObject *obj)
1555 static MonoClassField *mono_toggleref_test_field;
1556 MonoToggleRefStatus status = MONO_TOGGLE_REF_DROP;
1558 if (!mono_toggleref_test_field) {
1559 mono_toggleref_test_field = mono_class_get_field_from_name (mono_object_get_class (obj), "__test");
1560 g_assert (mono_toggleref_test_field);
1563 mono_field_get_value (obj, mono_toggleref_test_field, &status);
1564 printf ("toggleref-cb obj %d\n", status);
1569 register_test_toggleref_callback (void)
1571 mono_gc_toggleref_register_callback (test_toggleref_callback);
1575 is_finalization_aware (MonoObject *obj)
1577 MonoVTable *vt = obj->vtable;
1578 return (vt->gc_bits & BOEHM_GC_BIT_FINALIZER_AWARE) == BOEHM_GC_BIT_FINALIZER_AWARE;
1582 fin_notifier (MonoObject *obj)
1584 if (is_finalization_aware (obj))
1585 fin_callbacks.object_queued_for_finalization (obj);
1589 mono_gc_register_finalizer_callbacks (MonoGCFinalizerCallbacks *callbacks)
1591 if (callbacks->version != MONO_GC_FINALIZER_EXTENSION_VERSION)
1592 g_error ("Invalid finalizer callback version. Expected %d but got %d\n", MONO_GC_FINALIZER_EXTENSION_VERSION, callbacks->version);
1594 fin_callbacks = *callbacks;
1596 GC_set_await_finalize_proc ((void (*) (GC_PTR))fin_notifier);
1599 #define BITMAP_SIZE (sizeof (*((HandleData *)NULL)->bitmap) * CHAR_BIT)
1601 static inline gboolean
1602 slot_occupied (HandleData *handles, guint slot) {
1603 return handles->bitmap [slot / BITMAP_SIZE] & (1 << (slot % BITMAP_SIZE));
1607 vacate_slot (HandleData *handles, guint slot) {
1608 handles->bitmap [slot / BITMAP_SIZE] &= ~(1 << (slot % BITMAP_SIZE));
1612 occupy_slot (HandleData *handles, guint slot) {
1613 handles->bitmap [slot / BITMAP_SIZE] |= 1 << (slot % BITMAP_SIZE);
1617 find_first_unset (guint32 bitmap)
1620 for (i = 0; i < 32; ++i) {
1621 if (!(bitmap & (1 << i)))
1628 handle_data_alloc_entries (HandleData *handles)
1631 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1632 handles->entries = (void **)g_malloc0 (sizeof (*handles->entries) * handles->size);
1633 handles->domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * handles->size);
1635 handles->entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * handles->size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
1637 handles->bitmap = (guint32 *)g_malloc0 (handles->size / CHAR_BIT);
1641 handle_data_next_unset (HandleData *handles)
1644 for (slot = handles->slot_hint; slot < handles->size / BITMAP_SIZE; ++slot) {
1645 if (handles->bitmap [slot] == 0xffffffff)
1647 handles->slot_hint = slot;
1648 return find_first_unset (handles->bitmap [slot]);
1654 handle_data_first_unset (HandleData *handles)
1657 for (slot = 0; slot < handles->slot_hint; ++slot) {
1658 if (handles->bitmap [slot] == 0xffffffff)
1660 handles->slot_hint = slot;
1661 return find_first_unset (handles->bitmap [slot]);
1666 /* Returns the index of the current slot in the bitmap. */
1668 handle_data_grow (HandleData *handles, gboolean track)
1670 guint32 *new_bitmap;
1671 guint32 new_size = handles->size * 2; /* always double: we memset to 0 based on this below */
1673 /* resize and copy the bitmap */
1674 new_bitmap = (guint32 *)g_malloc0 (new_size / CHAR_BIT);
1675 memcpy (new_bitmap, handles->bitmap, handles->size / CHAR_BIT);
1676 g_free (handles->bitmap);
1677 handles->bitmap = new_bitmap;
1679 /* resize and copy the entries */
1680 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1682 guint16 *domain_ids;
1684 domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * new_size);
1685 entries = (void **)g_malloc0 (sizeof (*handles->entries) * new_size);
1686 memcpy (domain_ids, handles->domain_ids, sizeof (*handles->domain_ids) * handles->size);
1687 for (i = 0; i < handles->size; ++i) {
1688 MonoObject *obj = mono_gc_weak_link_get (&(handles->entries [i]));
1690 mono_gc_weak_link_add (&(entries [i]), obj, track);
1691 mono_gc_weak_link_remove (&(handles->entries [i]), track);
1693 g_assert (!handles->entries [i]);
1696 g_free (handles->entries);
1697 g_free (handles->domain_ids);
1698 handles->entries = entries;
1699 handles->domain_ids = domain_ids;
1702 entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * new_size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
1703 mono_gc_memmove_aligned (entries, handles->entries, sizeof (*handles->entries) * handles->size);
1704 mono_gc_free_fixed (handles->entries);
1705 handles->entries = entries;
1707 handles->slot_hint = handles->size / BITMAP_SIZE;
1708 handles->size = new_size;
1712 alloc_handle (HandleData *handles, MonoObject *obj, gboolean track)
1716 lock_handles (handles);
1718 handle_data_alloc_entries (handles);
1719 i = handle_data_next_unset (handles);
1720 if (i == -1 && handles->slot_hint != 0)
1721 i = handle_data_first_unset (handles);
1723 handle_data_grow (handles, track);
1726 slot = handles->slot_hint * BITMAP_SIZE + i;
1727 occupy_slot (handles, slot);
1728 handles->entries [slot] = NULL;
1729 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1730 /*FIXME, what to use when obj == null?*/
1731 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
1733 mono_gc_weak_link_add (&(handles->entries [slot]), obj, track);
1735 handles->entries [slot] = obj;
1738 #ifndef DISABLE_PERFCOUNTERS
1739 mono_perfcounters->gc_num_handles++;
1741 unlock_handles (handles);
1742 res = MONO_GC_HANDLE (slot, handles->type);
1743 MONO_PROFILER_RAISE (gc_handle_created, (res, handles->type, obj));
1748 * mono_gchandle_new:
1749 * \param obj managed object to get a handle for
1750 * \param pinned whether the object should be pinned
1752 * This returns a handle that wraps the object, this is used to keep a
1753 * reference to a managed object from the unmanaged world and preventing the
1754 * object from being disposed.
1756 * If \p pinned is false the address of the object can not be obtained, if it is
1757 * true the address of the object can be obtained. This will also pin the
1758 * object so it will not be possible by a moving garbage collector to move the
1761 * \returns a handle that can be used to access the object from
1765 mono_gchandle_new (MonoObject *obj, gboolean pinned)
1767 return alloc_handle (&gc_handles [pinned? HANDLE_PINNED: HANDLE_NORMAL], obj, FALSE);
1771 * mono_gchandle_new_weakref:
1772 * \param obj managed object to get a handle for
1773 * \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.
1775 * This returns a weak handle that wraps the object, this is used to
1776 * keep a reference to a managed object from the unmanaged world.
1777 * Unlike the \c mono_gchandle_new the object can be reclaimed by the
1778 * garbage collector. In this case the value of the GCHandle will be
1781 * If \p track_resurrection is TRUE the object will be tracked through
1782 * finalization and if the object is resurrected during the execution
1783 * of the finalizer, then the returned weakref will continue to hold
1784 * a reference to the object. If \p track_resurrection is FALSE, then
1785 * the weak reference's target will become NULL as soon as the object
1786 * is passed on to the finalizer.
1788 * \returns a handle that can be used to access the object from
1792 mono_gchandle_new_weakref (MonoObject *obj, gboolean track_resurrection)
1794 return alloc_handle (&gc_handles [track_resurrection? HANDLE_WEAK_TRACK: HANDLE_WEAK], obj, track_resurrection);
1798 * mono_gchandle_get_target:
1799 * \param gchandle a GCHandle's handle.
1801 * The handle was previously created by calling \c mono_gchandle_new or
1802 * \c mono_gchandle_new_weakref.
1804 * \returns A pointer to the \c MonoObject* represented by the handle or
1805 * NULL for a collected object if using a weakref handle.
1808 mono_gchandle_get_target (guint32 gchandle)
1810 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1811 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1812 HandleData *handles = &gc_handles [type];
1813 MonoObject *obj = NULL;
1814 if (type >= HANDLE_TYPE_MAX)
1817 lock_handles (handles);
1818 if (slot < handles->size && slot_occupied (handles, slot)) {
1819 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1820 obj = mono_gc_weak_link_get (&handles->entries [slot]);
1822 obj = (MonoObject *)handles->entries [slot];
1825 /* print a warning? */
1827 unlock_handles (handles);
1828 /*g_print ("get target of entry %d of type %d: %p\n", slot, handles->type, obj);*/
1833 mono_gchandle_set_target (guint32 gchandle, MonoObject *obj)
1835 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1836 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1837 HandleData *handles = &gc_handles [type];
1838 MonoObject *old_obj = NULL;
1840 g_assert (type < HANDLE_TYPE_MAX);
1841 lock_handles (handles);
1842 if (slot < handles->size && slot_occupied (handles, slot)) {
1843 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1844 old_obj = (MonoObject *)handles->entries [slot];
1845 if (handles->entries [slot])
1846 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1848 mono_gc_weak_link_add (&handles->entries [slot], obj, handles->type == HANDLE_WEAK_TRACK);
1849 /*FIXME, what to use when obj == null?*/
1850 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
1852 handles->entries [slot] = obj;
1855 /* print a warning? */
1857 /*g_print ("changed entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
1858 unlock_handles (handles);
1862 mono_gc_is_null (void)
1868 * mono_gchandle_is_in_domain:
1869 * \param gchandle a GCHandle's handle.
1870 * \param domain An application domain.
1872 * Use this function to determine if the \p gchandle points to an
1873 * object allocated in the specified \p domain.
1875 * \returns TRUE if the object wrapped by the \p gchandle belongs to the specific \p domain.
1878 mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain)
1880 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1881 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1882 HandleData *handles = &gc_handles [type];
1883 gboolean result = FALSE;
1885 if (type >= HANDLE_TYPE_MAX)
1888 lock_handles (handles);
1889 if (slot < handles->size && slot_occupied (handles, slot)) {
1890 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1891 result = domain->domain_id == handles->domain_ids [slot];
1894 obj = (MonoObject *)handles->entries [slot];
1898 result = domain == mono_object_domain (obj);
1901 /* print a warning? */
1903 unlock_handles (handles);
1908 * mono_gchandle_free:
1909 * \param gchandle a GCHandle's handle.
1911 * Frees the \p gchandle handle. If there are no outstanding
1912 * references, the garbage collector can reclaim the memory of the
1916 mono_gchandle_free (guint32 gchandle)
1918 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1919 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1920 HandleData *handles = &gc_handles [type];
1921 if (type >= HANDLE_TYPE_MAX)
1924 lock_handles (handles);
1925 if (slot < handles->size && slot_occupied (handles, slot)) {
1926 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1927 if (handles->entries [slot])
1928 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1930 handles->entries [slot] = NULL;
1932 vacate_slot (handles, slot);
1934 /* print a warning? */
1936 #ifndef DISABLE_PERFCOUNTERS
1937 mono_perfcounters->gc_num_handles--;
1939 /*g_print ("freed entry %d of type %d\n", slot, handles->type);*/
1940 unlock_handles (handles);
1941 MONO_PROFILER_RAISE (gc_handle_deleted, (gchandle, handles->type));
1945 * mono_gchandle_free_domain:
1946 * \param domain domain that is unloading
1948 * Function used internally to cleanup any GC handle for objects belonging
1949 * to the specified domain during appdomain unload.
1952 mono_gchandle_free_domain (MonoDomain *domain)
1956 for (type = HANDLE_TYPE_MIN; type < HANDLE_PINNED; ++type) {
1958 HandleData *handles = &gc_handles [type];
1959 lock_handles (handles);
1960 for (slot = 0; slot < handles->size; ++slot) {
1961 if (!slot_occupied (handles, slot))
1963 if (MONO_GC_HANDLE_TYPE_IS_WEAK (type)) {
1964 if (domain->domain_id == handles->domain_ids [slot]) {
1965 vacate_slot (handles, slot);
1966 if (handles->entries [slot])
1967 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1970 if (handles->entries [slot] && mono_object_domain (handles->entries [slot]) == domain) {
1971 vacate_slot (handles, slot);
1972 handles->entries [slot] = NULL;
1976 unlock_handles (handles);
1982 MONO_EMPTY_SOURCE_FILE (boehm_gc);
1983 #endif /* no Boehm GC */