5 * Author: Paolo Molaro <lupus@ximian.com>
7 * Copyright 2002-2003 Ximian, Inc (http://www.ximian.com)
8 * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
9 * Copyright 2012 Xamarin Inc (http://www.xamarin.com)
10 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
17 #include <mono/metadata/gc-internals.h>
18 #include <mono/metadata/mono-gc.h>
19 #include <mono/metadata/threads.h>
20 #include <mono/metadata/tabledefs.h>
21 #include <mono/metadata/exception.h>
22 #include <mono/metadata/profiler-private.h>
23 #include <mono/metadata/domain-internals.h>
24 #include <mono/metadata/class-internals.h>
25 #include <mono/metadata/metadata-internals.h>
26 #include <mono/metadata/mono-mlist.h>
27 #include <mono/metadata/threads-types.h>
28 #include <mono/metadata/threadpool.h>
29 #include <mono/sgen/sgen-conf.h>
30 #include <mono/sgen/sgen-gc.h>
31 #include <mono/utils/mono-logger-internals.h>
32 #include <mono/metadata/marshal.h> /* for mono_delegate_free_ftnptr () */
33 #include <mono/metadata/attach.h>
34 #include <mono/metadata/console-io.h>
35 #include <mono/metadata/w32process.h>
36 #include <mono/utils/mono-os-semaphore.h>
37 #include <mono/utils/mono-memory-model.h>
38 #include <mono/utils/mono-counters.h>
39 #include <mono/utils/mono-time.h>
40 #include <mono/utils/dtrace.h>
41 #include <mono/utils/mono-threads.h>
42 #include <mono/utils/mono-threads-coop.h>
43 #include <mono/utils/atomic.h>
44 #include <mono/utils/mono-coop-semaphore.h>
45 #include <mono/utils/hazard-pointer.h>
46 #include <mono/utils/w32api.h>
47 #include <mono/utils/unlocked.h>
53 typedef struct DomainFinalizationReq {
57 } DomainFinalizationReq;
59 static gboolean gc_disabled;
61 static gboolean finalizing_root_domain;
63 gboolean log_finalizers;
64 gboolean mono_do_not_finalize;
65 volatile gboolean suspend_finalizers;
66 gchar **mono_do_not_finalize_class_names ;
68 #define mono_finalizer_lock() mono_coop_mutex_lock (&finalizer_mutex)
69 #define mono_finalizer_unlock() mono_coop_mutex_unlock (&finalizer_mutex)
70 static MonoCoopMutex finalizer_mutex;
71 static MonoCoopMutex reference_queue_mutex;
73 static GSList *domains_to_finalize;
75 static gboolean finalizer_thread_exited;
76 /* Uses finalizer_mutex */
77 static MonoCoopCond exited_cond;
79 static MonoInternalThread *gc_thread;
82 static HANDLE pending_done_event;
84 static gboolean pending_done;
85 static MonoCoopCond pending_done_cond;
86 static MonoCoopMutex pending_done_mutex;
89 static void object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*));
91 static void reference_queue_proccess_all (void);
92 static void mono_reference_queue_cleanup (void);
93 static void reference_queue_clear_for_domain (MonoDomain *domain);
96 static MonoThreadInfoWaitRet
97 guarded_wait (MonoThreadHandle *thread_handle, guint32 timeout, gboolean alertable)
99 MonoThreadInfoWaitRet result;
102 result = mono_thread_info_wait_one_handle (thread_handle, timeout, alertable);
110 MonoCoopMutex *mutex;
111 } BreakCoopAlertableWaitUD;
114 break_coop_alertable_wait (gpointer user_data)
116 BreakCoopAlertableWaitUD *ud = (BreakCoopAlertableWaitUD*)user_data;
118 mono_coop_mutex_lock (ud->mutex);
119 mono_coop_cond_signal (ud->cond);
120 mono_coop_mutex_unlock (ud->mutex);
126 * coop_cond_timedwait_alertable:
128 * Wait on COND/MUTEX. If ALERTABLE is non-null, the wait can be interrupted.
129 * In that case, *ALERTABLE will be set to TRUE, and 0 is returned.
132 coop_cond_timedwait_alertable (MonoCoopCond *cond, MonoCoopMutex *mutex, guint32 timeout_ms, gboolean *alertable)
134 BreakCoopAlertableWaitUD *ud;
138 ud = g_new0 (BreakCoopAlertableWaitUD, 1);
142 mono_thread_info_install_interrupt (break_coop_alertable_wait, ud, alertable);
148 res = mono_coop_cond_timedwait (cond, mutex, timeout_ms);
150 mono_thread_info_uninstall_interrupt (alertable);
154 /* the interrupt token has not been taken by another
155 * thread, so it's our responsability to free it up. */
163 * actually, we might want to queue the finalize requests in a separate thread,
164 * but we need to be careful about the execution domain of the thread...
167 mono_gc_run_finalize (void *obj, void *data)
170 MonoObject *exc = NULL;
175 MonoMethod* finalizer = NULL;
176 MonoDomain *caller_domain = mono_domain_get ();
178 RuntimeInvokeFunction runtime_invoke;
180 // This function is called from the innards of the GC, so our best alternative for now is to do polling here
181 mono_threads_safepoint ();
183 o = (MonoObject*)((char*)obj + GPOINTER_TO_UINT (data));
185 if (mono_do_not_finalize) {
186 if (!mono_do_not_finalize_class_names)
189 size_t namespace_len = strlen (o->vtable->klass->name_space);
190 for (int i = 0; mono_do_not_finalize_class_names [i]; ++i) {
191 const char *name = mono_do_not_finalize_class_names [i];
192 if (strncmp (name, o->vtable->klass->name_space, namespace_len))
194 if (name [namespace_len] != '.')
196 if (strcmp (name + namespace_len + 1, o->vtable->klass->name))
203 g_log ("mono-gc-finalizers", G_LOG_LEVEL_DEBUG, "<%s at %p> Starting finalizer checks.", o->vtable->klass->name, o);
205 if (suspend_finalizers)
208 domain = o->vtable->domain;
211 mono_domain_finalizers_lock (domain);
213 o2 = (MonoObject *)g_hash_table_lookup (domain->finalizable_objects_hash, o);
215 mono_domain_finalizers_unlock (domain);
218 /* Already finalized somehow */
222 /* make sure the finalizer is not called again if the object is resurrected */
223 object_register_finalizer ((MonoObject *)obj, NULL);
226 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Registered finalizer as processed.", o->vtable->klass->name, o);
228 if (o->vtable->klass == mono_defaults.internal_thread_class) {
229 MonoInternalThread *t = (MonoInternalThread*)o;
231 if (mono_gc_is_finalizer_internal_thread (t))
232 /* Avoid finalizing ourselves */
236 if (o->vtable->klass->image == mono_defaults.corlib && !strcmp (o->vtable->klass->name, "DynamicMethod") && finalizing_root_domain) {
238 * These can't be finalized during unloading/shutdown, since that would
239 * free the native code which can still be referenced by other
241 * FIXME: This is not perfect, objects dying at the same time as
242 * dynamic methods can still reference them even when !shutdown.
247 if (mono_runtime_get_no_exec ())
250 /* speedup later... and use a timeout */
251 /* g_print ("Finalize run on %p %s.%s\n", o, mono_object_class (o)->name_space, mono_object_class (o)->name); */
253 /* Use _internal here, since this thread can enter a doomed appdomain */
254 mono_domain_set_internal (mono_object_domain (o));
256 /* delegates that have a native function pointer allocated are
257 * registered for finalization, but they don't have a Finalize
258 * method, because in most cases it's not needed and it's just a waste.
260 if (o->vtable->klass->delegate) {
261 MonoDelegate* del = (MonoDelegate*)o;
262 if (del->delegate_trampoline)
263 mono_delegate_free_ftnptr ((MonoDelegate*)o);
264 mono_domain_set_internal (caller_domain);
268 finalizer = mono_class_get_finalizer (o->vtable->klass);
270 /* If object has a CCW but has no finalizer, it was only
271 * registered for finalization in order to free the CCW.
272 * Else it needs the regular finalizer run.
273 * FIXME: what to do about ressurection and suppression
274 * of finalizer on object with CCW.
276 if (mono_marshal_free_ccw (o) && !finalizer) {
277 mono_domain_set_internal (caller_domain);
282 * To avoid the locking plus the other overhead of mono_runtime_invoke_checked (),
283 * create and precompile a wrapper which calls the finalize method using
287 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Compiling finalizer.", o->vtable->klass->name, o);
289 if (!domain->finalize_runtime_invoke) {
290 MonoMethod *invoke = mono_marshal_get_runtime_invoke (mono_class_get_method_from_name_flags (mono_defaults.object_class, "Finalize", 0, 0), TRUE);
292 domain->finalize_runtime_invoke = mono_compile_method_checked (invoke, &error);
293 mono_error_assert_ok (&error); /* expect this not to fail */
296 runtime_invoke = (RuntimeInvokeFunction)domain->finalize_runtime_invoke;
298 mono_runtime_class_init_full (o->vtable, &error);
300 goto unhandled_error;
302 if (G_UNLIKELY (MONO_GC_FINALIZE_INVOKE_ENABLED ())) {
303 MONO_GC_FINALIZE_INVOKE ((unsigned long)o, mono_object_get_size (o),
304 o->vtable->klass->name_space, o->vtable->klass->name);
308 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Calling finalizer.", o->vtable->klass->name, o);
310 MONO_PROFILER_RAISE (gc_finalizing_object, (o));
312 runtime_invoke (o, NULL, &exc, NULL);
314 MONO_PROFILER_RAISE (gc_finalized_object, (o));
317 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Returned from finalizer.", o->vtable->klass->name, o);
321 exc = (MonoObject*)mono_error_convert_to_exception (&error);
323 mono_thread_internal_unhandled_exception (exc);
325 mono_domain_set_internal (caller_domain);
329 mono_gc_out_of_memory (size_t size)
332 * we could allocate at program startup some memory that we could release
333 * back to the system at this point if we're really low on memory (ie, size is
334 * lower than the memory we set apart)
336 mono_raise_exception (mono_domain_get ()->out_of_memory_ex);
342 * Some of our objects may point to a different address than the address returned by GC_malloc()
343 * (because of the GetHashCode hack), but we need to pass the real address to register_finalizer.
344 * This also means that in the callback we need to adjust the pointer to get back the real
346 * We also need to be consistent in the use of the GC_debug* variants of malloc and register_finalizer,
347 * since that, too, can cause the underlying pointer to be offset.
350 object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*))
354 g_assert (obj != NULL);
356 domain = obj->vtable->domain;
359 if (mono_domain_is_unloading (domain) && (callback != NULL))
361 * Can't register finalizers in a dying appdomain, since they
362 * could be invoked after the appdomain has been unloaded.
366 mono_domain_finalizers_lock (domain);
369 g_hash_table_insert (domain->finalizable_objects_hash, obj, obj);
371 g_hash_table_remove (domain->finalizable_objects_hash, obj);
373 mono_domain_finalizers_unlock (domain);
375 mono_gc_register_for_finalization (obj, callback);
376 #elif defined(HAVE_SGEN_GC)
378 * If we register finalizers for domains that are unloading we might
379 * end up running them while or after the domain is being cleared, so
380 * the objects will not be valid anymore.
382 if (!mono_domain_is_unloading (domain))
383 mono_gc_register_for_finalization (obj, callback);
388 * mono_object_register_finalizer:
389 * \param obj object to register
391 * Records that object \p obj has a finalizer, this will call the
392 * Finalize method when the garbage collector disposes the object.
396 mono_object_register_finalizer (MonoObject *obj)
398 /* g_print ("Registered finalizer on %p %s.%s\n", obj, mono_object_class (obj)->name_space, mono_object_class (obj)->name); */
399 object_register_finalizer (obj, mono_gc_run_finalize);
403 * mono_domain_finalize:
404 * \param domain the domain to finalize
405 * \param timeout msecs to wait for the finalization to complete, \c -1 to wait indefinitely
407 * Request finalization of all finalizable objects inside \p domain. Wait
408 * \p timeout msecs for the finalization to complete.
410 * \returns TRUE if succeeded, FALSE if there was a timeout
413 mono_domain_finalize (MonoDomain *domain, guint32 timeout)
415 DomainFinalizationReq *req;
416 MonoInternalThread *thread = mono_thread_internal_current ();
421 if (mono_thread_internal_current () == gc_thread)
422 /* We are called from inside a finalizer, not much we can do here */
426 * No need to create another thread 'cause the finalizer thread
427 * is still working and will take care of running the finalizers
433 /* We don't support domain finalization without a GC */
434 if (mono_gc_is_null ())
437 mono_gc_collect (mono_gc_max_generation ());
439 req = g_new0 (DomainFinalizationReq, 1);
441 req->domain = domain;
442 mono_coop_sem_init (&req->done, 0);
444 if (domain == mono_get_root_domain ())
445 finalizing_root_domain = TRUE;
447 mono_finalizer_lock ();
449 domains_to_finalize = g_slist_append (domains_to_finalize, req);
451 mono_finalizer_unlock ();
453 /* Tell the finalizer thread to finalize this appdomain */
454 mono_gc_finalize_notify ();
457 timeout = MONO_INFINITE_WAIT;
458 if (timeout != MONO_INFINITE_WAIT)
459 start = mono_msec_ticks ();
464 if (timeout == MONO_INFINITE_WAIT) {
465 res = mono_coop_sem_wait (&req->done, MONO_SEM_FLAGS_ALERTABLE);
467 gint64 elapsed = mono_msec_ticks () - start;
468 if (elapsed >= timeout) {
473 res = mono_coop_sem_timedwait (&req->done, timeout - elapsed, MONO_SEM_FLAGS_ALERTABLE);
476 if (res == MONO_SEM_TIMEDWAIT_RET_SUCCESS) {
478 } else if (res == MONO_SEM_TIMEDWAIT_RET_ALERTED) {
479 if ((thread->state & (ThreadState_AbortRequested | ThreadState_SuspendRequested)) != 0) {
483 } else if (res == MONO_SEM_TIMEDWAIT_RET_TIMEDOUT) {
487 g_error ("%s: unknown result %d", __func__, res);
492 /* Try removing the req from domains_to_finalize:
493 * - if it's not found: the domain is being finalized,
494 * so we the ref count is already decremented
495 * - if it's found: the domain is not yet being finalized,
496 * so we can safely decrement the ref */
500 mono_finalizer_lock ();
502 found = g_slist_index (domains_to_finalize, req) != -1;
504 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
506 mono_finalizer_unlock ();
509 /* We have to decrement it wherever we
510 * remove it from domains_to_finalize */
511 if (InterlockedDecrement (&req->ref) != 1)
512 g_error ("%s: req->ref should be 1, as we are the first one to decrement it", __func__);
519 if (InterlockedDecrement (&req->ref) == 0) {
520 mono_coop_sem_destroy (&req->done);
528 ves_icall_System_GC_InternalCollect (int generation)
530 mono_gc_collect (generation);
534 ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection)
537 mono_gc_collect (mono_gc_max_generation ());
538 return mono_gc_get_used_size ();
542 ves_icall_System_GC_KeepAlive (MonoObject *obj)
550 ves_icall_System_GC_ReRegisterForFinalize (MonoObject *obj)
552 MONO_CHECK_ARG_NULL (obj,);
554 object_register_finalizer (obj, mono_gc_run_finalize);
558 ves_icall_System_GC_SuppressFinalize (MonoObject *obj)
560 MONO_CHECK_ARG_NULL (obj,);
562 /* delegates have no finalizers, but we register them to deal with the
563 * unmanaged->managed trampoline. We don't let the user suppress it
564 * otherwise we'd leak it.
566 if (obj->vtable->klass->delegate)
569 /* FIXME: Need to handle case where obj has COM Callable Wrapper
570 * generated for it that needs cleaned up, but user wants to suppress
571 * their derived object finalizer. */
573 object_register_finalizer (obj, NULL);
577 ves_icall_System_GC_WaitForPendingFinalizers (void)
579 if (mono_gc_is_null ())
582 if (!mono_gc_pending_finalizers ())
585 if (mono_thread_internal_current () == gc_thread)
586 /* Avoid deadlocks */
590 If the finalizer thread is not live, lets pretend no finalizers are pending since the current thread might
591 be the one responsible for starting it up.
593 if (gc_thread == NULL)
597 ResetEvent (pending_done_event);
598 mono_gc_finalize_notify ();
599 /* g_print ("Waiting for pending finalizers....\n"); */
601 WaitForSingleObjectEx (pending_done_event, INFINITE, TRUE);
603 /* g_print ("Done pending....\n"); */
605 gboolean alerted = FALSE;
606 mono_coop_mutex_lock (&pending_done_mutex);
607 pending_done = FALSE;
608 mono_gc_finalize_notify ();
609 while (!pending_done) {
610 coop_cond_timedwait_alertable (&pending_done_cond, &pending_done_mutex, MONO_INFINITE_WAIT, &alerted);
614 mono_coop_mutex_unlock (&pending_done_mutex);
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 (mono_class_is_auto_layout (klass))
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;
708 * mono_gc_finalize_notify:
710 * Notify the finalizer thread that finalizers etc.
711 * are available to be processed.
712 * This is async signal safe.
715 mono_gc_finalize_notify (void)
718 g_message ( "%s: prodding finalizer", __func__);
721 if (mono_gc_is_null ())
724 mono_coop_sem_post (&finalizer_sem);
728 This is the number of entries allowed in the hazard free queue before
729 we explicitly cycle the finalizer thread to trigger pumping the queue.
731 It was picked empirically by running the corlib test suite in a stress
732 scenario where all hazard entries are queued.
734 In this extreme scenario we double the number of times we cycle the finalizer
735 thread compared to just GC calls.
737 Entries are usually in the order of 100's of bytes each, so we're limiting
738 floating garbage to be in the order of a dozen kb.
740 static gboolean finalizer_thread_pulsed;
741 #define HAZARD_QUEUE_OVERFLOW_SIZE 20
744 hazard_free_queue_is_too_big (size_t size)
746 if (size < HAZARD_QUEUE_OVERFLOW_SIZE)
749 if (finalizer_thread_pulsed || InterlockedCompareExchange (&finalizer_thread_pulsed, TRUE, FALSE))
752 mono_gc_finalize_notify ();
756 hazard_free_queue_pump (void)
758 mono_thread_hazardous_try_free_all ();
759 finalizer_thread_pulsed = FALSE;
765 collect_objects (gpointer key, gpointer value, gpointer user_data)
767 GPtrArray *arr = (GPtrArray*)user_data;
768 g_ptr_array_add (arr, key);
774 * finalize_domain_objects:
776 * Run the finalizers of all finalizable objects in req->domain.
779 finalize_domain_objects (void)
781 DomainFinalizationReq *req = NULL;
784 if (UnlockedReadPointer ((gpointer)&domains_to_finalize)) {
785 mono_finalizer_lock ();
786 if (domains_to_finalize) {
787 req = (DomainFinalizationReq *)domains_to_finalize->data;
788 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
790 mono_finalizer_unlock ();
796 domain = req->domain;
798 /* Process finalizers which are already in the queue */
799 mono_gc_invoke_finalizers ();
802 while (g_hash_table_size (domain->finalizable_objects_hash) > 0) {
806 * Since the domain is unloading, nobody is allowed to put
807 * new entries into the hash table. But finalize_object might
808 * remove entries from the hash table, so we make a copy.
810 objs = g_ptr_array_new ();
811 g_hash_table_foreach (domain->finalizable_objects_hash, collect_objects, objs);
812 /* printf ("FINALIZING %d OBJECTS.\n", objs->len); */
814 for (i = 0; i < objs->len; ++i) {
815 MonoObject *o = (MonoObject*)g_ptr_array_index (objs, i);
816 /* FIXME: Avoid finalizing threads, etc */
817 mono_gc_run_finalize (o, 0);
820 g_ptr_array_free (objs, TRUE);
822 #elif defined(HAVE_SGEN_GC)
823 mono_gc_finalize_domain (domain);
824 mono_gc_invoke_finalizers ();
827 /* cleanup the reference queue */
828 reference_queue_clear_for_domain (domain);
830 /* printf ("DONE.\n"); */
831 mono_coop_sem_post (&req->done);
833 if (InterlockedDecrement (&req->ref) == 0) {
834 /* mono_domain_finalize already returned, and
835 * doesn't hold a reference to req anymore. */
836 mono_coop_sem_destroy (&req->done);
842 finalizer_thread (gpointer unused)
845 gboolean wait = TRUE;
847 MonoString *finalizer = mono_string_new_checked (mono_get_root_domain (), "Finalizer", &error);
848 mono_error_assert_ok (&error);
849 mono_thread_set_name_internal (mono_thread_internal_current (), finalizer, FALSE, FALSE, &error);
850 mono_error_assert_ok (&error);
852 /* Register a hazard free queue pump callback */
853 mono_hazard_pointer_install_free_queue_size_callback (hazard_free_queue_is_too_big);
856 /* Wait to be notified that there's at least one
860 g_assert (mono_domain_get () == mono_get_root_domain ());
861 mono_gc_set_skip_thread (TRUE);
864 /* An alertable wait is required so this thread can be suspended on windows */
865 mono_coop_sem_wait (&finalizer_sem, MONO_SEM_FLAGS_ALERTABLE);
869 mono_gc_set_skip_thread (FALSE);
871 mono_threads_perform_thread_dump ();
873 mono_console_handle_async_ops ();
875 mono_attach_maybe_start ();
877 finalize_domain_objects ();
879 MONO_PROFILER_RAISE (gc_finalizing, ());
881 /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup),
882 * before the domain is unloaded.
884 mono_gc_invoke_finalizers ();
886 MONO_PROFILER_RAISE (gc_finalized, ());
888 mono_threads_join_threads ();
890 reference_queue_proccess_all ();
892 mono_w32process_signal_finished ();
894 hazard_free_queue_pump ();
896 /* Avoid posting the pending done event until there are pending finalizers */
897 if (mono_coop_sem_timedwait (&finalizer_sem, 0, MONO_SEM_FLAGS_NONE) == MONO_SEM_TIMEDWAIT_RET_SUCCESS) {
898 /* Don't wait again at the start of the loop */
902 SetEvent (pending_done_event);
904 mono_coop_mutex_lock (&pending_done_mutex);
906 mono_coop_cond_signal (&pending_done_cond);
907 mono_coop_mutex_unlock (&pending_done_mutex);
912 mono_finalizer_lock ();
913 finalizer_thread_exited = TRUE;
914 mono_coop_cond_signal (&exited_cond);
915 mono_finalizer_unlock ();
920 #ifndef LAZY_GC_THREAD_CREATION
924 mono_gc_init_finalizer_thread (void)
927 gc_thread = mono_thread_create_internal (mono_domain_get (), finalizer_thread, NULL, MONO_THREAD_CREATE_FLAGS_NONE, &error);
928 mono_error_assert_ok (&error);
934 mono_coop_mutex_init_recursive (&finalizer_mutex);
935 mono_coop_mutex_init_recursive (&reference_queue_mutex);
937 mono_counters_register ("Minor GC collections", MONO_COUNTER_GC | MONO_COUNTER_INT, &gc_stats.minor_gc_count);
938 mono_counters_register ("Major GC collections", MONO_COUNTER_GC | MONO_COUNTER_INT, &gc_stats.major_gc_count);
939 mono_counters_register ("Minor GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.minor_gc_time);
940 mono_counters_register ("Major GC time", MONO_COUNTER_GC | MONO_COUNTER_LONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time);
941 mono_counters_register ("Major GC time concurrent", MONO_COUNTER_GC | MONO_COUNTER_LONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time_concurrent);
943 mono_gc_base_init ();
945 if (mono_gc_is_disabled ()) {
951 pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
952 g_assert (pending_done_event);
954 mono_coop_cond_init (&pending_done_cond);
955 mono_coop_mutex_init (&pending_done_mutex);
958 mono_coop_cond_init (&exited_cond);
959 mono_coop_sem_init (&finalizer_sem, 0);
961 #ifndef LAZY_GC_THREAD_CREATION
962 mono_gc_init_finalizer_thread ();
967 mono_gc_cleanup (void)
970 g_message ("%s: cleaning up finalizer", __func__);
973 if (mono_gc_is_null ())
978 if (mono_thread_internal_current () != gc_thread) {
981 const gint64 timeout = 40 * 1000;
983 mono_gc_finalize_notify ();
985 start = mono_msec_ticks ();
987 /* Finishing the finalizer thread, so wait a little bit... */
988 /* MS seems to wait for about 2 seconds per finalizer thread */
989 /* and 40 seconds for all finalizers to finish */
993 if (finalizer_thread_exited) {
994 /* Wait for the thread to actually exit. We don't want the wait
995 * to be alertable, because we assert on the result to be SUCCESS_0 */
996 ret = guarded_wait (gc_thread->handle, MONO_INFINITE_WAIT, FALSE);
997 g_assert (ret == MONO_THREAD_INFO_WAIT_RET_SUCCESS_0);
999 mono_threads_add_joinable_thread (GUINT_TO_POINTER (gc_thread->tid));
1003 elapsed = mono_msec_ticks () - start;
1004 if (elapsed >= timeout) {
1007 /* Set a flag which the finalizer thread can check */
1008 suspend_finalizers = TRUE;
1009 mono_gc_suspend_finalizers ();
1011 /* Try to abort the thread, in the hope that it is running managed code */
1012 mono_thread_internal_abort (gc_thread, FALSE);
1014 /* Wait for it to stop */
1015 ret = guarded_wait (gc_thread->handle, 100, FALSE);
1016 if (ret == MONO_THREAD_INFO_WAIT_RET_TIMEOUT) {
1017 /* The finalizer thread refused to exit, suspend it forever. */
1018 mono_thread_internal_suspend_for_shutdown (gc_thread);
1022 g_assert (ret == MONO_THREAD_INFO_WAIT_RET_SUCCESS_0);
1024 mono_threads_add_joinable_thread (GUINT_TO_POINTER (gc_thread->tid));
1028 mono_finalizer_lock ();
1029 if (!finalizer_thread_exited)
1030 mono_coop_cond_timedwait (&exited_cond, &finalizer_mutex, timeout - elapsed);
1031 mono_finalizer_unlock ();
1035 mono_gc_base_cleanup ();
1038 mono_reference_queue_cleanup ();
1040 mono_coop_mutex_destroy (&finalizer_mutex);
1041 mono_coop_mutex_destroy (&reference_queue_mutex);
1045 mono_gc_is_finalizer_internal_thread (MonoInternalThread *thread)
1047 return thread == gc_thread;
1051 * mono_gc_is_finalizer_thread:
1052 * \param thread the thread to test.
1054 * In Mono objects are finalized asynchronously on a separate thread.
1055 * This routine tests whether the \p thread argument represents the
1056 * finalization thread.
1058 * \returns TRUE if \p thread is the finalization thread.
1061 mono_gc_is_finalizer_thread (MonoThread *thread)
1063 return mono_gc_is_finalizer_internal_thread (thread->internal_thread);
1066 #if defined(__MACH__)
1067 static pthread_t mach_exception_thread;
1070 mono_gc_register_mach_exception_thread (pthread_t thread)
1072 mach_exception_thread = thread;
1076 mono_gc_get_mach_exception_thread (void)
1078 return mach_exception_thread;
1082 static MonoReferenceQueue *ref_queues;
1085 ref_list_remove_element (RefQueueEntry **prev, RefQueueEntry *element)
1088 /* Guard if head is changed concurrently. */
1089 while (*prev != element)
1090 prev = &(*prev)->next;
1091 } while (prev && InterlockedCompareExchangePointer ((volatile gpointer *)prev, element->next, element) != element);
1095 ref_list_push (RefQueueEntry **head, RefQueueEntry *value)
1097 RefQueueEntry *current;
1100 value->next = current;
1101 STORE_STORE_FENCE; /*Must make sure the previous store is visible before the CAS. */
1102 } while (InterlockedCompareExchangePointer ((volatile gpointer *)head, value, current) != current);
1106 reference_queue_proccess (MonoReferenceQueue *queue)
1108 RefQueueEntry **iter = &queue->queue;
1109 RefQueueEntry *entry;
1110 while ((entry = *iter)) {
1111 if (queue->should_be_deleted || !mono_gchandle_get_target (entry->gchandle)) {
1112 mono_gchandle_free ((guint32)entry->gchandle);
1113 ref_list_remove_element (iter, entry);
1114 queue->callback (entry->user_data);
1117 iter = &entry->next;
1123 reference_queue_proccess_all (void)
1125 MonoReferenceQueue **iter;
1126 MonoReferenceQueue *queue = ref_queues;
1127 for (; queue; queue = queue->next)
1128 reference_queue_proccess (queue);
1131 mono_coop_mutex_lock (&reference_queue_mutex);
1132 for (iter = &ref_queues; *iter;) {
1134 if (!queue->should_be_deleted) {
1135 iter = &queue->next;
1139 mono_coop_mutex_unlock (&reference_queue_mutex);
1140 reference_queue_proccess (queue);
1143 *iter = queue->next;
1146 mono_coop_mutex_unlock (&reference_queue_mutex);
1150 mono_reference_queue_cleanup (void)
1152 MonoReferenceQueue *queue = ref_queues;
1153 for (; queue; queue = queue->next)
1154 queue->should_be_deleted = TRUE;
1155 reference_queue_proccess_all ();
1159 reference_queue_clear_for_domain (MonoDomain *domain)
1161 MonoReferenceQueue *queue = ref_queues;
1162 for (; queue; queue = queue->next) {
1163 RefQueueEntry **iter = &queue->queue;
1164 RefQueueEntry *entry;
1165 while ((entry = *iter)) {
1166 if (entry->domain == domain) {
1167 mono_gchandle_free ((guint32)entry->gchandle);
1168 ref_list_remove_element (iter, entry);
1169 queue->callback (entry->user_data);
1172 iter = &entry->next;
1178 * mono_gc_reference_queue_new:
1179 * \param callback callback used when processing collected entries.
1181 * Create a new reference queue used to process collected objects.
1182 * A reference queue let you add a pair of (managed object, user data)
1183 * using the \c mono_gc_reference_queue_add method.
1185 * Once the managed object is collected \p callback will be called
1186 * in the finalizer thread with 'user data' as argument.
1188 * The callback is called from the finalizer thread without any locks held.
1189 * When an AppDomain is unloaded, all callbacks for objects belonging to it
1192 * \returns the new queue.
1195 mono_gc_reference_queue_new (mono_reference_queue_callback callback)
1197 MonoReferenceQueue *res = g_new0 (MonoReferenceQueue, 1);
1198 res->callback = callback;
1200 mono_coop_mutex_lock (&reference_queue_mutex);
1201 res->next = ref_queues;
1203 mono_coop_mutex_unlock (&reference_queue_mutex);
1209 * mono_gc_reference_queue_add:
1210 * \param queue the queue to add the reference to.
1211 * \param obj the object to be watched for collection
1212 * \param user_data parameter to be passed to the queue callback
1214 * Queue an object to be watched for collection, when the \p obj is
1215 * collected, the callback that was registered for the \p queue will
1216 * be invoked with \p user_data as argument.
1218 * \returns FALSE if the queue is scheduled to be freed.
1221 mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *user_data)
1223 RefQueueEntry *entry;
1224 if (queue->should_be_deleted)
1227 g_assert (obj != NULL);
1229 entry = g_new0 (RefQueueEntry, 1);
1230 entry->user_data = user_data;
1231 entry->domain = mono_object_domain (obj);
1233 entry->gchandle = mono_gchandle_new_weakref (obj, TRUE);
1234 mono_object_register_finalizer (obj);
1236 ref_list_push (&queue->queue, entry);
1241 * mono_gc_reference_queue_free:
1242 * \param queue the queue that should be freed.
1244 * This operation signals that \p queue should be freed. This operation is deferred
1245 * as it happens on the finalizer thread.
1247 * After this call, no further objects can be queued. It's the responsibility of the
1248 * caller to make sure that no further attempt to access queue will be made.
1251 mono_gc_reference_queue_free (MonoReferenceQueue *queue)
1253 queue->should_be_deleted = TRUE;