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>
48 #include <mono/utils/mono-os-wait.h>
54 typedef struct DomainFinalizationReq {
58 } DomainFinalizationReq;
60 static gboolean gc_disabled;
62 static gboolean finalizing_root_domain;
64 gboolean log_finalizers;
65 gboolean mono_do_not_finalize;
66 volatile gboolean suspend_finalizers;
67 gchar **mono_do_not_finalize_class_names ;
69 #define mono_finalizer_lock() mono_coop_mutex_lock (&finalizer_mutex)
70 #define mono_finalizer_unlock() mono_coop_mutex_unlock (&finalizer_mutex)
71 static MonoCoopMutex finalizer_mutex;
72 static MonoCoopMutex reference_queue_mutex;
74 static GSList *domains_to_finalize;
76 static gboolean finalizer_thread_exited;
77 /* Uses finalizer_mutex */
78 static MonoCoopCond exited_cond;
80 static MonoInternalThread *gc_thread;
83 static HANDLE pending_done_event;
85 static gboolean pending_done;
86 static MonoCoopCond pending_done_cond;
87 static MonoCoopMutex pending_done_mutex;
90 static void object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*));
92 static void reference_queue_proccess_all (void);
93 static void mono_reference_queue_cleanup (void);
94 static void reference_queue_clear_for_domain (MonoDomain *domain);
97 static MonoThreadInfoWaitRet
98 guarded_wait (MonoThreadHandle *thread_handle, guint32 timeout, gboolean alertable)
100 MonoThreadInfoWaitRet result;
103 result = mono_thread_info_wait_one_handle (thread_handle, timeout, alertable);
111 MonoCoopMutex *mutex;
112 } BreakCoopAlertableWaitUD;
115 break_coop_alertable_wait (gpointer user_data)
117 BreakCoopAlertableWaitUD *ud = (BreakCoopAlertableWaitUD*)user_data;
119 mono_coop_mutex_lock (ud->mutex);
120 mono_coop_cond_signal (ud->cond);
121 mono_coop_mutex_unlock (ud->mutex);
127 * coop_cond_timedwait_alertable:
129 * Wait on COND/MUTEX. If ALERTABLE is non-null, the wait can be interrupted.
130 * In that case, *ALERTABLE will be set to TRUE, and 0 is returned.
133 coop_cond_timedwait_alertable (MonoCoopCond *cond, MonoCoopMutex *mutex, guint32 timeout_ms, gboolean *alertable)
135 BreakCoopAlertableWaitUD *ud;
139 ud = g_new0 (BreakCoopAlertableWaitUD, 1);
143 mono_thread_info_install_interrupt (break_coop_alertable_wait, ud, alertable);
149 res = mono_coop_cond_timedwait (cond, mutex, timeout_ms);
151 mono_thread_info_uninstall_interrupt (alertable);
155 /* the interrupt token has not been taken by another
156 * thread, so it's our responsability to free it up. */
164 * actually, we might want to queue the finalize requests in a separate thread,
165 * but we need to be careful about the execution domain of the thread...
168 mono_gc_run_finalize (void *obj, void *data)
171 MonoObject *exc = NULL;
176 MonoMethod* finalizer = NULL;
177 MonoDomain *caller_domain = mono_domain_get ();
179 RuntimeInvokeFunction runtime_invoke;
181 // This function is called from the innards of the GC, so our best alternative for now is to do polling here
182 mono_threads_safepoint ();
184 o = (MonoObject*)((char*)obj + GPOINTER_TO_UINT (data));
186 if (mono_do_not_finalize) {
187 if (!mono_do_not_finalize_class_names)
190 size_t namespace_len = strlen (o->vtable->klass->name_space);
191 for (int i = 0; mono_do_not_finalize_class_names [i]; ++i) {
192 const char *name = mono_do_not_finalize_class_names [i];
193 if (strncmp (name, o->vtable->klass->name_space, namespace_len))
195 if (name [namespace_len] != '.')
197 if (strcmp (name + namespace_len + 1, o->vtable->klass->name))
204 g_log ("mono-gc-finalizers", G_LOG_LEVEL_DEBUG, "<%s at %p> Starting finalizer checks.", o->vtable->klass->name, o);
206 if (suspend_finalizers)
209 domain = o->vtable->domain;
212 mono_domain_finalizers_lock (domain);
214 o2 = (MonoObject *)g_hash_table_lookup (domain->finalizable_objects_hash, o);
216 mono_domain_finalizers_unlock (domain);
219 /* Already finalized somehow */
223 /* make sure the finalizer is not called again if the object is resurrected */
224 object_register_finalizer ((MonoObject *)obj, NULL);
227 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Registered finalizer as processed.", o->vtable->klass->name, o);
229 if (o->vtable->klass == mono_defaults.internal_thread_class) {
230 MonoInternalThread *t = (MonoInternalThread*)o;
232 if (mono_gc_is_finalizer_internal_thread (t))
233 /* Avoid finalizing ourselves */
237 if (o->vtable->klass->image == mono_defaults.corlib && !strcmp (o->vtable->klass->name, "DynamicMethod") && finalizing_root_domain) {
239 * These can't be finalized during unloading/shutdown, since that would
240 * free the native code which can still be referenced by other
242 * FIXME: This is not perfect, objects dying at the same time as
243 * dynamic methods can still reference them even when !shutdown.
248 if (mono_runtime_get_no_exec ())
251 /* speedup later... and use a timeout */
252 /* g_print ("Finalize run on %p %s.%s\n", o, mono_object_class (o)->name_space, mono_object_class (o)->name); */
254 /* Use _internal here, since this thread can enter a doomed appdomain */
255 mono_domain_set_internal (mono_object_domain (o));
257 /* delegates that have a native function pointer allocated are
258 * registered for finalization, but they don't have a Finalize
259 * method, because in most cases it's not needed and it's just a waste.
261 if (o->vtable->klass->delegate) {
262 MonoDelegate* del = (MonoDelegate*)o;
263 if (del->delegate_trampoline)
264 mono_delegate_free_ftnptr ((MonoDelegate*)o);
265 mono_domain_set_internal (caller_domain);
269 finalizer = mono_class_get_finalizer (o->vtable->klass);
271 /* If object has a CCW but has no finalizer, it was only
272 * registered for finalization in order to free the CCW.
273 * Else it needs the regular finalizer run.
274 * FIXME: what to do about ressurection and suppression
275 * of finalizer on object with CCW.
277 if (mono_marshal_free_ccw (o) && !finalizer) {
278 mono_domain_set_internal (caller_domain);
283 * To avoid the locking plus the other overhead of mono_runtime_invoke_checked (),
284 * create and precompile a wrapper which calls the finalize method using
288 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Compiling finalizer.", o->vtable->klass->name, o);
290 if (!domain->finalize_runtime_invoke) {
291 MonoMethod *invoke = mono_marshal_get_runtime_invoke (mono_class_get_method_from_name_flags (mono_defaults.object_class, "Finalize", 0, 0), TRUE);
293 domain->finalize_runtime_invoke = mono_compile_method_checked (invoke, &error);
294 mono_error_assert_ok (&error); /* expect this not to fail */
297 runtime_invoke = (RuntimeInvokeFunction)domain->finalize_runtime_invoke;
299 mono_runtime_class_init_full (o->vtable, &error);
301 goto unhandled_error;
303 if (G_UNLIKELY (MONO_GC_FINALIZE_INVOKE_ENABLED ())) {
304 MONO_GC_FINALIZE_INVOKE ((unsigned long)o, mono_object_get_size (o),
305 o->vtable->klass->name_space, o->vtable->klass->name);
309 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Calling finalizer.", o->vtable->klass->name, o);
311 MONO_PROFILER_RAISE (gc_finalizing_object, (o));
313 runtime_invoke (o, NULL, &exc, NULL);
315 MONO_PROFILER_RAISE (gc_finalized_object, (o));
318 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Returned from finalizer.", o->vtable->klass->name, o);
322 exc = (MonoObject*)mono_error_convert_to_exception (&error);
324 mono_thread_internal_unhandled_exception (exc);
326 mono_domain_set_internal (caller_domain);
330 mono_gc_out_of_memory (size_t size)
333 * we could allocate at program startup some memory that we could release
334 * back to the system at this point if we're really low on memory (ie, size is
335 * lower than the memory we set apart)
337 mono_raise_exception (mono_domain_get ()->out_of_memory_ex);
343 * Some of our objects may point to a different address than the address returned by GC_malloc()
344 * (because of the GetHashCode hack), but we need to pass the real address to register_finalizer.
345 * This also means that in the callback we need to adjust the pointer to get back the real
347 * We also need to be consistent in the use of the GC_debug* variants of malloc and register_finalizer,
348 * since that, too, can cause the underlying pointer to be offset.
351 object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*))
355 g_assert (obj != NULL);
357 domain = obj->vtable->domain;
360 if (mono_domain_is_unloading (domain) && (callback != NULL))
362 * Can't register finalizers in a dying appdomain, since they
363 * could be invoked after the appdomain has been unloaded.
367 mono_domain_finalizers_lock (domain);
370 g_hash_table_insert (domain->finalizable_objects_hash, obj, obj);
372 g_hash_table_remove (domain->finalizable_objects_hash, obj);
374 mono_domain_finalizers_unlock (domain);
376 mono_gc_register_for_finalization (obj, callback);
377 #elif defined(HAVE_SGEN_GC)
379 * If we register finalizers for domains that are unloading we might
380 * end up running them while or after the domain is being cleared, so
381 * the objects will not be valid anymore.
383 if (!mono_domain_is_unloading (domain))
384 mono_gc_register_for_finalization (obj, callback);
389 * mono_object_register_finalizer:
390 * \param obj object to register
392 * Records that object \p obj has a finalizer, this will call the
393 * Finalize method when the garbage collector disposes the object.
397 mono_object_register_finalizer (MonoObject *obj)
399 /* g_print ("Registered finalizer on %p %s.%s\n", obj, mono_object_class (obj)->name_space, mono_object_class (obj)->name); */
400 object_register_finalizer (obj, mono_gc_run_finalize);
404 * mono_domain_finalize:
405 * \param domain the domain to finalize
406 * \param timeout msecs to wait for the finalization to complete, \c -1 to wait indefinitely
408 * Request finalization of all finalizable objects inside \p domain. Wait
409 * \p timeout msecs for the finalization to complete.
411 * \returns TRUE if succeeded, FALSE if there was a timeout
414 mono_domain_finalize (MonoDomain *domain, guint32 timeout)
416 DomainFinalizationReq *req;
417 MonoInternalThread *thread = mono_thread_internal_current ();
422 if (mono_thread_internal_current () == gc_thread)
423 /* We are called from inside a finalizer, not much we can do here */
427 * No need to create another thread 'cause the finalizer thread
428 * is still working and will take care of running the finalizers
434 /* We don't support domain finalization without a GC */
435 if (mono_gc_is_null ())
438 mono_gc_collect (mono_gc_max_generation ());
440 req = g_new0 (DomainFinalizationReq, 1);
442 req->domain = domain;
443 mono_coop_sem_init (&req->done, 0);
445 if (domain == mono_get_root_domain ())
446 finalizing_root_domain = TRUE;
448 mono_finalizer_lock ();
450 domains_to_finalize = g_slist_append (domains_to_finalize, req);
452 mono_finalizer_unlock ();
454 /* Tell the finalizer thread to finalize this appdomain */
455 mono_gc_finalize_notify ();
458 timeout = MONO_INFINITE_WAIT;
459 if (timeout != MONO_INFINITE_WAIT)
460 start = mono_msec_ticks ();
465 if (timeout == MONO_INFINITE_WAIT) {
466 res = mono_coop_sem_wait (&req->done, MONO_SEM_FLAGS_ALERTABLE);
468 gint64 elapsed = mono_msec_ticks () - start;
469 if (elapsed >= timeout) {
474 res = mono_coop_sem_timedwait (&req->done, timeout - elapsed, MONO_SEM_FLAGS_ALERTABLE);
477 if (res == MONO_SEM_TIMEDWAIT_RET_SUCCESS) {
479 } else if (res == MONO_SEM_TIMEDWAIT_RET_ALERTED) {
480 if ((thread->state & (ThreadState_AbortRequested | ThreadState_SuspendRequested)) != 0) {
484 } else if (res == MONO_SEM_TIMEDWAIT_RET_TIMEDOUT) {
488 g_error ("%s: unknown result %d", __func__, res);
493 /* Try removing the req from domains_to_finalize:
494 * - if it's not found: the domain is being finalized,
495 * so we the ref count is already decremented
496 * - if it's found: the domain is not yet being finalized,
497 * so we can safely decrement the ref */
501 mono_finalizer_lock ();
503 found = g_slist_index (domains_to_finalize, req) != -1;
505 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
507 mono_finalizer_unlock ();
510 /* We have to decrement it wherever we
511 * remove it from domains_to_finalize */
512 if (InterlockedDecrement (&req->ref) != 1)
513 g_error ("%s: req->ref should be 1, as we are the first one to decrement it", __func__);
520 if (InterlockedDecrement (&req->ref) == 0) {
521 mono_coop_sem_destroy (&req->done);
529 ves_icall_System_GC_InternalCollect (int generation)
531 mono_gc_collect (generation);
535 ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection)
538 mono_gc_collect (mono_gc_max_generation ());
539 return mono_gc_get_used_size ();
543 ves_icall_System_GC_KeepAlive (MonoObject *obj)
551 ves_icall_System_GC_ReRegisterForFinalize (MonoObject *obj)
553 MONO_CHECK_ARG_NULL (obj,);
555 object_register_finalizer (obj, mono_gc_run_finalize);
559 ves_icall_System_GC_SuppressFinalize (MonoObject *obj)
561 MONO_CHECK_ARG_NULL (obj,);
563 /* delegates have no finalizers, but we register them to deal with the
564 * unmanaged->managed trampoline. We don't let the user suppress it
565 * otherwise we'd leak it.
567 if (obj->vtable->klass->delegate)
570 /* FIXME: Need to handle case where obj has COM Callable Wrapper
571 * generated for it that needs cleaned up, but user wants to suppress
572 * their derived object finalizer. */
574 object_register_finalizer (obj, NULL);
578 ves_icall_System_GC_WaitForPendingFinalizers (void)
580 if (mono_gc_is_null ())
583 if (!mono_gc_pending_finalizers ())
586 if (mono_thread_internal_current () == gc_thread)
587 /* Avoid deadlocks */
591 If the finalizer thread is not live, lets pretend no finalizers are pending since the current thread might
592 be the one responsible for starting it up.
594 if (gc_thread == NULL)
598 ResetEvent (pending_done_event);
599 mono_gc_finalize_notify ();
600 /* g_print ("Waiting for pending finalizers....\n"); */
602 mono_win32_wait_for_single_object_ex (pending_done_event, INFINITE, TRUE);
604 /* g_print ("Done pending....\n"); */
606 gboolean alerted = FALSE;
607 mono_coop_mutex_lock (&pending_done_mutex);
608 pending_done = FALSE;
609 mono_gc_finalize_notify ();
610 while (!pending_done) {
611 coop_cond_timedwait_alertable (&pending_done_cond, &pending_done_mutex, MONO_INFINITE_WAIT, &alerted);
615 mono_coop_mutex_unlock (&pending_done_mutex);
620 ves_icall_System_GC_register_ephemeron_array (MonoObject *array)
623 if (!mono_gc_ephemeron_array_add (array)) {
624 mono_set_pending_exception (mono_object_domain (array)->out_of_memory_ex);
631 ves_icall_System_GC_get_ephemeron_tombstone (void)
633 return mono_domain_get ()->ephemeron_tombstone;
637 ves_icall_System_GCHandle_GetTarget (guint32 handle)
639 return mono_gchandle_get_target (handle);
643 * if type == -1, change the target of the handle, otherwise allocate a new handle.
646 ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type)
649 mono_gchandle_set_target (handle, obj);
650 /* the handle doesn't change */
655 return mono_gchandle_new_weakref (obj, FALSE);
656 case HANDLE_WEAK_TRACK:
657 return mono_gchandle_new_weakref (obj, TRUE);
659 return mono_gchandle_new (obj, FALSE);
661 return mono_gchandle_new (obj, TRUE);
663 g_assert_not_reached ();
669 ves_icall_System_GCHandle_FreeHandle (guint32 handle)
671 mono_gchandle_free (handle);
675 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
679 if (MONO_GC_HANDLE_TYPE (handle) != HANDLE_PINNED)
681 obj = mono_gchandle_get_target (handle);
683 MonoClass *klass = mono_object_class (obj);
684 if (klass == mono_defaults.string_class) {
685 return mono_string_chars ((MonoString*)obj);
686 } else if (klass->rank) {
687 return mono_array_addr ((MonoArray*)obj, char, 0);
689 /* the C# code will check and throw the exception */
690 /* FIXME: missing !klass->blittable test, see bug #61134 */
691 if (mono_class_is_auto_layout (klass))
693 return (char*)obj + sizeof (MonoObject);
700 mono_gc_GCHandle_CheckCurrentDomain (guint32 gchandle)
702 return mono_gchandle_is_in_domain (gchandle, mono_domain_get ());
705 static MonoCoopSem finalizer_sem;
706 static volatile gboolean finished;
709 * mono_gc_finalize_notify:
711 * Notify the finalizer thread that finalizers etc.
712 * are available to be processed.
713 * This is async signal safe.
716 mono_gc_finalize_notify (void)
719 g_message ( "%s: prodding finalizer", __func__);
722 if (mono_gc_is_null ())
725 mono_coop_sem_post (&finalizer_sem);
729 This is the number of entries allowed in the hazard free queue before
730 we explicitly cycle the finalizer thread to trigger pumping the queue.
732 It was picked empirically by running the corlib test suite in a stress
733 scenario where all hazard entries are queued.
735 In this extreme scenario we double the number of times we cycle the finalizer
736 thread compared to just GC calls.
738 Entries are usually in the order of 100's of bytes each, so we're limiting
739 floating garbage to be in the order of a dozen kb.
741 static gboolean finalizer_thread_pulsed;
742 #define HAZARD_QUEUE_OVERFLOW_SIZE 20
745 hazard_free_queue_is_too_big (size_t size)
747 if (size < HAZARD_QUEUE_OVERFLOW_SIZE)
750 if (finalizer_thread_pulsed || InterlockedCompareExchange (&finalizer_thread_pulsed, TRUE, FALSE))
753 mono_gc_finalize_notify ();
757 hazard_free_queue_pump (void)
759 mono_thread_hazardous_try_free_all ();
760 finalizer_thread_pulsed = FALSE;
766 collect_objects (gpointer key, gpointer value, gpointer user_data)
768 GPtrArray *arr = (GPtrArray*)user_data;
769 g_ptr_array_add (arr, key);
775 * finalize_domain_objects:
777 * Run the finalizers of all finalizable objects in req->domain.
780 finalize_domain_objects (void)
782 DomainFinalizationReq *req = NULL;
785 if (UnlockedReadPointer ((gpointer)&domains_to_finalize)) {
786 mono_finalizer_lock ();
787 if (domains_to_finalize) {
788 req = (DomainFinalizationReq *)domains_to_finalize->data;
789 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
791 mono_finalizer_unlock ();
797 domain = req->domain;
799 /* Process finalizers which are already in the queue */
800 mono_gc_invoke_finalizers ();
803 while (g_hash_table_size (domain->finalizable_objects_hash) > 0) {
807 * Since the domain is unloading, nobody is allowed to put
808 * new entries into the hash table. But finalize_object might
809 * remove entries from the hash table, so we make a copy.
811 objs = g_ptr_array_new ();
812 g_hash_table_foreach (domain->finalizable_objects_hash, collect_objects, objs);
813 /* printf ("FINALIZING %d OBJECTS.\n", objs->len); */
815 for (i = 0; i < objs->len; ++i) {
816 MonoObject *o = (MonoObject*)g_ptr_array_index (objs, i);
817 /* FIXME: Avoid finalizing threads, etc */
818 mono_gc_run_finalize (o, 0);
821 g_ptr_array_free (objs, TRUE);
823 #elif defined(HAVE_SGEN_GC)
824 mono_gc_finalize_domain (domain);
825 mono_gc_invoke_finalizers ();
828 /* cleanup the reference queue */
829 reference_queue_clear_for_domain (domain);
831 /* printf ("DONE.\n"); */
832 mono_coop_sem_post (&req->done);
834 if (InterlockedDecrement (&req->ref) == 0) {
835 /* mono_domain_finalize already returned, and
836 * doesn't hold a reference to req anymore. */
837 mono_coop_sem_destroy (&req->done);
843 finalizer_thread (gpointer unused)
846 gboolean wait = TRUE;
848 MonoString *finalizer = mono_string_new_checked (mono_get_root_domain (), "Finalizer", &error);
849 mono_error_assert_ok (&error);
850 mono_thread_set_name_internal (mono_thread_internal_current (), finalizer, FALSE, FALSE, &error);
851 mono_error_assert_ok (&error);
853 /* Register a hazard free queue pump callback */
854 mono_hazard_pointer_install_free_queue_size_callback (hazard_free_queue_is_too_big);
857 /* Wait to be notified that there's at least one
861 g_assert (mono_domain_get () == mono_get_root_domain ());
862 mono_gc_set_skip_thread (TRUE);
865 /* An alertable wait is required so this thread can be suspended on windows */
866 mono_coop_sem_wait (&finalizer_sem, MONO_SEM_FLAGS_ALERTABLE);
870 mono_gc_set_skip_thread (FALSE);
872 mono_threads_perform_thread_dump ();
874 mono_console_handle_async_ops ();
876 mono_attach_maybe_start ();
878 finalize_domain_objects ();
880 MONO_PROFILER_RAISE (gc_finalizing, ());
882 /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup),
883 * before the domain is unloaded.
885 mono_gc_invoke_finalizers ();
887 MONO_PROFILER_RAISE (gc_finalized, ());
889 mono_threads_join_threads ();
891 reference_queue_proccess_all ();
893 mono_w32process_signal_finished ();
895 hazard_free_queue_pump ();
897 /* Avoid posting the pending done event until there are pending finalizers */
898 if (mono_coop_sem_timedwait (&finalizer_sem, 0, MONO_SEM_FLAGS_NONE) == MONO_SEM_TIMEDWAIT_RET_SUCCESS) {
899 /* Don't wait again at the start of the loop */
903 SetEvent (pending_done_event);
905 mono_coop_mutex_lock (&pending_done_mutex);
907 mono_coop_cond_signal (&pending_done_cond);
908 mono_coop_mutex_unlock (&pending_done_mutex);
913 mono_finalizer_lock ();
914 finalizer_thread_exited = TRUE;
915 mono_coop_cond_signal (&exited_cond);
916 mono_finalizer_unlock ();
921 #ifndef LAZY_GC_THREAD_CREATION
925 mono_gc_init_finalizer_thread (void)
928 gc_thread = mono_thread_create_internal (mono_domain_get (), finalizer_thread, NULL, MONO_THREAD_CREATE_FLAGS_NONE, &error);
929 mono_error_assert_ok (&error);
935 mono_coop_mutex_init_recursive (&finalizer_mutex);
936 mono_coop_mutex_init_recursive (&reference_queue_mutex);
938 mono_counters_register ("Minor GC collections", MONO_COUNTER_GC | MONO_COUNTER_INT, &gc_stats.minor_gc_count);
939 mono_counters_register ("Major GC collections", MONO_COUNTER_GC | MONO_COUNTER_INT, &gc_stats.major_gc_count);
940 mono_counters_register ("Minor GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.minor_gc_time);
941 mono_counters_register ("Major GC time", MONO_COUNTER_GC | MONO_COUNTER_LONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time);
942 mono_counters_register ("Major GC time concurrent", MONO_COUNTER_GC | MONO_COUNTER_LONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time_concurrent);
944 mono_gc_base_init ();
946 if (mono_gc_is_disabled ()) {
952 pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
953 g_assert (pending_done_event);
955 mono_coop_cond_init (&pending_done_cond);
956 mono_coop_mutex_init (&pending_done_mutex);
959 mono_coop_cond_init (&exited_cond);
960 mono_coop_sem_init (&finalizer_sem, 0);
962 #ifndef LAZY_GC_THREAD_CREATION
963 mono_gc_init_finalizer_thread ();
968 mono_gc_cleanup (void)
971 g_message ("%s: cleaning up finalizer", __func__);
974 if (mono_gc_is_null ())
979 if (mono_thread_internal_current () != gc_thread) {
982 const gint64 timeout = 40 * 1000;
984 mono_gc_finalize_notify ();
986 start = mono_msec_ticks ();
988 /* Finishing the finalizer thread, so wait a little bit... */
989 /* MS seems to wait for about 2 seconds per finalizer thread */
990 /* and 40 seconds for all finalizers to finish */
994 if (finalizer_thread_exited) {
995 /* Wait for the thread to actually exit. We don't want the wait
996 * to be alertable, because we assert on the result to be SUCCESS_0 */
997 ret = guarded_wait (gc_thread->handle, MONO_INFINITE_WAIT, FALSE);
998 g_assert (ret == MONO_THREAD_INFO_WAIT_RET_SUCCESS_0);
1000 mono_threads_add_joinable_thread ((gpointer)(MONO_UINT_TO_NATIVE_THREAD_ID (gc_thread->tid)));
1004 elapsed = mono_msec_ticks () - start;
1005 if (elapsed >= timeout) {
1008 /* Set a flag which the finalizer thread can check */
1009 suspend_finalizers = TRUE;
1010 mono_gc_suspend_finalizers ();
1012 /* Try to abort the thread, in the hope that it is running managed code */
1013 mono_thread_internal_abort (gc_thread, FALSE);
1015 /* Wait for it to stop */
1016 ret = guarded_wait (gc_thread->handle, 100, FALSE);
1017 if (ret == MONO_THREAD_INFO_WAIT_RET_TIMEOUT) {
1018 /* The finalizer thread refused to exit, suspend it forever. */
1019 mono_thread_internal_suspend_for_shutdown (gc_thread);
1023 g_assert (ret == MONO_THREAD_INFO_WAIT_RET_SUCCESS_0);
1025 mono_threads_add_joinable_thread ((gpointer)(MONO_UINT_TO_NATIVE_THREAD_ID (gc_thread->tid)));
1029 mono_finalizer_lock ();
1030 if (!finalizer_thread_exited)
1031 mono_coop_cond_timedwait (&exited_cond, &finalizer_mutex, timeout - elapsed);
1032 mono_finalizer_unlock ();
1036 mono_gc_base_cleanup ();
1039 mono_reference_queue_cleanup ();
1041 mono_coop_mutex_destroy (&finalizer_mutex);
1042 mono_coop_mutex_destroy (&reference_queue_mutex);
1046 mono_gc_is_finalizer_internal_thread (MonoInternalThread *thread)
1048 return thread == gc_thread;
1052 * mono_gc_is_finalizer_thread:
1053 * \param thread the thread to test.
1055 * In Mono objects are finalized asynchronously on a separate thread.
1056 * This routine tests whether the \p thread argument represents the
1057 * finalization thread.
1059 * \returns TRUE if \p thread is the finalization thread.
1062 mono_gc_is_finalizer_thread (MonoThread *thread)
1064 return mono_gc_is_finalizer_internal_thread (thread->internal_thread);
1067 #if defined(__MACH__)
1068 static pthread_t mach_exception_thread;
1071 mono_gc_register_mach_exception_thread (pthread_t thread)
1073 mach_exception_thread = thread;
1077 mono_gc_get_mach_exception_thread (void)
1079 return mach_exception_thread;
1083 static MonoReferenceQueue *ref_queues;
1086 ref_list_remove_element (RefQueueEntry **prev, RefQueueEntry *element)
1089 /* Guard if head is changed concurrently. */
1090 while (*prev != element)
1091 prev = &(*prev)->next;
1092 } while (prev && InterlockedCompareExchangePointer ((volatile gpointer *)prev, element->next, element) != element);
1096 ref_list_push (RefQueueEntry **head, RefQueueEntry *value)
1098 RefQueueEntry *current;
1101 value->next = current;
1102 STORE_STORE_FENCE; /*Must make sure the previous store is visible before the CAS. */
1103 } while (InterlockedCompareExchangePointer ((volatile gpointer *)head, value, current) != current);
1107 reference_queue_proccess (MonoReferenceQueue *queue)
1109 RefQueueEntry **iter = &queue->queue;
1110 RefQueueEntry *entry;
1111 while ((entry = *iter)) {
1112 if (queue->should_be_deleted || !mono_gchandle_get_target (entry->gchandle)) {
1113 mono_gchandle_free ((guint32)entry->gchandle);
1114 ref_list_remove_element (iter, entry);
1115 queue->callback (entry->user_data);
1118 iter = &entry->next;
1124 reference_queue_proccess_all (void)
1126 MonoReferenceQueue **iter;
1127 MonoReferenceQueue *queue = ref_queues;
1128 for (; queue; queue = queue->next)
1129 reference_queue_proccess (queue);
1132 mono_coop_mutex_lock (&reference_queue_mutex);
1133 for (iter = &ref_queues; *iter;) {
1135 if (!queue->should_be_deleted) {
1136 iter = &queue->next;
1140 mono_coop_mutex_unlock (&reference_queue_mutex);
1141 reference_queue_proccess (queue);
1144 *iter = queue->next;
1147 mono_coop_mutex_unlock (&reference_queue_mutex);
1151 mono_reference_queue_cleanup (void)
1153 MonoReferenceQueue *queue = ref_queues;
1154 for (; queue; queue = queue->next)
1155 queue->should_be_deleted = TRUE;
1156 reference_queue_proccess_all ();
1160 reference_queue_clear_for_domain (MonoDomain *domain)
1162 MonoReferenceQueue *queue = ref_queues;
1163 for (; queue; queue = queue->next) {
1164 RefQueueEntry **iter = &queue->queue;
1165 RefQueueEntry *entry;
1166 while ((entry = *iter)) {
1167 if (entry->domain == domain) {
1168 mono_gchandle_free ((guint32)entry->gchandle);
1169 ref_list_remove_element (iter, entry);
1170 queue->callback (entry->user_data);
1173 iter = &entry->next;
1179 * mono_gc_reference_queue_new:
1180 * \param callback callback used when processing collected entries.
1182 * Create a new reference queue used to process collected objects.
1183 * A reference queue let you add a pair of (managed object, user data)
1184 * using the \c mono_gc_reference_queue_add method.
1186 * Once the managed object is collected \p callback will be called
1187 * in the finalizer thread with 'user data' as argument.
1189 * The callback is called from the finalizer thread without any locks held.
1190 * When an AppDomain is unloaded, all callbacks for objects belonging to it
1193 * \returns the new queue.
1196 mono_gc_reference_queue_new (mono_reference_queue_callback callback)
1198 MonoReferenceQueue *res = g_new0 (MonoReferenceQueue, 1);
1199 res->callback = callback;
1201 mono_coop_mutex_lock (&reference_queue_mutex);
1202 res->next = ref_queues;
1204 mono_coop_mutex_unlock (&reference_queue_mutex);
1210 * mono_gc_reference_queue_add:
1211 * \param queue the queue to add the reference to.
1212 * \param obj the object to be watched for collection
1213 * \param user_data parameter to be passed to the queue callback
1215 * Queue an object to be watched for collection, when the \p obj is
1216 * collected, the callback that was registered for the \p queue will
1217 * be invoked with \p user_data as argument.
1219 * \returns FALSE if the queue is scheduled to be freed.
1222 mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *user_data)
1224 RefQueueEntry *entry;
1225 if (queue->should_be_deleted)
1228 g_assert (obj != NULL);
1230 entry = g_new0 (RefQueueEntry, 1);
1231 entry->user_data = user_data;
1232 entry->domain = mono_object_domain (obj);
1234 entry->gchandle = mono_gchandle_new_weakref (obj, TRUE);
1235 mono_object_register_finalizer (obj);
1237 ref_list_push (&queue->queue, entry);
1242 * mono_gc_reference_queue_free:
1243 * \param queue the queue that should be freed.
1245 * This operation signals that \p queue should be freed. This operation is deferred
1246 * as it happens on the finalizer thread.
1248 * After this call, no further objects can be queued. It's the responsibility of the
1249 * caller to make sure that no further attempt to access queue will be made.
1252 mono_gc_reference_queue_free (MonoReferenceQueue *queue)
1254 queue->should_be_deleted = TRUE;