2 * metadata/gc.c: GC icalls.
4 * Author: Paolo Molaro <lupus@ximian.com>
6 * Copyright 2002-2003 Ximian, Inc (http://www.ximian.com)
7 * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
8 * Copyright 2012 Xamarin Inc (http://www.xamarin.com)
15 #include <mono/metadata/gc-internal.h>
16 #include <mono/metadata/mono-gc.h>
17 #include <mono/metadata/threads.h>
18 #include <mono/metadata/tabledefs.h>
19 #include <mono/metadata/exception.h>
20 #include <mono/metadata/profiler-private.h>
21 #include <mono/metadata/domain-internals.h>
22 #include <mono/metadata/class-internals.h>
23 #include <mono/metadata/metadata-internals.h>
24 #include <mono/metadata/mono-mlist.h>
25 #include <mono/metadata/threadpool.h>
26 #include <mono/metadata/threadpool-internals.h>
27 #include <mono/metadata/threads-types.h>
28 #include <mono/sgen/sgen-conf.h>
29 #include <mono/utils/mono-logger-internal.h>
30 #include <mono/metadata/gc-internal.h>
31 #include <mono/metadata/marshal.h> /* for mono_delegate_free_ftnptr () */
32 #include <mono/metadata/attach.h>
33 #include <mono/metadata/console-io.h>
34 #include <mono/utils/mono-semaphore.h>
35 #include <mono/utils/mono-memory-model.h>
36 #include <mono/utils/mono-counters.h>
37 #include <mono/utils/mono-time.h>
38 #include <mono/utils/dtrace.h>
39 #include <mono/utils/mono-threads.h>
40 #include <mono/utils/atomic.h>
46 typedef struct DomainFinalizationReq {
48 mono_mutex_t *done_mutex;
49 mono_cond_t *done_cond;
51 } DomainFinalizationReq;
53 #ifdef PLATFORM_WINCE /* FIXME: add accessors to gc.dll API */
54 extern void (*__imp_GC_finalizer_notifier)(void);
55 #define GC_finalizer_notifier __imp_GC_finalizer_notifier
56 extern int __imp_GC_finalize_on_demand;
57 #define GC_finalize_on_demand __imp_GC_finalize_on_demand
60 static gboolean gc_disabled = FALSE;
62 static gboolean finalizing_root_domain = FALSE;
64 gboolean log_finalizers = FALSE;
65 gboolean do_not_finalize = FALSE;
67 #define mono_finalizer_lock() mono_mutex_lock (&finalizer_mutex)
68 #define mono_finalizer_unlock() mono_mutex_unlock (&finalizer_mutex)
69 static mono_mutex_t finalizer_mutex;
70 static mono_mutex_t reference_queue_mutex;
72 static GSList *domains_to_finalize= NULL;
73 static MonoMList *threads_to_finalize = NULL;
75 static gboolean finalizer_thread_exited;
76 /* Uses finalizer_mutex */
77 static mono_cond_t exited_cond;
79 static MonoInternalThread *gc_thread;
81 static void object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*));
83 static void mono_gchandle_set_target (guint32 gchandle, MonoObject *obj);
85 static void reference_queue_proccess_all (void);
86 static void mono_reference_queue_cleanup (void);
87 static void reference_queue_clear_for_domain (MonoDomain *domain);
89 static HANDLE pending_done_event;
93 guarded_wait (HANDLE handle, guint32 timeout, gboolean alertable)
98 result = WaitForSingleObjectEx (handle, timeout, alertable);
105 guarded_cond_wait (mono_cond_t *cond, mono_mutex_t *mutex, guint32 timeout)
109 MONO_PREPARE_BLOCKING;
110 if (timeout == INFINITE)
111 result = mono_cond_wait (cond, mutex);
113 result = mono_cond_timedwait_ms (cond, mutex, timeout);
120 add_thread_to_finalize (MonoInternalThread *thread)
122 mono_finalizer_lock ();
123 if (!threads_to_finalize)
124 MONO_GC_REGISTER_ROOT_SINGLE (threads_to_finalize);
125 threads_to_finalize = mono_mlist_append (threads_to_finalize, (MonoObject*)thread);
126 mono_finalizer_unlock ();
129 static gboolean suspend_finalizers = FALSE;
131 * actually, we might want to queue the finalize requests in a separate thread,
132 * but we need to be careful about the execution domain of the thread...
135 mono_gc_run_finalize (void *obj, void *data)
140 MonoObject *exc = NULL;
145 MonoMethod* finalizer = NULL;
146 MonoDomain *caller_domain = mono_domain_get ();
148 RuntimeInvokeFunction runtime_invoke;
150 o = (MonoObject*)((char*)obj + GPOINTER_TO_UINT (data));
153 g_log ("mono-gc-finalizers", G_LOG_LEVEL_DEBUG, "<%s at %p> Starting finalizer checks.", o->vtable->klass->name, o);
155 if (suspend_finalizers)
158 domain = o->vtable->domain;
161 mono_domain_finalizers_lock (domain);
163 o2 = g_hash_table_lookup (domain->finalizable_objects_hash, o);
165 mono_domain_finalizers_unlock (domain);
168 /* Already finalized somehow */
172 /* make sure the finalizer is not called again if the object is resurrected */
173 object_register_finalizer (obj, NULL);
176 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Registered finalizer as processed.", o->vtable->klass->name, o);
178 if (o->vtable->klass == mono_defaults.internal_thread_class) {
179 MonoInternalThread *t = (MonoInternalThread*)o;
181 if (mono_gc_is_finalizer_internal_thread (t))
182 /* Avoid finalizing ourselves */
185 if (t->threadpool_thread && finalizing_root_domain) {
186 /* Don't finalize threadpool threads when
187 shutting down - they're finalized when the
188 threadpool shuts down. */
189 add_thread_to_finalize (t);
194 if (o->vtable->klass->image == mono_defaults.corlib && !strcmp (o->vtable->klass->name, "DynamicMethod") && finalizing_root_domain) {
196 * These can't be finalized during unloading/shutdown, since that would
197 * free the native code which can still be referenced by other
199 * FIXME: This is not perfect, objects dying at the same time as
200 * dynamic methods can still reference them even when !shutdown.
205 if (mono_runtime_get_no_exec ())
208 /* speedup later... and use a timeout */
209 /* g_print ("Finalize run on %p %s.%s\n", o, mono_object_class (o)->name_space, mono_object_class (o)->name); */
211 /* Use _internal here, since this thread can enter a doomed appdomain */
212 mono_domain_set_internal (mono_object_domain (o));
214 /* delegates that have a native function pointer allocated are
215 * registered for finalization, but they don't have a Finalize
216 * method, because in most cases it's not needed and it's just a waste.
218 if (o->vtable->klass->delegate) {
219 MonoDelegate* del = (MonoDelegate*)o;
220 if (del->delegate_trampoline)
221 mono_delegate_free_ftnptr ((MonoDelegate*)o);
222 mono_domain_set_internal (caller_domain);
226 finalizer = mono_class_get_finalizer (o->vtable->klass);
229 /* If object has a CCW but has no finalizer, it was only
230 * registered for finalization in order to free the CCW.
231 * Else it needs the regular finalizer run.
232 * FIXME: what to do about ressurection and suppression
233 * of finalizer on object with CCW.
235 if (mono_marshal_free_ccw (o) && !finalizer) {
236 mono_domain_set_internal (caller_domain);
242 * To avoid the locking plus the other overhead of mono_runtime_invoke (),
243 * create and precompile a wrapper which calls the finalize method using
247 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Compiling finalizer.", o->vtable->klass->name, o);
249 if (!domain->finalize_runtime_invoke) {
250 MonoMethod *invoke = mono_marshal_get_runtime_invoke (mono_class_get_method_from_name_flags (mono_defaults.object_class, "Finalize", 0, 0), TRUE);
252 domain->finalize_runtime_invoke = mono_compile_method (invoke);
255 runtime_invoke = domain->finalize_runtime_invoke;
257 mono_runtime_class_init (o->vtable);
259 if (G_UNLIKELY (MONO_GC_FINALIZE_INVOKE_ENABLED ())) {
260 MONO_GC_FINALIZE_INVOKE ((unsigned long)o, mono_object_get_size (o),
261 o->vtable->klass->name_space, o->vtable->klass->name);
265 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Calling finalizer.", o->vtable->klass->name, o);
267 runtime_invoke (o, NULL, &exc, NULL);
270 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Returned from finalizer.", o->vtable->klass->name, o);
273 mono_internal_thread_unhandled_exception (exc);
275 mono_domain_set_internal (caller_domain);
279 mono_gc_finalize_threadpool_threads (void)
281 while (threads_to_finalize) {
282 MonoInternalThread *thread = (MonoInternalThread*) mono_mlist_get_data (threads_to_finalize);
284 /* Force finalization of the thread. */
285 thread->threadpool_thread = FALSE;
286 mono_object_register_finalizer ((MonoObject*)thread);
288 mono_gc_run_finalize (thread, NULL);
290 threads_to_finalize = mono_mlist_next (threads_to_finalize);
295 mono_gc_out_of_memory (size_t size)
298 * we could allocate at program startup some memory that we could release
299 * back to the system at this point if we're really low on memory (ie, size is
300 * lower than the memory we set apart)
302 mono_raise_exception (mono_domain_get ()->out_of_memory_ex);
308 * Some of our objects may point to a different address than the address returned by GC_malloc()
309 * (because of the GetHashCode hack), but we need to pass the real address to register_finalizer.
310 * This also means that in the callback we need to adjust the pointer to get back the real
312 * We also need to be consistent in the use of the GC_debug* variants of malloc and register_finalizer,
313 * since that, too, can cause the underlying pointer to be offset.
316 object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*))
321 mono_raise_exception (mono_get_exception_argument_null ("obj"));
323 domain = obj->vtable->domain;
326 if (mono_domain_is_unloading (domain) && (callback != NULL))
328 * Can't register finalizers in a dying appdomain, since they
329 * could be invoked after the appdomain has been unloaded.
333 mono_domain_finalizers_lock (domain);
336 g_hash_table_insert (domain->finalizable_objects_hash, obj, obj);
338 g_hash_table_remove (domain->finalizable_objects_hash, obj);
340 mono_domain_finalizers_unlock (domain);
342 mono_gc_register_for_finalization (obj, callback);
343 #elif defined(HAVE_SGEN_GC)
345 * If we register finalizers for domains that are unloading we might
346 * end up running them while or after the domain is being cleared, so
347 * the objects will not be valid anymore.
349 if (!mono_domain_is_unloading (domain))
350 mono_gc_register_for_finalization (obj, callback);
355 * mono_object_register_finalizer:
356 * @obj: object to register
358 * Records that object @obj has a finalizer, this will call the
359 * Finalize method when the garbage collector disposes the object.
363 mono_object_register_finalizer (MonoObject *obj)
365 /* g_print ("Registered finalizer on %p %s.%s\n", obj, mono_object_class (obj)->name_space, mono_object_class (obj)->name); */
366 object_register_finalizer (obj, mono_gc_run_finalize);
370 * mono_domain_finalize:
371 * @domain: the domain to finalize
372 * @timeout: msects to wait for the finalization to complete, -1 to wait indefinitely
374 * Request finalization of all finalizable objects inside @domain. Wait
375 * @timeout msecs for the finalization to complete.
377 * Returns: TRUE if succeeded, FALSE if there was a timeout
381 mono_domain_finalize (MonoDomain *domain, guint32 timeout)
383 DomainFinalizationReq *req;
385 mono_mutex_t done_mutex;
386 mono_cond_t done_cond;
388 #if defined(__native_client__)
392 if (mono_thread_internal_current () == gc_thread)
393 /* We are called from inside a finalizer, not much we can do here */
397 * No need to create another thread 'cause the finalizer thread
398 * is still working and will take care of running the finalizers
405 mono_gc_collect (mono_gc_max_generation ());
407 mono_mutex_init (&done_mutex);
408 mono_cond_init (&done_cond, 0);
410 req = g_new0 (DomainFinalizationReq, 1);
411 req->domain = domain;
412 req->done_mutex = &done_mutex;
413 req->done_cond = &done_cond;
416 if (domain == mono_get_root_domain ())
417 finalizing_root_domain = TRUE;
419 mono_finalizer_lock ();
421 domains_to_finalize = g_slist_append (domains_to_finalize, req);
423 mono_finalizer_unlock ();
425 /* Tell the finalizer thread to finalize this appdomain */
426 mono_gc_finalize_notify ();
433 mono_mutex_lock (&done_mutex);
435 res = guarded_cond_wait (&done_cond, &done_mutex, timeout);
436 /* printf ("WAIT RES: %d.\n", res); */
438 mono_mutex_unlock (&done_mutex);
440 /* We leak memory here, since the finalizer thread might still be using it */
446 mono_mutex_destroy (&done_mutex);
447 mono_cond_destroy (&done_cond);
450 if (domain == mono_get_root_domain ()) {
451 mono_thread_pool_cleanup ();
452 mono_gc_finalize_threadpool_threads ();
457 /* We don't support domain finalization without a GC */
463 ves_icall_System_GC_InternalCollect (int generation)
465 mono_gc_collect (generation);
469 ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection)
472 mono_gc_collect (mono_gc_max_generation ());
473 return mono_gc_get_used_size ();
477 ves_icall_System_GC_KeepAlive (MonoObject *obj)
485 ves_icall_System_GC_ReRegisterForFinalize (MonoObject *obj)
487 MONO_CHECK_ARG_NULL (obj,);
489 object_register_finalizer (obj, mono_gc_run_finalize);
493 ves_icall_System_GC_SuppressFinalize (MonoObject *obj)
495 MONO_CHECK_ARG_NULL (obj,);
497 /* delegates have no finalizers, but we register them to deal with the
498 * unmanaged->managed trampoline. We don't let the user suppress it
499 * otherwise we'd leak it.
501 if (obj->vtable->klass->delegate)
504 /* FIXME: Need to handle case where obj has COM Callable Wrapper
505 * generated for it that needs cleaned up, but user wants to suppress
506 * their derived object finalizer. */
508 object_register_finalizer (obj, NULL);
512 ves_icall_System_GC_WaitForPendingFinalizers (void)
515 if (!mono_gc_pending_finalizers ())
518 if (mono_thread_internal_current () == gc_thread)
519 /* Avoid deadlocks */
523 If the finalizer thread is not live, lets pretend no finalizers are pending since the current thread might
524 be the one responsible for starting it up.
526 if (gc_thread == NULL)
529 ResetEvent (pending_done_event);
530 mono_gc_finalize_notify ();
531 /* g_print ("Waiting for pending finalizers....\n"); */
532 guarded_wait (pending_done_event, INFINITE, TRUE);
533 /* g_print ("Done pending....\n"); */
538 ves_icall_System_GC_register_ephemeron_array (MonoObject *array)
541 if (!mono_gc_ephemeron_array_add (array)) {
542 mono_set_pending_exception (mono_object_domain (array)->out_of_memory_ex);
549 ves_icall_System_GC_get_ephemeron_tombstone (void)
551 return mono_domain_get ()->ephemeron_tombstone;
554 #define mono_allocator_lock() mono_mutex_lock (&allocator_section)
555 #define mono_allocator_unlock() mono_mutex_unlock (&allocator_section)
556 static mono_mutex_t allocator_section;
557 static mono_mutex_t handle_section;
566 static HandleType mono_gchandle_get_type (guint32 gchandle);
569 ves_icall_System_GCHandle_GetTarget (guint32 handle)
571 return mono_gchandle_get_target (handle);
575 * if type == -1, change the target of the handle, otherwise allocate a new handle.
578 ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type)
581 mono_gchandle_set_target (handle, obj);
582 /* the handle doesn't change */
587 return mono_gchandle_new_weakref (obj, FALSE);
588 case HANDLE_WEAK_TRACK:
589 return mono_gchandle_new_weakref (obj, TRUE);
591 return mono_gchandle_new (obj, FALSE);
593 return mono_gchandle_new (obj, TRUE);
595 g_assert_not_reached ();
601 ves_icall_System_GCHandle_FreeHandle (guint32 handle)
603 mono_gchandle_free (handle);
607 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
611 if (mono_gchandle_get_type (handle) != HANDLE_PINNED)
613 obj = mono_gchandle_get_target (handle);
615 MonoClass *klass = mono_object_class (obj);
616 if (klass == mono_defaults.string_class) {
617 return mono_string_chars ((MonoString*)obj);
618 } else if (klass->rank) {
619 return mono_array_addr ((MonoArray*)obj, char, 0);
621 /* the C# code will check and throw the exception */
622 /* FIXME: missing !klass->blittable test, see bug #61134 */
623 if ((klass->flags & TYPE_ATTRIBUTE_LAYOUT_MASK) == TYPE_ATTRIBUTE_AUTO_LAYOUT)
625 return (char*)obj + sizeof (MonoObject);
632 ves_icall_Mono_Runtime_SetGCAllowSynchronousMajor (MonoBoolean flag)
634 return mono_gc_set_allow_synchronous_major (flag);
642 guint slot_hint : 24; /* starting slot for search */
643 /* 2^16 appdomains should be enough for everyone (though I know I'll regret this in 20 years) */
644 /* we alloc this only for weak refs, since we can get the domain directly in the other cases */
648 /* weak and weak-track arrays will be allocated in malloc memory
650 static HandleData gc_handles [] = {
651 {NULL, NULL, 0, HANDLE_WEAK, 0},
652 {NULL, NULL, 0, HANDLE_WEAK_TRACK, 0},
653 {NULL, NULL, 0, HANDLE_NORMAL, 0},
654 {NULL, NULL, 0, HANDLE_PINNED, 0}
657 #define lock_handles(handles) mono_mutex_lock (&handle_section)
658 #define unlock_handles(handles) mono_mutex_unlock (&handle_section)
661 find_first_unset (guint32 bitmap)
664 for (i = 0; i < 32; ++i) {
665 if (!(bitmap & (1 << i)))
672 make_root_descr_all_refs (int numbits, gboolean pinned)
678 return mono_gc_make_root_descr_all_refs (numbits);
682 alloc_handle (HandleData *handles, MonoObject *obj, gboolean track)
686 lock_handles (handles);
687 if (!handles->size) {
689 if (handles->type > HANDLE_WEAK_TRACK) {
690 handles->entries = mono_gc_alloc_fixed (sizeof (gpointer) * handles->size, make_root_descr_all_refs (handles->size, handles->type == HANDLE_PINNED));
692 handles->entries = g_malloc0 (sizeof (gpointer) * handles->size);
693 handles->domain_ids = g_malloc0 (sizeof (guint16) * handles->size);
695 handles->bitmap = g_malloc0 (handles->size / 8);
698 for (slot = handles->slot_hint; slot < handles->size / 32; ++slot) {
699 if (handles->bitmap [slot] != 0xffffffff) {
700 i = find_first_unset (handles->bitmap [slot]);
701 handles->slot_hint = slot;
705 if (i == -1 && handles->slot_hint != 0) {
706 for (slot = 0; slot < handles->slot_hint; ++slot) {
707 if (handles->bitmap [slot] != 0xffffffff) {
708 i = find_first_unset (handles->bitmap [slot]);
709 handles->slot_hint = slot;
716 guint32 new_size = handles->size * 2; /* always double: we memset to 0 based on this below */
718 /* resize and copy the bitmap */
719 new_bitmap = g_malloc0 (new_size / 8);
720 memcpy (new_bitmap, handles->bitmap, handles->size / 8);
721 g_free (handles->bitmap);
722 handles->bitmap = new_bitmap;
724 /* resize and copy the entries */
725 if (handles->type > HANDLE_WEAK_TRACK) {
728 entries = mono_gc_alloc_fixed (sizeof (gpointer) * new_size, make_root_descr_all_refs (new_size, handles->type == HANDLE_PINNED));
729 mono_gc_memmove_aligned (entries, handles->entries, sizeof (gpointer) * handles->size);
731 mono_gc_free_fixed (handles->entries);
732 handles->entries = entries;
736 domain_ids = g_malloc0 (sizeof (guint16) * new_size);
737 entries = g_malloc0 (sizeof (gpointer) * new_size);
738 memcpy (domain_ids, handles->domain_ids, sizeof (guint16) * handles->size);
739 for (i = 0; i < handles->size; ++i) {
740 MonoObject *obj = mono_gc_weak_link_get (&(handles->entries [i]));
742 mono_gc_weak_link_add (&(entries [i]), obj, track);
743 mono_gc_weak_link_remove (&(handles->entries [i]), track);
745 g_assert (!handles->entries [i]);
748 g_free (handles->entries);
749 g_free (handles->domain_ids);
750 handles->entries = entries;
751 handles->domain_ids = domain_ids;
754 /* set i and slot to the next free position */
756 slot = (handles->size + 1) / 32;
757 handles->slot_hint = handles->size + 1;
758 handles->size = new_size;
760 handles->bitmap [slot] |= 1 << i;
761 slot = slot * 32 + i;
762 handles->entries [slot] = NULL;
763 if (handles->type <= HANDLE_WEAK_TRACK) {
764 /*FIXME, what to use when obj == null?*/
765 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
767 mono_gc_weak_link_add (&(handles->entries [slot]), obj, track);
769 handles->entries [slot] = obj;
772 #ifndef DISABLE_PERFCOUNTERS
773 mono_perfcounters->gc_num_handles++;
775 unlock_handles (handles);
776 /*g_print ("allocated entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
777 res = (slot << 3) | (handles->type + 1);
778 mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_CREATED, handles->type, res, obj);
784 * @obj: managed object to get a handle for
785 * @pinned: whether the object should be pinned
787 * This returns a handle that wraps the object, this is used to keep a
788 * reference to a managed object from the unmanaged world and preventing the
789 * object from being disposed.
791 * If @pinned is false the address of the object can not be obtained, if it is
792 * true the address of the object can be obtained. This will also pin the
793 * object so it will not be possible by a moving garbage collector to move the
796 * Returns: a handle that can be used to access the object from
800 mono_gchandle_new (MonoObject *obj, gboolean pinned)
802 return alloc_handle (&gc_handles [pinned? HANDLE_PINNED: HANDLE_NORMAL], obj, FALSE);
806 * mono_gchandle_new_weakref:
807 * @obj: managed object to get a handle for
808 * @pinned: whether the object should be pinned
810 * This returns a weak handle that wraps the object, this is used to
811 * keep a reference to a managed object from the unmanaged world.
812 * Unlike the mono_gchandle_new the object can be reclaimed by the
813 * garbage collector. In this case the value of the GCHandle will be
816 * If @pinned is false the address of the object can not be obtained, if it is
817 * true the address of the object can be obtained. This will also pin the
818 * object so it will not be possible by a moving garbage collector to move the
821 * Returns: a handle that can be used to access the object from
825 mono_gchandle_new_weakref (MonoObject *obj, gboolean track_resurrection)
827 guint32 handle = alloc_handle (&gc_handles [track_resurrection? HANDLE_WEAK_TRACK: HANDLE_WEAK], obj, track_resurrection);
833 mono_gchandle_get_type (guint32 gchandle)
835 guint type = (gchandle & 7) - 1;
841 * mono_gchandle_get_target:
842 * @gchandle: a GCHandle's handle.
844 * The handle was previously created by calling mono_gchandle_new or
845 * mono_gchandle_new_weakref.
847 * Returns a pointer to the MonoObject represented by the handle or
848 * NULL for a collected object if using a weakref handle.
851 mono_gchandle_get_target (guint32 gchandle)
853 guint slot = gchandle >> 3;
854 guint type = (gchandle & 7) - 1;
855 HandleData *handles = &gc_handles [type];
856 MonoObject *obj = NULL;
859 lock_handles (handles);
860 if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
861 if (handles->type <= HANDLE_WEAK_TRACK) {
862 obj = mono_gc_weak_link_get (&handles->entries [slot]);
864 obj = handles->entries [slot];
867 /* print a warning? */
869 unlock_handles (handles);
870 /*g_print ("get target of entry %d of type %d: %p\n", slot, handles->type, obj);*/
875 mono_gchandle_set_target (guint32 gchandle, MonoObject *obj)
877 guint slot = gchandle >> 3;
878 guint type = (gchandle & 7) - 1;
879 HandleData *handles = &gc_handles [type];
883 lock_handles (handles);
884 if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
885 if (handles->type <= HANDLE_WEAK_TRACK) {
886 if (handles->entries [slot])
887 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
889 mono_gc_weak_link_add (&handles->entries [slot], obj, handles->type == HANDLE_WEAK_TRACK);
890 /*FIXME, what to use when obj == null?*/
891 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
893 handles->entries [slot] = obj;
896 /* print a warning? */
898 /*g_print ("changed entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
899 unlock_handles (handles);
903 * mono_gchandle_is_in_domain:
904 * @gchandle: a GCHandle's handle.
905 * @domain: An application domain.
907 * Returns: true if the object wrapped by the @gchandle belongs to the specific @domain.
910 mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain)
912 guint slot = gchandle >> 3;
913 guint type = (gchandle & 7) - 1;
914 HandleData *handles = &gc_handles [type];
915 gboolean result = FALSE;
918 lock_handles (handles);
919 if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
920 if (handles->type <= HANDLE_WEAK_TRACK) {
921 result = domain->domain_id == handles->domain_ids [slot];
924 obj = handles->entries [slot];
928 result = domain == mono_object_domain (obj);
931 /* print a warning? */
933 unlock_handles (handles);
938 * mono_gchandle_free:
939 * @gchandle: a GCHandle's handle.
941 * Frees the @gchandle handle. If there are no outstanding
942 * references, the garbage collector can reclaim the memory of the
946 mono_gchandle_free (guint32 gchandle)
948 guint slot = gchandle >> 3;
949 guint type = (gchandle & 7) - 1;
950 HandleData *handles = &gc_handles [type];
954 lock_handles (handles);
955 if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
956 if (handles->type <= HANDLE_WEAK_TRACK) {
957 if (handles->entries [slot])
958 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
960 handles->entries [slot] = NULL;
962 handles->bitmap [slot / 32] &= ~(1 << (slot % 32));
964 /* print a warning? */
966 #ifndef DISABLE_PERFCOUNTERS
967 mono_perfcounters->gc_num_handles--;
969 /*g_print ("freed entry %d of type %d\n", slot, handles->type);*/
970 unlock_handles (handles);
971 mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_DESTROYED, handles->type, gchandle, NULL);
975 * mono_gchandle_free_domain:
976 * @domain: domain that is unloading
978 * Function used internally to cleanup any GC handle for objects belonging
979 * to the specified domain during appdomain unload.
982 mono_gchandle_free_domain (MonoDomain *domain)
986 for (type = 0; type < 3; ++type) {
988 HandleData *handles = &gc_handles [type];
989 lock_handles (handles);
990 for (slot = 0; slot < handles->size; ++slot) {
991 if (!(handles->bitmap [slot / 32] & (1 << (slot % 32))))
993 if (type <= HANDLE_WEAK_TRACK) {
994 if (domain->domain_id == handles->domain_ids [slot]) {
995 handles->bitmap [slot / 32] &= ~(1 << (slot % 32));
996 if (handles->entries [slot])
997 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1000 if (handles->entries [slot] && mono_object_domain (handles->entries [slot]) == domain) {
1001 handles->bitmap [slot / 32] &= ~(1 << (slot % 32));
1002 handles->entries [slot] = NULL;
1006 unlock_handles (handles);
1012 mono_gc_GCHandle_CheckCurrentDomain (guint32 gchandle)
1014 return mono_gchandle_is_in_domain (gchandle, mono_domain_get ());
1017 #ifndef HAVE_NULL_GC
1019 #ifdef MONO_HAS_SEMAPHORES
1020 static MonoSemType finalizer_sem;
1022 static HANDLE finalizer_event;
1023 static volatile gboolean finished=FALSE;
1026 mono_gc_finalize_notify (void)
1029 g_message ( "%s: prodding finalizer", __func__);
1032 #ifdef MONO_HAS_SEMAPHORES
1033 MONO_SEM_POST (&finalizer_sem);
1035 SetEvent (finalizer_event);
1039 #ifdef HAVE_BOEHM_GC
1042 collect_objects (gpointer key, gpointer value, gpointer user_data)
1044 GPtrArray *arr = (GPtrArray*)user_data;
1045 g_ptr_array_add (arr, key);
1051 * finalize_domain_objects:
1053 * Run the finalizers of all finalizable objects in req->domain.
1056 finalize_domain_objects (DomainFinalizationReq *req)
1058 MonoDomain *domain = req->domain;
1061 #define NUM_FOBJECTS 64
1062 MonoObject *to_finalize [NUM_FOBJECTS];
1066 /* Process finalizers which are already in the queue */
1067 mono_gc_invoke_finalizers ();
1069 #ifdef HAVE_BOEHM_GC
1070 while (g_hash_table_size (domain->finalizable_objects_hash) > 0) {
1074 * Since the domain is unloading, nobody is allowed to put
1075 * new entries into the hash table. But finalize_object might
1076 * remove entries from the hash table, so we make a copy.
1078 objs = g_ptr_array_new ();
1079 g_hash_table_foreach (domain->finalizable_objects_hash, collect_objects, objs);
1080 /* printf ("FINALIZING %d OBJECTS.\n", objs->len); */
1082 for (i = 0; i < objs->len; ++i) {
1083 MonoObject *o = (MonoObject*)g_ptr_array_index (objs, i);
1084 /* FIXME: Avoid finalizing threads, etc */
1085 mono_gc_run_finalize (o, 0);
1088 g_ptr_array_free (objs, TRUE);
1090 #elif defined(HAVE_SGEN_GC)
1091 while ((count = mono_gc_finalizers_for_domain (domain, to_finalize, NUM_FOBJECTS))) {
1093 for (i = 0; i < count; ++i) {
1094 mono_gc_run_finalize (to_finalize [i], 0);
1099 /* cleanup the reference queue */
1100 reference_queue_clear_for_domain (domain);
1102 /* printf ("DONE.\n"); */
1104 mono_mutex_lock (req->done_mutex);
1106 mono_cond_signal (req->done_cond);
1107 mono_mutex_unlock (req->done_mutex);
1111 finalizer_thread (gpointer unused)
1113 gboolean wait = TRUE;
1116 /* Wait to be notified that there's at least one
1120 g_assert (mono_domain_get () == mono_get_root_domain ());
1121 mono_gc_set_skip_thread (TRUE);
1122 MONO_PREPARE_BLOCKING
1125 /* An alertable wait is required so this thread can be suspended on windows */
1126 #ifdef MONO_HAS_SEMAPHORES
1127 MONO_SEM_WAIT_ALERTABLE (&finalizer_sem, TRUE);
1129 WaitForSingleObjectEx (finalizer_event, INFINITE, TRUE);
1133 MONO_FINISH_BLOCKING
1134 mono_gc_set_skip_thread (FALSE);
1136 mono_threads_perform_thread_dump ();
1138 mono_console_handle_async_ops ();
1140 #ifndef DISABLE_ATTACH
1141 mono_attach_maybe_start ();
1144 if (domains_to_finalize) {
1145 mono_finalizer_lock ();
1146 if (domains_to_finalize) {
1147 DomainFinalizationReq *req = domains_to_finalize->data;
1148 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
1149 mono_finalizer_unlock ();
1151 finalize_domain_objects (req);
1153 mono_finalizer_unlock ();
1157 /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup),
1158 * before the domain is unloaded.
1160 mono_gc_invoke_finalizers ();
1162 mono_threads_join_threads ();
1164 reference_queue_proccess_all ();
1166 #ifdef MONO_HAS_SEMAPHORES
1167 /* Avoid posting the pending done event until there are pending finalizers */
1168 if (MONO_SEM_TIMEDWAIT (&finalizer_sem, 0) == 0)
1169 /* Don't wait again at the start of the loop */
1172 SetEvent (pending_done_event);
1174 SetEvent (pending_done_event);
1178 mono_finalizer_lock ();
1179 finalizer_thread_exited = TRUE;
1180 mono_cond_signal (&exited_cond);
1181 mono_finalizer_unlock ();
1186 #ifndef LAZY_GC_THREAD_CREATION
1190 mono_gc_init_finalizer_thread (void)
1192 gc_thread = mono_thread_create_internal (mono_domain_get (), finalizer_thread, NULL, FALSE, 0);
1193 ves_icall_System_Threading_Thread_SetName_internal (gc_thread, mono_string_new (mono_domain_get (), "Finalizer"));
1199 mono_mutex_init_recursive (&handle_section);
1200 mono_mutex_init_recursive (&allocator_section);
1202 mono_mutex_init_recursive (&finalizer_mutex);
1203 mono_mutex_init_recursive (&reference_queue_mutex);
1205 MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_NORMAL].entries);
1206 MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_PINNED].entries);
1208 mono_counters_register ("Minor GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.minor_gc_count);
1209 mono_counters_register ("Major GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.major_gc_count);
1210 mono_counters_register ("Minor GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.minor_gc_time);
1211 mono_counters_register ("Major GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time);
1212 mono_counters_register ("Major GC time concurrent", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time_concurrent);
1214 mono_gc_base_init ();
1216 if (mono_gc_is_disabled ()) {
1221 finalizer_event = CreateEvent (NULL, FALSE, FALSE, NULL);
1222 g_assert (finalizer_event);
1223 pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
1224 g_assert (pending_done_event);
1225 mono_cond_init (&exited_cond, 0);
1226 #ifdef MONO_HAS_SEMAPHORES
1227 MONO_SEM_INIT (&finalizer_sem, 0);
1230 #ifndef LAZY_GC_THREAD_CREATION
1231 mono_gc_init_finalizer_thread ();
1236 mono_gc_cleanup (void)
1239 g_message ("%s: cleaning up finalizer", __func__);
1244 if (mono_thread_internal_current () != gc_thread) {
1245 gboolean timed_out = FALSE;
1246 guint32 start_ticks = mono_msec_ticks ();
1247 guint32 end_ticks = start_ticks + 2000;
1249 mono_gc_finalize_notify ();
1250 /* Finishing the finalizer thread, so wait a little bit... */
1251 /* MS seems to wait for about 2 seconds */
1252 while (!finalizer_thread_exited) {
1253 guint32 current_ticks = mono_msec_ticks ();
1256 if (current_ticks >= end_ticks)
1259 timeout = end_ticks - current_ticks;
1260 MONO_PREPARE_BLOCKING;
1261 mono_finalizer_lock ();
1262 if (!finalizer_thread_exited)
1263 mono_cond_timedwait_ms (&exited_cond, &finalizer_mutex, timeout);
1264 mono_finalizer_unlock ();
1265 MONO_FINISH_BLOCKING;
1268 if (!finalizer_thread_exited) {
1271 /* Set a flag which the finalizer thread can check */
1272 suspend_finalizers = TRUE;
1274 /* Try to abort the thread, in the hope that it is running managed code */
1275 mono_thread_internal_stop (gc_thread);
1277 /* Wait for it to stop */
1278 ret = guarded_wait (gc_thread->handle, 100, TRUE);
1280 if (ret == WAIT_TIMEOUT) {
1282 * The finalizer thread refused to die. There is not much we
1283 * can do here, since the runtime is shutting down so the
1284 * state the finalizer thread depends on will vanish.
1286 g_warning ("Shutting down finalizer thread timed out.");
1294 /* Wait for the thread to actually exit */
1295 ret = guarded_wait (gc_thread->handle, INFINITE, TRUE);
1296 g_assert (ret == WAIT_OBJECT_0);
1298 mono_thread_join (GUINT_TO_POINTER (gc_thread->tid));
1302 #ifdef HAVE_BOEHM_GC
1303 GC_finalizer_notifier = NULL;
1307 mono_reference_queue_cleanup ();
1309 mono_mutex_destroy (&handle_section);
1310 mono_mutex_destroy (&allocator_section);
1311 mono_mutex_destroy (&finalizer_mutex);
1312 mono_mutex_destroy (&reference_queue_mutex);
1317 /* Null GC dummy functions */
1319 mono_gc_finalize_notify (void)
1323 void mono_gc_init (void)
1325 mono_mutex_init_recursive (&handle_section);
1328 void mono_gc_cleanup (void)
1335 mono_gc_is_finalizer_internal_thread (MonoInternalThread *thread)
1337 return thread == gc_thread;
1341 * mono_gc_is_finalizer_thread:
1342 * @thread: the thread to test.
1344 * In Mono objects are finalized asynchronously on a separate thread.
1345 * This routine tests whether the @thread argument represents the
1346 * finalization thread.
1348 * Returns true if @thread is the finalization thread.
1351 mono_gc_is_finalizer_thread (MonoThread *thread)
1353 return mono_gc_is_finalizer_internal_thread (thread->internal_thread);
1356 #if defined(__MACH__)
1357 static pthread_t mach_exception_thread;
1360 mono_gc_register_mach_exception_thread (pthread_t thread)
1362 mach_exception_thread = thread;
1366 mono_gc_get_mach_exception_thread (void)
1368 return mach_exception_thread;
1372 #ifndef HAVE_SGEN_GC
1374 mono_gc_alloc_mature (MonoVTable *vtable)
1376 return mono_object_new_specific (vtable);
1381 static MonoReferenceQueue *ref_queues;
1384 ref_list_remove_element (RefQueueEntry **prev, RefQueueEntry *element)
1387 /* Guard if head is changed concurrently. */
1388 while (*prev != element)
1389 prev = &(*prev)->next;
1390 } while (prev && InterlockedCompareExchangePointer ((void*)prev, element->next, element) != element);
1394 ref_list_push (RefQueueEntry **head, RefQueueEntry *value)
1396 RefQueueEntry *current;
1399 value->next = current;
1400 STORE_STORE_FENCE; /*Must make sure the previous store is visible before the CAS. */
1401 } while (InterlockedCompareExchangePointer ((void*)head, value, current) != current);
1405 reference_queue_proccess (MonoReferenceQueue *queue)
1407 RefQueueEntry **iter = &queue->queue;
1408 RefQueueEntry *entry;
1409 while ((entry = *iter)) {
1411 if (queue->should_be_deleted || !mono_gc_weak_link_get (&entry->dis_link)) {
1412 mono_gc_weak_link_remove (&entry->dis_link, TRUE);
1414 if (queue->should_be_deleted || !mono_gchandle_get_target (entry->gchandle)) {
1415 mono_gchandle_free ((guint32)entry->gchandle);
1417 ref_list_remove_element (iter, entry);
1418 queue->callback (entry->user_data);
1421 iter = &entry->next;
1427 reference_queue_proccess_all (void)
1429 MonoReferenceQueue **iter;
1430 MonoReferenceQueue *queue = ref_queues;
1431 for (; queue; queue = queue->next)
1432 reference_queue_proccess (queue);
1435 mono_mutex_lock (&reference_queue_mutex);
1436 for (iter = &ref_queues; *iter;) {
1438 if (!queue->should_be_deleted) {
1439 iter = &queue->next;
1443 mono_mutex_unlock (&reference_queue_mutex);
1444 reference_queue_proccess (queue);
1447 *iter = queue->next;
1450 mono_mutex_unlock (&reference_queue_mutex);
1454 mono_reference_queue_cleanup (void)
1456 MonoReferenceQueue *queue = ref_queues;
1457 for (; queue; queue = queue->next)
1458 queue->should_be_deleted = TRUE;
1459 reference_queue_proccess_all ();
1463 reference_queue_clear_for_domain (MonoDomain *domain)
1465 MonoReferenceQueue *queue = ref_queues;
1466 for (; queue; queue = queue->next) {
1467 RefQueueEntry **iter = &queue->queue;
1468 RefQueueEntry *entry;
1469 while ((entry = *iter)) {
1470 if (entry->domain == domain) {
1472 mono_gc_weak_link_remove (&entry->dis_link, TRUE);
1474 mono_gchandle_free ((guint32)entry->gchandle);
1476 ref_list_remove_element (iter, entry);
1477 queue->callback (entry->user_data);
1480 iter = &entry->next;
1486 * mono_gc_reference_queue_new:
1487 * @callback callback used when processing collected entries.
1489 * Create a new reference queue used to process collected objects.
1490 * A reference queue let you add a pair of (managed object, user data)
1491 * using the mono_gc_reference_queue_add method.
1493 * Once the managed object is collected @callback will be called
1494 * in the finalizer thread with 'user data' as argument.
1496 * The callback is called from the finalizer thread without any locks held.
1497 * When a AppDomain is unloaded, all callbacks for objects belonging to it
1500 * @returns the new queue.
1503 mono_gc_reference_queue_new (mono_reference_queue_callback callback)
1505 MonoReferenceQueue *res = g_new0 (MonoReferenceQueue, 1);
1506 res->callback = callback;
1508 mono_mutex_lock (&reference_queue_mutex);
1509 res->next = ref_queues;
1511 mono_mutex_unlock (&reference_queue_mutex);
1517 * mono_gc_reference_queue_add:
1518 * @queue the queue to add the reference to.
1519 * @obj the object to be watched for collection
1520 * @user_data parameter to be passed to the queue callback
1522 * Queue an object to be watched for collection, when the @obj is
1523 * collected, the callback that was registered for the @queue will
1524 * be invoked with @user_data as argument.
1526 * @returns false if the queue is scheduled to be freed.
1529 mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *user_data)
1531 RefQueueEntry *entry;
1532 if (queue->should_be_deleted)
1535 entry = g_new0 (RefQueueEntry, 1);
1536 entry->user_data = user_data;
1537 entry->domain = mono_object_domain (obj);
1540 mono_gc_weak_link_add (&entry->dis_link, obj, TRUE);
1542 entry->gchandle = mono_gchandle_new_weakref (obj, TRUE);
1543 mono_object_register_finalizer (obj);
1546 ref_list_push (&queue->queue, entry);
1551 * mono_gc_reference_queue_free:
1552 * @queue the queue that should be freed.
1554 * This operation signals that @queue should be freed. This operation is deferred
1555 * as it happens on the finalizer thread.
1557 * After this call, no further objects can be queued. It's the responsibility of the
1558 * caller to make sure that no further attempt to access queue will be made.
1561 mono_gc_reference_queue_free (MonoReferenceQueue *queue)
1563 queue->should_be_deleted = TRUE;