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>
36 #include <mono/utils/mono-compiler.h>
42 #define THREAD_LOCAL_ALLOC 1
43 #include "private/pthread_support.h"
45 #if defined(PLATFORM_MACOSX) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
46 void *pthread_get_stackaddr_np(pthread_t);
49 #define GC_NO_DESCRIPTOR ((gpointer)(0 | GC_DS_LENGTH))
50 /*Boehm max heap cannot be smaller than 16MB*/
51 #define MIN_BOEHM_MAX_HEAP_SIZE_IN_MB 16
52 #define MIN_BOEHM_MAX_HEAP_SIZE (MIN_BOEHM_MAX_HEAP_SIZE_IN_MB << 20)
54 static gboolean gc_initialized = FALSE;
55 static mono_mutex_t mono_gc_lock;
58 boehm_thread_register (MonoThreadInfo* info, void *baseptr);
60 boehm_thread_unregister (MonoThreadInfo *p);
62 boehm_thread_detach (MonoThreadInfo *p);
64 register_test_toggleref_callback (void);
66 #define BOEHM_GC_BIT_FINALIZER_AWARE 1
67 static MonoGCFinalizerCallbacks fin_callbacks;
71 static mono_mutex_t handle_section;
72 #define lock_handles(handles) mono_os_mutex_lock (&handle_section)
73 #define unlock_handles(handles) mono_os_mutex_unlock (&handle_section)
80 guint slot_hint : 24; /* starting slot for search in bitmap */
81 /* 2^16 appdomains should be enough for everyone (though I know I'll regret this in 20 years) */
82 /* we alloc this only for weak refs, since we can get the domain directly in the other cases */
86 #define EMPTY_HANDLE_DATA(type) {NULL, NULL, 0, (type), 0, NULL}
88 /* weak and weak-track arrays will be allocated in malloc memory
90 static HandleData gc_handles [] = {
91 EMPTY_HANDLE_DATA (HANDLE_WEAK),
92 EMPTY_HANDLE_DATA (HANDLE_WEAK_TRACK),
93 EMPTY_HANDLE_DATA (HANDLE_NORMAL),
94 EMPTY_HANDLE_DATA (HANDLE_PINNED)
98 mono_gc_warning (char *msg, GC_word arg)
100 mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_GC, msg, (unsigned long)arg);
103 static void on_gc_notification (GC_EventType event);
104 static void on_gc_heap_resize (size_t new_size);
107 mono_gc_base_init (void)
109 MonoThreadInfoCallbacks cb;
116 mono_counters_init ();
119 mono_w32handle_init ();
123 * Handle the case when we are called from a thread different from the main thread,
125 * FIXME: Move this to libgc where it belongs.
127 * we used to do this only when running on valgrind,
128 * but it happens also in other setups.
130 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) && !defined(__native_client__)
135 pthread_getattr_np (pthread_self (), &attr);
136 pthread_attr_getstack (&attr, &sstart, &size);
137 pthread_attr_destroy (&attr);
138 /*g_print ("stackbottom pth is: %p\n", (char*)sstart + size);*/
141 * The calculation above doesn't seem to work on ia64, also we need to set
142 * GC_register_stackbottom as well, but don't know how.
145 /* apparently with some linuxthreads implementations sstart can be NULL,
146 * fallback to the more imprecise method (bug# 78096).
149 GC_stackbottom = (char*)sstart + size;
152 gsize stack_bottom = (gsize)&dummy;
153 stack_bottom += 4095;
154 stack_bottom &= ~4095;
155 GC_stackbottom = (char*)stack_bottom;
159 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
160 GC_stackbottom = (char*)pthread_get_stackaddr_np (pthread_self ());
161 #elif defined(__OpenBSD__)
162 # include <pthread_np.h>
167 rslt = pthread_stackseg_np(pthread_self(), &ss);
168 g_assert (rslt == 0);
170 GC_stackbottom = (char*)ss.ss_sp;
172 #elif defined(__native_client__)
173 /* Do nothing, GC_stackbottom is set correctly in libgc */
177 gsize stack_bottom = (gsize)&dummy;
178 stack_bottom += 4095;
179 stack_bottom &= ~4095;
180 /*g_print ("stackbottom is: %p\n", (char*)stack_bottom);*/
181 GC_stackbottom = (char*)stack_bottom;
185 #if !defined(PLATFORM_ANDROID)
186 /* If GC_no_dls is set to true, GC_find_limit is not called. This causes a seg fault on Android. */
190 if ((env = g_getenv ("MONO_GC_DEBUG"))) {
191 char **opts = g_strsplit (env, ",", -1);
192 for (char **ptr = opts; ptr && *ptr; ptr ++) {
194 if (!strcmp (opt, "do-not-finalize")) {
195 mono_do_not_finalize = 1;
196 } else if (!strcmp (opt, "log-finalizers")) {
205 GC_set_warn_proc (mono_gc_warning);
206 GC_finalize_on_demand = 1;
207 GC_finalizer_notifier = mono_gc_finalize_notify;
209 GC_init_gcj_malloc (5, NULL);
210 GC_allow_register_threads ();
212 if ((env = g_getenv ("MONO_GC_PARAMS"))) {
213 char **ptr, **opts = g_strsplit (env, ",", -1);
214 for (ptr = opts; *ptr; ++ptr) {
216 if (g_str_has_prefix (opt, "max-heap-size=")) {
219 opt = strchr (opt, '=') + 1;
220 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &max_heap)) {
221 if (max_heap < MIN_BOEHM_MAX_HEAP_SIZE) {
222 fprintf (stderr, "max-heap-size must be at least %dMb.\n", MIN_BOEHM_MAX_HEAP_SIZE_IN_MB);
225 GC_set_max_heap_size (max_heap);
227 fprintf (stderr, "max-heap-size must be an integer.\n");
231 } else if (g_str_has_prefix (opt, "toggleref-test")) {
232 register_test_toggleref_callback ();
235 /* Could be a parameter for sgen */
237 fprintf (stderr, "MONO_GC_PARAMS must be a comma-delimited list of one or more of the following:\n");
238 fprintf (stderr, " max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
246 memset (&cb, 0, sizeof (cb));
247 cb.thread_register = boehm_thread_register;
248 cb.thread_unregister = boehm_thread_unregister;
249 cb.thread_detach = boehm_thread_detach;
250 cb.mono_method_is_critical = (gboolean (*)(void *))mono_runtime_is_critical_method;
252 mono_threads_init (&cb, sizeof (MonoThreadInfo));
253 mono_os_mutex_init (&mono_gc_lock);
254 mono_os_mutex_init_recursive (&handle_section);
256 mono_thread_info_attach (&dummy);
258 GC_set_on_collection_event (on_gc_notification);
259 GC_on_heap_resize = on_gc_heap_resize;
261 MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_NORMAL].entries, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
262 MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_PINNED].entries, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
264 gc_initialized = TRUE;
268 mono_gc_base_cleanup (void)
270 GC_finalizer_notifier = NULL;
275 * @generation: GC generation identifier
277 * Perform a garbage collection for the given generation, higher numbers
278 * mean usually older objects. Collecting a high-numbered generation
279 * implies collecting also the lower-numbered generations.
280 * The maximum value for @generation can be retrieved with a call to
281 * mono_gc_max_generation(), so this function is usually called as:
283 * mono_gc_collect (mono_gc_max_generation ());
286 mono_gc_collect (int generation)
288 #ifndef DISABLE_PERFCOUNTERS
289 mono_perfcounters->gc_induced++;
295 * mono_gc_max_generation:
297 * Get the maximum generation number used by the current garbage
298 * collector. The value will be 0 for the Boehm collector, 1 or more
299 * for the generational collectors.
301 * Returns: the maximum generation number.
304 mono_gc_max_generation (void)
310 * mono_gc_get_generation:
311 * @object: a managed object
313 * Get the garbage collector's generation that @object belongs to.
314 * Use this has a hint only.
316 * Returns: a garbage collector generation number
319 mono_gc_get_generation (MonoObject *object)
325 * mono_gc_collection_count:
326 * @generation: a GC generation number
328 * Get how many times a garbage collection has been performed
329 * for the given @generation number.
331 * Returns: the number of garbage collections
334 mono_gc_collection_count (int generation)
340 * mono_gc_add_memory_pressure:
341 * @value: amount of bytes
343 * Adjust the garbage collector's view of how many bytes of memory
344 * are indirectly referenced by managed objects (for example unmanaged
345 * memory holding image or other binary data).
346 * This is a hint only to the garbage collector algorithm.
347 * Note that negative amounts of @value will decrease the memory
351 mono_gc_add_memory_pressure (gint64 value)
356 * mono_gc_get_used_size:
358 * Get the approximate amount of memory used by managed objects.
360 * Returns: the amount of memory used in bytes
363 mono_gc_get_used_size (void)
365 return GC_get_heap_size () - GC_get_free_bytes ();
369 * mono_gc_get_heap_size:
371 * Get the amount of memory used by the garbage collector.
373 * Returns: the size of the heap in bytes
376 mono_gc_get_heap_size (void)
378 return GC_get_heap_size ();
382 mono_gc_is_gc_thread (void)
384 return GC_thread_is_registered ();
388 mono_gc_register_thread (void *baseptr)
390 return mono_thread_info_attach (baseptr) != NULL;
394 boehm_thread_register (MonoThreadInfo* info, void *baseptr)
396 struct GC_stack_base sb;
399 /* TODO: use GC_get_stack_base instead of baseptr. */
400 sb.mem_base = baseptr;
401 res = GC_register_my_thread (&sb);
402 if (res == GC_UNIMPLEMENTED)
403 return NULL; /* Cannot happen with GC v7+. */
405 info->handle_stack = mono_handle_stack_alloc ();
411 boehm_thread_unregister (MonoThreadInfo *p)
413 MonoNativeThreadId tid;
415 tid = mono_thread_info_get_tid (p);
417 if (p->runtime_thread)
418 mono_threads_add_joinable_thread ((gpointer)tid);
422 boehm_thread_detach (MonoThreadInfo *p)
424 if (mono_thread_internal_current_is_attached ())
425 mono_thread_detach_internal (mono_thread_internal_current ());
429 mono_object_is_alive (MonoObject* o)
431 return GC_is_marked ((ptr_t)o);
435 mono_gc_walk_heap (int flags, MonoGCReferences callback, void *data)
440 static gint64 gc_start_time;
443 on_gc_notification (GC_EventType event)
445 MonoGCEvent e = (MonoGCEvent)event;
448 case MONO_GC_EVENT_PRE_STOP_WORLD:
449 MONO_GC_WORLD_STOP_BEGIN ();
452 case MONO_GC_EVENT_POST_STOP_WORLD:
453 MONO_GC_WORLD_STOP_END ();
456 case MONO_GC_EVENT_PRE_START_WORLD:
457 MONO_GC_WORLD_RESTART_BEGIN (1);
460 case MONO_GC_EVENT_POST_START_WORLD:
461 MONO_GC_WORLD_RESTART_END (1);
464 case MONO_GC_EVENT_START:
466 #ifndef DISABLE_PERFCOUNTERS
467 if (mono_perfcounters)
468 mono_perfcounters->gc_collections0++;
470 gc_stats.major_gc_count ++;
471 gc_start_time = mono_100ns_ticks ();
474 case MONO_GC_EVENT_END:
476 #if defined(ENABLE_DTRACE) && defined(__sun__)
477 /* This works around a dtrace -G problem on Solaris.
478 Limit its actual use to when the probe is enabled. */
479 if (MONO_GC_END_ENABLED ())
483 #ifndef DISABLE_PERFCOUNTERS
484 if (mono_perfcounters) {
485 guint64 heap_size = GC_get_heap_size ();
486 guint64 used_size = heap_size - GC_get_free_bytes ();
487 mono_perfcounters->gc_total_bytes = used_size;
488 mono_perfcounters->gc_committed_bytes = heap_size;
489 mono_perfcounters->gc_reserved_bytes = heap_size;
490 mono_perfcounters->gc_gen0size = heap_size;
493 gc_stats.major_gc_time += mono_100ns_ticks () - gc_start_time;
494 mono_trace_message (MONO_TRACE_GC, "gc took %d usecs", (mono_100ns_ticks () - gc_start_time) / 10);
500 mono_profiler_gc_event (e, 0);
503 case MONO_GC_EVENT_PRE_STOP_WORLD:
504 mono_thread_info_suspend_lock ();
505 mono_profiler_gc_event (MONO_GC_EVENT_PRE_STOP_WORLD_LOCKED, 0);
507 case MONO_GC_EVENT_POST_START_WORLD:
508 mono_thread_info_suspend_unlock ();
509 mono_profiler_gc_event (MONO_GC_EVENT_POST_START_WORLD_UNLOCKED, 0);
518 on_gc_heap_resize (size_t new_size)
520 guint64 heap_size = GC_get_heap_size ();
521 #ifndef DISABLE_PERFCOUNTERS
522 if (mono_perfcounters) {
523 mono_perfcounters->gc_committed_bytes = heap_size;
524 mono_perfcounters->gc_reserved_bytes = heap_size;
525 mono_perfcounters->gc_gen0size = heap_size;
528 mono_profiler_gc_heap_resize (new_size);
532 mono_gc_register_root (char *start, size_t size, void *descr, MonoGCRootSource source, const char *msg)
534 /* for some strange reason, they want one extra byte on the end */
535 GC_add_roots (start, start + size + 1);
541 mono_gc_deregister_root (char* addr)
544 /* FIXME: libgc doesn't define this work win32 for some reason */
545 /* FIXME: No size info */
546 GC_remove_roots (addr, addr + sizeof (gpointer) + 1);
551 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
553 /* libgc requires that we use HIDE_POINTER... */
554 *link_addr = (void*)HIDE_POINTER (obj);
556 GC_REGISTER_LONG_LINK (link_addr, obj);
558 GC_GENERAL_REGISTER_DISAPPEARING_LINK (link_addr, obj);
562 mono_gc_weak_link_remove (void **link_addr, gboolean track)
565 GC_unregister_long_link (link_addr);
567 GC_unregister_disappearing_link (link_addr);
572 reveal_link (gpointer link_addr)
574 void **link_a = (void **)link_addr;
575 return REVEAL_POINTER (*link_a);
579 mono_gc_weak_link_get (void **link_addr)
581 MonoObject *obj = (MonoObject *)GC_call_with_alloc_lock (reveal_link, link_addr);
582 if (obj == (MonoObject *) -1)
588 mono_gc_make_descr_for_string (gsize *bitmap, int numbits)
590 return mono_gc_make_descr_from_bitmap (bitmap, numbits);
594 mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size)
596 return mono_gc_make_descr_from_bitmap (bitmap, numbits);
600 mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size)
602 /* libgc has no usable support for arrays... */
603 return GC_NO_DESCRIPTOR;
607 mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
609 /* It seems there are issues when the bitmap doesn't fit: play it safe */
611 return GC_NO_DESCRIPTOR;
613 return (gpointer)GC_make_descriptor ((GC_bitmap)bitmap, numbits);
617 mono_gc_make_root_descr_all_refs (int numbits)
623 mono_gc_alloc_fixed (size_t size, void *descr, MonoGCRootSource source, const char *msg)
625 /* To help track down typed allocation bugs */
629 if (count == atoi (g_getenv ("COUNT2")))
631 if (count > atoi (g_getenv ("COUNT2")))
632 return GC_MALLOC (size);
636 return GC_MALLOC_EXPLICITLY_TYPED (size, (GC_descr)descr);
638 return GC_MALLOC (size);
642 mono_gc_free_fixed (void* addr)
647 mono_gc_alloc_obj (MonoVTable *vtable, size_t size)
651 if (!vtable->klass->has_references) {
652 obj = (MonoObject *)GC_MALLOC_ATOMIC (size);
653 if (G_UNLIKELY (!obj))
656 obj->vtable = vtable;
657 obj->synchronisation = NULL;
659 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
660 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
661 obj = (MonoObject *)GC_GCJ_MALLOC (size, vtable);
662 if (G_UNLIKELY (!obj))
665 obj = (MonoObject *)GC_MALLOC (size);
666 if (G_UNLIKELY (!obj))
669 obj->vtable = vtable;
672 if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_ALLOCATIONS))
673 mono_profiler_allocation (obj);
679 mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length)
683 if (!vtable->klass->has_references) {
684 obj = (MonoArray *)GC_MALLOC_ATOMIC (size);
685 if (G_UNLIKELY (!obj))
688 obj->obj.vtable = vtable;
689 obj->obj.synchronisation = NULL;
691 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
692 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
693 obj = (MonoArray *)GC_GCJ_MALLOC (size, vtable);
694 if (G_UNLIKELY (!obj))
697 obj = (MonoArray *)GC_MALLOC (size);
698 if (G_UNLIKELY (!obj))
701 obj->obj.vtable = vtable;
704 obj->max_length = max_length;
706 if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_ALLOCATIONS))
707 mono_profiler_allocation (&obj->obj);
713 mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uintptr_t bounds_size)
717 if (!vtable->klass->has_references) {
718 obj = (MonoArray *)GC_MALLOC_ATOMIC (size);
719 if (G_UNLIKELY (!obj))
722 obj->obj.vtable = vtable;
723 obj->obj.synchronisation = NULL;
725 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
726 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
727 obj = (MonoArray *)GC_GCJ_MALLOC (size, vtable);
728 if (G_UNLIKELY (!obj))
731 obj = (MonoArray *)GC_MALLOC (size);
732 if (G_UNLIKELY (!obj))
735 obj->obj.vtable = vtable;
738 obj->max_length = max_length;
741 obj->bounds = (MonoArrayBounds *) ((char *) obj + size - bounds_size);
743 if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_ALLOCATIONS))
744 mono_profiler_allocation (&obj->obj);
750 mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len)
752 MonoString *obj = (MonoString *)GC_MALLOC_ATOMIC (size);
753 if (G_UNLIKELY (!obj))
756 obj->object.vtable = vtable;
757 obj->object.synchronisation = NULL;
759 obj->chars [len] = 0;
761 if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_ALLOCATIONS))
762 mono_profiler_allocation (&obj->object);
768 mono_gc_alloc_mature (MonoVTable *vtable, size_t size)
770 return mono_gc_alloc_obj (vtable, size);
774 mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size)
776 return mono_gc_alloc_obj (vtable, size);
780 mono_gc_invoke_finalizers (void)
782 /* There is a bug in GC_invoke_finalizer () in versions <= 6.2alpha4:
783 * the 'mem_freed' variable is not initialized when there are no
784 * objects to finalize, which leads to strange behavior later on.
785 * The check is necessary to work around that bug.
787 if (GC_should_invoke_finalizers ())
788 return GC_invoke_finalizers ();
793 mono_gc_pending_finalizers (void)
795 return GC_should_invoke_finalizers ();
799 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
801 *(void**)field_ptr = value;
805 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
807 *(void**)slot_ptr = value;
811 mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
813 mono_gc_memmove_aligned (dest_ptr, src_ptr, count * sizeof (gpointer));
817 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
819 *(void**)ptr = value;
823 mono_gc_wbarrier_generic_store_atomic (gpointer ptr, MonoObject *value)
825 InterlockedWritePointer ((volatile gpointer *)ptr, value);
829 mono_gc_wbarrier_generic_nostore (gpointer ptr)
834 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
836 mono_gc_memmove_atomic (dest, src, count * mono_class_value_size (klass, NULL));
840 mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
842 /* do not copy the sync state */
843 mono_gc_memmove_aligned ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
844 mono_object_class (obj)->instance_size - sizeof (MonoObject));
848 mono_gc_clear_domain (MonoDomain *domain)
853 mono_gc_suspend_finalizers (void)
858 mono_gc_get_suspend_signal (void)
860 return GC_get_suspend_signal ();
864 mono_gc_get_restart_signal (void)
866 return GC_get_thr_restart_signal ();
869 #if defined(USE_COMPILER_TLS) && defined(__linux__) && (defined(__i386__) || defined(__x86_64__))
870 extern __thread MONO_TLS_FAST void* GC_thread_tls;
871 #include "metadata-internals.h"
877 while (!(v & (1 << i)))
884 ATYPE_FREEPTR_FOR_BOX,
892 create_allocator (int atype, int tls_key, gboolean slowpath)
894 int index_var, bytes_var, my_fl_var, my_entry_var;
895 guint32 no_freelist_branch, not_small_enough_branch = 0;
896 guint32 size_overflow_branch = 0;
897 MonoMethodBuilder *mb;
899 MonoMethodSignature *csig;
900 const char *name = NULL;
903 if (atype == ATYPE_FREEPTR) {
904 name = slowpath ? "SlowAllocPtrfree" : "AllocPtrfree";
905 } else if (atype == ATYPE_FREEPTR_FOR_BOX) {
906 name = slowpath ? "SlowAllocPtrfreeBox" : "AllocPtrfreeBox";
907 } else if (atype == ATYPE_NORMAL) {
908 name = slowpath ? "SlowAlloc" : "Alloc";
909 } else if (atype == ATYPE_GCJ) {
910 name = slowpath ? "SlowAllocGcj" : "AllocGcj";
911 } else if (atype == ATYPE_STRING) {
912 name = slowpath ? "SlowAllocString" : "AllocString";
914 g_assert_not_reached ();
917 csig = mono_metadata_signature_alloc (mono_defaults.corlib, 2);
919 if (atype == ATYPE_STRING) {
920 csig->ret = &mono_defaults.string_class->byval_arg;
921 csig->params [0] = &mono_defaults.int_class->byval_arg;
922 csig->params [1] = &mono_defaults.int32_class->byval_arg;
924 csig->ret = &mono_defaults.object_class->byval_arg;
925 csig->params [0] = &mono_defaults.int_class->byval_arg;
926 csig->params [1] = &mono_defaults.int32_class->byval_arg;
929 mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_ALLOC);
932 goto always_slowpath;
934 bytes_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
935 if (atype == ATYPE_STRING) {
936 /* a string alloator method takes the args: (vtable, len) */
937 /* bytes = (offsetof (MonoString, chars) + ((len + 1) * 2)); */
938 mono_mb_emit_ldarg (mb, 1);
939 mono_mb_emit_icon (mb, 1);
940 mono_mb_emit_byte (mb, MONO_CEE_ADD);
941 mono_mb_emit_icon (mb, 1);
942 mono_mb_emit_byte (mb, MONO_CEE_SHL);
943 // sizeof (MonoString) might include padding
944 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, chars));
945 mono_mb_emit_byte (mb, MONO_CEE_ADD);
946 mono_mb_emit_stloc (mb, bytes_var);
948 mono_mb_emit_ldarg (mb, 1);
949 mono_mb_emit_stloc (mb, bytes_var);
952 /* this is needed for strings/arrays only as the other big types are never allocated with this method */
953 if (atype == ATYPE_STRING) {
955 /* if (!SMALL_ENOUGH (bytes)) jump slow_path;*/
956 mono_mb_emit_ldloc (mb, bytes_var);
957 mono_mb_emit_icon (mb, (NFREELISTS-1) * GRANULARITY);
958 not_small_enough_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_UN_S);
959 /* check for overflow */
960 mono_mb_emit_ldloc (mb, bytes_var);
961 mono_mb_emit_icon (mb, sizeof (MonoString));
962 size_overflow_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLE_UN_S);
965 /* int index = INDEX_FROM_BYTES(bytes); */
966 index_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
968 mono_mb_emit_ldloc (mb, bytes_var);
969 mono_mb_emit_icon (mb, GRANULARITY - 1);
970 mono_mb_emit_byte (mb, MONO_CEE_ADD);
971 mono_mb_emit_icon (mb, shift_amount (GRANULARITY));
972 mono_mb_emit_byte (mb, MONO_CEE_SHR_UN);
973 mono_mb_emit_icon (mb, shift_amount (sizeof (gpointer)));
974 mono_mb_emit_byte (mb, MONO_CEE_SHL);
975 /* index var is already adjusted into bytes */
976 mono_mb_emit_stloc (mb, index_var);
978 my_fl_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
979 my_entry_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
980 /* my_fl = ((GC_thread)tsd) -> ptrfree_freelists + index; */
981 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
982 mono_mb_emit_byte (mb, 0x0D); /* CEE_MONO_TLS */
983 mono_mb_emit_i4 (mb, tls_key);
984 if (atype == ATYPE_FREEPTR || atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING)
985 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
986 + G_STRUCT_OFFSET (struct thread_local_freelists,
988 else if (atype == ATYPE_NORMAL)
989 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
990 + G_STRUCT_OFFSET (struct thread_local_freelists,
992 else if (atype == ATYPE_GCJ)
993 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
994 + G_STRUCT_OFFSET (struct thread_local_freelists,
997 g_assert_not_reached ();
998 mono_mb_emit_byte (mb, MONO_CEE_ADD);
999 mono_mb_emit_ldloc (mb, index_var);
1000 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1001 mono_mb_emit_stloc (mb, my_fl_var);
1003 /* my_entry = *my_fl; */
1004 mono_mb_emit_ldloc (mb, my_fl_var);
1005 mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
1006 mono_mb_emit_stloc (mb, my_entry_var);
1008 /* if (EXPECT((word)my_entry >= HBLKSIZE, 1)) { */
1009 mono_mb_emit_ldloc (mb, my_entry_var);
1010 mono_mb_emit_icon (mb, HBLKSIZE);
1011 no_freelist_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
1013 /* ptr_t next = obj_link(my_entry); *my_fl = next; */
1014 mono_mb_emit_ldloc (mb, my_fl_var);
1015 mono_mb_emit_ldloc (mb, my_entry_var);
1016 mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
1017 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1019 /* set the vtable and clear the words in the object */
1020 mono_mb_emit_ldloc (mb, my_entry_var);
1021 mono_mb_emit_ldarg (mb, 0);
1022 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1024 if (atype == ATYPE_FREEPTR) {
1025 int start_var, end_var, start_loop;
1026 /* end = my_entry + bytes; start = my_entry + sizeof (gpointer);
1028 start_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1029 end_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1030 mono_mb_emit_ldloc (mb, my_entry_var);
1031 mono_mb_emit_ldloc (mb, bytes_var);
1032 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1033 mono_mb_emit_stloc (mb, end_var);
1034 mono_mb_emit_ldloc (mb, my_entry_var);
1035 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
1036 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1037 mono_mb_emit_stloc (mb, start_var);
1041 * } while (start < end);
1043 start_loop = mono_mb_get_label (mb);
1044 mono_mb_emit_ldloc (mb, start_var);
1045 mono_mb_emit_icon (mb, 0);
1046 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1047 mono_mb_emit_ldloc (mb, start_var);
1048 mono_mb_emit_icon (mb, sizeof (gpointer));
1049 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1050 mono_mb_emit_stloc (mb, start_var);
1052 mono_mb_emit_ldloc (mb, start_var);
1053 mono_mb_emit_ldloc (mb, end_var);
1054 mono_mb_emit_byte (mb, MONO_CEE_BLT_UN_S);
1055 mono_mb_emit_byte (mb, start_loop - (mono_mb_get_label (mb) + 1));
1056 } else if (atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING) {
1057 /* need to clear just the sync pointer */
1058 mono_mb_emit_ldloc (mb, my_entry_var);
1059 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
1060 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1061 mono_mb_emit_icon (mb, 0);
1062 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1065 if (atype == ATYPE_STRING) {
1066 /* need to set length and clear the last char */
1067 /* s->length = len; */
1068 mono_mb_emit_ldloc (mb, my_entry_var);
1069 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, length));
1070 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1071 mono_mb_emit_ldarg (mb, 1);
1072 mono_mb_emit_byte (mb, MONO_CEE_STIND_I4);
1073 /* s->chars [len] = 0; */
1074 mono_mb_emit_ldloc (mb, my_entry_var);
1075 mono_mb_emit_ldloc (mb, bytes_var);
1076 mono_mb_emit_icon (mb, 2);
1077 mono_mb_emit_byte (mb, MONO_CEE_SUB);
1078 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1079 mono_mb_emit_icon (mb, 0);
1080 mono_mb_emit_byte (mb, MONO_CEE_STIND_I2);
1083 /* return my_entry; */
1084 mono_mb_emit_ldloc (mb, my_entry_var);
1085 mono_mb_emit_byte (mb, MONO_CEE_RET);
1087 mono_mb_patch_short_branch (mb, no_freelist_branch);
1088 if (not_small_enough_branch > 0)
1089 mono_mb_patch_short_branch (mb, not_small_enough_branch);
1090 if (size_overflow_branch > 0)
1091 mono_mb_patch_short_branch (mb, size_overflow_branch);
1093 /* the slow path: we just call back into the runtime */
1095 if (atype == ATYPE_STRING) {
1096 mono_mb_emit_ldarg (mb, 1);
1097 mono_mb_emit_icall (mb, ves_icall_string_alloc);
1099 mono_mb_emit_ldarg (mb, 0);
1100 mono_mb_emit_icall (mb, ves_icall_object_new_specific);
1103 mono_mb_emit_byte (mb, MONO_CEE_RET);
1105 info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE);
1106 info->d.alloc.gc_name = "boehm";
1107 info->d.alloc.alloc_type = atype;
1108 mb->init_locals = FALSE;
1110 res = mono_mb_create (mb, csig, 8, info);
1116 static MonoMethod* alloc_method_cache [ATYPE_NUM];
1117 static MonoMethod* slowpath_alloc_method_cache [ATYPE_NUM];
1120 mono_gc_is_critical_method (MonoMethod *method)
1124 for (i = 0; i < ATYPE_NUM; ++i)
1125 if (method == alloc_method_cache [i] || method == slowpath_alloc_method_cache [i])
1132 * If possible, generate a managed method that can quickly allocate objects in class
1133 * @klass. The method will typically have an thread-local inline allocation sequence.
1134 * The signature of the called method is:
1135 * object allocate (MonoVTable *vtable)
1136 * Some of the logic here is similar to mono_class_get_allocation_ftn () i object.c,
1138 * The thread local alloc logic is taken from libgc/pthread_support.c.
1142 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size)
1146 MONO_THREAD_VAR_OFFSET (GC_thread_tls, offset);
1148 /*g_print ("thread tls: %d\n", offset);*/
1151 if (!SMALL_ENOUGH (klass->instance_size))
1153 if (mono_class_has_finalizer (klass) || mono_class_is_marshalbyref (klass))
1155 if (mono_profiler_get_events () & (MONO_PROFILE_ALLOCATIONS | MONO_PROFILE_STATISTICAL))
1159 if (mono_class_is_open_constructed_type (&klass->byval_arg))
1161 if (klass->byval_arg.type == MONO_TYPE_STRING) {
1162 atype = ATYPE_STRING;
1163 } else if (!known_instance_size) {
1165 } else if (!klass->has_references) {
1167 atype = ATYPE_FREEPTR_FOR_BOX;
1169 atype = ATYPE_FREEPTR;
1173 * disabled because we currently do a runtime choice anyway, to
1174 * deal with multiple appdomains.
1175 if (vtable->gc_descr != GC_NO_DESCRIPTOR)
1178 atype = ATYPE_NORMAL;
1181 return mono_gc_get_managed_allocator_by_type (atype, MANAGED_ALLOCATOR_REGULAR);
1185 mono_gc_get_managed_array_allocator (MonoClass *klass)
1191 * mono_gc_get_managed_allocator_by_type:
1193 * Return a managed allocator method corresponding to allocator type ATYPE.
1196 mono_gc_get_managed_allocator_by_type (int atype, ManagedAllocatorVariant variant)
1200 gboolean slowpath = variant != MANAGED_ALLOCATOR_REGULAR;
1201 MonoMethod **cache = slowpath ? slowpath_alloc_method_cache : alloc_method_cache;
1202 MONO_THREAD_VAR_OFFSET (GC_thread_tls, offset);
1204 mono_tls_key_set_offset (TLS_KEY_BOEHM_GC_THREAD, offset);
1206 res = cache [atype];
1210 res = create_allocator (atype, TLS_KEY_BOEHM_GC_THREAD, slowpath);
1211 mono_os_mutex_lock (&mono_gc_lock);
1212 if (cache [atype]) {
1213 mono_free_method (res);
1214 res = cache [atype];
1216 mono_memory_barrier ();
1217 cache [atype] = res;
1219 mono_os_mutex_unlock (&mono_gc_lock);
1224 mono_gc_get_managed_allocator_types (void)
1230 mono_gc_get_write_barrier (void)
1232 g_assert_not_reached ();
1239 mono_gc_is_critical_method (MonoMethod *method)
1245 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size)
1251 mono_gc_get_managed_array_allocator (MonoClass *klass)
1257 mono_gc_get_managed_allocator_by_type (int atype, ManagedAllocatorVariant variant)
1263 mono_gc_get_managed_allocator_types (void)
1269 mono_gc_get_write_barrier (void)
1271 g_assert_not_reached ();
1278 mono_gc_get_specific_write_barrier (gboolean is_concurrent)
1280 g_assert_not_reached ();
1285 mono_gc_get_aligned_size_for_allocator (int size)
1291 mono_gc_get_gc_name (void)
1297 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
1299 return GC_call_with_alloc_lock (func, data);
1303 mono_gc_get_description (void)
1305 return g_strdup (DEFAULT_GC_NAME);
1309 mono_gc_set_desktop_mode (void)
1315 mono_gc_is_moving (void)
1321 mono_gc_is_disabled (void)
1323 if (GC_dont_gc || g_getenv ("GC_DONT_GC"))
1330 mono_gc_wbarrier_value_copy_bitmap (gpointer _dest, gpointer _src, int size, unsigned bitmap)
1332 g_assert_not_reached ();
1337 mono_gc_get_card_table (int *shift_bits, gpointer *card_mask)
1339 g_assert_not_reached ();
1344 mono_gc_card_table_nursery_check (void)
1346 g_assert_not_reached ();
1351 mono_gc_get_nursery (int *shift_bits, size_t *size)
1357 mono_gc_precise_stack_mark_enabled (void)
1363 mono_gc_get_logfile (void)
1369 mono_gc_params_set (const char* options)
1374 mono_gc_debug_set (const char* options)
1379 mono_gc_conservatively_scan_area (void *start, void *end)
1381 g_assert_not_reached ();
1385 mono_gc_scan_object (void *obj, void *gc_data)
1387 g_assert_not_reached ();
1392 mono_gc_get_bitmap_for_descr (void *descr, int *numbits)
1394 g_assert_not_reached ();
1399 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
1404 mono_gc_set_stack_end (void *stack_end)
1408 void mono_gc_set_skip_thread (gboolean value)
1413 mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
1418 /* This assertion is not valid when GC_DEBUG is defined */
1419 g_assert (GC_base (obj) == (char*)obj - offset);
1422 GC_REGISTER_FINALIZER_NO_ORDER ((char*)obj - offset, (GC_finalization_proc)user_data, GUINT_TO_POINTER (offset), NULL, NULL);
1427 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
1429 /* it is being replaced by GC_pthread_create on some
1430 * platforms, see libgc/include/gc_pthread_redirects.h */
1431 return pthread_create (new_thread, attr, start_routine, arg);
1436 BOOL APIENTRY mono_gc_dllmain (HMODULE module_handle, DWORD reason, LPVOID reserved)
1438 return GC_DllMain (module_handle, reason, reserved);
1443 mono_gc_get_vtable_bits (MonoClass *klass)
1445 if (fin_callbacks.is_class_finalization_aware) {
1446 if (fin_callbacks.is_class_finalization_aware (klass))
1447 return BOEHM_GC_BIT_FINALIZER_AWARE;
1453 * mono_gc_register_altstack:
1455 * Register the dimensions of the normal stack and altstack with the collector.
1456 * Currently, STACK/STACK_SIZE is only used when the thread is suspended while it is on an altstack.
1459 mono_gc_register_altstack (gpointer stack, gint32 stack_size, gpointer altstack, gint32 altstack_size)
1461 GC_register_altstack (stack, stack_size, altstack, altstack_size);
1465 mono_gc_get_los_limit (void)
1471 mono_gc_set_string_length (MonoString *str, gint32 new_length)
1473 mono_unichar2 *new_end = str->chars + new_length;
1475 /* zero the discarded string. This null-delimits the string and allows
1476 * the space to be reclaimed by SGen. */
1478 memset (new_end, 0, (str->length - new_length + 1) * sizeof (mono_unichar2));
1479 str->length = new_length;
1483 mono_gc_user_markers_supported (void)
1489 mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
1491 g_assert_not_reached ();
1495 /* Toggleref support */
1498 mono_gc_toggleref_add (MonoObject *object, mono_bool strong_ref)
1500 if (GC_toggleref_add ((GC_PTR)object, (int)strong_ref) != GC_SUCCESS)
1501 g_error ("GC_toggleref_add failed\n");
1505 mono_gc_toggleref_register_callback (MonoToggleRefStatus (*proccess_toggleref) (MonoObject *obj))
1507 GC_set_toggleref_func ((GC_ToggleRefStatus (*) (GC_PTR obj)) proccess_toggleref);
1510 /* Test support code */
1512 static MonoToggleRefStatus
1513 test_toggleref_callback (MonoObject *obj)
1515 static MonoClassField *mono_toggleref_test_field;
1516 MonoToggleRefStatus status = MONO_TOGGLE_REF_DROP;
1518 if (!mono_toggleref_test_field) {
1519 mono_toggleref_test_field = mono_class_get_field_from_name (mono_object_get_class (obj), "__test");
1520 g_assert (mono_toggleref_test_field);
1523 mono_field_get_value (obj, mono_toggleref_test_field, &status);
1524 printf ("toggleref-cb obj %d\n", status);
1529 register_test_toggleref_callback (void)
1531 mono_gc_toggleref_register_callback (test_toggleref_callback);
1535 is_finalization_aware (MonoObject *obj)
1537 MonoVTable *vt = obj->vtable;
1538 return (vt->gc_bits & BOEHM_GC_BIT_FINALIZER_AWARE) == BOEHM_GC_BIT_FINALIZER_AWARE;
1542 fin_notifier (MonoObject *obj)
1544 if (is_finalization_aware (obj))
1545 fin_callbacks.object_queued_for_finalization (obj);
1549 mono_gc_register_finalizer_callbacks (MonoGCFinalizerCallbacks *callbacks)
1551 if (callbacks->version != MONO_GC_FINALIZER_EXTENSION_VERSION)
1552 g_error ("Invalid finalizer callback version. Expected %d but got %d\n", MONO_GC_FINALIZER_EXTENSION_VERSION, callbacks->version);
1554 fin_callbacks = *callbacks;
1556 GC_set_await_finalize_proc ((void (*) (GC_PTR))fin_notifier);
1559 #define BITMAP_SIZE (sizeof (*((HandleData *)NULL)->bitmap) * CHAR_BIT)
1561 static inline gboolean
1562 slot_occupied (HandleData *handles, guint slot) {
1563 return handles->bitmap [slot / BITMAP_SIZE] & (1 << (slot % BITMAP_SIZE));
1567 vacate_slot (HandleData *handles, guint slot) {
1568 handles->bitmap [slot / BITMAP_SIZE] &= ~(1 << (slot % BITMAP_SIZE));
1572 occupy_slot (HandleData *handles, guint slot) {
1573 handles->bitmap [slot / BITMAP_SIZE] |= 1 << (slot % BITMAP_SIZE);
1577 find_first_unset (guint32 bitmap)
1580 for (i = 0; i < 32; ++i) {
1581 if (!(bitmap & (1 << i)))
1588 handle_data_alloc_entries (HandleData *handles)
1591 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1592 handles->entries = (void **)g_malloc0 (sizeof (*handles->entries) * handles->size);
1593 handles->domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * handles->size);
1595 handles->entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * handles->size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
1597 handles->bitmap = (guint32 *)g_malloc0 (handles->size / CHAR_BIT);
1601 handle_data_next_unset (HandleData *handles)
1604 for (slot = handles->slot_hint; slot < handles->size / BITMAP_SIZE; ++slot) {
1605 if (handles->bitmap [slot] == 0xffffffff)
1607 handles->slot_hint = slot;
1608 return find_first_unset (handles->bitmap [slot]);
1614 handle_data_first_unset (HandleData *handles)
1617 for (slot = 0; slot < handles->slot_hint; ++slot) {
1618 if (handles->bitmap [slot] == 0xffffffff)
1620 handles->slot_hint = slot;
1621 return find_first_unset (handles->bitmap [slot]);
1626 /* Returns the index of the current slot in the bitmap. */
1628 handle_data_grow (HandleData *handles, gboolean track)
1630 guint32 *new_bitmap;
1631 guint32 new_size = handles->size * 2; /* always double: we memset to 0 based on this below */
1633 /* resize and copy the bitmap */
1634 new_bitmap = (guint32 *)g_malloc0 (new_size / CHAR_BIT);
1635 memcpy (new_bitmap, handles->bitmap, handles->size / CHAR_BIT);
1636 g_free (handles->bitmap);
1637 handles->bitmap = new_bitmap;
1639 /* resize and copy the entries */
1640 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1642 guint16 *domain_ids;
1644 domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * new_size);
1645 entries = (void **)g_malloc0 (sizeof (*handles->entries) * new_size);
1646 memcpy (domain_ids, handles->domain_ids, sizeof (*handles->domain_ids) * handles->size);
1647 for (i = 0; i < handles->size; ++i) {
1648 MonoObject *obj = mono_gc_weak_link_get (&(handles->entries [i]));
1650 mono_gc_weak_link_add (&(entries [i]), obj, track);
1651 mono_gc_weak_link_remove (&(handles->entries [i]), track);
1653 g_assert (!handles->entries [i]);
1656 g_free (handles->entries);
1657 g_free (handles->domain_ids);
1658 handles->entries = entries;
1659 handles->domain_ids = domain_ids;
1662 entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * new_size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
1663 mono_gc_memmove_aligned (entries, handles->entries, sizeof (*handles->entries) * handles->size);
1664 mono_gc_free_fixed (handles->entries);
1665 handles->entries = entries;
1667 handles->slot_hint = handles->size / BITMAP_SIZE;
1668 handles->size = new_size;
1672 alloc_handle (HandleData *handles, MonoObject *obj, gboolean track)
1676 lock_handles (handles);
1678 handle_data_alloc_entries (handles);
1679 i = handle_data_next_unset (handles);
1680 if (i == -1 && handles->slot_hint != 0)
1681 i = handle_data_first_unset (handles);
1683 handle_data_grow (handles, track);
1686 slot = handles->slot_hint * BITMAP_SIZE + i;
1687 occupy_slot (handles, slot);
1688 handles->entries [slot] = NULL;
1689 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1690 /*FIXME, what to use when obj == null?*/
1691 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
1693 mono_gc_weak_link_add (&(handles->entries [slot]), obj, track);
1695 handles->entries [slot] = obj;
1698 #ifndef DISABLE_PERFCOUNTERS
1699 mono_perfcounters->gc_num_handles++;
1701 unlock_handles (handles);
1702 res = MONO_GC_HANDLE (slot, handles->type);
1703 mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_CREATED, handles->type, res, obj);
1708 * mono_gchandle_new:
1709 * @obj: managed object to get a handle for
1710 * @pinned: whether the object should be pinned
1712 * This returns a handle that wraps the object, this is used to keep a
1713 * reference to a managed object from the unmanaged world and preventing the
1714 * object from being disposed.
1716 * If @pinned is false the address of the object can not be obtained, if it is
1717 * true the address of the object can be obtained. This will also pin the
1718 * object so it will not be possible by a moving garbage collector to move the
1721 * Returns: a handle that can be used to access the object from
1725 mono_gchandle_new (MonoObject *obj, gboolean pinned)
1727 return alloc_handle (&gc_handles [pinned? HANDLE_PINNED: HANDLE_NORMAL], obj, FALSE);
1731 * mono_gchandle_new_weakref:
1732 * @obj: managed object to get a handle for
1733 * @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.
1735 * This returns a weak handle that wraps the object, this is used to
1736 * keep a reference to a managed object from the unmanaged world.
1737 * Unlike the mono_gchandle_new the object can be reclaimed by the
1738 * garbage collector. In this case the value of the GCHandle will be
1741 * If @track_resurrection is TRUE the object will be tracked through
1742 * finalization and if the object is resurrected during the execution
1743 * of the finalizer, then the returned weakref will continue to hold
1744 * a reference to the object. If @track_resurrection is FALSE, then
1745 * the weak reference's target will become NULL as soon as the object
1746 * is passed on to the finalizer.
1748 * Returns: a handle that can be used to access the object from
1752 mono_gchandle_new_weakref (MonoObject *obj, gboolean track_resurrection)
1754 return alloc_handle (&gc_handles [track_resurrection? HANDLE_WEAK_TRACK: HANDLE_WEAK], obj, track_resurrection);
1758 * mono_gchandle_get_target:
1759 * @gchandle: a GCHandle's handle.
1761 * The handle was previously created by calling `mono_gchandle_new` or
1762 * `mono_gchandle_new_weakref`.
1764 * Returns: A pointer to the `MonoObject*` represented by the handle or
1765 * NULL for a collected object if using a weakref handle.
1768 mono_gchandle_get_target (guint32 gchandle)
1770 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1771 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1772 HandleData *handles = &gc_handles [type];
1773 MonoObject *obj = NULL;
1774 if (type >= HANDLE_TYPE_MAX)
1777 lock_handles (handles);
1778 if (slot < handles->size && slot_occupied (handles, slot)) {
1779 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1780 obj = mono_gc_weak_link_get (&handles->entries [slot]);
1782 obj = (MonoObject *)handles->entries [slot];
1785 /* print a warning? */
1787 unlock_handles (handles);
1788 /*g_print ("get target of entry %d of type %d: %p\n", slot, handles->type, obj);*/
1793 mono_gchandle_set_target (guint32 gchandle, MonoObject *obj)
1795 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1796 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1797 HandleData *handles = &gc_handles [type];
1798 MonoObject *old_obj = NULL;
1800 g_assert (type < HANDLE_TYPE_MAX);
1801 lock_handles (handles);
1802 if (slot < handles->size && slot_occupied (handles, slot)) {
1803 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1804 old_obj = (MonoObject *)handles->entries [slot];
1805 if (handles->entries [slot])
1806 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1808 mono_gc_weak_link_add (&handles->entries [slot], obj, handles->type == HANDLE_WEAK_TRACK);
1809 /*FIXME, what to use when obj == null?*/
1810 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
1812 handles->entries [slot] = obj;
1815 /* print a warning? */
1817 /*g_print ("changed entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
1818 unlock_handles (handles);
1822 mono_gc_is_null (void)
1828 * mono_gchandle_is_in_domain:
1829 * @gchandle: a GCHandle's handle.
1830 * @domain: An application domain.
1832 * Use this function to determine if the @gchandle points to an
1833 * object allocated in the specified @domain.
1835 * Returns: TRUE if the object wrapped by the @gchandle belongs to the specific @domain.
1838 mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain)
1840 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1841 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1842 HandleData *handles = &gc_handles [type];
1843 gboolean result = FALSE;
1845 if (type >= HANDLE_TYPE_MAX)
1848 lock_handles (handles);
1849 if (slot < handles->size && slot_occupied (handles, slot)) {
1850 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1851 result = domain->domain_id == handles->domain_ids [slot];
1854 obj = (MonoObject *)handles->entries [slot];
1858 result = domain == mono_object_domain (obj);
1861 /* print a warning? */
1863 unlock_handles (handles);
1868 * mono_gchandle_free:
1869 * @gchandle: a GCHandle's handle.
1871 * Frees the @gchandle handle. If there are no outstanding
1872 * references, the garbage collector can reclaim the memory of the
1876 mono_gchandle_free (guint32 gchandle)
1878 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1879 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1880 HandleData *handles = &gc_handles [type];
1881 if (type >= HANDLE_TYPE_MAX)
1884 lock_handles (handles);
1885 if (slot < handles->size && slot_occupied (handles, slot)) {
1886 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1887 if (handles->entries [slot])
1888 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1890 handles->entries [slot] = NULL;
1892 vacate_slot (handles, slot);
1894 /* print a warning? */
1896 #ifndef DISABLE_PERFCOUNTERS
1897 mono_perfcounters->gc_num_handles--;
1899 /*g_print ("freed entry %d of type %d\n", slot, handles->type);*/
1900 unlock_handles (handles);
1901 mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_DESTROYED, handles->type, gchandle, NULL);
1905 * mono_gchandle_free_domain:
1906 * @domain: domain that is unloading
1908 * Function used internally to cleanup any GC handle for objects belonging
1909 * to the specified domain during appdomain unload.
1912 mono_gchandle_free_domain (MonoDomain *domain)
1916 for (type = HANDLE_TYPE_MIN; type < HANDLE_PINNED; ++type) {
1918 HandleData *handles = &gc_handles [type];
1919 lock_handles (handles);
1920 for (slot = 0; slot < handles->size; ++slot) {
1921 if (!slot_occupied (handles, slot))
1923 if (MONO_GC_HANDLE_TYPE_IS_WEAK (type)) {
1924 if (domain->domain_id == handles->domain_ids [slot]) {
1925 vacate_slot (handles, slot);
1926 if (handles->entries [slot])
1927 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1930 if (handles->entries [slot] && mono_object_domain (handles->entries [slot]) == domain) {
1931 vacate_slot (handles, slot);
1932 handles->entries [slot] = NULL;
1936 unlock_handles (handles);
1942 MONO_EMPTY_SOURCE_FILE (boehm_gc);
1943 #endif /* no Boehm GC */