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 {
59 } DomainFinalizationReq;
61 static gboolean gc_disabled = FALSE;
63 static gboolean finalizing_root_domain = FALSE;
65 gboolean log_finalizers = FALSE;
66 gboolean mono_do_not_finalize = FALSE;
67 volatile gboolean suspend_finalizers = FALSE;
68 gchar **mono_do_not_finalize_class_names = NULL;
70 #define mono_finalizer_lock() mono_coop_mutex_lock (&finalizer_mutex)
71 #define mono_finalizer_unlock() mono_coop_mutex_unlock (&finalizer_mutex)
72 static MonoCoopMutex finalizer_mutex;
73 static MonoCoopMutex reference_queue_mutex;
75 static GSList *domains_to_finalize= NULL;
76 static MonoMList *threads_to_finalize = NULL;
78 static gboolean finalizer_thread_exited;
79 /* Uses finalizer_mutex */
80 static MonoCoopCond exited_cond;
82 static MonoInternalThread *gc_thread;
84 static void object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*));
86 static void reference_queue_proccess_all (void);
87 static void mono_reference_queue_cleanup (void);
88 static void reference_queue_clear_for_domain (MonoDomain *domain);
89 static HANDLE pending_done_event;
92 guarded_wait (HANDLE handle, guint32 timeout, gboolean alertable)
97 result = WaitForSingleObjectEx (handle, timeout, alertable);
105 MonoCoopMutex *mutex;
106 } BreakCoopAlertableWaitUD;
109 break_coop_alertable_wait (gpointer user_data)
111 BreakCoopAlertableWaitUD *ud = (BreakCoopAlertableWaitUD*)user_data;
113 mono_coop_mutex_lock (ud->mutex);
114 mono_coop_cond_signal (ud->cond);
115 mono_coop_mutex_unlock (ud->mutex);
119 * coop_cond_timedwait_alertable:
121 * Wait on COND/MUTEX. If ALERTABLE is non-null, the wait can be interrupted.
122 * In that case, *ALERTABLE will be set to TRUE, and 0 is returned.
125 coop_cond_timedwait_alertable (MonoCoopCond *cond, MonoCoopMutex *mutex, guint32 timeout_ms, gboolean *alertable)
130 BreakCoopAlertableWaitUD ud;
135 mono_thread_info_install_interrupt (break_coop_alertable_wait, &ud, alertable);
139 res = mono_coop_cond_timedwait (cond, mutex, timeout_ms);
141 mono_thread_info_uninstall_interrupt (alertable);
149 add_thread_to_finalize (MonoInternalThread *thread, MonoError *error)
151 mono_error_init (error);
152 mono_finalizer_lock ();
153 if (!threads_to_finalize)
154 MONO_GC_REGISTER_ROOT_SINGLE (threads_to_finalize, MONO_ROOT_SOURCE_FINALIZER_QUEUE, "finalizable threads list");
155 threads_to_finalize = mono_mlist_append_checked (threads_to_finalize, (MonoObject*)thread, error);
156 mono_finalizer_unlock ();
157 return is_ok (error);
161 * actually, we might want to queue the finalize requests in a separate thread,
162 * but we need to be careful about the execution domain of the thread...
165 mono_gc_run_finalize (void *obj, void *data)
168 MonoObject *exc = NULL;
173 MonoMethod* finalizer = NULL;
174 MonoDomain *caller_domain = mono_domain_get ();
176 RuntimeInvokeFunction runtime_invoke;
178 // This function is called from the innards of the GC, so our best alternative for now is to do polling here
179 mono_threads_safepoint ();
181 o = (MonoObject*)((char*)obj + GPOINTER_TO_UINT (data));
183 if (mono_do_not_finalize) {
184 if (!mono_do_not_finalize_class_names)
187 size_t namespace_len = strlen (o->vtable->klass->name_space);
188 for (int i = 0; mono_do_not_finalize_class_names [i]; ++i) {
189 const char *name = mono_do_not_finalize_class_names [i];
190 if (strncmp (name, o->vtable->klass->name_space, namespace_len))
192 if (name [namespace_len] != '.')
194 if (strcmp (name + namespace_len + 1, o->vtable->klass->name))
201 g_log ("mono-gc-finalizers", G_LOG_LEVEL_DEBUG, "<%s at %p> Starting finalizer checks.", o->vtable->klass->name, o);
203 if (suspend_finalizers)
206 domain = o->vtable->domain;
209 mono_domain_finalizers_lock (domain);
211 o2 = (MonoObject *)g_hash_table_lookup (domain->finalizable_objects_hash, o);
213 mono_domain_finalizers_unlock (domain);
216 /* Already finalized somehow */
220 /* make sure the finalizer is not called again if the object is resurrected */
221 object_register_finalizer ((MonoObject *)obj, NULL);
224 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Registered finalizer as processed.", o->vtable->klass->name, o);
226 if (o->vtable->klass == mono_defaults.internal_thread_class) {
227 MonoInternalThread *t = (MonoInternalThread*)o;
229 if (mono_gc_is_finalizer_internal_thread (t))
230 /* Avoid finalizing ourselves */
233 if (t->threadpool_thread && finalizing_root_domain) {
234 /* Don't finalize threadpool threads when
235 shutting down - they're finalized when the
236 threadpool shuts down. */
237 if (!add_thread_to_finalize (t, &error))
238 goto unhandled_error;
243 if (o->vtable->klass->image == mono_defaults.corlib && !strcmp (o->vtable->klass->name, "DynamicMethod") && finalizing_root_domain) {
245 * These can't be finalized during unloading/shutdown, since that would
246 * free the native code which can still be referenced by other
248 * FIXME: This is not perfect, objects dying at the same time as
249 * dynamic methods can still reference them even when !shutdown.
254 if (mono_runtime_get_no_exec ())
257 /* speedup later... and use a timeout */
258 /* g_print ("Finalize run on %p %s.%s\n", o, mono_object_class (o)->name_space, mono_object_class (o)->name); */
260 /* Use _internal here, since this thread can enter a doomed appdomain */
261 mono_domain_set_internal (mono_object_domain (o));
263 /* delegates that have a native function pointer allocated are
264 * registered for finalization, but they don't have a Finalize
265 * method, because in most cases it's not needed and it's just a waste.
267 if (o->vtable->klass->delegate) {
268 MonoDelegate* del = (MonoDelegate*)o;
269 if (del->delegate_trampoline)
270 mono_delegate_free_ftnptr ((MonoDelegate*)o);
271 mono_domain_set_internal (caller_domain);
275 finalizer = mono_class_get_finalizer (o->vtable->klass);
277 /* If object has a CCW but has no finalizer, it was only
278 * registered for finalization in order to free the CCW.
279 * Else it needs the regular finalizer run.
280 * FIXME: what to do about ressurection and suppression
281 * of finalizer on object with CCW.
283 if (mono_marshal_free_ccw (o) && !finalizer) {
284 mono_domain_set_internal (caller_domain);
289 * To avoid the locking plus the other overhead of mono_runtime_invoke_checked (),
290 * create and precompile a wrapper which calls the finalize method using
294 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Compiling finalizer.", o->vtable->klass->name, o);
296 if (!domain->finalize_runtime_invoke) {
297 MonoMethod *invoke = mono_marshal_get_runtime_invoke (mono_class_get_method_from_name_flags (mono_defaults.object_class, "Finalize", 0, 0), TRUE);
299 domain->finalize_runtime_invoke = mono_compile_method_checked (invoke, &error);
300 mono_error_assert_ok (&error); /* expect this not to fail */
303 runtime_invoke = (RuntimeInvokeFunction)domain->finalize_runtime_invoke;
305 mono_runtime_class_init_full (o->vtable, &error);
307 goto unhandled_error;
309 if (G_UNLIKELY (MONO_GC_FINALIZE_INVOKE_ENABLED ())) {
310 MONO_GC_FINALIZE_INVOKE ((unsigned long)o, mono_object_get_size (o),
311 o->vtable->klass->name_space, o->vtable->klass->name);
315 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Calling finalizer.", o->vtable->klass->name, o);
317 runtime_invoke (o, NULL, &exc, NULL);
320 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Returned from finalizer.", o->vtable->klass->name, o);
324 exc = (MonoObject*)mono_error_convert_to_exception (&error);
326 mono_thread_internal_unhandled_exception (exc);
328 mono_domain_set_internal (caller_domain);
332 mono_gc_finalize_threadpool_threads (void)
334 while (threads_to_finalize) {
335 MonoInternalThread *thread = (MonoInternalThread*) mono_mlist_get_data (threads_to_finalize);
337 /* Force finalization of the thread. */
338 thread->threadpool_thread = FALSE;
339 mono_object_register_finalizer ((MonoObject*)thread);
341 mono_gc_run_finalize (thread, NULL);
343 threads_to_finalize = mono_mlist_next (threads_to_finalize);
348 mono_gc_out_of_memory (size_t size)
351 * we could allocate at program startup some memory that we could release
352 * back to the system at this point if we're really low on memory (ie, size is
353 * lower than the memory we set apart)
355 mono_raise_exception (mono_domain_get ()->out_of_memory_ex);
361 * Some of our objects may point to a different address than the address returned by GC_malloc()
362 * (because of the GetHashCode hack), but we need to pass the real address to register_finalizer.
363 * This also means that in the callback we need to adjust the pointer to get back the real
365 * We also need to be consistent in the use of the GC_debug* variants of malloc and register_finalizer,
366 * since that, too, can cause the underlying pointer to be offset.
369 object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*))
373 g_assert (obj != NULL);
375 domain = obj->vtable->domain;
378 if (mono_domain_is_unloading (domain) && (callback != NULL))
380 * Can't register finalizers in a dying appdomain, since they
381 * could be invoked after the appdomain has been unloaded.
385 mono_domain_finalizers_lock (domain);
388 g_hash_table_insert (domain->finalizable_objects_hash, obj, obj);
390 g_hash_table_remove (domain->finalizable_objects_hash, obj);
392 mono_domain_finalizers_unlock (domain);
394 mono_gc_register_for_finalization (obj, callback);
395 #elif defined(HAVE_SGEN_GC)
397 * If we register finalizers for domains that are unloading we might
398 * end up running them while or after the domain is being cleared, so
399 * the objects will not be valid anymore.
401 if (!mono_domain_is_unloading (domain))
402 mono_gc_register_for_finalization (obj, callback);
407 * mono_object_register_finalizer:
408 * @obj: object to register
410 * Records that object @obj has a finalizer, this will call the
411 * Finalize method when the garbage collector disposes the object.
415 mono_object_register_finalizer (MonoObject *obj)
417 /* g_print ("Registered finalizer on %p %s.%s\n", obj, mono_object_class (obj)->name_space, mono_object_class (obj)->name); */
418 object_register_finalizer (obj, mono_gc_run_finalize);
422 * mono_domain_finalize:
423 * @domain: the domain to finalize
424 * @timeout: msects to wait for the finalization to complete, -1 to wait indefinitely
426 * Request finalization of all finalizable objects inside @domain. Wait
427 * @timeout msecs for the finalization to complete.
429 * Returns: TRUE if succeeded, FALSE if there was a timeout
433 mono_domain_finalize (MonoDomain *domain, guint32 timeout)
435 DomainFinalizationReq *req;
436 MonoInternalThread *thread = mono_thread_internal_current ();
438 #if defined(__native_client__)
442 if (mono_thread_internal_current () == gc_thread)
443 /* We are called from inside a finalizer, not much we can do here */
447 * No need to create another thread 'cause the finalizer thread
448 * is still working and will take care of running the finalizers
454 /* We don't support domain finalization without a GC */
455 if (mono_gc_is_null ())
458 mono_gc_collect (mono_gc_max_generation ());
460 req = g_new0 (DomainFinalizationReq, 1);
461 req->domain = domain;
464 req->done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
465 if (req->done_event == NULL) {
470 mono_coop_cond_init (&req->cond);
471 mono_coop_mutex_init (&req->mutex);
474 if (domain == mono_get_root_domain ())
475 finalizing_root_domain = TRUE;
477 mono_finalizer_lock ();
479 domains_to_finalize = g_slist_append (domains_to_finalize, req);
481 mono_finalizer_unlock ();
483 /* Tell the finalizer thread to finalize this appdomain */
484 mono_gc_finalize_notify ();
491 guint32 res = guarded_wait (req->done_event, timeout, TRUE);
492 /* printf ("WAIT RES: %d.\n", res); */
494 if (res == WAIT_IO_COMPLETION) {
495 if ((thread->state & (ThreadState_StopRequested | ThreadState_SuspendRequested)) != 0)
497 } else if (res == WAIT_TIMEOUT) {
498 /* We leak the handle here */
505 CloseHandle (req->done_event);
507 mono_coop_mutex_lock (&req->mutex);
510 int res = coop_cond_timedwait_alertable (&req->cond, &req->mutex, timeout, &alerted);
512 if ((thread->state & (ThreadState_StopRequested | ThreadState_SuspendRequested)) != 0) {
513 mono_coop_mutex_unlock (&req->mutex);
516 } else if (res == -1) {
517 /* We leak the cond/mutex here */
518 mono_coop_mutex_unlock (&req->mutex);
524 mono_coop_mutex_unlock (&req->mutex);
526 /* When we reach here, the other thread has already exited the critical section, so this is safe to free */
527 mono_coop_cond_destroy (&req->cond);
528 mono_coop_mutex_destroy (&req->mutex);
532 if (domain == mono_get_root_domain ()) {
533 mono_threadpool_ms_cleanup ();
534 mono_gc_finalize_threadpool_threads ();
537 mono_profiler_appdomain_event (domain, MONO_PROFILE_END_UNLOAD);
543 ves_icall_System_GC_InternalCollect (int generation)
545 mono_gc_collect (generation);
549 ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection)
552 mono_gc_collect (mono_gc_max_generation ());
553 return mono_gc_get_used_size ();
557 ves_icall_System_GC_KeepAlive (MonoObject *obj)
565 ves_icall_System_GC_ReRegisterForFinalize (MonoObject *obj)
567 MONO_CHECK_ARG_NULL (obj,);
569 object_register_finalizer (obj, mono_gc_run_finalize);
573 ves_icall_System_GC_SuppressFinalize (MonoObject *obj)
575 MONO_CHECK_ARG_NULL (obj,);
577 /* delegates have no finalizers, but we register them to deal with the
578 * unmanaged->managed trampoline. We don't let the user suppress it
579 * otherwise we'd leak it.
581 if (obj->vtable->klass->delegate)
584 /* FIXME: Need to handle case where obj has COM Callable Wrapper
585 * generated for it that needs cleaned up, but user wants to suppress
586 * their derived object finalizer. */
588 object_register_finalizer (obj, NULL);
592 ves_icall_System_GC_WaitForPendingFinalizers (void)
594 if (mono_gc_is_null ())
597 if (!mono_gc_pending_finalizers ())
600 if (mono_thread_internal_current () == gc_thread)
601 /* Avoid deadlocks */
605 If the finalizer thread is not live, lets pretend no finalizers are pending since the current thread might
606 be the one responsible for starting it up.
608 if (gc_thread == NULL)
611 ResetEvent (pending_done_event);
612 mono_gc_finalize_notify ();
613 /* g_print ("Waiting for pending finalizers....\n"); */
614 guarded_wait (pending_done_event, INFINITE, TRUE);
615 /* g_print ("Done pending....\n"); */
619 ves_icall_System_GC_register_ephemeron_array (MonoObject *array)
622 if (!mono_gc_ephemeron_array_add (array)) {
623 mono_set_pending_exception (mono_object_domain (array)->out_of_memory_ex);
630 ves_icall_System_GC_get_ephemeron_tombstone (void)
632 return mono_domain_get ()->ephemeron_tombstone;
636 ves_icall_System_GCHandle_GetTarget (guint32 handle)
638 return mono_gchandle_get_target (handle);
642 * if type == -1, change the target of the handle, otherwise allocate a new handle.
645 ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type)
648 mono_gchandle_set_target (handle, obj);
649 /* the handle doesn't change */
654 return mono_gchandle_new_weakref (obj, FALSE);
655 case HANDLE_WEAK_TRACK:
656 return mono_gchandle_new_weakref (obj, TRUE);
658 return mono_gchandle_new (obj, FALSE);
660 return mono_gchandle_new (obj, TRUE);
662 g_assert_not_reached ();
668 ves_icall_System_GCHandle_FreeHandle (guint32 handle)
670 mono_gchandle_free (handle);
674 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
678 if (MONO_GC_HANDLE_TYPE (handle) != HANDLE_PINNED)
680 obj = mono_gchandle_get_target (handle);
682 MonoClass *klass = mono_object_class (obj);
683 if (klass == mono_defaults.string_class) {
684 return mono_string_chars ((MonoString*)obj);
685 } else if (klass->rank) {
686 return mono_array_addr ((MonoArray*)obj, char, 0);
688 /* the C# code will check and throw the exception */
689 /* FIXME: missing !klass->blittable test, see bug #61134 */
690 if ((klass->flags & TYPE_ATTRIBUTE_LAYOUT_MASK) == TYPE_ATTRIBUTE_AUTO_LAYOUT)
692 return (char*)obj + sizeof (MonoObject);
699 mono_gc_GCHandle_CheckCurrentDomain (guint32 gchandle)
701 return mono_gchandle_is_in_domain (gchandle, mono_domain_get ());
704 static MonoCoopSem finalizer_sem;
705 static volatile gboolean finished=FALSE;
708 mono_gc_finalize_notify (void)
711 g_message ( "%s: prodding finalizer", __func__);
714 if (mono_gc_is_null ())
717 mono_coop_sem_post (&finalizer_sem);
721 This is the number of entries allowed in the hazard free queue before
722 we explicitly cycle the finalizer thread to trigger pumping the queue.
724 It was picked empirically by running the corlib test suite in a stress
725 scenario where all hazard entries are queued.
727 In this extreme scenario we double the number of times we cycle the finalizer
728 thread compared to just GC calls.
730 Entries are usually in the order of 100's of bytes each, so we're limiting
731 floating garbage to be in the order of a dozen kb.
733 static gboolean finalizer_thread_pulsed;
734 #define HAZARD_QUEUE_OVERFLOW_SIZE 20
737 hazard_free_queue_is_too_big (size_t size)
739 if (size < HAZARD_QUEUE_OVERFLOW_SIZE)
742 if (finalizer_thread_pulsed || InterlockedCompareExchange (&finalizer_thread_pulsed, TRUE, FALSE))
745 mono_gc_finalize_notify ();
749 hazard_free_queue_pump (void)
751 mono_thread_hazardous_try_free_all ();
752 finalizer_thread_pulsed = FALSE;
758 collect_objects (gpointer key, gpointer value, gpointer user_data)
760 GPtrArray *arr = (GPtrArray*)user_data;
761 g_ptr_array_add (arr, key);
767 * finalize_domain_objects:
769 * Run the finalizers of all finalizable objects in req->domain.
772 finalize_domain_objects (DomainFinalizationReq *req)
774 MonoDomain *domain = req->domain;
776 /* Process finalizers which are already in the queue */
777 mono_gc_invoke_finalizers ();
780 while (g_hash_table_size (domain->finalizable_objects_hash) > 0) {
784 * Since the domain is unloading, nobody is allowed to put
785 * new entries into the hash table. But finalize_object might
786 * remove entries from the hash table, so we make a copy.
788 objs = g_ptr_array_new ();
789 g_hash_table_foreach (domain->finalizable_objects_hash, collect_objects, objs);
790 /* printf ("FINALIZING %d OBJECTS.\n", objs->len); */
792 for (i = 0; i < objs->len; ++i) {
793 MonoObject *o = (MonoObject*)g_ptr_array_index (objs, i);
794 /* FIXME: Avoid finalizing threads, etc */
795 mono_gc_run_finalize (o, 0);
798 g_ptr_array_free (objs, TRUE);
800 #elif defined(HAVE_SGEN_GC)
801 mono_gc_finalize_domain (domain);
802 mono_gc_invoke_finalizers ();
805 /* cleanup the reference queue */
806 reference_queue_clear_for_domain (domain);
808 /* printf ("DONE.\n"); */
810 SetEvent (req->done_event);
812 /* The event is closed in mono_domain_finalize if we get here */
815 mono_coop_mutex_lock (&req->mutex);
817 mono_coop_cond_signal (&req->cond);
818 mono_coop_mutex_unlock (&req->mutex);
823 finalizer_thread (gpointer unused)
826 mono_thread_set_name_internal (mono_thread_internal_current (), mono_string_new (mono_get_root_domain (), "Finalizer"), FALSE, &error);
827 mono_error_assert_ok (&error);
829 gboolean wait = TRUE;
831 /* Register a hazard free queue pump callback */
832 mono_hazard_pointer_install_free_queue_size_callback (hazard_free_queue_is_too_big);
835 /* Wait to be notified that there's at least one
839 g_assert (mono_domain_get () == mono_get_root_domain ());
840 mono_gc_set_skip_thread (TRUE);
843 /* An alertable wait is required so this thread can be suspended on windows */
844 mono_coop_sem_wait (&finalizer_sem, MONO_SEM_FLAGS_ALERTABLE);
848 mono_gc_set_skip_thread (FALSE);
850 mono_threads_perform_thread_dump ();
852 mono_console_handle_async_ops ();
854 mono_attach_maybe_start ();
856 if (domains_to_finalize) {
857 mono_finalizer_lock ();
858 if (domains_to_finalize) {
859 DomainFinalizationReq *req = (DomainFinalizationReq *)domains_to_finalize->data;
860 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
861 mono_finalizer_unlock ();
863 finalize_domain_objects (req);
865 mono_finalizer_unlock ();
869 /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup),
870 * before the domain is unloaded.
872 mono_gc_invoke_finalizers ();
874 mono_threads_join_threads ();
876 reference_queue_proccess_all ();
878 hazard_free_queue_pump ();
880 /* Avoid posting the pending done event until there are pending finalizers */
881 if (mono_coop_sem_timedwait (&finalizer_sem, 0, MONO_SEM_FLAGS_NONE) == MONO_SEM_TIMEDWAIT_RET_SUCCESS) {
882 /* Don't wait again at the start of the loop */
885 SetEvent (pending_done_event);
889 mono_finalizer_lock ();
890 finalizer_thread_exited = TRUE;
891 mono_coop_cond_signal (&exited_cond);
892 mono_finalizer_unlock ();
897 #ifndef LAZY_GC_THREAD_CREATION
901 mono_gc_init_finalizer_thread (void)
904 gc_thread = mono_thread_create_internal (mono_domain_get (), finalizer_thread, NULL, FALSE, 0, &error);
905 mono_error_assert_ok (&error);
911 mono_coop_mutex_init_recursive (&finalizer_mutex);
912 mono_coop_mutex_init_recursive (&reference_queue_mutex);
914 mono_counters_register ("Minor GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.minor_gc_count);
915 mono_counters_register ("Major GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.major_gc_count);
916 mono_counters_register ("Minor GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.minor_gc_time);
917 mono_counters_register ("Major GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time);
918 mono_counters_register ("Major GC time concurrent", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time_concurrent);
920 mono_gc_base_init ();
922 if (mono_gc_is_disabled ()) {
927 pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
928 g_assert (pending_done_event);
929 mono_coop_cond_init (&exited_cond);
930 mono_coop_sem_init (&finalizer_sem, 0);
932 #ifndef LAZY_GC_THREAD_CREATION
933 mono_gc_init_finalizer_thread ();
938 mono_gc_cleanup (void)
941 g_message ("%s: cleaning up finalizer", __func__);
944 if (mono_gc_is_null ())
949 if (mono_thread_internal_current () != gc_thread) {
950 gint64 start_ticks = mono_msec_ticks ();
951 gint64 end_ticks = start_ticks + 2000;
953 mono_gc_finalize_notify ();
954 /* Finishing the finalizer thread, so wait a little bit... */
955 /* MS seems to wait for about 2 seconds */
956 while (!finalizer_thread_exited) {
957 gint64 current_ticks = mono_msec_ticks ();
960 if (current_ticks >= end_ticks)
963 timeout = end_ticks - current_ticks;
964 mono_finalizer_lock ();
965 if (!finalizer_thread_exited)
966 mono_coop_cond_timedwait (&exited_cond, &finalizer_mutex, timeout);
967 mono_finalizer_unlock ();
970 if (!finalizer_thread_exited) {
973 /* Set a flag which the finalizer thread can check */
974 suspend_finalizers = TRUE;
975 mono_gc_suspend_finalizers ();
977 /* Try to abort the thread, in the hope that it is running managed code */
978 mono_thread_internal_abort (gc_thread);
980 /* Wait for it to stop */
981 ret = guarded_wait (gc_thread->handle, 100, TRUE);
983 if (ret == WAIT_TIMEOUT) {
985 * The finalizer thread refused to exit. Make it stop.
987 mono_thread_internal_stop (gc_thread);
988 ret = guarded_wait (gc_thread->handle, 100, TRUE);
989 g_assert (ret != WAIT_TIMEOUT);
990 /* The thread can't set this flag */
991 finalizer_thread_exited = TRUE;
997 /* Wait for the thread to actually exit */
998 ret = guarded_wait (gc_thread->handle, INFINITE, TRUE);
999 g_assert (ret == WAIT_OBJECT_0);
1001 mono_thread_join (GUINT_TO_POINTER (gc_thread->tid));
1002 g_assert (finalizer_thread_exited);
1005 mono_gc_base_cleanup ();
1008 mono_reference_queue_cleanup ();
1010 mono_coop_mutex_destroy (&finalizer_mutex);
1011 mono_coop_mutex_destroy (&reference_queue_mutex);
1015 mono_gc_is_finalizer_internal_thread (MonoInternalThread *thread)
1017 return thread == gc_thread;
1021 * mono_gc_is_finalizer_thread:
1022 * @thread: the thread to test.
1024 * In Mono objects are finalized asynchronously on a separate thread.
1025 * This routine tests whether the @thread argument represents the
1026 * finalization thread.
1028 * Returns: TRUE if @thread is the finalization thread.
1031 mono_gc_is_finalizer_thread (MonoThread *thread)
1033 return mono_gc_is_finalizer_internal_thread (thread->internal_thread);
1036 #if defined(__MACH__)
1037 static pthread_t mach_exception_thread;
1040 mono_gc_register_mach_exception_thread (pthread_t thread)
1042 mach_exception_thread = thread;
1046 mono_gc_get_mach_exception_thread (void)
1048 return mach_exception_thread;
1052 static MonoReferenceQueue *ref_queues;
1055 ref_list_remove_element (RefQueueEntry **prev, RefQueueEntry *element)
1058 /* Guard if head is changed concurrently. */
1059 while (*prev != element)
1060 prev = &(*prev)->next;
1061 } while (prev && InterlockedCompareExchangePointer ((volatile gpointer *)prev, element->next, element) != element);
1065 ref_list_push (RefQueueEntry **head, RefQueueEntry *value)
1067 RefQueueEntry *current;
1070 value->next = current;
1071 STORE_STORE_FENCE; /*Must make sure the previous store is visible before the CAS. */
1072 } while (InterlockedCompareExchangePointer ((volatile gpointer *)head, value, current) != current);
1076 reference_queue_proccess (MonoReferenceQueue *queue)
1078 RefQueueEntry **iter = &queue->queue;
1079 RefQueueEntry *entry;
1080 while ((entry = *iter)) {
1081 if (queue->should_be_deleted || !mono_gchandle_get_target (entry->gchandle)) {
1082 mono_gchandle_free ((guint32)entry->gchandle);
1083 ref_list_remove_element (iter, entry);
1084 queue->callback (entry->user_data);
1087 iter = &entry->next;
1093 reference_queue_proccess_all (void)
1095 MonoReferenceQueue **iter;
1096 MonoReferenceQueue *queue = ref_queues;
1097 for (; queue; queue = queue->next)
1098 reference_queue_proccess (queue);
1101 mono_coop_mutex_lock (&reference_queue_mutex);
1102 for (iter = &ref_queues; *iter;) {
1104 if (!queue->should_be_deleted) {
1105 iter = &queue->next;
1109 mono_coop_mutex_unlock (&reference_queue_mutex);
1110 reference_queue_proccess (queue);
1113 *iter = queue->next;
1116 mono_coop_mutex_unlock (&reference_queue_mutex);
1120 mono_reference_queue_cleanup (void)
1122 MonoReferenceQueue *queue = ref_queues;
1123 for (; queue; queue = queue->next)
1124 queue->should_be_deleted = TRUE;
1125 reference_queue_proccess_all ();
1129 reference_queue_clear_for_domain (MonoDomain *domain)
1131 MonoReferenceQueue *queue = ref_queues;
1132 for (; queue; queue = queue->next) {
1133 RefQueueEntry **iter = &queue->queue;
1134 RefQueueEntry *entry;
1135 while ((entry = *iter)) {
1136 if (entry->domain == domain) {
1137 mono_gchandle_free ((guint32)entry->gchandle);
1138 ref_list_remove_element (iter, entry);
1139 queue->callback (entry->user_data);
1142 iter = &entry->next;
1148 * mono_gc_reference_queue_new:
1149 * @callback callback used when processing collected entries.
1151 * Create a new reference queue used to process collected objects.
1152 * A reference queue let you add a pair of (managed object, user data)
1153 * using the mono_gc_reference_queue_add method.
1155 * Once the managed object is collected @callback will be called
1156 * in the finalizer thread with 'user data' as argument.
1158 * The callback is called from the finalizer thread without any locks held.
1159 * When a AppDomain is unloaded, all callbacks for objects belonging to it
1162 * @returns the new queue.
1165 mono_gc_reference_queue_new (mono_reference_queue_callback callback)
1167 MonoReferenceQueue *res = g_new0 (MonoReferenceQueue, 1);
1168 res->callback = callback;
1170 mono_coop_mutex_lock (&reference_queue_mutex);
1171 res->next = ref_queues;
1173 mono_coop_mutex_unlock (&reference_queue_mutex);
1179 * mono_gc_reference_queue_add:
1180 * @queue the queue to add the reference to.
1181 * @obj the object to be watched for collection
1182 * @user_data parameter to be passed to the queue callback
1184 * Queue an object to be watched for collection, when the @obj is
1185 * collected, the callback that was registered for the @queue will
1186 * be invoked with @user_data as argument.
1188 * @returns false if the queue is scheduled to be freed.
1191 mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *user_data)
1193 RefQueueEntry *entry;
1194 if (queue->should_be_deleted)
1197 g_assert (obj != NULL);
1199 entry = g_new0 (RefQueueEntry, 1);
1200 entry->user_data = user_data;
1201 entry->domain = mono_object_domain (obj);
1203 entry->gchandle = mono_gchandle_new_weakref (obj, TRUE);
1204 mono_object_register_finalizer (obj);
1206 ref_list_push (&queue->queue, entry);
1211 * mono_gc_reference_queue_free:
1212 * @queue the queue that should be freed.
1214 * This operation signals that @queue should be freed. This operation is deferred
1215 * as it happens on the finalizer thread.
1217 * After this call, no further objects can be queued. It's the responsibility of the
1218 * caller to make sure that no further attempt to access queue will be made.
1221 mono_gc_reference_queue_free (MonoReferenceQueue *queue)
1223 queue->should_be_deleted = TRUE;