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 /* Avoid posting the pending done event until there are pending finalizers */
761 if (mono_coop_sem_timedwait (&finalizer_sem, 0, MONO_SEM_FLAGS_NONE) == 0) {
762 /* Don't wait again at the start of the loop */
765 SetEvent (pending_done_event);
769 mono_finalizer_lock ();
770 finalizer_thread_exited = TRUE;
771 mono_coop_cond_signal (&exited_cond);
772 mono_finalizer_unlock ();
777 #ifndef LAZY_GC_THREAD_CREATION
781 mono_gc_init_finalizer_thread (void)
783 gc_thread = mono_thread_create_internal (mono_domain_get (), finalizer_thread, NULL, FALSE, 0);
784 ves_icall_System_Threading_Thread_SetName_internal (gc_thread, mono_string_new (mono_domain_get (), "Finalizer"));
790 mono_coop_mutex_init_recursive (&finalizer_mutex);
791 mono_coop_mutex_init_recursive (&reference_queue_mutex);
793 mono_counters_register ("Minor GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.minor_gc_count);
794 mono_counters_register ("Major GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.major_gc_count);
795 mono_counters_register ("Minor GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.minor_gc_time);
796 mono_counters_register ("Major GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time);
797 mono_counters_register ("Major GC time concurrent", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time_concurrent);
799 mono_gc_base_init ();
801 if (mono_gc_is_disabled ()) {
806 pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
807 g_assert (pending_done_event);
808 mono_coop_cond_init (&exited_cond);
809 mono_coop_sem_init (&finalizer_sem, 0);
811 #ifndef LAZY_GC_THREAD_CREATION
812 mono_gc_init_finalizer_thread ();
817 mono_gc_cleanup (void)
820 g_message ("%s: cleaning up finalizer", __func__);
823 if (mono_gc_is_null ())
828 if (mono_thread_internal_current () != gc_thread) {
829 gboolean timed_out = FALSE;
830 guint32 start_ticks = mono_msec_ticks ();
831 guint32 end_ticks = start_ticks + 2000;
833 mono_gc_finalize_notify ();
834 /* Finishing the finalizer thread, so wait a little bit... */
835 /* MS seems to wait for about 2 seconds */
836 while (!finalizer_thread_exited) {
837 guint32 current_ticks = mono_msec_ticks ();
840 if (current_ticks >= end_ticks)
843 timeout = end_ticks - current_ticks;
844 mono_finalizer_lock ();
845 if (!finalizer_thread_exited)
846 mono_coop_cond_timedwait (&exited_cond, &finalizer_mutex, timeout);
847 mono_finalizer_unlock ();
850 if (!finalizer_thread_exited) {
853 /* Set a flag which the finalizer thread can check */
854 suspend_finalizers = TRUE;
856 /* Try to abort the thread, in the hope that it is running managed code */
857 mono_thread_internal_stop (gc_thread);
859 /* Wait for it to stop */
860 ret = guarded_wait (gc_thread->handle, 100, TRUE);
862 if (ret == WAIT_TIMEOUT) {
864 * The finalizer thread refused to die. There is not much we
865 * can do here, since the runtime is shutting down so the
866 * state the finalizer thread depends on will vanish.
868 g_warning ("Shutting down finalizer thread timed out.");
876 /* Wait for the thread to actually exit */
877 ret = guarded_wait (gc_thread->handle, INFINITE, TRUE);
878 g_assert (ret == WAIT_OBJECT_0);
880 mono_thread_join (GUINT_TO_POINTER (gc_thread->tid));
882 g_assert (finalizer_thread_exited);
885 mono_gc_base_cleanup ();
888 mono_reference_queue_cleanup ();
890 mono_coop_mutex_destroy (&finalizer_mutex);
891 mono_coop_mutex_destroy (&reference_queue_mutex);
895 mono_gc_is_finalizer_internal_thread (MonoInternalThread *thread)
897 return thread == gc_thread;
901 * mono_gc_is_finalizer_thread:
902 * @thread: the thread to test.
904 * In Mono objects are finalized asynchronously on a separate thread.
905 * This routine tests whether the @thread argument represents the
906 * finalization thread.
908 * Returns: TRUE if @thread is the finalization thread.
911 mono_gc_is_finalizer_thread (MonoThread *thread)
913 return mono_gc_is_finalizer_internal_thread (thread->internal_thread);
916 #if defined(__MACH__)
917 static pthread_t mach_exception_thread;
920 mono_gc_register_mach_exception_thread (pthread_t thread)
922 mach_exception_thread = thread;
926 mono_gc_get_mach_exception_thread (void)
928 return mach_exception_thread;
932 static MonoReferenceQueue *ref_queues;
935 ref_list_remove_element (RefQueueEntry **prev, RefQueueEntry *element)
938 /* Guard if head is changed concurrently. */
939 while (*prev != element)
940 prev = &(*prev)->next;
941 } while (prev && InterlockedCompareExchangePointer ((volatile gpointer *)prev, element->next, element) != element);
945 ref_list_push (RefQueueEntry **head, RefQueueEntry *value)
947 RefQueueEntry *current;
950 value->next = current;
951 STORE_STORE_FENCE; /*Must make sure the previous store is visible before the CAS. */
952 } while (InterlockedCompareExchangePointer ((volatile gpointer *)head, value, current) != current);
956 reference_queue_proccess (MonoReferenceQueue *queue)
958 RefQueueEntry **iter = &queue->queue;
959 RefQueueEntry *entry;
960 while ((entry = *iter)) {
961 if (queue->should_be_deleted || !mono_gchandle_get_target (entry->gchandle)) {
962 mono_gchandle_free ((guint32)entry->gchandle);
963 ref_list_remove_element (iter, entry);
964 queue->callback (entry->user_data);
973 reference_queue_proccess_all (void)
975 MonoReferenceQueue **iter;
976 MonoReferenceQueue *queue = ref_queues;
977 for (; queue; queue = queue->next)
978 reference_queue_proccess (queue);
981 mono_coop_mutex_lock (&reference_queue_mutex);
982 for (iter = &ref_queues; *iter;) {
984 if (!queue->should_be_deleted) {
989 mono_coop_mutex_unlock (&reference_queue_mutex);
990 reference_queue_proccess (queue);
996 mono_coop_mutex_unlock (&reference_queue_mutex);
1000 mono_reference_queue_cleanup (void)
1002 MonoReferenceQueue *queue = ref_queues;
1003 for (; queue; queue = queue->next)
1004 queue->should_be_deleted = TRUE;
1005 reference_queue_proccess_all ();
1009 reference_queue_clear_for_domain (MonoDomain *domain)
1011 MonoReferenceQueue *queue = ref_queues;
1012 for (; queue; queue = queue->next) {
1013 RefQueueEntry **iter = &queue->queue;
1014 RefQueueEntry *entry;
1015 while ((entry = *iter)) {
1016 if (entry->domain == domain) {
1017 mono_gchandle_free ((guint32)entry->gchandle);
1018 ref_list_remove_element (iter, entry);
1019 queue->callback (entry->user_data);
1022 iter = &entry->next;
1028 * mono_gc_reference_queue_new:
1029 * @callback callback used when processing collected entries.
1031 * Create a new reference queue used to process collected objects.
1032 * A reference queue let you add a pair of (managed object, user data)
1033 * using the mono_gc_reference_queue_add method.
1035 * Once the managed object is collected @callback will be called
1036 * in the finalizer thread with 'user data' as argument.
1038 * The callback is called from the finalizer thread without any locks held.
1039 * When a AppDomain is unloaded, all callbacks for objects belonging to it
1042 * @returns the new queue.
1045 mono_gc_reference_queue_new (mono_reference_queue_callback callback)
1047 MonoReferenceQueue *res = g_new0 (MonoReferenceQueue, 1);
1048 res->callback = callback;
1050 mono_coop_mutex_lock (&reference_queue_mutex);
1051 res->next = ref_queues;
1053 mono_coop_mutex_unlock (&reference_queue_mutex);
1059 * mono_gc_reference_queue_add:
1060 * @queue the queue to add the reference to.
1061 * @obj the object to be watched for collection
1062 * @user_data parameter to be passed to the queue callback
1064 * Queue an object to be watched for collection, when the @obj is
1065 * collected, the callback that was registered for the @queue will
1066 * be invoked with @user_data as argument.
1068 * @returns false if the queue is scheduled to be freed.
1071 mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *user_data)
1074 RefQueueEntry *entry;
1075 if (queue->should_be_deleted)
1078 entry = g_new0 (RefQueueEntry, 1);
1079 entry->user_data = user_data;
1080 entry->domain = mono_object_domain (obj);
1082 entry->gchandle = mono_gchandle_new_weakref (obj, TRUE);
1083 mono_object_register_finalizer (obj, &error);
1084 mono_error_assert_ok (&error);
1086 ref_list_push (&queue->queue, entry);
1091 * mono_gc_reference_queue_free:
1092 * @queue the queue that should be freed.
1094 * This operation signals that @queue should be freed. This operation is deferred
1095 * as it happens on the finalizer thread.
1097 * After this call, no further objects can be queued. It's the responsibility of the
1098 * caller to make sure that no further attempt to access queue will be made.
1101 mono_gc_reference_queue_free (MonoReferenceQueue *queue)
1103 queue->should_be_deleted = TRUE;