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/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>
50 typedef struct DomainFinalizationReq {
54 } DomainFinalizationReq;
56 static gboolean gc_disabled;
58 static gboolean finalizing_root_domain;
60 gboolean log_finalizers;
61 gboolean mono_do_not_finalize;
62 volatile gboolean suspend_finalizers;
63 gchar **mono_do_not_finalize_class_names ;
65 #define mono_finalizer_lock() mono_coop_mutex_lock (&finalizer_mutex)
66 #define mono_finalizer_unlock() mono_coop_mutex_unlock (&finalizer_mutex)
67 static MonoCoopMutex finalizer_mutex;
68 static MonoCoopMutex reference_queue_mutex;
70 static GSList *domains_to_finalize;
71 static MonoMList *threads_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);
95 guarded_wait (HANDLE handle, guint32 timeout, gboolean alertable)
100 result = WaitForSingleObjectEx (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);
122 * coop_cond_timedwait_alertable:
124 * Wait on COND/MUTEX. If ALERTABLE is non-null, the wait can be interrupted.
125 * In that case, *ALERTABLE will be set to TRUE, and 0 is returned.
128 coop_cond_timedwait_alertable (MonoCoopCond *cond, MonoCoopMutex *mutex, guint32 timeout_ms, gboolean *alertable)
133 BreakCoopAlertableWaitUD ud;
138 mono_thread_info_install_interrupt (break_coop_alertable_wait, &ud, alertable);
142 res = mono_coop_cond_timedwait (cond, mutex, timeout_ms);
144 mono_thread_info_uninstall_interrupt (alertable);
152 add_thread_to_finalize (MonoInternalThread *thread, MonoError *error)
154 mono_error_init (error);
155 mono_finalizer_lock ();
156 if (!threads_to_finalize)
157 MONO_GC_REGISTER_ROOT_SINGLE (threads_to_finalize, MONO_ROOT_SOURCE_FINALIZER_QUEUE, "finalizable threads list");
158 threads_to_finalize = mono_mlist_append_checked (threads_to_finalize, (MonoObject*)thread, error);
159 mono_finalizer_unlock ();
160 return is_ok (error);
164 * actually, we might want to queue the finalize requests in a separate thread,
165 * but we need to be careful about the execution domain of the thread...
168 mono_gc_run_finalize (void *obj, void *data)
171 MonoObject *exc = NULL;
176 MonoMethod* finalizer = NULL;
177 MonoDomain *caller_domain = mono_domain_get ();
179 RuntimeInvokeFunction runtime_invoke;
181 // This function is called from the innards of the GC, so our best alternative for now is to do polling here
182 mono_threads_safepoint ();
184 o = (MonoObject*)((char*)obj + GPOINTER_TO_UINT (data));
186 if (mono_do_not_finalize) {
187 if (!mono_do_not_finalize_class_names)
190 size_t namespace_len = strlen (o->vtable->klass->name_space);
191 for (int i = 0; mono_do_not_finalize_class_names [i]; ++i) {
192 const char *name = mono_do_not_finalize_class_names [i];
193 if (strncmp (name, o->vtable->klass->name_space, namespace_len))
195 if (name [namespace_len] != '.')
197 if (strcmp (name + namespace_len + 1, o->vtable->klass->name))
204 g_log ("mono-gc-finalizers", G_LOG_LEVEL_DEBUG, "<%s at %p> Starting finalizer checks.", o->vtable->klass->name, o);
206 if (suspend_finalizers)
209 domain = o->vtable->domain;
212 mono_domain_finalizers_lock (domain);
214 o2 = (MonoObject *)g_hash_table_lookup (domain->finalizable_objects_hash, o);
216 mono_domain_finalizers_unlock (domain);
219 /* Already finalized somehow */
223 /* make sure the finalizer is not called again if the object is resurrected */
224 object_register_finalizer ((MonoObject *)obj, NULL);
227 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Registered finalizer as processed.", o->vtable->klass->name, o);
229 if (o->vtable->klass == mono_defaults.internal_thread_class) {
230 MonoInternalThread *t = (MonoInternalThread*)o;
232 if (mono_gc_is_finalizer_internal_thread (t))
233 /* Avoid finalizing ourselves */
236 if (t->threadpool_thread && finalizing_root_domain) {
237 /* Don't finalize threadpool threads when
238 shutting down - they're finalized when the
239 threadpool shuts down. */
240 if (!add_thread_to_finalize (t, &error))
241 goto unhandled_error;
246 if (o->vtable->klass->image == mono_defaults.corlib && !strcmp (o->vtable->klass->name, "DynamicMethod") && finalizing_root_domain) {
248 * These can't be finalized during unloading/shutdown, since that would
249 * free the native code which can still be referenced by other
251 * FIXME: This is not perfect, objects dying at the same time as
252 * dynamic methods can still reference them even when !shutdown.
257 if (mono_runtime_get_no_exec ())
260 /* speedup later... and use a timeout */
261 /* g_print ("Finalize run on %p %s.%s\n", o, mono_object_class (o)->name_space, mono_object_class (o)->name); */
263 /* Use _internal here, since this thread can enter a doomed appdomain */
264 mono_domain_set_internal (mono_object_domain (o));
266 /* delegates that have a native function pointer allocated are
267 * registered for finalization, but they don't have a Finalize
268 * method, because in most cases it's not needed and it's just a waste.
270 if (o->vtable->klass->delegate) {
271 MonoDelegate* del = (MonoDelegate*)o;
272 if (del->delegate_trampoline)
273 mono_delegate_free_ftnptr ((MonoDelegate*)o);
274 mono_domain_set_internal (caller_domain);
278 finalizer = mono_class_get_finalizer (o->vtable->klass);
280 /* If object has a CCW but has no finalizer, it was only
281 * registered for finalization in order to free the CCW.
282 * Else it needs the regular finalizer run.
283 * FIXME: what to do about ressurection and suppression
284 * of finalizer on object with CCW.
286 if (mono_marshal_free_ccw (o) && !finalizer) {
287 mono_domain_set_internal (caller_domain);
292 * To avoid the locking plus the other overhead of mono_runtime_invoke_checked (),
293 * create and precompile a wrapper which calls the finalize method using
297 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Compiling finalizer.", o->vtable->klass->name, o);
299 if (!domain->finalize_runtime_invoke) {
300 MonoMethod *invoke = mono_marshal_get_runtime_invoke (mono_class_get_method_from_name_flags (mono_defaults.object_class, "Finalize", 0, 0), TRUE);
302 domain->finalize_runtime_invoke = mono_compile_method_checked (invoke, &error);
303 mono_error_assert_ok (&error); /* expect this not to fail */
306 runtime_invoke = (RuntimeInvokeFunction)domain->finalize_runtime_invoke;
308 mono_runtime_class_init_full (o->vtable, &error);
310 goto unhandled_error;
312 if (G_UNLIKELY (MONO_GC_FINALIZE_INVOKE_ENABLED ())) {
313 MONO_GC_FINALIZE_INVOKE ((unsigned long)o, mono_object_get_size (o),
314 o->vtable->klass->name_space, o->vtable->klass->name);
318 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Calling finalizer.", o->vtable->klass->name, o);
320 runtime_invoke (o, NULL, &exc, NULL);
323 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Returned from finalizer.", o->vtable->klass->name, o);
327 exc = (MonoObject*)mono_error_convert_to_exception (&error);
329 mono_thread_internal_unhandled_exception (exc);
331 mono_domain_set_internal (caller_domain);
335 mono_gc_finalize_threadpool_threads (void)
337 while (threads_to_finalize) {
338 MonoInternalThread *thread = (MonoInternalThread*) mono_mlist_get_data (threads_to_finalize);
340 /* Force finalization of the thread. */
341 thread->threadpool_thread = FALSE;
342 mono_object_register_finalizer ((MonoObject*)thread);
344 mono_gc_run_finalize (thread, NULL);
346 threads_to_finalize = mono_mlist_next (threads_to_finalize);
351 mono_gc_out_of_memory (size_t size)
354 * we could allocate at program startup some memory that we could release
355 * back to the system at this point if we're really low on memory (ie, size is
356 * lower than the memory we set apart)
358 mono_raise_exception (mono_domain_get ()->out_of_memory_ex);
364 * Some of our objects may point to a different address than the address returned by GC_malloc()
365 * (because of the GetHashCode hack), but we need to pass the real address to register_finalizer.
366 * This also means that in the callback we need to adjust the pointer to get back the real
368 * We also need to be consistent in the use of the GC_debug* variants of malloc and register_finalizer,
369 * since that, too, can cause the underlying pointer to be offset.
372 object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*))
376 g_assert (obj != NULL);
378 domain = obj->vtable->domain;
381 if (mono_domain_is_unloading (domain) && (callback != NULL))
383 * Can't register finalizers in a dying appdomain, since they
384 * could be invoked after the appdomain has been unloaded.
388 mono_domain_finalizers_lock (domain);
391 g_hash_table_insert (domain->finalizable_objects_hash, obj, obj);
393 g_hash_table_remove (domain->finalizable_objects_hash, obj);
395 mono_domain_finalizers_unlock (domain);
397 mono_gc_register_for_finalization (obj, callback);
398 #elif defined(HAVE_SGEN_GC)
400 * If we register finalizers for domains that are unloading we might
401 * end up running them while or after the domain is being cleared, so
402 * the objects will not be valid anymore.
404 if (!mono_domain_is_unloading (domain))
405 mono_gc_register_for_finalization (obj, callback);
410 * mono_object_register_finalizer:
411 * @obj: object to register
413 * Records that object @obj has a finalizer, this will call the
414 * Finalize method when the garbage collector disposes the object.
418 mono_object_register_finalizer (MonoObject *obj)
420 /* g_print ("Registered finalizer on %p %s.%s\n", obj, mono_object_class (obj)->name_space, mono_object_class (obj)->name); */
421 object_register_finalizer (obj, mono_gc_run_finalize);
425 * mono_domain_finalize:
426 * @domain: the domain to finalize
427 * @timeout: msects to wait for the finalization to complete, -1 to wait indefinitely
429 * Request finalization of all finalizable objects inside @domain. Wait
430 * @timeout msecs for the finalization to complete.
432 * Returns: TRUE if succeeded, FALSE if there was a timeout
436 mono_domain_finalize (MonoDomain *domain, guint32 timeout)
438 DomainFinalizationReq *req;
439 MonoInternalThread *thread = mono_thread_internal_current ();
444 #if defined(__native_client__)
448 if (mono_thread_internal_current () == gc_thread)
449 /* We are called from inside a finalizer, not much we can do here */
453 * No need to create another thread 'cause the finalizer thread
454 * is still working and will take care of running the finalizers
460 /* We don't support domain finalization without a GC */
461 if (mono_gc_is_null ())
464 mono_gc_collect (mono_gc_max_generation ());
466 req = g_new0 (DomainFinalizationReq, 1);
468 req->domain = domain;
469 mono_coop_sem_init (&req->done, 0);
471 if (domain == mono_get_root_domain ())
472 finalizing_root_domain = TRUE;
474 mono_finalizer_lock ();
476 domains_to_finalize = g_slist_append (domains_to_finalize, req);
478 mono_finalizer_unlock ();
480 /* Tell the finalizer thread to finalize this appdomain */
481 mono_gc_finalize_notify ();
485 if (timeout != INFINITE)
486 start = mono_msec_ticks ();
491 if (timeout == INFINITE) {
492 res = mono_coop_sem_wait (&req->done, MONO_SEM_FLAGS_ALERTABLE);
494 gint64 elapsed = mono_msec_ticks () - start;
495 if (elapsed >= timeout) {
500 res = mono_coop_sem_timedwait (&req->done, timeout - elapsed, MONO_SEM_FLAGS_ALERTABLE);
503 if (res == MONO_SEM_TIMEDWAIT_RET_SUCCESS) {
505 } else if (res == MONO_SEM_TIMEDWAIT_RET_ALERTED) {
506 if ((thread->state & (ThreadState_StopRequested | ThreadState_SuspendRequested)) != 0) {
510 } else if (res == MONO_SEM_TIMEDWAIT_RET_TIMEDOUT) {
514 g_error ("%s: unknown result %d", __func__, res);
519 /* Try removing the req from domains_to_finalize:
520 * - if it's not found: the domain is being finalized,
521 * so we the ref count is already decremented
522 * - if it's found: the domain is not yet being finalized,
523 * so we can safely decrement the ref */
527 mono_finalizer_lock ();
529 found = g_slist_index (domains_to_finalize, req) != -1;
531 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
533 mono_finalizer_unlock ();
536 /* We have to decrement it wherever we
537 * remove it from domains_to_finalize */
538 if (InterlockedDecrement (&req->ref) != 1)
539 g_error ("%s: req->ref should be 1, as we are the first one to decrement it", __func__);
545 if (domain == mono_get_root_domain ()) {
546 mono_threadpool_ms_cleanup ();
547 mono_gc_finalize_threadpool_threads ();
550 mono_profiler_appdomain_event (domain, MONO_PROFILE_END_UNLOAD);
553 if (InterlockedDecrement (&req->ref) == 0) {
554 mono_coop_sem_destroy (&req->done);
562 ves_icall_System_GC_InternalCollect (int generation)
564 mono_gc_collect (generation);
568 ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection)
571 mono_gc_collect (mono_gc_max_generation ());
572 return mono_gc_get_used_size ();
576 ves_icall_System_GC_KeepAlive (MonoObject *obj)
584 ves_icall_System_GC_ReRegisterForFinalize (MonoObject *obj)
586 MONO_CHECK_ARG_NULL (obj,);
588 object_register_finalizer (obj, mono_gc_run_finalize);
592 ves_icall_System_GC_SuppressFinalize (MonoObject *obj)
594 MONO_CHECK_ARG_NULL (obj,);
596 /* delegates have no finalizers, but we register them to deal with the
597 * unmanaged->managed trampoline. We don't let the user suppress it
598 * otherwise we'd leak it.
600 if (obj->vtable->klass->delegate)
603 /* FIXME: Need to handle case where obj has COM Callable Wrapper
604 * generated for it that needs cleaned up, but user wants to suppress
605 * their derived object finalizer. */
607 object_register_finalizer (obj, NULL);
611 ves_icall_System_GC_WaitForPendingFinalizers (void)
613 if (mono_gc_is_null ())
616 if (!mono_gc_pending_finalizers ())
619 if (mono_thread_internal_current () == gc_thread)
620 /* Avoid deadlocks */
624 If the finalizer thread is not live, lets pretend no finalizers are pending since the current thread might
625 be the one responsible for starting it up.
627 if (gc_thread == NULL)
631 ResetEvent (pending_done_event);
632 mono_gc_finalize_notify ();
633 /* g_print ("Waiting for pending finalizers....\n"); */
634 guarded_wait (pending_done_event, INFINITE, TRUE);
635 /* g_print ("Done pending....\n"); */
637 gboolean alerted = FALSE;
638 mono_coop_mutex_lock (&pending_done_mutex);
639 pending_done = FALSE;
640 mono_gc_finalize_notify ();
641 while (!pending_done) {
642 coop_cond_timedwait_alertable (&pending_done_cond, &pending_done_mutex, INFINITE, &alerted);
646 mono_coop_mutex_unlock (&pending_done_mutex);
651 ves_icall_System_GC_register_ephemeron_array (MonoObject *array)
654 if (!mono_gc_ephemeron_array_add (array)) {
655 mono_set_pending_exception (mono_object_domain (array)->out_of_memory_ex);
662 ves_icall_System_GC_get_ephemeron_tombstone (void)
664 return mono_domain_get ()->ephemeron_tombstone;
668 ves_icall_System_GCHandle_GetTarget (guint32 handle)
670 return mono_gchandle_get_target (handle);
674 * if type == -1, change the target of the handle, otherwise allocate a new handle.
677 ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type)
680 mono_gchandle_set_target (handle, obj);
681 /* the handle doesn't change */
686 return mono_gchandle_new_weakref (obj, FALSE);
687 case HANDLE_WEAK_TRACK:
688 return mono_gchandle_new_weakref (obj, TRUE);
690 return mono_gchandle_new (obj, FALSE);
692 return mono_gchandle_new (obj, TRUE);
694 g_assert_not_reached ();
700 ves_icall_System_GCHandle_FreeHandle (guint32 handle)
702 mono_gchandle_free (handle);
706 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
710 if (MONO_GC_HANDLE_TYPE (handle) != HANDLE_PINNED)
712 obj = mono_gchandle_get_target (handle);
714 MonoClass *klass = mono_object_class (obj);
715 if (klass == mono_defaults.string_class) {
716 return mono_string_chars ((MonoString*)obj);
717 } else if (klass->rank) {
718 return mono_array_addr ((MonoArray*)obj, char, 0);
720 /* the C# code will check and throw the exception */
721 /* FIXME: missing !klass->blittable test, see bug #61134 */
722 if ((klass->flags & TYPE_ATTRIBUTE_LAYOUT_MASK) == TYPE_ATTRIBUTE_AUTO_LAYOUT)
724 return (char*)obj + sizeof (MonoObject);
731 mono_gc_GCHandle_CheckCurrentDomain (guint32 gchandle)
733 return mono_gchandle_is_in_domain (gchandle, mono_domain_get ());
736 static MonoCoopSem finalizer_sem;
737 static volatile gboolean finished;
740 * mono_gc_finalize_notify:
742 * Notify the finalizer thread that finalizers etc.
743 * are available to be processed.
746 mono_gc_finalize_notify (void)
749 g_message ( "%s: prodding finalizer", __func__);
752 if (mono_gc_is_null ())
755 mono_coop_sem_post (&finalizer_sem);
759 This is the number of entries allowed in the hazard free queue before
760 we explicitly cycle the finalizer thread to trigger pumping the queue.
762 It was picked empirically by running the corlib test suite in a stress
763 scenario where all hazard entries are queued.
765 In this extreme scenario we double the number of times we cycle the finalizer
766 thread compared to just GC calls.
768 Entries are usually in the order of 100's of bytes each, so we're limiting
769 floating garbage to be in the order of a dozen kb.
771 static gboolean finalizer_thread_pulsed;
772 #define HAZARD_QUEUE_OVERFLOW_SIZE 20
775 hazard_free_queue_is_too_big (size_t size)
777 if (size < HAZARD_QUEUE_OVERFLOW_SIZE)
780 if (finalizer_thread_pulsed || InterlockedCompareExchange (&finalizer_thread_pulsed, TRUE, FALSE))
783 mono_gc_finalize_notify ();
787 hazard_free_queue_pump (void)
789 mono_thread_hazardous_try_free_all ();
790 finalizer_thread_pulsed = FALSE;
796 collect_objects (gpointer key, gpointer value, gpointer user_data)
798 GPtrArray *arr = (GPtrArray*)user_data;
799 g_ptr_array_add (arr, key);
805 * finalize_domain_objects:
807 * Run the finalizers of all finalizable objects in req->domain.
810 finalize_domain_objects (void)
812 DomainFinalizationReq *req = NULL;
815 if (domains_to_finalize) {
816 mono_finalizer_lock ();
817 if (domains_to_finalize) {
818 req = (DomainFinalizationReq *)domains_to_finalize->data;
819 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
821 mono_finalizer_unlock ();
827 domain = req->domain;
829 /* Process finalizers which are already in the queue */
830 mono_gc_invoke_finalizers ();
833 while (g_hash_table_size (domain->finalizable_objects_hash) > 0) {
837 * Since the domain is unloading, nobody is allowed to put
838 * new entries into the hash table. But finalize_object might
839 * remove entries from the hash table, so we make a copy.
841 objs = g_ptr_array_new ();
842 g_hash_table_foreach (domain->finalizable_objects_hash, collect_objects, objs);
843 /* printf ("FINALIZING %d OBJECTS.\n", objs->len); */
845 for (i = 0; i < objs->len; ++i) {
846 MonoObject *o = (MonoObject*)g_ptr_array_index (objs, i);
847 /* FIXME: Avoid finalizing threads, etc */
848 mono_gc_run_finalize (o, 0);
851 g_ptr_array_free (objs, TRUE);
853 #elif defined(HAVE_SGEN_GC)
854 mono_gc_finalize_domain (domain);
855 mono_gc_invoke_finalizers ();
858 /* cleanup the reference queue */
859 reference_queue_clear_for_domain (domain);
861 /* printf ("DONE.\n"); */
862 mono_coop_sem_post (&req->done);
864 if (InterlockedDecrement (&req->ref) == 0) {
865 /* mono_domain_finalize already returned, and
866 * doesn't hold a reference to req anymore. */
867 mono_coop_sem_destroy (&req->done);
873 finalizer_thread (gpointer unused)
876 gboolean wait = TRUE;
878 mono_thread_set_name_internal (mono_thread_internal_current (), mono_string_new (mono_get_root_domain (), "Finalizer"), FALSE, &error);
879 mono_error_assert_ok (&error);
881 /* Register a hazard free queue pump callback */
882 mono_hazard_pointer_install_free_queue_size_callback (hazard_free_queue_is_too_big);
885 /* Wait to be notified that there's at least one
889 g_assert (mono_domain_get () == mono_get_root_domain ());
890 mono_gc_set_skip_thread (TRUE);
893 /* An alertable wait is required so this thread can be suspended on windows */
894 mono_coop_sem_wait (&finalizer_sem, MONO_SEM_FLAGS_ALERTABLE);
898 mono_gc_set_skip_thread (FALSE);
900 mono_threads_perform_thread_dump ();
902 mono_console_handle_async_ops ();
904 mono_attach_maybe_start ();
906 finalize_domain_objects ();
908 /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup),
909 * before the domain is unloaded.
911 mono_gc_invoke_finalizers ();
913 mono_threads_join_threads ();
915 reference_queue_proccess_all ();
917 hazard_free_queue_pump ();
919 /* Avoid posting the pending done event until there are pending finalizers */
920 if (mono_coop_sem_timedwait (&finalizer_sem, 0, MONO_SEM_FLAGS_NONE) == MONO_SEM_TIMEDWAIT_RET_SUCCESS) {
921 /* Don't wait again at the start of the loop */
925 SetEvent (pending_done_event);
927 mono_coop_mutex_lock (&pending_done_mutex);
929 mono_coop_cond_signal (&pending_done_cond);
930 mono_coop_mutex_unlock (&pending_done_mutex);
935 mono_finalizer_lock ();
936 finalizer_thread_exited = TRUE;
937 mono_coop_cond_signal (&exited_cond);
938 mono_finalizer_unlock ();
943 #ifndef LAZY_GC_THREAD_CREATION
947 mono_gc_init_finalizer_thread (void)
950 gc_thread = mono_thread_create_internal (mono_domain_get (), finalizer_thread, NULL, FALSE, 0, &error);
951 mono_error_assert_ok (&error);
957 mono_coop_mutex_init_recursive (&finalizer_mutex);
958 mono_coop_mutex_init_recursive (&reference_queue_mutex);
960 mono_counters_register ("Minor GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.minor_gc_count);
961 mono_counters_register ("Major GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.major_gc_count);
962 mono_counters_register ("Minor GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.minor_gc_time);
963 mono_counters_register ("Major GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time);
964 mono_counters_register ("Major GC time concurrent", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time_concurrent);
966 mono_gc_base_init ();
968 if (mono_gc_is_disabled ()) {
974 pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
975 g_assert (pending_done_event);
977 mono_coop_cond_init (&pending_done_cond);
978 mono_coop_mutex_init (&pending_done_mutex);
981 mono_coop_cond_init (&exited_cond);
982 mono_coop_sem_init (&finalizer_sem, 0);
984 #ifndef LAZY_GC_THREAD_CREATION
985 mono_gc_init_finalizer_thread ();
990 mono_gc_cleanup (void)
993 g_message ("%s: cleaning up finalizer", __func__);
996 if (mono_gc_is_null ())
1001 if (mono_thread_internal_current () != gc_thread) {
1002 gint64 start_ticks = mono_msec_ticks ();
1003 gint64 end_ticks = start_ticks + 2000;
1005 mono_gc_finalize_notify ();
1006 /* Finishing the finalizer thread, so wait a little bit... */
1007 /* MS seems to wait for about 2 seconds */
1008 while (!finalizer_thread_exited) {
1009 gint64 current_ticks = mono_msec_ticks ();
1012 if (current_ticks >= end_ticks)
1015 timeout = end_ticks - current_ticks;
1016 mono_finalizer_lock ();
1017 if (!finalizer_thread_exited)
1018 mono_coop_cond_timedwait (&exited_cond, &finalizer_mutex, timeout);
1019 mono_finalizer_unlock ();
1022 if (!finalizer_thread_exited) {
1025 /* Set a flag which the finalizer thread can check */
1026 suspend_finalizers = TRUE;
1027 mono_gc_suspend_finalizers ();
1029 /* Try to abort the thread, in the hope that it is running managed code */
1030 mono_thread_internal_abort (gc_thread);
1032 /* Wait for it to stop */
1033 ret = guarded_wait (gc_thread->handle, 100, TRUE);
1035 if (ret == WAIT_TIMEOUT) {
1037 * The finalizer thread refused to exit. Make it stop.
1039 mono_thread_internal_stop (gc_thread);
1040 ret = guarded_wait (gc_thread->handle, 100, TRUE);
1041 g_assert (ret != WAIT_TIMEOUT);
1042 /* The thread can't set this flag */
1043 finalizer_thread_exited = TRUE;
1049 /* Wait for the thread to actually exit */
1050 ret = guarded_wait (gc_thread->handle, INFINITE, TRUE);
1051 g_assert (ret == WAIT_OBJECT_0);
1053 mono_thread_join (GUINT_TO_POINTER (gc_thread->tid));
1054 g_assert (finalizer_thread_exited);
1057 mono_gc_base_cleanup ();
1060 mono_reference_queue_cleanup ();
1062 mono_coop_mutex_destroy (&finalizer_mutex);
1063 mono_coop_mutex_destroy (&reference_queue_mutex);
1067 mono_gc_is_finalizer_internal_thread (MonoInternalThread *thread)
1069 return thread == gc_thread;
1073 * mono_gc_is_finalizer_thread:
1074 * @thread: the thread to test.
1076 * In Mono objects are finalized asynchronously on a separate thread.
1077 * This routine tests whether the @thread argument represents the
1078 * finalization thread.
1080 * Returns: TRUE if @thread is the finalization thread.
1083 mono_gc_is_finalizer_thread (MonoThread *thread)
1085 return mono_gc_is_finalizer_internal_thread (thread->internal_thread);
1088 #if defined(__MACH__)
1089 static pthread_t mach_exception_thread;
1092 mono_gc_register_mach_exception_thread (pthread_t thread)
1094 mach_exception_thread = thread;
1098 mono_gc_get_mach_exception_thread (void)
1100 return mach_exception_thread;
1104 static MonoReferenceQueue *ref_queues;
1107 ref_list_remove_element (RefQueueEntry **prev, RefQueueEntry *element)
1110 /* Guard if head is changed concurrently. */
1111 while (*prev != element)
1112 prev = &(*prev)->next;
1113 } while (prev && InterlockedCompareExchangePointer ((volatile gpointer *)prev, element->next, element) != element);
1117 ref_list_push (RefQueueEntry **head, RefQueueEntry *value)
1119 RefQueueEntry *current;
1122 value->next = current;
1123 STORE_STORE_FENCE; /*Must make sure the previous store is visible before the CAS. */
1124 } while (InterlockedCompareExchangePointer ((volatile gpointer *)head, value, current) != current);
1128 reference_queue_proccess (MonoReferenceQueue *queue)
1130 RefQueueEntry **iter = &queue->queue;
1131 RefQueueEntry *entry;
1132 while ((entry = *iter)) {
1133 if (queue->should_be_deleted || !mono_gchandle_get_target (entry->gchandle)) {
1134 mono_gchandle_free ((guint32)entry->gchandle);
1135 ref_list_remove_element (iter, entry);
1136 queue->callback (entry->user_data);
1139 iter = &entry->next;
1145 reference_queue_proccess_all (void)
1147 MonoReferenceQueue **iter;
1148 MonoReferenceQueue *queue = ref_queues;
1149 for (; queue; queue = queue->next)
1150 reference_queue_proccess (queue);
1153 mono_coop_mutex_lock (&reference_queue_mutex);
1154 for (iter = &ref_queues; *iter;) {
1156 if (!queue->should_be_deleted) {
1157 iter = &queue->next;
1161 mono_coop_mutex_unlock (&reference_queue_mutex);
1162 reference_queue_proccess (queue);
1165 *iter = queue->next;
1168 mono_coop_mutex_unlock (&reference_queue_mutex);
1172 mono_reference_queue_cleanup (void)
1174 MonoReferenceQueue *queue = ref_queues;
1175 for (; queue; queue = queue->next)
1176 queue->should_be_deleted = TRUE;
1177 reference_queue_proccess_all ();
1181 reference_queue_clear_for_domain (MonoDomain *domain)
1183 MonoReferenceQueue *queue = ref_queues;
1184 for (; queue; queue = queue->next) {
1185 RefQueueEntry **iter = &queue->queue;
1186 RefQueueEntry *entry;
1187 while ((entry = *iter)) {
1188 if (entry->domain == domain) {
1189 mono_gchandle_free ((guint32)entry->gchandle);
1190 ref_list_remove_element (iter, entry);
1191 queue->callback (entry->user_data);
1194 iter = &entry->next;
1200 * mono_gc_reference_queue_new:
1201 * @callback callback used when processing collected entries.
1203 * Create a new reference queue used to process collected objects.
1204 * A reference queue let you add a pair of (managed object, user data)
1205 * using the mono_gc_reference_queue_add method.
1207 * Once the managed object is collected @callback will be called
1208 * in the finalizer thread with 'user data' as argument.
1210 * The callback is called from the finalizer thread without any locks held.
1211 * When a AppDomain is unloaded, all callbacks for objects belonging to it
1214 * @returns the new queue.
1217 mono_gc_reference_queue_new (mono_reference_queue_callback callback)
1219 MonoReferenceQueue *res = g_new0 (MonoReferenceQueue, 1);
1220 res->callback = callback;
1222 mono_coop_mutex_lock (&reference_queue_mutex);
1223 res->next = ref_queues;
1225 mono_coop_mutex_unlock (&reference_queue_mutex);
1231 * mono_gc_reference_queue_add:
1232 * @queue the queue to add the reference to.
1233 * @obj the object to be watched for collection
1234 * @user_data parameter to be passed to the queue callback
1236 * Queue an object to be watched for collection, when the @obj is
1237 * collected, the callback that was registered for the @queue will
1238 * be invoked with @user_data as argument.
1240 * @returns false if the queue is scheduled to be freed.
1243 mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *user_data)
1245 RefQueueEntry *entry;
1246 if (queue->should_be_deleted)
1249 g_assert (obj != NULL);
1251 entry = g_new0 (RefQueueEntry, 1);
1252 entry->user_data = user_data;
1253 entry->domain = mono_object_domain (obj);
1255 entry->gchandle = mono_gchandle_new_weakref (obj, TRUE);
1256 mono_object_register_finalizer (obj);
1258 ref_list_push (&queue->queue, entry);
1263 * mono_gc_reference_queue_free:
1264 * @queue the queue that should be freed.
1266 * This operation signals that @queue should be freed. This operation is deferred
1267 * as it happens on the finalizer thread.
1269 * After this call, no further objects can be queued. It's the responsibility of the
1270 * caller to make sure that no further attempt to access queue will be made.
1273 mono_gc_reference_queue_free (MonoReferenceQueue *queue)
1275 queue->should_be_deleted = TRUE;