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)
9 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
16 #include <mono/metadata/gc-internals.h>
17 #include <mono/metadata/mono-gc.h>
18 #include <mono/metadata/threads.h>
19 #include <mono/metadata/tabledefs.h>
20 #include <mono/metadata/exception.h>
21 #include <mono/metadata/profiler-private.h>
22 #include <mono/metadata/domain-internals.h>
23 #include <mono/metadata/class-internals.h>
24 #include <mono/metadata/metadata-internals.h>
25 #include <mono/metadata/mono-mlist.h>
26 #include <mono/metadata/threads-types.h>
27 #include <mono/metadata/threadpool-ms.h>
28 #include <mono/sgen/sgen-conf.h>
29 #include <mono/sgen/sgen-gc.h>
30 #include <mono/utils/mono-logger-internals.h>
31 #include <mono/metadata/gc-internals.h>
32 #include <mono/metadata/marshal.h> /* for mono_delegate_free_ftnptr () */
33 #include <mono/metadata/attach.h>
34 #include <mono/metadata/console-io.h>
35 #include <mono/utils/mono-os-semaphore.h>
36 #include <mono/utils/mono-memory-model.h>
37 #include <mono/utils/mono-counters.h>
38 #include <mono/utils/mono-time.h>
39 #include <mono/utils/dtrace.h>
40 #include <mono/utils/mono-threads.h>
41 #include <mono/utils/atomic.h>
42 #include <mono/utils/mono-coop-semaphore.h>
43 #include <mono/utils/hazard-pointer.h>
49 typedef struct DomainFinalizationReq {
52 } DomainFinalizationReq;
54 static gboolean gc_disabled = FALSE;
56 static gboolean finalizing_root_domain = FALSE;
58 gboolean log_finalizers = FALSE;
59 gboolean mono_do_not_finalize = FALSE;
60 gchar **mono_do_not_finalize_class_names = NULL;
62 #define mono_finalizer_lock() mono_coop_mutex_lock (&finalizer_mutex)
63 #define mono_finalizer_unlock() mono_coop_mutex_unlock (&finalizer_mutex)
64 static MonoCoopMutex finalizer_mutex;
65 static MonoCoopMutex reference_queue_mutex;
67 static GSList *domains_to_finalize= NULL;
68 static MonoMList *threads_to_finalize = NULL;
70 static gboolean finalizer_thread_exited;
71 /* Uses finalizer_mutex */
72 static MonoCoopCond exited_cond;
74 static MonoInternalThread *gc_thread;
76 static void object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*), MonoError *error);
78 static void reference_queue_proccess_all (void);
79 static void mono_reference_queue_cleanup (void);
80 static void reference_queue_clear_for_domain (MonoDomain *domain);
81 static HANDLE pending_done_event;
84 guarded_wait (HANDLE handle, guint32 timeout, gboolean alertable)
89 result = WaitForSingleObjectEx (handle, timeout, alertable);
96 add_thread_to_finalize (MonoInternalThread *thread)
99 mono_finalizer_lock ();
100 if (!threads_to_finalize)
101 MONO_GC_REGISTER_ROOT_SINGLE (threads_to_finalize, MONO_ROOT_SOURCE_FINALIZER_QUEUE, "finalizable threads list");
102 threads_to_finalize = mono_mlist_append_checked (threads_to_finalize, (MonoObject*)thread, &error);
103 mono_finalizer_unlock ();
104 mono_error_raise_exception (&error); /* FIXME don't raise here */
107 static gboolean suspend_finalizers = FALSE;
109 * actually, we might want to queue the finalize requests in a separate thread,
110 * but we need to be careful about the execution domain of the thread...
113 mono_gc_run_finalize (void *obj, void *data)
116 MonoObject *exc = NULL;
121 MonoMethod* finalizer = NULL;
122 MonoDomain *caller_domain = mono_domain_get ();
124 RuntimeInvokeFunction runtime_invoke;
126 // This function is called from the innards of the GC, so our best alternative for now is to do polling here
127 mono_threads_safepoint ();
129 o = (MonoObject*)((char*)obj + GPOINTER_TO_UINT (data));
131 if (mono_do_not_finalize) {
132 if (!mono_do_not_finalize_class_names)
135 size_t namespace_len = strlen (o->vtable->klass->name_space);
136 for (int i = 0; mono_do_not_finalize_class_names [i]; ++i) {
137 const char *name = mono_do_not_finalize_class_names [i];
138 if (strncmp (name, o->vtable->klass->name_space, namespace_len))
140 if (name [namespace_len] != '.')
142 if (strcmp (name + namespace_len + 1, o->vtable->klass->name))
149 g_log ("mono-gc-finalizers", G_LOG_LEVEL_DEBUG, "<%s at %p> Starting finalizer checks.", o->vtable->klass->name, o);
151 if (suspend_finalizers)
154 domain = o->vtable->domain;
157 mono_domain_finalizers_lock (domain);
159 o2 = (MonoObject *)g_hash_table_lookup (domain->finalizable_objects_hash, o);
161 mono_domain_finalizers_unlock (domain);
164 /* Already finalized somehow */
168 /* make sure the finalizer is not called again if the object is resurrected */
169 object_register_finalizer ((MonoObject *)obj, NULL, &error);
170 mono_error_assert_ok (&error); /* FIXME don't swallow the error */
173 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Registered finalizer as processed.", o->vtable->klass->name, o);
175 if (o->vtable->klass == mono_defaults.internal_thread_class) {
176 MonoInternalThread *t = (MonoInternalThread*)o;
178 if (mono_gc_is_finalizer_internal_thread (t))
179 /* Avoid finalizing ourselves */
182 if (t->threadpool_thread && finalizing_root_domain) {
183 /* Don't finalize threadpool threads when
184 shutting down - they're finalized when the
185 threadpool shuts down. */
186 add_thread_to_finalize (t);
191 if (o->vtable->klass->image == mono_defaults.corlib && !strcmp (o->vtable->klass->name, "DynamicMethod") && finalizing_root_domain) {
193 * These can't be finalized during unloading/shutdown, since that would
194 * free the native code which can still be referenced by other
196 * FIXME: This is not perfect, objects dying at the same time as
197 * dynamic methods can still reference them even when !shutdown.
202 if (mono_runtime_get_no_exec ())
205 /* speedup later... and use a timeout */
206 /* g_print ("Finalize run on %p %s.%s\n", o, mono_object_class (o)->name_space, mono_object_class (o)->name); */
208 /* Use _internal here, since this thread can enter a doomed appdomain */
209 mono_domain_set_internal (mono_object_domain (o));
211 /* delegates that have a native function pointer allocated are
212 * registered for finalization, but they don't have a Finalize
213 * method, because in most cases it's not needed and it's just a waste.
215 if (o->vtable->klass->delegate) {
216 MonoDelegate* del = (MonoDelegate*)o;
217 if (del->delegate_trampoline)
218 mono_delegate_free_ftnptr ((MonoDelegate*)o);
219 mono_domain_set_internal (caller_domain);
223 finalizer = mono_class_get_finalizer (o->vtable->klass);
225 /* If object has a CCW but has no finalizer, it was only
226 * registered for finalization in order to free the CCW.
227 * Else it needs the regular finalizer run.
228 * FIXME: what to do about ressurection and suppression
229 * of finalizer on object with CCW.
231 if (mono_marshal_free_ccw (o) && !finalizer) {
232 mono_domain_set_internal (caller_domain);
237 * To avoid the locking plus the other overhead of mono_runtime_invoke_checked (),
238 * create and precompile a wrapper which calls the finalize method using
242 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Compiling finalizer.", o->vtable->klass->name, o);
244 if (!domain->finalize_runtime_invoke) {
245 MonoMethod *invoke = mono_marshal_get_runtime_invoke (mono_class_get_method_from_name_flags (mono_defaults.object_class, "Finalize", 0, 0), TRUE);
247 domain->finalize_runtime_invoke = mono_compile_method (invoke);
250 runtime_invoke = (RuntimeInvokeFunction)domain->finalize_runtime_invoke;
252 mono_runtime_class_init_full (o->vtable, &error);
253 mono_error_raise_exception (&error); /* FIXME don't raise here */
255 if (G_UNLIKELY (MONO_GC_FINALIZE_INVOKE_ENABLED ())) {
256 MONO_GC_FINALIZE_INVOKE ((unsigned long)o, mono_object_get_size (o),
257 o->vtable->klass->name_space, o->vtable->klass->name);
261 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Calling finalizer.", o->vtable->klass->name, o);
263 runtime_invoke (o, NULL, &exc, NULL);
266 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Returned from finalizer.", o->vtable->klass->name, o);
269 mono_thread_internal_unhandled_exception (exc);
271 mono_domain_set_internal (caller_domain);
275 mono_gc_finalize_threadpool_threads (void)
278 while (threads_to_finalize) {
279 MonoInternalThread *thread = (MonoInternalThread*) mono_mlist_get_data (threads_to_finalize);
281 /* Force finalization of the thread. */
282 thread->threadpool_thread = FALSE;
283 mono_object_register_finalizer ((MonoObject*)thread, &error);
284 mono_error_assert_ok (&error); /* FIXME don't swallow the error */
286 mono_gc_run_finalize (thread, NULL);
288 threads_to_finalize = mono_mlist_next (threads_to_finalize);
293 mono_gc_out_of_memory (size_t size)
296 * we could allocate at program startup some memory that we could release
297 * back to the system at this point if we're really low on memory (ie, size is
298 * lower than the memory we set apart)
300 mono_raise_exception (mono_domain_get ()->out_of_memory_ex);
306 * Some of our objects may point to a different address than the address returned by GC_malloc()
307 * (because of the GetHashCode hack), but we need to pass the real address to register_finalizer.
308 * This also means that in the callback we need to adjust the pointer to get back the real
310 * We also need to be consistent in the use of the GC_debug* variants of malloc and register_finalizer,
311 * since that, too, can cause the underlying pointer to be offset.
314 object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*), MonoError *error)
318 mono_error_init (error);
321 mono_error_set_argument_null (error, "obj", "");
325 domain = obj->vtable->domain;
328 if (mono_domain_is_unloading (domain) && (callback != NULL))
330 * Can't register finalizers in a dying appdomain, since they
331 * could be invoked after the appdomain has been unloaded.
335 mono_domain_finalizers_lock (domain);
338 g_hash_table_insert (domain->finalizable_objects_hash, obj, obj);
340 g_hash_table_remove (domain->finalizable_objects_hash, obj);
342 mono_domain_finalizers_unlock (domain);
344 mono_gc_register_for_finalization (obj, callback);
345 #elif defined(HAVE_SGEN_GC)
347 * If we register finalizers for domains that are unloading we might
348 * end up running them while or after the domain is being cleared, so
349 * the objects will not be valid anymore.
351 if (!mono_domain_is_unloading (domain))
352 mono_gc_register_for_finalization (obj, callback);
357 * mono_object_register_finalizer:
358 * @obj: object to register
360 * Records that object @obj has a finalizer, this will call the
361 * Finalize method when the garbage collector disposes the object.
365 mono_object_register_finalizer (MonoObject *obj, MonoError *error)
367 /* g_print ("Registered finalizer on %p %s.%s\n", obj, mono_object_class (obj)->name_space, mono_object_class (obj)->name); */
368 object_register_finalizer (obj, mono_gc_run_finalize, error);
372 * mono_domain_finalize:
373 * @domain: the domain to finalize
374 * @timeout: msects to wait for the finalization to complete, -1 to wait indefinitely
376 * Request finalization of all finalizable objects inside @domain. Wait
377 * @timeout msecs for the finalization to complete.
379 * Returns: TRUE if succeeded, FALSE if there was a timeout
383 mono_domain_finalize (MonoDomain *domain, guint32 timeout)
385 DomainFinalizationReq *req;
388 MonoInternalThread *thread = mono_thread_internal_current ();
390 #if defined(__native_client__)
394 if (mono_thread_internal_current () == gc_thread)
395 /* We are called from inside a finalizer, not much we can do here */
399 * No need to create another thread 'cause the finalizer thread
400 * is still working and will take care of running the finalizers
406 /* We don't support domain finalization without a GC */
407 if (mono_gc_is_null ())
410 mono_gc_collect (mono_gc_max_generation ());
412 done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
413 if (done_event == NULL) {
417 req = g_new0 (DomainFinalizationReq, 1);
418 req->domain = domain;
419 req->done_event = done_event;
421 if (domain == mono_get_root_domain ())
422 finalizing_root_domain = TRUE;
424 mono_finalizer_lock ();
426 domains_to_finalize = g_slist_append (domains_to_finalize, req);
428 mono_finalizer_unlock ();
430 /* Tell the finalizer thread to finalize this appdomain */
431 mono_gc_finalize_notify ();
437 res = guarded_wait (done_event, timeout, TRUE);
438 /* printf ("WAIT RES: %d.\n", res); */
440 if (res == WAIT_IO_COMPLETION) {
441 if ((thread->state & (ThreadState_StopRequested | ThreadState_SuspendRequested)) != 0)
443 } else if (res == WAIT_TIMEOUT) {
444 /* We leak the handle here */
451 CloseHandle (done_event);
453 if (domain == mono_get_root_domain ()) {
454 mono_threadpool_ms_cleanup ();
455 mono_gc_finalize_threadpool_threads ();
458 mono_profiler_appdomain_event (domain, MONO_PROFILE_END_UNLOAD);
464 ves_icall_System_GC_InternalCollect (int generation)
466 mono_gc_collect (generation);
470 ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection)
473 mono_gc_collect (mono_gc_max_generation ());
474 return mono_gc_get_used_size ();
478 ves_icall_System_GC_KeepAlive (MonoObject *obj)
486 ves_icall_System_GC_ReRegisterForFinalize (MonoObject *obj)
490 MONO_CHECK_ARG_NULL (obj,);
492 object_register_finalizer (obj, mono_gc_run_finalize, &error);
493 mono_error_set_pending_exception (&error);
497 ves_icall_System_GC_SuppressFinalize (MonoObject *obj)
501 MONO_CHECK_ARG_NULL (obj,);
503 /* delegates have no finalizers, but we register them to deal with the
504 * unmanaged->managed trampoline. We don't let the user suppress it
505 * otherwise we'd leak it.
507 if (obj->vtable->klass->delegate)
510 /* FIXME: Need to handle case where obj has COM Callable Wrapper
511 * generated for it that needs cleaned up, but user wants to suppress
512 * their derived object finalizer. */
514 object_register_finalizer (obj, NULL, &error);
515 mono_error_set_pending_exception (&error);
519 ves_icall_System_GC_WaitForPendingFinalizers (void)
521 if (mono_gc_is_null ())
524 if (!mono_gc_pending_finalizers ())
527 if (mono_thread_internal_current () == gc_thread)
528 /* Avoid deadlocks */
532 If the finalizer thread is not live, lets pretend no finalizers are pending since the current thread might
533 be the one responsible for starting it up.
535 if (gc_thread == NULL)
538 ResetEvent (pending_done_event);
539 mono_gc_finalize_notify ();
540 /* g_print ("Waiting for pending finalizers....\n"); */
541 guarded_wait (pending_done_event, INFINITE, TRUE);
542 /* g_print ("Done pending....\n"); */
546 ves_icall_System_GC_register_ephemeron_array (MonoObject *array)
549 if (!mono_gc_ephemeron_array_add (array)) {
550 mono_set_pending_exception (mono_object_domain (array)->out_of_memory_ex);
557 ves_icall_System_GC_get_ephemeron_tombstone (void)
559 return mono_domain_get ()->ephemeron_tombstone;
563 ves_icall_System_GCHandle_GetTarget (guint32 handle)
565 return mono_gchandle_get_target (handle);
569 * if type == -1, change the target of the handle, otherwise allocate a new handle.
572 ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type)
575 mono_gchandle_set_target (handle, obj);
576 /* the handle doesn't change */
581 return mono_gchandle_new_weakref (obj, FALSE);
582 case HANDLE_WEAK_TRACK:
583 return mono_gchandle_new_weakref (obj, TRUE);
585 return mono_gchandle_new (obj, FALSE);
587 return mono_gchandle_new (obj, TRUE);
589 g_assert_not_reached ();
595 ves_icall_System_GCHandle_FreeHandle (guint32 handle)
597 mono_gchandle_free (handle);
601 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
605 if (MONO_GC_HANDLE_TYPE (handle) != HANDLE_PINNED)
607 obj = mono_gchandle_get_target (handle);
609 MonoClass *klass = mono_object_class (obj);
610 if (klass == mono_defaults.string_class) {
611 return mono_string_chars ((MonoString*)obj);
612 } else if (klass->rank) {
613 return mono_array_addr ((MonoArray*)obj, char, 0);
615 /* the C# code will check and throw the exception */
616 /* FIXME: missing !klass->blittable test, see bug #61134 */
617 if ((klass->flags & TYPE_ATTRIBUTE_LAYOUT_MASK) == TYPE_ATTRIBUTE_AUTO_LAYOUT)
619 return (char*)obj + sizeof (MonoObject);
626 mono_gc_GCHandle_CheckCurrentDomain (guint32 gchandle)
628 return mono_gchandle_is_in_domain (gchandle, mono_domain_get ());
631 static MonoCoopSem finalizer_sem;
632 static volatile gboolean finished=FALSE;
635 mono_gc_finalize_notify (void)
638 g_message ( "%s: prodding finalizer", __func__);
641 if (mono_gc_is_null ())
644 mono_coop_sem_post (&finalizer_sem);
648 This is the number of entries allowed in the hazard free queue before
649 we explicitly cycle the finalizer thread to trigger pumping the queue.
651 It was picked empirically by running the corlib test suite in a stress
652 scenario where all hazard entries are queued.
654 In this extreme scenario we double the number of times we cycle the finalizer
655 thread compared to just GC calls.
657 Entries are usually in the order of 100's of bytes each, so we're limiting
658 floating garbage to be in the order of a dozen kb.
660 static gboolean finalizer_thread_pulsed;
661 #define HAZARD_QUEUE_OVERFLOW_SIZE 20
664 hazard_free_queue_is_too_big (size_t size)
666 if (size < HAZARD_QUEUE_OVERFLOW_SIZE)
669 if (finalizer_thread_pulsed || InterlockedCompareExchange (&finalizer_thread_pulsed, TRUE, FALSE))
672 mono_gc_finalize_notify ();
676 hazard_free_queue_pump (void)
678 mono_thread_hazardous_try_free_all ();
679 finalizer_thread_pulsed = FALSE;
685 collect_objects (gpointer key, gpointer value, gpointer user_data)
687 GPtrArray *arr = (GPtrArray*)user_data;
688 g_ptr_array_add (arr, key);
694 * finalize_domain_objects:
696 * Run the finalizers of all finalizable objects in req->domain.
699 finalize_domain_objects (DomainFinalizationReq *req)
701 MonoDomain *domain = req->domain;
704 #define NUM_FOBJECTS 64
705 MonoObject *to_finalize [NUM_FOBJECTS];
709 /* Process finalizers which are already in the queue */
710 mono_gc_invoke_finalizers ();
713 while (g_hash_table_size (domain->finalizable_objects_hash) > 0) {
717 * Since the domain is unloading, nobody is allowed to put
718 * new entries into the hash table. But finalize_object might
719 * remove entries from the hash table, so we make a copy.
721 objs = g_ptr_array_new ();
722 g_hash_table_foreach (domain->finalizable_objects_hash, collect_objects, objs);
723 /* printf ("FINALIZING %d OBJECTS.\n", objs->len); */
725 for (i = 0; i < objs->len; ++i) {
726 MonoObject *o = (MonoObject*)g_ptr_array_index (objs, i);
727 /* FIXME: Avoid finalizing threads, etc */
728 mono_gc_run_finalize (o, 0);
731 g_ptr_array_free (objs, TRUE);
733 #elif defined(HAVE_SGEN_GC)
734 while ((count = mono_gc_finalizers_for_domain (domain, to_finalize, NUM_FOBJECTS))) {
736 for (i = 0; i < count; ++i) {
737 mono_gc_run_finalize (to_finalize [i], 0);
742 /* cleanup the reference queue */
743 reference_queue_clear_for_domain (domain);
745 /* printf ("DONE.\n"); */
746 SetEvent (req->done_event);
748 /* The event is closed in mono_domain_finalize if we get here */
753 finalizer_thread (gpointer unused)
756 mono_thread_set_name_internal (mono_thread_internal_current (), mono_string_new (mono_get_root_domain (), "Finalizer"), FALSE, &error);
757 mono_error_assert_ok (&error);
759 gboolean wait = TRUE;
761 /* Register a hazard free queue pump callback */
762 mono_hazard_pointer_install_free_queue_size_callback (hazard_free_queue_is_too_big);
765 /* Wait to be notified that there's at least one
769 g_assert (mono_domain_get () == mono_get_root_domain ());
770 mono_gc_set_skip_thread (TRUE);
773 /* An alertable wait is required so this thread can be suspended on windows */
774 mono_coop_sem_wait (&finalizer_sem, MONO_SEM_FLAGS_ALERTABLE);
778 mono_gc_set_skip_thread (FALSE);
780 mono_threads_perform_thread_dump ();
782 mono_console_handle_async_ops ();
784 mono_attach_maybe_start ();
786 if (domains_to_finalize) {
787 mono_finalizer_lock ();
788 if (domains_to_finalize) {
789 DomainFinalizationReq *req = (DomainFinalizationReq *)domains_to_finalize->data;
790 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
791 mono_finalizer_unlock ();
793 finalize_domain_objects (req);
795 mono_finalizer_unlock ();
799 /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup),
800 * before the domain is unloaded.
802 mono_gc_invoke_finalizers ();
804 mono_threads_join_threads ();
806 reference_queue_proccess_all ();
808 hazard_free_queue_pump ();
810 /* Avoid posting the pending done event until there are pending finalizers */
811 if (mono_coop_sem_timedwait (&finalizer_sem, 0, MONO_SEM_FLAGS_NONE) == 0) {
812 /* Don't wait again at the start of the loop */
815 SetEvent (pending_done_event);
819 mono_finalizer_lock ();
820 finalizer_thread_exited = TRUE;
821 mono_coop_cond_signal (&exited_cond);
822 mono_finalizer_unlock ();
827 #ifndef LAZY_GC_THREAD_CREATION
831 mono_gc_init_finalizer_thread (void)
834 gc_thread = mono_thread_create_internal (mono_domain_get (), finalizer_thread, NULL, FALSE, 0, &error);
835 mono_error_assert_ok (&error);
841 mono_coop_mutex_init_recursive (&finalizer_mutex);
842 mono_coop_mutex_init_recursive (&reference_queue_mutex);
844 mono_counters_register ("Minor GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.minor_gc_count);
845 mono_counters_register ("Major GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.major_gc_count);
846 mono_counters_register ("Minor GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.minor_gc_time);
847 mono_counters_register ("Major GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time);
848 mono_counters_register ("Major GC time concurrent", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time_concurrent);
850 mono_gc_base_init ();
852 if (mono_gc_is_disabled ()) {
857 pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
858 g_assert (pending_done_event);
859 mono_coop_cond_init (&exited_cond);
860 mono_coop_sem_init (&finalizer_sem, 0);
862 #ifndef LAZY_GC_THREAD_CREATION
863 mono_gc_init_finalizer_thread ();
868 mono_gc_cleanup (void)
871 g_message ("%s: cleaning up finalizer", __func__);
874 if (mono_gc_is_null ())
879 if (mono_thread_internal_current () != gc_thread) {
880 gboolean timed_out = FALSE;
881 gint64 start_ticks = mono_msec_ticks ();
882 gint64 end_ticks = start_ticks + 2000;
884 mono_gc_finalize_notify ();
885 /* Finishing the finalizer thread, so wait a little bit... */
886 /* MS seems to wait for about 2 seconds */
887 while (!finalizer_thread_exited) {
888 gint64 current_ticks = mono_msec_ticks ();
891 if (current_ticks >= end_ticks)
894 timeout = end_ticks - current_ticks;
895 mono_finalizer_lock ();
896 if (!finalizer_thread_exited)
897 mono_coop_cond_timedwait (&exited_cond, &finalizer_mutex, timeout);
898 mono_finalizer_unlock ();
901 if (!finalizer_thread_exited) {
904 /* Set a flag which the finalizer thread can check */
905 suspend_finalizers = TRUE;
907 /* Try to abort the thread, in the hope that it is running managed code */
908 mono_thread_internal_stop (gc_thread);
910 /* Wait for it to stop */
911 ret = guarded_wait (gc_thread->handle, 100, TRUE);
913 if (ret == WAIT_TIMEOUT) {
915 * The finalizer thread refused to die. There is not much we
916 * can do here, since the runtime is shutting down so the
917 * state the finalizer thread depends on will vanish.
919 g_warning ("Shutting down finalizer thread timed out.");
927 /* Wait for the thread to actually exit */
928 ret = guarded_wait (gc_thread->handle, INFINITE, TRUE);
929 g_assert (ret == WAIT_OBJECT_0);
931 mono_thread_join (GUINT_TO_POINTER (gc_thread->tid));
933 g_assert (finalizer_thread_exited);
936 mono_gc_base_cleanup ();
939 mono_reference_queue_cleanup ();
941 mono_coop_mutex_destroy (&finalizer_mutex);
942 mono_coop_mutex_destroy (&reference_queue_mutex);
946 mono_gc_is_finalizer_internal_thread (MonoInternalThread *thread)
948 return thread == gc_thread;
952 * mono_gc_is_finalizer_thread:
953 * @thread: the thread to test.
955 * In Mono objects are finalized asynchronously on a separate thread.
956 * This routine tests whether the @thread argument represents the
957 * finalization thread.
959 * Returns: TRUE if @thread is the finalization thread.
962 mono_gc_is_finalizer_thread (MonoThread *thread)
964 return mono_gc_is_finalizer_internal_thread (thread->internal_thread);
967 #if defined(__MACH__)
968 static pthread_t mach_exception_thread;
971 mono_gc_register_mach_exception_thread (pthread_t thread)
973 mach_exception_thread = thread;
977 mono_gc_get_mach_exception_thread (void)
979 return mach_exception_thread;
983 static MonoReferenceQueue *ref_queues;
986 ref_list_remove_element (RefQueueEntry **prev, RefQueueEntry *element)
989 /* Guard if head is changed concurrently. */
990 while (*prev != element)
991 prev = &(*prev)->next;
992 } while (prev && InterlockedCompareExchangePointer ((volatile gpointer *)prev, element->next, element) != element);
996 ref_list_push (RefQueueEntry **head, RefQueueEntry *value)
998 RefQueueEntry *current;
1001 value->next = current;
1002 STORE_STORE_FENCE; /*Must make sure the previous store is visible before the CAS. */
1003 } while (InterlockedCompareExchangePointer ((volatile gpointer *)head, value, current) != current);
1007 reference_queue_proccess (MonoReferenceQueue *queue)
1009 RefQueueEntry **iter = &queue->queue;
1010 RefQueueEntry *entry;
1011 while ((entry = *iter)) {
1012 if (queue->should_be_deleted || !mono_gchandle_get_target (entry->gchandle)) {
1013 mono_gchandle_free ((guint32)entry->gchandle);
1014 ref_list_remove_element (iter, entry);
1015 queue->callback (entry->user_data);
1018 iter = &entry->next;
1024 reference_queue_proccess_all (void)
1026 MonoReferenceQueue **iter;
1027 MonoReferenceQueue *queue = ref_queues;
1028 for (; queue; queue = queue->next)
1029 reference_queue_proccess (queue);
1032 mono_coop_mutex_lock (&reference_queue_mutex);
1033 for (iter = &ref_queues; *iter;) {
1035 if (!queue->should_be_deleted) {
1036 iter = &queue->next;
1040 mono_coop_mutex_unlock (&reference_queue_mutex);
1041 reference_queue_proccess (queue);
1044 *iter = queue->next;
1047 mono_coop_mutex_unlock (&reference_queue_mutex);
1051 mono_reference_queue_cleanup (void)
1053 MonoReferenceQueue *queue = ref_queues;
1054 for (; queue; queue = queue->next)
1055 queue->should_be_deleted = TRUE;
1056 reference_queue_proccess_all ();
1060 reference_queue_clear_for_domain (MonoDomain *domain)
1062 MonoReferenceQueue *queue = ref_queues;
1063 for (; queue; queue = queue->next) {
1064 RefQueueEntry **iter = &queue->queue;
1065 RefQueueEntry *entry;
1066 while ((entry = *iter)) {
1067 if (entry->domain == domain) {
1068 mono_gchandle_free ((guint32)entry->gchandle);
1069 ref_list_remove_element (iter, entry);
1070 queue->callback (entry->user_data);
1073 iter = &entry->next;
1079 * mono_gc_reference_queue_new:
1080 * @callback callback used when processing collected entries.
1082 * Create a new reference queue used to process collected objects.
1083 * A reference queue let you add a pair of (managed object, user data)
1084 * using the mono_gc_reference_queue_add method.
1086 * Once the managed object is collected @callback will be called
1087 * in the finalizer thread with 'user data' as argument.
1089 * The callback is called from the finalizer thread without any locks held.
1090 * When a AppDomain is unloaded, all callbacks for objects belonging to it
1093 * @returns the new queue.
1096 mono_gc_reference_queue_new (mono_reference_queue_callback callback)
1098 MonoReferenceQueue *res = g_new0 (MonoReferenceQueue, 1);
1099 res->callback = callback;
1101 mono_coop_mutex_lock (&reference_queue_mutex);
1102 res->next = ref_queues;
1104 mono_coop_mutex_unlock (&reference_queue_mutex);
1110 * mono_gc_reference_queue_add:
1111 * @queue the queue to add the reference to.
1112 * @obj the object to be watched for collection
1113 * @user_data parameter to be passed to the queue callback
1115 * Queue an object to be watched for collection, when the @obj is
1116 * collected, the callback that was registered for the @queue will
1117 * be invoked with @user_data as argument.
1119 * @returns false if the queue is scheduled to be freed.
1122 mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *user_data)
1125 RefQueueEntry *entry;
1126 if (queue->should_be_deleted)
1129 entry = g_new0 (RefQueueEntry, 1);
1130 entry->user_data = user_data;
1131 entry->domain = mono_object_domain (obj);
1133 entry->gchandle = mono_gchandle_new_weakref (obj, TRUE);
1134 mono_object_register_finalizer (obj, &error);
1135 mono_error_assert_ok (&error);
1137 ref_list_push (&queue->queue, entry);
1142 * mono_gc_reference_queue_free:
1143 * @queue the queue that should be freed.
1145 * This operation signals that @queue should be freed. This operation is deferred
1146 * as it happens on the finalizer thread.
1148 * After this call, no further objects can be queued. It's the responsibility of the
1149 * caller to make sure that no further attempt to access queue will be made.
1152 mono_gc_reference_queue_free (MonoReferenceQueue *queue)
1154 queue->should_be_deleted = TRUE;