5 * Author: Paolo Molaro <lupus@ximian.com>
7 * Copyright 2002-2003 Ximian, Inc (http://www.ximian.com)
8 * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
9 * Copyright 2012 Xamarin Inc (http://www.xamarin.com)
10 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
17 #include <mono/metadata/gc-internals.h>
18 #include <mono/metadata/mono-gc.h>
19 #include <mono/metadata/threads.h>
20 #include <mono/metadata/tabledefs.h>
21 #include <mono/metadata/exception.h>
22 #include <mono/metadata/profiler-private.h>
23 #include <mono/metadata/domain-internals.h>
24 #include <mono/metadata/class-internals.h>
25 #include <mono/metadata/metadata-internals.h>
26 #include <mono/metadata/mono-mlist.h>
27 #include <mono/metadata/threads-types.h>
28 #include <mono/metadata/threadpool.h>
29 #include <mono/sgen/sgen-conf.h>
30 #include <mono/sgen/sgen-gc.h>
31 #include <mono/utils/mono-logger-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/mono-threads-coop.h>
42 #include <mono/utils/atomic.h>
43 #include <mono/utils/mono-coop-semaphore.h>
44 #include <mono/utils/hazard-pointer.h>
45 #include <mono/utils/w32api.h>
51 typedef struct DomainFinalizationReq {
55 } DomainFinalizationReq;
57 static gboolean gc_disabled;
59 static gboolean finalizing_root_domain;
61 gboolean log_finalizers;
62 gboolean mono_do_not_finalize;
63 volatile gboolean suspend_finalizers;
64 gchar **mono_do_not_finalize_class_names ;
66 #define mono_finalizer_lock() mono_coop_mutex_lock (&finalizer_mutex)
67 #define mono_finalizer_unlock() mono_coop_mutex_unlock (&finalizer_mutex)
68 static MonoCoopMutex finalizer_mutex;
69 static MonoCoopMutex reference_queue_mutex;
71 static GSList *domains_to_finalize;
73 static gboolean finalizer_thread_exited;
74 /* Uses finalizer_mutex */
75 static MonoCoopCond exited_cond;
77 static MonoInternalThread *gc_thread;
80 static HANDLE pending_done_event;
82 static gboolean pending_done;
83 static MonoCoopCond pending_done_cond;
84 static MonoCoopMutex pending_done_mutex;
87 static void object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*));
89 static void reference_queue_proccess_all (void);
90 static void mono_reference_queue_cleanup (void);
91 static void reference_queue_clear_for_domain (MonoDomain *domain);
94 static MonoThreadInfoWaitRet
95 guarded_wait (MonoThreadHandle *thread_handle, guint32 timeout, gboolean alertable)
97 MonoThreadInfoWaitRet result;
100 result = mono_thread_info_wait_one_handle (thread_handle, timeout, alertable);
108 MonoCoopMutex *mutex;
109 } BreakCoopAlertableWaitUD;
112 break_coop_alertable_wait (gpointer user_data)
114 BreakCoopAlertableWaitUD *ud = (BreakCoopAlertableWaitUD*)user_data;
116 mono_coop_mutex_lock (ud->mutex);
117 mono_coop_cond_signal (ud->cond);
118 mono_coop_mutex_unlock (ud->mutex);
124 * coop_cond_timedwait_alertable:
126 * Wait on COND/MUTEX. If ALERTABLE is non-null, the wait can be interrupted.
127 * In that case, *ALERTABLE will be set to TRUE, and 0 is returned.
130 coop_cond_timedwait_alertable (MonoCoopCond *cond, MonoCoopMutex *mutex, guint32 timeout_ms, gboolean *alertable)
132 BreakCoopAlertableWaitUD *ud;
136 ud = g_new0 (BreakCoopAlertableWaitUD, 1);
140 mono_thread_info_install_interrupt (break_coop_alertable_wait, ud, alertable);
146 res = mono_coop_cond_timedwait (cond, mutex, timeout_ms);
148 mono_thread_info_uninstall_interrupt (alertable);
152 /* the interrupt token has not been taken by another
153 * thread, so it's our responsability to free it up. */
161 * actually, we might want to queue the finalize requests in a separate thread,
162 * but we need to be careful about the execution domain of the thread...
165 mono_gc_run_finalize (void *obj, void *data)
168 MonoObject *exc = NULL;
173 MonoMethod* finalizer = NULL;
174 MonoDomain *caller_domain = mono_domain_get ();
176 RuntimeInvokeFunction runtime_invoke;
178 // This function is called from the innards of the GC, so our best alternative for now is to do polling here
179 mono_threads_safepoint ();
181 o = (MonoObject*)((char*)obj + GPOINTER_TO_UINT (data));
183 if (mono_do_not_finalize) {
184 if (!mono_do_not_finalize_class_names)
187 size_t namespace_len = strlen (o->vtable->klass->name_space);
188 for (int i = 0; mono_do_not_finalize_class_names [i]; ++i) {
189 const char *name = mono_do_not_finalize_class_names [i];
190 if (strncmp (name, o->vtable->klass->name_space, namespace_len))
192 if (name [namespace_len] != '.')
194 if (strcmp (name + namespace_len + 1, o->vtable->klass->name))
201 g_log ("mono-gc-finalizers", G_LOG_LEVEL_DEBUG, "<%s at %p> Starting finalizer checks.", o->vtable->klass->name, o);
203 if (suspend_finalizers)
206 domain = o->vtable->domain;
209 mono_domain_finalizers_lock (domain);
211 o2 = (MonoObject *)g_hash_table_lookup (domain->finalizable_objects_hash, o);
213 mono_domain_finalizers_unlock (domain);
216 /* Already finalized somehow */
220 /* make sure the finalizer is not called again if the object is resurrected */
221 object_register_finalizer ((MonoObject *)obj, NULL);
224 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Registered finalizer as processed.", o->vtable->klass->name, o);
226 if (o->vtable->klass == mono_defaults.internal_thread_class) {
227 MonoInternalThread *t = (MonoInternalThread*)o;
229 if (mono_gc_is_finalizer_internal_thread (t))
230 /* Avoid finalizing ourselves */
234 if (o->vtable->klass->image == mono_defaults.corlib && !strcmp (o->vtable->klass->name, "DynamicMethod") && finalizing_root_domain) {
236 * These can't be finalized during unloading/shutdown, since that would
237 * free the native code which can still be referenced by other
239 * FIXME: This is not perfect, objects dying at the same time as
240 * dynamic methods can still reference them even when !shutdown.
245 if (mono_runtime_get_no_exec ())
248 /* speedup later... and use a timeout */
249 /* g_print ("Finalize run on %p %s.%s\n", o, mono_object_class (o)->name_space, mono_object_class (o)->name); */
251 /* Use _internal here, since this thread can enter a doomed appdomain */
252 mono_domain_set_internal (mono_object_domain (o));
254 /* delegates that have a native function pointer allocated are
255 * registered for finalization, but they don't have a Finalize
256 * method, because in most cases it's not needed and it's just a waste.
258 if (o->vtable->klass->delegate) {
259 MonoDelegate* del = (MonoDelegate*)o;
260 if (del->delegate_trampoline)
261 mono_delegate_free_ftnptr ((MonoDelegate*)o);
262 mono_domain_set_internal (caller_domain);
266 finalizer = mono_class_get_finalizer (o->vtable->klass);
268 /* If object has a CCW but has no finalizer, it was only
269 * registered for finalization in order to free the CCW.
270 * Else it needs the regular finalizer run.
271 * FIXME: what to do about ressurection and suppression
272 * of finalizer on object with CCW.
274 if (mono_marshal_free_ccw (o) && !finalizer) {
275 mono_domain_set_internal (caller_domain);
280 * To avoid the locking plus the other overhead of mono_runtime_invoke_checked (),
281 * create and precompile a wrapper which calls the finalize method using
285 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Compiling finalizer.", o->vtable->klass->name, o);
287 if (!domain->finalize_runtime_invoke) {
288 MonoMethod *invoke = mono_marshal_get_runtime_invoke (mono_class_get_method_from_name_flags (mono_defaults.object_class, "Finalize", 0, 0), TRUE);
290 domain->finalize_runtime_invoke = mono_compile_method_checked (invoke, &error);
291 mono_error_assert_ok (&error); /* expect this not to fail */
294 runtime_invoke = (RuntimeInvokeFunction)domain->finalize_runtime_invoke;
296 mono_runtime_class_init_full (o->vtable, &error);
298 goto unhandled_error;
300 if (G_UNLIKELY (MONO_GC_FINALIZE_INVOKE_ENABLED ())) {
301 MONO_GC_FINALIZE_INVOKE ((unsigned long)o, mono_object_get_size (o),
302 o->vtable->klass->name_space, o->vtable->klass->name);
306 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Calling finalizer.", o->vtable->klass->name, o);
308 MONO_PROFILER_RAISE (gc_finalizing_object, (o));
310 runtime_invoke (o, NULL, &exc, NULL);
312 MONO_PROFILER_RAISE (gc_finalized_object, (o));
315 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Returned from finalizer.", o->vtable->klass->name, o);
319 exc = (MonoObject*)mono_error_convert_to_exception (&error);
321 mono_thread_internal_unhandled_exception (exc);
323 mono_domain_set_internal (caller_domain);
327 mono_gc_out_of_memory (size_t size)
330 * we could allocate at program startup some memory that we could release
331 * back to the system at this point if we're really low on memory (ie, size is
332 * lower than the memory we set apart)
334 mono_raise_exception (mono_domain_get ()->out_of_memory_ex);
340 * Some of our objects may point to a different address than the address returned by GC_malloc()
341 * (because of the GetHashCode hack), but we need to pass the real address to register_finalizer.
342 * This also means that in the callback we need to adjust the pointer to get back the real
344 * We also need to be consistent in the use of the GC_debug* variants of malloc and register_finalizer,
345 * since that, too, can cause the underlying pointer to be offset.
348 object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*))
352 g_assert (obj != NULL);
354 domain = obj->vtable->domain;
357 if (mono_domain_is_unloading (domain) && (callback != NULL))
359 * Can't register finalizers in a dying appdomain, since they
360 * could be invoked after the appdomain has been unloaded.
364 mono_domain_finalizers_lock (domain);
367 g_hash_table_insert (domain->finalizable_objects_hash, obj, obj);
369 g_hash_table_remove (domain->finalizable_objects_hash, obj);
371 mono_domain_finalizers_unlock (domain);
373 mono_gc_register_for_finalization (obj, callback);
374 #elif defined(HAVE_SGEN_GC)
376 * If we register finalizers for domains that are unloading we might
377 * end up running them while or after the domain is being cleared, so
378 * the objects will not be valid anymore.
380 if (!mono_domain_is_unloading (domain))
381 mono_gc_register_for_finalization (obj, callback);
386 * mono_object_register_finalizer:
387 * \param obj object to register
389 * Records that object \p obj has a finalizer, this will call the
390 * Finalize method when the garbage collector disposes the object.
394 mono_object_register_finalizer (MonoObject *obj)
396 /* g_print ("Registered finalizer on %p %s.%s\n", obj, mono_object_class (obj)->name_space, mono_object_class (obj)->name); */
397 object_register_finalizer (obj, mono_gc_run_finalize);
401 * mono_domain_finalize:
402 * \param domain the domain to finalize
403 * \param timeout msecs to wait for the finalization to complete, \c -1 to wait indefinitely
405 * Request finalization of all finalizable objects inside \p domain. Wait
406 * \p timeout msecs for the finalization to complete.
408 * \returns TRUE if succeeded, FALSE if there was a timeout
411 mono_domain_finalize (MonoDomain *domain, guint32 timeout)
413 DomainFinalizationReq *req;
414 MonoInternalThread *thread = mono_thread_internal_current ();
419 if (mono_thread_internal_current () == gc_thread)
420 /* We are called from inside a finalizer, not much we can do here */
424 * No need to create another thread 'cause the finalizer thread
425 * is still working and will take care of running the finalizers
431 /* We don't support domain finalization without a GC */
432 if (mono_gc_is_null ())
435 mono_gc_collect (mono_gc_max_generation ());
437 req = g_new0 (DomainFinalizationReq, 1);
439 req->domain = domain;
440 mono_coop_sem_init (&req->done, 0);
442 if (domain == mono_get_root_domain ())
443 finalizing_root_domain = TRUE;
445 mono_finalizer_lock ();
447 domains_to_finalize = g_slist_append (domains_to_finalize, req);
449 mono_finalizer_unlock ();
451 /* Tell the finalizer thread to finalize this appdomain */
452 mono_gc_finalize_notify ();
455 timeout = MONO_INFINITE_WAIT;
456 if (timeout != MONO_INFINITE_WAIT)
457 start = mono_msec_ticks ();
462 if (timeout == MONO_INFINITE_WAIT) {
463 res = mono_coop_sem_wait (&req->done, MONO_SEM_FLAGS_ALERTABLE);
465 gint64 elapsed = mono_msec_ticks () - start;
466 if (elapsed >= timeout) {
471 res = mono_coop_sem_timedwait (&req->done, timeout - elapsed, MONO_SEM_FLAGS_ALERTABLE);
474 if (res == MONO_SEM_TIMEDWAIT_RET_SUCCESS) {
476 } else if (res == MONO_SEM_TIMEDWAIT_RET_ALERTED) {
477 if ((thread->state & (ThreadState_AbortRequested | ThreadState_SuspendRequested)) != 0) {
481 } else if (res == MONO_SEM_TIMEDWAIT_RET_TIMEDOUT) {
485 g_error ("%s: unknown result %d", __func__, res);
490 /* Try removing the req from domains_to_finalize:
491 * - if it's not found: the domain is being finalized,
492 * so we the ref count is already decremented
493 * - if it's found: the domain is not yet being finalized,
494 * so we can safely decrement the ref */
498 mono_finalizer_lock ();
500 found = g_slist_index (domains_to_finalize, req) != -1;
502 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
504 mono_finalizer_unlock ();
507 /* We have to decrement it wherever we
508 * remove it from domains_to_finalize */
509 if (InterlockedDecrement (&req->ref) != 1)
510 g_error ("%s: req->ref should be 1, as we are the first one to decrement it", __func__);
517 if (InterlockedDecrement (&req->ref) == 0) {
518 mono_coop_sem_destroy (&req->done);
526 ves_icall_System_GC_InternalCollect (int generation)
528 mono_gc_collect (generation);
532 ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection)
535 mono_gc_collect (mono_gc_max_generation ());
536 return mono_gc_get_used_size ();
540 ves_icall_System_GC_KeepAlive (MonoObject *obj)
548 ves_icall_System_GC_ReRegisterForFinalize (MonoObject *obj)
550 MONO_CHECK_ARG_NULL (obj,);
552 object_register_finalizer (obj, mono_gc_run_finalize);
556 ves_icall_System_GC_SuppressFinalize (MonoObject *obj)
558 MONO_CHECK_ARG_NULL (obj,);
560 /* delegates have no finalizers, but we register them to deal with the
561 * unmanaged->managed trampoline. We don't let the user suppress it
562 * otherwise we'd leak it.
564 if (obj->vtable->klass->delegate)
567 /* FIXME: Need to handle case where obj has COM Callable Wrapper
568 * generated for it that needs cleaned up, but user wants to suppress
569 * their derived object finalizer. */
571 object_register_finalizer (obj, NULL);
575 ves_icall_System_GC_WaitForPendingFinalizers (void)
577 if (mono_gc_is_null ())
580 if (!mono_gc_pending_finalizers ())
583 if (mono_thread_internal_current () == gc_thread)
584 /* Avoid deadlocks */
588 If the finalizer thread is not live, lets pretend no finalizers are pending since the current thread might
589 be the one responsible for starting it up.
591 if (gc_thread == NULL)
595 ResetEvent (pending_done_event);
596 mono_gc_finalize_notify ();
597 /* g_print ("Waiting for pending finalizers....\n"); */
599 WaitForSingleObjectEx (pending_done_event, INFINITE, TRUE);
601 /* g_print ("Done pending....\n"); */
603 gboolean alerted = FALSE;
604 mono_coop_mutex_lock (&pending_done_mutex);
605 pending_done = FALSE;
606 mono_gc_finalize_notify ();
607 while (!pending_done) {
608 coop_cond_timedwait_alertable (&pending_done_cond, &pending_done_mutex, MONO_INFINITE_WAIT, &alerted);
612 mono_coop_mutex_unlock (&pending_done_mutex);
617 ves_icall_System_GC_register_ephemeron_array (MonoObject *array)
620 if (!mono_gc_ephemeron_array_add (array)) {
621 mono_set_pending_exception (mono_object_domain (array)->out_of_memory_ex);
628 ves_icall_System_GC_get_ephemeron_tombstone (void)
630 return mono_domain_get ()->ephemeron_tombstone;
634 ves_icall_System_GCHandle_GetTarget (guint32 handle)
636 return mono_gchandle_get_target (handle);
640 * if type == -1, change the target of the handle, otherwise allocate a new handle.
643 ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type)
646 mono_gchandle_set_target (handle, obj);
647 /* the handle doesn't change */
652 return mono_gchandle_new_weakref (obj, FALSE);
653 case HANDLE_WEAK_TRACK:
654 return mono_gchandle_new_weakref (obj, TRUE);
656 return mono_gchandle_new (obj, FALSE);
658 return mono_gchandle_new (obj, TRUE);
660 g_assert_not_reached ();
666 ves_icall_System_GCHandle_FreeHandle (guint32 handle)
668 mono_gchandle_free (handle);
672 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
676 if (MONO_GC_HANDLE_TYPE (handle) != HANDLE_PINNED)
678 obj = mono_gchandle_get_target (handle);
680 MonoClass *klass = mono_object_class (obj);
681 if (klass == mono_defaults.string_class) {
682 return mono_string_chars ((MonoString*)obj);
683 } else if (klass->rank) {
684 return mono_array_addr ((MonoArray*)obj, char, 0);
686 /* the C# code will check and throw the exception */
687 /* FIXME: missing !klass->blittable test, see bug #61134 */
688 if (mono_class_is_auto_layout (klass))
690 return (char*)obj + sizeof (MonoObject);
697 mono_gc_GCHandle_CheckCurrentDomain (guint32 gchandle)
699 return mono_gchandle_is_in_domain (gchandle, mono_domain_get ());
702 static MonoCoopSem finalizer_sem;
703 static volatile gboolean finished;
706 * mono_gc_finalize_notify:
708 * Notify the finalizer thread that finalizers etc.
709 * are available to be processed.
712 mono_gc_finalize_notify (void)
715 g_message ( "%s: prodding finalizer", __func__);
718 if (mono_gc_is_null ())
721 mono_coop_sem_post (&finalizer_sem);
725 This is the number of entries allowed in the hazard free queue before
726 we explicitly cycle the finalizer thread to trigger pumping the queue.
728 It was picked empirically by running the corlib test suite in a stress
729 scenario where all hazard entries are queued.
731 In this extreme scenario we double the number of times we cycle the finalizer
732 thread compared to just GC calls.
734 Entries are usually in the order of 100's of bytes each, so we're limiting
735 floating garbage to be in the order of a dozen kb.
737 static gboolean finalizer_thread_pulsed;
738 #define HAZARD_QUEUE_OVERFLOW_SIZE 20
741 hazard_free_queue_is_too_big (size_t size)
743 if (size < HAZARD_QUEUE_OVERFLOW_SIZE)
746 if (finalizer_thread_pulsed || InterlockedCompareExchange (&finalizer_thread_pulsed, TRUE, FALSE))
749 mono_gc_finalize_notify ();
753 hazard_free_queue_pump (void)
755 mono_thread_hazardous_try_free_all ();
756 finalizer_thread_pulsed = FALSE;
762 collect_objects (gpointer key, gpointer value, gpointer user_data)
764 GPtrArray *arr = (GPtrArray*)user_data;
765 g_ptr_array_add (arr, key);
771 * finalize_domain_objects:
773 * Run the finalizers of all finalizable objects in req->domain.
776 finalize_domain_objects (void)
778 DomainFinalizationReq *req = NULL;
781 if (domains_to_finalize) {
782 mono_finalizer_lock ();
783 if (domains_to_finalize) {
784 req = (DomainFinalizationReq *)domains_to_finalize->data;
785 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
787 mono_finalizer_unlock ();
793 domain = req->domain;
795 /* Process finalizers which are already in the queue */
796 mono_gc_invoke_finalizers ();
799 while (g_hash_table_size (domain->finalizable_objects_hash) > 0) {
803 * Since the domain is unloading, nobody is allowed to put
804 * new entries into the hash table. But finalize_object might
805 * remove entries from the hash table, so we make a copy.
807 objs = g_ptr_array_new ();
808 g_hash_table_foreach (domain->finalizable_objects_hash, collect_objects, objs);
809 /* printf ("FINALIZING %d OBJECTS.\n", objs->len); */
811 for (i = 0; i < objs->len; ++i) {
812 MonoObject *o = (MonoObject*)g_ptr_array_index (objs, i);
813 /* FIXME: Avoid finalizing threads, etc */
814 mono_gc_run_finalize (o, 0);
817 g_ptr_array_free (objs, TRUE);
819 #elif defined(HAVE_SGEN_GC)
820 mono_gc_finalize_domain (domain);
821 mono_gc_invoke_finalizers ();
824 /* cleanup the reference queue */
825 reference_queue_clear_for_domain (domain);
827 /* printf ("DONE.\n"); */
828 mono_coop_sem_post (&req->done);
830 if (InterlockedDecrement (&req->ref) == 0) {
831 /* mono_domain_finalize already returned, and
832 * doesn't hold a reference to req anymore. */
833 mono_coop_sem_destroy (&req->done);
839 finalizer_thread (gpointer unused)
842 gboolean wait = TRUE;
844 MonoString *finalizer = mono_string_new_checked (mono_get_root_domain (), "Finalizer", &error);
845 mono_error_assert_ok (&error);
846 mono_thread_set_name_internal (mono_thread_internal_current (), finalizer, FALSE, FALSE, &error);
847 mono_error_assert_ok (&error);
849 /* Register a hazard free queue pump callback */
850 mono_hazard_pointer_install_free_queue_size_callback (hazard_free_queue_is_too_big);
853 /* Wait to be notified that there's at least one
857 g_assert (mono_domain_get () == mono_get_root_domain ());
858 mono_gc_set_skip_thread (TRUE);
861 /* An alertable wait is required so this thread can be suspended on windows */
862 mono_coop_sem_wait (&finalizer_sem, MONO_SEM_FLAGS_ALERTABLE);
866 mono_gc_set_skip_thread (FALSE);
868 mono_threads_perform_thread_dump ();
870 mono_console_handle_async_ops ();
872 mono_attach_maybe_start ();
874 finalize_domain_objects ();
876 MONO_PROFILER_RAISE (gc_finalizing, ());
878 /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup),
879 * before the domain is unloaded.
881 mono_gc_invoke_finalizers ();
883 MONO_PROFILER_RAISE (gc_finalized, ());
885 mono_threads_join_threads ();
887 reference_queue_proccess_all ();
889 hazard_free_queue_pump ();
891 /* Avoid posting the pending done event until there are pending finalizers */
892 if (mono_coop_sem_timedwait (&finalizer_sem, 0, MONO_SEM_FLAGS_NONE) == MONO_SEM_TIMEDWAIT_RET_SUCCESS) {
893 /* Don't wait again at the start of the loop */
897 SetEvent (pending_done_event);
899 mono_coop_mutex_lock (&pending_done_mutex);
901 mono_coop_cond_signal (&pending_done_cond);
902 mono_coop_mutex_unlock (&pending_done_mutex);
907 mono_finalizer_lock ();
908 finalizer_thread_exited = TRUE;
909 mono_coop_cond_signal (&exited_cond);
910 mono_finalizer_unlock ();
915 #ifndef LAZY_GC_THREAD_CREATION
919 mono_gc_init_finalizer_thread (void)
922 gc_thread = mono_thread_create_internal (mono_domain_get (), finalizer_thread, NULL, MONO_THREAD_CREATE_FLAGS_NONE, &error);
923 mono_error_assert_ok (&error);
929 mono_coop_mutex_init_recursive (&finalizer_mutex);
930 mono_coop_mutex_init_recursive (&reference_queue_mutex);
932 mono_counters_register ("Minor GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.minor_gc_count);
933 mono_counters_register ("Major GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.major_gc_count);
934 mono_counters_register ("Minor GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.minor_gc_time);
935 mono_counters_register ("Major GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time);
936 mono_counters_register ("Major GC time concurrent", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time_concurrent);
938 mono_gc_base_init ();
940 if (mono_gc_is_disabled ()) {
946 pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
947 g_assert (pending_done_event);
949 mono_coop_cond_init (&pending_done_cond);
950 mono_coop_mutex_init (&pending_done_mutex);
953 mono_coop_cond_init (&exited_cond);
954 mono_coop_sem_init (&finalizer_sem, 0);
956 #ifndef LAZY_GC_THREAD_CREATION
957 mono_gc_init_finalizer_thread ();
962 mono_gc_cleanup (void)
965 g_message ("%s: cleaning up finalizer", __func__);
968 if (mono_gc_is_null ())
973 if (mono_thread_internal_current () != gc_thread) {
976 const gint64 timeout = 40 * 1000;
978 mono_gc_finalize_notify ();
980 start = mono_msec_ticks ();
982 /* Finishing the finalizer thread, so wait a little bit... */
983 /* MS seems to wait for about 2 seconds per finalizer thread */
984 /* and 40 seconds for all finalizers to finish */
988 if (finalizer_thread_exited) {
989 /* Wait for the thread to actually exit. We don't want the wait
990 * to be alertable, because we assert on the result to be SUCCESS_0 */
991 ret = guarded_wait (gc_thread->handle, MONO_INFINITE_WAIT, FALSE);
992 g_assert (ret == MONO_THREAD_INFO_WAIT_RET_SUCCESS_0);
994 mono_thread_join (GUINT_TO_POINTER (gc_thread->tid));
998 elapsed = mono_msec_ticks () - start;
999 if (elapsed >= timeout) {
1002 /* Set a flag which the finalizer thread can check */
1003 suspend_finalizers = TRUE;
1004 mono_gc_suspend_finalizers ();
1006 /* Try to abort the thread, in the hope that it is running managed code */
1007 mono_thread_internal_abort (gc_thread);
1009 /* Wait for it to stop */
1010 ret = guarded_wait (gc_thread->handle, 100, FALSE);
1011 if (ret == MONO_THREAD_INFO_WAIT_RET_TIMEOUT) {
1012 /* The finalizer thread refused to exit, suspend it forever. */
1013 mono_thread_internal_suspend_for_shutdown (gc_thread);
1017 g_assert (ret == MONO_THREAD_INFO_WAIT_RET_SUCCESS_0);
1019 mono_thread_join (GUINT_TO_POINTER (gc_thread->tid));
1023 mono_finalizer_lock ();
1024 if (!finalizer_thread_exited)
1025 mono_coop_cond_timedwait (&exited_cond, &finalizer_mutex, timeout - elapsed);
1026 mono_finalizer_unlock ();
1030 mono_gc_base_cleanup ();
1033 mono_reference_queue_cleanup ();
1035 mono_coop_mutex_destroy (&finalizer_mutex);
1036 mono_coop_mutex_destroy (&reference_queue_mutex);
1040 mono_gc_is_finalizer_internal_thread (MonoInternalThread *thread)
1042 return thread == gc_thread;
1046 * mono_gc_is_finalizer_thread:
1047 * \param thread the thread to test.
1049 * In Mono objects are finalized asynchronously on a separate thread.
1050 * This routine tests whether the \p thread argument represents the
1051 * finalization thread.
1053 * \returns TRUE if \p thread is the finalization thread.
1056 mono_gc_is_finalizer_thread (MonoThread *thread)
1058 return mono_gc_is_finalizer_internal_thread (thread->internal_thread);
1061 #if defined(__MACH__)
1062 static pthread_t mach_exception_thread;
1065 mono_gc_register_mach_exception_thread (pthread_t thread)
1067 mach_exception_thread = thread;
1071 mono_gc_get_mach_exception_thread (void)
1073 return mach_exception_thread;
1077 static MonoReferenceQueue *ref_queues;
1080 ref_list_remove_element (RefQueueEntry **prev, RefQueueEntry *element)
1083 /* Guard if head is changed concurrently. */
1084 while (*prev != element)
1085 prev = &(*prev)->next;
1086 } while (prev && InterlockedCompareExchangePointer ((volatile gpointer *)prev, element->next, element) != element);
1090 ref_list_push (RefQueueEntry **head, RefQueueEntry *value)
1092 RefQueueEntry *current;
1095 value->next = current;
1096 STORE_STORE_FENCE; /*Must make sure the previous store is visible before the CAS. */
1097 } while (InterlockedCompareExchangePointer ((volatile gpointer *)head, value, current) != current);
1101 reference_queue_proccess (MonoReferenceQueue *queue)
1103 RefQueueEntry **iter = &queue->queue;
1104 RefQueueEntry *entry;
1105 while ((entry = *iter)) {
1106 if (queue->should_be_deleted || !mono_gchandle_get_target (entry->gchandle)) {
1107 mono_gchandle_free ((guint32)entry->gchandle);
1108 ref_list_remove_element (iter, entry);
1109 queue->callback (entry->user_data);
1112 iter = &entry->next;
1118 reference_queue_proccess_all (void)
1120 MonoReferenceQueue **iter;
1121 MonoReferenceQueue *queue = ref_queues;
1122 for (; queue; queue = queue->next)
1123 reference_queue_proccess (queue);
1126 mono_coop_mutex_lock (&reference_queue_mutex);
1127 for (iter = &ref_queues; *iter;) {
1129 if (!queue->should_be_deleted) {
1130 iter = &queue->next;
1134 mono_coop_mutex_unlock (&reference_queue_mutex);
1135 reference_queue_proccess (queue);
1138 *iter = queue->next;
1141 mono_coop_mutex_unlock (&reference_queue_mutex);
1145 mono_reference_queue_cleanup (void)
1147 MonoReferenceQueue *queue = ref_queues;
1148 for (; queue; queue = queue->next)
1149 queue->should_be_deleted = TRUE;
1150 reference_queue_proccess_all ();
1154 reference_queue_clear_for_domain (MonoDomain *domain)
1156 MonoReferenceQueue *queue = ref_queues;
1157 for (; queue; queue = queue->next) {
1158 RefQueueEntry **iter = &queue->queue;
1159 RefQueueEntry *entry;
1160 while ((entry = *iter)) {
1161 if (entry->domain == domain) {
1162 mono_gchandle_free ((guint32)entry->gchandle);
1163 ref_list_remove_element (iter, entry);
1164 queue->callback (entry->user_data);
1167 iter = &entry->next;
1173 * mono_gc_reference_queue_new:
1174 * \param callback callback used when processing collected entries.
1176 * Create a new reference queue used to process collected objects.
1177 * A reference queue let you add a pair of (managed object, user data)
1178 * using the \c mono_gc_reference_queue_add method.
1180 * Once the managed object is collected \p callback will be called
1181 * in the finalizer thread with 'user data' as argument.
1183 * The callback is called from the finalizer thread without any locks held.
1184 * When an AppDomain is unloaded, all callbacks for objects belonging to it
1187 * \returns the new queue.
1190 mono_gc_reference_queue_new (mono_reference_queue_callback callback)
1192 MonoReferenceQueue *res = g_new0 (MonoReferenceQueue, 1);
1193 res->callback = callback;
1195 mono_coop_mutex_lock (&reference_queue_mutex);
1196 res->next = ref_queues;
1198 mono_coop_mutex_unlock (&reference_queue_mutex);
1204 * mono_gc_reference_queue_add:
1205 * \param queue the queue to add the reference to.
1206 * \param obj the object to be watched for collection
1207 * \param user_data parameter to be passed to the queue callback
1209 * Queue an object to be watched for collection, when the \p obj is
1210 * collected, the callback that was registered for the \p queue will
1211 * be invoked with \p user_data as argument.
1213 * \returns FALSE if the queue is scheduled to be freed.
1216 mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *user_data)
1218 RefQueueEntry *entry;
1219 if (queue->should_be_deleted)
1222 g_assert (obj != NULL);
1224 entry = g_new0 (RefQueueEntry, 1);
1225 entry->user_data = user_data;
1226 entry->domain = mono_object_domain (obj);
1228 entry->gchandle = mono_gchandle_new_weakref (obj, TRUE);
1229 mono_object_register_finalizer (obj);
1231 ref_list_push (&queue->queue, entry);
1236 * mono_gc_reference_queue_free:
1237 * \param queue the queue that should be freed.
1239 * This operation signals that \p queue should be freed. This operation is deferred
1240 * as it happens on the finalizer thread.
1242 * After this call, no further objects can be queued. It's the responsibility of the
1243 * caller to make sure that no further attempt to access queue will be made.
1246 mono_gc_reference_queue_free (MonoReferenceQueue *queue)
1248 queue->should_be_deleted = TRUE;