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/method-builder-ilgen.h>
22 #include <mono/metadata/method-builder-ilgen-internals.h>
23 #include <mono/metadata/opcodes.h>
24 #include <mono/metadata/domain-internals.h>
25 #include <mono/metadata/metadata-internals.h>
26 #include <mono/metadata/marshal.h>
27 #include <mono/metadata/runtime.h>
28 #include <mono/metadata/handle.h>
29 #include <mono/metadata/sgen-toggleref.h>
30 #include <mono/metadata/w32handle.h>
31 #include <mono/utils/atomic.h>
32 #include <mono/utils/mono-logger-internals.h>
33 #include <mono/utils/mono-memory-model.h>
34 #include <mono/utils/mono-time.h>
35 #include <mono/utils/mono-threads.h>
36 #include <mono/utils/dtrace.h>
37 #include <mono/utils/gc_wrapper.h>
38 #include <mono/utils/mono-os-mutex.h>
39 #include <mono/utils/mono-counters.h>
40 #include <mono/utils/mono-compiler.h>
41 #include <mono/utils/unlocked.h>
48 #define THREAD_LOCAL_ALLOC 1
49 #include "private/pthread_support.h"
51 #if defined(HOST_DARWIN) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
52 void *pthread_get_stackaddr_np(pthread_t
);
55 #define GC_NO_DESCRIPTOR ((gpointer)(0 | GC_DS_LENGTH))
56 /*Boehm max heap cannot be smaller than 16MB*/
57 #define MIN_BOEHM_MAX_HEAP_SIZE_IN_MB 16
58 #define MIN_BOEHM_MAX_HEAP_SIZE (MIN_BOEHM_MAX_HEAP_SIZE_IN_MB << 20)
60 static gboolean gc_initialized
= FALSE
;
61 static mono_mutex_t mono_gc_lock
;
63 typedef void (*GC_push_other_roots_proc
)(void);
65 static GC_push_other_roots_proc default_push_other_roots
;
66 static GHashTable
*roots
;
69 mono_push_other_roots(void);
72 register_test_toggleref_callback (void);
74 #define BOEHM_GC_BIT_FINALIZER_AWARE 1
75 static MonoGCFinalizerCallbacks fin_callbacks
;
79 static mono_mutex_t handle_section
;
80 #define lock_handles(handles) mono_os_mutex_lock (&handle_section)
81 #define unlock_handles(handles) mono_os_mutex_unlock (&handle_section)
88 guint slot_hint
: 24; /* starting slot for search in bitmap */
89 /* 2^16 appdomains should be enough for everyone (though I know I'll regret this in 20 years) */
90 /* we alloc this only for weak refs, since we can get the domain directly in the other cases */
94 #define EMPTY_HANDLE_DATA(type) {NULL, NULL, 0, (type), 0, NULL}
96 /* weak and weak-track arrays will be allocated in malloc memory
98 static HandleData gc_handles
[] = {
99 EMPTY_HANDLE_DATA (HANDLE_WEAK
),
100 EMPTY_HANDLE_DATA (HANDLE_WEAK_TRACK
),
101 EMPTY_HANDLE_DATA (HANDLE_NORMAL
),
102 EMPTY_HANDLE_DATA (HANDLE_PINNED
)
106 mono_gc_warning (char *msg
, GC_word arg
)
108 mono_trace (G_LOG_LEVEL_WARNING
, MONO_TRACE_GC
, msg
, (unsigned long)arg
);
111 static void on_gc_notification (GC_EventType event
);
112 static void on_gc_heap_resize (size_t new_size
);
115 mono_gc_base_init (void)
122 mono_counters_init ();
125 mono_w32handle_init ();
129 * Handle the case when we are called from a thread different from the main thread,
131 * FIXME: Move this to libgc where it belongs.
133 * we used to do this only when running on valgrind,
134 * but it happens also in other setups.
136 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
141 pthread_getattr_np (pthread_self (), &attr
);
142 pthread_attr_getstack (&attr
, &sstart
, &size
);
143 pthread_attr_destroy (&attr
);
144 /*g_print ("stackbottom pth is: %p\n", (char*)sstart + size);*/
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
;
158 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
159 GC_stackbottom
= (char*)pthread_get_stackaddr_np (pthread_self ());
160 #elif defined(__OpenBSD__)
161 # include <pthread_np.h>
166 rslt
= pthread_stackseg_np(pthread_self(), &ss
);
167 g_assert (rslt
== 0);
169 GC_stackbottom
= (char*)ss
.ss_sp
;
174 gsize stack_bottom
= (gsize
)&dummy
;
175 stack_bottom
+= 4095;
176 stack_bottom
&= ~4095;
177 /*g_print ("stackbottom is: %p\n", (char*)stack_bottom);*/
178 GC_stackbottom
= (char*)stack_bottom
;
182 roots
= g_hash_table_new (NULL
, NULL
);
183 default_push_other_roots
= GC_push_other_roots
;
184 GC_push_other_roots
= mono_push_other_roots
;
186 #if !defined(HOST_ANDROID)
187 /* If GC_no_dls is set to true, GC_find_limit is not called. This causes a seg fault on Android. */
191 if ((env
= g_getenv ("MONO_GC_DEBUG"))) {
192 char **opts
= g_strsplit (env
, ",", -1);
193 for (char **ptr
= opts
; ptr
&& *ptr
; ptr
++) {
195 if (!strcmp (opt
, "do-not-finalize")) {
196 mono_do_not_finalize
= 1;
197 } else if (!strcmp (opt
, "log-finalizers")) {
198 mono_log_finalizers
= 1;
207 GC_set_warn_proc (mono_gc_warning
);
208 GC_finalize_on_demand
= 1;
209 GC_finalizer_notifier
= mono_gc_finalize_notify
;
211 GC_init_gcj_malloc (5, NULL
);
212 GC_allow_register_threads ();
214 if ((env
= g_getenv ("MONO_GC_PARAMS"))) {
215 char **ptr
, **opts
= g_strsplit (env
, ",", -1);
216 for (ptr
= opts
; *ptr
; ++ptr
) {
218 if (g_str_has_prefix (opt
, "max-heap-size=")) {
221 opt
= strchr (opt
, '=') + 1;
222 if (*opt
&& mono_gc_parse_environment_string_extract_number (opt
, &max_heap
)) {
223 if (max_heap
< MIN_BOEHM_MAX_HEAP_SIZE
) {
224 fprintf (stderr
, "max-heap-size must be at least %dMb.\n", MIN_BOEHM_MAX_HEAP_SIZE_IN_MB
);
227 GC_set_max_heap_size (max_heap
);
229 fprintf (stderr
, "max-heap-size must be an integer.\n");
233 } else if (g_str_has_prefix (opt
, "toggleref-test")) {
234 register_test_toggleref_callback ();
237 /* Could be a parameter for sgen */
239 fprintf (stderr, "MONO_GC_PARAMS must be a comma-delimited list of one or more of the following:\n");
240 fprintf (stderr, " max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
249 mono_thread_callbacks_init ();
250 mono_thread_info_init (sizeof (MonoThreadInfo
));
251 mono_os_mutex_init (&mono_gc_lock
);
252 mono_os_mutex_init_recursive (&handle_section
);
254 mono_thread_info_attach ();
256 GC_set_on_collection_event (on_gc_notification
);
257 GC_on_heap_resize
= on_gc_heap_resize
;
259 gc_initialized
= TRUE
;
263 mono_gc_base_cleanup (void)
265 GC_finalizer_notifier
= NULL
;
270 * \param generation GC generation identifier
272 * Perform a garbage collection for the given generation, higher numbers
273 * mean usually older objects. Collecting a high-numbered generation
274 * implies collecting also the lower-numbered generations.
275 * The maximum value for \p generation can be retrieved with a call to
276 * \c mono_gc_max_generation, so this function is usually called as:
278 * <code>mono_gc_collect (mono_gc_max_generation ());</code>
281 mono_gc_collect (int generation
)
283 #ifndef DISABLE_PERFCOUNTERS
284 mono_atomic_inc_i32 (&mono_perfcounters
->gc_induced
);
290 * mono_gc_max_generation:
292 * Get the maximum generation number used by the current garbage
293 * collector. The value will be 0 for the Boehm collector, 1 or more
294 * for the generational collectors.
296 * Returns: the maximum generation number.
299 mono_gc_max_generation (void)
305 * mono_gc_get_generation:
306 * \param object a managed object
308 * Get the garbage collector's generation that \p object belongs to.
309 * Use this has a hint only.
311 * \returns a garbage collector generation number
314 mono_gc_get_generation (MonoObject
*object
)
320 * mono_gc_collection_count:
321 * \param generation a GC generation number
323 * Get how many times a garbage collection has been performed
324 * for the given \p generation number.
326 * \returns the number of garbage collections
329 mono_gc_collection_count (int generation
)
335 * mono_gc_add_memory_pressure:
336 * \param value amount of bytes
338 * Adjust the garbage collector's view of how many bytes of memory
339 * are indirectly referenced by managed objects (for example unmanaged
340 * memory holding image or other binary data).
341 * This is a hint only to the garbage collector algorithm.
342 * Note that negative amounts of p value will decrease the memory
346 mono_gc_add_memory_pressure (gint64 value
)
351 * mono_gc_get_used_size:
353 * Get the approximate amount of memory used by managed objects.
355 * Returns: the amount of memory used in bytes
358 mono_gc_get_used_size (void)
360 return GC_get_heap_size () - GC_get_free_bytes ();
364 * mono_gc_get_heap_size:
366 * Get the amount of memory used by the garbage collector.
368 * Returns: the size of the heap in bytes
371 mono_gc_get_heap_size (void)
373 return GC_get_heap_size ();
377 mono_gc_is_gc_thread (void)
379 return GC_thread_is_registered ();
383 mono_gc_thread_attach (MonoThreadInfo
* info
)
385 struct GC_stack_base sb
;
388 /* TODO: use GC_get_stack_base instead of baseptr. */
389 sb
.mem_base
= info
->stack_end
;
390 res
= GC_register_my_thread (&sb
);
391 if (res
== GC_UNIMPLEMENTED
)
392 return NULL
; /* Cannot happen with GC v7+. */
394 info
->handle_stack
= mono_handle_stack_alloc ();
400 mono_gc_thread_detach_with_lock (MonoThreadInfo
*p
)
402 MonoNativeThreadId tid
;
404 tid
= mono_thread_info_get_tid (p
);
406 if (p
->runtime_thread
)
407 mono_threads_add_joinable_thread ((gpointer
)tid
);
409 mono_handle_stack_free (p
->handle_stack
);
410 p
->handle_stack
= NULL
;
414 mono_gc_thread_in_critical_region (MonoThreadInfo
*info
)
420 mono_object_is_alive (MonoObject
* o
)
422 return GC_is_marked ((ptr_t
)o
);
426 mono_gc_walk_heap (int flags
, MonoGCReferences callback
, void *data
)
431 static gint64 gc_start_time
;
434 on_gc_notification (GC_EventType event
)
436 MonoProfilerGCEvent e
;
439 case GC_EVENT_PRE_STOP_WORLD
:
440 e
= MONO_GC_EVENT_PRE_STOP_WORLD
;
441 MONO_GC_WORLD_STOP_BEGIN ();
444 case GC_EVENT_POST_STOP_WORLD
:
445 e
= MONO_GC_EVENT_POST_STOP_WORLD
;
446 MONO_GC_WORLD_STOP_END ();
449 case GC_EVENT_PRE_START_WORLD
:
450 e
= MONO_GC_EVENT_PRE_START_WORLD
;
451 MONO_GC_WORLD_RESTART_BEGIN (1);
454 case GC_EVENT_POST_START_WORLD
:
455 e
= MONO_GC_EVENT_POST_START_WORLD
;
456 MONO_GC_WORLD_RESTART_END (1);
460 e
= MONO_GC_EVENT_START
;
462 #ifndef DISABLE_PERFCOUNTERS
463 if (mono_perfcounters
)
464 mono_atomic_inc_i32 (&mono_perfcounters
->gc_collections0
);
466 mono_atomic_inc_i32 (&mono_gc_stats
.major_gc_count
);
467 gc_start_time
= mono_100ns_ticks ();
471 e
= MONO_GC_EVENT_END
;
473 #if defined(ENABLE_DTRACE) && defined(__sun__)
474 /* This works around a dtrace -G problem on Solaris.
475 Limit its actual use to when the probe is enabled. */
476 if (MONO_GC_END_ENABLED ())
480 #ifndef DISABLE_PERFCOUNTERS
481 if (mono_perfcounters
) {
482 guint64 heap_size
= GC_get_heap_size ();
483 guint64 used_size
= heap_size
- GC_get_free_bytes ();
484 /* FIXME: change these to mono_atomic_store_i64 () */
485 UnlockedWrite64 (&mono_perfcounters
->gc_total_bytes
, used_size
);
486 UnlockedWrite64 (&mono_perfcounters
->gc_committed_bytes
, heap_size
);
487 UnlockedWrite64 (&mono_perfcounters
->gc_reserved_bytes
, heap_size
);
488 UnlockedWrite64 (&mono_perfcounters
->gc_gen0size
, heap_size
);
491 UnlockedAdd64 (&mono_gc_stats
.major_gc_time
, mono_100ns_ticks () - gc_start_time
);
492 mono_trace_message (MONO_TRACE_GC
, "gc took %" G_GINT64_FORMAT
" usecs", (mono_100ns_ticks () - gc_start_time
) / 10);
499 case GC_EVENT_MARK_START
:
500 case GC_EVENT_MARK_END
:
501 case GC_EVENT_RECLAIM_START
:
502 case GC_EVENT_RECLAIM_END
:
505 MONO_PROFILER_RAISE (gc_event
, (e
, 0));
506 MONO_PROFILER_RAISE (gc_event2
, (e
, 0, TRUE
));
511 case GC_EVENT_PRE_STOP_WORLD
:
512 mono_thread_info_suspend_lock ();
513 MONO_PROFILER_RAISE (gc_event
, (MONO_GC_EVENT_PRE_STOP_WORLD_LOCKED
, 0));
514 MONO_PROFILER_RAISE (gc_event2
, (MONO_GC_EVENT_PRE_STOP_WORLD_LOCKED
, 0, TRUE
));
516 case GC_EVENT_POST_START_WORLD
:
517 mono_thread_info_suspend_unlock ();
518 MONO_PROFILER_RAISE (gc_event
, (MONO_GC_EVENT_POST_START_WORLD_UNLOCKED
, 0));
519 MONO_PROFILER_RAISE (gc_event2
, (MONO_GC_EVENT_POST_START_WORLD_UNLOCKED
, 0, TRUE
));
528 on_gc_heap_resize (size_t new_size
)
530 guint64 heap_size
= GC_get_heap_size ();
531 #ifndef DISABLE_PERFCOUNTERS
532 if (mono_perfcounters
) {
533 /* FIXME: change these to mono_atomic_store_i64 () */
534 UnlockedWrite64 (&mono_perfcounters
->gc_committed_bytes
, heap_size
);
535 UnlockedWrite64 (&mono_perfcounters
->gc_reserved_bytes
, heap_size
);
536 UnlockedWrite64 (&mono_perfcounters
->gc_gen0size
, heap_size
);
540 MONO_PROFILER_RAISE (gc_resize
, (new_size
));
549 register_root (gpointer arg
)
551 RootData
* root_data
= arg
;
552 g_hash_table_insert (roots
, root_data
->start
, root_data
->end
);
557 mono_gc_register_root (char *start
, size_t size
, void *descr
, MonoGCRootSource source
, void *key
, const char *msg
)
560 root_data
.start
= start
;
561 /* Boehm root processing requires one byte past end of region to be scanned */
562 root_data
.end
= start
+ size
+ 1;
563 GC_call_with_alloc_lock (register_root
, &root_data
);
564 MONO_PROFILER_RAISE (gc_root_register
, ((const mono_byte
*) start
, size
, source
, key
, msg
));
569 mono_gc_register_root_wbarrier (char *start
, size_t size
, MonoGCDescriptor descr
, MonoGCRootSource source
, void *key
, const char *msg
)
571 return mono_gc_register_root (start
, size
, descr
, source
, key
, msg
);
575 deregister_root (gpointer arg
)
577 gboolean removed
= g_hash_table_remove (roots
, arg
);
583 mono_gc_deregister_root (char* addr
)
585 GC_call_with_alloc_lock (deregister_root
, addr
);
586 MONO_PROFILER_RAISE (gc_root_unregister
, ((const mono_byte
*) addr
));
590 push_root (gpointer key
, gpointer value
, gpointer user_data
)
592 GC_push_all (key
, value
);
596 push_handle_stack (HandleStack
* stack
)
598 HandleChunk
*cur
= stack
->bottom
;
599 HandleChunk
*last
= stack
->top
;
606 GC_push_all ((gpointer
)&cur
->elems
[0], (char*)(cur
->elems
+ cur
->size
) + 1);
614 mono_push_other_roots (void)
616 g_hash_table_foreach (roots
, push_root
, NULL
);
617 FOREACH_THREAD_EXCLUDE (info
, MONO_THREAD_INFO_FLAGS_NO_GC
) {
618 HandleStack
* stack
= (HandleStack
*)info
->handle_stack
;
620 push_handle_stack (stack
);
622 if (default_push_other_roots
)
623 default_push_other_roots ();
627 mono_gc_weak_link_add (void **link_addr
, MonoObject
*obj
, gboolean track
)
629 /* libgc requires that we use HIDE_POINTER... */
630 *link_addr
= (void*)HIDE_POINTER (obj
);
632 GC_REGISTER_LONG_LINK (link_addr
, obj
);
634 GC_GENERAL_REGISTER_DISAPPEARING_LINK (link_addr
, obj
);
638 mono_gc_weak_link_remove (void **link_addr
, gboolean track
)
641 GC_unregister_long_link (link_addr
);
643 GC_unregister_disappearing_link (link_addr
);
648 reveal_link (gpointer link_addr
)
650 void **link_a
= (void **)link_addr
;
651 return REVEAL_POINTER (*link_a
);
655 mono_gc_weak_link_get (void **link_addr
)
657 MonoObject
*obj
= (MonoObject
*)GC_call_with_alloc_lock (reveal_link
, link_addr
);
658 if (obj
== (MonoObject
*) -1)
664 mono_gc_make_descr_for_string (gsize
*bitmap
, int numbits
)
666 return mono_gc_make_descr_from_bitmap (bitmap
, numbits
);
670 mono_gc_make_descr_for_object (gsize
*bitmap
, int numbits
, size_t obj_size
)
672 return mono_gc_make_descr_from_bitmap (bitmap
, numbits
);
676 mono_gc_make_descr_for_array (int vector
, gsize
*elem_bitmap
, int numbits
, size_t elem_size
)
678 /* libgc has no usable support for arrays... */
679 return GC_NO_DESCRIPTOR
;
683 mono_gc_make_descr_from_bitmap (gsize
*bitmap
, int numbits
)
685 /* It seems there are issues when the bitmap doesn't fit: play it safe */
687 return GC_NO_DESCRIPTOR
;
689 return (gpointer
)GC_make_descriptor ((GC_bitmap
)bitmap
, numbits
);
693 mono_gc_make_vector_descr (void)
699 mono_gc_make_root_descr_all_refs (int numbits
)
705 mono_gc_alloc_fixed (size_t size
, void *descr
, MonoGCRootSource source
, void *key
, const char *msg
)
707 void *start
= GC_MALLOC_UNCOLLECTABLE (size
);
708 MONO_PROFILER_RAISE (gc_root_register
, ((const mono_byte
*) start
, size
, source
, key
, msg
));
713 mono_gc_free_fixed (void* addr
)
715 MONO_PROFILER_RAISE (gc_root_unregister
, ((const mono_byte
*) addr
));
720 mono_gc_alloc_obj (MonoVTable
*vtable
, size_t size
)
724 if (!m_class_has_references (vtable
->klass
)) {
725 obj
= (MonoObject
*)GC_MALLOC_ATOMIC (size
);
726 if (G_UNLIKELY (!obj
))
729 obj
->vtable
= vtable
;
730 obj
->synchronisation
= NULL
;
732 memset ((char *) obj
+ sizeof (MonoObject
), 0, size
- sizeof (MonoObject
));
733 } else if (vtable
->gc_descr
!= GC_NO_DESCRIPTOR
) {
734 obj
= (MonoObject
*)GC_GCJ_MALLOC (size
, vtable
);
735 if (G_UNLIKELY (!obj
))
738 obj
= (MonoObject
*)GC_MALLOC (size
);
739 if (G_UNLIKELY (!obj
))
742 obj
->vtable
= vtable
;
745 if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
746 MONO_PROFILER_RAISE (gc_allocation
, (obj
));
752 mono_gc_alloc_vector (MonoVTable
*vtable
, size_t size
, uintptr_t max_length
)
756 if (!m_class_has_references (vtable
->klass
)) {
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
;
779 if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
780 MONO_PROFILER_RAISE (gc_allocation
, (&obj
->obj
));
786 mono_gc_alloc_array (MonoVTable
*vtable
, size_t size
, uintptr_t max_length
, uintptr_t bounds_size
)
790 if (!m_class_has_references (vtable
->klass
)) {
791 obj
= (MonoArray
*)GC_MALLOC_ATOMIC (size
);
792 if (G_UNLIKELY (!obj
))
795 obj
->obj
.vtable
= vtable
;
796 obj
->obj
.synchronisation
= NULL
;
798 memset ((char *) obj
+ sizeof (MonoObject
), 0, size
- sizeof (MonoObject
));
799 } else if (vtable
->gc_descr
!= GC_NO_DESCRIPTOR
) {
800 obj
= (MonoArray
*)GC_GCJ_MALLOC (size
, vtable
);
801 if (G_UNLIKELY (!obj
))
804 obj
= (MonoArray
*)GC_MALLOC (size
);
805 if (G_UNLIKELY (!obj
))
808 obj
->obj
.vtable
= vtable
;
811 obj
->max_length
= max_length
;
814 obj
->bounds
= (MonoArrayBounds
*) ((char *) obj
+ size
- bounds_size
);
816 if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
817 MONO_PROFILER_RAISE (gc_allocation
, (&obj
->obj
));
823 mono_gc_alloc_string (MonoVTable
*vtable
, size_t size
, gint32 len
)
825 MonoString
*obj
= (MonoString
*)GC_MALLOC_ATOMIC (size
);
826 if (G_UNLIKELY (!obj
))
829 obj
->object
.vtable
= vtable
;
830 obj
->object
.synchronisation
= NULL
;
832 obj
->chars
[len
] = 0;
834 if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
835 MONO_PROFILER_RAISE (gc_allocation
, (&obj
->object
));
841 mono_gc_alloc_mature (MonoVTable
*vtable
, size_t size
)
843 return mono_gc_alloc_obj (vtable
, size
);
847 mono_gc_alloc_pinned_obj (MonoVTable
*vtable
, size_t size
)
849 return mono_gc_alloc_obj (vtable
, size
);
853 mono_gc_invoke_finalizers (void)
855 /* There is a bug in GC_invoke_finalizer () in versions <= 6.2alpha4:
856 * the 'mem_freed' variable is not initialized when there are no
857 * objects to finalize, which leads to strange behavior later on.
858 * The check is necessary to work around that bug.
860 if (GC_should_invoke_finalizers ())
861 return GC_invoke_finalizers ();
866 mono_gc_pending_finalizers (void)
868 return GC_should_invoke_finalizers ();
872 mono_gc_wbarrier_set_field (MonoObject
*obj
, gpointer field_ptr
, MonoObject
* value
)
874 *(void**)field_ptr
= value
;
878 mono_gc_wbarrier_set_arrayref (MonoArray
*arr
, gpointer slot_ptr
, MonoObject
* value
)
880 *(void**)slot_ptr
= value
;
884 mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr
, gpointer src_ptr
, int count
)
886 mono_gc_memmove_aligned (dest_ptr
, src_ptr
, count
* sizeof (gpointer
));
890 mono_gc_wbarrier_generic_store (gpointer ptr
, MonoObject
* value
)
892 *(void**)ptr
= value
;
896 mono_gc_wbarrier_generic_store_atomic (gpointer ptr
, MonoObject
*value
)
898 mono_atomic_store_ptr ((volatile gpointer
*)ptr
, value
);
902 mono_gc_wbarrier_generic_nostore (gpointer ptr
)
907 mono_gc_wbarrier_value_copy (gpointer dest
, gpointer src
, int count
, MonoClass
*klass
)
909 mono_gc_memmove_atomic (dest
, src
, count
* mono_class_value_size (klass
, NULL
));
913 mono_gc_wbarrier_object_copy (MonoObject
* obj
, MonoObject
*src
)
915 /* do not copy the sync state */
916 mono_gc_memmove_aligned ((char*)obj
+ sizeof (MonoObject
), (char*)src
+ sizeof (MonoObject
),
917 m_class_get_instance_size (mono_object_class (obj
)) - sizeof (MonoObject
));
921 mono_gc_clear_domain (MonoDomain
*domain
)
926 mono_gc_suspend_finalizers (void)
931 mono_gc_get_suspend_signal (void)
933 return GC_get_suspend_signal ();
937 mono_gc_get_restart_signal (void)
939 return GC_get_thr_restart_signal ();
942 #if defined(USE_COMPILER_TLS) && defined(__linux__) && (defined(__i386__) || defined(__x86_64__))
943 extern __thread
void* GC_thread_tls
;
944 #include "metadata-internals.h"
950 while (!(v
& (1 << i
)))
957 ATYPE_FREEPTR_FOR_BOX
,
965 create_allocator (int atype
, int tls_key
, gboolean slowpath
)
967 int index_var
, bytes_var
, my_fl_var
, my_entry_var
;
968 guint32 no_freelist_branch
, not_small_enough_branch
= 0;
969 guint32 size_overflow_branch
= 0;
970 MonoMethodBuilder
*mb
;
972 MonoMethodSignature
*csig
;
973 const char *name
= NULL
;
976 g_assert_not_reached ();
978 if (atype
== ATYPE_FREEPTR
) {
979 name
= slowpath
? "SlowAllocPtrfree" : "AllocPtrfree";
980 } else if (atype
== ATYPE_FREEPTR_FOR_BOX
) {
981 name
= slowpath
? "SlowAllocPtrfreeBox" : "AllocPtrfreeBox";
982 } else if (atype
== ATYPE_NORMAL
) {
983 name
= slowpath
? "SlowAlloc" : "Alloc";
984 } else if (atype
== ATYPE_GCJ
) {
985 name
= slowpath
? "SlowAllocGcj" : "AllocGcj";
986 } else if (atype
== ATYPE_STRING
) {
987 name
= slowpath
? "SlowAllocString" : "AllocString";
989 g_assert_not_reached ();
992 csig
= mono_metadata_signature_alloc (mono_defaults
.corlib
, 2);
994 if (atype
== ATYPE_STRING
) {
995 csig
->ret
= m_class_get_byval_arg (mono_defaults
.string_class
);
996 csig
->params
[0] = m_class_get_byval_arg (mono_defaults
.int_class
);
997 csig
->params
[1] = m_class_get_byval_arg (mono_defaults
.int32_class
);
999 csig
->ret
= m_class_get_byval_arg (mono_defaults
.object_class
);
1000 csig
->params
[0] = m_class_get_byval_arg (mono_defaults
.int_class
);
1001 csig
->params
[1] = m_class_get_byval_arg (mono_defaults
.int32_class
);
1004 mb
= mono_mb_new (mono_defaults
.object_class
, name
, MONO_WRAPPER_ALLOC
);
1007 goto always_slowpath
;
1009 bytes_var
= mono_mb_add_local (mb
, m_class_get_byval_arg (mono_defaults
.int32_class
));
1010 if (atype
== ATYPE_STRING
) {
1011 /* a string alloator method takes the args: (vtable, len) */
1012 /* bytes = (offsetof (MonoString, chars) + ((len + 1) * 2)); */
1013 mono_mb_emit_ldarg (mb
, 1);
1014 mono_mb_emit_icon (mb
, 1);
1015 mono_mb_emit_byte (mb
, MONO_CEE_ADD
);
1016 mono_mb_emit_icon (mb
, 1);
1017 mono_mb_emit_byte (mb
, MONO_CEE_SHL
);
1018 // sizeof (MonoString) might include padding
1019 mono_mb_emit_icon (mb
, G_STRUCT_OFFSET (MonoString
, chars
));
1020 mono_mb_emit_byte (mb
, MONO_CEE_ADD
);
1021 mono_mb_emit_stloc (mb
, bytes_var
);
1023 mono_mb_emit_ldarg (mb
, 1);
1024 mono_mb_emit_stloc (mb
, bytes_var
);
1027 /* this is needed for strings/arrays only as the other big types are never allocated with this method */
1028 if (atype
== ATYPE_STRING
) {
1029 /* check for size */
1030 /* if (!SMALL_ENOUGH (bytes)) jump slow_path;*/
1031 mono_mb_emit_ldloc (mb
, bytes_var
);
1032 mono_mb_emit_icon (mb
, (NFREELISTS
-1) * GRANULARITY
);
1033 not_small_enough_branch
= mono_mb_emit_short_branch (mb
, MONO_CEE_BGT_UN_S
);
1034 /* check for overflow */
1035 mono_mb_emit_ldloc (mb
, bytes_var
);
1036 mono_mb_emit_icon (mb
, sizeof (MonoString
));
1037 size_overflow_branch
= mono_mb_emit_short_branch (mb
, MONO_CEE_BLE_UN_S
);
1040 /* int index = INDEX_FROM_BYTES(bytes); */
1041 index_var
= mono_mb_add_local (mb
, m_class_get_byval_arg (mono_defaults
.int32_class
));
1043 mono_mb_emit_ldloc (mb
, bytes_var
);
1044 mono_mb_emit_icon (mb
, GRANULARITY
- 1);
1045 mono_mb_emit_byte (mb
, MONO_CEE_ADD
);
1046 mono_mb_emit_icon (mb
, shift_amount (GRANULARITY
));
1047 mono_mb_emit_byte (mb
, MONO_CEE_SHR_UN
);
1048 mono_mb_emit_icon (mb
, shift_amount (sizeof (gpointer
)));
1049 mono_mb_emit_byte (mb
, MONO_CEE_SHL
);
1050 /* index var is already adjusted into bytes */
1051 mono_mb_emit_stloc (mb
, index_var
);
1053 my_fl_var
= mono_mb_add_local (mb
, m_class_get_byval_arg (mono_defaults
.int_class
));
1054 my_entry_var
= mono_mb_add_local (mb
, m_class_get_byval_arg (mono_defaults
.int_class
));
1055 /* my_fl = ((GC_thread)tsd) -> ptrfree_freelists + index; */
1056 mono_mb_emit_byte (mb
, MONO_CUSTOM_PREFIX
);
1057 mono_mb_emit_byte (mb
, 0x0D); /* CEE_MONO_TLS */
1058 mono_mb_emit_i4 (mb
, tls_key
);
1059 if (atype
== ATYPE_FREEPTR
|| atype
== ATYPE_FREEPTR_FOR_BOX
|| atype
== ATYPE_STRING
)
1060 mono_mb_emit_icon (mb
, G_STRUCT_OFFSET (struct GC_Thread_Rep
, tlfs
)
1061 + G_STRUCT_OFFSET (struct thread_local_freelists
,
1062 ptrfree_freelists
));
1063 else if (atype
== ATYPE_NORMAL
)
1064 mono_mb_emit_icon (mb
, G_STRUCT_OFFSET (struct GC_Thread_Rep
, tlfs
)
1065 + G_STRUCT_OFFSET (struct thread_local_freelists
,
1067 else if (atype
== ATYPE_GCJ
)
1068 mono_mb_emit_icon (mb
, G_STRUCT_OFFSET (struct GC_Thread_Rep
, tlfs
)
1069 + G_STRUCT_OFFSET (struct thread_local_freelists
,
1072 g_assert_not_reached ();
1073 mono_mb_emit_byte (mb
, MONO_CEE_ADD
);
1074 mono_mb_emit_ldloc (mb
, index_var
);
1075 mono_mb_emit_byte (mb
, MONO_CEE_ADD
);
1076 mono_mb_emit_stloc (mb
, my_fl_var
);
1078 /* my_entry = *my_fl; */
1079 mono_mb_emit_ldloc (mb
, my_fl_var
);
1080 mono_mb_emit_byte (mb
, MONO_CEE_LDIND_I
);
1081 mono_mb_emit_stloc (mb
, my_entry_var
);
1083 /* if (EXPECT((word)my_entry >= HBLKSIZE, 1)) { */
1084 mono_mb_emit_ldloc (mb
, my_entry_var
);
1085 mono_mb_emit_icon (mb
, HBLKSIZE
);
1086 no_freelist_branch
= mono_mb_emit_short_branch (mb
, MONO_CEE_BLT_UN_S
);
1088 /* ptr_t next = obj_link(my_entry); *my_fl = next; */
1089 mono_mb_emit_ldloc (mb
, my_fl_var
);
1090 mono_mb_emit_ldloc (mb
, my_entry_var
);
1091 mono_mb_emit_byte (mb
, MONO_CEE_LDIND_I
);
1092 mono_mb_emit_byte (mb
, MONO_CEE_STIND_I
);
1094 /* set the vtable and clear the words in the object */
1095 mono_mb_emit_ldloc (mb
, my_entry_var
);
1096 mono_mb_emit_ldarg (mb
, 0);
1097 mono_mb_emit_byte (mb
, MONO_CEE_STIND_I
);
1099 if (atype
== ATYPE_FREEPTR
) {
1100 int start_var
, end_var
, start_loop
;
1101 /* end = my_entry + bytes; start = my_entry + sizeof (gpointer);
1103 start_var
= mono_mb_add_local (mb
, m_class_get_byval_arg (mono_defaults
.int_class
));
1104 end_var
= mono_mb_add_local (mb
, m_class_get_byval_arg (mono_defaults
.int_class
));
1105 mono_mb_emit_ldloc (mb
, my_entry_var
);
1106 mono_mb_emit_ldloc (mb
, bytes_var
);
1107 mono_mb_emit_byte (mb
, MONO_CEE_ADD
);
1108 mono_mb_emit_stloc (mb
, end_var
);
1109 mono_mb_emit_ldloc (mb
, my_entry_var
);
1110 mono_mb_emit_icon (mb
, G_STRUCT_OFFSET (MonoObject
, synchronisation
));
1111 mono_mb_emit_byte (mb
, MONO_CEE_ADD
);
1112 mono_mb_emit_stloc (mb
, start_var
);
1116 * } while (start < end);
1118 start_loop
= mono_mb_get_label (mb
);
1119 mono_mb_emit_ldloc (mb
, start_var
);
1120 mono_mb_emit_icon (mb
, 0);
1121 mono_mb_emit_byte (mb
, MONO_CEE_STIND_I
);
1122 mono_mb_emit_ldloc (mb
, start_var
);
1123 mono_mb_emit_icon (mb
, sizeof (gpointer
));
1124 mono_mb_emit_byte (mb
, MONO_CEE_ADD
);
1125 mono_mb_emit_stloc (mb
, start_var
);
1127 mono_mb_emit_ldloc (mb
, start_var
);
1128 mono_mb_emit_ldloc (mb
, end_var
);
1129 mono_mb_emit_byte (mb
, MONO_CEE_BLT_UN_S
);
1130 mono_mb_emit_byte (mb
, start_loop
- (mono_mb_get_label (mb
) + 1));
1131 } else if (atype
== ATYPE_FREEPTR_FOR_BOX
|| atype
== ATYPE_STRING
) {
1132 /* need to clear just the sync pointer */
1133 mono_mb_emit_ldloc (mb
, my_entry_var
);
1134 mono_mb_emit_icon (mb
, G_STRUCT_OFFSET (MonoObject
, synchronisation
));
1135 mono_mb_emit_byte (mb
, MONO_CEE_ADD
);
1136 mono_mb_emit_icon (mb
, 0);
1137 mono_mb_emit_byte (mb
, MONO_CEE_STIND_I
);
1140 if (atype
== ATYPE_STRING
) {
1141 /* need to set length and clear the last char */
1142 /* s->length = len; */
1143 mono_mb_emit_ldloc (mb
, my_entry_var
);
1144 mono_mb_emit_icon (mb
, G_STRUCT_OFFSET (MonoString
, length
));
1145 mono_mb_emit_byte (mb
, MONO_CEE_ADD
);
1146 mono_mb_emit_ldarg (mb
, 1);
1147 mono_mb_emit_byte (mb
, MONO_CEE_STIND_I4
);
1148 /* s->chars [len] = 0; */
1149 mono_mb_emit_ldloc (mb
, my_entry_var
);
1150 mono_mb_emit_ldloc (mb
, bytes_var
);
1151 mono_mb_emit_icon (mb
, 2);
1152 mono_mb_emit_byte (mb
, MONO_CEE_SUB
);
1153 mono_mb_emit_byte (mb
, MONO_CEE_ADD
);
1154 mono_mb_emit_icon (mb
, 0);
1155 mono_mb_emit_byte (mb
, MONO_CEE_STIND_I2
);
1158 /* return my_entry; */
1159 mono_mb_emit_ldloc (mb
, my_entry_var
);
1160 mono_mb_emit_byte (mb
, MONO_CEE_RET
);
1162 mono_mb_patch_short_branch (mb
, no_freelist_branch
);
1163 if (not_small_enough_branch
> 0)
1164 mono_mb_patch_short_branch (mb
, not_small_enough_branch
);
1165 if (size_overflow_branch
> 0)
1166 mono_mb_patch_short_branch (mb
, size_overflow_branch
);
1168 /* the slow path: we just call back into the runtime */
1170 if (atype
== ATYPE_STRING
) {
1171 mono_mb_emit_ldarg (mb
, 1);
1172 mono_mb_emit_icall (mb
, ves_icall_string_alloc
);
1174 mono_mb_emit_ldarg (mb
, 0);
1175 mono_mb_emit_icall (mb
, ves_icall_object_new_specific
);
1178 mono_mb_emit_byte (mb
, MONO_CEE_RET
);
1180 info
= mono_wrapper_info_create (mb
, WRAPPER_SUBTYPE_NONE
);
1181 info
->d
.alloc
.gc_name
= "boehm";
1182 info
->d
.alloc
.alloc_type
= atype
;
1183 mb
->init_locals
= FALSE
;
1185 res
= mono_mb_create (mb
, csig
, 8, info
);
1191 static MonoMethod
* alloc_method_cache
[ATYPE_NUM
];
1192 static MonoMethod
* slowpath_alloc_method_cache
[ATYPE_NUM
];
1195 mono_gc_is_critical_method (MonoMethod
*method
)
1199 for (i
= 0; i
< ATYPE_NUM
; ++i
)
1200 if (method
== alloc_method_cache
[i
] || method
== slowpath_alloc_method_cache
[i
])
1207 * If possible, generate a managed method that can quickly allocate objects in class
1208 * @klass. The method will typically have an thread-local inline allocation sequence.
1209 * The signature of the called method is:
1210 * object allocate (MonoVTable *vtable)
1211 * The thread local alloc logic is taken from libgc/pthread_support.c.
1214 mono_gc_get_managed_allocator (MonoClass
*klass
, gboolean for_box
, gboolean known_instance_size
)
1219 * Tls implementation changed, we jump to tls native getters/setters.
1220 * Is boehm managed allocator ok with this ? Do we even care ?
1224 if (!SMALL_ENOUGH (m_class_get_instance_size (klass
)))
1226 if (mono_class_has_finalizer (klass
) || mono_class_is_marshalbyref (klass
))
1228 if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
1230 if (m_class_get_rank (klass
))
1232 if (mono_class_is_open_constructed_type (m_class_get_byval_arg (klass
)))
1234 if (m_class_get_byval_arg (klass
)->type
== MONO_TYPE_STRING
) {
1235 atype
= ATYPE_STRING
;
1236 } else if (!known_instance_size
) {
1238 } else if (!m_class_has_references (klass
)) {
1240 atype
= ATYPE_FREEPTR_FOR_BOX
;
1242 atype
= ATYPE_FREEPTR
;
1246 * disabled because we currently do a runtime choice anyway, to
1247 * deal with multiple appdomains.
1248 if (vtable->gc_descr != GC_NO_DESCRIPTOR)
1251 atype = ATYPE_NORMAL;
1254 return mono_gc_get_managed_allocator_by_type (atype
, MANAGED_ALLOCATOR_REGULAR
);
1258 mono_gc_get_managed_array_allocator (MonoClass
*klass
)
1264 * mono_gc_get_managed_allocator_by_type:
1266 * Return a managed allocator method corresponding to allocator type ATYPE.
1269 mono_gc_get_managed_allocator_by_type (int atype
, ManagedAllocatorVariant variant
)
1272 gboolean slowpath
= variant
!= MANAGED_ALLOCATOR_REGULAR
;
1273 MonoMethod
**cache
= slowpath
? slowpath_alloc_method_cache
: alloc_method_cache
;
1277 res
= cache
[atype
];
1281 res
= create_allocator (atype
, -1, slowpath
);
1282 mono_os_mutex_lock (&mono_gc_lock
);
1283 if (cache
[atype
]) {
1284 mono_free_method (res
);
1285 res
= cache
[atype
];
1287 mono_memory_barrier ();
1288 cache
[atype
] = res
;
1290 mono_os_mutex_unlock (&mono_gc_lock
);
1295 mono_gc_get_managed_allocator_types (void)
1301 mono_gc_get_write_barrier (void)
1303 g_assert_not_reached ();
1310 mono_gc_is_critical_method (MonoMethod
*method
)
1316 mono_gc_get_managed_allocator (MonoClass
*klass
, gboolean for_box
, gboolean known_instance_size
)
1322 mono_gc_get_managed_array_allocator (MonoClass
*klass
)
1328 mono_gc_get_managed_allocator_by_type (int atype
, ManagedAllocatorVariant variant
)
1334 mono_gc_get_managed_allocator_types (void)
1340 mono_gc_get_write_barrier (void)
1342 g_assert_not_reached ();
1349 mono_gc_get_specific_write_barrier (gboolean is_concurrent
)
1351 g_assert_not_reached ();
1356 mono_gc_get_aligned_size_for_allocator (int size
)
1362 mono_gc_get_gc_name (void)
1368 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func
, void *data
)
1370 return GC_call_with_alloc_lock (func
, data
);
1374 mono_gc_get_description (void)
1376 return g_strdup (DEFAULT_GC_NAME
);
1380 mono_gc_set_desktop_mode (void)
1386 mono_gc_is_moving (void)
1392 mono_gc_is_disabled (void)
1394 if (GC_dont_gc
|| g_hasenv ("GC_DONT_GC"))
1401 mono_gc_wbarrier_range_copy (gpointer _dest
, gpointer _src
, int size
)
1403 g_assert_not_reached ();
1407 mono_gc_get_range_copy_func (void)
1409 return &mono_gc_wbarrier_range_copy
;
1413 mono_gc_get_card_table (int *shift_bits
, gpointer
*card_mask
)
1415 g_assert_not_reached ();
1420 mono_gc_card_table_nursery_check (void)
1422 g_assert_not_reached ();
1427 mono_gc_get_nursery (int *shift_bits
, size_t *size
)
1433 mono_gc_precise_stack_mark_enabled (void)
1439 mono_gc_get_logfile (void)
1445 mono_gc_params_set (const char* options
)
1450 mono_gc_debug_set (const char* options
)
1455 mono_gc_conservatively_scan_area (void *start
, void *end
)
1457 g_assert_not_reached ();
1461 mono_gc_scan_object (void *obj
, void *gc_data
)
1463 g_assert_not_reached ();
1468 mono_gc_get_bitmap_for_descr (void *descr
, int *numbits
)
1470 g_assert_not_reached ();
1475 mono_gc_set_gc_callbacks (MonoGCCallbacks
*callbacks
)
1480 mono_gc_set_stack_end (void *stack_end
)
1485 mono_gc_skip_thread_changing (gboolean skip
)
1488 * Unlike SGen, Boehm doesn't respect our thread info flags. We need to
1489 * inform Boehm manually to skip/not skip the current thread.
1493 GC_start_blocking ();
1499 mono_gc_skip_thread_changed (gboolean skip
)
1504 mono_gc_register_for_finalization (MonoObject
*obj
, MonoFinalizationProc user_data
)
1509 /* This assertion is not valid when GC_DEBUG is defined */
1510 g_assert (GC_base (obj
) == (char*)obj
- offset
);
1513 GC_REGISTER_FINALIZER_NO_ORDER ((char*)obj
- offset
, (GC_finalization_proc
)user_data
, GUINT_TO_POINTER (offset
), NULL
, NULL
);
1518 mono_gc_pthread_create (pthread_t
*new_thread
, const pthread_attr_t
*attr
, void *(*start_routine
)(void *), void *arg
)
1520 /* it is being replaced by GC_pthread_create on some
1521 * platforms, see libgc/include/gc_pthread_redirects.h */
1522 return pthread_create (new_thread
, attr
, start_routine
, arg
);
1527 BOOL APIENTRY
mono_gc_dllmain (HMODULE module_handle
, DWORD reason
, LPVOID reserved
)
1529 return GC_DllMain (module_handle
, reason
, reserved
);
1534 mono_gc_get_vtable (MonoObject
*obj
)
1536 // No pointer tagging.
1541 mono_gc_get_vtable_bits (MonoClass
*klass
)
1543 if (fin_callbacks
.is_class_finalization_aware
) {
1544 if (fin_callbacks
.is_class_finalization_aware (klass
))
1545 return BOEHM_GC_BIT_FINALIZER_AWARE
;
1551 * mono_gc_register_altstack:
1553 * Register the dimensions of the normal stack and altstack with the collector.
1554 * Currently, STACK/STACK_SIZE is only used when the thread is suspended while it is on an altstack.
1557 mono_gc_register_altstack (gpointer stack
, gint32 stack_size
, gpointer altstack
, gint32 altstack_size
)
1559 GC_register_altstack (stack
, stack_size
, altstack
, altstack_size
);
1563 mono_gc_get_los_limit (void)
1569 mono_gc_set_string_length (MonoString
*str
, gint32 new_length
)
1571 mono_unichar2
*new_end
= str
->chars
+ new_length
;
1573 /* zero the discarded string. This null-delimits the string and allows
1574 * the space to be reclaimed by SGen. */
1576 memset (new_end
, 0, (str
->length
- new_length
+ 1) * sizeof (mono_unichar2
));
1577 str
->length
= new_length
;
1581 mono_gc_user_markers_supported (void)
1587 mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker
)
1589 g_assert_not_reached ();
1593 /* Toggleref support */
1596 mono_gc_toggleref_add (MonoObject
*object
, mono_bool strong_ref
)
1598 if (GC_toggleref_add ((GC_PTR
)object
, (int)strong_ref
) != GC_SUCCESS
)
1599 g_error ("GC_toggleref_add failed\n");
1603 mono_gc_toggleref_register_callback (MonoToggleRefStatus (*proccess_toggleref
) (MonoObject
*obj
))
1605 GC_set_toggleref_func ((GC_ToggleRefStatus (*) (GC_PTR obj
)) proccess_toggleref
);
1608 /* Test support code */
1610 static MonoToggleRefStatus
1611 test_toggleref_callback (MonoObject
*obj
)
1613 static MonoClassField
*mono_toggleref_test_field
;
1614 MonoToggleRefStatus status
= MONO_TOGGLE_REF_DROP
;
1616 if (!mono_toggleref_test_field
) {
1617 mono_toggleref_test_field
= mono_class_get_field_from_name (mono_object_get_class (obj
), "__test");
1618 g_assert (mono_toggleref_test_field
);
1621 mono_field_get_value (obj
, mono_toggleref_test_field
, &status
);
1622 printf ("toggleref-cb obj %d\n", status
);
1627 register_test_toggleref_callback (void)
1629 mono_gc_toggleref_register_callback (test_toggleref_callback
);
1633 is_finalization_aware (MonoObject
*obj
)
1635 MonoVTable
*vt
= obj
->vtable
;
1636 return (vt
->gc_bits
& BOEHM_GC_BIT_FINALIZER_AWARE
) == BOEHM_GC_BIT_FINALIZER_AWARE
;
1640 fin_notifier (MonoObject
*obj
)
1642 if (is_finalization_aware (obj
))
1643 fin_callbacks
.object_queued_for_finalization (obj
);
1647 mono_gc_register_finalizer_callbacks (MonoGCFinalizerCallbacks
*callbacks
)
1649 if (callbacks
->version
!= MONO_GC_FINALIZER_EXTENSION_VERSION
)
1650 g_error ("Invalid finalizer callback version. Expected %d but got %d\n", MONO_GC_FINALIZER_EXTENSION_VERSION
, callbacks
->version
);
1652 fin_callbacks
= *callbacks
;
1654 GC_set_await_finalize_proc ((void (*) (GC_PTR
))fin_notifier
);
1657 #define BITMAP_SIZE (sizeof (*((HandleData *)NULL)->bitmap) * CHAR_BIT)
1659 static inline gboolean
1660 slot_occupied (HandleData
*handles
, guint slot
) {
1661 return handles
->bitmap
[slot
/ BITMAP_SIZE
] & (1 << (slot
% BITMAP_SIZE
));
1665 vacate_slot (HandleData
*handles
, guint slot
) {
1666 handles
->bitmap
[slot
/ BITMAP_SIZE
] &= ~(1 << (slot
% BITMAP_SIZE
));
1670 occupy_slot (HandleData
*handles
, guint slot
) {
1671 handles
->bitmap
[slot
/ BITMAP_SIZE
] |= 1 << (slot
% BITMAP_SIZE
);
1675 find_first_unset (guint32 bitmap
)
1678 for (i
= 0; i
< 32; ++i
) {
1679 if (!(bitmap
& (1 << i
)))
1686 handle_data_alloc_entries (HandleData
*handles
)
1689 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles
->type
)) {
1690 handles
->entries
= (void **)g_malloc0 (sizeof (*handles
->entries
) * handles
->size
);
1691 handles
->domain_ids
= (guint16
*)g_malloc0 (sizeof (*handles
->domain_ids
) * handles
->size
);
1693 handles
->entries
= (void **)mono_gc_alloc_fixed (sizeof (*handles
->entries
) * handles
->size
, NULL
, MONO_ROOT_SOURCE_GC_HANDLE
, NULL
, "GC Handle Table (Boehm)");
1695 handles
->bitmap
= (guint32
*)g_malloc0 (handles
->size
/ CHAR_BIT
);
1699 handle_data_next_unset (HandleData
*handles
)
1702 for (slot
= handles
->slot_hint
; slot
< handles
->size
/ BITMAP_SIZE
; ++slot
) {
1703 if (handles
->bitmap
[slot
] == 0xffffffff)
1705 handles
->slot_hint
= slot
;
1706 return find_first_unset (handles
->bitmap
[slot
]);
1712 handle_data_first_unset (HandleData
*handles
)
1715 for (slot
= 0; slot
< handles
->slot_hint
; ++slot
) {
1716 if (handles
->bitmap
[slot
] == 0xffffffff)
1718 handles
->slot_hint
= slot
;
1719 return find_first_unset (handles
->bitmap
[slot
]);
1724 /* Returns the index of the current slot in the bitmap. */
1726 handle_data_grow (HandleData
*handles
, gboolean track
)
1728 guint32
*new_bitmap
;
1729 guint32 new_size
= handles
->size
* 2; /* always double: we memset to 0 based on this below */
1731 /* resize and copy the bitmap */
1732 new_bitmap
= (guint32
*)g_malloc0 (new_size
/ CHAR_BIT
);
1733 memcpy (new_bitmap
, handles
->bitmap
, handles
->size
/ CHAR_BIT
);
1734 g_free (handles
->bitmap
);
1735 handles
->bitmap
= new_bitmap
;
1737 /* resize and copy the entries */
1738 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles
->type
)) {
1740 guint16
*domain_ids
;
1742 domain_ids
= (guint16
*)g_malloc0 (sizeof (*handles
->domain_ids
) * new_size
);
1743 entries
= (void **)g_malloc0 (sizeof (*handles
->entries
) * new_size
);
1744 memcpy (domain_ids
, handles
->domain_ids
, sizeof (*handles
->domain_ids
) * handles
->size
);
1745 for (i
= 0; i
< handles
->size
; ++i
) {
1746 MonoObject
*obj
= mono_gc_weak_link_get (&(handles
->entries
[i
]));
1748 mono_gc_weak_link_add (&(entries
[i
]), obj
, track
);
1749 mono_gc_weak_link_remove (&(handles
->entries
[i
]), track
);
1751 g_assert (!handles
->entries
[i
]);
1754 g_free (handles
->entries
);
1755 g_free (handles
->domain_ids
);
1756 handles
->entries
= entries
;
1757 handles
->domain_ids
= domain_ids
;
1760 entries
= (void **)mono_gc_alloc_fixed (sizeof (*handles
->entries
) * new_size
, NULL
, MONO_ROOT_SOURCE_GC_HANDLE
, NULL
, "GC Handle Table (Boehm)");
1761 mono_gc_memmove_aligned (entries
, handles
->entries
, sizeof (*handles
->entries
) * handles
->size
);
1762 mono_gc_free_fixed (handles
->entries
);
1763 handles
->entries
= entries
;
1765 handles
->slot_hint
= handles
->size
/ BITMAP_SIZE
;
1766 handles
->size
= new_size
;
1770 alloc_handle (HandleData
*handles
, MonoObject
*obj
, gboolean track
)
1774 lock_handles (handles
);
1776 handle_data_alloc_entries (handles
);
1777 i
= handle_data_next_unset (handles
);
1778 if (i
== -1 && handles
->slot_hint
!= 0)
1779 i
= handle_data_first_unset (handles
);
1781 handle_data_grow (handles
, track
);
1784 slot
= handles
->slot_hint
* BITMAP_SIZE
+ i
;
1785 occupy_slot (handles
, slot
);
1786 handles
->entries
[slot
] = NULL
;
1787 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles
->type
)) {
1788 /*FIXME, what to use when obj == null?*/
1789 handles
->domain_ids
[slot
] = (obj
? mono_object_get_domain (obj
) : mono_domain_get ())->domain_id
;
1791 mono_gc_weak_link_add (&(handles
->entries
[slot
]), obj
, track
);
1793 handles
->entries
[slot
] = obj
;
1796 #ifndef DISABLE_PERFCOUNTERS
1797 mono_atomic_inc_i32 (&mono_perfcounters
->gc_num_handles
);
1799 unlock_handles (handles
);
1800 res
= MONO_GC_HANDLE (slot
, handles
->type
);
1801 MONO_PROFILER_RAISE (gc_handle_created
, (res
, handles
->type
, obj
));
1806 * mono_gchandle_new:
1807 * \param obj managed object to get a handle for
1808 * \param pinned whether the object should be pinned
1810 * This returns a handle that wraps the object, this is used to keep a
1811 * reference to a managed object from the unmanaged world and preventing the
1812 * object from being disposed.
1814 * If \p pinned is false the address of the object can not be obtained, if it is
1815 * true the address of the object can be obtained. This will also pin the
1816 * object so it will not be possible by a moving garbage collector to move the
1819 * \returns a handle that can be used to access the object from
1823 mono_gchandle_new (MonoObject
*obj
, gboolean pinned
)
1825 return alloc_handle (&gc_handles
[pinned
? HANDLE_PINNED
: HANDLE_NORMAL
], obj
, FALSE
);
1829 * mono_gchandle_new_weakref:
1830 * \param obj managed object to get a handle for
1831 * \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.
1833 * This returns a weak handle that wraps the object, this is used to
1834 * keep a reference to a managed object from the unmanaged world.
1835 * Unlike the \c mono_gchandle_new the object can be reclaimed by the
1836 * garbage collector. In this case the value of the GCHandle will be
1839 * If \p track_resurrection is TRUE the object will be tracked through
1840 * finalization and if the object is resurrected during the execution
1841 * of the finalizer, then the returned weakref will continue to hold
1842 * a reference to the object. If \p track_resurrection is FALSE, then
1843 * the weak reference's target will become NULL as soon as the object
1844 * is passed on to the finalizer.
1846 * \returns a handle that can be used to access the object from
1850 mono_gchandle_new_weakref (MonoObject
*obj
, gboolean track_resurrection
)
1852 return alloc_handle (&gc_handles
[track_resurrection
? HANDLE_WEAK_TRACK
: HANDLE_WEAK
], obj
, track_resurrection
);
1856 * mono_gchandle_get_target:
1857 * \param gchandle a GCHandle's handle.
1859 * The handle was previously created by calling \c mono_gchandle_new or
1860 * \c mono_gchandle_new_weakref.
1862 * \returns A pointer to the \c MonoObject* represented by the handle or
1863 * NULL for a collected object if using a weakref handle.
1866 mono_gchandle_get_target (guint32 gchandle
)
1868 guint slot
= MONO_GC_HANDLE_SLOT (gchandle
);
1869 guint type
= MONO_GC_HANDLE_TYPE (gchandle
);
1870 HandleData
*handles
= &gc_handles
[type
];
1871 MonoObject
*obj
= NULL
;
1872 if (type
>= HANDLE_TYPE_MAX
)
1875 lock_handles (handles
);
1876 if (slot
< handles
->size
&& slot_occupied (handles
, slot
)) {
1877 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles
->type
)) {
1878 obj
= mono_gc_weak_link_get (&handles
->entries
[slot
]);
1880 obj
= (MonoObject
*)handles
->entries
[slot
];
1883 /* print a warning? */
1885 unlock_handles (handles
);
1886 /*g_print ("get target of entry %d of type %d: %p\n", slot, handles->type, obj);*/
1891 mono_gchandle_set_target (guint32 gchandle
, MonoObject
*obj
)
1893 guint slot
= MONO_GC_HANDLE_SLOT (gchandle
);
1894 guint type
= MONO_GC_HANDLE_TYPE (gchandle
);
1895 HandleData
*handles
= &gc_handles
[type
];
1896 MonoObject
*old_obj
= NULL
;
1898 g_assert (type
< HANDLE_TYPE_MAX
);
1899 lock_handles (handles
);
1900 if (slot
< handles
->size
&& slot_occupied (handles
, slot
)) {
1901 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles
->type
)) {
1902 old_obj
= (MonoObject
*)handles
->entries
[slot
];
1903 if (handles
->entries
[slot
])
1904 mono_gc_weak_link_remove (&handles
->entries
[slot
], handles
->type
== HANDLE_WEAK_TRACK
);
1906 mono_gc_weak_link_add (&handles
->entries
[slot
], obj
, handles
->type
== HANDLE_WEAK_TRACK
);
1907 /*FIXME, what to use when obj == null?*/
1908 handles
->domain_ids
[slot
] = (obj
? mono_object_get_domain (obj
) : mono_domain_get ())->domain_id
;
1910 handles
->entries
[slot
] = obj
;
1913 /* print a warning? */
1915 /*g_print ("changed entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
1916 unlock_handles (handles
);
1920 mono_gc_is_null (void)
1926 * mono_gchandle_is_in_domain:
1927 * \param gchandle a GCHandle's handle.
1928 * \param domain An application domain.
1930 * Use this function to determine if the \p gchandle points to an
1931 * object allocated in the specified \p domain.
1933 * \returns TRUE if the object wrapped by the \p gchandle belongs to the specific \p domain.
1936 mono_gchandle_is_in_domain (guint32 gchandle
, MonoDomain
*domain
)
1938 guint slot
= MONO_GC_HANDLE_SLOT (gchandle
);
1939 guint type
= MONO_GC_HANDLE_TYPE (gchandle
);
1940 HandleData
*handles
= &gc_handles
[type
];
1941 gboolean result
= FALSE
;
1943 if (type
>= HANDLE_TYPE_MAX
)
1946 lock_handles (handles
);
1947 if (slot
< handles
->size
&& slot_occupied (handles
, slot
)) {
1948 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles
->type
)) {
1949 result
= domain
->domain_id
== handles
->domain_ids
[slot
];
1952 obj
= (MonoObject
*)handles
->entries
[slot
];
1956 result
= domain
== mono_object_domain (obj
);
1959 /* print a warning? */
1961 unlock_handles (handles
);
1966 * mono_gchandle_free:
1967 * \param gchandle a GCHandle's handle.
1969 * Frees the \p gchandle handle. If there are no outstanding
1970 * references, the garbage collector can reclaim the memory of the
1974 mono_gchandle_free (guint32 gchandle
)
1976 guint slot
= MONO_GC_HANDLE_SLOT (gchandle
);
1977 guint type
= MONO_GC_HANDLE_TYPE (gchandle
);
1978 HandleData
*handles
= &gc_handles
[type
];
1979 if (type
>= HANDLE_TYPE_MAX
)
1982 lock_handles (handles
);
1983 if (slot
< handles
->size
&& slot_occupied (handles
, slot
)) {
1984 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles
->type
)) {
1985 if (handles
->entries
[slot
])
1986 mono_gc_weak_link_remove (&handles
->entries
[slot
], handles
->type
== HANDLE_WEAK_TRACK
);
1988 handles
->entries
[slot
] = NULL
;
1990 vacate_slot (handles
, slot
);
1992 /* print a warning? */
1994 #ifndef DISABLE_PERFCOUNTERS
1995 mono_atomic_dec_i32 (&mono_perfcounters
->gc_num_handles
);
1997 /*g_print ("freed entry %d of type %d\n", slot, handles->type);*/
1998 unlock_handles (handles
);
1999 MONO_PROFILER_RAISE (gc_handle_deleted
, (gchandle
, handles
->type
));
2003 * mono_gchandle_free_domain:
2004 * \param domain domain that is unloading
2006 * Function used internally to cleanup any GC handle for objects belonging
2007 * to the specified domain during appdomain unload.
2010 mono_gchandle_free_domain (MonoDomain
*domain
)
2014 for (type
= HANDLE_TYPE_MIN
; type
< HANDLE_PINNED
; ++type
) {
2016 HandleData
*handles
= &gc_handles
[type
];
2017 lock_handles (handles
);
2018 for (slot
= 0; slot
< handles
->size
; ++slot
) {
2019 if (!slot_occupied (handles
, slot
))
2021 if (MONO_GC_HANDLE_TYPE_IS_WEAK (type
)) {
2022 if (domain
->domain_id
== handles
->domain_ids
[slot
]) {
2023 vacate_slot (handles
, slot
);
2024 if (handles
->entries
[slot
])
2025 mono_gc_weak_link_remove (&handles
->entries
[slot
], handles
->type
== HANDLE_WEAK_TRACK
);
2028 if (handles
->entries
[slot
] && mono_object_domain (handles
->entries
[slot
]) == domain
) {
2029 vacate_slot (handles
, slot
);
2030 handles
->entries
[slot
] = NULL
;
2034 unlock_handles (handles
);
2040 mono_gc_register_obj_with_weak_fields (void *obj
)
2042 g_error ("Weak fields not supported by boehm gc");
2047 MONO_EMPTY_SOURCE_FILE (boehm_gc
);
2048 #endif /* no Boehm GC */