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/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/utils/w32api.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;
73 static gboolean finalizer_thread_exited;
74 /* Uses finalizer_mutex */
75 static MonoCoopCond exited_cond;
77 static MonoInternalThread *gc_thread;
80 static HANDLE pending_done_event;
82 static gboolean pending_done;
83 static MonoCoopCond pending_done_cond;
84 static MonoCoopMutex pending_done_mutex;
87 static void object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*));
89 static void reference_queue_proccess_all (void);
90 static void mono_reference_queue_cleanup (void);
91 static void reference_queue_clear_for_domain (MonoDomain *domain);
94 static MonoThreadInfoWaitRet
95 guarded_wait (MonoThreadHandle *thread_handle, guint32 timeout, gboolean alertable)
97 MonoThreadInfoWaitRet result;
100 result = mono_thread_info_wait_one_handle (thread_handle, timeout, alertable);
108 MonoCoopMutex *mutex;
109 } BreakCoopAlertableWaitUD;
112 break_coop_alertable_wait (gpointer user_data)
114 BreakCoopAlertableWaitUD *ud = (BreakCoopAlertableWaitUD*)user_data;
116 mono_coop_mutex_lock (ud->mutex);
117 mono_coop_cond_signal (ud->cond);
118 mono_coop_mutex_unlock (ud->mutex);
124 * coop_cond_timedwait_alertable:
126 * Wait on COND/MUTEX. If ALERTABLE is non-null, the wait can be interrupted.
127 * In that case, *ALERTABLE will be set to TRUE, and 0 is returned.
130 coop_cond_timedwait_alertable (MonoCoopCond *cond, MonoCoopMutex *mutex, guint32 timeout_ms, gboolean *alertable)
132 BreakCoopAlertableWaitUD *ud;
136 ud = g_new0 (BreakCoopAlertableWaitUD, 1);
140 mono_thread_info_install_interrupt (break_coop_alertable_wait, ud, alertable);
146 res = mono_coop_cond_timedwait (cond, mutex, timeout_ms);
148 mono_thread_info_uninstall_interrupt (alertable);
152 /* the interrupt token has not been taken by another
153 * thread, so it's our responsability to free it up. */
161 * 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 */
234 if (o->vtable->klass->image == mono_defaults.corlib && !strcmp (o->vtable->klass->name, "DynamicMethod") && finalizing_root_domain) {
236 * These can't be finalized during unloading/shutdown, since that would
237 * free the native code which can still be referenced by other
239 * FIXME: This is not perfect, objects dying at the same time as
240 * dynamic methods can still reference them even when !shutdown.
245 if (mono_runtime_get_no_exec ())
248 /* speedup later... and use a timeout */
249 /* g_print ("Finalize run on %p %s.%s\n", o, mono_object_class (o)->name_space, mono_object_class (o)->name); */
251 /* Use _internal here, since this thread can enter a doomed appdomain */
252 mono_domain_set_internal (mono_object_domain (o));
254 /* delegates that have a native function pointer allocated are
255 * registered for finalization, but they don't have a Finalize
256 * method, because in most cases it's not needed and it's just a waste.
258 if (o->vtable->klass->delegate) {
259 MonoDelegate* del = (MonoDelegate*)o;
260 if (del->delegate_trampoline)
261 mono_delegate_free_ftnptr ((MonoDelegate*)o);
262 mono_domain_set_internal (caller_domain);
266 finalizer = mono_class_get_finalizer (o->vtable->klass);
268 /* If object has a CCW but has no finalizer, it was only
269 * registered for finalization in order to free the CCW.
270 * Else it needs the regular finalizer run.
271 * FIXME: what to do about ressurection and suppression
272 * of finalizer on object with CCW.
274 if (mono_marshal_free_ccw (o) && !finalizer) {
275 mono_domain_set_internal (caller_domain);
280 * To avoid the locking plus the other overhead of mono_runtime_invoke_checked (),
281 * create and precompile a wrapper which calls the finalize method using
285 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Compiling finalizer.", o->vtable->klass->name, o);
287 if (!domain->finalize_runtime_invoke) {
288 MonoMethod *invoke = mono_marshal_get_runtime_invoke (mono_class_get_method_from_name_flags (mono_defaults.object_class, "Finalize", 0, 0), TRUE);
290 domain->finalize_runtime_invoke = mono_compile_method_checked (invoke, &error);
291 mono_error_assert_ok (&error); /* expect this not to fail */
294 runtime_invoke = (RuntimeInvokeFunction)domain->finalize_runtime_invoke;
296 mono_runtime_class_init_full (o->vtable, &error);
298 goto unhandled_error;
300 if (G_UNLIKELY (MONO_GC_FINALIZE_INVOKE_ENABLED ())) {
301 MONO_GC_FINALIZE_INVOKE ((unsigned long)o, mono_object_get_size (o),
302 o->vtable->klass->name_space, o->vtable->klass->name);
306 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Calling finalizer.", o->vtable->klass->name, o);
308 mono_profiler_gc_finalize_object_begin (o);
310 runtime_invoke (o, NULL, &exc, NULL);
312 mono_profiler_gc_finalize_object_end (o);
315 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Returned from finalizer.", o->vtable->klass->name, o);
319 exc = (MonoObject*)mono_error_convert_to_exception (&error);
321 mono_thread_internal_unhandled_exception (exc);
323 mono_domain_set_internal (caller_domain);
327 mono_gc_out_of_memory (size_t size)
330 * we could allocate at program startup some memory that we could release
331 * back to the system at this point if we're really low on memory (ie, size is
332 * lower than the memory we set apart)
334 mono_raise_exception (mono_domain_get ()->out_of_memory_ex);
340 * Some of our objects may point to a different address than the address returned by GC_malloc()
341 * (because of the GetHashCode hack), but we need to pass the real address to register_finalizer.
342 * This also means that in the callback we need to adjust the pointer to get back the real
344 * We also need to be consistent in the use of the GC_debug* variants of malloc and register_finalizer,
345 * since that, too, can cause the underlying pointer to be offset.
348 object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*))
352 g_assert (obj != NULL);
354 domain = obj->vtable->domain;
357 if (mono_domain_is_unloading (domain) && (callback != NULL))
359 * Can't register finalizers in a dying appdomain, since they
360 * could be invoked after the appdomain has been unloaded.
364 mono_domain_finalizers_lock (domain);
367 g_hash_table_insert (domain->finalizable_objects_hash, obj, obj);
369 g_hash_table_remove (domain->finalizable_objects_hash, obj);
371 mono_domain_finalizers_unlock (domain);
373 mono_gc_register_for_finalization (obj, callback);
374 #elif defined(HAVE_SGEN_GC)
376 * If we register finalizers for domains that are unloading we might
377 * end up running them while or after the domain is being cleared, so
378 * the objects will not be valid anymore.
380 if (!mono_domain_is_unloading (domain))
381 mono_gc_register_for_finalization (obj, callback);
386 * mono_object_register_finalizer:
387 * \param obj object to register
389 * Records that object \p obj has a finalizer, this will call the
390 * Finalize method when the garbage collector disposes the object.
394 mono_object_register_finalizer (MonoObject *obj)
396 /* g_print ("Registered finalizer on %p %s.%s\n", obj, mono_object_class (obj)->name_space, mono_object_class (obj)->name); */
397 object_register_finalizer (obj, mono_gc_run_finalize);
401 * mono_domain_finalize:
402 * \param domain the domain to finalize
403 * \param timeout msects to wait for the finalization to complete, -1 to wait indefinitely
405 * Request finalization of all finalizable objects inside \p domain. Wait
406 * \p timeout msecs for the finalization to complete.
408 * \returns TRUE if succeeded, FALSE if there was a timeout
412 mono_domain_finalize (MonoDomain *domain, guint32 timeout)
414 DomainFinalizationReq *req;
415 MonoInternalThread *thread = mono_thread_internal_current ();
420 #if defined(__native_client__)
424 if (mono_thread_internal_current () == gc_thread)
425 /* We are called from inside a finalizer, not much we can do here */
429 * No need to create another thread 'cause the finalizer thread
430 * is still working and will take care of running the finalizers
436 /* We don't support domain finalization without a GC */
437 if (mono_gc_is_null ())
440 mono_gc_collect (mono_gc_max_generation ());
442 req = g_new0 (DomainFinalizationReq, 1);
444 req->domain = domain;
445 mono_coop_sem_init (&req->done, 0);
447 if (domain == mono_get_root_domain ())
448 finalizing_root_domain = TRUE;
450 mono_finalizer_lock ();
452 domains_to_finalize = g_slist_append (domains_to_finalize, req);
454 mono_finalizer_unlock ();
456 /* Tell the finalizer thread to finalize this appdomain */
457 mono_gc_finalize_notify ();
460 timeout = MONO_INFINITE_WAIT;
461 if (timeout != MONO_INFINITE_WAIT)
462 start = mono_msec_ticks ();
467 if (timeout == MONO_INFINITE_WAIT) {
468 res = mono_coop_sem_wait (&req->done, MONO_SEM_FLAGS_ALERTABLE);
470 gint64 elapsed = mono_msec_ticks () - start;
471 if (elapsed >= timeout) {
476 res = mono_coop_sem_timedwait (&req->done, timeout - elapsed, MONO_SEM_FLAGS_ALERTABLE);
479 if (res == MONO_SEM_TIMEDWAIT_RET_SUCCESS) {
481 } else if (res == MONO_SEM_TIMEDWAIT_RET_ALERTED) {
482 if ((thread->state & (ThreadState_AbortRequested | ThreadState_SuspendRequested)) != 0) {
486 } else if (res == MONO_SEM_TIMEDWAIT_RET_TIMEDOUT) {
490 g_error ("%s: unknown result %d", __func__, res);
495 /* Try removing the req from domains_to_finalize:
496 * - if it's not found: the domain is being finalized,
497 * so we the ref count is already decremented
498 * - if it's found: the domain is not yet being finalized,
499 * so we can safely decrement the ref */
503 mono_finalizer_lock ();
505 found = g_slist_index (domains_to_finalize, req) != -1;
507 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
509 mono_finalizer_unlock ();
512 /* We have to decrement it wherever we
513 * remove it from domains_to_finalize */
514 if (InterlockedDecrement (&req->ref) != 1)
515 g_error ("%s: req->ref should be 1, as we are the first one to decrement it", __func__);
522 if (InterlockedDecrement (&req->ref) == 0) {
523 mono_coop_sem_destroy (&req->done);
531 ves_icall_System_GC_InternalCollect (int generation)
533 mono_gc_collect (generation);
537 ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection)
540 mono_gc_collect (mono_gc_max_generation ());
541 return mono_gc_get_used_size ();
545 ves_icall_System_GC_KeepAlive (MonoObject *obj)
553 ves_icall_System_GC_ReRegisterForFinalize (MonoObject *obj)
555 MONO_CHECK_ARG_NULL (obj,);
557 object_register_finalizer (obj, mono_gc_run_finalize);
561 ves_icall_System_GC_SuppressFinalize (MonoObject *obj)
563 MONO_CHECK_ARG_NULL (obj,);
565 /* delegates have no finalizers, but we register them to deal with the
566 * unmanaged->managed trampoline. We don't let the user suppress it
567 * otherwise we'd leak it.
569 if (obj->vtable->klass->delegate)
572 /* FIXME: Need to handle case where obj has COM Callable Wrapper
573 * generated for it that needs cleaned up, but user wants to suppress
574 * their derived object finalizer. */
576 object_register_finalizer (obj, NULL);
580 ves_icall_System_GC_WaitForPendingFinalizers (void)
582 if (mono_gc_is_null ())
585 if (!mono_gc_pending_finalizers ())
588 if (mono_thread_internal_current () == gc_thread)
589 /* Avoid deadlocks */
593 If the finalizer thread is not live, lets pretend no finalizers are pending since the current thread might
594 be the one responsible for starting it up.
596 if (gc_thread == NULL)
600 ResetEvent (pending_done_event);
601 mono_gc_finalize_notify ();
602 /* g_print ("Waiting for pending finalizers....\n"); */
604 WaitForSingleObjectEx (pending_done_event, INFINITE, TRUE);
606 /* g_print ("Done pending....\n"); */
608 gboolean alerted = FALSE;
609 mono_coop_mutex_lock (&pending_done_mutex);
610 pending_done = FALSE;
611 mono_gc_finalize_notify ();
612 while (!pending_done) {
613 coop_cond_timedwait_alertable (&pending_done_cond, &pending_done_mutex, MONO_INFINITE_WAIT, &alerted);
617 mono_coop_mutex_unlock (&pending_done_mutex);
622 ves_icall_System_GC_register_ephemeron_array (MonoObject *array)
625 if (!mono_gc_ephemeron_array_add (array)) {
626 mono_set_pending_exception (mono_object_domain (array)->out_of_memory_ex);
633 ves_icall_System_GC_get_ephemeron_tombstone (void)
635 return mono_domain_get ()->ephemeron_tombstone;
639 ves_icall_System_GCHandle_GetTarget (guint32 handle)
641 return mono_gchandle_get_target (handle);
645 * if type == -1, change the target of the handle, otherwise allocate a new handle.
648 ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type)
651 mono_gchandle_set_target (handle, obj);
652 /* the handle doesn't change */
657 return mono_gchandle_new_weakref (obj, FALSE);
658 case HANDLE_WEAK_TRACK:
659 return mono_gchandle_new_weakref (obj, TRUE);
661 return mono_gchandle_new (obj, FALSE);
663 return mono_gchandle_new (obj, TRUE);
665 g_assert_not_reached ();
671 ves_icall_System_GCHandle_FreeHandle (guint32 handle)
673 mono_gchandle_free (handle);
677 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
681 if (MONO_GC_HANDLE_TYPE (handle) != HANDLE_PINNED)
683 obj = mono_gchandle_get_target (handle);
685 MonoClass *klass = mono_object_class (obj);
686 if (klass == mono_defaults.string_class) {
687 return mono_string_chars ((MonoString*)obj);
688 } else if (klass->rank) {
689 return mono_array_addr ((MonoArray*)obj, char, 0);
691 /* the C# code will check and throw the exception */
692 /* FIXME: missing !klass->blittable test, see bug #61134 */
693 if (mono_class_is_auto_layout (klass))
695 return (char*)obj + sizeof (MonoObject);
702 mono_gc_GCHandle_CheckCurrentDomain (guint32 gchandle)
704 return mono_gchandle_is_in_domain (gchandle, mono_domain_get ());
707 static MonoCoopSem finalizer_sem;
708 static volatile gboolean finished;
711 * mono_gc_finalize_notify:
713 * Notify the finalizer thread that finalizers etc.
714 * are available to be processed.
717 mono_gc_finalize_notify (void)
720 g_message ( "%s: prodding finalizer", __func__);
723 if (mono_gc_is_null ())
726 mono_coop_sem_post (&finalizer_sem);
730 This is the number of entries allowed in the hazard free queue before
731 we explicitly cycle the finalizer thread to trigger pumping the queue.
733 It was picked empirically by running the corlib test suite in a stress
734 scenario where all hazard entries are queued.
736 In this extreme scenario we double the number of times we cycle the finalizer
737 thread compared to just GC calls.
739 Entries are usually in the order of 100's of bytes each, so we're limiting
740 floating garbage to be in the order of a dozen kb.
742 static gboolean finalizer_thread_pulsed;
743 #define HAZARD_QUEUE_OVERFLOW_SIZE 20
746 hazard_free_queue_is_too_big (size_t size)
748 if (size < HAZARD_QUEUE_OVERFLOW_SIZE)
751 if (finalizer_thread_pulsed || InterlockedCompareExchange (&finalizer_thread_pulsed, TRUE, FALSE))
754 mono_gc_finalize_notify ();
758 hazard_free_queue_pump (void)
760 mono_thread_hazardous_try_free_all ();
761 finalizer_thread_pulsed = FALSE;
767 collect_objects (gpointer key, gpointer value, gpointer user_data)
769 GPtrArray *arr = (GPtrArray*)user_data;
770 g_ptr_array_add (arr, key);
776 * finalize_domain_objects:
778 * Run the finalizers of all finalizable objects in req->domain.
781 finalize_domain_objects (void)
783 DomainFinalizationReq *req = NULL;
786 if (domains_to_finalize) {
787 mono_finalizer_lock ();
788 if (domains_to_finalize) {
789 req = (DomainFinalizationReq *)domains_to_finalize->data;
790 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
792 mono_finalizer_unlock ();
798 domain = req->domain;
800 /* Process finalizers which are already in the queue */
801 mono_gc_invoke_finalizers ();
804 while (g_hash_table_size (domain->finalizable_objects_hash) > 0) {
808 * Since the domain is unloading, nobody is allowed to put
809 * new entries into the hash table. But finalize_object might
810 * remove entries from the hash table, so we make a copy.
812 objs = g_ptr_array_new ();
813 g_hash_table_foreach (domain->finalizable_objects_hash, collect_objects, objs);
814 /* printf ("FINALIZING %d OBJECTS.\n", objs->len); */
816 for (i = 0; i < objs->len; ++i) {
817 MonoObject *o = (MonoObject*)g_ptr_array_index (objs, i);
818 /* FIXME: Avoid finalizing threads, etc */
819 mono_gc_run_finalize (o, 0);
822 g_ptr_array_free (objs, TRUE);
824 #elif defined(HAVE_SGEN_GC)
825 mono_gc_finalize_domain (domain);
826 mono_gc_invoke_finalizers ();
829 /* cleanup the reference queue */
830 reference_queue_clear_for_domain (domain);
832 /* printf ("DONE.\n"); */
833 mono_coop_sem_post (&req->done);
835 if (InterlockedDecrement (&req->ref) == 0) {
836 /* mono_domain_finalize already returned, and
837 * doesn't hold a reference to req anymore. */
838 mono_coop_sem_destroy (&req->done);
844 finalizer_thread (gpointer unused)
847 gboolean wait = TRUE;
849 mono_thread_set_name_internal (mono_thread_internal_current (), mono_string_new (mono_get_root_domain (), "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_gc_finalize_begin ();
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_gc_finalize_end ();
888 mono_threads_join_threads ();
890 reference_queue_proccess_all ();
892 hazard_free_queue_pump ();
894 /* Avoid posting the pending done event until there are pending finalizers */
895 if (mono_coop_sem_timedwait (&finalizer_sem, 0, MONO_SEM_FLAGS_NONE) == MONO_SEM_TIMEDWAIT_RET_SUCCESS) {
896 /* Don't wait again at the start of the loop */
900 SetEvent (pending_done_event);
902 mono_coop_mutex_lock (&pending_done_mutex);
904 mono_coop_cond_signal (&pending_done_cond);
905 mono_coop_mutex_unlock (&pending_done_mutex);
910 mono_finalizer_lock ();
911 finalizer_thread_exited = TRUE;
912 mono_coop_cond_signal (&exited_cond);
913 mono_finalizer_unlock ();
918 #ifndef LAZY_GC_THREAD_CREATION
922 mono_gc_init_finalizer_thread (void)
925 gc_thread = mono_thread_create_internal (mono_domain_get (), finalizer_thread, NULL, MONO_THREAD_CREATE_FLAGS_NONE, &error);
926 mono_error_assert_ok (&error);
932 mono_coop_mutex_init_recursive (&finalizer_mutex);
933 mono_coop_mutex_init_recursive (&reference_queue_mutex);
935 mono_counters_register ("Minor GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.minor_gc_count);
936 mono_counters_register ("Major GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.major_gc_count);
937 mono_counters_register ("Minor GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.minor_gc_time);
938 mono_counters_register ("Major GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time);
939 mono_counters_register ("Major GC time concurrent", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time_concurrent);
941 mono_gc_base_init ();
943 if (mono_gc_is_disabled ()) {
949 pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
950 g_assert (pending_done_event);
952 mono_coop_cond_init (&pending_done_cond);
953 mono_coop_mutex_init (&pending_done_mutex);
956 mono_coop_cond_init (&exited_cond);
957 mono_coop_sem_init (&finalizer_sem, 0);
959 #ifndef LAZY_GC_THREAD_CREATION
960 mono_gc_init_finalizer_thread ();
965 mono_gc_cleanup (void)
968 g_message ("%s: cleaning up finalizer", __func__);
971 if (mono_gc_is_null ())
976 if (mono_thread_internal_current () != gc_thread) {
979 const gint64 timeout = 40 * 1000;
981 mono_gc_finalize_notify ();
983 start = mono_msec_ticks ();
985 /* Finishing the finalizer thread, so wait a little bit... */
986 /* MS seems to wait for about 2 seconds per finalizer thread */
987 /* and 40 seconds for all finalizers to finish */
991 if (finalizer_thread_exited) {
992 /* Wait for the thread to actually exit. We don't want the wait
993 * to be alertable, because we assert on the result to be SUCCESS_0 */
994 ret = guarded_wait (gc_thread->handle, MONO_INFINITE_WAIT, FALSE);
995 g_assert (ret == MONO_THREAD_INFO_WAIT_RET_SUCCESS_0);
997 mono_thread_join (GUINT_TO_POINTER (gc_thread->tid));
1001 elapsed = mono_msec_ticks () - start;
1002 if (elapsed >= timeout) {
1005 /* Set a flag which the finalizer thread can check */
1006 suspend_finalizers = TRUE;
1007 mono_gc_suspend_finalizers ();
1009 /* Try to abort the thread, in the hope that it is running managed code */
1010 mono_thread_internal_abort (gc_thread);
1012 /* Wait for it to stop */
1013 ret = guarded_wait (gc_thread->handle, 100, FALSE);
1014 if (ret == MONO_THREAD_INFO_WAIT_RET_TIMEOUT) {
1015 /* The finalizer thread refused to exit, suspend it forever. */
1016 mono_thread_internal_suspend_for_shutdown (gc_thread);
1020 g_assert (ret == MONO_THREAD_INFO_WAIT_RET_SUCCESS_0);
1022 mono_thread_join (GUINT_TO_POINTER (gc_thread->tid));
1026 mono_finalizer_lock ();
1027 if (!finalizer_thread_exited)
1028 mono_coop_cond_timedwait (&exited_cond, &finalizer_mutex, timeout - elapsed);
1029 mono_finalizer_unlock ();
1033 mono_gc_base_cleanup ();
1036 mono_reference_queue_cleanup ();
1038 mono_coop_mutex_destroy (&finalizer_mutex);
1039 mono_coop_mutex_destroy (&reference_queue_mutex);
1043 mono_gc_is_finalizer_internal_thread (MonoInternalThread *thread)
1045 return thread == gc_thread;
1049 * mono_gc_is_finalizer_thread:
1050 * \param thread the thread to test.
1052 * In Mono objects are finalized asynchronously on a separate thread.
1053 * This routine tests whether the \p thread argument represents the
1054 * finalization thread.
1056 * \returns TRUE if \p thread is the finalization thread.
1059 mono_gc_is_finalizer_thread (MonoThread *thread)
1061 return mono_gc_is_finalizer_internal_thread (thread->internal_thread);
1064 #if defined(__MACH__)
1065 static pthread_t mach_exception_thread;
1068 mono_gc_register_mach_exception_thread (pthread_t thread)
1070 mach_exception_thread = thread;
1074 mono_gc_get_mach_exception_thread (void)
1076 return mach_exception_thread;
1080 static MonoReferenceQueue *ref_queues;
1083 ref_list_remove_element (RefQueueEntry **prev, RefQueueEntry *element)
1086 /* Guard if head is changed concurrently. */
1087 while (*prev != element)
1088 prev = &(*prev)->next;
1089 } while (prev && InterlockedCompareExchangePointer ((volatile gpointer *)prev, element->next, element) != element);
1093 ref_list_push (RefQueueEntry **head, RefQueueEntry *value)
1095 RefQueueEntry *current;
1098 value->next = current;
1099 STORE_STORE_FENCE; /*Must make sure the previous store is visible before the CAS. */
1100 } while (InterlockedCompareExchangePointer ((volatile gpointer *)head, value, current) != current);
1104 reference_queue_proccess (MonoReferenceQueue *queue)
1106 RefQueueEntry **iter = &queue->queue;
1107 RefQueueEntry *entry;
1108 while ((entry = *iter)) {
1109 if (queue->should_be_deleted || !mono_gchandle_get_target (entry->gchandle)) {
1110 mono_gchandle_free ((guint32)entry->gchandle);
1111 ref_list_remove_element (iter, entry);
1112 queue->callback (entry->user_data);
1115 iter = &entry->next;
1121 reference_queue_proccess_all (void)
1123 MonoReferenceQueue **iter;
1124 MonoReferenceQueue *queue = ref_queues;
1125 for (; queue; queue = queue->next)
1126 reference_queue_proccess (queue);
1129 mono_coop_mutex_lock (&reference_queue_mutex);
1130 for (iter = &ref_queues; *iter;) {
1132 if (!queue->should_be_deleted) {
1133 iter = &queue->next;
1137 mono_coop_mutex_unlock (&reference_queue_mutex);
1138 reference_queue_proccess (queue);
1141 *iter = queue->next;
1144 mono_coop_mutex_unlock (&reference_queue_mutex);
1148 mono_reference_queue_cleanup (void)
1150 MonoReferenceQueue *queue = ref_queues;
1151 for (; queue; queue = queue->next)
1152 queue->should_be_deleted = TRUE;
1153 reference_queue_proccess_all ();
1157 reference_queue_clear_for_domain (MonoDomain *domain)
1159 MonoReferenceQueue *queue = ref_queues;
1160 for (; queue; queue = queue->next) {
1161 RefQueueEntry **iter = &queue->queue;
1162 RefQueueEntry *entry;
1163 while ((entry = *iter)) {
1164 if (entry->domain == domain) {
1165 mono_gchandle_free ((guint32)entry->gchandle);
1166 ref_list_remove_element (iter, entry);
1167 queue->callback (entry->user_data);
1170 iter = &entry->next;
1176 * mono_gc_reference_queue_new:
1177 * \param callback callback used when processing collected entries.
1179 * Create a new reference queue used to process collected objects.
1180 * A reference queue let you add a pair of (managed object, user data)
1181 * using the mono_gc_reference_queue_add method.
1183 * Once the managed object is collected \p callback will be called
1184 * in the finalizer thread with 'user data' as argument.
1186 * The callback is called from the finalizer thread without any locks held.
1187 * When a AppDomain is unloaded, all callbacks for objects belonging to it
1190 * \returns the new queue.
1193 mono_gc_reference_queue_new (mono_reference_queue_callback callback)
1195 MonoReferenceQueue *res = g_new0 (MonoReferenceQueue, 1);
1196 res->callback = callback;
1198 mono_coop_mutex_lock (&reference_queue_mutex);
1199 res->next = ref_queues;
1201 mono_coop_mutex_unlock (&reference_queue_mutex);
1207 * mono_gc_reference_queue_add:
1208 * \param queue the queue to add the reference to.
1209 * \param obj the object to be watched for collection
1210 * \param user_data parameter to be passed to the queue callback
1212 * Queue an object to be watched for collection, when the \p obj is
1213 * collected, the callback that was registered for the \p queue will
1214 * be invoked with \p user_data as argument.
1216 * \returns FALSE if the queue is scheduled to be freed.
1219 mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *user_data)
1221 RefQueueEntry *entry;
1222 if (queue->should_be_deleted)
1225 g_assert (obj != NULL);
1227 entry = g_new0 (RefQueueEntry, 1);
1228 entry->user_data = user_data;
1229 entry->domain = mono_object_domain (obj);
1231 entry->gchandle = mono_gchandle_new_weakref (obj, TRUE);
1232 mono_object_register_finalizer (obj);
1234 ref_list_push (&queue->queue, entry);
1239 * mono_gc_reference_queue_free:
1240 * \param queue the queue that should be freed.
1242 * This operation signals that \p queue should be freed. This operation is deferred
1243 * as it happens on the finalizer thread.
1245 * After this call, no further objects can be queued. It's the responsibility of the
1246 * caller to make sure that no further attempt to access queue will be made.
1249 mono_gc_reference_queue_free (MonoReferenceQueue *queue)
1251 queue->should_be_deleted = TRUE;