2 * boehm-gc.c: GC implementation using either the installed or included Boehm GC.
4 * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
5 * Copyright 2004-2011 Novell, Inc (http://www.novell.com)
6 * Copyright 2011-2012 Xamarin, Inc (http://www.xamarin.com)
7 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
14 #define GC_I_HIDE_POINTERS
15 #include <mono/metadata/gc-internals.h>
16 #include <mono/metadata/mono-gc.h>
17 #include <mono/metadata/profiler-private.h>
18 #include <mono/metadata/class-internals.h>
19 #include <mono/metadata/method-builder.h>
20 #include <mono/metadata/opcodes.h>
21 #include <mono/metadata/domain-internals.h>
22 #include <mono/metadata/metadata-internals.h>
23 #include <mono/metadata/marshal.h>
24 #include <mono/metadata/runtime.h>
25 #include <mono/metadata/handle.h>
26 #include <mono/metadata/sgen-toggleref.h>
27 #include <mono/utils/atomic.h>
28 #include <mono/utils/mono-logger-internals.h>
29 #include <mono/utils/mono-memory-model.h>
30 #include <mono/utils/mono-time.h>
31 #include <mono/utils/mono-threads.h>
32 #include <mono/utils/dtrace.h>
33 #include <mono/utils/gc_wrapper.h>
34 #include <mono/utils/mono-os-mutex.h>
35 #include <mono/utils/mono-counters.h>
41 #define THREAD_LOCAL_ALLOC 1
42 #include "private/pthread_support.h"
44 #if defined(PLATFORM_MACOSX) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
45 void *pthread_get_stackaddr_np(pthread_t);
48 #define GC_NO_DESCRIPTOR ((gpointer)(0 | GC_DS_LENGTH))
49 /*Boehm max heap cannot be smaller than 16MB*/
50 #define MIN_BOEHM_MAX_HEAP_SIZE_IN_MB 16
51 #define MIN_BOEHM_MAX_HEAP_SIZE (MIN_BOEHM_MAX_HEAP_SIZE_IN_MB << 20)
53 static gboolean gc_initialized = FALSE;
54 static mono_mutex_t mono_gc_lock;
57 boehm_thread_register (MonoThreadInfo* info, void *baseptr);
59 boehm_thread_unregister (MonoThreadInfo *p);
61 register_test_toggleref_callback (void);
63 #define BOEHM_GC_BIT_FINALIZER_AWARE 1
64 static MonoGCFinalizerCallbacks fin_callbacks;
68 static mono_mutex_t handle_section;
69 #define lock_handles(handles) mono_os_mutex_lock (&handle_section)
70 #define unlock_handles(handles) mono_os_mutex_unlock (&handle_section)
77 guint slot_hint : 24; /* starting slot for search in bitmap */
78 /* 2^16 appdomains should be enough for everyone (though I know I'll regret this in 20 years) */
79 /* we alloc this only for weak refs, since we can get the domain directly in the other cases */
83 #define EMPTY_HANDLE_DATA(type) {NULL, NULL, 0, (type), 0, NULL}
85 /* weak and weak-track arrays will be allocated in malloc memory
87 static HandleData gc_handles [] = {
88 EMPTY_HANDLE_DATA (HANDLE_WEAK),
89 EMPTY_HANDLE_DATA (HANDLE_WEAK_TRACK),
90 EMPTY_HANDLE_DATA (HANDLE_NORMAL),
91 EMPTY_HANDLE_DATA (HANDLE_PINNED)
95 mono_gc_warning (char *msg, GC_word arg)
97 mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_GC, msg, (unsigned long)arg);
101 mono_gc_base_init (void)
103 MonoThreadInfoCallbacks cb;
110 mono_counters_init ();
113 * Handle the case when we are called from a thread different from the main thread,
115 * FIXME: Move this to libgc where it belongs.
117 * we used to do this only when running on valgrind,
118 * but it happens also in other setups.
120 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) && !defined(__native_client__)
125 pthread_getattr_np (pthread_self (), &attr);
126 pthread_attr_getstack (&attr, &sstart, &size);
127 pthread_attr_destroy (&attr);
128 /*g_print ("stackbottom pth is: %p\n", (char*)sstart + size);*/
131 * The calculation above doesn't seem to work on ia64, also we need to set
132 * GC_register_stackbottom as well, but don't know how.
135 /* apparently with some linuxthreads implementations sstart can be NULL,
136 * fallback to the more imprecise method (bug# 78096).
139 GC_stackbottom = (char*)sstart + size;
142 gsize stack_bottom = (gsize)&dummy;
143 stack_bottom += 4095;
144 stack_bottom &= ~4095;
145 GC_stackbottom = (char*)stack_bottom;
149 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
150 GC_stackbottom = (char*)pthread_get_stackaddr_np (pthread_self ());
151 #elif defined(__OpenBSD__)
152 # include <pthread_np.h>
157 rslt = pthread_stackseg_np(pthread_self(), &ss);
158 g_assert (rslt == 0);
160 GC_stackbottom = (char*)ss.ss_sp;
162 #elif defined(__native_client__)
163 /* Do nothing, GC_stackbottom is set correctly in libgc */
167 gsize stack_bottom = (gsize)&dummy;
168 stack_bottom += 4095;
169 stack_bottom &= ~4095;
170 /*g_print ("stackbottom is: %p\n", (char*)stack_bottom);*/
171 GC_stackbottom = (char*)stack_bottom;
175 #if !defined(PLATFORM_ANDROID)
176 /* If GC_no_dls is set to true, GC_find_limit is not called. This causes a seg fault on Android. */
180 if ((env = g_getenv ("MONO_GC_DEBUG"))) {
181 char **opts = g_strsplit (env, ",", -1);
182 for (char **ptr = opts; ptr && *ptr; ptr ++) {
184 if (!strcmp (opt, "do-not-finalize")) {
185 mono_do_not_finalize = 1;
186 } else if (!strcmp (opt, "log-finalizers")) {
195 GC_set_warn_proc (mono_gc_warning);
196 GC_finalize_on_demand = 1;
197 GC_finalizer_notifier = mono_gc_finalize_notify;
199 GC_init_gcj_malloc (5, NULL);
200 GC_allow_register_threads ();
202 if ((env = g_getenv ("MONO_GC_PARAMS"))) {
203 char **ptr, **opts = g_strsplit (env, ",", -1);
204 for (ptr = opts; *ptr; ++ptr) {
206 if (g_str_has_prefix (opt, "max-heap-size=")) {
209 opt = strchr (opt, '=') + 1;
210 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &max_heap)) {
211 if (max_heap < MIN_BOEHM_MAX_HEAP_SIZE) {
212 fprintf (stderr, "max-heap-size must be at least %dMb.\n", MIN_BOEHM_MAX_HEAP_SIZE_IN_MB);
215 GC_set_max_heap_size (max_heap);
217 fprintf (stderr, "max-heap-size must be an integer.\n");
221 } else if (g_str_has_prefix (opt, "toggleref-test")) {
222 register_test_toggleref_callback ();
225 /* Could be a parameter for sgen */
227 fprintf (stderr, "MONO_GC_PARAMS must be a comma-delimited list of one or more of the following:\n");
228 fprintf (stderr, " max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
236 memset (&cb, 0, sizeof (cb));
237 cb.thread_register = boehm_thread_register;
238 cb.thread_unregister = boehm_thread_unregister;
239 cb.mono_method_is_critical = (gboolean (*)(void *))mono_runtime_is_critical_method;
241 mono_threads_init (&cb, sizeof (MonoThreadInfo));
242 mono_os_mutex_init (&mono_gc_lock);
243 mono_os_mutex_init_recursive (&handle_section);
245 mono_thread_info_attach (&dummy);
247 mono_gc_enable_events ();
249 MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_NORMAL].entries, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
250 MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_PINNED].entries, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
252 gc_initialized = TRUE;
256 mono_gc_base_cleanup (void)
258 GC_finalizer_notifier = NULL;
263 * @generation: GC generation identifier
265 * Perform a garbage collection for the given generation, higher numbers
266 * mean usually older objects. Collecting a high-numbered generation
267 * implies collecting also the lower-numbered generations.
268 * The maximum value for @generation can be retrieved with a call to
269 * mono_gc_max_generation(), so this function is usually called as:
271 * mono_gc_collect (mono_gc_max_generation ());
274 mono_gc_collect (int generation)
276 #ifndef DISABLE_PERFCOUNTERS
277 mono_perfcounters->gc_induced++;
283 * mono_gc_max_generation:
285 * Get the maximum generation number used by the current garbage
286 * collector. The value will be 0 for the Boehm collector, 1 or more
287 * for the generational collectors.
289 * Returns: the maximum generation number.
292 mono_gc_max_generation (void)
298 * mono_gc_get_generation:
299 * @object: a managed object
301 * Get the garbage collector's generation that @object belongs to.
302 * Use this has a hint only.
304 * Returns: a garbage collector generation number
307 mono_gc_get_generation (MonoObject *object)
313 * mono_gc_collection_count:
314 * @generation: a GC generation number
316 * Get how many times a garbage collection has been performed
317 * for the given @generation number.
319 * Returns: the number of garbage collections
322 mono_gc_collection_count (int generation)
328 * mono_gc_add_memory_pressure:
329 * @value: amount of bytes
331 * Adjust the garbage collector's view of how many bytes of memory
332 * are indirectly referenced by managed objects (for example unmanaged
333 * memory holding image or other binary data).
334 * This is a hint only to the garbage collector algorithm.
335 * Note that negative amounts of @value will decrease the memory
339 mono_gc_add_memory_pressure (gint64 value)
344 * mono_gc_get_used_size:
346 * Get the approximate amount of memory used by managed objects.
348 * Returns: the amount of memory used in bytes
351 mono_gc_get_used_size (void)
353 return GC_get_heap_size () - GC_get_free_bytes ();
357 * mono_gc_get_heap_size:
359 * Get the amount of memory used by the garbage collector.
361 * Returns: the size of the heap in bytes
364 mono_gc_get_heap_size (void)
366 return GC_get_heap_size ();
370 mono_gc_is_gc_thread (void)
372 return GC_thread_is_registered ();
376 mono_gc_register_thread (void *baseptr)
378 return mono_thread_info_attach (baseptr) != NULL;
382 boehm_thread_register (MonoThreadInfo* info, void *baseptr)
384 struct GC_stack_base sb;
387 /* TODO: use GC_get_stack_base instead of baseptr. */
388 sb.mem_base = baseptr;
389 res = GC_register_my_thread (&sb);
390 if (res == GC_UNIMPLEMENTED)
391 return NULL; /* Cannot happen with GC v7+. */
393 info->handle_stack = mono_handle_stack_alloc ();
399 boehm_thread_unregister (MonoThreadInfo *p)
401 MonoNativeThreadId tid;
403 tid = mono_thread_info_get_tid (p);
405 if (p->runtime_thread)
406 mono_threads_add_joinable_thread ((gpointer)tid);
410 mono_object_is_alive (MonoObject* o)
412 return GC_is_marked ((ptr_t)o);
416 mono_gc_walk_heap (int flags, MonoGCReferences callback, void *data)
421 static gint64 gc_start_time;
424 on_gc_notification (GC_EventType event)
426 MonoGCEvent e = (MonoGCEvent)event;
429 case MONO_GC_EVENT_PRE_STOP_WORLD:
430 MONO_GC_WORLD_STOP_BEGIN ();
433 case MONO_GC_EVENT_POST_STOP_WORLD:
434 MONO_GC_WORLD_STOP_END ();
437 case MONO_GC_EVENT_PRE_START_WORLD:
438 MONO_GC_WORLD_RESTART_BEGIN (1);
441 case MONO_GC_EVENT_POST_START_WORLD:
442 MONO_GC_WORLD_RESTART_END (1);
445 case MONO_GC_EVENT_START:
447 #ifndef DISABLE_PERFCOUNTERS
448 if (mono_perfcounters)
449 mono_perfcounters->gc_collections0++;
451 gc_stats.major_gc_count ++;
452 gc_start_time = mono_100ns_ticks ();
455 case MONO_GC_EVENT_END:
457 #if defined(ENABLE_DTRACE) && defined(__sun__)
458 /* This works around a dtrace -G problem on Solaris.
459 Limit its actual use to when the probe is enabled. */
460 if (MONO_GC_END_ENABLED ())
464 #ifndef DISABLE_PERFCOUNTERS
465 if (mono_perfcounters) {
466 guint64 heap_size = GC_get_heap_size ();
467 guint64 used_size = heap_size - GC_get_free_bytes ();
468 mono_perfcounters->gc_total_bytes = used_size;
469 mono_perfcounters->gc_committed_bytes = heap_size;
470 mono_perfcounters->gc_reserved_bytes = heap_size;
471 mono_perfcounters->gc_gen0size = heap_size;
474 gc_stats.major_gc_time += mono_100ns_ticks () - gc_start_time;
475 mono_trace_message (MONO_TRACE_GC, "gc took %d usecs", (mono_100ns_ticks () - gc_start_time) / 10);
481 mono_profiler_gc_event (e, 0);
484 case MONO_GC_EVENT_PRE_STOP_WORLD:
485 mono_thread_info_suspend_lock ();
486 mono_profiler_gc_event (MONO_GC_EVENT_PRE_STOP_WORLD_LOCKED, 0);
488 case MONO_GC_EVENT_POST_START_WORLD:
489 mono_thread_info_suspend_unlock ();
490 mono_profiler_gc_event (MONO_GC_EVENT_POST_START_WORLD_UNLOCKED, 0);
499 on_gc_heap_resize (size_t new_size)
501 guint64 heap_size = GC_get_heap_size ();
502 #ifndef DISABLE_PERFCOUNTERS
503 if (mono_perfcounters) {
504 mono_perfcounters->gc_committed_bytes = heap_size;
505 mono_perfcounters->gc_reserved_bytes = heap_size;
506 mono_perfcounters->gc_gen0size = heap_size;
509 mono_profiler_gc_heap_resize (new_size);
513 mono_gc_enable_events (void)
515 GC_set_on_collection_event (on_gc_notification);
516 GC_on_heap_resize = on_gc_heap_resize;
519 static gboolean alloc_events = FALSE;
522 mono_gc_enable_alloc_events (void)
528 mono_gc_register_root (char *start, size_t size, void *descr, MonoGCRootSource source, const char *msg)
530 /* for some strange reason, they want one extra byte on the end */
531 GC_add_roots (start, start + size + 1);
537 mono_gc_deregister_root (char* addr)
540 /* FIXME: libgc doesn't define this work win32 for some reason */
541 /* FIXME: No size info */
542 GC_remove_roots (addr, addr + sizeof (gpointer) + 1);
547 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
549 /* libgc requires that we use HIDE_POINTER... */
550 *link_addr = (void*)HIDE_POINTER (obj);
552 GC_REGISTER_LONG_LINK (link_addr, obj);
554 GC_GENERAL_REGISTER_DISAPPEARING_LINK (link_addr, obj);
558 mono_gc_weak_link_remove (void **link_addr, gboolean track)
561 GC_unregister_long_link (link_addr);
563 GC_unregister_disappearing_link (link_addr);
568 reveal_link (gpointer link_addr)
570 void **link_a = (void **)link_addr;
571 return REVEAL_POINTER (*link_a);
575 mono_gc_weak_link_get (void **link_addr)
577 MonoObject *obj = (MonoObject *)GC_call_with_alloc_lock (reveal_link, link_addr);
578 if (obj == (MonoObject *) -1)
584 mono_gc_make_descr_for_string (gsize *bitmap, int numbits)
586 return mono_gc_make_descr_from_bitmap (bitmap, numbits);
590 mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size)
592 return mono_gc_make_descr_from_bitmap (bitmap, numbits);
596 mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size)
598 /* libgc has no usable support for arrays... */
599 return GC_NO_DESCRIPTOR;
603 mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
605 /* It seems there are issues when the bitmap doesn't fit: play it safe */
607 return GC_NO_DESCRIPTOR;
609 return (gpointer)GC_make_descriptor ((GC_bitmap)bitmap, numbits);
613 mono_gc_make_root_descr_all_refs (int numbits)
619 mono_gc_alloc_fixed (size_t size, void *descr, MonoGCRootSource source, const char *msg)
621 /* To help track down typed allocation bugs */
625 if (count == atoi (g_getenv ("COUNT2")))
627 if (count > atoi (g_getenv ("COUNT2")))
628 return GC_MALLOC (size);
632 return GC_MALLOC_EXPLICITLY_TYPED (size, (GC_descr)descr);
634 return GC_MALLOC (size);
638 mono_gc_free_fixed (void* addr)
643 mono_gc_alloc_obj (MonoVTable *vtable, size_t size)
647 if (!vtable->klass->has_references) {
648 obj = (MonoObject *)GC_MALLOC_ATOMIC (size);
649 if (G_UNLIKELY (!obj))
652 obj->vtable = vtable;
653 obj->synchronisation = NULL;
655 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
656 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
657 obj = (MonoObject *)GC_GCJ_MALLOC (size, vtable);
658 if (G_UNLIKELY (!obj))
661 obj = (MonoObject *)GC_MALLOC (size);
662 if (G_UNLIKELY (!obj))
665 obj->vtable = vtable;
668 if (G_UNLIKELY (alloc_events))
669 mono_profiler_allocation (obj);
675 mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length)
679 if (!vtable->klass->has_references) {
680 obj = (MonoArray *)GC_MALLOC_ATOMIC (size);
681 if (G_UNLIKELY (!obj))
684 obj->obj.vtable = vtable;
685 obj->obj.synchronisation = NULL;
687 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
688 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
689 obj = (MonoArray *)GC_GCJ_MALLOC (size, vtable);
690 if (G_UNLIKELY (!obj))
693 obj = (MonoArray *)GC_MALLOC (size);
694 if (G_UNLIKELY (!obj))
697 obj->obj.vtable = vtable;
700 obj->max_length = max_length;
702 if (G_UNLIKELY (alloc_events))
703 mono_profiler_allocation (&obj->obj);
709 mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uintptr_t bounds_size)
713 if (!vtable->klass->has_references) {
714 obj = (MonoArray *)GC_MALLOC_ATOMIC (size);
715 if (G_UNLIKELY (!obj))
718 obj->obj.vtable = vtable;
719 obj->obj.synchronisation = NULL;
721 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
722 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
723 obj = (MonoArray *)GC_GCJ_MALLOC (size, vtable);
724 if (G_UNLIKELY (!obj))
727 obj = (MonoArray *)GC_MALLOC (size);
728 if (G_UNLIKELY (!obj))
731 obj->obj.vtable = vtable;
734 obj->max_length = max_length;
737 obj->bounds = (MonoArrayBounds *) ((char *) obj + size - bounds_size);
739 if (G_UNLIKELY (alloc_events))
740 mono_profiler_allocation (&obj->obj);
746 mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len)
748 MonoString *obj = (MonoString *)GC_MALLOC_ATOMIC (size);
749 if (G_UNLIKELY (!obj))
752 obj->object.vtable = vtable;
753 obj->object.synchronisation = NULL;
755 obj->chars [len] = 0;
757 if (G_UNLIKELY (alloc_events))
758 mono_profiler_allocation (&obj->object);
764 mono_gc_alloc_mature (MonoVTable *vtable, size_t size)
766 return mono_gc_alloc_obj (vtable, size);
770 mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size)
772 return mono_gc_alloc_obj (vtable, size);
776 mono_gc_invoke_finalizers (void)
778 /* There is a bug in GC_invoke_finalizer () in versions <= 6.2alpha4:
779 * the 'mem_freed' variable is not initialized when there are no
780 * objects to finalize, which leads to strange behavior later on.
781 * The check is necessary to work around that bug.
783 if (GC_should_invoke_finalizers ())
784 return GC_invoke_finalizers ();
789 mono_gc_pending_finalizers (void)
791 return GC_should_invoke_finalizers ();
795 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
797 *(void**)field_ptr = value;
801 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
803 *(void**)slot_ptr = value;
807 mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
809 mono_gc_memmove_aligned (dest_ptr, src_ptr, count * sizeof (gpointer));
813 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
815 *(void**)ptr = value;
819 mono_gc_wbarrier_generic_store_atomic (gpointer ptr, MonoObject *value)
821 InterlockedWritePointer ((volatile gpointer *)ptr, value);
825 mono_gc_wbarrier_generic_nostore (gpointer ptr)
830 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
832 mono_gc_memmove_atomic (dest, src, count * mono_class_value_size (klass, NULL));
836 mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
838 /* do not copy the sync state */
839 mono_gc_memmove_aligned ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
840 mono_object_class (obj)->instance_size - sizeof (MonoObject));
844 mono_gc_clear_domain (MonoDomain *domain)
849 mono_gc_suspend_finalizers (void)
854 mono_gc_get_suspend_signal (void)
856 return GC_get_suspend_signal ();
860 mono_gc_get_restart_signal (void)
862 return GC_get_thr_restart_signal ();
865 #if defined(USE_COMPILER_TLS) && defined(__linux__) && (defined(__i386__) || defined(__x86_64__))
866 extern __thread MONO_TLS_FAST void* GC_thread_tls;
867 #include "metadata-internals.h"
873 while (!(v & (1 << i)))
880 ATYPE_FREEPTR_FOR_BOX,
888 create_allocator (int atype, int tls_key, gboolean slowpath)
890 int index_var, bytes_var, my_fl_var, my_entry_var;
891 guint32 no_freelist_branch, not_small_enough_branch = 0;
892 guint32 size_overflow_branch = 0;
893 MonoMethodBuilder *mb;
895 MonoMethodSignature *csig;
896 const char *name = NULL;
899 if (atype == ATYPE_FREEPTR) {
900 name = slowpath ? "SlowAllocPtrfree" : "AllocPtrfree";
901 } else if (atype == ATYPE_FREEPTR_FOR_BOX) {
902 name = slowpath ? "SlowAllocPtrfreeBox" : "AllocPtrfreeBox";
903 } else if (atype == ATYPE_NORMAL) {
904 name = slowpath ? "SlowAlloc" : "Alloc";
905 } else if (atype == ATYPE_GCJ) {
906 name = slowpath ? "SlowAllocGcj" : "AllocGcj";
907 } else if (atype == ATYPE_STRING) {
908 name = slowpath ? "SlowAllocString" : "AllocString";
910 g_assert_not_reached ();
913 csig = mono_metadata_signature_alloc (mono_defaults.corlib, 2);
915 if (atype == ATYPE_STRING) {
916 csig->ret = &mono_defaults.string_class->byval_arg;
917 csig->params [0] = &mono_defaults.int_class->byval_arg;
918 csig->params [1] = &mono_defaults.int32_class->byval_arg;
920 csig->ret = &mono_defaults.object_class->byval_arg;
921 csig->params [0] = &mono_defaults.int_class->byval_arg;
922 csig->params [1] = &mono_defaults.int32_class->byval_arg;
925 mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_ALLOC);
928 goto always_slowpath;
930 bytes_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
931 if (atype == ATYPE_STRING) {
932 /* a string alloator method takes the args: (vtable, len) */
933 /* bytes = (offsetof (MonoString, chars) + ((len + 1) * 2)); */
934 mono_mb_emit_ldarg (mb, 1);
935 mono_mb_emit_icon (mb, 1);
936 mono_mb_emit_byte (mb, MONO_CEE_ADD);
937 mono_mb_emit_icon (mb, 1);
938 mono_mb_emit_byte (mb, MONO_CEE_SHL);
939 // sizeof (MonoString) might include padding
940 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, chars));
941 mono_mb_emit_byte (mb, MONO_CEE_ADD);
942 mono_mb_emit_stloc (mb, bytes_var);
944 mono_mb_emit_ldarg (mb, 1);
945 mono_mb_emit_stloc (mb, bytes_var);
948 /* this is needed for strings/arrays only as the other big types are never allocated with this method */
949 if (atype == ATYPE_STRING) {
951 /* if (!SMALL_ENOUGH (bytes)) jump slow_path;*/
952 mono_mb_emit_ldloc (mb, bytes_var);
953 mono_mb_emit_icon (mb, (NFREELISTS-1) * GRANULARITY);
954 not_small_enough_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_UN_S);
955 /* check for overflow */
956 mono_mb_emit_ldloc (mb, bytes_var);
957 mono_mb_emit_icon (mb, sizeof (MonoString));
958 size_overflow_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLE_UN_S);
961 /* int index = INDEX_FROM_BYTES(bytes); */
962 index_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
964 mono_mb_emit_ldloc (mb, bytes_var);
965 mono_mb_emit_icon (mb, GRANULARITY - 1);
966 mono_mb_emit_byte (mb, MONO_CEE_ADD);
967 mono_mb_emit_icon (mb, shift_amount (GRANULARITY));
968 mono_mb_emit_byte (mb, MONO_CEE_SHR_UN);
969 mono_mb_emit_icon (mb, shift_amount (sizeof (gpointer)));
970 mono_mb_emit_byte (mb, MONO_CEE_SHL);
971 /* index var is already adjusted into bytes */
972 mono_mb_emit_stloc (mb, index_var);
974 my_fl_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
975 my_entry_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
976 /* my_fl = ((GC_thread)tsd) -> ptrfree_freelists + index; */
977 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
978 mono_mb_emit_byte (mb, 0x0D); /* CEE_MONO_TLS */
979 mono_mb_emit_i4 (mb, tls_key);
980 if (atype == ATYPE_FREEPTR || atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING)
981 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
982 + G_STRUCT_OFFSET (struct thread_local_freelists,
984 else if (atype == ATYPE_NORMAL)
985 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
986 + G_STRUCT_OFFSET (struct thread_local_freelists,
988 else if (atype == ATYPE_GCJ)
989 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
990 + G_STRUCT_OFFSET (struct thread_local_freelists,
993 g_assert_not_reached ();
994 mono_mb_emit_byte (mb, MONO_CEE_ADD);
995 mono_mb_emit_ldloc (mb, index_var);
996 mono_mb_emit_byte (mb, MONO_CEE_ADD);
997 mono_mb_emit_stloc (mb, my_fl_var);
999 /* my_entry = *my_fl; */
1000 mono_mb_emit_ldloc (mb, my_fl_var);
1001 mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
1002 mono_mb_emit_stloc (mb, my_entry_var);
1004 /* if (EXPECT((word)my_entry >= HBLKSIZE, 1)) { */
1005 mono_mb_emit_ldloc (mb, my_entry_var);
1006 mono_mb_emit_icon (mb, HBLKSIZE);
1007 no_freelist_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
1009 /* ptr_t next = obj_link(my_entry); *my_fl = next; */
1010 mono_mb_emit_ldloc (mb, my_fl_var);
1011 mono_mb_emit_ldloc (mb, my_entry_var);
1012 mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
1013 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1015 /* set the vtable and clear the words in the object */
1016 mono_mb_emit_ldloc (mb, my_entry_var);
1017 mono_mb_emit_ldarg (mb, 0);
1018 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1020 if (atype == ATYPE_FREEPTR) {
1021 int start_var, end_var, start_loop;
1022 /* end = my_entry + bytes; start = my_entry + sizeof (gpointer);
1024 start_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1025 end_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1026 mono_mb_emit_ldloc (mb, my_entry_var);
1027 mono_mb_emit_ldloc (mb, bytes_var);
1028 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1029 mono_mb_emit_stloc (mb, end_var);
1030 mono_mb_emit_ldloc (mb, my_entry_var);
1031 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
1032 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1033 mono_mb_emit_stloc (mb, start_var);
1037 * } while (start < end);
1039 start_loop = mono_mb_get_label (mb);
1040 mono_mb_emit_ldloc (mb, start_var);
1041 mono_mb_emit_icon (mb, 0);
1042 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1043 mono_mb_emit_ldloc (mb, start_var);
1044 mono_mb_emit_icon (mb, sizeof (gpointer));
1045 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1046 mono_mb_emit_stloc (mb, start_var);
1048 mono_mb_emit_ldloc (mb, start_var);
1049 mono_mb_emit_ldloc (mb, end_var);
1050 mono_mb_emit_byte (mb, MONO_CEE_BLT_UN_S);
1051 mono_mb_emit_byte (mb, start_loop - (mono_mb_get_label (mb) + 1));
1052 } else if (atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING) {
1053 /* need to clear just the sync pointer */
1054 mono_mb_emit_ldloc (mb, my_entry_var);
1055 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
1056 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1057 mono_mb_emit_icon (mb, 0);
1058 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1061 if (atype == ATYPE_STRING) {
1062 /* need to set length and clear the last char */
1063 /* s->length = len; */
1064 mono_mb_emit_ldloc (mb, my_entry_var);
1065 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, length));
1066 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1067 mono_mb_emit_ldarg (mb, 1);
1068 mono_mb_emit_byte (mb, MONO_CEE_STIND_I4);
1069 /* s->chars [len] = 0; */
1070 mono_mb_emit_ldloc (mb, my_entry_var);
1071 mono_mb_emit_ldloc (mb, bytes_var);
1072 mono_mb_emit_icon (mb, 2);
1073 mono_mb_emit_byte (mb, MONO_CEE_SUB);
1074 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1075 mono_mb_emit_icon (mb, 0);
1076 mono_mb_emit_byte (mb, MONO_CEE_STIND_I2);
1079 /* return my_entry; */
1080 mono_mb_emit_ldloc (mb, my_entry_var);
1081 mono_mb_emit_byte (mb, MONO_CEE_RET);
1083 mono_mb_patch_short_branch (mb, no_freelist_branch);
1084 if (not_small_enough_branch > 0)
1085 mono_mb_patch_short_branch (mb, not_small_enough_branch);
1086 if (size_overflow_branch > 0)
1087 mono_mb_patch_short_branch (mb, size_overflow_branch);
1089 /* the slow path: we just call back into the runtime */
1091 if (atype == ATYPE_STRING) {
1092 mono_mb_emit_ldarg (mb, 1);
1093 mono_mb_emit_icall (mb, ves_icall_string_alloc);
1095 mono_mb_emit_ldarg (mb, 0);
1096 mono_mb_emit_icall (mb, ves_icall_object_new_specific);
1099 mono_mb_emit_byte (mb, MONO_CEE_RET);
1101 info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE);
1102 info->d.alloc.gc_name = "boehm";
1103 info->d.alloc.alloc_type = atype;
1104 mb->init_locals = FALSE;
1106 res = mono_mb_create (mb, csig, 8, info);
1112 static MonoMethod* alloc_method_cache [ATYPE_NUM];
1113 static MonoMethod* slowpath_alloc_method_cache [ATYPE_NUM];
1115 static G_GNUC_UNUSED gboolean
1116 mono_gc_is_critical_method (MonoMethod *method)
1120 for (i = 0; i < ATYPE_NUM; ++i)
1121 if (method == alloc_method_cache [i] || method == slowpath_alloc_method_cache [i])
1128 * If possible, generate a managed method that can quickly allocate objects in class
1129 * @klass. The method will typically have an thread-local inline allocation sequence.
1130 * The signature of the called method is:
1131 * object allocate (MonoVTable *vtable)
1132 * Some of the logic here is similar to mono_class_get_allocation_ftn () i object.c,
1134 * The thread local alloc logic is taken from libgc/pthread_support.c.
1138 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size)
1142 MONO_THREAD_VAR_OFFSET (GC_thread_tls, offset);
1144 /*g_print ("thread tls: %d\n", offset);*/
1147 if (!SMALL_ENOUGH (klass->instance_size))
1149 if (mono_class_has_finalizer (klass) || mono_class_is_marshalbyref (klass))
1151 if (mono_profiler_get_events () & (MONO_PROFILE_ALLOCATIONS | MONO_PROFILE_STATISTICAL))
1155 if (mono_class_is_open_constructed_type (&klass->byval_arg))
1157 if (klass->byval_arg.type == MONO_TYPE_STRING) {
1158 atype = ATYPE_STRING;
1159 } else if (!known_instance_size) {
1161 } else if (!klass->has_references) {
1163 atype = ATYPE_FREEPTR_FOR_BOX;
1165 atype = ATYPE_FREEPTR;
1169 * disabled because we currently do a runtime choice anyway, to
1170 * deal with multiple appdomains.
1171 if (vtable->gc_descr != GC_NO_DESCRIPTOR)
1174 atype = ATYPE_NORMAL;
1177 return mono_gc_get_managed_allocator_by_type (atype, MANAGED_ALLOCATOR_REGULAR);
1181 mono_gc_get_managed_array_allocator (MonoClass *klass)
1187 * mono_gc_get_managed_allocator_by_type:
1189 * Return a managed allocator method corresponding to allocator type ATYPE.
1192 mono_gc_get_managed_allocator_by_type (int atype, ManagedAllocatorVariant variant)
1196 gboolean slowpath = variant != MANAGED_ALLOCATOR_REGULAR;
1197 MonoMethod **cache = slowpath ? slowpath_alloc_method_cache : alloc_method_cache;
1198 MONO_THREAD_VAR_OFFSET (GC_thread_tls, offset);
1200 mono_tls_key_set_offset (TLS_KEY_BOEHM_GC_THREAD, offset);
1202 res = cache [atype];
1206 res = create_allocator (atype, TLS_KEY_BOEHM_GC_THREAD, slowpath);
1207 mono_os_mutex_lock (&mono_gc_lock);
1208 if (cache [atype]) {
1209 mono_free_method (res);
1210 res = cache [atype];
1212 mono_memory_barrier ();
1213 cache [atype] = res;
1215 mono_os_mutex_unlock (&mono_gc_lock);
1220 mono_gc_get_managed_allocator_types (void)
1226 mono_gc_get_write_barrier (void)
1228 g_assert_not_reached ();
1234 static G_GNUC_UNUSED gboolean
1235 mono_gc_is_critical_method (MonoMethod *method)
1241 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size)
1247 mono_gc_get_managed_array_allocator (MonoClass *klass)
1253 mono_gc_get_managed_allocator_by_type (int atype, ManagedAllocatorVariant variant)
1259 mono_gc_get_managed_allocator_types (void)
1265 mono_gc_get_write_barrier (void)
1267 g_assert_not_reached ();
1274 mono_gc_get_specific_write_barrier (gboolean is_concurrent)
1276 g_assert_not_reached ();
1281 mono_gc_get_aligned_size_for_allocator (int size)
1287 mono_gc_get_gc_name (void)
1293 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
1295 return GC_call_with_alloc_lock (func, data);
1299 mono_gc_get_description (void)
1301 return g_strdup (DEFAULT_GC_NAME);
1305 mono_gc_set_desktop_mode (void)
1311 mono_gc_is_moving (void)
1317 mono_gc_is_disabled (void)
1319 if (GC_dont_gc || g_getenv ("GC_DONT_GC"))
1326 mono_gc_wbarrier_value_copy_bitmap (gpointer _dest, gpointer _src, int size, unsigned bitmap)
1328 g_assert_not_reached ();
1333 mono_gc_get_card_table (int *shift_bits, gpointer *card_mask)
1335 g_assert_not_reached ();
1340 mono_gc_card_table_nursery_check (void)
1342 g_assert_not_reached ();
1347 mono_gc_get_nursery (int *shift_bits, size_t *size)
1353 mono_gc_set_current_thread_appdomain (MonoDomain *domain)
1358 mono_gc_precise_stack_mark_enabled (void)
1364 mono_gc_get_logfile (void)
1370 mono_gc_conservatively_scan_area (void *start, void *end)
1372 g_assert_not_reached ();
1376 mono_gc_scan_object (void *obj, void *gc_data)
1378 g_assert_not_reached ();
1383 mono_gc_get_bitmap_for_descr (void *descr, int *numbits)
1385 g_assert_not_reached ();
1390 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
1395 mono_gc_set_stack_end (void *stack_end)
1399 void mono_gc_set_skip_thread (gboolean value)
1404 mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
1409 /* This assertion is not valid when GC_DEBUG is defined */
1410 g_assert (GC_base (obj) == (char*)obj - offset);
1413 GC_REGISTER_FINALIZER_NO_ORDER ((char*)obj - offset, (GC_finalization_proc)user_data, GUINT_TO_POINTER (offset), NULL, NULL);
1418 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
1420 /* it is being replaced by GC_pthread_create on some
1421 * platforms, see libgc/include/gc_pthread_redirects.h */
1422 return pthread_create (new_thread, attr, start_routine, arg);
1427 BOOL APIENTRY mono_gc_dllmain (HMODULE module_handle, DWORD reason, LPVOID reserved)
1429 return GC_DllMain (module_handle, reason, reserved);
1434 mono_gc_get_vtable_bits (MonoClass *klass)
1436 if (fin_callbacks.is_class_finalization_aware) {
1437 if (fin_callbacks.is_class_finalization_aware (klass))
1438 return BOEHM_GC_BIT_FINALIZER_AWARE;
1444 * mono_gc_register_altstack:
1446 * Register the dimensions of the normal stack and altstack with the collector.
1447 * Currently, STACK/STACK_SIZE is only used when the thread is suspended while it is on an altstack.
1450 mono_gc_register_altstack (gpointer stack, gint32 stack_size, gpointer altstack, gint32 altstack_size)
1452 GC_register_altstack (stack, stack_size, altstack, altstack_size);
1456 mono_gc_get_los_limit (void)
1462 mono_gc_set_string_length (MonoString *str, gint32 new_length)
1464 mono_unichar2 *new_end = str->chars + new_length;
1466 /* zero the discarded string. This null-delimits the string and allows
1467 * the space to be reclaimed by SGen. */
1469 memset (new_end, 0, (str->length - new_length + 1) * sizeof (mono_unichar2));
1470 str->length = new_length;
1474 mono_gc_user_markers_supported (void)
1480 mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
1482 g_assert_not_reached ();
1486 /* Toggleref support */
1489 mono_gc_toggleref_add (MonoObject *object, mono_bool strong_ref)
1491 if (GC_toggleref_add ((GC_PTR)object, (int)strong_ref) != GC_SUCCESS)
1492 g_error ("GC_toggleref_add failed\n");
1496 mono_gc_toggleref_register_callback (MonoToggleRefStatus (*proccess_toggleref) (MonoObject *obj))
1498 GC_set_toggleref_func ((GC_ToggleRefStatus (*) (GC_PTR obj)) proccess_toggleref);
1501 /* Test support code */
1503 static MonoToggleRefStatus
1504 test_toggleref_callback (MonoObject *obj)
1506 static MonoClassField *mono_toggleref_test_field;
1507 MonoToggleRefStatus status = MONO_TOGGLE_REF_DROP;
1509 if (!mono_toggleref_test_field) {
1510 mono_toggleref_test_field = mono_class_get_field_from_name (mono_object_get_class (obj), "__test");
1511 g_assert (mono_toggleref_test_field);
1514 mono_field_get_value (obj, mono_toggleref_test_field, &status);
1515 printf ("toggleref-cb obj %d\n", status);
1520 register_test_toggleref_callback (void)
1522 mono_gc_toggleref_register_callback (test_toggleref_callback);
1526 is_finalization_aware (MonoObject *obj)
1528 MonoVTable *vt = obj->vtable;
1529 return (vt->gc_bits & BOEHM_GC_BIT_FINALIZER_AWARE) == BOEHM_GC_BIT_FINALIZER_AWARE;
1533 fin_notifier (MonoObject *obj)
1535 if (is_finalization_aware (obj))
1536 fin_callbacks.object_queued_for_finalization (obj);
1540 mono_gc_register_finalizer_callbacks (MonoGCFinalizerCallbacks *callbacks)
1542 if (callbacks->version != MONO_GC_FINALIZER_EXTENSION_VERSION)
1543 g_error ("Invalid finalizer callback version. Expected %d but got %d\n", MONO_GC_FINALIZER_EXTENSION_VERSION, callbacks->version);
1545 fin_callbacks = *callbacks;
1547 GC_set_await_finalize_proc ((void (*) (GC_PTR))fin_notifier);
1550 #define BITMAP_SIZE (sizeof (*((HandleData *)NULL)->bitmap) * CHAR_BIT)
1552 static inline gboolean
1553 slot_occupied (HandleData *handles, guint slot) {
1554 return handles->bitmap [slot / BITMAP_SIZE] & (1 << (slot % BITMAP_SIZE));
1558 vacate_slot (HandleData *handles, guint slot) {
1559 handles->bitmap [slot / BITMAP_SIZE] &= ~(1 << (slot % BITMAP_SIZE));
1563 occupy_slot (HandleData *handles, guint slot) {
1564 handles->bitmap [slot / BITMAP_SIZE] |= 1 << (slot % BITMAP_SIZE);
1568 find_first_unset (guint32 bitmap)
1571 for (i = 0; i < 32; ++i) {
1572 if (!(bitmap & (1 << i)))
1579 handle_data_alloc_entries (HandleData *handles)
1582 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1583 handles->entries = (void **)g_malloc0 (sizeof (*handles->entries) * handles->size);
1584 handles->domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * handles->size);
1586 handles->entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * handles->size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
1588 handles->bitmap = (guint32 *)g_malloc0 (handles->size / CHAR_BIT);
1592 handle_data_next_unset (HandleData *handles)
1595 for (slot = handles->slot_hint; slot < handles->size / BITMAP_SIZE; ++slot) {
1596 if (handles->bitmap [slot] == 0xffffffff)
1598 handles->slot_hint = slot;
1599 return find_first_unset (handles->bitmap [slot]);
1605 handle_data_first_unset (HandleData *handles)
1608 for (slot = 0; slot < handles->slot_hint; ++slot) {
1609 if (handles->bitmap [slot] == 0xffffffff)
1611 handles->slot_hint = slot;
1612 return find_first_unset (handles->bitmap [slot]);
1617 /* Returns the index of the current slot in the bitmap. */
1619 handle_data_grow (HandleData *handles, gboolean track)
1621 guint32 *new_bitmap;
1622 guint32 new_size = handles->size * 2; /* always double: we memset to 0 based on this below */
1624 /* resize and copy the bitmap */
1625 new_bitmap = (guint32 *)g_malloc0 (new_size / CHAR_BIT);
1626 memcpy (new_bitmap, handles->bitmap, handles->size / CHAR_BIT);
1627 g_free (handles->bitmap);
1628 handles->bitmap = new_bitmap;
1630 /* resize and copy the entries */
1631 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1633 guint16 *domain_ids;
1635 domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * new_size);
1636 entries = (void **)g_malloc0 (sizeof (*handles->entries) * new_size);
1637 memcpy (domain_ids, handles->domain_ids, sizeof (*handles->domain_ids) * handles->size);
1638 for (i = 0; i < handles->size; ++i) {
1639 MonoObject *obj = mono_gc_weak_link_get (&(handles->entries [i]));
1641 mono_gc_weak_link_add (&(entries [i]), obj, track);
1642 mono_gc_weak_link_remove (&(handles->entries [i]), track);
1644 g_assert (!handles->entries [i]);
1647 g_free (handles->entries);
1648 g_free (handles->domain_ids);
1649 handles->entries = entries;
1650 handles->domain_ids = domain_ids;
1653 entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * new_size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
1654 mono_gc_memmove_aligned (entries, handles->entries, sizeof (*handles->entries) * handles->size);
1655 mono_gc_free_fixed (handles->entries);
1656 handles->entries = entries;
1658 handles->slot_hint = handles->size / BITMAP_SIZE;
1659 handles->size = new_size;
1663 alloc_handle (HandleData *handles, MonoObject *obj, gboolean track)
1667 lock_handles (handles);
1669 handle_data_alloc_entries (handles);
1670 i = handle_data_next_unset (handles);
1671 if (i == -1 && handles->slot_hint != 0)
1672 i = handle_data_first_unset (handles);
1674 handle_data_grow (handles, track);
1677 slot = handles->slot_hint * BITMAP_SIZE + i;
1678 occupy_slot (handles, slot);
1679 handles->entries [slot] = NULL;
1680 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1681 /*FIXME, what to use when obj == null?*/
1682 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
1684 mono_gc_weak_link_add (&(handles->entries [slot]), obj, track);
1686 handles->entries [slot] = obj;
1689 #ifndef DISABLE_PERFCOUNTERS
1690 mono_perfcounters->gc_num_handles++;
1692 unlock_handles (handles);
1693 res = MONO_GC_HANDLE (slot, handles->type);
1694 mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_CREATED, handles->type, res, obj);
1699 * mono_gchandle_new:
1700 * @obj: managed object to get a handle for
1701 * @pinned: whether the object should be pinned
1703 * This returns a handle that wraps the object, this is used to keep a
1704 * reference to a managed object from the unmanaged world and preventing the
1705 * object from being disposed.
1707 * If @pinned is false the address of the object can not be obtained, if it is
1708 * true the address of the object can be obtained. This will also pin the
1709 * object so it will not be possible by a moving garbage collector to move the
1712 * Returns: a handle that can be used to access the object from
1716 mono_gchandle_new (MonoObject *obj, gboolean pinned)
1718 return alloc_handle (&gc_handles [pinned? HANDLE_PINNED: HANDLE_NORMAL], obj, FALSE);
1722 * mono_gchandle_new_weakref:
1723 * @obj: managed object to get a handle for
1724 * @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.
1726 * This returns a weak handle that wraps the object, this is used to
1727 * keep a reference to a managed object from the unmanaged world.
1728 * Unlike the mono_gchandle_new the object can be reclaimed by the
1729 * garbage collector. In this case the value of the GCHandle will be
1732 * If @track_resurrection is TRUE the object will be tracked through
1733 * finalization and if the object is resurrected during the execution
1734 * of the finalizer, then the returned weakref will continue to hold
1735 * a reference to the object. If @track_resurrection is FALSE, then
1736 * the weak reference's target will become NULL as soon as the object
1737 * is passed on to the finalizer.
1739 * Returns: a handle that can be used to access the object from
1743 mono_gchandle_new_weakref (MonoObject *obj, gboolean track_resurrection)
1745 return alloc_handle (&gc_handles [track_resurrection? HANDLE_WEAK_TRACK: HANDLE_WEAK], obj, track_resurrection);
1749 * mono_gchandle_get_target:
1750 * @gchandle: a GCHandle's handle.
1752 * The handle was previously created by calling `mono_gchandle_new` or
1753 * `mono_gchandle_new_weakref`.
1755 * Returns: A pointer to the `MonoObject*` represented by the handle or
1756 * NULL for a collected object if using a weakref handle.
1759 mono_gchandle_get_target (guint32 gchandle)
1761 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1762 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1763 HandleData *handles = &gc_handles [type];
1764 MonoObject *obj = NULL;
1765 if (type >= HANDLE_TYPE_MAX)
1768 lock_handles (handles);
1769 if (slot < handles->size && slot_occupied (handles, slot)) {
1770 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1771 obj = mono_gc_weak_link_get (&handles->entries [slot]);
1773 obj = (MonoObject *)handles->entries [slot];
1776 /* print a warning? */
1778 unlock_handles (handles);
1779 /*g_print ("get target of entry %d of type %d: %p\n", slot, handles->type, obj);*/
1784 mono_gchandle_set_target (guint32 gchandle, MonoObject *obj)
1786 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1787 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1788 HandleData *handles = &gc_handles [type];
1789 MonoObject *old_obj = NULL;
1791 g_assert (type < HANDLE_TYPE_MAX);
1792 lock_handles (handles);
1793 if (slot < handles->size && slot_occupied (handles, slot)) {
1794 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1795 old_obj = (MonoObject *)handles->entries [slot];
1796 if (handles->entries [slot])
1797 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1799 mono_gc_weak_link_add (&handles->entries [slot], obj, handles->type == HANDLE_WEAK_TRACK);
1800 /*FIXME, what to use when obj == null?*/
1801 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
1803 handles->entries [slot] = obj;
1806 /* print a warning? */
1808 /*g_print ("changed entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
1809 unlock_handles (handles);
1813 mono_gc_is_null (void)
1819 * mono_gchandle_is_in_domain:
1820 * @gchandle: a GCHandle's handle.
1821 * @domain: An application domain.
1823 * Use this function to determine if the @gchandle points to an
1824 * object allocated in the specified @domain.
1826 * Returns: TRUE if the object wrapped by the @gchandle belongs to the specific @domain.
1829 mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain)
1831 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1832 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1833 HandleData *handles = &gc_handles [type];
1834 gboolean result = FALSE;
1836 if (type >= HANDLE_TYPE_MAX)
1839 lock_handles (handles);
1840 if (slot < handles->size && slot_occupied (handles, slot)) {
1841 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1842 result = domain->domain_id == handles->domain_ids [slot];
1845 obj = (MonoObject *)handles->entries [slot];
1849 result = domain == mono_object_domain (obj);
1852 /* print a warning? */
1854 unlock_handles (handles);
1859 * mono_gchandle_free:
1860 * @gchandle: a GCHandle's handle.
1862 * Frees the @gchandle handle. If there are no outstanding
1863 * references, the garbage collector can reclaim the memory of the
1867 mono_gchandle_free (guint32 gchandle)
1869 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1870 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1871 HandleData *handles = &gc_handles [type];
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 if (handles->entries [slot])
1879 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1881 handles->entries [slot] = NULL;
1883 vacate_slot (handles, slot);
1885 /* print a warning? */
1887 #ifndef DISABLE_PERFCOUNTERS
1888 mono_perfcounters->gc_num_handles--;
1890 /*g_print ("freed entry %d of type %d\n", slot, handles->type);*/
1891 unlock_handles (handles);
1892 mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_DESTROYED, handles->type, gchandle, NULL);
1896 * mono_gchandle_free_domain:
1897 * @domain: domain that is unloading
1899 * Function used internally to cleanup any GC handle for objects belonging
1900 * to the specified domain during appdomain unload.
1903 mono_gchandle_free_domain (MonoDomain *domain)
1907 for (type = HANDLE_TYPE_MIN; type < HANDLE_PINNED; ++type) {
1909 HandleData *handles = &gc_handles [type];
1910 lock_handles (handles);
1911 for (slot = 0; slot < handles->size; ++slot) {
1912 if (!slot_occupied (handles, slot))
1914 if (MONO_GC_HANDLE_TYPE_IS_WEAK (type)) {
1915 if (domain->domain_id == handles->domain_ids [slot]) {
1916 vacate_slot (handles, slot);
1917 if (handles->entries [slot])
1918 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1921 if (handles->entries [slot] && mono_object_domain (handles->entries [slot]) == domain) {
1922 vacate_slot (handles, slot);
1923 handles->entries [slot] = NULL;
1927 unlock_handles (handles);
1933 // Quiet Visual Studio linker warning, LNK4221, in cases when this source file intentional ends up empty.
1934 void __mono_win32_boehm_gc_quiet_lnk4221(void) {}
1936 #endif /* no Boehm GC */