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.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>
45 #include <mono/io-layer/io-layer.h>
51 typedef struct DomainFinalizationReq {
55 } DomainFinalizationReq;
57 static gboolean gc_disabled;
59 static gboolean finalizing_root_domain;
61 gboolean log_finalizers;
62 gboolean mono_do_not_finalize;
63 volatile gboolean suspend_finalizers;
64 gchar **mono_do_not_finalize_class_names ;
66 #define mono_finalizer_lock() mono_coop_mutex_lock (&finalizer_mutex)
67 #define mono_finalizer_unlock() mono_coop_mutex_unlock (&finalizer_mutex)
68 static MonoCoopMutex finalizer_mutex;
69 static MonoCoopMutex reference_queue_mutex;
71 static GSList *domains_to_finalize;
72 static MonoMList *threads_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 add_thread_to_finalize (MonoInternalThread *thread, MonoError *error)
164 mono_error_init (error);
165 mono_finalizer_lock ();
166 if (!threads_to_finalize)
167 MONO_GC_REGISTER_ROOT_SINGLE (threads_to_finalize, MONO_ROOT_SOURCE_FINALIZER_QUEUE, "finalizable threads list");
168 threads_to_finalize = mono_mlist_append_checked (threads_to_finalize, (MonoObject*)thread, error);
169 mono_finalizer_unlock ();
170 return is_ok (error);
174 * actually, we might want to queue the finalize requests in a separate thread,
175 * but we need to be careful about the execution domain of the thread...
178 mono_gc_run_finalize (void *obj, void *data)
181 MonoObject *exc = NULL;
186 MonoMethod* finalizer = NULL;
187 MonoDomain *caller_domain = mono_domain_get ();
189 RuntimeInvokeFunction runtime_invoke;
191 // This function is called from the innards of the GC, so our best alternative for now is to do polling here
192 mono_threads_safepoint ();
194 o = (MonoObject*)((char*)obj + GPOINTER_TO_UINT (data));
196 if (mono_do_not_finalize) {
197 if (!mono_do_not_finalize_class_names)
200 size_t namespace_len = strlen (o->vtable->klass->name_space);
201 for (int i = 0; mono_do_not_finalize_class_names [i]; ++i) {
202 const char *name = mono_do_not_finalize_class_names [i];
203 if (strncmp (name, o->vtable->klass->name_space, namespace_len))
205 if (name [namespace_len] != '.')
207 if (strcmp (name + namespace_len + 1, o->vtable->klass->name))
214 g_log ("mono-gc-finalizers", G_LOG_LEVEL_DEBUG, "<%s at %p> Starting finalizer checks.", o->vtable->klass->name, o);
216 if (suspend_finalizers)
219 domain = o->vtable->domain;
222 mono_domain_finalizers_lock (domain);
224 o2 = (MonoObject *)g_hash_table_lookup (domain->finalizable_objects_hash, o);
226 mono_domain_finalizers_unlock (domain);
229 /* Already finalized somehow */
233 /* make sure the finalizer is not called again if the object is resurrected */
234 object_register_finalizer ((MonoObject *)obj, NULL);
237 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Registered finalizer as processed.", o->vtable->klass->name, o);
239 if (o->vtable->klass == mono_defaults.internal_thread_class) {
240 MonoInternalThread *t = (MonoInternalThread*)o;
242 if (mono_gc_is_finalizer_internal_thread (t))
243 /* Avoid finalizing ourselves */
246 if (t->threadpool_thread && finalizing_root_domain) {
247 /* Don't finalize threadpool threads when
248 shutting down - they're finalized when the
249 threadpool shuts down. */
250 if (!add_thread_to_finalize (t, &error))
251 goto unhandled_error;
256 if (o->vtable->klass->image == mono_defaults.corlib && !strcmp (o->vtable->klass->name, "DynamicMethod") && finalizing_root_domain) {
258 * These can't be finalized during unloading/shutdown, since that would
259 * free the native code which can still be referenced by other
261 * FIXME: This is not perfect, objects dying at the same time as
262 * dynamic methods can still reference them even when !shutdown.
267 if (mono_runtime_get_no_exec ())
270 /* speedup later... and use a timeout */
271 /* g_print ("Finalize run on %p %s.%s\n", o, mono_object_class (o)->name_space, mono_object_class (o)->name); */
273 /* Use _internal here, since this thread can enter a doomed appdomain */
274 mono_domain_set_internal (mono_object_domain (o));
276 /* delegates that have a native function pointer allocated are
277 * registered for finalization, but they don't have a Finalize
278 * method, because in most cases it's not needed and it's just a waste.
280 if (o->vtable->klass->delegate) {
281 MonoDelegate* del = (MonoDelegate*)o;
282 if (del->delegate_trampoline)
283 mono_delegate_free_ftnptr ((MonoDelegate*)o);
284 mono_domain_set_internal (caller_domain);
288 finalizer = mono_class_get_finalizer (o->vtable->klass);
290 /* If object has a CCW but has no finalizer, it was only
291 * registered for finalization in order to free the CCW.
292 * Else it needs the regular finalizer run.
293 * FIXME: what to do about ressurection and suppression
294 * of finalizer on object with CCW.
296 if (mono_marshal_free_ccw (o) && !finalizer) {
297 mono_domain_set_internal (caller_domain);
302 * To avoid the locking plus the other overhead of mono_runtime_invoke_checked (),
303 * create and precompile a wrapper which calls the finalize method using
307 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Compiling finalizer.", o->vtable->klass->name, o);
309 if (!domain->finalize_runtime_invoke) {
310 MonoMethod *invoke = mono_marshal_get_runtime_invoke (mono_class_get_method_from_name_flags (mono_defaults.object_class, "Finalize", 0, 0), TRUE);
312 domain->finalize_runtime_invoke = mono_compile_method_checked (invoke, &error);
313 mono_error_assert_ok (&error); /* expect this not to fail */
316 runtime_invoke = (RuntimeInvokeFunction)domain->finalize_runtime_invoke;
318 mono_runtime_class_init_full (o->vtable, &error);
320 goto unhandled_error;
322 if (G_UNLIKELY (MONO_GC_FINALIZE_INVOKE_ENABLED ())) {
323 MONO_GC_FINALIZE_INVOKE ((unsigned long)o, mono_object_get_size (o),
324 o->vtable->klass->name_space, o->vtable->klass->name);
328 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Calling finalizer.", o->vtable->klass->name, o);
330 mono_profiler_gc_finalize_object_begin (o);
332 runtime_invoke (o, NULL, &exc, NULL);
334 mono_profiler_gc_finalize_object_end (o);
337 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Returned from finalizer.", o->vtable->klass->name, o);
341 exc = (MonoObject*)mono_error_convert_to_exception (&error);
343 mono_thread_internal_unhandled_exception (exc);
345 mono_domain_set_internal (caller_domain);
349 mono_gc_finalize_threadpool_threads (void)
351 while (threads_to_finalize) {
352 MonoInternalThread *thread = (MonoInternalThread*) mono_mlist_get_data (threads_to_finalize);
354 /* Force finalization of the thread. */
355 thread->threadpool_thread = FALSE;
356 mono_object_register_finalizer ((MonoObject*)thread);
358 mono_gc_run_finalize (thread, NULL);
360 threads_to_finalize = mono_mlist_next (threads_to_finalize);
365 mono_gc_out_of_memory (size_t size)
368 * we could allocate at program startup some memory that we could release
369 * back to the system at this point if we're really low on memory (ie, size is
370 * lower than the memory we set apart)
372 mono_raise_exception (mono_domain_get ()->out_of_memory_ex);
378 * Some of our objects may point to a different address than the address returned by GC_malloc()
379 * (because of the GetHashCode hack), but we need to pass the real address to register_finalizer.
380 * This also means that in the callback we need to adjust the pointer to get back the real
382 * We also need to be consistent in the use of the GC_debug* variants of malloc and register_finalizer,
383 * since that, too, can cause the underlying pointer to be offset.
386 object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*))
390 g_assert (obj != NULL);
392 domain = obj->vtable->domain;
395 if (mono_domain_is_unloading (domain) && (callback != NULL))
397 * Can't register finalizers in a dying appdomain, since they
398 * could be invoked after the appdomain has been unloaded.
402 mono_domain_finalizers_lock (domain);
405 g_hash_table_insert (domain->finalizable_objects_hash, obj, obj);
407 g_hash_table_remove (domain->finalizable_objects_hash, obj);
409 mono_domain_finalizers_unlock (domain);
411 mono_gc_register_for_finalization (obj, callback);
412 #elif defined(HAVE_SGEN_GC)
414 * If we register finalizers for domains that are unloading we might
415 * end up running them while or after the domain is being cleared, so
416 * the objects will not be valid anymore.
418 if (!mono_domain_is_unloading (domain))
419 mono_gc_register_for_finalization (obj, callback);
424 * mono_object_register_finalizer:
425 * @obj: object to register
427 * Records that object @obj has a finalizer, this will call the
428 * Finalize method when the garbage collector disposes the object.
432 mono_object_register_finalizer (MonoObject *obj)
434 /* g_print ("Registered finalizer on %p %s.%s\n", obj, mono_object_class (obj)->name_space, mono_object_class (obj)->name); */
435 object_register_finalizer (obj, mono_gc_run_finalize);
439 * mono_domain_finalize:
440 * @domain: the domain to finalize
441 * @timeout: msects to wait for the finalization to complete, -1 to wait indefinitely
443 * Request finalization of all finalizable objects inside @domain. Wait
444 * @timeout msecs for the finalization to complete.
446 * Returns: TRUE if succeeded, FALSE if there was a timeout
450 mono_domain_finalize (MonoDomain *domain, guint32 timeout)
452 DomainFinalizationReq *req;
453 MonoInternalThread *thread = mono_thread_internal_current ();
458 #if defined(__native_client__)
462 if (mono_thread_internal_current () == gc_thread)
463 /* We are called from inside a finalizer, not much we can do here */
467 * No need to create another thread 'cause the finalizer thread
468 * is still working and will take care of running the finalizers
474 /* We don't support domain finalization without a GC */
475 if (mono_gc_is_null ())
478 mono_gc_collect (mono_gc_max_generation ());
480 req = g_new0 (DomainFinalizationReq, 1);
482 req->domain = domain;
483 mono_coop_sem_init (&req->done, 0);
485 if (domain == mono_get_root_domain ())
486 finalizing_root_domain = TRUE;
488 mono_finalizer_lock ();
490 domains_to_finalize = g_slist_append (domains_to_finalize, req);
492 mono_finalizer_unlock ();
494 /* Tell the finalizer thread to finalize this appdomain */
495 mono_gc_finalize_notify ();
498 timeout = MONO_INFINITE_WAIT;
499 if (timeout != MONO_INFINITE_WAIT)
500 start = mono_msec_ticks ();
505 if (timeout == MONO_INFINITE_WAIT) {
506 res = mono_coop_sem_wait (&req->done, MONO_SEM_FLAGS_ALERTABLE);
508 gint64 elapsed = mono_msec_ticks () - start;
509 if (elapsed >= timeout) {
514 res = mono_coop_sem_timedwait (&req->done, timeout - elapsed, MONO_SEM_FLAGS_ALERTABLE);
517 if (res == MONO_SEM_TIMEDWAIT_RET_SUCCESS) {
519 } else if (res == MONO_SEM_TIMEDWAIT_RET_ALERTED) {
520 if ((thread->state & (ThreadState_StopRequested | ThreadState_SuspendRequested)) != 0) {
524 } else if (res == MONO_SEM_TIMEDWAIT_RET_TIMEDOUT) {
528 g_error ("%s: unknown result %d", __func__, res);
533 /* Try removing the req from domains_to_finalize:
534 * - if it's not found: the domain is being finalized,
535 * so we the ref count is already decremented
536 * - if it's found: the domain is not yet being finalized,
537 * so we can safely decrement the ref */
541 mono_finalizer_lock ();
543 found = g_slist_index (domains_to_finalize, req) != -1;
545 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
547 mono_finalizer_unlock ();
550 /* We have to decrement it wherever we
551 * remove it from domains_to_finalize */
552 if (InterlockedDecrement (&req->ref) != 1)
553 g_error ("%s: req->ref should be 1, as we are the first one to decrement it", __func__);
559 if (domain == mono_get_root_domain ()) {
560 mono_threadpool_cleanup ();
561 mono_gc_finalize_threadpool_threads ();
565 if (InterlockedDecrement (&req->ref) == 0) {
566 mono_coop_sem_destroy (&req->done);
574 ves_icall_System_GC_InternalCollect (int generation)
576 mono_gc_collect (generation);
580 ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection)
583 mono_gc_collect (mono_gc_max_generation ());
584 return mono_gc_get_used_size ();
588 ves_icall_System_GC_KeepAlive (MonoObject *obj)
596 ves_icall_System_GC_ReRegisterForFinalize (MonoObject *obj)
598 MONO_CHECK_ARG_NULL (obj,);
600 object_register_finalizer (obj, mono_gc_run_finalize);
604 ves_icall_System_GC_SuppressFinalize (MonoObject *obj)
606 MONO_CHECK_ARG_NULL (obj,);
608 /* delegates have no finalizers, but we register them to deal with the
609 * unmanaged->managed trampoline. We don't let the user suppress it
610 * otherwise we'd leak it.
612 if (obj->vtable->klass->delegate)
615 /* FIXME: Need to handle case where obj has COM Callable Wrapper
616 * generated for it that needs cleaned up, but user wants to suppress
617 * their derived object finalizer. */
619 object_register_finalizer (obj, NULL);
623 ves_icall_System_GC_WaitForPendingFinalizers (void)
625 if (mono_gc_is_null ())
628 if (!mono_gc_pending_finalizers ())
631 if (mono_thread_internal_current () == gc_thread)
632 /* Avoid deadlocks */
636 If the finalizer thread is not live, lets pretend no finalizers are pending since the current thread might
637 be the one responsible for starting it up.
639 if (gc_thread == NULL)
643 ResetEvent (pending_done_event);
644 mono_gc_finalize_notify ();
645 /* g_print ("Waiting for pending finalizers....\n"); */
647 WaitForSingleObjectEx (pending_done_event, INFINITE, TRUE);
649 /* g_print ("Done pending....\n"); */
651 gboolean alerted = FALSE;
652 mono_coop_mutex_lock (&pending_done_mutex);
653 pending_done = FALSE;
654 mono_gc_finalize_notify ();
655 while (!pending_done) {
656 coop_cond_timedwait_alertable (&pending_done_cond, &pending_done_mutex, MONO_INFINITE_WAIT, &alerted);
660 mono_coop_mutex_unlock (&pending_done_mutex);
665 ves_icall_System_GC_register_ephemeron_array (MonoObject *array)
668 if (!mono_gc_ephemeron_array_add (array)) {
669 mono_set_pending_exception (mono_object_domain (array)->out_of_memory_ex);
676 ves_icall_System_GC_get_ephemeron_tombstone (void)
678 return mono_domain_get ()->ephemeron_tombstone;
682 ves_icall_System_GCHandle_GetTarget (guint32 handle)
684 return mono_gchandle_get_target (handle);
688 * if type == -1, change the target of the handle, otherwise allocate a new handle.
691 ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type)
694 mono_gchandle_set_target (handle, obj);
695 /* the handle doesn't change */
700 return mono_gchandle_new_weakref (obj, FALSE);
701 case HANDLE_WEAK_TRACK:
702 return mono_gchandle_new_weakref (obj, TRUE);
704 return mono_gchandle_new (obj, FALSE);
706 return mono_gchandle_new (obj, TRUE);
708 g_assert_not_reached ();
714 ves_icall_System_GCHandle_FreeHandle (guint32 handle)
716 mono_gchandle_free (handle);
720 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
724 if (MONO_GC_HANDLE_TYPE (handle) != HANDLE_PINNED)
726 obj = mono_gchandle_get_target (handle);
728 MonoClass *klass = mono_object_class (obj);
729 if (klass == mono_defaults.string_class) {
730 return mono_string_chars ((MonoString*)obj);
731 } else if (klass->rank) {
732 return mono_array_addr ((MonoArray*)obj, char, 0);
734 /* the C# code will check and throw the exception */
735 /* FIXME: missing !klass->blittable test, see bug #61134 */
736 if (mono_class_is_auto_layout (klass))
738 return (char*)obj + sizeof (MonoObject);
745 mono_gc_GCHandle_CheckCurrentDomain (guint32 gchandle)
747 return mono_gchandle_is_in_domain (gchandle, mono_domain_get ());
750 static MonoCoopSem finalizer_sem;
751 static volatile gboolean finished;
754 * mono_gc_finalize_notify:
756 * Notify the finalizer thread that finalizers etc.
757 * are available to be processed.
760 mono_gc_finalize_notify (void)
763 g_message ( "%s: prodding finalizer", __func__);
766 if (mono_gc_is_null ())
769 mono_coop_sem_post (&finalizer_sem);
773 This is the number of entries allowed in the hazard free queue before
774 we explicitly cycle the finalizer thread to trigger pumping the queue.
776 It was picked empirically by running the corlib test suite in a stress
777 scenario where all hazard entries are queued.
779 In this extreme scenario we double the number of times we cycle the finalizer
780 thread compared to just GC calls.
782 Entries are usually in the order of 100's of bytes each, so we're limiting
783 floating garbage to be in the order of a dozen kb.
785 static gboolean finalizer_thread_pulsed;
786 #define HAZARD_QUEUE_OVERFLOW_SIZE 20
789 hazard_free_queue_is_too_big (size_t size)
791 if (size < HAZARD_QUEUE_OVERFLOW_SIZE)
794 if (finalizer_thread_pulsed || InterlockedCompareExchange (&finalizer_thread_pulsed, TRUE, FALSE))
797 mono_gc_finalize_notify ();
801 hazard_free_queue_pump (void)
803 mono_thread_hazardous_try_free_all ();
804 finalizer_thread_pulsed = FALSE;
810 collect_objects (gpointer key, gpointer value, gpointer user_data)
812 GPtrArray *arr = (GPtrArray*)user_data;
813 g_ptr_array_add (arr, key);
819 * finalize_domain_objects:
821 * Run the finalizers of all finalizable objects in req->domain.
824 finalize_domain_objects (void)
826 DomainFinalizationReq *req = NULL;
829 if (domains_to_finalize) {
830 mono_finalizer_lock ();
831 if (domains_to_finalize) {
832 req = (DomainFinalizationReq *)domains_to_finalize->data;
833 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
835 mono_finalizer_unlock ();
841 domain = req->domain;
843 /* Process finalizers which are already in the queue */
844 mono_gc_invoke_finalizers ();
847 while (g_hash_table_size (domain->finalizable_objects_hash) > 0) {
851 * Since the domain is unloading, nobody is allowed to put
852 * new entries into the hash table. But finalize_object might
853 * remove entries from the hash table, so we make a copy.
855 objs = g_ptr_array_new ();
856 g_hash_table_foreach (domain->finalizable_objects_hash, collect_objects, objs);
857 /* printf ("FINALIZING %d OBJECTS.\n", objs->len); */
859 for (i = 0; i < objs->len; ++i) {
860 MonoObject *o = (MonoObject*)g_ptr_array_index (objs, i);
861 /* FIXME: Avoid finalizing threads, etc */
862 mono_gc_run_finalize (o, 0);
865 g_ptr_array_free (objs, TRUE);
867 #elif defined(HAVE_SGEN_GC)
868 mono_gc_finalize_domain (domain);
869 mono_gc_invoke_finalizers ();
872 /* cleanup the reference queue */
873 reference_queue_clear_for_domain (domain);
875 /* printf ("DONE.\n"); */
876 mono_coop_sem_post (&req->done);
878 if (InterlockedDecrement (&req->ref) == 0) {
879 /* mono_domain_finalize already returned, and
880 * doesn't hold a reference to req anymore. */
881 mono_coop_sem_destroy (&req->done);
887 finalizer_thread (gpointer unused)
890 gboolean wait = TRUE;
892 mono_thread_set_name_internal (mono_thread_internal_current (), mono_string_new (mono_get_root_domain (), "Finalizer"), FALSE, &error);
893 mono_error_assert_ok (&error);
895 /* Register a hazard free queue pump callback */
896 mono_hazard_pointer_install_free_queue_size_callback (hazard_free_queue_is_too_big);
899 /* Wait to be notified that there's at least one
903 g_assert (mono_domain_get () == mono_get_root_domain ());
904 mono_gc_set_skip_thread (TRUE);
907 /* An alertable wait is required so this thread can be suspended on windows */
908 mono_coop_sem_wait (&finalizer_sem, MONO_SEM_FLAGS_ALERTABLE);
912 mono_gc_set_skip_thread (FALSE);
914 mono_threads_perform_thread_dump ();
916 mono_console_handle_async_ops ();
918 mono_attach_maybe_start ();
920 finalize_domain_objects ();
922 mono_profiler_gc_finalize_begin ();
924 /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup),
925 * before the domain is unloaded.
927 mono_gc_invoke_finalizers ();
929 mono_profiler_gc_finalize_end ();
931 mono_threads_join_threads ();
933 reference_queue_proccess_all ();
935 hazard_free_queue_pump ();
937 /* Avoid posting the pending done event until there are pending finalizers */
938 if (mono_coop_sem_timedwait (&finalizer_sem, 0, MONO_SEM_FLAGS_NONE) == MONO_SEM_TIMEDWAIT_RET_SUCCESS) {
939 /* Don't wait again at the start of the loop */
943 SetEvent (pending_done_event);
945 mono_coop_mutex_lock (&pending_done_mutex);
947 mono_coop_cond_signal (&pending_done_cond);
948 mono_coop_mutex_unlock (&pending_done_mutex);
953 mono_finalizer_lock ();
954 finalizer_thread_exited = TRUE;
955 mono_coop_cond_signal (&exited_cond);
956 mono_finalizer_unlock ();
961 #ifndef LAZY_GC_THREAD_CREATION
965 mono_gc_init_finalizer_thread (void)
968 gc_thread = mono_thread_create_internal (mono_domain_get (), finalizer_thread, NULL, FALSE, 0, &error);
969 mono_error_assert_ok (&error);
975 mono_coop_mutex_init_recursive (&finalizer_mutex);
976 mono_coop_mutex_init_recursive (&reference_queue_mutex);
978 mono_counters_register ("Minor GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.minor_gc_count);
979 mono_counters_register ("Major GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.major_gc_count);
980 mono_counters_register ("Minor GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.minor_gc_time);
981 mono_counters_register ("Major GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time);
982 mono_counters_register ("Major GC time concurrent", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time_concurrent);
984 mono_gc_base_init ();
986 if (mono_gc_is_disabled ()) {
992 pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
993 g_assert (pending_done_event);
995 mono_coop_cond_init (&pending_done_cond);
996 mono_coop_mutex_init (&pending_done_mutex);
999 mono_coop_cond_init (&exited_cond);
1000 mono_coop_sem_init (&finalizer_sem, 0);
1002 #ifndef LAZY_GC_THREAD_CREATION
1003 mono_gc_init_finalizer_thread ();
1008 mono_gc_cleanup (void)
1011 g_message ("%s: cleaning up finalizer", __func__);
1014 if (mono_gc_is_null ())
1019 if (mono_thread_internal_current () != gc_thread) {
1021 gint64 start_ticks = mono_msec_ticks ();
1022 gint64 end_ticks = start_ticks + 40000;
1024 mono_gc_finalize_notify ();
1025 /* Finishing the finalizer thread, so wait a little bit... */
1026 /* MS seems to wait for about 2 seconds per finalizer thread */
1027 /* and 40 seconds for all finalizers to finish */
1028 while (!finalizer_thread_exited) {
1029 gint64 current_ticks = mono_msec_ticks ();
1032 if (current_ticks >= end_ticks)
1035 timeout = end_ticks - current_ticks;
1036 mono_finalizer_lock ();
1037 if (!finalizer_thread_exited)
1038 mono_coop_cond_timedwait (&exited_cond, &finalizer_mutex, timeout);
1039 mono_finalizer_unlock ();
1042 if (!finalizer_thread_exited) {
1043 /* Set a flag which the finalizer thread can check */
1044 suspend_finalizers = TRUE;
1045 mono_gc_suspend_finalizers ();
1047 /* Try to abort the thread, in the hope that it is running managed code */
1048 mono_thread_internal_abort (gc_thread);
1050 /* Wait for it to stop */
1051 ret = guarded_wait (gc_thread->handle, 100, TRUE);
1053 if (ret == MONO_THREAD_INFO_WAIT_RET_TIMEOUT) {
1055 * The finalizer thread refused to exit. Make it stop.
1057 mono_thread_internal_stop (gc_thread);
1058 ret = guarded_wait (gc_thread->handle, 100, TRUE);
1059 g_assert (ret != MONO_THREAD_INFO_WAIT_RET_TIMEOUT);
1060 /* The thread can't set this flag */
1061 finalizer_thread_exited = TRUE;
1066 /* Wait for the thread to actually exit */
1067 ret = guarded_wait (gc_thread->handle, MONO_INFINITE_WAIT, TRUE);
1068 g_assert (ret == MONO_THREAD_INFO_WAIT_RET_SUCCESS_0);
1070 mono_thread_join (GUINT_TO_POINTER (gc_thread->tid));
1071 g_assert (finalizer_thread_exited);
1074 mono_gc_base_cleanup ();
1077 mono_reference_queue_cleanup ();
1079 mono_coop_mutex_destroy (&finalizer_mutex);
1080 mono_coop_mutex_destroy (&reference_queue_mutex);
1084 mono_gc_is_finalizer_internal_thread (MonoInternalThread *thread)
1086 return thread == gc_thread;
1090 * mono_gc_is_finalizer_thread:
1091 * @thread: the thread to test.
1093 * In Mono objects are finalized asynchronously on a separate thread.
1094 * This routine tests whether the @thread argument represents the
1095 * finalization thread.
1097 * Returns: TRUE if @thread is the finalization thread.
1100 mono_gc_is_finalizer_thread (MonoThread *thread)
1102 return mono_gc_is_finalizer_internal_thread (thread->internal_thread);
1105 #if defined(__MACH__)
1106 static pthread_t mach_exception_thread;
1109 mono_gc_register_mach_exception_thread (pthread_t thread)
1111 mach_exception_thread = thread;
1115 mono_gc_get_mach_exception_thread (void)
1117 return mach_exception_thread;
1121 static MonoReferenceQueue *ref_queues;
1124 ref_list_remove_element (RefQueueEntry **prev, RefQueueEntry *element)
1127 /* Guard if head is changed concurrently. */
1128 while (*prev != element)
1129 prev = &(*prev)->next;
1130 } while (prev && InterlockedCompareExchangePointer ((volatile gpointer *)prev, element->next, element) != element);
1134 ref_list_push (RefQueueEntry **head, RefQueueEntry *value)
1136 RefQueueEntry *current;
1139 value->next = current;
1140 STORE_STORE_FENCE; /*Must make sure the previous store is visible before the CAS. */
1141 } while (InterlockedCompareExchangePointer ((volatile gpointer *)head, value, current) != current);
1145 reference_queue_proccess (MonoReferenceQueue *queue)
1147 RefQueueEntry **iter = &queue->queue;
1148 RefQueueEntry *entry;
1149 while ((entry = *iter)) {
1150 if (queue->should_be_deleted || !mono_gchandle_get_target (entry->gchandle)) {
1151 mono_gchandle_free ((guint32)entry->gchandle);
1152 ref_list_remove_element (iter, entry);
1153 queue->callback (entry->user_data);
1156 iter = &entry->next;
1162 reference_queue_proccess_all (void)
1164 MonoReferenceQueue **iter;
1165 MonoReferenceQueue *queue = ref_queues;
1166 for (; queue; queue = queue->next)
1167 reference_queue_proccess (queue);
1170 mono_coop_mutex_lock (&reference_queue_mutex);
1171 for (iter = &ref_queues; *iter;) {
1173 if (!queue->should_be_deleted) {
1174 iter = &queue->next;
1178 mono_coop_mutex_unlock (&reference_queue_mutex);
1179 reference_queue_proccess (queue);
1182 *iter = queue->next;
1185 mono_coop_mutex_unlock (&reference_queue_mutex);
1189 mono_reference_queue_cleanup (void)
1191 MonoReferenceQueue *queue = ref_queues;
1192 for (; queue; queue = queue->next)
1193 queue->should_be_deleted = TRUE;
1194 reference_queue_proccess_all ();
1198 reference_queue_clear_for_domain (MonoDomain *domain)
1200 MonoReferenceQueue *queue = ref_queues;
1201 for (; queue; queue = queue->next) {
1202 RefQueueEntry **iter = &queue->queue;
1203 RefQueueEntry *entry;
1204 while ((entry = *iter)) {
1205 if (entry->domain == domain) {
1206 mono_gchandle_free ((guint32)entry->gchandle);
1207 ref_list_remove_element (iter, entry);
1208 queue->callback (entry->user_data);
1211 iter = &entry->next;
1217 * mono_gc_reference_queue_new:
1218 * @callback callback used when processing collected entries.
1220 * Create a new reference queue used to process collected objects.
1221 * A reference queue let you add a pair of (managed object, user data)
1222 * using the mono_gc_reference_queue_add method.
1224 * Once the managed object is collected @callback will be called
1225 * in the finalizer thread with 'user data' as argument.
1227 * The callback is called from the finalizer thread without any locks held.
1228 * When a AppDomain is unloaded, all callbacks for objects belonging to it
1231 * @returns the new queue.
1234 mono_gc_reference_queue_new (mono_reference_queue_callback callback)
1236 MonoReferenceQueue *res = g_new0 (MonoReferenceQueue, 1);
1237 res->callback = callback;
1239 mono_coop_mutex_lock (&reference_queue_mutex);
1240 res->next = ref_queues;
1242 mono_coop_mutex_unlock (&reference_queue_mutex);
1248 * mono_gc_reference_queue_add:
1249 * @queue the queue to add the reference to.
1250 * @obj the object to be watched for collection
1251 * @user_data parameter to be passed to the queue callback
1253 * Queue an object to be watched for collection, when the @obj is
1254 * collected, the callback that was registered for the @queue will
1255 * be invoked with @user_data as argument.
1257 * @returns false if the queue is scheduled to be freed.
1260 mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *user_data)
1262 RefQueueEntry *entry;
1263 if (queue->should_be_deleted)
1266 g_assert (obj != NULL);
1268 entry = g_new0 (RefQueueEntry, 1);
1269 entry->user_data = user_data;
1270 entry->domain = mono_object_domain (obj);
1272 entry->gchandle = mono_gchandle_new_weakref (obj, TRUE);
1273 mono_object_register_finalizer (obj);
1275 ref_list_push (&queue->queue, entry);
1280 * mono_gc_reference_queue_free:
1281 * @queue the queue that should be freed.
1283 * This operation signals that @queue should be freed. This operation is deferred
1284 * as it happens on the finalizer thread.
1286 * After this call, no further objects can be queued. It's the responsibility of the
1287 * caller to make sure that no further attempt to access queue will be made.
1290 mono_gc_reference_queue_free (MonoReferenceQueue *queue)
1292 queue->should_be_deleted = TRUE;