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-internals.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/threads-types.h>
26 #include <mono/metadata/threadpool-ms.h>
27 #include <mono/sgen/sgen-conf.h>
28 #include <mono/sgen/sgen-gc.h>
29 #include <mono/utils/mono-logger-internals.h>
30 #include <mono/metadata/gc-internals.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-os-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>
41 #include <mono/utils/mono-coop-semaphore.h>
47 typedef struct DomainFinalizationReq {
50 } DomainFinalizationReq;
52 static gboolean gc_disabled = FALSE;
54 static gboolean finalizing_root_domain = FALSE;
56 gboolean log_finalizers = FALSE;
57 gboolean mono_do_not_finalize = FALSE;
58 gchar **mono_do_not_finalize_class_names = NULL;
60 #define mono_finalizer_lock() mono_coop_mutex_lock (&finalizer_mutex)
61 #define mono_finalizer_unlock() mono_coop_mutex_unlock (&finalizer_mutex)
62 static MonoCoopMutex finalizer_mutex;
63 static MonoCoopMutex reference_queue_mutex;
65 static GSList *domains_to_finalize= NULL;
66 static MonoMList *threads_to_finalize = NULL;
68 static gboolean finalizer_thread_exited;
69 /* Uses finalizer_mutex */
70 static MonoCoopCond exited_cond;
72 static MonoInternalThread *gc_thread;
74 static void object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*), MonoError *error);
76 static void reference_queue_proccess_all (void);
77 static void mono_reference_queue_cleanup (void);
78 static void reference_queue_clear_for_domain (MonoDomain *domain);
79 static HANDLE pending_done_event;
82 guarded_wait (HANDLE handle, guint32 timeout, gboolean alertable)
86 MONO_PREPARE_BLOCKING;
87 result = WaitForSingleObjectEx (handle, timeout, alertable);
94 add_thread_to_finalize (MonoInternalThread *thread)
96 mono_finalizer_lock ();
97 if (!threads_to_finalize)
98 MONO_GC_REGISTER_ROOT_SINGLE (threads_to_finalize, MONO_ROOT_SOURCE_FINALIZER_QUEUE, "finalizable threads list");
99 threads_to_finalize = mono_mlist_append (threads_to_finalize, (MonoObject*)thread);
100 mono_finalizer_unlock ();
103 static gboolean suspend_finalizers = FALSE;
105 * actually, we might want to queue the finalize requests in a separate thread,
106 * but we need to be careful about the execution domain of the thread...
109 mono_gc_run_finalize (void *obj, void *data)
112 MonoObject *exc = NULL;
117 MonoMethod* finalizer = NULL;
118 MonoDomain *caller_domain = mono_domain_get ();
120 RuntimeInvokeFunction runtime_invoke;
122 // This function is called from the innards of the GC, so our best alternative for now is to do polling here
123 mono_threads_safepoint ();
125 o = (MonoObject*)((char*)obj + GPOINTER_TO_UINT (data));
127 if (mono_do_not_finalize) {
128 if (!mono_do_not_finalize_class_names)
131 size_t namespace_len = strlen (o->vtable->klass->name_space);
132 for (int i = 0; mono_do_not_finalize_class_names [i]; ++i) {
133 const char *name = mono_do_not_finalize_class_names [i];
134 if (strncmp (name, o->vtable->klass->name_space, namespace_len))
136 if (name [namespace_len] != '.')
138 if (strcmp (name + namespace_len + 1, o->vtable->klass->name))
145 g_log ("mono-gc-finalizers", G_LOG_LEVEL_DEBUG, "<%s at %p> Starting finalizer checks.", o->vtable->klass->name, o);
147 if (suspend_finalizers)
150 domain = o->vtable->domain;
153 mono_domain_finalizers_lock (domain);
155 o2 = (MonoObject *)g_hash_table_lookup (domain->finalizable_objects_hash, o);
157 mono_domain_finalizers_unlock (domain);
160 /* Already finalized somehow */
164 /* make sure the finalizer is not called again if the object is resurrected */
165 object_register_finalizer ((MonoObject *)obj, NULL, &error);
166 mono_error_assert_ok (&error); /* FIXME don't swallow the error */
169 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Registered finalizer as processed.", o->vtable->klass->name, o);
171 if (o->vtable->klass == mono_defaults.internal_thread_class) {
172 MonoInternalThread *t = (MonoInternalThread*)o;
174 if (mono_gc_is_finalizer_internal_thread (t))
175 /* Avoid finalizing ourselves */
178 if (t->threadpool_thread && finalizing_root_domain) {
179 /* Don't finalize threadpool threads when
180 shutting down - they're finalized when the
181 threadpool shuts down. */
182 add_thread_to_finalize (t);
187 if (o->vtable->klass->image == mono_defaults.corlib && !strcmp (o->vtable->klass->name, "DynamicMethod") && finalizing_root_domain) {
189 * These can't be finalized during unloading/shutdown, since that would
190 * free the native code which can still be referenced by other
192 * FIXME: This is not perfect, objects dying at the same time as
193 * dynamic methods can still reference them even when !shutdown.
198 if (mono_runtime_get_no_exec ())
201 /* speedup later... and use a timeout */
202 /* g_print ("Finalize run on %p %s.%s\n", o, mono_object_class (o)->name_space, mono_object_class (o)->name); */
204 /* Use _internal here, since this thread can enter a doomed appdomain */
205 mono_domain_set_internal (mono_object_domain (o));
207 /* delegates that have a native function pointer allocated are
208 * registered for finalization, but they don't have a Finalize
209 * method, because in most cases it's not needed and it's just a waste.
211 if (o->vtable->klass->delegate) {
212 MonoDelegate* del = (MonoDelegate*)o;
213 if (del->delegate_trampoline)
214 mono_delegate_free_ftnptr ((MonoDelegate*)o);
215 mono_domain_set_internal (caller_domain);
219 finalizer = mono_class_get_finalizer (o->vtable->klass);
221 /* If object has a CCW but has no finalizer, it was only
222 * registered for finalization in order to free the CCW.
223 * Else it needs the regular finalizer run.
224 * FIXME: what to do about ressurection and suppression
225 * of finalizer on object with CCW.
227 if (mono_marshal_free_ccw (o) && !finalizer) {
228 mono_domain_set_internal (caller_domain);
233 * To avoid the locking plus the other overhead of mono_runtime_invoke_checked (),
234 * create and precompile a wrapper which calls the finalize method using
238 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Compiling finalizer.", o->vtable->klass->name, o);
240 if (!domain->finalize_runtime_invoke) {
241 MonoMethod *invoke = mono_marshal_get_runtime_invoke (mono_class_get_method_from_name_flags (mono_defaults.object_class, "Finalize", 0, 0), TRUE);
243 domain->finalize_runtime_invoke = mono_compile_method (invoke);
246 runtime_invoke = (RuntimeInvokeFunction)domain->finalize_runtime_invoke;
248 mono_runtime_class_init_full (o->vtable, &error);
249 mono_error_raise_exception (&error); /* FIXME don't raise here */
251 if (G_UNLIKELY (MONO_GC_FINALIZE_INVOKE_ENABLED ())) {
252 MONO_GC_FINALIZE_INVOKE ((unsigned long)o, mono_object_get_size (o),
253 o->vtable->klass->name_space, o->vtable->klass->name);
257 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Calling finalizer.", o->vtable->klass->name, o);
259 runtime_invoke (o, NULL, &exc, NULL);
262 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Returned from finalizer.", o->vtable->klass->name, o);
265 mono_thread_internal_unhandled_exception (exc);
267 mono_domain_set_internal (caller_domain);
271 mono_gc_finalize_threadpool_threads (void)
274 while (threads_to_finalize) {
275 MonoInternalThread *thread = (MonoInternalThread*) mono_mlist_get_data (threads_to_finalize);
277 /* Force finalization of the thread. */
278 thread->threadpool_thread = FALSE;
279 mono_object_register_finalizer ((MonoObject*)thread, &error);
280 mono_error_assert_ok (&error); /* FIXME don't swallow the error */
282 mono_gc_run_finalize (thread, NULL);
284 threads_to_finalize = mono_mlist_next (threads_to_finalize);
289 mono_gc_out_of_memory (size_t size)
292 * we could allocate at program startup some memory that we could release
293 * back to the system at this point if we're really low on memory (ie, size is
294 * lower than the memory we set apart)
296 mono_raise_exception (mono_domain_get ()->out_of_memory_ex);
302 * Some of our objects may point to a different address than the address returned by GC_malloc()
303 * (because of the GetHashCode hack), but we need to pass the real address to register_finalizer.
304 * This also means that in the callback we need to adjust the pointer to get back the real
306 * We also need to be consistent in the use of the GC_debug* variants of malloc and register_finalizer,
307 * since that, too, can cause the underlying pointer to be offset.
310 object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*), MonoError *error)
314 mono_error_init (error);
317 mono_error_set_argument_null (error, "obj", "");
321 domain = obj->vtable->domain;
324 if (mono_domain_is_unloading (domain) && (callback != NULL))
326 * Can't register finalizers in a dying appdomain, since they
327 * could be invoked after the appdomain has been unloaded.
331 mono_domain_finalizers_lock (domain);
334 g_hash_table_insert (domain->finalizable_objects_hash, obj, obj);
336 g_hash_table_remove (domain->finalizable_objects_hash, obj);
338 mono_domain_finalizers_unlock (domain);
340 mono_gc_register_for_finalization (obj, callback);
341 #elif defined(HAVE_SGEN_GC)
343 * If we register finalizers for domains that are unloading we might
344 * end up running them while or after the domain is being cleared, so
345 * the objects will not be valid anymore.
347 if (!mono_domain_is_unloading (domain))
348 mono_gc_register_for_finalization (obj, callback);
353 * mono_object_register_finalizer:
354 * @obj: object to register
356 * Records that object @obj has a finalizer, this will call the
357 * Finalize method when the garbage collector disposes the object.
361 mono_object_register_finalizer (MonoObject *obj, MonoError *error)
363 /* g_print ("Registered finalizer on %p %s.%s\n", obj, mono_object_class (obj)->name_space, mono_object_class (obj)->name); */
364 object_register_finalizer (obj, mono_gc_run_finalize, error);
368 * mono_domain_finalize:
369 * @domain: the domain to finalize
370 * @timeout: msects to wait for the finalization to complete, -1 to wait indefinitely
372 * Request finalization of all finalizable objects inside @domain. Wait
373 * @timeout msecs for the finalization to complete.
375 * Returns: TRUE if succeeded, FALSE if there was a timeout
379 mono_domain_finalize (MonoDomain *domain, guint32 timeout)
381 DomainFinalizationReq *req;
384 MonoInternalThread *thread = mono_thread_internal_current ();
386 #if defined(__native_client__)
390 if (mono_thread_internal_current () == gc_thread)
391 /* We are called from inside a finalizer, not much we can do here */
395 * No need to create another thread 'cause the finalizer thread
396 * is still working and will take care of running the finalizers
402 /* We don't support domain finalization without a GC */
403 if (mono_gc_is_null ())
406 mono_gc_collect (mono_gc_max_generation ());
408 done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
409 if (done_event == NULL) {
413 req = g_new0 (DomainFinalizationReq, 1);
414 req->domain = domain;
415 req->done_event = done_event;
417 if (domain == mono_get_root_domain ())
418 finalizing_root_domain = TRUE;
420 mono_finalizer_lock ();
422 domains_to_finalize = g_slist_append (domains_to_finalize, req);
424 mono_finalizer_unlock ();
426 /* Tell the finalizer thread to finalize this appdomain */
427 mono_gc_finalize_notify ();
433 res = guarded_wait (done_event, timeout, TRUE);
434 /* printf ("WAIT RES: %d.\n", res); */
436 if (res == WAIT_IO_COMPLETION) {
437 if ((thread->state & (ThreadState_StopRequested | ThreadState_SuspendRequested)) != 0)
439 } else if (res == WAIT_TIMEOUT) {
440 /* We leak the handle here */
447 CloseHandle (done_event);
449 if (domain == mono_get_root_domain ()) {
450 mono_threadpool_ms_cleanup ();
451 mono_gc_finalize_threadpool_threads ();
458 ves_icall_System_GC_InternalCollect (int generation)
460 mono_gc_collect (generation);
464 ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection)
467 mono_gc_collect (mono_gc_max_generation ());
468 return mono_gc_get_used_size ();
472 ves_icall_System_GC_KeepAlive (MonoObject *obj)
480 ves_icall_System_GC_ReRegisterForFinalize (MonoObject *obj)
484 MONO_CHECK_ARG_NULL (obj,);
486 object_register_finalizer (obj, mono_gc_run_finalize, &error);
487 mono_error_set_pending_exception (&error);
491 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, &error);
509 mono_error_set_pending_exception (&error);
513 ves_icall_System_GC_WaitForPendingFinalizers (void)
515 if (mono_gc_is_null ())
518 if (!mono_gc_pending_finalizers ())
521 if (mono_thread_internal_current () == gc_thread)
522 /* Avoid deadlocks */
526 If the finalizer thread is not live, lets pretend no finalizers are pending since the current thread might
527 be the one responsible for starting it up.
529 if (gc_thread == NULL)
532 ResetEvent (pending_done_event);
533 mono_gc_finalize_notify ();
534 /* g_print ("Waiting for pending finalizers....\n"); */
535 guarded_wait (pending_done_event, INFINITE, TRUE);
536 /* g_print ("Done pending....\n"); */
540 ves_icall_System_GC_register_ephemeron_array (MonoObject *array)
543 if (!mono_gc_ephemeron_array_add (array)) {
544 mono_set_pending_exception (mono_object_domain (array)->out_of_memory_ex);
551 ves_icall_System_GC_get_ephemeron_tombstone (void)
553 return mono_domain_get ()->ephemeron_tombstone;
557 ves_icall_System_GCHandle_GetTarget (guint32 handle)
559 return mono_gchandle_get_target (handle);
563 * if type == -1, change the target of the handle, otherwise allocate a new handle.
566 ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type)
569 mono_gchandle_set_target (handle, obj);
570 /* the handle doesn't change */
575 return mono_gchandle_new_weakref (obj, FALSE);
576 case HANDLE_WEAK_TRACK:
577 return mono_gchandle_new_weakref (obj, TRUE);
579 return mono_gchandle_new (obj, FALSE);
581 return mono_gchandle_new (obj, TRUE);
583 g_assert_not_reached ();
589 ves_icall_System_GCHandle_FreeHandle (guint32 handle)
591 mono_gchandle_free (handle);
595 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
599 if (MONO_GC_HANDLE_TYPE (handle) != HANDLE_PINNED)
601 obj = mono_gchandle_get_target (handle);
603 MonoClass *klass = mono_object_class (obj);
604 if (klass == mono_defaults.string_class) {
605 return mono_string_chars ((MonoString*)obj);
606 } else if (klass->rank) {
607 return mono_array_addr ((MonoArray*)obj, char, 0);
609 /* the C# code will check and throw the exception */
610 /* FIXME: missing !klass->blittable test, see bug #61134 */
611 if ((klass->flags & TYPE_ATTRIBUTE_LAYOUT_MASK) == TYPE_ATTRIBUTE_AUTO_LAYOUT)
613 return (char*)obj + sizeof (MonoObject);
620 mono_gc_GCHandle_CheckCurrentDomain (guint32 gchandle)
622 return mono_gchandle_is_in_domain (gchandle, mono_domain_get ());
625 static MonoCoopSem finalizer_sem;
626 static volatile gboolean finished=FALSE;
629 mono_gc_finalize_notify (void)
632 g_message ( "%s: prodding finalizer", __func__);
635 if (mono_gc_is_null ())
638 mono_coop_sem_post (&finalizer_sem);
644 collect_objects (gpointer key, gpointer value, gpointer user_data)
646 GPtrArray *arr = (GPtrArray*)user_data;
647 g_ptr_array_add (arr, key);
653 * finalize_domain_objects:
655 * Run the finalizers of all finalizable objects in req->domain.
658 finalize_domain_objects (DomainFinalizationReq *req)
660 MonoDomain *domain = req->domain;
663 #define NUM_FOBJECTS 64
664 MonoObject *to_finalize [NUM_FOBJECTS];
668 /* Process finalizers which are already in the queue */
669 mono_gc_invoke_finalizers ();
672 while (g_hash_table_size (domain->finalizable_objects_hash) > 0) {
676 * Since the domain is unloading, nobody is allowed to put
677 * new entries into the hash table. But finalize_object might
678 * remove entries from the hash table, so we make a copy.
680 objs = g_ptr_array_new ();
681 g_hash_table_foreach (domain->finalizable_objects_hash, collect_objects, objs);
682 /* printf ("FINALIZING %d OBJECTS.\n", objs->len); */
684 for (i = 0; i < objs->len; ++i) {
685 MonoObject *o = (MonoObject*)g_ptr_array_index (objs, i);
686 /* FIXME: Avoid finalizing threads, etc */
687 mono_gc_run_finalize (o, 0);
690 g_ptr_array_free (objs, TRUE);
692 #elif defined(HAVE_SGEN_GC)
693 while ((count = mono_gc_finalizers_for_domain (domain, to_finalize, NUM_FOBJECTS))) {
695 for (i = 0; i < count; ++i) {
696 mono_gc_run_finalize (to_finalize [i], 0);
701 /* cleanup the reference queue */
702 reference_queue_clear_for_domain (domain);
704 /* printf ("DONE.\n"); */
705 SetEvent (req->done_event);
707 /* The event is closed in mono_domain_finalize if we get here */
712 finalizer_thread (gpointer unused)
714 gboolean wait = TRUE;
717 /* Wait to be notified that there's at least one
721 g_assert (mono_domain_get () == mono_get_root_domain ());
722 mono_gc_set_skip_thread (TRUE);
725 /* An alertable wait is required so this thread can be suspended on windows */
726 mono_coop_sem_wait (&finalizer_sem, MONO_SEM_FLAGS_ALERTABLE);
730 mono_gc_set_skip_thread (FALSE);
732 mono_threads_perform_thread_dump ();
734 mono_console_handle_async_ops ();
736 mono_attach_maybe_start ();
738 if (domains_to_finalize) {
739 mono_finalizer_lock ();
740 if (domains_to_finalize) {
741 DomainFinalizationReq *req = (DomainFinalizationReq *)domains_to_finalize->data;
742 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
743 mono_finalizer_unlock ();
745 finalize_domain_objects (req);
747 mono_finalizer_unlock ();
751 /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup),
752 * before the domain is unloaded.
754 mono_gc_invoke_finalizers ();
756 mono_threads_join_threads ();
758 reference_queue_proccess_all ();
760 mono_thread_hazardous_try_free_all ();
762 /* Avoid posting the pending done event until there are pending finalizers */
763 if (mono_coop_sem_timedwait (&finalizer_sem, 0, MONO_SEM_FLAGS_NONE) == 0) {
764 /* Don't wait again at the start of the loop */
767 SetEvent (pending_done_event);
771 mono_finalizer_lock ();
772 finalizer_thread_exited = TRUE;
773 mono_coop_cond_signal (&exited_cond);
774 mono_finalizer_unlock ();
779 #ifndef LAZY_GC_THREAD_CREATION
783 mono_gc_init_finalizer_thread (void)
785 gc_thread = mono_thread_create_internal (mono_domain_get (), finalizer_thread, NULL, FALSE, 0);
786 ves_icall_System_Threading_Thread_SetName_internal (gc_thread, mono_string_new (mono_domain_get (), "Finalizer"));
792 mono_coop_mutex_init_recursive (&finalizer_mutex);
793 mono_coop_mutex_init_recursive (&reference_queue_mutex);
795 mono_counters_register ("Minor GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.minor_gc_count);
796 mono_counters_register ("Major GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.major_gc_count);
797 mono_counters_register ("Minor GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.minor_gc_time);
798 mono_counters_register ("Major GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time);
799 mono_counters_register ("Major GC time concurrent", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time_concurrent);
801 mono_gc_base_init ();
803 if (mono_gc_is_disabled ()) {
808 pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
809 g_assert (pending_done_event);
810 mono_coop_cond_init (&exited_cond);
811 mono_coop_sem_init (&finalizer_sem, 0);
813 #ifndef LAZY_GC_THREAD_CREATION
814 mono_gc_init_finalizer_thread ();
819 mono_gc_cleanup (void)
822 g_message ("%s: cleaning up finalizer", __func__);
825 if (mono_gc_is_null ())
830 if (mono_thread_internal_current () != gc_thread) {
831 gboolean timed_out = FALSE;
832 guint32 start_ticks = mono_msec_ticks ();
833 guint32 end_ticks = start_ticks + 2000;
835 mono_gc_finalize_notify ();
836 /* Finishing the finalizer thread, so wait a little bit... */
837 /* MS seems to wait for about 2 seconds */
838 while (!finalizer_thread_exited) {
839 guint32 current_ticks = mono_msec_ticks ();
842 if (current_ticks >= end_ticks)
845 timeout = end_ticks - current_ticks;
846 mono_finalizer_lock ();
847 if (!finalizer_thread_exited)
848 mono_coop_cond_timedwait (&exited_cond, &finalizer_mutex, timeout);
849 mono_finalizer_unlock ();
852 if (!finalizer_thread_exited) {
855 /* Set a flag which the finalizer thread can check */
856 suspend_finalizers = TRUE;
858 /* Try to abort the thread, in the hope that it is running managed code */
859 mono_thread_internal_stop (gc_thread);
861 /* Wait for it to stop */
862 ret = guarded_wait (gc_thread->handle, 100, TRUE);
864 if (ret == WAIT_TIMEOUT) {
866 * The finalizer thread refused to die. There is not much we
867 * can do here, since the runtime is shutting down so the
868 * state the finalizer thread depends on will vanish.
870 g_warning ("Shutting down finalizer thread timed out.");
878 /* Wait for the thread to actually exit */
879 ret = guarded_wait (gc_thread->handle, INFINITE, TRUE);
880 g_assert (ret == WAIT_OBJECT_0);
882 mono_thread_join (GUINT_TO_POINTER (gc_thread->tid));
884 g_assert (finalizer_thread_exited);
887 mono_gc_base_cleanup ();
890 mono_reference_queue_cleanup ();
892 mono_coop_mutex_destroy (&finalizer_mutex);
893 mono_coop_mutex_destroy (&reference_queue_mutex);
897 mono_gc_is_finalizer_internal_thread (MonoInternalThread *thread)
899 return thread == gc_thread;
903 * mono_gc_is_finalizer_thread:
904 * @thread: the thread to test.
906 * In Mono objects are finalized asynchronously on a separate thread.
907 * This routine tests whether the @thread argument represents the
908 * finalization thread.
910 * Returns: TRUE if @thread is the finalization thread.
913 mono_gc_is_finalizer_thread (MonoThread *thread)
915 return mono_gc_is_finalizer_internal_thread (thread->internal_thread);
918 #if defined(__MACH__)
919 static pthread_t mach_exception_thread;
922 mono_gc_register_mach_exception_thread (pthread_t thread)
924 mach_exception_thread = thread;
928 mono_gc_get_mach_exception_thread (void)
930 return mach_exception_thread;
934 static MonoReferenceQueue *ref_queues;
937 ref_list_remove_element (RefQueueEntry **prev, RefQueueEntry *element)
940 /* Guard if head is changed concurrently. */
941 while (*prev != element)
942 prev = &(*prev)->next;
943 } while (prev && InterlockedCompareExchangePointer ((volatile gpointer *)prev, element->next, element) != element);
947 ref_list_push (RefQueueEntry **head, RefQueueEntry *value)
949 RefQueueEntry *current;
952 value->next = current;
953 STORE_STORE_FENCE; /*Must make sure the previous store is visible before the CAS. */
954 } while (InterlockedCompareExchangePointer ((volatile gpointer *)head, value, current) != current);
958 reference_queue_proccess (MonoReferenceQueue *queue)
960 RefQueueEntry **iter = &queue->queue;
961 RefQueueEntry *entry;
962 while ((entry = *iter)) {
963 if (queue->should_be_deleted || !mono_gchandle_get_target (entry->gchandle)) {
964 mono_gchandle_free ((guint32)entry->gchandle);
965 ref_list_remove_element (iter, entry);
966 queue->callback (entry->user_data);
975 reference_queue_proccess_all (void)
977 MonoReferenceQueue **iter;
978 MonoReferenceQueue *queue = ref_queues;
979 for (; queue; queue = queue->next)
980 reference_queue_proccess (queue);
983 mono_coop_mutex_lock (&reference_queue_mutex);
984 for (iter = &ref_queues; *iter;) {
986 if (!queue->should_be_deleted) {
991 mono_coop_mutex_unlock (&reference_queue_mutex);
992 reference_queue_proccess (queue);
998 mono_coop_mutex_unlock (&reference_queue_mutex);
1002 mono_reference_queue_cleanup (void)
1004 MonoReferenceQueue *queue = ref_queues;
1005 for (; queue; queue = queue->next)
1006 queue->should_be_deleted = TRUE;
1007 reference_queue_proccess_all ();
1011 reference_queue_clear_for_domain (MonoDomain *domain)
1013 MonoReferenceQueue *queue = ref_queues;
1014 for (; queue; queue = queue->next) {
1015 RefQueueEntry **iter = &queue->queue;
1016 RefQueueEntry *entry;
1017 while ((entry = *iter)) {
1018 if (entry->domain == domain) {
1019 mono_gchandle_free ((guint32)entry->gchandle);
1020 ref_list_remove_element (iter, entry);
1021 queue->callback (entry->user_data);
1024 iter = &entry->next;
1030 * mono_gc_reference_queue_new:
1031 * @callback callback used when processing collected entries.
1033 * Create a new reference queue used to process collected objects.
1034 * A reference queue let you add a pair of (managed object, user data)
1035 * using the mono_gc_reference_queue_add method.
1037 * Once the managed object is collected @callback will be called
1038 * in the finalizer thread with 'user data' as argument.
1040 * The callback is called from the finalizer thread without any locks held.
1041 * When a AppDomain is unloaded, all callbacks for objects belonging to it
1044 * @returns the new queue.
1047 mono_gc_reference_queue_new (mono_reference_queue_callback callback)
1049 MonoReferenceQueue *res = g_new0 (MonoReferenceQueue, 1);
1050 res->callback = callback;
1052 mono_coop_mutex_lock (&reference_queue_mutex);
1053 res->next = ref_queues;
1055 mono_coop_mutex_unlock (&reference_queue_mutex);
1061 * mono_gc_reference_queue_add:
1062 * @queue the queue to add the reference to.
1063 * @obj the object to be watched for collection
1064 * @user_data parameter to be passed to the queue callback
1066 * Queue an object to be watched for collection, when the @obj is
1067 * collected, the callback that was registered for the @queue will
1068 * be invoked with @user_data as argument.
1070 * @returns false if the queue is scheduled to be freed.
1073 mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *user_data)
1076 RefQueueEntry *entry;
1077 if (queue->should_be_deleted)
1080 entry = g_new0 (RefQueueEntry, 1);
1081 entry->user_data = user_data;
1082 entry->domain = mono_object_domain (obj);
1084 entry->gchandle = mono_gchandle_new_weakref (obj, TRUE);
1085 mono_object_register_finalizer (obj, &error);
1086 mono_error_assert_ok (&error);
1088 ref_list_push (&queue->queue, entry);
1093 * mono_gc_reference_queue_free:
1094 * @queue the queue that should be freed.
1096 * This operation signals that @queue should be freed. This operation is deferred
1097 * as it happens on the finalizer thread.
1099 * After this call, no further objects can be queued. It's the responsibility of the
1100 * caller to make sure that no further attempt to access queue will be made.
1103 mono_gc_reference_queue_free (MonoReferenceQueue *queue)
1105 queue->should_be_deleted = TRUE;