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);
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 add_thread_to_finalize (MonoInternalThread *thread, MonoError *error)
163 mono_error_init (error);
164 mono_finalizer_lock ();
165 if (!threads_to_finalize)
166 MONO_GC_REGISTER_ROOT_SINGLE (threads_to_finalize, MONO_ROOT_SOURCE_FINALIZER_QUEUE, "finalizable threads list");
167 threads_to_finalize = mono_mlist_append_checked (threads_to_finalize, (MonoObject*)thread, error);
168 mono_finalizer_unlock ();
169 return is_ok (error);
173 * actually, we might want to queue the finalize requests in a separate thread,
174 * but we need to be careful about the execution domain of the thread...
177 mono_gc_run_finalize (void *obj, void *data)
180 MonoObject *exc = NULL;
185 MonoMethod* finalizer = NULL;
186 MonoDomain *caller_domain = mono_domain_get ();
188 RuntimeInvokeFunction runtime_invoke;
190 // This function is called from the innards of the GC, so our best alternative for now is to do polling here
191 mono_threads_safepoint ();
193 o = (MonoObject*)((char*)obj + GPOINTER_TO_UINT (data));
195 if (mono_do_not_finalize) {
196 if (!mono_do_not_finalize_class_names)
199 size_t namespace_len = strlen (o->vtable->klass->name_space);
200 for (int i = 0; mono_do_not_finalize_class_names [i]; ++i) {
201 const char *name = mono_do_not_finalize_class_names [i];
202 if (strncmp (name, o->vtable->klass->name_space, namespace_len))
204 if (name [namespace_len] != '.')
206 if (strcmp (name + namespace_len + 1, o->vtable->klass->name))
213 g_log ("mono-gc-finalizers", G_LOG_LEVEL_DEBUG, "<%s at %p> Starting finalizer checks.", o->vtable->klass->name, o);
215 if (suspend_finalizers)
218 domain = o->vtable->domain;
221 mono_domain_finalizers_lock (domain);
223 o2 = (MonoObject *)g_hash_table_lookup (domain->finalizable_objects_hash, o);
225 mono_domain_finalizers_unlock (domain);
228 /* Already finalized somehow */
232 /* make sure the finalizer is not called again if the object is resurrected */
233 object_register_finalizer ((MonoObject *)obj, NULL);
236 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Registered finalizer as processed.", o->vtable->klass->name, o);
238 if (o->vtable->klass == mono_defaults.internal_thread_class) {
239 MonoInternalThread *t = (MonoInternalThread*)o;
241 if (mono_gc_is_finalizer_internal_thread (t))
242 /* Avoid finalizing ourselves */
245 if (t->threadpool_thread && finalizing_root_domain) {
246 /* Don't finalize threadpool threads when
247 shutting down - they're finalized when the
248 threadpool shuts down. */
249 if (!add_thread_to_finalize (t, &error))
250 goto unhandled_error;
255 if (o->vtable->klass->image == mono_defaults.corlib && !strcmp (o->vtable->klass->name, "DynamicMethod") && finalizing_root_domain) {
257 * These can't be finalized during unloading/shutdown, since that would
258 * free the native code which can still be referenced by other
260 * FIXME: This is not perfect, objects dying at the same time as
261 * dynamic methods can still reference them even when !shutdown.
266 if (mono_runtime_get_no_exec ())
269 /* speedup later... and use a timeout */
270 /* g_print ("Finalize run on %p %s.%s\n", o, mono_object_class (o)->name_space, mono_object_class (o)->name); */
272 /* Use _internal here, since this thread can enter a doomed appdomain */
273 mono_domain_set_internal (mono_object_domain (o));
275 /* delegates that have a native function pointer allocated are
276 * registered for finalization, but they don't have a Finalize
277 * method, because in most cases it's not needed and it's just a waste.
279 if (o->vtable->klass->delegate) {
280 MonoDelegate* del = (MonoDelegate*)o;
281 if (del->delegate_trampoline)
282 mono_delegate_free_ftnptr ((MonoDelegate*)o);
283 mono_domain_set_internal (caller_domain);
287 finalizer = mono_class_get_finalizer (o->vtable->klass);
289 /* If object has a CCW but has no finalizer, it was only
290 * registered for finalization in order to free the CCW.
291 * Else it needs the regular finalizer run.
292 * FIXME: what to do about ressurection and suppression
293 * of finalizer on object with CCW.
295 if (mono_marshal_free_ccw (o) && !finalizer) {
296 mono_domain_set_internal (caller_domain);
301 * To avoid the locking plus the other overhead of mono_runtime_invoke_checked (),
302 * create and precompile a wrapper which calls the finalize method using
306 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Compiling finalizer.", o->vtable->klass->name, o);
308 if (!domain->finalize_runtime_invoke) {
309 MonoMethod *invoke = mono_marshal_get_runtime_invoke (mono_class_get_method_from_name_flags (mono_defaults.object_class, "Finalize", 0, 0), TRUE);
311 domain->finalize_runtime_invoke = mono_compile_method_checked (invoke, &error);
312 mono_error_assert_ok (&error); /* expect this not to fail */
315 runtime_invoke = (RuntimeInvokeFunction)domain->finalize_runtime_invoke;
317 mono_runtime_class_init_full (o->vtable, &error);
319 goto unhandled_error;
321 if (G_UNLIKELY (MONO_GC_FINALIZE_INVOKE_ENABLED ())) {
322 MONO_GC_FINALIZE_INVOKE ((unsigned long)o, mono_object_get_size (o),
323 o->vtable->klass->name_space, o->vtable->klass->name);
327 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Calling finalizer.", o->vtable->klass->name, o);
329 mono_profiler_gc_finalize_object_begin (o);
331 runtime_invoke (o, NULL, &exc, NULL);
333 mono_profiler_gc_finalize_object_end (o);
336 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Returned from finalizer.", o->vtable->klass->name, o);
340 exc = (MonoObject*)mono_error_convert_to_exception (&error);
342 mono_thread_internal_unhandled_exception (exc);
344 mono_domain_set_internal (caller_domain);
348 mono_gc_finalize_threadpool_threads (void)
350 while (threads_to_finalize) {
351 MonoInternalThread *thread = (MonoInternalThread*) mono_mlist_get_data (threads_to_finalize);
353 /* Force finalization of the thread. */
354 thread->threadpool_thread = FALSE;
355 mono_object_register_finalizer ((MonoObject*)thread);
357 mono_gc_run_finalize (thread, NULL);
359 threads_to_finalize = mono_mlist_next (threads_to_finalize);
364 mono_gc_out_of_memory (size_t size)
367 * we could allocate at program startup some memory that we could release
368 * back to the system at this point if we're really low on memory (ie, size is
369 * lower than the memory we set apart)
371 mono_raise_exception (mono_domain_get ()->out_of_memory_ex);
377 * Some of our objects may point to a different address than the address returned by GC_malloc()
378 * (because of the GetHashCode hack), but we need to pass the real address to register_finalizer.
379 * This also means that in the callback we need to adjust the pointer to get back the real
381 * We also need to be consistent in the use of the GC_debug* variants of malloc and register_finalizer,
382 * since that, too, can cause the underlying pointer to be offset.
385 object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*))
389 g_assert (obj != NULL);
391 domain = obj->vtable->domain;
394 if (mono_domain_is_unloading (domain) && (callback != NULL))
396 * Can't register finalizers in a dying appdomain, since they
397 * could be invoked after the appdomain has been unloaded.
401 mono_domain_finalizers_lock (domain);
404 g_hash_table_insert (domain->finalizable_objects_hash, obj, obj);
406 g_hash_table_remove (domain->finalizable_objects_hash, obj);
408 mono_domain_finalizers_unlock (domain);
410 mono_gc_register_for_finalization (obj, callback);
411 #elif defined(HAVE_SGEN_GC)
413 * If we register finalizers for domains that are unloading we might
414 * end up running them while or after the domain is being cleared, so
415 * the objects will not be valid anymore.
417 if (!mono_domain_is_unloading (domain))
418 mono_gc_register_for_finalization (obj, callback);
423 * mono_object_register_finalizer:
424 * @obj: object to register
426 * Records that object @obj has a finalizer, this will call the
427 * Finalize method when the garbage collector disposes the object.
431 mono_object_register_finalizer (MonoObject *obj)
433 /* g_print ("Registered finalizer on %p %s.%s\n", obj, mono_object_class (obj)->name_space, mono_object_class (obj)->name); */
434 object_register_finalizer (obj, mono_gc_run_finalize);
438 * mono_domain_finalize:
439 * @domain: the domain to finalize
440 * @timeout: msects to wait for the finalization to complete, -1 to wait indefinitely
442 * Request finalization of all finalizable objects inside @domain. Wait
443 * @timeout msecs for the finalization to complete.
445 * Returns: TRUE if succeeded, FALSE if there was a timeout
449 mono_domain_finalize (MonoDomain *domain, guint32 timeout)
451 DomainFinalizationReq *req;
452 MonoInternalThread *thread = mono_thread_internal_current ();
457 #if defined(__native_client__)
461 if (mono_thread_internal_current () == gc_thread)
462 /* We are called from inside a finalizer, not much we can do here */
466 * No need to create another thread 'cause the finalizer thread
467 * is still working and will take care of running the finalizers
473 /* We don't support domain finalization without a GC */
474 if (mono_gc_is_null ())
477 mono_gc_collect (mono_gc_max_generation ());
479 req = g_new0 (DomainFinalizationReq, 1);
481 req->domain = domain;
482 mono_coop_sem_init (&req->done, 0);
484 if (domain == mono_get_root_domain ())
485 finalizing_root_domain = TRUE;
487 mono_finalizer_lock ();
489 domains_to_finalize = g_slist_append (domains_to_finalize, req);
491 mono_finalizer_unlock ();
493 /* Tell the finalizer thread to finalize this appdomain */
494 mono_gc_finalize_notify ();
498 if (timeout != INFINITE)
499 start = mono_msec_ticks ();
504 if (timeout == INFINITE) {
505 res = mono_coop_sem_wait (&req->done, MONO_SEM_FLAGS_ALERTABLE);
507 gint64 elapsed = mono_msec_ticks () - start;
508 if (elapsed >= timeout) {
513 res = mono_coop_sem_timedwait (&req->done, timeout - elapsed, MONO_SEM_FLAGS_ALERTABLE);
516 if (res == MONO_SEM_TIMEDWAIT_RET_SUCCESS) {
518 } else if (res == MONO_SEM_TIMEDWAIT_RET_ALERTED) {
519 if ((thread->state & (ThreadState_StopRequested | ThreadState_SuspendRequested)) != 0) {
523 } else if (res == MONO_SEM_TIMEDWAIT_RET_TIMEDOUT) {
527 g_error ("%s: unknown result %d", __func__, res);
532 /* Try removing the req from domains_to_finalize:
533 * - if it's not found: the domain is being finalized,
534 * so we the ref count is already decremented
535 * - if it's found: the domain is not yet being finalized,
536 * so we can safely decrement the ref */
540 mono_finalizer_lock ();
542 found = g_slist_index (domains_to_finalize, req) != -1;
544 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
546 mono_finalizer_unlock ();
549 /* We have to decrement it wherever we
550 * remove it from domains_to_finalize */
551 if (InterlockedDecrement (&req->ref) != 1)
552 g_error ("%s: req->ref should be 1, as we are the first one to decrement it", __func__);
558 if (domain == mono_get_root_domain ()) {
559 mono_threadpool_ms_cleanup ();
560 mono_gc_finalize_threadpool_threads ();
564 if (InterlockedDecrement (&req->ref) == 0) {
565 mono_coop_sem_destroy (&req->done);
573 ves_icall_System_GC_InternalCollect (int generation)
575 mono_gc_collect (generation);
579 ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection)
582 mono_gc_collect (mono_gc_max_generation ());
583 return mono_gc_get_used_size ();
587 ves_icall_System_GC_KeepAlive (MonoObject *obj)
595 ves_icall_System_GC_ReRegisterForFinalize (MonoObject *obj)
597 MONO_CHECK_ARG_NULL (obj,);
599 object_register_finalizer (obj, mono_gc_run_finalize);
603 ves_icall_System_GC_SuppressFinalize (MonoObject *obj)
605 MONO_CHECK_ARG_NULL (obj,);
607 /* delegates have no finalizers, but we register them to deal with the
608 * unmanaged->managed trampoline. We don't let the user suppress it
609 * otherwise we'd leak it.
611 if (obj->vtable->klass->delegate)
614 /* FIXME: Need to handle case where obj has COM Callable Wrapper
615 * generated for it that needs cleaned up, but user wants to suppress
616 * their derived object finalizer. */
618 object_register_finalizer (obj, NULL);
622 ves_icall_System_GC_WaitForPendingFinalizers (void)
624 if (mono_gc_is_null ())
627 if (!mono_gc_pending_finalizers ())
630 if (mono_thread_internal_current () == gc_thread)
631 /* Avoid deadlocks */
635 If the finalizer thread is not live, lets pretend no finalizers are pending since the current thread might
636 be the one responsible for starting it up.
638 if (gc_thread == NULL)
642 ResetEvent (pending_done_event);
643 mono_gc_finalize_notify ();
644 /* g_print ("Waiting for pending finalizers....\n"); */
646 WaitForSingleObjectEx (pending_done_event, INFINITE, TRUE);
648 /* g_print ("Done pending....\n"); */
650 gboolean alerted = FALSE;
651 mono_coop_mutex_lock (&pending_done_mutex);
652 pending_done = FALSE;
653 mono_gc_finalize_notify ();
654 while (!pending_done) {
655 coop_cond_timedwait_alertable (&pending_done_cond, &pending_done_mutex, INFINITE, &alerted);
659 mono_coop_mutex_unlock (&pending_done_mutex);
664 ves_icall_System_GC_register_ephemeron_array (MonoObject *array)
667 if (!mono_gc_ephemeron_array_add (array)) {
668 mono_set_pending_exception (mono_object_domain (array)->out_of_memory_ex);
675 ves_icall_System_GC_get_ephemeron_tombstone (void)
677 return mono_domain_get ()->ephemeron_tombstone;
681 ves_icall_System_GCHandle_GetTarget (guint32 handle)
683 return mono_gchandle_get_target (handle);
687 * if type == -1, change the target of the handle, otherwise allocate a new handle.
690 ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type)
693 mono_gchandle_set_target (handle, obj);
694 /* the handle doesn't change */
699 return mono_gchandle_new_weakref (obj, FALSE);
700 case HANDLE_WEAK_TRACK:
701 return mono_gchandle_new_weakref (obj, TRUE);
703 return mono_gchandle_new (obj, FALSE);
705 return mono_gchandle_new (obj, TRUE);
707 g_assert_not_reached ();
713 ves_icall_System_GCHandle_FreeHandle (guint32 handle)
715 mono_gchandle_free (handle);
719 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
723 if (MONO_GC_HANDLE_TYPE (handle) != HANDLE_PINNED)
725 obj = mono_gchandle_get_target (handle);
727 MonoClass *klass = mono_object_class (obj);
728 if (klass == mono_defaults.string_class) {
729 return mono_string_chars ((MonoString*)obj);
730 } else if (klass->rank) {
731 return mono_array_addr ((MonoArray*)obj, char, 0);
733 /* the C# code will check and throw the exception */
734 /* FIXME: missing !klass->blittable test, see bug #61134 */
735 if (mono_class_is_auto_layout (klass))
737 return (char*)obj + sizeof (MonoObject);
744 mono_gc_GCHandle_CheckCurrentDomain (guint32 gchandle)
746 return mono_gchandle_is_in_domain (gchandle, mono_domain_get ());
749 static MonoCoopSem finalizer_sem;
750 static volatile gboolean finished;
753 * mono_gc_finalize_notify:
755 * Notify the finalizer thread that finalizers etc.
756 * are available to be processed.
759 mono_gc_finalize_notify (void)
762 g_message ( "%s: prodding finalizer", __func__);
765 if (mono_gc_is_null ())
768 mono_coop_sem_post (&finalizer_sem);
772 This is the number of entries allowed in the hazard free queue before
773 we explicitly cycle the finalizer thread to trigger pumping the queue.
775 It was picked empirically by running the corlib test suite in a stress
776 scenario where all hazard entries are queued.
778 In this extreme scenario we double the number of times we cycle the finalizer
779 thread compared to just GC calls.
781 Entries are usually in the order of 100's of bytes each, so we're limiting
782 floating garbage to be in the order of a dozen kb.
784 static gboolean finalizer_thread_pulsed;
785 #define HAZARD_QUEUE_OVERFLOW_SIZE 20
788 hazard_free_queue_is_too_big (size_t size)
790 if (size < HAZARD_QUEUE_OVERFLOW_SIZE)
793 if (finalizer_thread_pulsed || InterlockedCompareExchange (&finalizer_thread_pulsed, TRUE, FALSE))
796 mono_gc_finalize_notify ();
800 hazard_free_queue_pump (void)
802 mono_thread_hazardous_try_free_all ();
803 finalizer_thread_pulsed = FALSE;
809 collect_objects (gpointer key, gpointer value, gpointer user_data)
811 GPtrArray *arr = (GPtrArray*)user_data;
812 g_ptr_array_add (arr, key);
818 * finalize_domain_objects:
820 * Run the finalizers of all finalizable objects in req->domain.
823 finalize_domain_objects (void)
825 DomainFinalizationReq *req = NULL;
828 if (domains_to_finalize) {
829 mono_finalizer_lock ();
830 if (domains_to_finalize) {
831 req = (DomainFinalizationReq *)domains_to_finalize->data;
832 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
834 mono_finalizer_unlock ();
840 domain = req->domain;
842 /* Process finalizers which are already in the queue */
843 mono_gc_invoke_finalizers ();
846 while (g_hash_table_size (domain->finalizable_objects_hash) > 0) {
850 * Since the domain is unloading, nobody is allowed to put
851 * new entries into the hash table. But finalize_object might
852 * remove entries from the hash table, so we make a copy.
854 objs = g_ptr_array_new ();
855 g_hash_table_foreach (domain->finalizable_objects_hash, collect_objects, objs);
856 /* printf ("FINALIZING %d OBJECTS.\n", objs->len); */
858 for (i = 0; i < objs->len; ++i) {
859 MonoObject *o = (MonoObject*)g_ptr_array_index (objs, i);
860 /* FIXME: Avoid finalizing threads, etc */
861 mono_gc_run_finalize (o, 0);
864 g_ptr_array_free (objs, TRUE);
866 #elif defined(HAVE_SGEN_GC)
867 mono_gc_finalize_domain (domain);
868 mono_gc_invoke_finalizers ();
871 /* cleanup the reference queue */
872 reference_queue_clear_for_domain (domain);
874 /* printf ("DONE.\n"); */
875 mono_coop_sem_post (&req->done);
877 if (InterlockedDecrement (&req->ref) == 0) {
878 /* mono_domain_finalize already returned, and
879 * doesn't hold a reference to req anymore. */
880 mono_coop_sem_destroy (&req->done);
886 finalizer_thread (gpointer unused)
889 gboolean wait = TRUE;
891 mono_thread_set_name_internal (mono_thread_internal_current (), mono_string_new (mono_get_root_domain (), "Finalizer"), FALSE, &error);
892 mono_error_assert_ok (&error);
894 /* Register a hazard free queue pump callback */
895 mono_hazard_pointer_install_free_queue_size_callback (hazard_free_queue_is_too_big);
898 /* Wait to be notified that there's at least one
902 g_assert (mono_domain_get () == mono_get_root_domain ());
903 mono_gc_set_skip_thread (TRUE);
906 /* An alertable wait is required so this thread can be suspended on windows */
907 mono_coop_sem_wait (&finalizer_sem, MONO_SEM_FLAGS_ALERTABLE);
911 mono_gc_set_skip_thread (FALSE);
913 mono_threads_perform_thread_dump ();
915 mono_console_handle_async_ops ();
917 mono_attach_maybe_start ();
919 finalize_domain_objects ();
921 mono_profiler_gc_finalize_begin ();
923 /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup),
924 * before the domain is unloaded.
926 mono_gc_invoke_finalizers ();
928 mono_profiler_gc_finalize_end ();
930 mono_threads_join_threads ();
932 reference_queue_proccess_all ();
934 hazard_free_queue_pump ();
936 /* Avoid posting the pending done event until there are pending finalizers */
937 if (mono_coop_sem_timedwait (&finalizer_sem, 0, MONO_SEM_FLAGS_NONE) == MONO_SEM_TIMEDWAIT_RET_SUCCESS) {
938 /* Don't wait again at the start of the loop */
942 SetEvent (pending_done_event);
944 mono_coop_mutex_lock (&pending_done_mutex);
946 mono_coop_cond_signal (&pending_done_cond);
947 mono_coop_mutex_unlock (&pending_done_mutex);
952 mono_finalizer_lock ();
953 finalizer_thread_exited = TRUE;
954 mono_coop_cond_signal (&exited_cond);
955 mono_finalizer_unlock ();
960 #ifndef LAZY_GC_THREAD_CREATION
964 mono_gc_init_finalizer_thread (void)
967 gc_thread = mono_thread_create_internal (mono_domain_get (), finalizer_thread, NULL, FALSE, 0, &error);
968 mono_error_assert_ok (&error);
974 mono_coop_mutex_init_recursive (&finalizer_mutex);
975 mono_coop_mutex_init_recursive (&reference_queue_mutex);
977 mono_counters_register ("Minor GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.minor_gc_count);
978 mono_counters_register ("Major GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.major_gc_count);
979 mono_counters_register ("Minor GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.minor_gc_time);
980 mono_counters_register ("Major GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time);
981 mono_counters_register ("Major GC time concurrent", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time_concurrent);
983 mono_gc_base_init ();
985 if (mono_gc_is_disabled ()) {
991 pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
992 g_assert (pending_done_event);
994 mono_coop_cond_init (&pending_done_cond);
995 mono_coop_mutex_init (&pending_done_mutex);
998 mono_coop_cond_init (&exited_cond);
999 mono_coop_sem_init (&finalizer_sem, 0);
1001 #ifndef LAZY_GC_THREAD_CREATION
1002 mono_gc_init_finalizer_thread ();
1007 mono_gc_cleanup (void)
1010 g_message ("%s: cleaning up finalizer", __func__);
1013 if (mono_gc_is_null ())
1018 if (mono_thread_internal_current () != gc_thread) {
1020 gint64 start_ticks = mono_msec_ticks ();
1021 gint64 end_ticks = start_ticks + 2000;
1023 mono_gc_finalize_notify ();
1024 /* Finishing the finalizer thread, so wait a little bit... */
1025 /* MS seems to wait for about 2 seconds */
1026 while (!finalizer_thread_exited) {
1027 gint64 current_ticks = mono_msec_ticks ();
1030 if (current_ticks >= end_ticks)
1033 timeout = end_ticks - current_ticks;
1034 mono_finalizer_lock ();
1035 if (!finalizer_thread_exited)
1036 mono_coop_cond_timedwait (&exited_cond, &finalizer_mutex, timeout);
1037 mono_finalizer_unlock ();
1040 if (!finalizer_thread_exited) {
1041 /* Set a flag which the finalizer thread can check */
1042 suspend_finalizers = TRUE;
1043 mono_gc_suspend_finalizers ();
1045 /* Try to abort the thread, in the hope that it is running managed code */
1046 mono_thread_internal_abort (gc_thread);
1048 /* Wait for it to stop */
1049 ret = guarded_wait (gc_thread->handle, 100, TRUE);
1051 if (ret == MONO_THREAD_INFO_WAIT_RET_TIMEOUT) {
1053 * The finalizer thread refused to exit. Make it stop.
1055 mono_thread_internal_stop (gc_thread);
1056 ret = guarded_wait (gc_thread->handle, 100, TRUE);
1057 g_assert (ret != MONO_THREAD_INFO_WAIT_RET_TIMEOUT);
1058 /* The thread can't set this flag */
1059 finalizer_thread_exited = TRUE;
1064 /* Wait for the thread to actually exit */
1065 ret = guarded_wait (gc_thread->handle, INFINITE, TRUE);
1066 g_assert (ret == MONO_THREAD_INFO_WAIT_RET_SUCCESS_0);
1068 mono_thread_join (GUINT_TO_POINTER (gc_thread->tid));
1069 g_assert (finalizer_thread_exited);
1072 mono_gc_base_cleanup ();
1075 mono_reference_queue_cleanup ();
1077 mono_coop_mutex_destroy (&finalizer_mutex);
1078 mono_coop_mutex_destroy (&reference_queue_mutex);
1082 mono_gc_is_finalizer_internal_thread (MonoInternalThread *thread)
1084 return thread == gc_thread;
1088 * mono_gc_is_finalizer_thread:
1089 * @thread: the thread to test.
1091 * In Mono objects are finalized asynchronously on a separate thread.
1092 * This routine tests whether the @thread argument represents the
1093 * finalization thread.
1095 * Returns: TRUE if @thread is the finalization thread.
1098 mono_gc_is_finalizer_thread (MonoThread *thread)
1100 return mono_gc_is_finalizer_internal_thread (thread->internal_thread);
1103 #if defined(__MACH__)
1104 static pthread_t mach_exception_thread;
1107 mono_gc_register_mach_exception_thread (pthread_t thread)
1109 mach_exception_thread = thread;
1113 mono_gc_get_mach_exception_thread (void)
1115 return mach_exception_thread;
1119 static MonoReferenceQueue *ref_queues;
1122 ref_list_remove_element (RefQueueEntry **prev, RefQueueEntry *element)
1125 /* Guard if head is changed concurrently. */
1126 while (*prev != element)
1127 prev = &(*prev)->next;
1128 } while (prev && InterlockedCompareExchangePointer ((volatile gpointer *)prev, element->next, element) != element);
1132 ref_list_push (RefQueueEntry **head, RefQueueEntry *value)
1134 RefQueueEntry *current;
1137 value->next = current;
1138 STORE_STORE_FENCE; /*Must make sure the previous store is visible before the CAS. */
1139 } while (InterlockedCompareExchangePointer ((volatile gpointer *)head, value, current) != current);
1143 reference_queue_proccess (MonoReferenceQueue *queue)
1145 RefQueueEntry **iter = &queue->queue;
1146 RefQueueEntry *entry;
1147 while ((entry = *iter)) {
1148 if (queue->should_be_deleted || !mono_gchandle_get_target (entry->gchandle)) {
1149 mono_gchandle_free ((guint32)entry->gchandle);
1150 ref_list_remove_element (iter, entry);
1151 queue->callback (entry->user_data);
1154 iter = &entry->next;
1160 reference_queue_proccess_all (void)
1162 MonoReferenceQueue **iter;
1163 MonoReferenceQueue *queue = ref_queues;
1164 for (; queue; queue = queue->next)
1165 reference_queue_proccess (queue);
1168 mono_coop_mutex_lock (&reference_queue_mutex);
1169 for (iter = &ref_queues; *iter;) {
1171 if (!queue->should_be_deleted) {
1172 iter = &queue->next;
1176 mono_coop_mutex_unlock (&reference_queue_mutex);
1177 reference_queue_proccess (queue);
1180 *iter = queue->next;
1183 mono_coop_mutex_unlock (&reference_queue_mutex);
1187 mono_reference_queue_cleanup (void)
1189 MonoReferenceQueue *queue = ref_queues;
1190 for (; queue; queue = queue->next)
1191 queue->should_be_deleted = TRUE;
1192 reference_queue_proccess_all ();
1196 reference_queue_clear_for_domain (MonoDomain *domain)
1198 MonoReferenceQueue *queue = ref_queues;
1199 for (; queue; queue = queue->next) {
1200 RefQueueEntry **iter = &queue->queue;
1201 RefQueueEntry *entry;
1202 while ((entry = *iter)) {
1203 if (entry->domain == domain) {
1204 mono_gchandle_free ((guint32)entry->gchandle);
1205 ref_list_remove_element (iter, entry);
1206 queue->callback (entry->user_data);
1209 iter = &entry->next;
1215 * mono_gc_reference_queue_new:
1216 * @callback callback used when processing collected entries.
1218 * Create a new reference queue used to process collected objects.
1219 * A reference queue let you add a pair of (managed object, user data)
1220 * using the mono_gc_reference_queue_add method.
1222 * Once the managed object is collected @callback will be called
1223 * in the finalizer thread with 'user data' as argument.
1225 * The callback is called from the finalizer thread without any locks held.
1226 * When a AppDomain is unloaded, all callbacks for objects belonging to it
1229 * @returns the new queue.
1232 mono_gc_reference_queue_new (mono_reference_queue_callback callback)
1234 MonoReferenceQueue *res = g_new0 (MonoReferenceQueue, 1);
1235 res->callback = callback;
1237 mono_coop_mutex_lock (&reference_queue_mutex);
1238 res->next = ref_queues;
1240 mono_coop_mutex_unlock (&reference_queue_mutex);
1246 * mono_gc_reference_queue_add:
1247 * @queue the queue to add the reference to.
1248 * @obj the object to be watched for collection
1249 * @user_data parameter to be passed to the queue callback
1251 * Queue an object to be watched for collection, when the @obj is
1252 * collected, the callback that was registered for the @queue will
1253 * be invoked with @user_data as argument.
1255 * @returns false if the queue is scheduled to be freed.
1258 mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *user_data)
1260 RefQueueEntry *entry;
1261 if (queue->should_be_deleted)
1264 g_assert (obj != NULL);
1266 entry = g_new0 (RefQueueEntry, 1);
1267 entry->user_data = user_data;
1268 entry->domain = mono_object_domain (obj);
1270 entry->gchandle = mono_gchandle_new_weakref (obj, TRUE);
1271 mono_object_register_finalizer (obj);
1273 ref_list_push (&queue->queue, entry);
1278 * mono_gc_reference_queue_free:
1279 * @queue the queue that should be freed.
1281 * This operation signals that @queue should be freed. This operation is deferred
1282 * as it happens on the finalizer thread.
1284 * After this call, no further objects can be queued. It's the responsibility of the
1285 * caller to make sure that no further attempt to access queue will be made.
1288 mono_gc_reference_queue_free (MonoReferenceQueue *queue)
1290 queue->should_be_deleted = TRUE;