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/metadata/w32process.h>
36 #include <mono/utils/mono-os-semaphore.h>
37 #include <mono/utils/mono-memory-model.h>
38 #include <mono/utils/mono-counters.h>
39 #include <mono/utils/mono-time.h>
40 #include <mono/utils/dtrace.h>
41 #include <mono/utils/mono-threads.h>
42 #include <mono/utils/mono-threads-coop.h>
43 #include <mono/utils/atomic.h>
44 #include <mono/utils/mono-coop-semaphore.h>
45 #include <mono/utils/hazard-pointer.h>
46 #include <mono/utils/w32api.h>
52 typedef struct DomainFinalizationReq {
56 } DomainFinalizationReq;
58 static gboolean gc_disabled;
60 static gboolean finalizing_root_domain;
62 gboolean log_finalizers;
63 gboolean mono_do_not_finalize;
64 volatile gboolean suspend_finalizers;
65 gchar **mono_do_not_finalize_class_names ;
67 #define mono_finalizer_lock() mono_coop_mutex_lock (&finalizer_mutex)
68 #define mono_finalizer_unlock() mono_coop_mutex_unlock (&finalizer_mutex)
69 static MonoCoopMutex finalizer_mutex;
70 static MonoCoopMutex reference_queue_mutex;
72 static GSList *domains_to_finalize;
74 static gboolean finalizer_thread_exited;
75 /* Uses finalizer_mutex */
76 static MonoCoopCond exited_cond;
78 static MonoInternalThread *gc_thread;
81 static HANDLE pending_done_event;
83 static gboolean pending_done;
84 static MonoCoopCond pending_done_cond;
85 static MonoCoopMutex pending_done_mutex;
88 static void object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*));
90 static void reference_queue_proccess_all (void);
91 static void mono_reference_queue_cleanup (void);
92 static void reference_queue_clear_for_domain (MonoDomain *domain);
95 static MonoThreadInfoWaitRet
96 guarded_wait (MonoThreadHandle *thread_handle, guint32 timeout, gboolean alertable)
98 MonoThreadInfoWaitRet result;
101 result = mono_thread_info_wait_one_handle (thread_handle, timeout, alertable);
109 MonoCoopMutex *mutex;
110 } BreakCoopAlertableWaitUD;
113 break_coop_alertable_wait (gpointer user_data)
115 BreakCoopAlertableWaitUD *ud = (BreakCoopAlertableWaitUD*)user_data;
117 mono_coop_mutex_lock (ud->mutex);
118 mono_coop_cond_signal (ud->cond);
119 mono_coop_mutex_unlock (ud->mutex);
125 * coop_cond_timedwait_alertable:
127 * Wait on COND/MUTEX. If ALERTABLE is non-null, the wait can be interrupted.
128 * In that case, *ALERTABLE will be set to TRUE, and 0 is returned.
131 coop_cond_timedwait_alertable (MonoCoopCond *cond, MonoCoopMutex *mutex, guint32 timeout_ms, gboolean *alertable)
133 BreakCoopAlertableWaitUD *ud;
137 ud = g_new0 (BreakCoopAlertableWaitUD, 1);
141 mono_thread_info_install_interrupt (break_coop_alertable_wait, ud, alertable);
147 res = mono_coop_cond_timedwait (cond, mutex, timeout_ms);
149 mono_thread_info_uninstall_interrupt (alertable);
153 /* the interrupt token has not been taken by another
154 * thread, so it's our responsability to free it up. */
162 * actually, we might want to queue the finalize requests in a separate thread,
163 * but we need to be careful about the execution domain of the thread...
166 mono_gc_run_finalize (void *obj, void *data)
169 MonoObject *exc = NULL;
174 MonoMethod* finalizer = NULL;
175 MonoDomain *caller_domain = mono_domain_get ();
177 RuntimeInvokeFunction runtime_invoke;
179 // This function is called from the innards of the GC, so our best alternative for now is to do polling here
180 mono_threads_safepoint ();
182 o = (MonoObject*)((char*)obj + GPOINTER_TO_UINT (data));
184 if (mono_do_not_finalize) {
185 if (!mono_do_not_finalize_class_names)
188 size_t namespace_len = strlen (o->vtable->klass->name_space);
189 for (int i = 0; mono_do_not_finalize_class_names [i]; ++i) {
190 const char *name = mono_do_not_finalize_class_names [i];
191 if (strncmp (name, o->vtable->klass->name_space, namespace_len))
193 if (name [namespace_len] != '.')
195 if (strcmp (name + namespace_len + 1, o->vtable->klass->name))
202 g_log ("mono-gc-finalizers", G_LOG_LEVEL_DEBUG, "<%s at %p> Starting finalizer checks.", o->vtable->klass->name, o);
204 if (suspend_finalizers)
207 domain = o->vtable->domain;
210 mono_domain_finalizers_lock (domain);
212 o2 = (MonoObject *)g_hash_table_lookup (domain->finalizable_objects_hash, o);
214 mono_domain_finalizers_unlock (domain);
217 /* Already finalized somehow */
221 /* make sure the finalizer is not called again if the object is resurrected */
222 object_register_finalizer ((MonoObject *)obj, NULL);
225 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Registered finalizer as processed.", o->vtable->klass->name, o);
227 if (o->vtable->klass == mono_defaults.internal_thread_class) {
228 MonoInternalThread *t = (MonoInternalThread*)o;
230 if (mono_gc_is_finalizer_internal_thread (t))
231 /* Avoid finalizing ourselves */
235 if (o->vtable->klass->image == mono_defaults.corlib && !strcmp (o->vtable->klass->name, "DynamicMethod") && finalizing_root_domain) {
237 * These can't be finalized during unloading/shutdown, since that would
238 * free the native code which can still be referenced by other
240 * FIXME: This is not perfect, objects dying at the same time as
241 * dynamic methods can still reference them even when !shutdown.
246 if (mono_runtime_get_no_exec ())
249 /* speedup later... and use a timeout */
250 /* g_print ("Finalize run on %p %s.%s\n", o, mono_object_class (o)->name_space, mono_object_class (o)->name); */
252 /* Use _internal here, since this thread can enter a doomed appdomain */
253 mono_domain_set_internal (mono_object_domain (o));
255 /* delegates that have a native function pointer allocated are
256 * registered for finalization, but they don't have a Finalize
257 * method, because in most cases it's not needed and it's just a waste.
259 if (o->vtable->klass->delegate) {
260 MonoDelegate* del = (MonoDelegate*)o;
261 if (del->delegate_trampoline)
262 mono_delegate_free_ftnptr ((MonoDelegate*)o);
263 mono_domain_set_internal (caller_domain);
267 finalizer = mono_class_get_finalizer (o->vtable->klass);
269 /* If object has a CCW but has no finalizer, it was only
270 * registered for finalization in order to free the CCW.
271 * Else it needs the regular finalizer run.
272 * FIXME: what to do about ressurection and suppression
273 * of finalizer on object with CCW.
275 if (mono_marshal_free_ccw (o) && !finalizer) {
276 mono_domain_set_internal (caller_domain);
281 * To avoid the locking plus the other overhead of mono_runtime_invoke_checked (),
282 * create and precompile a wrapper which calls the finalize method using
286 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Compiling finalizer.", o->vtable->klass->name, o);
288 if (!domain->finalize_runtime_invoke) {
289 MonoMethod *invoke = mono_marshal_get_runtime_invoke (mono_class_get_method_from_name_flags (mono_defaults.object_class, "Finalize", 0, 0), TRUE);
291 domain->finalize_runtime_invoke = mono_compile_method_checked (invoke, &error);
292 mono_error_assert_ok (&error); /* expect this not to fail */
295 runtime_invoke = (RuntimeInvokeFunction)domain->finalize_runtime_invoke;
297 mono_runtime_class_init_full (o->vtable, &error);
299 goto unhandled_error;
301 if (G_UNLIKELY (MONO_GC_FINALIZE_INVOKE_ENABLED ())) {
302 MONO_GC_FINALIZE_INVOKE ((unsigned long)o, mono_object_get_size (o),
303 o->vtable->klass->name_space, o->vtable->klass->name);
307 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Calling finalizer.", o->vtable->klass->name, o);
309 MONO_PROFILER_RAISE (gc_finalizing_object, (o));
311 runtime_invoke (o, NULL, &exc, NULL);
313 MONO_PROFILER_RAISE (gc_finalized_object, (o));
316 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Returned from finalizer.", o->vtable->klass->name, o);
320 exc = (MonoObject*)mono_error_convert_to_exception (&error);
322 mono_thread_internal_unhandled_exception (exc);
324 mono_domain_set_internal (caller_domain);
328 mono_gc_out_of_memory (size_t size)
331 * we could allocate at program startup some memory that we could release
332 * back to the system at this point if we're really low on memory (ie, size is
333 * lower than the memory we set apart)
335 mono_raise_exception (mono_domain_get ()->out_of_memory_ex);
341 * Some of our objects may point to a different address than the address returned by GC_malloc()
342 * (because of the GetHashCode hack), but we need to pass the real address to register_finalizer.
343 * This also means that in the callback we need to adjust the pointer to get back the real
345 * We also need to be consistent in the use of the GC_debug* variants of malloc and register_finalizer,
346 * since that, too, can cause the underlying pointer to be offset.
349 object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*))
353 g_assert (obj != NULL);
355 domain = obj->vtable->domain;
358 if (mono_domain_is_unloading (domain) && (callback != NULL))
360 * Can't register finalizers in a dying appdomain, since they
361 * could be invoked after the appdomain has been unloaded.
365 mono_domain_finalizers_lock (domain);
368 g_hash_table_insert (domain->finalizable_objects_hash, obj, obj);
370 g_hash_table_remove (domain->finalizable_objects_hash, obj);
372 mono_domain_finalizers_unlock (domain);
374 mono_gc_register_for_finalization (obj, callback);
375 #elif defined(HAVE_SGEN_GC)
377 * If we register finalizers for domains that are unloading we might
378 * end up running them while or after the domain is being cleared, so
379 * the objects will not be valid anymore.
381 if (!mono_domain_is_unloading (domain))
382 mono_gc_register_for_finalization (obj, callback);
387 * mono_object_register_finalizer:
388 * \param obj object to register
390 * Records that object \p obj has a finalizer, this will call the
391 * Finalize method when the garbage collector disposes the object.
395 mono_object_register_finalizer (MonoObject *obj)
397 /* g_print ("Registered finalizer on %p %s.%s\n", obj, mono_object_class (obj)->name_space, mono_object_class (obj)->name); */
398 object_register_finalizer (obj, mono_gc_run_finalize);
402 * mono_domain_finalize:
403 * \param domain the domain to finalize
404 * \param timeout msecs to wait for the finalization to complete, \c -1 to wait indefinitely
406 * Request finalization of all finalizable objects inside \p domain. Wait
407 * \p timeout msecs for the finalization to complete.
409 * \returns TRUE if succeeded, FALSE if there was a timeout
412 mono_domain_finalize (MonoDomain *domain, guint32 timeout)
414 DomainFinalizationReq *req;
415 MonoInternalThread *thread = mono_thread_internal_current ();
420 if (mono_thread_internal_current () == gc_thread)
421 /* We are called from inside a finalizer, not much we can do here */
425 * No need to create another thread 'cause the finalizer thread
426 * is still working and will take care of running the finalizers
432 /* We don't support domain finalization without a GC */
433 if (mono_gc_is_null ())
436 mono_gc_collect (mono_gc_max_generation ());
438 req = g_new0 (DomainFinalizationReq, 1);
440 req->domain = domain;
441 mono_coop_sem_init (&req->done, 0);
443 if (domain == mono_get_root_domain ())
444 finalizing_root_domain = TRUE;
446 mono_finalizer_lock ();
448 domains_to_finalize = g_slist_append (domains_to_finalize, req);
450 mono_finalizer_unlock ();
452 /* Tell the finalizer thread to finalize this appdomain */
453 mono_gc_finalize_notify ();
456 timeout = MONO_INFINITE_WAIT;
457 if (timeout != MONO_INFINITE_WAIT)
458 start = mono_msec_ticks ();
463 if (timeout == MONO_INFINITE_WAIT) {
464 res = mono_coop_sem_wait (&req->done, MONO_SEM_FLAGS_ALERTABLE);
466 gint64 elapsed = mono_msec_ticks () - start;
467 if (elapsed >= timeout) {
472 res = mono_coop_sem_timedwait (&req->done, timeout - elapsed, MONO_SEM_FLAGS_ALERTABLE);
475 if (res == MONO_SEM_TIMEDWAIT_RET_SUCCESS) {
477 } else if (res == MONO_SEM_TIMEDWAIT_RET_ALERTED) {
478 if ((thread->state & (ThreadState_AbortRequested | ThreadState_SuspendRequested)) != 0) {
482 } else if (res == MONO_SEM_TIMEDWAIT_RET_TIMEDOUT) {
486 g_error ("%s: unknown result %d", __func__, res);
491 /* Try removing the req from domains_to_finalize:
492 * - if it's not found: the domain is being finalized,
493 * so we the ref count is already decremented
494 * - if it's found: the domain is not yet being finalized,
495 * so we can safely decrement the ref */
499 mono_finalizer_lock ();
501 found = g_slist_index (domains_to_finalize, req) != -1;
503 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
505 mono_finalizer_unlock ();
508 /* We have to decrement it wherever we
509 * remove it from domains_to_finalize */
510 if (InterlockedDecrement (&req->ref) != 1)
511 g_error ("%s: req->ref should be 1, as we are the first one to decrement it", __func__);
518 if (InterlockedDecrement (&req->ref) == 0) {
519 mono_coop_sem_destroy (&req->done);
527 ves_icall_System_GC_InternalCollect (int generation)
529 mono_gc_collect (generation);
533 ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection)
536 mono_gc_collect (mono_gc_max_generation ());
537 return mono_gc_get_used_size ();
541 ves_icall_System_GC_KeepAlive (MonoObject *obj)
549 ves_icall_System_GC_ReRegisterForFinalize (MonoObject *obj)
551 MONO_CHECK_ARG_NULL (obj,);
553 object_register_finalizer (obj, mono_gc_run_finalize);
557 ves_icall_System_GC_SuppressFinalize (MonoObject *obj)
559 MONO_CHECK_ARG_NULL (obj,);
561 /* delegates have no finalizers, but we register them to deal with the
562 * unmanaged->managed trampoline. We don't let the user suppress it
563 * otherwise we'd leak it.
565 if (obj->vtable->klass->delegate)
568 /* FIXME: Need to handle case where obj has COM Callable Wrapper
569 * generated for it that needs cleaned up, but user wants to suppress
570 * their derived object finalizer. */
572 object_register_finalizer (obj, NULL);
576 ves_icall_System_GC_WaitForPendingFinalizers (void)
578 if (mono_gc_is_null ())
581 if (!mono_gc_pending_finalizers ())
584 if (mono_thread_internal_current () == gc_thread)
585 /* Avoid deadlocks */
589 If the finalizer thread is not live, lets pretend no finalizers are pending since the current thread might
590 be the one responsible for starting it up.
592 if (gc_thread == NULL)
596 ResetEvent (pending_done_event);
597 mono_gc_finalize_notify ();
598 /* g_print ("Waiting for pending finalizers....\n"); */
600 WaitForSingleObjectEx (pending_done_event, INFINITE, TRUE);
602 /* g_print ("Done pending....\n"); */
604 gboolean alerted = FALSE;
605 mono_coop_mutex_lock (&pending_done_mutex);
606 pending_done = FALSE;
607 mono_gc_finalize_notify ();
608 while (!pending_done) {
609 coop_cond_timedwait_alertable (&pending_done_cond, &pending_done_mutex, MONO_INFINITE_WAIT, &alerted);
613 mono_coop_mutex_unlock (&pending_done_mutex);
618 ves_icall_System_GC_register_ephemeron_array (MonoObject *array)
621 if (!mono_gc_ephemeron_array_add (array)) {
622 mono_set_pending_exception (mono_object_domain (array)->out_of_memory_ex);
629 ves_icall_System_GC_get_ephemeron_tombstone (void)
631 return mono_domain_get ()->ephemeron_tombstone;
635 ves_icall_System_GCHandle_GetTarget (guint32 handle)
637 return mono_gchandle_get_target (handle);
641 * if type == -1, change the target of the handle, otherwise allocate a new handle.
644 ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type)
647 mono_gchandle_set_target (handle, obj);
648 /* the handle doesn't change */
653 return mono_gchandle_new_weakref (obj, FALSE);
654 case HANDLE_WEAK_TRACK:
655 return mono_gchandle_new_weakref (obj, TRUE);
657 return mono_gchandle_new (obj, FALSE);
659 return mono_gchandle_new (obj, TRUE);
661 g_assert_not_reached ();
667 ves_icall_System_GCHandle_FreeHandle (guint32 handle)
669 mono_gchandle_free (handle);
673 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
677 if (MONO_GC_HANDLE_TYPE (handle) != HANDLE_PINNED)
679 obj = mono_gchandle_get_target (handle);
681 MonoClass *klass = mono_object_class (obj);
682 if (klass == mono_defaults.string_class) {
683 return mono_string_chars ((MonoString*)obj);
684 } else if (klass->rank) {
685 return mono_array_addr ((MonoArray*)obj, char, 0);
687 /* the C# code will check and throw the exception */
688 /* FIXME: missing !klass->blittable test, see bug #61134 */
689 if (mono_class_is_auto_layout (klass))
691 return (char*)obj + sizeof (MonoObject);
698 mono_gc_GCHandle_CheckCurrentDomain (guint32 gchandle)
700 return mono_gchandle_is_in_domain (gchandle, mono_domain_get ());
703 static MonoCoopSem finalizer_sem;
704 static volatile gboolean finished;
707 * mono_gc_finalize_notify:
709 * Notify the finalizer thread that finalizers etc.
710 * are available to be processed.
711 * This is async signal safe.
714 mono_gc_finalize_notify (void)
717 g_message ( "%s: prodding finalizer", __func__);
720 if (mono_gc_is_null ())
723 mono_coop_sem_post (&finalizer_sem);
727 This is the number of entries allowed in the hazard free queue before
728 we explicitly cycle the finalizer thread to trigger pumping the queue.
730 It was picked empirically by running the corlib test suite in a stress
731 scenario where all hazard entries are queued.
733 In this extreme scenario we double the number of times we cycle the finalizer
734 thread compared to just GC calls.
736 Entries are usually in the order of 100's of bytes each, so we're limiting
737 floating garbage to be in the order of a dozen kb.
739 static gboolean finalizer_thread_pulsed;
740 #define HAZARD_QUEUE_OVERFLOW_SIZE 20
743 hazard_free_queue_is_too_big (size_t size)
745 if (size < HAZARD_QUEUE_OVERFLOW_SIZE)
748 if (finalizer_thread_pulsed || InterlockedCompareExchange (&finalizer_thread_pulsed, TRUE, FALSE))
751 mono_gc_finalize_notify ();
755 hazard_free_queue_pump (void)
757 mono_thread_hazardous_try_free_all ();
758 finalizer_thread_pulsed = FALSE;
764 collect_objects (gpointer key, gpointer value, gpointer user_data)
766 GPtrArray *arr = (GPtrArray*)user_data;
767 g_ptr_array_add (arr, key);
773 * finalize_domain_objects:
775 * Run the finalizers of all finalizable objects in req->domain.
778 finalize_domain_objects (void)
780 DomainFinalizationReq *req = NULL;
783 if (domains_to_finalize) {
784 mono_finalizer_lock ();
785 if (domains_to_finalize) {
786 req = (DomainFinalizationReq *)domains_to_finalize->data;
787 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
789 mono_finalizer_unlock ();
795 domain = req->domain;
797 /* Process finalizers which are already in the queue */
798 mono_gc_invoke_finalizers ();
801 while (g_hash_table_size (domain->finalizable_objects_hash) > 0) {
805 * Since the domain is unloading, nobody is allowed to put
806 * new entries into the hash table. But finalize_object might
807 * remove entries from the hash table, so we make a copy.
809 objs = g_ptr_array_new ();
810 g_hash_table_foreach (domain->finalizable_objects_hash, collect_objects, objs);
811 /* printf ("FINALIZING %d OBJECTS.\n", objs->len); */
813 for (i = 0; i < objs->len; ++i) {
814 MonoObject *o = (MonoObject*)g_ptr_array_index (objs, i);
815 /* FIXME: Avoid finalizing threads, etc */
816 mono_gc_run_finalize (o, 0);
819 g_ptr_array_free (objs, TRUE);
821 #elif defined(HAVE_SGEN_GC)
822 mono_gc_finalize_domain (domain);
823 mono_gc_invoke_finalizers ();
826 /* cleanup the reference queue */
827 reference_queue_clear_for_domain (domain);
829 /* printf ("DONE.\n"); */
830 mono_coop_sem_post (&req->done);
832 if (InterlockedDecrement (&req->ref) == 0) {
833 /* mono_domain_finalize already returned, and
834 * doesn't hold a reference to req anymore. */
835 mono_coop_sem_destroy (&req->done);
841 finalizer_thread (gpointer unused)
844 gboolean wait = TRUE;
846 MonoString *finalizer = mono_string_new_checked (mono_get_root_domain (), "Finalizer", &error);
847 mono_error_assert_ok (&error);
848 mono_thread_set_name_internal (mono_thread_internal_current (), finalizer, FALSE, FALSE, &error);
849 mono_error_assert_ok (&error);
851 /* Register a hazard free queue pump callback */
852 mono_hazard_pointer_install_free_queue_size_callback (hazard_free_queue_is_too_big);
855 /* Wait to be notified that there's at least one
859 g_assert (mono_domain_get () == mono_get_root_domain ());
860 mono_gc_set_skip_thread (TRUE);
863 /* An alertable wait is required so this thread can be suspended on windows */
864 mono_coop_sem_wait (&finalizer_sem, MONO_SEM_FLAGS_ALERTABLE);
868 mono_gc_set_skip_thread (FALSE);
870 mono_threads_perform_thread_dump ();
872 mono_console_handle_async_ops ();
874 mono_attach_maybe_start ();
876 finalize_domain_objects ();
878 MONO_PROFILER_RAISE (gc_finalizing, ());
880 /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup),
881 * before the domain is unloaded.
883 mono_gc_invoke_finalizers ();
885 MONO_PROFILER_RAISE (gc_finalized, ());
887 mono_threads_join_threads ();
889 reference_queue_proccess_all ();
891 mono_w32process_signal_finished ();
893 hazard_free_queue_pump ();
895 /* Avoid posting the pending done event until there are pending finalizers */
896 if (mono_coop_sem_timedwait (&finalizer_sem, 0, MONO_SEM_FLAGS_NONE) == MONO_SEM_TIMEDWAIT_RET_SUCCESS) {
897 /* Don't wait again at the start of the loop */
901 SetEvent (pending_done_event);
903 mono_coop_mutex_lock (&pending_done_mutex);
905 mono_coop_cond_signal (&pending_done_cond);
906 mono_coop_mutex_unlock (&pending_done_mutex);
911 mono_finalizer_lock ();
912 finalizer_thread_exited = TRUE;
913 mono_coop_cond_signal (&exited_cond);
914 mono_finalizer_unlock ();
919 #ifndef LAZY_GC_THREAD_CREATION
923 mono_gc_init_finalizer_thread (void)
926 gc_thread = mono_thread_create_internal (mono_domain_get (), finalizer_thread, NULL, MONO_THREAD_CREATE_FLAGS_NONE, &error);
927 mono_error_assert_ok (&error);
933 mono_coop_mutex_init_recursive (&finalizer_mutex);
934 mono_coop_mutex_init_recursive (&reference_queue_mutex);
936 mono_counters_register ("Minor GC collections", MONO_COUNTER_GC | MONO_COUNTER_INT, &gc_stats.minor_gc_count);
937 mono_counters_register ("Major GC collections", MONO_COUNTER_GC | MONO_COUNTER_INT, &gc_stats.major_gc_count);
938 mono_counters_register ("Minor GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.minor_gc_time);
939 mono_counters_register ("Major GC time", MONO_COUNTER_GC | MONO_COUNTER_LONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time);
940 mono_counters_register ("Major GC time concurrent", MONO_COUNTER_GC | MONO_COUNTER_LONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time_concurrent);
942 mono_gc_base_init ();
944 if (mono_gc_is_disabled ()) {
950 pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
951 g_assert (pending_done_event);
953 mono_coop_cond_init (&pending_done_cond);
954 mono_coop_mutex_init (&pending_done_mutex);
957 mono_coop_cond_init (&exited_cond);
958 mono_coop_sem_init (&finalizer_sem, 0);
960 #ifndef LAZY_GC_THREAD_CREATION
961 mono_gc_init_finalizer_thread ();
966 mono_gc_cleanup (void)
969 g_message ("%s: cleaning up finalizer", __func__);
972 if (mono_gc_is_null ())
977 if (mono_thread_internal_current () != gc_thread) {
980 const gint64 timeout = 40 * 1000;
982 mono_gc_finalize_notify ();
984 start = mono_msec_ticks ();
986 /* Finishing the finalizer thread, so wait a little bit... */
987 /* MS seems to wait for about 2 seconds per finalizer thread */
988 /* and 40 seconds for all finalizers to finish */
992 if (finalizer_thread_exited) {
993 /* Wait for the thread to actually exit. We don't want the wait
994 * to be alertable, because we assert on the result to be SUCCESS_0 */
995 ret = guarded_wait (gc_thread->handle, MONO_INFINITE_WAIT, FALSE);
996 g_assert (ret == MONO_THREAD_INFO_WAIT_RET_SUCCESS_0);
998 mono_threads_add_joinable_thread (GUINT_TO_POINTER (gc_thread->tid));
1002 elapsed = mono_msec_ticks () - start;
1003 if (elapsed >= timeout) {
1006 /* Set a flag which the finalizer thread can check */
1007 suspend_finalizers = TRUE;
1008 mono_gc_suspend_finalizers ();
1010 /* Try to abort the thread, in the hope that it is running managed code */
1011 mono_thread_internal_abort (gc_thread);
1013 /* Wait for it to stop */
1014 ret = guarded_wait (gc_thread->handle, 100, FALSE);
1015 if (ret == MONO_THREAD_INFO_WAIT_RET_TIMEOUT) {
1016 /* The finalizer thread refused to exit, suspend it forever. */
1017 mono_thread_internal_suspend_for_shutdown (gc_thread);
1021 g_assert (ret == MONO_THREAD_INFO_WAIT_RET_SUCCESS_0);
1023 mono_threads_add_joinable_thread (GUINT_TO_POINTER (gc_thread->tid));
1027 mono_finalizer_lock ();
1028 if (!finalizer_thread_exited)
1029 mono_coop_cond_timedwait (&exited_cond, &finalizer_mutex, timeout - elapsed);
1030 mono_finalizer_unlock ();
1034 mono_gc_base_cleanup ();
1037 mono_reference_queue_cleanup ();
1039 mono_coop_mutex_destroy (&finalizer_mutex);
1040 mono_coop_mutex_destroy (&reference_queue_mutex);
1044 mono_gc_is_finalizer_internal_thread (MonoInternalThread *thread)
1046 return thread == gc_thread;
1050 * mono_gc_is_finalizer_thread:
1051 * \param thread the thread to test.
1053 * In Mono objects are finalized asynchronously on a separate thread.
1054 * This routine tests whether the \p thread argument represents the
1055 * finalization thread.
1057 * \returns TRUE if \p thread is the finalization thread.
1060 mono_gc_is_finalizer_thread (MonoThread *thread)
1062 return mono_gc_is_finalizer_internal_thread (thread->internal_thread);
1065 #if defined(__MACH__)
1066 static pthread_t mach_exception_thread;
1069 mono_gc_register_mach_exception_thread (pthread_t thread)
1071 mach_exception_thread = thread;
1075 mono_gc_get_mach_exception_thread (void)
1077 return mach_exception_thread;
1081 static MonoReferenceQueue *ref_queues;
1084 ref_list_remove_element (RefQueueEntry **prev, RefQueueEntry *element)
1087 /* Guard if head is changed concurrently. */
1088 while (*prev != element)
1089 prev = &(*prev)->next;
1090 } while (prev && InterlockedCompareExchangePointer ((volatile gpointer *)prev, element->next, element) != element);
1094 ref_list_push (RefQueueEntry **head, RefQueueEntry *value)
1096 RefQueueEntry *current;
1099 value->next = current;
1100 STORE_STORE_FENCE; /*Must make sure the previous store is visible before the CAS. */
1101 } while (InterlockedCompareExchangePointer ((volatile gpointer *)head, value, current) != current);
1105 reference_queue_proccess (MonoReferenceQueue *queue)
1107 RefQueueEntry **iter = &queue->queue;
1108 RefQueueEntry *entry;
1109 while ((entry = *iter)) {
1110 if (queue->should_be_deleted || !mono_gchandle_get_target (entry->gchandle)) {
1111 mono_gchandle_free ((guint32)entry->gchandle);
1112 ref_list_remove_element (iter, entry);
1113 queue->callback (entry->user_data);
1116 iter = &entry->next;
1122 reference_queue_proccess_all (void)
1124 MonoReferenceQueue **iter;
1125 MonoReferenceQueue *queue = ref_queues;
1126 for (; queue; queue = queue->next)
1127 reference_queue_proccess (queue);
1130 mono_coop_mutex_lock (&reference_queue_mutex);
1131 for (iter = &ref_queues; *iter;) {
1133 if (!queue->should_be_deleted) {
1134 iter = &queue->next;
1138 mono_coop_mutex_unlock (&reference_queue_mutex);
1139 reference_queue_proccess (queue);
1142 *iter = queue->next;
1145 mono_coop_mutex_unlock (&reference_queue_mutex);
1149 mono_reference_queue_cleanup (void)
1151 MonoReferenceQueue *queue = ref_queues;
1152 for (; queue; queue = queue->next)
1153 queue->should_be_deleted = TRUE;
1154 reference_queue_proccess_all ();
1158 reference_queue_clear_for_domain (MonoDomain *domain)
1160 MonoReferenceQueue *queue = ref_queues;
1161 for (; queue; queue = queue->next) {
1162 RefQueueEntry **iter = &queue->queue;
1163 RefQueueEntry *entry;
1164 while ((entry = *iter)) {
1165 if (entry->domain == domain) {
1166 mono_gchandle_free ((guint32)entry->gchandle);
1167 ref_list_remove_element (iter, entry);
1168 queue->callback (entry->user_data);
1171 iter = &entry->next;
1177 * mono_gc_reference_queue_new:
1178 * \param callback callback used when processing collected entries.
1180 * Create a new reference queue used to process collected objects.
1181 * A reference queue let you add a pair of (managed object, user data)
1182 * using the \c mono_gc_reference_queue_add method.
1184 * Once the managed object is collected \p callback will be called
1185 * in the finalizer thread with 'user data' as argument.
1187 * The callback is called from the finalizer thread without any locks held.
1188 * When an AppDomain is unloaded, all callbacks for objects belonging to it
1191 * \returns the new queue.
1194 mono_gc_reference_queue_new (mono_reference_queue_callback callback)
1196 MonoReferenceQueue *res = g_new0 (MonoReferenceQueue, 1);
1197 res->callback = callback;
1199 mono_coop_mutex_lock (&reference_queue_mutex);
1200 res->next = ref_queues;
1202 mono_coop_mutex_unlock (&reference_queue_mutex);
1208 * mono_gc_reference_queue_add:
1209 * \param queue the queue to add the reference to.
1210 * \param obj the object to be watched for collection
1211 * \param user_data parameter to be passed to the queue callback
1213 * Queue an object to be watched for collection, when the \p obj is
1214 * collected, the callback that was registered for the \p queue will
1215 * be invoked with \p user_data as argument.
1217 * \returns FALSE if the queue is scheduled to be freed.
1220 mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *user_data)
1222 RefQueueEntry *entry;
1223 if (queue->should_be_deleted)
1226 g_assert (obj != NULL);
1228 entry = g_new0 (RefQueueEntry, 1);
1229 entry->user_data = user_data;
1230 entry->domain = mono_object_domain (obj);
1232 entry->gchandle = mono_gchandle_new_weakref (obj, TRUE);
1233 mono_object_register_finalizer (obj);
1235 ref_list_push (&queue->queue, entry);
1240 * mono_gc_reference_queue_free:
1241 * \param queue the queue that should be freed.
1243 * This operation signals that \p queue should be freed. This operation is deferred
1244 * as it happens on the finalizer thread.
1246 * After this call, no further objects can be queued. It's the responsibility of the
1247 * caller to make sure that no further attempt to access queue will be made.
1250 mono_gc_reference_queue_free (MonoReferenceQueue *queue)
1252 queue->should_be_deleted = TRUE;