3 * GC implementation using either the installed or included Boehm GC.
5 * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
6 * Copyright 2004-2011 Novell, Inc (http://www.novell.com)
7 * Copyright 2011-2012 Xamarin, Inc (http://www.xamarin.com)
8 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
15 #define GC_I_HIDE_POINTERS
16 #include <mono/metadata/gc-internals.h>
17 #include <mono/metadata/mono-gc.h>
18 #include <mono/metadata/profiler-private.h>
19 #include <mono/metadata/class-internals.h>
20 #include <mono/metadata/method-builder.h>
21 #include <mono/metadata/opcodes.h>
22 #include <mono/metadata/domain-internals.h>
23 #include <mono/metadata/metadata-internals.h>
24 #include <mono/metadata/marshal.h>
25 #include <mono/metadata/runtime.h>
26 #include <mono/metadata/handle.h>
27 #include <mono/metadata/sgen-toggleref.h>
28 #include <mono/metadata/w32handle.h>
29 #include <mono/utils/atomic.h>
30 #include <mono/utils/mono-logger-internals.h>
31 #include <mono/utils/mono-memory-model.h>
32 #include <mono/utils/mono-time.h>
33 #include <mono/utils/mono-threads.h>
34 #include <mono/utils/dtrace.h>
35 #include <mono/utils/gc_wrapper.h>
36 #include <mono/utils/mono-os-mutex.h>
37 #include <mono/utils/mono-counters.h>
38 #include <mono/utils/mono-compiler.h>
39 #include <mono/utils/unlocked.h>
45 #define THREAD_LOCAL_ALLOC 1
46 #include "private/pthread_support.h"
48 #if defined(HOST_DARWIN) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
49 void *pthread_get_stackaddr_np(pthread_t);
52 #define GC_NO_DESCRIPTOR ((gpointer)(0 | GC_DS_LENGTH))
53 /*Boehm max heap cannot be smaller than 16MB*/
54 #define MIN_BOEHM_MAX_HEAP_SIZE_IN_MB 16
55 #define MIN_BOEHM_MAX_HEAP_SIZE (MIN_BOEHM_MAX_HEAP_SIZE_IN_MB << 20)
57 static gboolean gc_initialized = FALSE;
58 static mono_mutex_t mono_gc_lock;
60 typedef void (*GC_push_other_roots_proc)(void);
62 static GC_push_other_roots_proc default_push_other_roots;
63 static GHashTable *roots;
66 mono_push_other_roots(void);
69 register_test_toggleref_callback (void);
71 #define BOEHM_GC_BIT_FINALIZER_AWARE 1
72 static MonoGCFinalizerCallbacks fin_callbacks;
76 static mono_mutex_t handle_section;
77 #define lock_handles(handles) mono_os_mutex_lock (&handle_section)
78 #define unlock_handles(handles) mono_os_mutex_unlock (&handle_section)
85 guint slot_hint : 24; /* starting slot for search in bitmap */
86 /* 2^16 appdomains should be enough for everyone (though I know I'll regret this in 20 years) */
87 /* we alloc this only for weak refs, since we can get the domain directly in the other cases */
91 #define EMPTY_HANDLE_DATA(type) {NULL, NULL, 0, (type), 0, NULL}
93 /* weak and weak-track arrays will be allocated in malloc memory
95 static HandleData gc_handles [] = {
96 EMPTY_HANDLE_DATA (HANDLE_WEAK),
97 EMPTY_HANDLE_DATA (HANDLE_WEAK_TRACK),
98 EMPTY_HANDLE_DATA (HANDLE_NORMAL),
99 EMPTY_HANDLE_DATA (HANDLE_PINNED)
103 mono_gc_warning (char *msg, GC_word arg)
105 mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_GC, msg, (unsigned long)arg);
108 static void on_gc_notification (GC_EventType event);
109 static void on_gc_heap_resize (size_t new_size);
112 mono_gc_base_init (void)
119 mono_counters_init ();
122 mono_w32handle_init ();
126 * Handle the case when we are called from a thread different from the main thread,
128 * FIXME: Move this to libgc where it belongs.
130 * we used to do this only when running on valgrind,
131 * but it happens also in other setups.
133 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
138 pthread_getattr_np (pthread_self (), &attr);
139 pthread_attr_getstack (&attr, &sstart, &size);
140 pthread_attr_destroy (&attr);
141 /*g_print ("stackbottom pth is: %p\n", (char*)sstart + size);*/
142 /* apparently with some linuxthreads implementations sstart can be NULL,
143 * fallback to the more imprecise method (bug# 78096).
146 GC_stackbottom = (char*)sstart + size;
149 gsize stack_bottom = (gsize)&dummy;
150 stack_bottom += 4095;
151 stack_bottom &= ~4095;
152 GC_stackbottom = (char*)stack_bottom;
155 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
156 GC_stackbottom = (char*)pthread_get_stackaddr_np (pthread_self ());
157 #elif defined(__OpenBSD__)
158 # include <pthread_np.h>
163 rslt = pthread_stackseg_np(pthread_self(), &ss);
164 g_assert (rslt == 0);
166 GC_stackbottom = (char*)ss.ss_sp;
171 gsize stack_bottom = (gsize)&dummy;
172 stack_bottom += 4095;
173 stack_bottom &= ~4095;
174 /*g_print ("stackbottom is: %p\n", (char*)stack_bottom);*/
175 GC_stackbottom = (char*)stack_bottom;
179 roots = g_hash_table_new (NULL, NULL);
180 default_push_other_roots = GC_push_other_roots;
181 GC_push_other_roots = mono_push_other_roots;
183 #if !defined(HOST_ANDROID)
184 /* If GC_no_dls is set to true, GC_find_limit is not called. This causes a seg fault on Android. */
188 if ((env = g_getenv ("MONO_GC_DEBUG"))) {
189 char **opts = g_strsplit (env, ",", -1);
190 for (char **ptr = opts; ptr && *ptr; ptr ++) {
192 if (!strcmp (opt, "do-not-finalize")) {
193 mono_do_not_finalize = 1;
194 } else if (!strcmp (opt, "log-finalizers")) {
204 GC_set_warn_proc (mono_gc_warning);
205 GC_finalize_on_demand = 1;
206 GC_finalizer_notifier = mono_gc_finalize_notify;
208 GC_init_gcj_malloc (5, NULL);
209 GC_allow_register_threads ();
211 if ((env = g_getenv ("MONO_GC_PARAMS"))) {
212 char **ptr, **opts = g_strsplit (env, ",", -1);
213 for (ptr = opts; *ptr; ++ptr) {
215 if (g_str_has_prefix (opt, "max-heap-size=")) {
218 opt = strchr (opt, '=') + 1;
219 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &max_heap)) {
220 if (max_heap < MIN_BOEHM_MAX_HEAP_SIZE) {
221 fprintf (stderr, "max-heap-size must be at least %dMb.\n", MIN_BOEHM_MAX_HEAP_SIZE_IN_MB);
224 GC_set_max_heap_size (max_heap);
226 fprintf (stderr, "max-heap-size must be an integer.\n");
230 } else if (g_str_has_prefix (opt, "toggleref-test")) {
231 register_test_toggleref_callback ();
234 /* Could be a parameter for sgen */
236 fprintf (stderr, "MONO_GC_PARAMS must be a comma-delimited list of one or more of the following:\n");
237 fprintf (stderr, " max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
246 mono_thread_callbacks_init ();
247 mono_thread_info_init (sizeof (MonoThreadInfo));
248 mono_os_mutex_init (&mono_gc_lock);
249 mono_os_mutex_init_recursive (&handle_section);
251 mono_thread_info_attach ();
253 GC_set_on_collection_event (on_gc_notification);
254 GC_on_heap_resize = on_gc_heap_resize;
256 gc_initialized = TRUE;
260 mono_gc_base_cleanup (void)
262 GC_finalizer_notifier = NULL;
267 * \param generation GC generation identifier
269 * Perform a garbage collection for the given generation, higher numbers
270 * mean usually older objects. Collecting a high-numbered generation
271 * implies collecting also the lower-numbered generations.
272 * The maximum value for \p generation can be retrieved with a call to
273 * \c mono_gc_max_generation, so this function is usually called as:
275 * <code>mono_gc_collect (mono_gc_max_generation ());</code>
278 mono_gc_collect (int generation)
280 #ifndef DISABLE_PERFCOUNTERS
281 InterlockedIncrement (&mono_perfcounters->gc_induced);
287 * mono_gc_max_generation:
289 * Get the maximum generation number used by the current garbage
290 * collector. The value will be 0 for the Boehm collector, 1 or more
291 * for the generational collectors.
293 * Returns: the maximum generation number.
296 mono_gc_max_generation (void)
302 * mono_gc_get_generation:
303 * \param object a managed object
305 * Get the garbage collector's generation that \p object belongs to.
306 * Use this has a hint only.
308 * \returns a garbage collector generation number
311 mono_gc_get_generation (MonoObject *object)
317 * mono_gc_collection_count:
318 * \param generation a GC generation number
320 * Get how many times a garbage collection has been performed
321 * for the given \p generation number.
323 * \returns the number of garbage collections
326 mono_gc_collection_count (int generation)
332 * mono_gc_add_memory_pressure:
333 * \param value amount of bytes
335 * Adjust the garbage collector's view of how many bytes of memory
336 * are indirectly referenced by managed objects (for example unmanaged
337 * memory holding image or other binary data).
338 * This is a hint only to the garbage collector algorithm.
339 * Note that negative amounts of p value will decrease the memory
343 mono_gc_add_memory_pressure (gint64 value)
348 * mono_gc_get_used_size:
350 * Get the approximate amount of memory used by managed objects.
352 * Returns: the amount of memory used in bytes
355 mono_gc_get_used_size (void)
357 return GC_get_heap_size () - GC_get_free_bytes ();
361 * mono_gc_get_heap_size:
363 * Get the amount of memory used by the garbage collector.
365 * Returns: the size of the heap in bytes
368 mono_gc_get_heap_size (void)
370 return GC_get_heap_size ();
374 mono_gc_is_gc_thread (void)
376 return GC_thread_is_registered ();
380 mono_gc_thread_attach (MonoThreadInfo* info)
382 struct GC_stack_base sb;
385 /* TODO: use GC_get_stack_base instead of baseptr. */
386 sb.mem_base = info->stack_end;
387 res = GC_register_my_thread (&sb);
388 if (res == GC_UNIMPLEMENTED)
389 return NULL; /* Cannot happen with GC v7+. */
391 info->handle_stack = mono_handle_stack_alloc ();
397 mono_gc_thread_detach_with_lock (MonoThreadInfo *p)
399 MonoNativeThreadId tid;
401 tid = mono_thread_info_get_tid (p);
403 if (p->runtime_thread)
404 mono_threads_add_joinable_thread ((gpointer)tid);
406 mono_handle_stack_free (p->handle_stack);
410 mono_gc_thread_in_critical_region (MonoThreadInfo *info)
416 mono_object_is_alive (MonoObject* o)
418 return GC_is_marked ((ptr_t)o);
422 mono_gc_walk_heap (int flags, MonoGCReferences callback, void *data)
427 static gint64 gc_start_time;
430 on_gc_notification (GC_EventType event)
432 MonoProfilerGCEvent e;
435 case GC_EVENT_PRE_STOP_WORLD:
436 e = MONO_GC_EVENT_PRE_STOP_WORLD;
437 MONO_GC_WORLD_STOP_BEGIN ();
440 case GC_EVENT_POST_STOP_WORLD:
441 e = MONO_GC_EVENT_POST_STOP_WORLD;
442 MONO_GC_WORLD_STOP_END ();
445 case GC_EVENT_PRE_START_WORLD:
446 e = MONO_GC_EVENT_PRE_START_WORLD;
447 MONO_GC_WORLD_RESTART_BEGIN (1);
450 case GC_EVENT_POST_START_WORLD:
451 e = MONO_GC_EVENT_POST_START_WORLD;
452 MONO_GC_WORLD_RESTART_END (1);
456 e = MONO_GC_EVENT_START;
458 #ifndef DISABLE_PERFCOUNTERS
459 if (mono_perfcounters)
460 InterlockedIncrement (&mono_perfcounters->gc_collections0);
462 InterlockedIncrement (&gc_stats.major_gc_count);
463 gc_start_time = mono_100ns_ticks ();
467 e = MONO_GC_EVENT_END;
469 #if defined(ENABLE_DTRACE) && defined(__sun__)
470 /* This works around a dtrace -G problem on Solaris.
471 Limit its actual use to when the probe is enabled. */
472 if (MONO_GC_END_ENABLED ())
476 #ifndef DISABLE_PERFCOUNTERS
477 if (mono_perfcounters) {
478 guint64 heap_size = GC_get_heap_size ();
479 guint64 used_size = heap_size - GC_get_free_bytes ();
480 /* FIXME: change these to InterlockedWrite64 () */
481 UnlockedWrite64 (&mono_perfcounters->gc_total_bytes, used_size);
482 UnlockedWrite64 (&mono_perfcounters->gc_committed_bytes, heap_size);
483 UnlockedWrite64 (&mono_perfcounters->gc_reserved_bytes, heap_size);
484 UnlockedWrite64 (&mono_perfcounters->gc_gen0size, heap_size);
487 UnlockedAdd64 (&gc_stats.major_gc_time, mono_100ns_ticks () - gc_start_time);
488 mono_trace_message (MONO_TRACE_GC, "gc took %" G_GINT64_FORMAT " usecs", (mono_100ns_ticks () - gc_start_time) / 10);
495 case GC_EVENT_MARK_START:
496 case GC_EVENT_MARK_END:
497 case GC_EVENT_RECLAIM_START:
498 case GC_EVENT_RECLAIM_END:
501 MONO_PROFILER_RAISE (gc_event, (e, 0));
506 case GC_EVENT_PRE_STOP_WORLD:
507 mono_thread_info_suspend_lock ();
508 MONO_PROFILER_RAISE (gc_event, (MONO_GC_EVENT_PRE_STOP_WORLD_LOCKED, 0));
510 case GC_EVENT_POST_START_WORLD:
511 mono_thread_info_suspend_unlock ();
512 MONO_PROFILER_RAISE (gc_event, (MONO_GC_EVENT_POST_START_WORLD_UNLOCKED, 0));
521 on_gc_heap_resize (size_t new_size)
523 guint64 heap_size = GC_get_heap_size ();
524 #ifndef DISABLE_PERFCOUNTERS
525 if (mono_perfcounters) {
526 /* FIXME: change these to InterlockedWrite64 () */
527 UnlockedWrite64 (&mono_perfcounters->gc_committed_bytes, heap_size);
528 UnlockedWrite64 (&mono_perfcounters->gc_reserved_bytes, heap_size);
529 UnlockedWrite64 (&mono_perfcounters->gc_gen0size, heap_size);
533 MONO_PROFILER_RAISE (gc_resize, (new_size));
542 register_root (gpointer arg)
544 RootData* root_data = arg;
545 g_hash_table_insert (roots, root_data->start, root_data->end);
550 mono_gc_register_root (char *start, size_t size, void *descr, MonoGCRootSource source, const char *msg)
553 root_data.start = start;
554 /* Boehm root processing requires one byte past end of region to be scanned */
555 root_data.end = start + size + 1;
556 GC_call_with_alloc_lock (register_root, &root_data);
562 mono_gc_register_root_wbarrier (char *start, size_t size, MonoGCDescriptor descr, MonoGCRootSource source, const char *msg)
564 return mono_gc_register_root (start, size, descr, source, msg);
568 deregister_root (gpointer arg)
570 gboolean removed = g_hash_table_remove (roots, arg);
576 mono_gc_deregister_root (char* addr)
578 GC_call_with_alloc_lock (deregister_root, addr);
582 push_root (gpointer key, gpointer value, gpointer user_data)
584 GC_push_all (key, value);
588 mono_push_other_roots (void)
590 g_hash_table_foreach (roots, push_root, NULL);
591 if (default_push_other_roots)
592 default_push_other_roots ();
596 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
598 /* libgc requires that we use HIDE_POINTER... */
599 *link_addr = (void*)HIDE_POINTER (obj);
601 GC_REGISTER_LONG_LINK (link_addr, obj);
603 GC_GENERAL_REGISTER_DISAPPEARING_LINK (link_addr, obj);
607 mono_gc_weak_link_remove (void **link_addr, gboolean track)
610 GC_unregister_long_link (link_addr);
612 GC_unregister_disappearing_link (link_addr);
617 reveal_link (gpointer link_addr)
619 void **link_a = (void **)link_addr;
620 return REVEAL_POINTER (*link_a);
624 mono_gc_weak_link_get (void **link_addr)
626 MonoObject *obj = (MonoObject *)GC_call_with_alloc_lock (reveal_link, link_addr);
627 if (obj == (MonoObject *) -1)
633 mono_gc_make_descr_for_string (gsize *bitmap, int numbits)
635 return mono_gc_make_descr_from_bitmap (bitmap, numbits);
639 mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size)
641 return mono_gc_make_descr_from_bitmap (bitmap, numbits);
645 mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size)
647 /* libgc has no usable support for arrays... */
648 return GC_NO_DESCRIPTOR;
652 mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
654 /* It seems there are issues when the bitmap doesn't fit: play it safe */
656 return GC_NO_DESCRIPTOR;
658 return (gpointer)GC_make_descriptor ((GC_bitmap)bitmap, numbits);
662 mono_gc_make_vector_descr (void)
668 mono_gc_make_root_descr_all_refs (int numbits)
674 mono_gc_alloc_fixed (size_t size, void *descr, MonoGCRootSource source, const char *msg)
676 return GC_MALLOC_UNCOLLECTABLE (size);
680 mono_gc_free_fixed (void* addr)
686 mono_gc_alloc_obj (MonoVTable *vtable, size_t size)
690 if (!vtable->klass->has_references) {
691 obj = (MonoObject *)GC_MALLOC_ATOMIC (size);
692 if (G_UNLIKELY (!obj))
695 obj->vtable = vtable;
696 obj->synchronisation = NULL;
698 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
699 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
700 obj = (MonoObject *)GC_GCJ_MALLOC (size, vtable);
701 if (G_UNLIKELY (!obj))
704 obj = (MonoObject *)GC_MALLOC (size);
705 if (G_UNLIKELY (!obj))
708 obj->vtable = vtable;
711 if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
712 MONO_PROFILER_RAISE (gc_allocation, (obj));
718 mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length)
722 if (!vtable->klass->has_references) {
723 obj = (MonoArray *)GC_MALLOC_ATOMIC (size);
724 if (G_UNLIKELY (!obj))
727 obj->obj.vtable = vtable;
728 obj->obj.synchronisation = NULL;
730 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
731 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
732 obj = (MonoArray *)GC_GCJ_MALLOC (size, vtable);
733 if (G_UNLIKELY (!obj))
736 obj = (MonoArray *)GC_MALLOC (size);
737 if (G_UNLIKELY (!obj))
740 obj->obj.vtable = vtable;
743 obj->max_length = max_length;
745 if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
746 MONO_PROFILER_RAISE (gc_allocation, (&obj->obj));
752 mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uintptr_t bounds_size)
756 if (!vtable->klass->has_references) {
757 obj = (MonoArray *)GC_MALLOC_ATOMIC (size);
758 if (G_UNLIKELY (!obj))
761 obj->obj.vtable = vtable;
762 obj->obj.synchronisation = NULL;
764 memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
765 } else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
766 obj = (MonoArray *)GC_GCJ_MALLOC (size, vtable);
767 if (G_UNLIKELY (!obj))
770 obj = (MonoArray *)GC_MALLOC (size);
771 if (G_UNLIKELY (!obj))
774 obj->obj.vtable = vtable;
777 obj->max_length = max_length;
780 obj->bounds = (MonoArrayBounds *) ((char *) obj + size - bounds_size);
782 if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
783 MONO_PROFILER_RAISE (gc_allocation, (&obj->obj));
789 mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len)
791 MonoString *obj = (MonoString *)GC_MALLOC_ATOMIC (size);
792 if (G_UNLIKELY (!obj))
795 obj->object.vtable = vtable;
796 obj->object.synchronisation = NULL;
798 obj->chars [len] = 0;
800 if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
801 MONO_PROFILER_RAISE (gc_allocation, (&obj->object));
807 mono_gc_alloc_mature (MonoVTable *vtable, size_t size)
809 return mono_gc_alloc_obj (vtable, size);
813 mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size)
815 return mono_gc_alloc_obj (vtable, size);
819 mono_gc_invoke_finalizers (void)
821 /* There is a bug in GC_invoke_finalizer () in versions <= 6.2alpha4:
822 * the 'mem_freed' variable is not initialized when there are no
823 * objects to finalize, which leads to strange behavior later on.
824 * The check is necessary to work around that bug.
826 if (GC_should_invoke_finalizers ())
827 return GC_invoke_finalizers ();
832 mono_gc_pending_finalizers (void)
834 return GC_should_invoke_finalizers ();
838 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
840 *(void**)field_ptr = value;
844 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
846 *(void**)slot_ptr = value;
850 mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
852 mono_gc_memmove_aligned (dest_ptr, src_ptr, count * sizeof (gpointer));
856 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
858 *(void**)ptr = value;
862 mono_gc_wbarrier_generic_store_atomic (gpointer ptr, MonoObject *value)
864 InterlockedWritePointer ((volatile gpointer *)ptr, value);
868 mono_gc_wbarrier_generic_nostore (gpointer ptr)
873 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
875 mono_gc_memmove_atomic (dest, src, count * mono_class_value_size (klass, NULL));
879 mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
881 /* do not copy the sync state */
882 mono_gc_memmove_aligned ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
883 mono_object_class (obj)->instance_size - sizeof (MonoObject));
887 mono_gc_clear_domain (MonoDomain *domain)
892 mono_gc_suspend_finalizers (void)
897 mono_gc_get_suspend_signal (void)
899 return GC_get_suspend_signal ();
903 mono_gc_get_restart_signal (void)
905 return GC_get_thr_restart_signal ();
908 #if defined(USE_COMPILER_TLS) && defined(__linux__) && (defined(__i386__) || defined(__x86_64__))
909 extern __thread void* GC_thread_tls;
910 #include "metadata-internals.h"
916 while (!(v & (1 << i)))
923 ATYPE_FREEPTR_FOR_BOX,
931 create_allocator (int atype, int tls_key, gboolean slowpath)
933 int index_var, bytes_var, my_fl_var, my_entry_var;
934 guint32 no_freelist_branch, not_small_enough_branch = 0;
935 guint32 size_overflow_branch = 0;
936 MonoMethodBuilder *mb;
938 MonoMethodSignature *csig;
939 const char *name = NULL;
942 g_assert_not_reached ();
944 if (atype == ATYPE_FREEPTR) {
945 name = slowpath ? "SlowAllocPtrfree" : "AllocPtrfree";
946 } else if (atype == ATYPE_FREEPTR_FOR_BOX) {
947 name = slowpath ? "SlowAllocPtrfreeBox" : "AllocPtrfreeBox";
948 } else if (atype == ATYPE_NORMAL) {
949 name = slowpath ? "SlowAlloc" : "Alloc";
950 } else if (atype == ATYPE_GCJ) {
951 name = slowpath ? "SlowAllocGcj" : "AllocGcj";
952 } else if (atype == ATYPE_STRING) {
953 name = slowpath ? "SlowAllocString" : "AllocString";
955 g_assert_not_reached ();
958 csig = mono_metadata_signature_alloc (mono_defaults.corlib, 2);
960 if (atype == ATYPE_STRING) {
961 csig->ret = &mono_defaults.string_class->byval_arg;
962 csig->params [0] = &mono_defaults.int_class->byval_arg;
963 csig->params [1] = &mono_defaults.int32_class->byval_arg;
965 csig->ret = &mono_defaults.object_class->byval_arg;
966 csig->params [0] = &mono_defaults.int_class->byval_arg;
967 csig->params [1] = &mono_defaults.int32_class->byval_arg;
970 mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_ALLOC);
973 goto always_slowpath;
975 bytes_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
976 if (atype == ATYPE_STRING) {
977 /* a string alloator method takes the args: (vtable, len) */
978 /* bytes = (offsetof (MonoString, chars) + ((len + 1) * 2)); */
979 mono_mb_emit_ldarg (mb, 1);
980 mono_mb_emit_icon (mb, 1);
981 mono_mb_emit_byte (mb, MONO_CEE_ADD);
982 mono_mb_emit_icon (mb, 1);
983 mono_mb_emit_byte (mb, MONO_CEE_SHL);
984 // sizeof (MonoString) might include padding
985 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, chars));
986 mono_mb_emit_byte (mb, MONO_CEE_ADD);
987 mono_mb_emit_stloc (mb, bytes_var);
989 mono_mb_emit_ldarg (mb, 1);
990 mono_mb_emit_stloc (mb, bytes_var);
993 /* this is needed for strings/arrays only as the other big types are never allocated with this method */
994 if (atype == ATYPE_STRING) {
996 /* if (!SMALL_ENOUGH (bytes)) jump slow_path;*/
997 mono_mb_emit_ldloc (mb, bytes_var);
998 mono_mb_emit_icon (mb, (NFREELISTS-1) * GRANULARITY);
999 not_small_enough_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_UN_S);
1000 /* check for overflow */
1001 mono_mb_emit_ldloc (mb, bytes_var);
1002 mono_mb_emit_icon (mb, sizeof (MonoString));
1003 size_overflow_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLE_UN_S);
1006 /* int index = INDEX_FROM_BYTES(bytes); */
1007 index_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
1009 mono_mb_emit_ldloc (mb, bytes_var);
1010 mono_mb_emit_icon (mb, GRANULARITY - 1);
1011 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1012 mono_mb_emit_icon (mb, shift_amount (GRANULARITY));
1013 mono_mb_emit_byte (mb, MONO_CEE_SHR_UN);
1014 mono_mb_emit_icon (mb, shift_amount (sizeof (gpointer)));
1015 mono_mb_emit_byte (mb, MONO_CEE_SHL);
1016 /* index var is already adjusted into bytes */
1017 mono_mb_emit_stloc (mb, index_var);
1019 my_fl_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1020 my_entry_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1021 /* my_fl = ((GC_thread)tsd) -> ptrfree_freelists + index; */
1022 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
1023 mono_mb_emit_byte (mb, 0x0D); /* CEE_MONO_TLS */
1024 mono_mb_emit_i4 (mb, tls_key);
1025 if (atype == ATYPE_FREEPTR || atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING)
1026 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
1027 + G_STRUCT_OFFSET (struct thread_local_freelists,
1028 ptrfree_freelists));
1029 else if (atype == ATYPE_NORMAL)
1030 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
1031 + G_STRUCT_OFFSET (struct thread_local_freelists,
1033 else if (atype == ATYPE_GCJ)
1034 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
1035 + G_STRUCT_OFFSET (struct thread_local_freelists,
1038 g_assert_not_reached ();
1039 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1040 mono_mb_emit_ldloc (mb, index_var);
1041 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1042 mono_mb_emit_stloc (mb, my_fl_var);
1044 /* my_entry = *my_fl; */
1045 mono_mb_emit_ldloc (mb, my_fl_var);
1046 mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
1047 mono_mb_emit_stloc (mb, my_entry_var);
1049 /* if (EXPECT((word)my_entry >= HBLKSIZE, 1)) { */
1050 mono_mb_emit_ldloc (mb, my_entry_var);
1051 mono_mb_emit_icon (mb, HBLKSIZE);
1052 no_freelist_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
1054 /* ptr_t next = obj_link(my_entry); *my_fl = next; */
1055 mono_mb_emit_ldloc (mb, my_fl_var);
1056 mono_mb_emit_ldloc (mb, my_entry_var);
1057 mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
1058 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1060 /* set the vtable and clear the words in the object */
1061 mono_mb_emit_ldloc (mb, my_entry_var);
1062 mono_mb_emit_ldarg (mb, 0);
1063 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1065 if (atype == ATYPE_FREEPTR) {
1066 int start_var, end_var, start_loop;
1067 /* end = my_entry + bytes; start = my_entry + sizeof (gpointer);
1069 start_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1070 end_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1071 mono_mb_emit_ldloc (mb, my_entry_var);
1072 mono_mb_emit_ldloc (mb, bytes_var);
1073 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1074 mono_mb_emit_stloc (mb, end_var);
1075 mono_mb_emit_ldloc (mb, my_entry_var);
1076 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
1077 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1078 mono_mb_emit_stloc (mb, start_var);
1082 * } while (start < end);
1084 start_loop = mono_mb_get_label (mb);
1085 mono_mb_emit_ldloc (mb, start_var);
1086 mono_mb_emit_icon (mb, 0);
1087 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1088 mono_mb_emit_ldloc (mb, start_var);
1089 mono_mb_emit_icon (mb, sizeof (gpointer));
1090 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1091 mono_mb_emit_stloc (mb, start_var);
1093 mono_mb_emit_ldloc (mb, start_var);
1094 mono_mb_emit_ldloc (mb, end_var);
1095 mono_mb_emit_byte (mb, MONO_CEE_BLT_UN_S);
1096 mono_mb_emit_byte (mb, start_loop - (mono_mb_get_label (mb) + 1));
1097 } else if (atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING) {
1098 /* need to clear just the sync pointer */
1099 mono_mb_emit_ldloc (mb, my_entry_var);
1100 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
1101 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1102 mono_mb_emit_icon (mb, 0);
1103 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1106 if (atype == ATYPE_STRING) {
1107 /* need to set length and clear the last char */
1108 /* s->length = len; */
1109 mono_mb_emit_ldloc (mb, my_entry_var);
1110 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, length));
1111 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1112 mono_mb_emit_ldarg (mb, 1);
1113 mono_mb_emit_byte (mb, MONO_CEE_STIND_I4);
1114 /* s->chars [len] = 0; */
1115 mono_mb_emit_ldloc (mb, my_entry_var);
1116 mono_mb_emit_ldloc (mb, bytes_var);
1117 mono_mb_emit_icon (mb, 2);
1118 mono_mb_emit_byte (mb, MONO_CEE_SUB);
1119 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1120 mono_mb_emit_icon (mb, 0);
1121 mono_mb_emit_byte (mb, MONO_CEE_STIND_I2);
1124 /* return my_entry; */
1125 mono_mb_emit_ldloc (mb, my_entry_var);
1126 mono_mb_emit_byte (mb, MONO_CEE_RET);
1128 mono_mb_patch_short_branch (mb, no_freelist_branch);
1129 if (not_small_enough_branch > 0)
1130 mono_mb_patch_short_branch (mb, not_small_enough_branch);
1131 if (size_overflow_branch > 0)
1132 mono_mb_patch_short_branch (mb, size_overflow_branch);
1134 /* the slow path: we just call back into the runtime */
1136 if (atype == ATYPE_STRING) {
1137 mono_mb_emit_ldarg (mb, 1);
1138 mono_mb_emit_icall (mb, ves_icall_string_alloc);
1140 mono_mb_emit_ldarg (mb, 0);
1141 mono_mb_emit_icall (mb, ves_icall_object_new_specific);
1144 mono_mb_emit_byte (mb, MONO_CEE_RET);
1146 info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE);
1147 info->d.alloc.gc_name = "boehm";
1148 info->d.alloc.alloc_type = atype;
1149 mb->init_locals = FALSE;
1151 res = mono_mb_create (mb, csig, 8, info);
1157 static MonoMethod* alloc_method_cache [ATYPE_NUM];
1158 static MonoMethod* slowpath_alloc_method_cache [ATYPE_NUM];
1161 mono_gc_is_critical_method (MonoMethod *method)
1165 for (i = 0; i < ATYPE_NUM; ++i)
1166 if (method == alloc_method_cache [i] || method == slowpath_alloc_method_cache [i])
1173 * If possible, generate a managed method that can quickly allocate objects in class
1174 * @klass. The method will typically have an thread-local inline allocation sequence.
1175 * The signature of the called method is:
1176 * object allocate (MonoVTable *vtable)
1177 * The thread local alloc logic is taken from libgc/pthread_support.c.
1180 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size)
1185 * Tls implementation changed, we jump to tls native getters/setters.
1186 * Is boehm managed allocator ok with this ? Do we even care ?
1190 if (!SMALL_ENOUGH (klass->instance_size))
1192 if (mono_class_has_finalizer (klass) || mono_class_is_marshalbyref (klass))
1194 if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
1198 if (mono_class_is_open_constructed_type (&klass->byval_arg))
1200 if (klass->byval_arg.type == MONO_TYPE_STRING) {
1201 atype = ATYPE_STRING;
1202 } else if (!known_instance_size) {
1204 } else if (!klass->has_references) {
1206 atype = ATYPE_FREEPTR_FOR_BOX;
1208 atype = ATYPE_FREEPTR;
1212 * disabled because we currently do a runtime choice anyway, to
1213 * deal with multiple appdomains.
1214 if (vtable->gc_descr != GC_NO_DESCRIPTOR)
1217 atype = ATYPE_NORMAL;
1220 return mono_gc_get_managed_allocator_by_type (atype, MANAGED_ALLOCATOR_REGULAR);
1224 mono_gc_get_managed_array_allocator (MonoClass *klass)
1230 * mono_gc_get_managed_allocator_by_type:
1232 * Return a managed allocator method corresponding to allocator type ATYPE.
1235 mono_gc_get_managed_allocator_by_type (int atype, ManagedAllocatorVariant variant)
1238 gboolean slowpath = variant != MANAGED_ALLOCATOR_REGULAR;
1239 MonoMethod **cache = slowpath ? slowpath_alloc_method_cache : alloc_method_cache;
1243 res = cache [atype];
1247 res = create_allocator (atype, -1, slowpath);
1248 mono_os_mutex_lock (&mono_gc_lock);
1249 if (cache [atype]) {
1250 mono_free_method (res);
1251 res = cache [atype];
1253 mono_memory_barrier ();
1254 cache [atype] = res;
1256 mono_os_mutex_unlock (&mono_gc_lock);
1261 mono_gc_get_managed_allocator_types (void)
1267 mono_gc_get_write_barrier (void)
1269 g_assert_not_reached ();
1276 mono_gc_is_critical_method (MonoMethod *method)
1282 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size)
1288 mono_gc_get_managed_array_allocator (MonoClass *klass)
1294 mono_gc_get_managed_allocator_by_type (int atype, ManagedAllocatorVariant variant)
1300 mono_gc_get_managed_allocator_types (void)
1306 mono_gc_get_write_barrier (void)
1308 g_assert_not_reached ();
1315 mono_gc_get_specific_write_barrier (gboolean is_concurrent)
1317 g_assert_not_reached ();
1322 mono_gc_get_aligned_size_for_allocator (int size)
1328 mono_gc_get_gc_name (void)
1334 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
1336 return GC_call_with_alloc_lock (func, data);
1340 mono_gc_get_description (void)
1342 return g_strdup (DEFAULT_GC_NAME);
1346 mono_gc_set_desktop_mode (void)
1352 mono_gc_is_moving (void)
1358 mono_gc_is_disabled (void)
1360 if (GC_dont_gc || g_hasenv ("GC_DONT_GC"))
1367 mono_gc_wbarrier_range_copy (gpointer _dest, gpointer _src, int size)
1369 g_assert_not_reached ();
1373 mono_gc_get_range_copy_func (void)
1375 return &mono_gc_wbarrier_range_copy;
1379 mono_gc_get_card_table (int *shift_bits, gpointer *card_mask)
1381 g_assert_not_reached ();
1386 mono_gc_card_table_nursery_check (void)
1388 g_assert_not_reached ();
1393 mono_gc_get_nursery (int *shift_bits, size_t *size)
1399 mono_gc_precise_stack_mark_enabled (void)
1405 mono_gc_get_logfile (void)
1411 mono_gc_params_set (const char* options)
1416 mono_gc_debug_set (const char* options)
1421 mono_gc_conservatively_scan_area (void *start, void *end)
1423 g_assert_not_reached ();
1427 mono_gc_scan_object (void *obj, void *gc_data)
1429 g_assert_not_reached ();
1434 mono_gc_get_bitmap_for_descr (void *descr, int *numbits)
1436 g_assert_not_reached ();
1441 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
1446 mono_gc_set_stack_end (void *stack_end)
1450 void mono_gc_set_skip_thread (gboolean value)
1455 mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
1460 /* This assertion is not valid when GC_DEBUG is defined */
1461 g_assert (GC_base (obj) == (char*)obj - offset);
1464 GC_REGISTER_FINALIZER_NO_ORDER ((char*)obj - offset, (GC_finalization_proc)user_data, GUINT_TO_POINTER (offset), NULL, NULL);
1469 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
1471 /* it is being replaced by GC_pthread_create on some
1472 * platforms, see libgc/include/gc_pthread_redirects.h */
1473 return pthread_create (new_thread, attr, start_routine, arg);
1478 BOOL APIENTRY mono_gc_dllmain (HMODULE module_handle, DWORD reason, LPVOID reserved)
1480 return GC_DllMain (module_handle, reason, reserved);
1485 mono_gc_get_vtable_bits (MonoClass *klass)
1487 if (fin_callbacks.is_class_finalization_aware) {
1488 if (fin_callbacks.is_class_finalization_aware (klass))
1489 return BOEHM_GC_BIT_FINALIZER_AWARE;
1495 * mono_gc_register_altstack:
1497 * Register the dimensions of the normal stack and altstack with the collector.
1498 * Currently, STACK/STACK_SIZE is only used when the thread is suspended while it is on an altstack.
1501 mono_gc_register_altstack (gpointer stack, gint32 stack_size, gpointer altstack, gint32 altstack_size)
1503 GC_register_altstack (stack, stack_size, altstack, altstack_size);
1507 mono_gc_get_los_limit (void)
1513 mono_gc_set_string_length (MonoString *str, gint32 new_length)
1515 mono_unichar2 *new_end = str->chars + new_length;
1517 /* zero the discarded string. This null-delimits the string and allows
1518 * the space to be reclaimed by SGen. */
1520 memset (new_end, 0, (str->length - new_length + 1) * sizeof (mono_unichar2));
1521 str->length = new_length;
1525 mono_gc_user_markers_supported (void)
1531 mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
1533 g_assert_not_reached ();
1537 /* Toggleref support */
1540 mono_gc_toggleref_add (MonoObject *object, mono_bool strong_ref)
1542 if (GC_toggleref_add ((GC_PTR)object, (int)strong_ref) != GC_SUCCESS)
1543 g_error ("GC_toggleref_add failed\n");
1547 mono_gc_toggleref_register_callback (MonoToggleRefStatus (*proccess_toggleref) (MonoObject *obj))
1549 GC_set_toggleref_func ((GC_ToggleRefStatus (*) (GC_PTR obj)) proccess_toggleref);
1552 /* Test support code */
1554 static MonoToggleRefStatus
1555 test_toggleref_callback (MonoObject *obj)
1557 static MonoClassField *mono_toggleref_test_field;
1558 MonoToggleRefStatus status = MONO_TOGGLE_REF_DROP;
1560 if (!mono_toggleref_test_field) {
1561 mono_toggleref_test_field = mono_class_get_field_from_name (mono_object_get_class (obj), "__test");
1562 g_assert (mono_toggleref_test_field);
1565 mono_field_get_value (obj, mono_toggleref_test_field, &status);
1566 printf ("toggleref-cb obj %d\n", status);
1571 register_test_toggleref_callback (void)
1573 mono_gc_toggleref_register_callback (test_toggleref_callback);
1577 is_finalization_aware (MonoObject *obj)
1579 MonoVTable *vt = obj->vtable;
1580 return (vt->gc_bits & BOEHM_GC_BIT_FINALIZER_AWARE) == BOEHM_GC_BIT_FINALIZER_AWARE;
1584 fin_notifier (MonoObject *obj)
1586 if (is_finalization_aware (obj))
1587 fin_callbacks.object_queued_for_finalization (obj);
1591 mono_gc_register_finalizer_callbacks (MonoGCFinalizerCallbacks *callbacks)
1593 if (callbacks->version != MONO_GC_FINALIZER_EXTENSION_VERSION)
1594 g_error ("Invalid finalizer callback version. Expected %d but got %d\n", MONO_GC_FINALIZER_EXTENSION_VERSION, callbacks->version);
1596 fin_callbacks = *callbacks;
1598 GC_set_await_finalize_proc ((void (*) (GC_PTR))fin_notifier);
1601 #define BITMAP_SIZE (sizeof (*((HandleData *)NULL)->bitmap) * CHAR_BIT)
1603 static inline gboolean
1604 slot_occupied (HandleData *handles, guint slot) {
1605 return handles->bitmap [slot / BITMAP_SIZE] & (1 << (slot % BITMAP_SIZE));
1609 vacate_slot (HandleData *handles, guint slot) {
1610 handles->bitmap [slot / BITMAP_SIZE] &= ~(1 << (slot % BITMAP_SIZE));
1614 occupy_slot (HandleData *handles, guint slot) {
1615 handles->bitmap [slot / BITMAP_SIZE] |= 1 << (slot % BITMAP_SIZE);
1619 find_first_unset (guint32 bitmap)
1622 for (i = 0; i < 32; ++i) {
1623 if (!(bitmap & (1 << i)))
1630 handle_data_alloc_entries (HandleData *handles)
1633 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1634 handles->entries = (void **)g_malloc0 (sizeof (*handles->entries) * handles->size);
1635 handles->domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * handles->size);
1637 handles->entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * handles->size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
1639 handles->bitmap = (guint32 *)g_malloc0 (handles->size / CHAR_BIT);
1643 handle_data_next_unset (HandleData *handles)
1646 for (slot = handles->slot_hint; slot < handles->size / BITMAP_SIZE; ++slot) {
1647 if (handles->bitmap [slot] == 0xffffffff)
1649 handles->slot_hint = slot;
1650 return find_first_unset (handles->bitmap [slot]);
1656 handle_data_first_unset (HandleData *handles)
1659 for (slot = 0; slot < handles->slot_hint; ++slot) {
1660 if (handles->bitmap [slot] == 0xffffffff)
1662 handles->slot_hint = slot;
1663 return find_first_unset (handles->bitmap [slot]);
1668 /* Returns the index of the current slot in the bitmap. */
1670 handle_data_grow (HandleData *handles, gboolean track)
1672 guint32 *new_bitmap;
1673 guint32 new_size = handles->size * 2; /* always double: we memset to 0 based on this below */
1675 /* resize and copy the bitmap */
1676 new_bitmap = (guint32 *)g_malloc0 (new_size / CHAR_BIT);
1677 memcpy (new_bitmap, handles->bitmap, handles->size / CHAR_BIT);
1678 g_free (handles->bitmap);
1679 handles->bitmap = new_bitmap;
1681 /* resize and copy the entries */
1682 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1684 guint16 *domain_ids;
1686 domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * new_size);
1687 entries = (void **)g_malloc0 (sizeof (*handles->entries) * new_size);
1688 memcpy (domain_ids, handles->domain_ids, sizeof (*handles->domain_ids) * handles->size);
1689 for (i = 0; i < handles->size; ++i) {
1690 MonoObject *obj = mono_gc_weak_link_get (&(handles->entries [i]));
1692 mono_gc_weak_link_add (&(entries [i]), obj, track);
1693 mono_gc_weak_link_remove (&(handles->entries [i]), track);
1695 g_assert (!handles->entries [i]);
1698 g_free (handles->entries);
1699 g_free (handles->domain_ids);
1700 handles->entries = entries;
1701 handles->domain_ids = domain_ids;
1704 entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * new_size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
1705 mono_gc_memmove_aligned (entries, handles->entries, sizeof (*handles->entries) * handles->size);
1706 mono_gc_free_fixed (handles->entries);
1707 handles->entries = entries;
1709 handles->slot_hint = handles->size / BITMAP_SIZE;
1710 handles->size = new_size;
1714 alloc_handle (HandleData *handles, MonoObject *obj, gboolean track)
1718 lock_handles (handles);
1720 handle_data_alloc_entries (handles);
1721 i = handle_data_next_unset (handles);
1722 if (i == -1 && handles->slot_hint != 0)
1723 i = handle_data_first_unset (handles);
1725 handle_data_grow (handles, track);
1728 slot = handles->slot_hint * BITMAP_SIZE + i;
1729 occupy_slot (handles, slot);
1730 handles->entries [slot] = NULL;
1731 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1732 /*FIXME, what to use when obj == null?*/
1733 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
1735 mono_gc_weak_link_add (&(handles->entries [slot]), obj, track);
1737 handles->entries [slot] = obj;
1740 #ifndef DISABLE_PERFCOUNTERS
1741 InterlockedIncrement (&mono_perfcounters->gc_num_handles);
1743 unlock_handles (handles);
1744 res = MONO_GC_HANDLE (slot, handles->type);
1745 MONO_PROFILER_RAISE (gc_handle_created, (res, handles->type, obj));
1750 * mono_gchandle_new:
1751 * \param obj managed object to get a handle for
1752 * \param pinned whether the object should be pinned
1754 * This returns a handle that wraps the object, this is used to keep a
1755 * reference to a managed object from the unmanaged world and preventing the
1756 * object from being disposed.
1758 * If \p pinned is false the address of the object can not be obtained, if it is
1759 * true the address of the object can be obtained. This will also pin the
1760 * object so it will not be possible by a moving garbage collector to move the
1763 * \returns a handle that can be used to access the object from
1767 mono_gchandle_new (MonoObject *obj, gboolean pinned)
1769 return alloc_handle (&gc_handles [pinned? HANDLE_PINNED: HANDLE_NORMAL], obj, FALSE);
1773 * mono_gchandle_new_weakref:
1774 * \param obj managed object to get a handle for
1775 * \param track_resurrection Determines how long to track the object, if this is set to TRUE, the object is tracked after finalization, if FALSE, the object is only tracked up until the point of finalization.
1777 * This returns a weak handle that wraps the object, this is used to
1778 * keep a reference to a managed object from the unmanaged world.
1779 * Unlike the \c mono_gchandle_new the object can be reclaimed by the
1780 * garbage collector. In this case the value of the GCHandle will be
1783 * If \p track_resurrection is TRUE the object will be tracked through
1784 * finalization and if the object is resurrected during the execution
1785 * of the finalizer, then the returned weakref will continue to hold
1786 * a reference to the object. If \p track_resurrection is FALSE, then
1787 * the weak reference's target will become NULL as soon as the object
1788 * is passed on to the finalizer.
1790 * \returns a handle that can be used to access the object from
1794 mono_gchandle_new_weakref (MonoObject *obj, gboolean track_resurrection)
1796 return alloc_handle (&gc_handles [track_resurrection? HANDLE_WEAK_TRACK: HANDLE_WEAK], obj, track_resurrection);
1800 * mono_gchandle_get_target:
1801 * \param gchandle a GCHandle's handle.
1803 * The handle was previously created by calling \c mono_gchandle_new or
1804 * \c mono_gchandle_new_weakref.
1806 * \returns A pointer to the \c MonoObject* represented by the handle or
1807 * NULL for a collected object if using a weakref handle.
1810 mono_gchandle_get_target (guint32 gchandle)
1812 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1813 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1814 HandleData *handles = &gc_handles [type];
1815 MonoObject *obj = NULL;
1816 if (type >= HANDLE_TYPE_MAX)
1819 lock_handles (handles);
1820 if (slot < handles->size && slot_occupied (handles, slot)) {
1821 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1822 obj = mono_gc_weak_link_get (&handles->entries [slot]);
1824 obj = (MonoObject *)handles->entries [slot];
1827 /* print a warning? */
1829 unlock_handles (handles);
1830 /*g_print ("get target of entry %d of type %d: %p\n", slot, handles->type, obj);*/
1835 mono_gchandle_set_target (guint32 gchandle, MonoObject *obj)
1837 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1838 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1839 HandleData *handles = &gc_handles [type];
1840 MonoObject *old_obj = NULL;
1842 g_assert (type < HANDLE_TYPE_MAX);
1843 lock_handles (handles);
1844 if (slot < handles->size && slot_occupied (handles, slot)) {
1845 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1846 old_obj = (MonoObject *)handles->entries [slot];
1847 if (handles->entries [slot])
1848 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1850 mono_gc_weak_link_add (&handles->entries [slot], obj, handles->type == HANDLE_WEAK_TRACK);
1851 /*FIXME, what to use when obj == null?*/
1852 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
1854 handles->entries [slot] = obj;
1857 /* print a warning? */
1859 /*g_print ("changed entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
1860 unlock_handles (handles);
1864 mono_gc_is_null (void)
1870 * mono_gchandle_is_in_domain:
1871 * \param gchandle a GCHandle's handle.
1872 * \param domain An application domain.
1874 * Use this function to determine if the \p gchandle points to an
1875 * object allocated in the specified \p domain.
1877 * \returns TRUE if the object wrapped by the \p gchandle belongs to the specific \p domain.
1880 mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain)
1882 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1883 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1884 HandleData *handles = &gc_handles [type];
1885 gboolean result = FALSE;
1887 if (type >= HANDLE_TYPE_MAX)
1890 lock_handles (handles);
1891 if (slot < handles->size && slot_occupied (handles, slot)) {
1892 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1893 result = domain->domain_id == handles->domain_ids [slot];
1896 obj = (MonoObject *)handles->entries [slot];
1900 result = domain == mono_object_domain (obj);
1903 /* print a warning? */
1905 unlock_handles (handles);
1910 * mono_gchandle_free:
1911 * \param gchandle a GCHandle's handle.
1913 * Frees the \p gchandle handle. If there are no outstanding
1914 * references, the garbage collector can reclaim the memory of the
1918 mono_gchandle_free (guint32 gchandle)
1920 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1921 guint type = MONO_GC_HANDLE_TYPE (gchandle);
1922 HandleData *handles = &gc_handles [type];
1923 if (type >= HANDLE_TYPE_MAX)
1926 lock_handles (handles);
1927 if (slot < handles->size && slot_occupied (handles, slot)) {
1928 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1929 if (handles->entries [slot])
1930 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1932 handles->entries [slot] = NULL;
1934 vacate_slot (handles, slot);
1936 /* print a warning? */
1938 #ifndef DISABLE_PERFCOUNTERS
1939 InterlockedDecrement (&mono_perfcounters->gc_num_handles);
1941 /*g_print ("freed entry %d of type %d\n", slot, handles->type);*/
1942 unlock_handles (handles);
1943 MONO_PROFILER_RAISE (gc_handle_deleted, (gchandle, handles->type));
1947 * mono_gchandle_free_domain:
1948 * \param domain domain that is unloading
1950 * Function used internally to cleanup any GC handle for objects belonging
1951 * to the specified domain during appdomain unload.
1954 mono_gchandle_free_domain (MonoDomain *domain)
1958 for (type = HANDLE_TYPE_MIN; type < HANDLE_PINNED; ++type) {
1960 HandleData *handles = &gc_handles [type];
1961 lock_handles (handles);
1962 for (slot = 0; slot < handles->size; ++slot) {
1963 if (!slot_occupied (handles, slot))
1965 if (MONO_GC_HANDLE_TYPE_IS_WEAK (type)) {
1966 if (domain->domain_id == handles->domain_ids [slot]) {
1967 vacate_slot (handles, slot);
1968 if (handles->entries [slot])
1969 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1972 if (handles->entries [slot] && mono_object_domain (handles->entries [slot]) == domain) {
1973 vacate_slot (handles, slot);
1974 handles->entries [slot] = NULL;
1978 unlock_handles (handles);
1984 MONO_EMPTY_SOURCE_FILE (boehm_gc);
1985 #endif /* no Boehm GC */