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 boehm_thread_detach (MonoThreadInfo *p);
63 register_test_toggleref_callback (void);
65 #define BOEHM_GC_BIT_FINALIZER_AWARE 1
66 static MonoGCFinalizerCallbacks fin_callbacks;
70 static mono_mutex_t handle_section;
71 #define lock_handles(handles) mono_os_mutex_lock (&handle_section)
72 #define unlock_handles(handles) mono_os_mutex_unlock (&handle_section)
79 guint slot_hint : 24; /* starting slot for search in bitmap */
80 /* 2^16 appdomains should be enough for everyone (though I know I'll regret this in 20 years) */
81 /* we alloc this only for weak refs, since we can get the domain directly in the other cases */
85 #define EMPTY_HANDLE_DATA(type) {NULL, NULL, 0, (type), 0, NULL}
87 /* weak and weak-track arrays will be allocated in malloc memory
89 static HandleData gc_handles [] = {
90 EMPTY_HANDLE_DATA (HANDLE_WEAK),
91 EMPTY_HANDLE_DATA (HANDLE_WEAK_TRACK),
92 EMPTY_HANDLE_DATA (HANDLE_NORMAL),
93 EMPTY_HANDLE_DATA (HANDLE_PINNED)
97 mono_gc_warning (char *msg, GC_word arg)
99 mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_GC, msg, (unsigned long)arg);
103 mono_gc_base_init (void)
105 MonoThreadInfoCallbacks cb;
112 mono_counters_init ();
115 * Handle the case when we are called from a thread different from the main thread,
117 * FIXME: Move this to libgc where it belongs.
119 * we used to do this only when running on valgrind,
120 * but it happens also in other setups.
122 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) && !defined(__native_client__)
127 pthread_getattr_np (pthread_self (), &attr);
128 pthread_attr_getstack (&attr, &sstart, &size);
129 pthread_attr_destroy (&attr);
130 /*g_print ("stackbottom pth is: %p\n", (char*)sstart + size);*/
133 * The calculation above doesn't seem to work on ia64, also we need to set
134 * GC_register_stackbottom as well, but don't know how.
137 /* apparently with some linuxthreads implementations sstart can be NULL,
138 * fallback to the more imprecise method (bug# 78096).
141 GC_stackbottom = (char*)sstart + size;
144 gsize stack_bottom = (gsize)&dummy;
145 stack_bottom += 4095;
146 stack_bottom &= ~4095;
147 GC_stackbottom = (char*)stack_bottom;
151 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
152 GC_stackbottom = (char*)pthread_get_stackaddr_np (pthread_self ());
153 #elif defined(__OpenBSD__)
154 # include <pthread_np.h>
159 rslt = pthread_stackseg_np(pthread_self(), &ss);
160 g_assert (rslt == 0);
162 GC_stackbottom = (char*)ss.ss_sp;
164 #elif defined(__native_client__)
165 /* Do nothing, GC_stackbottom is set correctly in libgc */
169 gsize stack_bottom = (gsize)&dummy;
170 stack_bottom += 4095;
171 stack_bottom &= ~4095;
172 /*g_print ("stackbottom is: %p\n", (char*)stack_bottom);*/
173 GC_stackbottom = (char*)stack_bottom;
177 #if !defined(PLATFORM_ANDROID)
178 /* If GC_no_dls is set to true, GC_find_limit is not called. This causes a seg fault on Android. */
182 if ((env = g_getenv ("MONO_GC_DEBUG"))) {
183 char **opts = g_strsplit (env, ",", -1);
184 for (char **ptr = opts; ptr && *ptr; ptr ++) {
186 if (!strcmp (opt, "do-not-finalize")) {
187 mono_do_not_finalize = 1;
188 } else if (!strcmp (opt, "log-finalizers")) {
197 GC_set_warn_proc (mono_gc_warning);
198 GC_finalize_on_demand = 1;
199 GC_finalizer_notifier = mono_gc_finalize_notify;
201 GC_init_gcj_malloc (5, NULL);
202 GC_allow_register_threads ();
204 if ((env = g_getenv ("MONO_GC_PARAMS"))) {
205 char **ptr, **opts = g_strsplit (env, ",", -1);
206 for (ptr = opts; *ptr; ++ptr) {
208 if (g_str_has_prefix (opt, "max-heap-size=")) {
211 opt = strchr (opt, '=') + 1;
212 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &max_heap)) {
213 if (max_heap < MIN_BOEHM_MAX_HEAP_SIZE) {
214 fprintf (stderr, "max-heap-size must be at least %dMb.\n", MIN_BOEHM_MAX_HEAP_SIZE_IN_MB);
217 GC_set_max_heap_size (max_heap);
219 fprintf (stderr, "max-heap-size must be an integer.\n");
223 } else if (g_str_has_prefix (opt, "toggleref-test")) {
224 register_test_toggleref_callback ();
227 /* Could be a parameter for sgen */
229 fprintf (stderr, "MONO_GC_PARAMS must be a comma-delimited list of one or more of the following:\n");
230 fprintf (stderr, " max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
238 memset (&cb, 0, sizeof (cb));
239 cb.thread_register = boehm_thread_register;
240 cb.thread_unregister = boehm_thread_unregister;
241 cb.thread_detach = boehm_thread_detach;
242 cb.mono_method_is_critical = (gboolean (*)(void *))mono_runtime_is_critical_method;
244 mono_threads_init (&cb, sizeof (MonoThreadInfo));
245 mono_os_mutex_init (&mono_gc_lock);
246 mono_os_mutex_init_recursive (&handle_section);
248 mono_thread_info_attach (&dummy);
250 mono_gc_enable_events ();
252 MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_NORMAL].entries, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
253 MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_PINNED].entries, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
255 gc_initialized = TRUE;
259 mono_gc_base_cleanup (void)
261 GC_finalizer_notifier = NULL;
266 * @generation: GC generation identifier
268 * Perform a garbage collection for the given generation, higher numbers
269 * mean usually older objects. Collecting a high-numbered generation
270 * implies collecting also the lower-numbered generations.
271 * The maximum value for @generation can be retrieved with a call to
272 * mono_gc_max_generation(), so this function is usually called as:
274 * mono_gc_collect (mono_gc_max_generation ());
277 mono_gc_collect (int generation)
279 #ifndef DISABLE_PERFCOUNTERS
280 mono_perfcounters->gc_induced++;
286 * mono_gc_max_generation:
288 * Get the maximum generation number used by the current garbage
289 * collector. The value will be 0 for the Boehm collector, 1 or more
290 * for the generational collectors.
292 * Returns: the maximum generation number.
295 mono_gc_max_generation (void)
301 * mono_gc_get_generation:
302 * @object: a managed object
304 * Get the garbage collector's generation that @object belongs to.
305 * Use this has a hint only.
307 * Returns: a garbage collector generation number
310 mono_gc_get_generation (MonoObject *object)
316 * mono_gc_collection_count:
317 * @generation: a GC generation number
319 * Get how many times a garbage collection has been performed
320 * for the given @generation number.
322 * Returns: the number of garbage collections
325 mono_gc_collection_count (int generation)
331 * mono_gc_add_memory_pressure:
332 * @value: amount of bytes
334 * Adjust the garbage collector's view of how many bytes of memory
335 * are indirectly referenced by managed objects (for example unmanaged
336 * memory holding image or other binary data).
337 * This is a hint only to the garbage collector algorithm.
338 * Note that negative amounts of @value will decrease the memory
342 mono_gc_add_memory_pressure (gint64 value)
347 * mono_gc_get_used_size:
349 * Get the approximate amount of memory used by managed objects.
351 * Returns: the amount of memory used in bytes
354 mono_gc_get_used_size (void)
356 return GC_get_heap_size () - GC_get_free_bytes ();
360 * mono_gc_get_heap_size:
362 * Get the amount of memory used by the garbage collector.
364 * Returns: the size of the heap in bytes
367 mono_gc_get_heap_size (void)
369 return GC_get_heap_size ();
373 mono_gc_is_gc_thread (void)
375 return GC_thread_is_registered ();
379 mono_gc_register_thread (void *baseptr)
381 return mono_thread_info_attach (baseptr) != NULL;
385 boehm_thread_register (MonoThreadInfo* info, void *baseptr)
387 struct GC_stack_base sb;
390 /* TODO: use GC_get_stack_base instead of baseptr. */
391 sb.mem_base = baseptr;
392 res = GC_register_my_thread (&sb);
393 if (res == GC_UNIMPLEMENTED)
394 return NULL; /* Cannot happen with GC v7+. */
396 info->handle_stack = mono_handle_stack_alloc ();
402 boehm_thread_unregister (MonoThreadInfo *p)
404 MonoNativeThreadId tid;
406 tid = mono_thread_info_get_tid (p);
408 if (p->runtime_thread)
409 mono_threads_add_joinable_thread ((gpointer)tid);
413 boehm_thread_detach (MonoThreadInfo *p)
415 if (mono_thread_internal_current_is_attached ())
416 mono_thread_detach_internal (mono_thread_internal_current ());
420 mono_object_is_alive (MonoObject* o)
422 return GC_is_marked ((ptr_t)o);
426 mono_gc_walk_heap (int flags, MonoGCReferences callback, void *data)
431 static gint64 gc_start_time;
434 on_gc_notification (GC_EventType event)
436 MonoGCEvent e = (MonoGCEvent)event;
439 case MONO_GC_EVENT_PRE_STOP_WORLD:
440 MONO_GC_WORLD_STOP_BEGIN ();
443 case MONO_GC_EVENT_POST_STOP_WORLD:
444 MONO_GC_WORLD_STOP_END ();
447 case MONO_GC_EVENT_PRE_START_WORLD:
448 MONO_GC_WORLD_RESTART_BEGIN (1);
451 case MONO_GC_EVENT_POST_START_WORLD:
452 MONO_GC_WORLD_RESTART_END (1);
455 case MONO_GC_EVENT_START:
457 #ifndef DISABLE_PERFCOUNTERS
458 if (mono_perfcounters)
459 mono_perfcounters->gc_collections0++;
461 gc_stats.major_gc_count ++;
462 gc_start_time = mono_100ns_ticks ();
465 case MONO_GC_EVENT_END:
467 #if defined(ENABLE_DTRACE) && defined(__sun__)
468 /* This works around a dtrace -G problem on Solaris.
469 Limit its actual use to when the probe is enabled. */
470 if (MONO_GC_END_ENABLED ())
474 #ifndef DISABLE_PERFCOUNTERS
475 if (mono_perfcounters) {
476 guint64 heap_size = GC_get_heap_size ();
477 guint64 used_size = heap_size - GC_get_free_bytes ();
478 mono_perfcounters->gc_total_bytes = used_size;
479 mono_perfcounters->gc_committed_bytes = heap_size;
480 mono_perfcounters->gc_reserved_bytes = heap_size;
481 mono_perfcounters->gc_gen0size = heap_size;
484 gc_stats.major_gc_time += mono_100ns_ticks () - gc_start_time;
485 mono_trace_message (MONO_TRACE_GC, "gc took %d usecs", (mono_100ns_ticks () - gc_start_time) / 10);
491 mono_profiler_gc_event (e, 0);
494 case MONO_GC_EVENT_PRE_STOP_WORLD:
495 mono_thread_info_suspend_lock ();
496 mono_profiler_gc_event (MONO_GC_EVENT_PRE_STOP_WORLD_LOCKED, 0);
498 case MONO_GC_EVENT_POST_START_WORLD:
499 mono_thread_info_suspend_unlock ();
500 mono_profiler_gc_event (MONO_GC_EVENT_POST_START_WORLD_UNLOCKED, 0);
509 on_gc_heap_resize (size_t new_size)
511 guint64 heap_size = GC_get_heap_size ();
512 #ifndef DISABLE_PERFCOUNTERS
513 if (mono_perfcounters) {
514 mono_perfcounters->gc_committed_bytes = heap_size;
515 mono_perfcounters->gc_reserved_bytes = heap_size;
516 mono_perfcounters->gc_gen0size = heap_size;
519 mono_profiler_gc_heap_resize (new_size);
523 mono_gc_enable_events (void)
525 GC_set_on_collection_event (on_gc_notification);
526 GC_on_heap_resize = on_gc_heap_resize;
529 static gboolean alloc_events = FALSE;
532 mono_gc_enable_alloc_events (void)
538 mono_gc_register_root (char *start, size_t size, void *descr, MonoGCRootSource source, const char *msg)
540 /* for some strange reason, they want one extra byte on the end */
541 GC_add_roots (start, start + size + 1);
547 mono_gc_deregister_root (char* addr)
550 /* FIXME: libgc doesn't define this work win32 for some reason */
551 /* FIXME: No size info */
552 GC_remove_roots (addr, addr + sizeof (gpointer) + 1);
557 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
559 /* libgc requires that we use HIDE_POINTER... */
560 *link_addr = (void*)HIDE_POINTER (obj);
562 GC_REGISTER_LONG_LINK (link_addr, obj);
564 GC_GENERAL_REGISTER_DISAPPEARING_LINK (link_addr, obj);
568 mono_gc_weak_link_remove (void **link_addr, gboolean track)
571 GC_unregister_long_link (link_addr);
573 GC_unregister_disappearing_link (link_addr);
578 reveal_link (gpointer link_addr)
580 void **link_a = (void **)link_addr;
581 return REVEAL_POINTER (*link_a);
585 mono_gc_weak_link_get (void **link_addr)
587 MonoObject *obj = (MonoObject *)GC_call_with_alloc_lock (reveal_link, link_addr);
588 if (obj == (MonoObject *) -1)
594 mono_gc_make_descr_for_string (gsize *bitmap, int numbits)
596 return mono_gc_make_descr_from_bitmap (bitmap, numbits);
600 mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size)
602 return mono_gc_make_descr_from_bitmap (bitmap, numbits);
606 mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size)
608 /* libgc has no usable support for arrays... */
609 return GC_NO_DESCRIPTOR;
613 mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
615 /* It seems there are issues when the bitmap doesn't fit: play it safe */
617 return GC_NO_DESCRIPTOR;
619 return (gpointer)GC_make_descriptor ((GC_bitmap)bitmap, numbits);
623 mono_gc_make_root_descr_all_refs (int numbits)
629 mono_gc_alloc_fixed (size_t size, void *descr, MonoGCRootSource source, const char *msg)
631 /* To help track down typed allocation bugs */
635 if (count == atoi (g_getenv ("COUNT2")))
637 if (count > atoi (g_getenv ("COUNT2")))
638 return GC_MALLOC (size);
642 return GC_MALLOC_EXPLICITLY_TYPED (size, (GC_descr)descr);
644 return GC_MALLOC (size);
648 mono_gc_free_fixed (void* addr)
653 mono_gc_alloc_obj (MonoVTable *vtable, size_t size)
657 if (!vtable->klass->has_references) {
658 obj = (MonoObject *)GC_MALLOC_ATOMIC (size);
659 if (G_UNLIKELY (!obj))
662 obj->vtable = vtable;
663 obj->synchronisation = NULL;
665 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
666 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
667 obj = (MonoObject *)GC_GCJ_MALLOC (size, vtable);
668 if (G_UNLIKELY (!obj))
671 obj = (MonoObject *)GC_MALLOC (size);
672 if (G_UNLIKELY (!obj))
675 obj->vtable = vtable;
678 if (G_UNLIKELY (alloc_events))
679 mono_profiler_allocation (obj);
685 mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length)
689 if (!vtable->klass->has_references) {
690 obj = (MonoArray *)GC_MALLOC_ATOMIC (size);
691 if (G_UNLIKELY (!obj))
694 obj->obj.vtable = vtable;
695 obj->obj.synchronisation = NULL;
697 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
698 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
699 obj = (MonoArray *)GC_GCJ_MALLOC (size, vtable);
700 if (G_UNLIKELY (!obj))
703 obj = (MonoArray *)GC_MALLOC (size);
704 if (G_UNLIKELY (!obj))
707 obj->obj.vtable = vtable;
710 obj->max_length = max_length;
712 if (G_UNLIKELY (alloc_events))
713 mono_profiler_allocation (&obj->obj);
719 mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uintptr_t bounds_size)
723 if (!vtable->klass->has_references) {
724 obj = (MonoArray *)GC_MALLOC_ATOMIC (size);
725 if (G_UNLIKELY (!obj))
728 obj->obj.vtable = vtable;
729 obj->obj.synchronisation = NULL;
731 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
732 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
733 obj = (MonoArray *)GC_GCJ_MALLOC (size, vtable);
734 if (G_UNLIKELY (!obj))
737 obj = (MonoArray *)GC_MALLOC (size);
738 if (G_UNLIKELY (!obj))
741 obj->obj.vtable = vtable;
744 obj->max_length = max_length;
747 obj->bounds = (MonoArrayBounds *) ((char *) obj + size - bounds_size);
749 if (G_UNLIKELY (alloc_events))
750 mono_profiler_allocation (&obj->obj);
756 mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len)
758 MonoString *obj = (MonoString *)GC_MALLOC_ATOMIC (size);
759 if (G_UNLIKELY (!obj))
762 obj->object.vtable = vtable;
763 obj->object.synchronisation = NULL;
765 obj->chars [len] = 0;
767 if (G_UNLIKELY (alloc_events))
768 mono_profiler_allocation (&obj->object);
774 mono_gc_alloc_mature (MonoVTable *vtable, size_t size)
776 return mono_gc_alloc_obj (vtable, size);
780 mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size)
782 return mono_gc_alloc_obj (vtable, size);
786 mono_gc_invoke_finalizers (void)
788 /* There is a bug in GC_invoke_finalizer () in versions <= 6.2alpha4:
789 * the 'mem_freed' variable is not initialized when there are no
790 * objects to finalize, which leads to strange behavior later on.
791 * The check is necessary to work around that bug.
793 if (GC_should_invoke_finalizers ())
794 return GC_invoke_finalizers ();
799 mono_gc_pending_finalizers (void)
801 return GC_should_invoke_finalizers ();
805 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
807 *(void**)field_ptr = value;
811 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
813 *(void**)slot_ptr = value;
817 mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
819 mono_gc_memmove_aligned (dest_ptr, src_ptr, count * sizeof (gpointer));
823 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
825 *(void**)ptr = value;
829 mono_gc_wbarrier_generic_store_atomic (gpointer ptr, MonoObject *value)
831 InterlockedWritePointer ((volatile gpointer *)ptr, value);
835 mono_gc_wbarrier_generic_nostore (gpointer ptr)
840 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
842 mono_gc_memmove_atomic (dest, src, count * mono_class_value_size (klass, NULL));
846 mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
848 /* do not copy the sync state */
849 mono_gc_memmove_aligned ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
850 mono_object_class (obj)->instance_size - sizeof (MonoObject));
854 mono_gc_clear_domain (MonoDomain *domain)
859 mono_gc_suspend_finalizers (void)
864 mono_gc_get_suspend_signal (void)
866 return GC_get_suspend_signal ();
870 mono_gc_get_restart_signal (void)
872 return GC_get_thr_restart_signal ();
875 #if defined(USE_COMPILER_TLS) && defined(__linux__) && (defined(__i386__) || defined(__x86_64__))
876 extern __thread MONO_TLS_FAST void* GC_thread_tls;
877 #include "metadata-internals.h"
883 while (!(v & (1 << i)))
890 ATYPE_FREEPTR_FOR_BOX,
898 create_allocator (int atype, int tls_key, gboolean slowpath)
900 int index_var, bytes_var, my_fl_var, my_entry_var;
901 guint32 no_freelist_branch, not_small_enough_branch = 0;
902 guint32 size_overflow_branch = 0;
903 MonoMethodBuilder *mb;
905 MonoMethodSignature *csig;
906 const char *name = NULL;
909 if (atype == ATYPE_FREEPTR) {
910 name = slowpath ? "SlowAllocPtrfree" : "AllocPtrfree";
911 } else if (atype == ATYPE_FREEPTR_FOR_BOX) {
912 name = slowpath ? "SlowAllocPtrfreeBox" : "AllocPtrfreeBox";
913 } else if (atype == ATYPE_NORMAL) {
914 name = slowpath ? "SlowAlloc" : "Alloc";
915 } else if (atype == ATYPE_GCJ) {
916 name = slowpath ? "SlowAllocGcj" : "AllocGcj";
917 } else if (atype == ATYPE_STRING) {
918 name = slowpath ? "SlowAllocString" : "AllocString";
920 g_assert_not_reached ();
923 csig = mono_metadata_signature_alloc (mono_defaults.corlib, 2);
925 if (atype == ATYPE_STRING) {
926 csig->ret = &mono_defaults.string_class->byval_arg;
927 csig->params [0] = &mono_defaults.int_class->byval_arg;
928 csig->params [1] = &mono_defaults.int32_class->byval_arg;
930 csig->ret = &mono_defaults.object_class->byval_arg;
931 csig->params [0] = &mono_defaults.int_class->byval_arg;
932 csig->params [1] = &mono_defaults.int32_class->byval_arg;
935 mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_ALLOC);
938 goto always_slowpath;
940 bytes_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
941 if (atype == ATYPE_STRING) {
942 /* a string alloator method takes the args: (vtable, len) */
943 /* bytes = (offsetof (MonoString, chars) + ((len + 1) * 2)); */
944 mono_mb_emit_ldarg (mb, 1);
945 mono_mb_emit_icon (mb, 1);
946 mono_mb_emit_byte (mb, MONO_CEE_ADD);
947 mono_mb_emit_icon (mb, 1);
948 mono_mb_emit_byte (mb, MONO_CEE_SHL);
949 // sizeof (MonoString) might include padding
950 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, chars));
951 mono_mb_emit_byte (mb, MONO_CEE_ADD);
952 mono_mb_emit_stloc (mb, bytes_var);
954 mono_mb_emit_ldarg (mb, 1);
955 mono_mb_emit_stloc (mb, bytes_var);
958 /* this is needed for strings/arrays only as the other big types are never allocated with this method */
959 if (atype == ATYPE_STRING) {
961 /* if (!SMALL_ENOUGH (bytes)) jump slow_path;*/
962 mono_mb_emit_ldloc (mb, bytes_var);
963 mono_mb_emit_icon (mb, (NFREELISTS-1) * GRANULARITY);
964 not_small_enough_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_UN_S);
965 /* check for overflow */
966 mono_mb_emit_ldloc (mb, bytes_var);
967 mono_mb_emit_icon (mb, sizeof (MonoString));
968 size_overflow_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLE_UN_S);
971 /* int index = INDEX_FROM_BYTES(bytes); */
972 index_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
974 mono_mb_emit_ldloc (mb, bytes_var);
975 mono_mb_emit_icon (mb, GRANULARITY - 1);
976 mono_mb_emit_byte (mb, MONO_CEE_ADD);
977 mono_mb_emit_icon (mb, shift_amount (GRANULARITY));
978 mono_mb_emit_byte (mb, MONO_CEE_SHR_UN);
979 mono_mb_emit_icon (mb, shift_amount (sizeof (gpointer)));
980 mono_mb_emit_byte (mb, MONO_CEE_SHL);
981 /* index var is already adjusted into bytes */
982 mono_mb_emit_stloc (mb, index_var);
984 my_fl_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
985 my_entry_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
986 /* my_fl = ((GC_thread)tsd) -> ptrfree_freelists + index; */
987 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
988 mono_mb_emit_byte (mb, 0x0D); /* CEE_MONO_TLS */
989 mono_mb_emit_i4 (mb, tls_key);
990 if (atype == ATYPE_FREEPTR || atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING)
991 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
992 + G_STRUCT_OFFSET (struct thread_local_freelists,
994 else if (atype == ATYPE_NORMAL)
995 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
996 + G_STRUCT_OFFSET (struct thread_local_freelists,
998 else if (atype == ATYPE_GCJ)
999 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
1000 + G_STRUCT_OFFSET (struct thread_local_freelists,
1003 g_assert_not_reached ();
1004 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1005 mono_mb_emit_ldloc (mb, index_var);
1006 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1007 mono_mb_emit_stloc (mb, my_fl_var);
1009 /* my_entry = *my_fl; */
1010 mono_mb_emit_ldloc (mb, my_fl_var);
1011 mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
1012 mono_mb_emit_stloc (mb, my_entry_var);
1014 /* if (EXPECT((word)my_entry >= HBLKSIZE, 1)) { */
1015 mono_mb_emit_ldloc (mb, my_entry_var);
1016 mono_mb_emit_icon (mb, HBLKSIZE);
1017 no_freelist_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
1019 /* ptr_t next = obj_link(my_entry); *my_fl = next; */
1020 mono_mb_emit_ldloc (mb, my_fl_var);
1021 mono_mb_emit_ldloc (mb, my_entry_var);
1022 mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
1023 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1025 /* set the vtable and clear the words in the object */
1026 mono_mb_emit_ldloc (mb, my_entry_var);
1027 mono_mb_emit_ldarg (mb, 0);
1028 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1030 if (atype == ATYPE_FREEPTR) {
1031 int start_var, end_var, start_loop;
1032 /* end = my_entry + bytes; start = my_entry + sizeof (gpointer);
1034 start_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1035 end_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1036 mono_mb_emit_ldloc (mb, my_entry_var);
1037 mono_mb_emit_ldloc (mb, bytes_var);
1038 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1039 mono_mb_emit_stloc (mb, end_var);
1040 mono_mb_emit_ldloc (mb, my_entry_var);
1041 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
1042 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1043 mono_mb_emit_stloc (mb, start_var);
1047 * } while (start < end);
1049 start_loop = mono_mb_get_label (mb);
1050 mono_mb_emit_ldloc (mb, start_var);
1051 mono_mb_emit_icon (mb, 0);
1052 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1053 mono_mb_emit_ldloc (mb, start_var);
1054 mono_mb_emit_icon (mb, sizeof (gpointer));
1055 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1056 mono_mb_emit_stloc (mb, start_var);
1058 mono_mb_emit_ldloc (mb, start_var);
1059 mono_mb_emit_ldloc (mb, end_var);
1060 mono_mb_emit_byte (mb, MONO_CEE_BLT_UN_S);
1061 mono_mb_emit_byte (mb, start_loop - (mono_mb_get_label (mb) + 1));
1062 } else if (atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING) {
1063 /* need to clear just the sync pointer */
1064 mono_mb_emit_ldloc (mb, my_entry_var);
1065 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
1066 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1067 mono_mb_emit_icon (mb, 0);
1068 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1071 if (atype == ATYPE_STRING) {
1072 /* need to set length and clear the last char */
1073 /* s->length = len; */
1074 mono_mb_emit_ldloc (mb, my_entry_var);
1075 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, length));
1076 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1077 mono_mb_emit_ldarg (mb, 1);
1078 mono_mb_emit_byte (mb, MONO_CEE_STIND_I4);
1079 /* s->chars [len] = 0; */
1080 mono_mb_emit_ldloc (mb, my_entry_var);
1081 mono_mb_emit_ldloc (mb, bytes_var);
1082 mono_mb_emit_icon (mb, 2);
1083 mono_mb_emit_byte (mb, MONO_CEE_SUB);
1084 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1085 mono_mb_emit_icon (mb, 0);
1086 mono_mb_emit_byte (mb, MONO_CEE_STIND_I2);
1089 /* return my_entry; */
1090 mono_mb_emit_ldloc (mb, my_entry_var);
1091 mono_mb_emit_byte (mb, MONO_CEE_RET);
1093 mono_mb_patch_short_branch (mb, no_freelist_branch);
1094 if (not_small_enough_branch > 0)
1095 mono_mb_patch_short_branch (mb, not_small_enough_branch);
1096 if (size_overflow_branch > 0)
1097 mono_mb_patch_short_branch (mb, size_overflow_branch);
1099 /* the slow path: we just call back into the runtime */
1101 if (atype == ATYPE_STRING) {
1102 mono_mb_emit_ldarg (mb, 1);
1103 mono_mb_emit_icall (mb, ves_icall_string_alloc);
1105 mono_mb_emit_ldarg (mb, 0);
1106 mono_mb_emit_icall (mb, ves_icall_object_new_specific);
1109 mono_mb_emit_byte (mb, MONO_CEE_RET);
1111 info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE);
1112 info->d.alloc.gc_name = "boehm";
1113 info->d.alloc.alloc_type = atype;
1114 mb->init_locals = FALSE;
1116 res = mono_mb_create (mb, csig, 8, info);
1122 static MonoMethod* alloc_method_cache [ATYPE_NUM];
1123 static MonoMethod* slowpath_alloc_method_cache [ATYPE_NUM];
1125 static G_GNUC_UNUSED gboolean
1126 mono_gc_is_critical_method (MonoMethod *method)
1130 for (i = 0; i < ATYPE_NUM; ++i)
1131 if (method == alloc_method_cache [i] || method == slowpath_alloc_method_cache [i])
1138 * If possible, generate a managed method that can quickly allocate objects in class
1139 * @klass. The method will typically have an thread-local inline allocation sequence.
1140 * The signature of the called method is:
1141 * object allocate (MonoVTable *vtable)
1142 * Some of the logic here is similar to mono_class_get_allocation_ftn () i object.c,
1144 * The thread local alloc logic is taken from libgc/pthread_support.c.
1148 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size)
1152 MONO_THREAD_VAR_OFFSET (GC_thread_tls, offset);
1154 /*g_print ("thread tls: %d\n", offset);*/
1157 if (!SMALL_ENOUGH (klass->instance_size))
1159 if (mono_class_has_finalizer (klass) || mono_class_is_marshalbyref (klass))
1161 if (mono_profiler_get_events () & (MONO_PROFILE_ALLOCATIONS | MONO_PROFILE_STATISTICAL))
1165 if (mono_class_is_open_constructed_type (&klass->byval_arg))
1167 if (klass->byval_arg.type == MONO_TYPE_STRING) {
1168 atype = ATYPE_STRING;
1169 } else if (!known_instance_size) {
1171 } else if (!klass->has_references) {
1173 atype = ATYPE_FREEPTR_FOR_BOX;
1175 atype = ATYPE_FREEPTR;
1179 * disabled because we currently do a runtime choice anyway, to
1180 * deal with multiple appdomains.
1181 if (vtable->gc_descr != GC_NO_DESCRIPTOR)
1184 atype = ATYPE_NORMAL;
1187 return mono_gc_get_managed_allocator_by_type (atype, MANAGED_ALLOCATOR_REGULAR);
1191 mono_gc_get_managed_array_allocator (MonoClass *klass)
1197 * mono_gc_get_managed_allocator_by_type:
1199 * Return a managed allocator method corresponding to allocator type ATYPE.
1202 mono_gc_get_managed_allocator_by_type (int atype, ManagedAllocatorVariant variant)
1206 gboolean slowpath = variant != MANAGED_ALLOCATOR_REGULAR;
1207 MonoMethod **cache = slowpath ? slowpath_alloc_method_cache : alloc_method_cache;
1208 MONO_THREAD_VAR_OFFSET (GC_thread_tls, offset);
1210 mono_tls_key_set_offset (TLS_KEY_BOEHM_GC_THREAD, offset);
1212 res = cache [atype];
1216 res = create_allocator (atype, TLS_KEY_BOEHM_GC_THREAD, slowpath);
1217 mono_os_mutex_lock (&mono_gc_lock);
1218 if (cache [atype]) {
1219 mono_free_method (res);
1220 res = cache [atype];
1222 mono_memory_barrier ();
1223 cache [atype] = res;
1225 mono_os_mutex_unlock (&mono_gc_lock);
1230 mono_gc_get_managed_allocator_types (void)
1236 mono_gc_get_write_barrier (void)
1238 g_assert_not_reached ();
1244 static G_GNUC_UNUSED gboolean
1245 mono_gc_is_critical_method (MonoMethod *method)
1251 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size)
1257 mono_gc_get_managed_array_allocator (MonoClass *klass)
1263 mono_gc_get_managed_allocator_by_type (int atype, ManagedAllocatorVariant variant)
1269 mono_gc_get_managed_allocator_types (void)
1275 mono_gc_get_write_barrier (void)
1277 g_assert_not_reached ();
1284 mono_gc_get_specific_write_barrier (gboolean is_concurrent)
1286 g_assert_not_reached ();
1291 mono_gc_get_aligned_size_for_allocator (int size)
1297 mono_gc_get_gc_name (void)
1303 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
1305 return GC_call_with_alloc_lock (func, data);
1309 mono_gc_get_description (void)
1311 return g_strdup (DEFAULT_GC_NAME);
1315 mono_gc_set_desktop_mode (void)
1321 mono_gc_is_moving (void)
1327 mono_gc_is_disabled (void)
1329 if (GC_dont_gc || g_getenv ("GC_DONT_GC"))
1336 mono_gc_wbarrier_value_copy_bitmap (gpointer _dest, gpointer _src, int size, unsigned bitmap)
1338 g_assert_not_reached ();
1343 mono_gc_get_card_table (int *shift_bits, gpointer *card_mask)
1345 g_assert_not_reached ();
1350 mono_gc_card_table_nursery_check (void)
1352 g_assert_not_reached ();
1357 mono_gc_get_nursery (int *shift_bits, size_t *size)
1363 mono_gc_set_current_thread_appdomain (MonoDomain *domain)
1368 mono_gc_precise_stack_mark_enabled (void)
1374 mono_gc_get_logfile (void)
1380 mono_gc_conservatively_scan_area (void *start, void *end)
1382 g_assert_not_reached ();
1386 mono_gc_scan_object (void *obj, void *gc_data)
1388 g_assert_not_reached ();
1393 mono_gc_get_bitmap_for_descr (void *descr, int *numbits)
1395 g_assert_not_reached ();
1400 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
1405 mono_gc_set_stack_end (void *stack_end)
1409 void mono_gc_set_skip_thread (gboolean value)
1414 mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
1419 /* This assertion is not valid when GC_DEBUG is defined */
1420 g_assert (GC_base (obj) == (char*)obj - offset);
1423 GC_REGISTER_FINALIZER_NO_ORDER ((char*)obj - offset, (GC_finalization_proc)user_data, GUINT_TO_POINTER (offset), NULL, NULL);
1428 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
1430 /* it is being replaced by GC_pthread_create on some
1431 * platforms, see libgc/include/gc_pthread_redirects.h */
1432 return pthread_create (new_thread, attr, start_routine, arg);
1437 BOOL APIENTRY mono_gc_dllmain (HMODULE module_handle, DWORD reason, LPVOID reserved)
1439 return GC_DllMain (module_handle, reason, reserved);
1444 mono_gc_get_vtable_bits (MonoClass *klass)
1446 if (fin_callbacks.is_class_finalization_aware) {
1447 if (fin_callbacks.is_class_finalization_aware (klass))
1448 return BOEHM_GC_BIT_FINALIZER_AWARE;
1454 * mono_gc_register_altstack:
1456 * Register the dimensions of the normal stack and altstack with the collector.
1457 * Currently, STACK/STACK_SIZE is only used when the thread is suspended while it is on an altstack.
1460 mono_gc_register_altstack (gpointer stack, gint32 stack_size, gpointer altstack, gint32 altstack_size)
1462 GC_register_altstack (stack, stack_size, altstack, altstack_size);
1466 mono_gc_get_los_limit (void)
1472 mono_gc_set_string_length (MonoString *str, gint32 new_length)
1474 mono_unichar2 *new_end = str->chars + new_length;
1476 /* zero the discarded string. This null-delimits the string and allows
1477 * the space to be reclaimed by SGen. */
1479 memset (new_end, 0, (str->length - new_length + 1) * sizeof (mono_unichar2));
1480 str->length = new_length;
1484 mono_gc_user_markers_supported (void)
1490 mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
1492 g_assert_not_reached ();
1496 /* Toggleref support */
1499 mono_gc_toggleref_add (MonoObject *object, mono_bool strong_ref)
1501 if (GC_toggleref_add ((GC_PTR)object, (int)strong_ref) != GC_SUCCESS)
1502 g_error ("GC_toggleref_add failed\n");
1506 mono_gc_toggleref_register_callback (MonoToggleRefStatus (*proccess_toggleref) (MonoObject *obj))
1508 GC_set_toggleref_func ((GC_ToggleRefStatus (*) (GC_PTR obj)) proccess_toggleref);
1511 /* Test support code */
1513 static MonoToggleRefStatus
1514 test_toggleref_callback (MonoObject *obj)
1516 static MonoClassField *mono_toggleref_test_field;
1517 MonoToggleRefStatus status = MONO_TOGGLE_REF_DROP;
1519 if (!mono_toggleref_test_field) {
1520 mono_toggleref_test_field = mono_class_get_field_from_name (mono_object_get_class (obj), "__test");
1521 g_assert (mono_toggleref_test_field);
1524 mono_field_get_value (obj, mono_toggleref_test_field, &status);
1525 printf ("toggleref-cb obj %d\n", status);
1530 register_test_toggleref_callback (void)
1532 mono_gc_toggleref_register_callback (test_toggleref_callback);
1536 is_finalization_aware (MonoObject *obj)
1538 MonoVTable *vt = obj->vtable;
1539 return (vt->gc_bits & BOEHM_GC_BIT_FINALIZER_AWARE) == BOEHM_GC_BIT_FINALIZER_AWARE;
1543 fin_notifier (MonoObject *obj)
1545 if (is_finalization_aware (obj))
1546 fin_callbacks.object_queued_for_finalization (obj);
1550 mono_gc_register_finalizer_callbacks (MonoGCFinalizerCallbacks *callbacks)
1552 if (callbacks->version != MONO_GC_FINALIZER_EXTENSION_VERSION)
1553 g_error ("Invalid finalizer callback version. Expected %d but got %d\n", MONO_GC_FINALIZER_EXTENSION_VERSION, callbacks->version);
1555 fin_callbacks = *callbacks;
1557 GC_set_await_finalize_proc ((void (*) (GC_PTR))fin_notifier);
1560 #define BITMAP_SIZE (sizeof (*((HandleData *)NULL)->bitmap) * CHAR_BIT)
1562 static inline gboolean
1563 slot_occupied (HandleData *handles, guint slot) {
1564 return handles->bitmap [slot / BITMAP_SIZE] & (1 << (slot % BITMAP_SIZE));
1568 vacate_slot (HandleData *handles, guint slot) {
1569 handles->bitmap [slot / BITMAP_SIZE] &= ~(1 << (slot % BITMAP_SIZE));
1573 occupy_slot (HandleData *handles, guint slot) {
1574 handles->bitmap [slot / BITMAP_SIZE] |= 1 << (slot % BITMAP_SIZE);
1578 find_first_unset (guint32 bitmap)
1581 for (i = 0; i < 32; ++i) {
1582 if (!(bitmap & (1 << i)))
1589 handle_data_alloc_entries (HandleData *handles)
1592 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1593 handles->entries = (void **)g_malloc0 (sizeof (*handles->entries) * handles->size);
1594 handles->domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * handles->size);
1596 handles->entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * handles->size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
1598 handles->bitmap = (guint32 *)g_malloc0 (handles->size / CHAR_BIT);
1602 handle_data_next_unset (HandleData *handles)
1605 for (slot = handles->slot_hint; slot < handles->size / BITMAP_SIZE; ++slot) {
1606 if (handles->bitmap [slot] == 0xffffffff)
1608 handles->slot_hint = slot;
1609 return find_first_unset (handles->bitmap [slot]);
1615 handle_data_first_unset (HandleData *handles)
1618 for (slot = 0; slot < handles->slot_hint; ++slot) {
1619 if (handles->bitmap [slot] == 0xffffffff)
1621 handles->slot_hint = slot;
1622 return find_first_unset (handles->bitmap [slot]);
1627 /* Returns the index of the current slot in the bitmap. */
1629 handle_data_grow (HandleData *handles, gboolean track)
1631 guint32 *new_bitmap;
1632 guint32 new_size = handles->size * 2; /* always double: we memset to 0 based on this below */
1634 /* resize and copy the bitmap */
1635 new_bitmap = (guint32 *)g_malloc0 (new_size / CHAR_BIT);
1636 memcpy (new_bitmap, handles->bitmap, handles->size / CHAR_BIT);
1637 g_free (handles->bitmap);
1638 handles->bitmap = new_bitmap;
1640 /* resize and copy the entries */
1641 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1643 guint16 *domain_ids;
1645 domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * new_size);
1646 entries = (void **)g_malloc0 (sizeof (*handles->entries) * new_size);
1647 memcpy (domain_ids, handles->domain_ids, sizeof (*handles->domain_ids) * handles->size);
1648 for (i = 0; i < handles->size; ++i) {
1649 MonoObject *obj = mono_gc_weak_link_get (&(handles->entries [i]));
1651 mono_gc_weak_link_add (&(entries [i]), obj, track);
1652 mono_gc_weak_link_remove (&(handles->entries [i]), track);
1654 g_assert (!handles->entries [i]);
1657 g_free (handles->entries);
1658 g_free (handles->domain_ids);
1659 handles->entries = entries;
1660 handles->domain_ids = domain_ids;
1663 entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * new_size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
1664 mono_gc_memmove_aligned (entries, handles->entries, sizeof (*handles->entries) * handles->size);
1665 mono_gc_free_fixed (handles->entries);
1666 handles->entries = entries;
1668 handles->slot_hint = handles->size / BITMAP_SIZE;
1669 handles->size = new_size;
1673 alloc_handle (HandleData *handles, MonoObject *obj, gboolean track)
1677 lock_handles (handles);
1679 handle_data_alloc_entries (handles);
1680 i = handle_data_next_unset (handles);
1681 if (i == -1 && handles->slot_hint != 0)
1682 i = handle_data_first_unset (handles);
1684 handle_data_grow (handles, track);
1687 slot = handles->slot_hint * BITMAP_SIZE + i;
1688 occupy_slot (handles, slot);
1689 handles->entries [slot] = NULL;
1690 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1691 /*FIXME, what to use when obj == null?*/
1692 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
1694 mono_gc_weak_link_add (&(handles->entries [slot]), obj, track);
1696 handles->entries [slot] = obj;
1699 #ifndef DISABLE_PERFCOUNTERS
1700 mono_perfcounters->gc_num_handles++;
1702 unlock_handles (handles);
1703 res = MONO_GC_HANDLE (slot, handles->type);
1704 mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_CREATED, handles->type, res, obj);
1709 * mono_gchandle_new:
1710 * @obj: managed object to get a handle for
1711 * @pinned: whether the object should be pinned
1713 * This returns a handle that wraps the object, this is used to keep a
1714 * reference to a managed object from the unmanaged world and preventing the
1715 * object from being disposed.
1717 * If @pinned is false the address of the object can not be obtained, if it is
1718 * true the address of the object can be obtained. This will also pin the
1719 * object so it will not be possible by a moving garbage collector to move the
1722 * Returns: a handle that can be used to access the object from
1726 mono_gchandle_new (MonoObject *obj, gboolean pinned)
1728 return alloc_handle (&gc_handles [pinned? HANDLE_PINNED: HANDLE_NORMAL], obj, FALSE);
1732 * mono_gchandle_new_weakref:
1733 * @obj: managed object to get a handle for
1734 * @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.
1736 * This returns a weak handle that wraps the object, this is used to
1737 * keep a reference to a managed object from the unmanaged world.
1738 * Unlike the mono_gchandle_new the object can be reclaimed by the
1739 * garbage collector. In this case the value of the GCHandle will be
1742 * If @track_resurrection is TRUE the object will be tracked through
1743 * finalization and if the object is resurrected during the execution
1744 * of the finalizer, then the returned weakref will continue to hold
1745 * a reference to the object. If @track_resurrection is FALSE, then
1746 * the weak reference's target will become NULL as soon as the object
1747 * is passed on to the finalizer.
1749 * Returns: a handle that can be used to access the object from
1753 mono_gchandle_new_weakref (MonoObject *obj, gboolean track_resurrection)
1755 return alloc_handle (&gc_handles [track_resurrection? HANDLE_WEAK_TRACK: HANDLE_WEAK], obj, track_resurrection);
1759 * mono_gchandle_get_target:
1760 * @gchandle: a GCHandle's handle.
1762 * The handle was previously created by calling `mono_gchandle_new` or
1763 * `mono_gchandle_new_weakref`.
1765 * Returns: A pointer to the `MonoObject*` represented by the handle or
1766 * NULL for a collected object if using a weakref handle.
1769 mono_gchandle_get_target (guint32 gchandle)
1771 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1772 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1773 HandleData *handles = &gc_handles [type];
1774 MonoObject *obj = NULL;
1775 if (type >= HANDLE_TYPE_MAX)
1778 lock_handles (handles);
1779 if (slot < handles->size && slot_occupied (handles, slot)) {
1780 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1781 obj = mono_gc_weak_link_get (&handles->entries [slot]);
1783 obj = (MonoObject *)handles->entries [slot];
1786 /* print a warning? */
1788 unlock_handles (handles);
1789 /*g_print ("get target of entry %d of type %d: %p\n", slot, handles->type, obj);*/
1794 mono_gchandle_set_target (guint32 gchandle, MonoObject *obj)
1796 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1797 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1798 HandleData *handles = &gc_handles [type];
1799 MonoObject *old_obj = NULL;
1801 g_assert (type < HANDLE_TYPE_MAX);
1802 lock_handles (handles);
1803 if (slot < handles->size && slot_occupied (handles, slot)) {
1804 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1805 old_obj = (MonoObject *)handles->entries [slot];
1806 if (handles->entries [slot])
1807 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1809 mono_gc_weak_link_add (&handles->entries [slot], obj, handles->type == HANDLE_WEAK_TRACK);
1810 /*FIXME, what to use when obj == null?*/
1811 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
1813 handles->entries [slot] = obj;
1816 /* print a warning? */
1818 /*g_print ("changed entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
1819 unlock_handles (handles);
1823 mono_gc_is_null (void)
1829 * mono_gchandle_is_in_domain:
1830 * @gchandle: a GCHandle's handle.
1831 * @domain: An application domain.
1833 * Use this function to determine if the @gchandle points to an
1834 * object allocated in the specified @domain.
1836 * Returns: TRUE if the object wrapped by the @gchandle belongs to the specific @domain.
1839 mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain)
1841 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1842 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1843 HandleData *handles = &gc_handles [type];
1844 gboolean result = FALSE;
1846 if (type >= HANDLE_TYPE_MAX)
1849 lock_handles (handles);
1850 if (slot < handles->size && slot_occupied (handles, slot)) {
1851 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1852 result = domain->domain_id == handles->domain_ids [slot];
1855 obj = (MonoObject *)handles->entries [slot];
1859 result = domain == mono_object_domain (obj);
1862 /* print a warning? */
1864 unlock_handles (handles);
1869 * mono_gchandle_free:
1870 * @gchandle: a GCHandle's handle.
1872 * Frees the @gchandle handle. If there are no outstanding
1873 * references, the garbage collector can reclaim the memory of the
1877 mono_gchandle_free (guint32 gchandle)
1879 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1880 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1881 HandleData *handles = &gc_handles [type];
1882 if (type >= HANDLE_TYPE_MAX)
1885 lock_handles (handles);
1886 if (slot < handles->size && slot_occupied (handles, slot)) {
1887 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1888 if (handles->entries [slot])
1889 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1891 handles->entries [slot] = NULL;
1893 vacate_slot (handles, slot);
1895 /* print a warning? */
1897 #ifndef DISABLE_PERFCOUNTERS
1898 mono_perfcounters->gc_num_handles--;
1900 /*g_print ("freed entry %d of type %d\n", slot, handles->type);*/
1901 unlock_handles (handles);
1902 mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_DESTROYED, handles->type, gchandle, NULL);
1906 * mono_gchandle_free_domain:
1907 * @domain: domain that is unloading
1909 * Function used internally to cleanup any GC handle for objects belonging
1910 * to the specified domain during appdomain unload.
1913 mono_gchandle_free_domain (MonoDomain *domain)
1917 for (type = HANDLE_TYPE_MIN; type < HANDLE_PINNED; ++type) {
1919 HandleData *handles = &gc_handles [type];
1920 lock_handles (handles);
1921 for (slot = 0; slot < handles->size; ++slot) {
1922 if (!slot_occupied (handles, slot))
1924 if (MONO_GC_HANDLE_TYPE_IS_WEAK (type)) {
1925 if (domain->domain_id == handles->domain_ids [slot]) {
1926 vacate_slot (handles, slot);
1927 if (handles->entries [slot])
1928 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1931 if (handles->entries [slot] && mono_object_domain (handles->entries [slot]) == domain) {
1932 vacate_slot (handles, slot);
1933 handles->entries [slot] = NULL;
1937 unlock_handles (handles);
1943 // Quiet Visual Studio linker warning, LNK4221, in cases when this source file intentional ends up empty.
1944 void __mono_win32_boehm_gc_quiet_lnk4221(void) {}
1946 #endif /* no Boehm GC */