2 * metadata/gc.c: GC icalls.
4 * Author: Paolo Molaro <lupus@ximian.com>
6 * Copyright 2002-2003 Ximian, Inc (http://www.ximian.com)
7 * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
8 * Copyright 2012 Xamarin Inc (http://www.xamarin.com)
9 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
16 #include <mono/metadata/gc-internals.h>
17 #include <mono/metadata/mono-gc.h>
18 #include <mono/metadata/threads.h>
19 #include <mono/metadata/tabledefs.h>
20 #include <mono/metadata/exception.h>
21 #include <mono/metadata/profiler-private.h>
22 #include <mono/metadata/domain-internals.h>
23 #include <mono/metadata/class-internals.h>
24 #include <mono/metadata/metadata-internals.h>
25 #include <mono/metadata/mono-mlist.h>
26 #include <mono/metadata/threads-types.h>
27 #include <mono/metadata/threadpool-ms.h>
28 #include <mono/sgen/sgen-conf.h>
29 #include <mono/sgen/sgen-gc.h>
30 #include <mono/utils/mono-logger-internals.h>
31 #include <mono/metadata/gc-internals.h>
32 #include <mono/metadata/marshal.h> /* for mono_delegate_free_ftnptr () */
33 #include <mono/metadata/attach.h>
34 #include <mono/metadata/console-io.h>
35 #include <mono/utils/mono-os-semaphore.h>
36 #include <mono/utils/mono-memory-model.h>
37 #include <mono/utils/mono-counters.h>
38 #include <mono/utils/mono-time.h>
39 #include <mono/utils/dtrace.h>
40 #include <mono/utils/mono-threads.h>
41 #include <mono/utils/mono-threads-coop.h>
42 #include <mono/utils/atomic.h>
43 #include <mono/utils/mono-coop-semaphore.h>
44 #include <mono/utils/hazard-pointer.h>
50 typedef struct DomainFinalizationReq {
53 } DomainFinalizationReq;
55 static gboolean gc_disabled = FALSE;
57 static gboolean finalizing_root_domain = FALSE;
59 gboolean log_finalizers = FALSE;
60 gboolean mono_do_not_finalize = FALSE;
61 gchar **mono_do_not_finalize_class_names = NULL;
63 #define mono_finalizer_lock() mono_coop_mutex_lock (&finalizer_mutex)
64 #define mono_finalizer_unlock() mono_coop_mutex_unlock (&finalizer_mutex)
65 static MonoCoopMutex finalizer_mutex;
66 static MonoCoopMutex reference_queue_mutex;
68 static GSList *domains_to_finalize= NULL;
69 static MonoMList *threads_to_finalize = NULL;
71 static gboolean finalizer_thread_exited;
72 /* Uses finalizer_mutex */
73 static MonoCoopCond exited_cond;
75 static MonoInternalThread *gc_thread;
77 static void object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*));
79 static void reference_queue_proccess_all (void);
80 static void mono_reference_queue_cleanup (void);
81 static void reference_queue_clear_for_domain (MonoDomain *domain);
82 static HANDLE pending_done_event;
85 guarded_wait (HANDLE handle, guint32 timeout, gboolean alertable)
90 result = WaitForSingleObjectEx (handle, timeout, alertable);
97 add_thread_to_finalize (MonoInternalThread *thread, MonoError *error)
99 mono_error_init (error);
100 mono_finalizer_lock ();
101 if (!threads_to_finalize)
102 MONO_GC_REGISTER_ROOT_SINGLE (threads_to_finalize, MONO_ROOT_SOURCE_FINALIZER_QUEUE, "finalizable threads list");
103 threads_to_finalize = mono_mlist_append_checked (threads_to_finalize, (MonoObject*)thread, error);
104 mono_finalizer_unlock ();
105 return is_ok (error);
108 static volatile gboolean suspend_finalizers = FALSE;
110 * actually, we might want to queue the finalize requests in a separate thread,
111 * but we need to be careful about the execution domain of the thread...
114 mono_gc_run_finalize (void *obj, void *data)
117 MonoObject *exc = NULL;
122 MonoMethod* finalizer = NULL;
123 MonoDomain *caller_domain = mono_domain_get ();
125 RuntimeInvokeFunction runtime_invoke;
127 // This function is called from the innards of the GC, so our best alternative for now is to do polling here
128 mono_threads_safepoint ();
130 o = (MonoObject*)((char*)obj + GPOINTER_TO_UINT (data));
132 if (mono_do_not_finalize) {
133 if (!mono_do_not_finalize_class_names)
136 size_t namespace_len = strlen (o->vtable->klass->name_space);
137 for (int i = 0; mono_do_not_finalize_class_names [i]; ++i) {
138 const char *name = mono_do_not_finalize_class_names [i];
139 if (strncmp (name, o->vtable->klass->name_space, namespace_len))
141 if (name [namespace_len] != '.')
143 if (strcmp (name + namespace_len + 1, o->vtable->klass->name))
150 g_log ("mono-gc-finalizers", G_LOG_LEVEL_DEBUG, "<%s at %p> Starting finalizer checks.", o->vtable->klass->name, o);
152 if (suspend_finalizers)
155 domain = o->vtable->domain;
158 mono_domain_finalizers_lock (domain);
160 o2 = (MonoObject *)g_hash_table_lookup (domain->finalizable_objects_hash, o);
162 mono_domain_finalizers_unlock (domain);
165 /* Already finalized somehow */
169 /* make sure the finalizer is not called again if the object is resurrected */
170 object_register_finalizer ((MonoObject *)obj, NULL);
173 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Registered finalizer as processed.", o->vtable->klass->name, o);
175 if (o->vtable->klass == mono_defaults.internal_thread_class) {
176 MonoInternalThread *t = (MonoInternalThread*)o;
178 if (mono_gc_is_finalizer_internal_thread (t))
179 /* Avoid finalizing ourselves */
182 if (t->threadpool_thread && finalizing_root_domain) {
183 /* Don't finalize threadpool threads when
184 shutting down - they're finalized when the
185 threadpool shuts down. */
186 if (!add_thread_to_finalize (t, &error))
187 goto unhandled_error;
192 if (o->vtable->klass->image == mono_defaults.corlib && !strcmp (o->vtable->klass->name, "DynamicMethod") && finalizing_root_domain) {
194 * These can't be finalized during unloading/shutdown, since that would
195 * free the native code which can still be referenced by other
197 * FIXME: This is not perfect, objects dying at the same time as
198 * dynamic methods can still reference them even when !shutdown.
203 if (mono_runtime_get_no_exec ())
206 /* speedup later... and use a timeout */
207 /* g_print ("Finalize run on %p %s.%s\n", o, mono_object_class (o)->name_space, mono_object_class (o)->name); */
209 /* Use _internal here, since this thread can enter a doomed appdomain */
210 mono_domain_set_internal (mono_object_domain (o));
212 /* delegates that have a native function pointer allocated are
213 * registered for finalization, but they don't have a Finalize
214 * method, because in most cases it's not needed and it's just a waste.
216 if (o->vtable->klass->delegate) {
217 MonoDelegate* del = (MonoDelegate*)o;
218 if (del->delegate_trampoline)
219 mono_delegate_free_ftnptr ((MonoDelegate*)o);
220 mono_domain_set_internal (caller_domain);
224 finalizer = mono_class_get_finalizer (o->vtable->klass);
226 /* If object has a CCW but has no finalizer, it was only
227 * registered for finalization in order to free the CCW.
228 * Else it needs the regular finalizer run.
229 * FIXME: what to do about ressurection and suppression
230 * of finalizer on object with CCW.
232 if (mono_marshal_free_ccw (o) && !finalizer) {
233 mono_domain_set_internal (caller_domain);
238 * To avoid the locking plus the other overhead of mono_runtime_invoke_checked (),
239 * create and precompile a wrapper which calls the finalize method using
243 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Compiling finalizer.", o->vtable->klass->name, o);
245 if (!domain->finalize_runtime_invoke) {
246 MonoMethod *invoke = mono_marshal_get_runtime_invoke (mono_class_get_method_from_name_flags (mono_defaults.object_class, "Finalize", 0, 0), TRUE);
248 domain->finalize_runtime_invoke = mono_compile_method_checked (invoke, &error);
249 mono_error_assert_ok (&error); /* expect this not to fail */
252 runtime_invoke = (RuntimeInvokeFunction)domain->finalize_runtime_invoke;
254 mono_runtime_class_init_full (o->vtable, &error);
256 goto unhandled_error;
258 if (G_UNLIKELY (MONO_GC_FINALIZE_INVOKE_ENABLED ())) {
259 MONO_GC_FINALIZE_INVOKE ((unsigned long)o, mono_object_get_size (o),
260 o->vtable->klass->name_space, o->vtable->klass->name);
264 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Calling finalizer.", o->vtable->klass->name, o);
266 runtime_invoke (o, NULL, &exc, NULL);
269 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Returned from finalizer.", o->vtable->klass->name, o);
273 exc = (MonoObject*)mono_error_convert_to_exception (&error);
275 mono_thread_internal_unhandled_exception (exc);
277 mono_domain_set_internal (caller_domain);
281 mono_gc_finalize_threadpool_threads (void)
283 while (threads_to_finalize) {
284 MonoInternalThread *thread = (MonoInternalThread*) mono_mlist_get_data (threads_to_finalize);
286 /* Force finalization of the thread. */
287 thread->threadpool_thread = FALSE;
288 mono_object_register_finalizer ((MonoObject*)thread);
290 mono_gc_run_finalize (thread, NULL);
292 threads_to_finalize = mono_mlist_next (threads_to_finalize);
297 mono_gc_out_of_memory (size_t size)
300 * we could allocate at program startup some memory that we could release
301 * back to the system at this point if we're really low on memory (ie, size is
302 * lower than the memory we set apart)
304 mono_raise_exception (mono_domain_get ()->out_of_memory_ex);
310 * Some of our objects may point to a different address than the address returned by GC_malloc()
311 * (because of the GetHashCode hack), but we need to pass the real address to register_finalizer.
312 * This also means that in the callback we need to adjust the pointer to get back the real
314 * We also need to be consistent in the use of the GC_debug* variants of malloc and register_finalizer,
315 * since that, too, can cause the underlying pointer to be offset.
318 object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*))
322 g_assert (obj != NULL);
324 domain = obj->vtable->domain;
327 if (mono_domain_is_unloading (domain) && (callback != NULL))
329 * Can't register finalizers in a dying appdomain, since they
330 * could be invoked after the appdomain has been unloaded.
334 mono_domain_finalizers_lock (domain);
337 g_hash_table_insert (domain->finalizable_objects_hash, obj, obj);
339 g_hash_table_remove (domain->finalizable_objects_hash, obj);
341 mono_domain_finalizers_unlock (domain);
343 mono_gc_register_for_finalization (obj, callback);
344 #elif defined(HAVE_SGEN_GC)
346 * If we register finalizers for domains that are unloading we might
347 * end up running them while or after the domain is being cleared, so
348 * the objects will not be valid anymore.
350 if (!mono_domain_is_unloading (domain))
351 mono_gc_register_for_finalization (obj, callback);
356 * mono_object_register_finalizer:
357 * @obj: object to register
359 * Records that object @obj has a finalizer, this will call the
360 * Finalize method when the garbage collector disposes the object.
364 mono_object_register_finalizer (MonoObject *obj)
366 /* g_print ("Registered finalizer on %p %s.%s\n", obj, mono_object_class (obj)->name_space, mono_object_class (obj)->name); */
367 object_register_finalizer (obj, mono_gc_run_finalize);
371 * mono_domain_finalize:
372 * @domain: the domain to finalize
373 * @timeout: msects to wait for the finalization to complete, -1 to wait indefinitely
375 * Request finalization of all finalizable objects inside @domain. Wait
376 * @timeout msecs for the finalization to complete.
378 * Returns: TRUE if succeeded, FALSE if there was a timeout
382 mono_domain_finalize (MonoDomain *domain, guint32 timeout)
384 DomainFinalizationReq *req;
387 MonoInternalThread *thread = mono_thread_internal_current ();
389 #if defined(__native_client__)
393 if (mono_thread_internal_current () == gc_thread)
394 /* We are called from inside a finalizer, not much we can do here */
398 * No need to create another thread 'cause the finalizer thread
399 * is still working and will take care of running the finalizers
405 /* We don't support domain finalization without a GC */
406 if (mono_gc_is_null ())
409 mono_gc_collect (mono_gc_max_generation ());
411 done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
412 if (done_event == NULL) {
416 req = g_new0 (DomainFinalizationReq, 1);
417 req->domain = domain;
418 req->done_event = done_event;
420 if (domain == mono_get_root_domain ())
421 finalizing_root_domain = TRUE;
423 mono_finalizer_lock ();
425 domains_to_finalize = g_slist_append (domains_to_finalize, req);
427 mono_finalizer_unlock ();
429 /* Tell the finalizer thread to finalize this appdomain */
430 mono_gc_finalize_notify ();
436 res = guarded_wait (done_event, timeout, TRUE);
437 /* printf ("WAIT RES: %d.\n", res); */
439 if (res == WAIT_IO_COMPLETION) {
440 if ((thread->state & (ThreadState_StopRequested | ThreadState_SuspendRequested)) != 0)
442 } else if (res == WAIT_TIMEOUT) {
443 /* We leak the handle here */
450 CloseHandle (done_event);
452 if (domain == mono_get_root_domain ()) {
453 mono_threadpool_ms_cleanup ();
454 mono_gc_finalize_threadpool_threads ();
457 mono_profiler_appdomain_event (domain, MONO_PROFILE_END_UNLOAD);
463 ves_icall_System_GC_InternalCollect (int generation)
465 mono_gc_collect (generation);
469 ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection)
472 mono_gc_collect (mono_gc_max_generation ());
473 return mono_gc_get_used_size ();
477 ves_icall_System_GC_KeepAlive (MonoObject *obj)
485 ves_icall_System_GC_ReRegisterForFinalize (MonoObject *obj)
487 MONO_CHECK_ARG_NULL (obj,);
489 object_register_finalizer (obj, mono_gc_run_finalize);
493 ves_icall_System_GC_SuppressFinalize (MonoObject *obj)
495 MONO_CHECK_ARG_NULL (obj,);
497 /* delegates have no finalizers, but we register them to deal with the
498 * unmanaged->managed trampoline. We don't let the user suppress it
499 * otherwise we'd leak it.
501 if (obj->vtable->klass->delegate)
504 /* FIXME: Need to handle case where obj has COM Callable Wrapper
505 * generated for it that needs cleaned up, but user wants to suppress
506 * their derived object finalizer. */
508 object_register_finalizer (obj, NULL);
512 ves_icall_System_GC_WaitForPendingFinalizers (void)
514 if (mono_gc_is_null ())
517 if (!mono_gc_pending_finalizers ())
520 if (mono_thread_internal_current () == gc_thread)
521 /* Avoid deadlocks */
525 If the finalizer thread is not live, lets pretend no finalizers are pending since the current thread might
526 be the one responsible for starting it up.
528 if (gc_thread == NULL)
531 ResetEvent (pending_done_event);
532 mono_gc_finalize_notify ();
533 /* g_print ("Waiting for pending finalizers....\n"); */
534 guarded_wait (pending_done_event, INFINITE, TRUE);
535 /* g_print ("Done pending....\n"); */
539 ves_icall_System_GC_register_ephemeron_array (MonoObject *array)
542 if (!mono_gc_ephemeron_array_add (array)) {
543 mono_set_pending_exception (mono_object_domain (array)->out_of_memory_ex);
550 ves_icall_System_GC_get_ephemeron_tombstone (void)
552 return mono_domain_get ()->ephemeron_tombstone;
556 ves_icall_System_GCHandle_GetTarget (guint32 handle)
558 return mono_gchandle_get_target (handle);
562 * if type == -1, change the target of the handle, otherwise allocate a new handle.
565 ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type)
568 mono_gchandle_set_target (handle, obj);
569 /* the handle doesn't change */
574 return mono_gchandle_new_weakref (obj, FALSE);
575 case HANDLE_WEAK_TRACK:
576 return mono_gchandle_new_weakref (obj, TRUE);
578 return mono_gchandle_new (obj, FALSE);
580 return mono_gchandle_new (obj, TRUE);
582 g_assert_not_reached ();
588 ves_icall_System_GCHandle_FreeHandle (guint32 handle)
590 mono_gchandle_free (handle);
594 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
598 if (MONO_GC_HANDLE_TYPE (handle) != HANDLE_PINNED)
600 obj = mono_gchandle_get_target (handle);
602 MonoClass *klass = mono_object_class (obj);
603 if (klass == mono_defaults.string_class) {
604 return mono_string_chars ((MonoString*)obj);
605 } else if (klass->rank) {
606 return mono_array_addr ((MonoArray*)obj, char, 0);
608 /* the C# code will check and throw the exception */
609 /* FIXME: missing !klass->blittable test, see bug #61134 */
610 if ((klass->flags & TYPE_ATTRIBUTE_LAYOUT_MASK) == TYPE_ATTRIBUTE_AUTO_LAYOUT)
612 return (char*)obj + sizeof (MonoObject);
619 mono_gc_GCHandle_CheckCurrentDomain (guint32 gchandle)
621 return mono_gchandle_is_in_domain (gchandle, mono_domain_get ());
624 static MonoCoopSem finalizer_sem;
625 static volatile gboolean finished=FALSE;
628 mono_gc_finalize_notify (void)
631 g_message ( "%s: prodding finalizer", __func__);
634 if (mono_gc_is_null ())
637 mono_coop_sem_post (&finalizer_sem);
641 This is the number of entries allowed in the hazard free queue before
642 we explicitly cycle the finalizer thread to trigger pumping the queue.
644 It was picked empirically by running the corlib test suite in a stress
645 scenario where all hazard entries are queued.
647 In this extreme scenario we double the number of times we cycle the finalizer
648 thread compared to just GC calls.
650 Entries are usually in the order of 100's of bytes each, so we're limiting
651 floating garbage to be in the order of a dozen kb.
653 static gboolean finalizer_thread_pulsed;
654 #define HAZARD_QUEUE_OVERFLOW_SIZE 20
657 hazard_free_queue_is_too_big (size_t size)
659 if (size < HAZARD_QUEUE_OVERFLOW_SIZE)
662 if (finalizer_thread_pulsed || InterlockedCompareExchange (&finalizer_thread_pulsed, TRUE, FALSE))
665 mono_gc_finalize_notify ();
669 hazard_free_queue_pump (void)
671 mono_thread_hazardous_try_free_all ();
672 finalizer_thread_pulsed = FALSE;
678 collect_objects (gpointer key, gpointer value, gpointer user_data)
680 GPtrArray *arr = (GPtrArray*)user_data;
681 g_ptr_array_add (arr, key);
687 * finalize_domain_objects:
689 * Run the finalizers of all finalizable objects in req->domain.
692 finalize_domain_objects (DomainFinalizationReq *req)
694 MonoDomain *domain = req->domain;
697 #define NUM_FOBJECTS 64
698 MonoObject *to_finalize [NUM_FOBJECTS];
702 /* Process finalizers which are already in the queue */
703 mono_gc_invoke_finalizers ();
706 while (g_hash_table_size (domain->finalizable_objects_hash) > 0) {
710 * Since the domain is unloading, nobody is allowed to put
711 * new entries into the hash table. But finalize_object might
712 * remove entries from the hash table, so we make a copy.
714 objs = g_ptr_array_new ();
715 g_hash_table_foreach (domain->finalizable_objects_hash, collect_objects, objs);
716 /* printf ("FINALIZING %d OBJECTS.\n", objs->len); */
718 for (i = 0; i < objs->len; ++i) {
719 MonoObject *o = (MonoObject*)g_ptr_array_index (objs, i);
720 /* FIXME: Avoid finalizing threads, etc */
721 mono_gc_run_finalize (o, 0);
724 g_ptr_array_free (objs, TRUE);
726 #elif defined(HAVE_SGEN_GC)
727 while (!suspend_finalizers && (count = mono_gc_finalizers_for_domain (domain, to_finalize, NUM_FOBJECTS))) {
729 for (i = 0; i < count; ++i) {
730 mono_gc_run_finalize (to_finalize [i], 0);
735 /* cleanup the reference queue */
736 reference_queue_clear_for_domain (domain);
738 /* printf ("DONE.\n"); */
739 SetEvent (req->done_event);
741 /* The event is closed in mono_domain_finalize if we get here */
746 finalizer_thread (gpointer unused)
749 mono_thread_set_name_internal (mono_thread_internal_current (), mono_string_new (mono_get_root_domain (), "Finalizer"), FALSE, &error);
750 mono_error_assert_ok (&error);
752 gboolean wait = TRUE;
754 /* Register a hazard free queue pump callback */
755 mono_hazard_pointer_install_free_queue_size_callback (hazard_free_queue_is_too_big);
758 /* Wait to be notified that there's at least one
762 g_assert (mono_domain_get () == mono_get_root_domain ());
763 mono_gc_set_skip_thread (TRUE);
766 /* An alertable wait is required so this thread can be suspended on windows */
767 mono_coop_sem_wait (&finalizer_sem, MONO_SEM_FLAGS_ALERTABLE);
771 mono_gc_set_skip_thread (FALSE);
773 mono_threads_perform_thread_dump ();
775 mono_console_handle_async_ops ();
777 mono_attach_maybe_start ();
779 if (domains_to_finalize) {
780 mono_finalizer_lock ();
781 if (domains_to_finalize) {
782 DomainFinalizationReq *req = (DomainFinalizationReq *)domains_to_finalize->data;
783 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
784 mono_finalizer_unlock ();
786 finalize_domain_objects (req);
788 mono_finalizer_unlock ();
792 /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup),
793 * before the domain is unloaded.
795 mono_gc_invoke_finalizers ();
797 mono_threads_join_threads ();
799 reference_queue_proccess_all ();
801 hazard_free_queue_pump ();
803 /* Avoid posting the pending done event until there are pending finalizers */
804 if (mono_coop_sem_timedwait (&finalizer_sem, 0, MONO_SEM_FLAGS_NONE) == 0) {
805 /* Don't wait again at the start of the loop */
808 SetEvent (pending_done_event);
812 mono_finalizer_lock ();
813 finalizer_thread_exited = TRUE;
814 mono_coop_cond_signal (&exited_cond);
815 mono_finalizer_unlock ();
820 #ifndef LAZY_GC_THREAD_CREATION
824 mono_gc_init_finalizer_thread (void)
827 gc_thread = mono_thread_create_internal (mono_domain_get (), finalizer_thread, NULL, FALSE, 0, &error);
828 mono_error_assert_ok (&error);
834 mono_coop_mutex_init_recursive (&finalizer_mutex);
835 mono_coop_mutex_init_recursive (&reference_queue_mutex);
837 mono_counters_register ("Minor GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.minor_gc_count);
838 mono_counters_register ("Major GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.major_gc_count);
839 mono_counters_register ("Minor GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.minor_gc_time);
840 mono_counters_register ("Major GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time);
841 mono_counters_register ("Major GC time concurrent", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time_concurrent);
843 mono_gc_base_init ();
845 if (mono_gc_is_disabled ()) {
850 pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
851 g_assert (pending_done_event);
852 mono_coop_cond_init (&exited_cond);
853 mono_coop_sem_init (&finalizer_sem, 0);
855 #ifndef LAZY_GC_THREAD_CREATION
856 mono_gc_init_finalizer_thread ();
861 mono_gc_cleanup (void)
864 g_message ("%s: cleaning up finalizer", __func__);
867 if (mono_gc_is_null ())
872 if (mono_thread_internal_current () != gc_thread) {
873 gint64 start_ticks = mono_msec_ticks ();
874 gint64 end_ticks = start_ticks + 2000;
876 mono_gc_finalize_notify ();
877 /* Finishing the finalizer thread, so wait a little bit... */
878 /* MS seems to wait for about 2 seconds */
879 while (!finalizer_thread_exited) {
880 gint64 current_ticks = mono_msec_ticks ();
883 if (current_ticks >= end_ticks)
886 timeout = end_ticks - current_ticks;
887 mono_finalizer_lock ();
888 if (!finalizer_thread_exited)
889 mono_coop_cond_timedwait (&exited_cond, &finalizer_mutex, timeout);
890 mono_finalizer_unlock ();
893 if (!finalizer_thread_exited) {
896 /* Set a flag which the finalizer thread can check */
897 suspend_finalizers = TRUE;
899 /* Try to abort the thread, in the hope that it is running managed code */
900 mono_thread_internal_abort (gc_thread);
902 /* Wait for it to stop */
903 ret = guarded_wait (gc_thread->handle, 100, TRUE);
905 if (ret == WAIT_TIMEOUT) {
907 * The finalizer thread refused to exit. Make it stop.
909 mono_thread_internal_stop (gc_thread);
910 ret = guarded_wait (gc_thread->handle, 100, TRUE);
911 g_assert (ret != WAIT_TIMEOUT);
912 /* The thread can't set this flag */
913 finalizer_thread_exited = TRUE;
919 /* Wait for the thread to actually exit */
920 ret = guarded_wait (gc_thread->handle, INFINITE, TRUE);
921 g_assert (ret == WAIT_OBJECT_0);
923 mono_thread_join (GUINT_TO_POINTER (gc_thread->tid));
924 g_assert (finalizer_thread_exited);
927 mono_gc_base_cleanup ();
930 mono_reference_queue_cleanup ();
932 mono_coop_mutex_destroy (&finalizer_mutex);
933 mono_coop_mutex_destroy (&reference_queue_mutex);
937 mono_gc_is_finalizer_internal_thread (MonoInternalThread *thread)
939 return thread == gc_thread;
943 * mono_gc_is_finalizer_thread:
944 * @thread: the thread to test.
946 * In Mono objects are finalized asynchronously on a separate thread.
947 * This routine tests whether the @thread argument represents the
948 * finalization thread.
950 * Returns: TRUE if @thread is the finalization thread.
953 mono_gc_is_finalizer_thread (MonoThread *thread)
955 return mono_gc_is_finalizer_internal_thread (thread->internal_thread);
958 #if defined(__MACH__)
959 static pthread_t mach_exception_thread;
962 mono_gc_register_mach_exception_thread (pthread_t thread)
964 mach_exception_thread = thread;
968 mono_gc_get_mach_exception_thread (void)
970 return mach_exception_thread;
974 static MonoReferenceQueue *ref_queues;
977 ref_list_remove_element (RefQueueEntry **prev, RefQueueEntry *element)
980 /* Guard if head is changed concurrently. */
981 while (*prev != element)
982 prev = &(*prev)->next;
983 } while (prev && InterlockedCompareExchangePointer ((volatile gpointer *)prev, element->next, element) != element);
987 ref_list_push (RefQueueEntry **head, RefQueueEntry *value)
989 RefQueueEntry *current;
992 value->next = current;
993 STORE_STORE_FENCE; /*Must make sure the previous store is visible before the CAS. */
994 } while (InterlockedCompareExchangePointer ((volatile gpointer *)head, value, current) != current);
998 reference_queue_proccess (MonoReferenceQueue *queue)
1000 RefQueueEntry **iter = &queue->queue;
1001 RefQueueEntry *entry;
1002 while ((entry = *iter)) {
1003 if (queue->should_be_deleted || !mono_gchandle_get_target (entry->gchandle)) {
1004 mono_gchandle_free ((guint32)entry->gchandle);
1005 ref_list_remove_element (iter, entry);
1006 queue->callback (entry->user_data);
1009 iter = &entry->next;
1015 reference_queue_proccess_all (void)
1017 MonoReferenceQueue **iter;
1018 MonoReferenceQueue *queue = ref_queues;
1019 for (; queue; queue = queue->next)
1020 reference_queue_proccess (queue);
1023 mono_coop_mutex_lock (&reference_queue_mutex);
1024 for (iter = &ref_queues; *iter;) {
1026 if (!queue->should_be_deleted) {
1027 iter = &queue->next;
1031 mono_coop_mutex_unlock (&reference_queue_mutex);
1032 reference_queue_proccess (queue);
1035 *iter = queue->next;
1038 mono_coop_mutex_unlock (&reference_queue_mutex);
1042 mono_reference_queue_cleanup (void)
1044 MonoReferenceQueue *queue = ref_queues;
1045 for (; queue; queue = queue->next)
1046 queue->should_be_deleted = TRUE;
1047 reference_queue_proccess_all ();
1051 reference_queue_clear_for_domain (MonoDomain *domain)
1053 MonoReferenceQueue *queue = ref_queues;
1054 for (; queue; queue = queue->next) {
1055 RefQueueEntry **iter = &queue->queue;
1056 RefQueueEntry *entry;
1057 while ((entry = *iter)) {
1058 if (entry->domain == domain) {
1059 mono_gchandle_free ((guint32)entry->gchandle);
1060 ref_list_remove_element (iter, entry);
1061 queue->callback (entry->user_data);
1064 iter = &entry->next;
1070 * mono_gc_reference_queue_new:
1071 * @callback callback used when processing collected entries.
1073 * Create a new reference queue used to process collected objects.
1074 * A reference queue let you add a pair of (managed object, user data)
1075 * using the mono_gc_reference_queue_add method.
1077 * Once the managed object is collected @callback will be called
1078 * in the finalizer thread with 'user data' as argument.
1080 * The callback is called from the finalizer thread without any locks held.
1081 * When a AppDomain is unloaded, all callbacks for objects belonging to it
1084 * @returns the new queue.
1087 mono_gc_reference_queue_new (mono_reference_queue_callback callback)
1089 MonoReferenceQueue *res = g_new0 (MonoReferenceQueue, 1);
1090 res->callback = callback;
1092 mono_coop_mutex_lock (&reference_queue_mutex);
1093 res->next = ref_queues;
1095 mono_coop_mutex_unlock (&reference_queue_mutex);
1101 * mono_gc_reference_queue_add:
1102 * @queue the queue to add the reference to.
1103 * @obj the object to be watched for collection
1104 * @user_data parameter to be passed to the queue callback
1106 * Queue an object to be watched for collection, when the @obj is
1107 * collected, the callback that was registered for the @queue will
1108 * be invoked with @user_data as argument.
1110 * @returns false if the queue is scheduled to be freed.
1113 mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *user_data)
1115 RefQueueEntry *entry;
1116 if (queue->should_be_deleted)
1119 g_assert (obj != NULL);
1121 entry = g_new0 (RefQueueEntry, 1);
1122 entry->user_data = user_data;
1123 entry->domain = mono_object_domain (obj);
1125 entry->gchandle = mono_gchandle_new_weakref (obj, TRUE);
1126 mono_object_register_finalizer (obj);
1128 ref_list_push (&queue->queue, entry);
1133 * mono_gc_reference_queue_free:
1134 * @queue the queue that should be freed.
1136 * This operation signals that @queue should be freed. This operation is deferred
1137 * as it happens on the finalizer thread.
1139 * After this call, no further objects can be queued. It's the responsibility of the
1140 * caller to make sure that no further attempt to access queue will be made.
1143 mono_gc_reference_queue_free (MonoReferenceQueue *queue)
1145 queue->should_be_deleted = TRUE;