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/atomic.h>
42 #include <mono/utils/mono-coop-semaphore.h>
43 #include <mono/utils/hazard-pointer.h>
49 typedef struct DomainFinalizationReq {
52 } DomainFinalizationReq;
54 static gboolean gc_disabled = FALSE;
56 static gboolean finalizing_root_domain = FALSE;
58 gboolean log_finalizers = FALSE;
59 gboolean mono_do_not_finalize = FALSE;
60 gchar **mono_do_not_finalize_class_names = NULL;
62 #define mono_finalizer_lock() mono_coop_mutex_lock (&finalizer_mutex)
63 #define mono_finalizer_unlock() mono_coop_mutex_unlock (&finalizer_mutex)
64 static MonoCoopMutex finalizer_mutex;
65 static MonoCoopMutex reference_queue_mutex;
67 static GSList *domains_to_finalize= NULL;
68 static MonoMList *threads_to_finalize = NULL;
70 static gboolean finalizer_thread_exited;
71 /* Uses finalizer_mutex */
72 static MonoCoopCond exited_cond;
74 static MonoInternalThread *gc_thread;
76 static void object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*), MonoError *error);
78 static void reference_queue_proccess_all (void);
79 static void mono_reference_queue_cleanup (void);
80 static void reference_queue_clear_for_domain (MonoDomain *domain);
81 static HANDLE pending_done_event;
84 guarded_wait (HANDLE handle, guint32 timeout, gboolean alertable)
88 MONO_PREPARE_BLOCKING;
89 result = WaitForSingleObjectEx (handle, timeout, alertable);
96 add_thread_to_finalize (MonoInternalThread *thread)
98 mono_finalizer_lock ();
99 if (!threads_to_finalize)
100 MONO_GC_REGISTER_ROOT_SINGLE (threads_to_finalize, MONO_ROOT_SOURCE_FINALIZER_QUEUE, "finalizable threads list");
101 threads_to_finalize = mono_mlist_append (threads_to_finalize, (MonoObject*)thread);
102 mono_finalizer_unlock ();
105 static gboolean suspend_finalizers = FALSE;
107 * actually, we might want to queue the finalize requests in a separate thread,
108 * but we need to be careful about the execution domain of the thread...
111 mono_gc_run_finalize (void *obj, void *data)
114 MonoObject *exc = NULL;
119 MonoMethod* finalizer = NULL;
120 MonoDomain *caller_domain = mono_domain_get ();
122 RuntimeInvokeFunction runtime_invoke;
124 // This function is called from the innards of the GC, so our best alternative for now is to do polling here
125 mono_threads_safepoint ();
127 o = (MonoObject*)((char*)obj + GPOINTER_TO_UINT (data));
129 if (mono_do_not_finalize) {
130 if (!mono_do_not_finalize_class_names)
133 size_t namespace_len = strlen (o->vtable->klass->name_space);
134 for (int i = 0; mono_do_not_finalize_class_names [i]; ++i) {
135 const char *name = mono_do_not_finalize_class_names [i];
136 if (strncmp (name, o->vtable->klass->name_space, namespace_len))
138 if (name [namespace_len] != '.')
140 if (strcmp (name + namespace_len + 1, o->vtable->klass->name))
147 g_log ("mono-gc-finalizers", G_LOG_LEVEL_DEBUG, "<%s at %p> Starting finalizer checks.", o->vtable->klass->name, o);
149 if (suspend_finalizers)
152 domain = o->vtable->domain;
155 mono_domain_finalizers_lock (domain);
157 o2 = (MonoObject *)g_hash_table_lookup (domain->finalizable_objects_hash, o);
159 mono_domain_finalizers_unlock (domain);
162 /* Already finalized somehow */
166 /* make sure the finalizer is not called again if the object is resurrected */
167 object_register_finalizer ((MonoObject *)obj, NULL, &error);
168 mono_error_assert_ok (&error); /* FIXME don't swallow the error */
171 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Registered finalizer as processed.", o->vtable->klass->name, o);
173 if (o->vtable->klass == mono_defaults.internal_thread_class) {
174 MonoInternalThread *t = (MonoInternalThread*)o;
176 if (mono_gc_is_finalizer_internal_thread (t))
177 /* Avoid finalizing ourselves */
180 if (t->threadpool_thread && finalizing_root_domain) {
181 /* Don't finalize threadpool threads when
182 shutting down - they're finalized when the
183 threadpool shuts down. */
184 add_thread_to_finalize (t);
189 if (o->vtable->klass->image == mono_defaults.corlib && !strcmp (o->vtable->klass->name, "DynamicMethod") && finalizing_root_domain) {
191 * These can't be finalized during unloading/shutdown, since that would
192 * free the native code which can still be referenced by other
194 * FIXME: This is not perfect, objects dying at the same time as
195 * dynamic methods can still reference them even when !shutdown.
200 if (mono_runtime_get_no_exec ())
203 /* speedup later... and use a timeout */
204 /* g_print ("Finalize run on %p %s.%s\n", o, mono_object_class (o)->name_space, mono_object_class (o)->name); */
206 /* Use _internal here, since this thread can enter a doomed appdomain */
207 mono_domain_set_internal (mono_object_domain (o));
209 /* delegates that have a native function pointer allocated are
210 * registered for finalization, but they don't have a Finalize
211 * method, because in most cases it's not needed and it's just a waste.
213 if (o->vtable->klass->delegate) {
214 MonoDelegate* del = (MonoDelegate*)o;
215 if (del->delegate_trampoline)
216 mono_delegate_free_ftnptr ((MonoDelegate*)o);
217 mono_domain_set_internal (caller_domain);
221 finalizer = mono_class_get_finalizer (o->vtable->klass);
223 /* If object has a CCW but has no finalizer, it was only
224 * registered for finalization in order to free the CCW.
225 * Else it needs the regular finalizer run.
226 * FIXME: what to do about ressurection and suppression
227 * of finalizer on object with CCW.
229 if (mono_marshal_free_ccw (o) && !finalizer) {
230 mono_domain_set_internal (caller_domain);
235 * To avoid the locking plus the other overhead of mono_runtime_invoke_checked (),
236 * create and precompile a wrapper which calls the finalize method using
240 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Compiling finalizer.", o->vtable->klass->name, o);
242 if (!domain->finalize_runtime_invoke) {
243 MonoMethod *invoke = mono_marshal_get_runtime_invoke (mono_class_get_method_from_name_flags (mono_defaults.object_class, "Finalize", 0, 0), TRUE);
245 domain->finalize_runtime_invoke = mono_compile_method (invoke);
248 runtime_invoke = (RuntimeInvokeFunction)domain->finalize_runtime_invoke;
250 mono_runtime_class_init_full (o->vtable, &error);
251 mono_error_raise_exception (&error); /* FIXME don't raise here */
253 if (G_UNLIKELY (MONO_GC_FINALIZE_INVOKE_ENABLED ())) {
254 MONO_GC_FINALIZE_INVOKE ((unsigned long)o, mono_object_get_size (o),
255 o->vtable->klass->name_space, o->vtable->klass->name);
259 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Calling finalizer.", o->vtable->klass->name, o);
261 runtime_invoke (o, NULL, &exc, NULL);
264 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Returned from finalizer.", o->vtable->klass->name, o);
267 mono_thread_internal_unhandled_exception (exc);
269 mono_domain_set_internal (caller_domain);
273 mono_gc_finalize_threadpool_threads (void)
276 while (threads_to_finalize) {
277 MonoInternalThread *thread = (MonoInternalThread*) mono_mlist_get_data (threads_to_finalize);
279 /* Force finalization of the thread. */
280 thread->threadpool_thread = FALSE;
281 mono_object_register_finalizer ((MonoObject*)thread, &error);
282 mono_error_assert_ok (&error); /* FIXME don't swallow the error */
284 mono_gc_run_finalize (thread, NULL);
286 threads_to_finalize = mono_mlist_next (threads_to_finalize);
291 mono_gc_out_of_memory (size_t size)
294 * we could allocate at program startup some memory that we could release
295 * back to the system at this point if we're really low on memory (ie, size is
296 * lower than the memory we set apart)
298 mono_raise_exception (mono_domain_get ()->out_of_memory_ex);
304 * Some of our objects may point to a different address than the address returned by GC_malloc()
305 * (because of the GetHashCode hack), but we need to pass the real address to register_finalizer.
306 * This also means that in the callback we need to adjust the pointer to get back the real
308 * We also need to be consistent in the use of the GC_debug* variants of malloc and register_finalizer,
309 * since that, too, can cause the underlying pointer to be offset.
312 object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*), MonoError *error)
316 mono_error_init (error);
319 mono_error_set_argument_null (error, "obj", "");
323 domain = obj->vtable->domain;
326 if (mono_domain_is_unloading (domain) && (callback != NULL))
328 * Can't register finalizers in a dying appdomain, since they
329 * could be invoked after the appdomain has been unloaded.
333 mono_domain_finalizers_lock (domain);
336 g_hash_table_insert (domain->finalizable_objects_hash, obj, obj);
338 g_hash_table_remove (domain->finalizable_objects_hash, obj);
340 mono_domain_finalizers_unlock (domain);
342 mono_gc_register_for_finalization (obj, callback);
343 #elif defined(HAVE_SGEN_GC)
345 * If we register finalizers for domains that are unloading we might
346 * end up running them while or after the domain is being cleared, so
347 * the objects will not be valid anymore.
349 if (!mono_domain_is_unloading (domain))
350 mono_gc_register_for_finalization (obj, callback);
355 * mono_object_register_finalizer:
356 * @obj: object to register
358 * Records that object @obj has a finalizer, this will call the
359 * Finalize method when the garbage collector disposes the object.
363 mono_object_register_finalizer (MonoObject *obj, MonoError *error)
365 /* g_print ("Registered finalizer on %p %s.%s\n", obj, mono_object_class (obj)->name_space, mono_object_class (obj)->name); */
366 object_register_finalizer (obj, mono_gc_run_finalize, error);
370 * mono_domain_finalize:
371 * @domain: the domain to finalize
372 * @timeout: msects to wait for the finalization to complete, -1 to wait indefinitely
374 * Request finalization of all finalizable objects inside @domain. Wait
375 * @timeout msecs for the finalization to complete.
377 * Returns: TRUE if succeeded, FALSE if there was a timeout
381 mono_domain_finalize (MonoDomain *domain, guint32 timeout)
383 DomainFinalizationReq *req;
386 MonoInternalThread *thread = mono_thread_internal_current ();
388 #if defined(__native_client__)
392 if (mono_thread_internal_current () == gc_thread)
393 /* We are called from inside a finalizer, not much we can do here */
397 * No need to create another thread 'cause the finalizer thread
398 * is still working and will take care of running the finalizers
404 /* We don't support domain finalization without a GC */
405 if (mono_gc_is_null ())
408 mono_gc_collect (mono_gc_max_generation ());
410 done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
411 if (done_event == NULL) {
415 req = g_new0 (DomainFinalizationReq, 1);
416 req->domain = domain;
417 req->done_event = done_event;
419 if (domain == mono_get_root_domain ())
420 finalizing_root_domain = TRUE;
422 mono_finalizer_lock ();
424 domains_to_finalize = g_slist_append (domains_to_finalize, req);
426 mono_finalizer_unlock ();
428 /* Tell the finalizer thread to finalize this appdomain */
429 mono_gc_finalize_notify ();
435 res = guarded_wait (done_event, timeout, TRUE);
436 /* printf ("WAIT RES: %d.\n", res); */
438 if (res == WAIT_IO_COMPLETION) {
439 if ((thread->state & (ThreadState_StopRequested | ThreadState_SuspendRequested)) != 0)
441 } else if (res == WAIT_TIMEOUT) {
442 /* We leak the handle here */
449 CloseHandle (done_event);
451 if (domain == mono_get_root_domain ()) {
452 mono_threadpool_ms_cleanup ();
453 mono_gc_finalize_threadpool_threads ();
456 mono_profiler_appdomain_event (domain, MONO_PROFILE_END_UNLOAD);
462 ves_icall_System_GC_InternalCollect (int generation)
464 mono_gc_collect (generation);
468 ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection)
471 mono_gc_collect (mono_gc_max_generation ());
472 return mono_gc_get_used_size ();
476 ves_icall_System_GC_KeepAlive (MonoObject *obj)
484 ves_icall_System_GC_ReRegisterForFinalize (MonoObject *obj)
488 MONO_CHECK_ARG_NULL (obj,);
490 object_register_finalizer (obj, mono_gc_run_finalize, &error);
491 mono_error_set_pending_exception (&error);
495 ves_icall_System_GC_SuppressFinalize (MonoObject *obj)
499 MONO_CHECK_ARG_NULL (obj,);
501 /* delegates have no finalizers, but we register them to deal with the
502 * unmanaged->managed trampoline. We don't let the user suppress it
503 * otherwise we'd leak it.
505 if (obj->vtable->klass->delegate)
508 /* FIXME: Need to handle case where obj has COM Callable Wrapper
509 * generated for it that needs cleaned up, but user wants to suppress
510 * their derived object finalizer. */
512 object_register_finalizer (obj, NULL, &error);
513 mono_error_set_pending_exception (&error);
517 ves_icall_System_GC_WaitForPendingFinalizers (void)
519 if (mono_gc_is_null ())
522 if (!mono_gc_pending_finalizers ())
525 if (mono_thread_internal_current () == gc_thread)
526 /* Avoid deadlocks */
530 If the finalizer thread is not live, lets pretend no finalizers are pending since the current thread might
531 be the one responsible for starting it up.
533 if (gc_thread == NULL)
536 ResetEvent (pending_done_event);
537 mono_gc_finalize_notify ();
538 /* g_print ("Waiting for pending finalizers....\n"); */
539 guarded_wait (pending_done_event, INFINITE, TRUE);
540 /* g_print ("Done pending....\n"); */
544 ves_icall_System_GC_register_ephemeron_array (MonoObject *array)
547 if (!mono_gc_ephemeron_array_add (array)) {
548 mono_set_pending_exception (mono_object_domain (array)->out_of_memory_ex);
555 ves_icall_System_GC_get_ephemeron_tombstone (void)
557 return mono_domain_get ()->ephemeron_tombstone;
561 ves_icall_System_GCHandle_GetTarget (guint32 handle)
563 return mono_gchandle_get_target (handle);
567 * if type == -1, change the target of the handle, otherwise allocate a new handle.
570 ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type)
573 mono_gchandle_set_target (handle, obj);
574 /* the handle doesn't change */
579 return mono_gchandle_new_weakref (obj, FALSE);
580 case HANDLE_WEAK_TRACK:
581 return mono_gchandle_new_weakref (obj, TRUE);
583 return mono_gchandle_new (obj, FALSE);
585 return mono_gchandle_new (obj, TRUE);
587 g_assert_not_reached ();
593 ves_icall_System_GCHandle_FreeHandle (guint32 handle)
595 mono_gchandle_free (handle);
599 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
603 if (MONO_GC_HANDLE_TYPE (handle) != HANDLE_PINNED)
605 obj = mono_gchandle_get_target (handle);
607 MonoClass *klass = mono_object_class (obj);
608 if (klass == mono_defaults.string_class) {
609 return mono_string_chars ((MonoString*)obj);
610 } else if (klass->rank) {
611 return mono_array_addr ((MonoArray*)obj, char, 0);
613 /* the C# code will check and throw the exception */
614 /* FIXME: missing !klass->blittable test, see bug #61134 */
615 if ((klass->flags & TYPE_ATTRIBUTE_LAYOUT_MASK) == TYPE_ATTRIBUTE_AUTO_LAYOUT)
617 return (char*)obj + sizeof (MonoObject);
624 mono_gc_GCHandle_CheckCurrentDomain (guint32 gchandle)
626 return mono_gchandle_is_in_domain (gchandle, mono_domain_get ());
629 static MonoCoopSem finalizer_sem;
630 static volatile gboolean finished=FALSE;
633 mono_gc_finalize_notify (void)
636 g_message ( "%s: prodding finalizer", __func__);
639 if (mono_gc_is_null ())
642 mono_coop_sem_post (&finalizer_sem);
646 This is the number of entries allowed in the hazard free queue before
647 we explicitly cycle the finalizer thread to trigger pumping the queue.
649 It was picked empirically by running the corlib test suite in a stress
650 scenario where all hazard entries are queued.
652 In this extreme scenario we double the number of times we cycle the finalizer
653 thread compared to just GC calls.
655 Entries are usually in the order of 100's of bytes each, so we're limiting
656 floating garbage to be in the order of a dozen kb.
658 static gboolean finalizer_thread_pulsed;
659 #define HAZARD_QUEUE_OVERFLOW_SIZE 20
662 hazard_free_queue_is_too_big (size_t size)
664 if (size < HAZARD_QUEUE_OVERFLOW_SIZE)
667 if (finalizer_thread_pulsed || InterlockedCompareExchange (&finalizer_thread_pulsed, TRUE, FALSE))
670 mono_gc_finalize_notify ();
674 hazard_free_queue_pump (void)
676 mono_thread_hazardous_try_free_all ();
677 finalizer_thread_pulsed = FALSE;
683 collect_objects (gpointer key, gpointer value, gpointer user_data)
685 GPtrArray *arr = (GPtrArray*)user_data;
686 g_ptr_array_add (arr, key);
692 * finalize_domain_objects:
694 * Run the finalizers of all finalizable objects in req->domain.
697 finalize_domain_objects (DomainFinalizationReq *req)
699 MonoDomain *domain = req->domain;
702 #define NUM_FOBJECTS 64
703 MonoObject *to_finalize [NUM_FOBJECTS];
707 /* Process finalizers which are already in the queue */
708 mono_gc_invoke_finalizers ();
711 while (g_hash_table_size (domain->finalizable_objects_hash) > 0) {
715 * Since the domain is unloading, nobody is allowed to put
716 * new entries into the hash table. But finalize_object might
717 * remove entries from the hash table, so we make a copy.
719 objs = g_ptr_array_new ();
720 g_hash_table_foreach (domain->finalizable_objects_hash, collect_objects, objs);
721 /* printf ("FINALIZING %d OBJECTS.\n", objs->len); */
723 for (i = 0; i < objs->len; ++i) {
724 MonoObject *o = (MonoObject*)g_ptr_array_index (objs, i);
725 /* FIXME: Avoid finalizing threads, etc */
726 mono_gc_run_finalize (o, 0);
729 g_ptr_array_free (objs, TRUE);
731 #elif defined(HAVE_SGEN_GC)
732 while ((count = mono_gc_finalizers_for_domain (domain, to_finalize, NUM_FOBJECTS))) {
734 for (i = 0; i < count; ++i) {
735 mono_gc_run_finalize (to_finalize [i], 0);
740 /* cleanup the reference queue */
741 reference_queue_clear_for_domain (domain);
743 /* printf ("DONE.\n"); */
744 SetEvent (req->done_event);
746 /* The event is closed in mono_domain_finalize if we get here */
751 finalizer_thread (gpointer unused)
754 mono_thread_set_name_internal (mono_thread_internal_current (), mono_string_new (mono_get_root_domain (), "Finalizer"), FALSE, &error);
755 mono_error_assert_ok (&error);
757 gboolean wait = TRUE;
759 /* Register a hazard free queue pump callback */
760 mono_hazard_pointer_install_free_queue_size_callback (hazard_free_queue_is_too_big);
763 /* Wait to be notified that there's at least one
767 g_assert (mono_domain_get () == mono_get_root_domain ());
768 mono_gc_set_skip_thread (TRUE);
771 /* An alertable wait is required so this thread can be suspended on windows */
772 mono_coop_sem_wait (&finalizer_sem, MONO_SEM_FLAGS_ALERTABLE);
776 mono_gc_set_skip_thread (FALSE);
778 mono_threads_perform_thread_dump ();
780 mono_console_handle_async_ops ();
782 mono_attach_maybe_start ();
784 if (domains_to_finalize) {
785 mono_finalizer_lock ();
786 if (domains_to_finalize) {
787 DomainFinalizationReq *req = (DomainFinalizationReq *)domains_to_finalize->data;
788 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
789 mono_finalizer_unlock ();
791 finalize_domain_objects (req);
793 mono_finalizer_unlock ();
797 /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup),
798 * before the domain is unloaded.
800 mono_gc_invoke_finalizers ();
802 mono_threads_join_threads ();
804 reference_queue_proccess_all ();
806 hazard_free_queue_pump ();
808 /* Avoid posting the pending done event until there are pending finalizers */
809 if (mono_coop_sem_timedwait (&finalizer_sem, 0, MONO_SEM_FLAGS_NONE) == 0) {
810 /* Don't wait again at the start of the loop */
813 SetEvent (pending_done_event);
817 mono_finalizer_lock ();
818 finalizer_thread_exited = TRUE;
819 mono_coop_cond_signal (&exited_cond);
820 mono_finalizer_unlock ();
825 #ifndef LAZY_GC_THREAD_CREATION
829 mono_gc_init_finalizer_thread (void)
832 gc_thread = mono_thread_create_internal (mono_domain_get (), finalizer_thread, NULL, FALSE, 0, &error);
833 mono_error_assert_ok (&error);
839 mono_coop_mutex_init_recursive (&finalizer_mutex);
840 mono_coop_mutex_init_recursive (&reference_queue_mutex);
842 mono_counters_register ("Minor GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.minor_gc_count);
843 mono_counters_register ("Major GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.major_gc_count);
844 mono_counters_register ("Minor GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.minor_gc_time);
845 mono_counters_register ("Major GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time);
846 mono_counters_register ("Major GC time concurrent", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time_concurrent);
848 mono_gc_base_init ();
850 if (mono_gc_is_disabled ()) {
855 pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
856 g_assert (pending_done_event);
857 mono_coop_cond_init (&exited_cond);
858 mono_coop_sem_init (&finalizer_sem, 0);
860 #ifndef LAZY_GC_THREAD_CREATION
861 mono_gc_init_finalizer_thread ();
866 mono_gc_cleanup (void)
869 g_message ("%s: cleaning up finalizer", __func__);
872 if (mono_gc_is_null ())
877 if (mono_thread_internal_current () != gc_thread) {
878 gboolean timed_out = FALSE;
879 gint64 start_ticks = mono_msec_ticks ();
880 gint64 end_ticks = start_ticks + 2000;
882 mono_gc_finalize_notify ();
883 /* Finishing the finalizer thread, so wait a little bit... */
884 /* MS seems to wait for about 2 seconds */
885 while (!finalizer_thread_exited) {
886 gint64 current_ticks = mono_msec_ticks ();
889 if (current_ticks >= end_ticks)
892 timeout = end_ticks - current_ticks;
893 mono_finalizer_lock ();
894 if (!finalizer_thread_exited)
895 mono_coop_cond_timedwait (&exited_cond, &finalizer_mutex, timeout);
896 mono_finalizer_unlock ();
899 if (!finalizer_thread_exited) {
902 /* Set a flag which the finalizer thread can check */
903 suspend_finalizers = TRUE;
905 /* Try to abort the thread, in the hope that it is running managed code */
906 mono_thread_internal_stop (gc_thread);
908 /* Wait for it to stop */
909 ret = guarded_wait (gc_thread->handle, 100, TRUE);
911 if (ret == WAIT_TIMEOUT) {
913 * The finalizer thread refused to die. There is not much we
914 * can do here, since the runtime is shutting down so the
915 * state the finalizer thread depends on will vanish.
917 g_warning ("Shutting down finalizer thread timed out.");
925 /* Wait for the thread to actually exit */
926 ret = guarded_wait (gc_thread->handle, INFINITE, TRUE);
927 g_assert (ret == WAIT_OBJECT_0);
929 mono_thread_join (GUINT_TO_POINTER (gc_thread->tid));
931 g_assert (finalizer_thread_exited);
934 mono_gc_base_cleanup ();
937 mono_reference_queue_cleanup ();
939 mono_coop_mutex_destroy (&finalizer_mutex);
940 mono_coop_mutex_destroy (&reference_queue_mutex);
944 mono_gc_is_finalizer_internal_thread (MonoInternalThread *thread)
946 return thread == gc_thread;
950 * mono_gc_is_finalizer_thread:
951 * @thread: the thread to test.
953 * In Mono objects are finalized asynchronously on a separate thread.
954 * This routine tests whether the @thread argument represents the
955 * finalization thread.
957 * Returns: TRUE if @thread is the finalization thread.
960 mono_gc_is_finalizer_thread (MonoThread *thread)
962 return mono_gc_is_finalizer_internal_thread (thread->internal_thread);
965 #if defined(__MACH__)
966 static pthread_t mach_exception_thread;
969 mono_gc_register_mach_exception_thread (pthread_t thread)
971 mach_exception_thread = thread;
975 mono_gc_get_mach_exception_thread (void)
977 return mach_exception_thread;
981 static MonoReferenceQueue *ref_queues;
984 ref_list_remove_element (RefQueueEntry **prev, RefQueueEntry *element)
987 /* Guard if head is changed concurrently. */
988 while (*prev != element)
989 prev = &(*prev)->next;
990 } while (prev && InterlockedCompareExchangePointer ((volatile gpointer *)prev, element->next, element) != element);
994 ref_list_push (RefQueueEntry **head, RefQueueEntry *value)
996 RefQueueEntry *current;
999 value->next = current;
1000 STORE_STORE_FENCE; /*Must make sure the previous store is visible before the CAS. */
1001 } while (InterlockedCompareExchangePointer ((volatile gpointer *)head, value, current) != current);
1005 reference_queue_proccess (MonoReferenceQueue *queue)
1007 RefQueueEntry **iter = &queue->queue;
1008 RefQueueEntry *entry;
1009 while ((entry = *iter)) {
1010 if (queue->should_be_deleted || !mono_gchandle_get_target (entry->gchandle)) {
1011 mono_gchandle_free ((guint32)entry->gchandle);
1012 ref_list_remove_element (iter, entry);
1013 queue->callback (entry->user_data);
1016 iter = &entry->next;
1022 reference_queue_proccess_all (void)
1024 MonoReferenceQueue **iter;
1025 MonoReferenceQueue *queue = ref_queues;
1026 for (; queue; queue = queue->next)
1027 reference_queue_proccess (queue);
1030 mono_coop_mutex_lock (&reference_queue_mutex);
1031 for (iter = &ref_queues; *iter;) {
1033 if (!queue->should_be_deleted) {
1034 iter = &queue->next;
1038 mono_coop_mutex_unlock (&reference_queue_mutex);
1039 reference_queue_proccess (queue);
1042 *iter = queue->next;
1045 mono_coop_mutex_unlock (&reference_queue_mutex);
1049 mono_reference_queue_cleanup (void)
1051 MonoReferenceQueue *queue = ref_queues;
1052 for (; queue; queue = queue->next)
1053 queue->should_be_deleted = TRUE;
1054 reference_queue_proccess_all ();
1058 reference_queue_clear_for_domain (MonoDomain *domain)
1060 MonoReferenceQueue *queue = ref_queues;
1061 for (; queue; queue = queue->next) {
1062 RefQueueEntry **iter = &queue->queue;
1063 RefQueueEntry *entry;
1064 while ((entry = *iter)) {
1065 if (entry->domain == domain) {
1066 mono_gchandle_free ((guint32)entry->gchandle);
1067 ref_list_remove_element (iter, entry);
1068 queue->callback (entry->user_data);
1071 iter = &entry->next;
1077 * mono_gc_reference_queue_new:
1078 * @callback callback used when processing collected entries.
1080 * Create a new reference queue used to process collected objects.
1081 * A reference queue let you add a pair of (managed object, user data)
1082 * using the mono_gc_reference_queue_add method.
1084 * Once the managed object is collected @callback will be called
1085 * in the finalizer thread with 'user data' as argument.
1087 * The callback is called from the finalizer thread without any locks held.
1088 * When a AppDomain is unloaded, all callbacks for objects belonging to it
1091 * @returns the new queue.
1094 mono_gc_reference_queue_new (mono_reference_queue_callback callback)
1096 MonoReferenceQueue *res = g_new0 (MonoReferenceQueue, 1);
1097 res->callback = callback;
1099 mono_coop_mutex_lock (&reference_queue_mutex);
1100 res->next = ref_queues;
1102 mono_coop_mutex_unlock (&reference_queue_mutex);
1108 * mono_gc_reference_queue_add:
1109 * @queue the queue to add the reference to.
1110 * @obj the object to be watched for collection
1111 * @user_data parameter to be passed to the queue callback
1113 * Queue an object to be watched for collection, when the @obj is
1114 * collected, the callback that was registered for the @queue will
1115 * be invoked with @user_data as argument.
1117 * @returns false if the queue is scheduled to be freed.
1120 mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *user_data)
1123 RefQueueEntry *entry;
1124 if (queue->should_be_deleted)
1127 entry = g_new0 (RefQueueEntry, 1);
1128 entry->user_data = user_data;
1129 entry->domain = mono_object_domain (obj);
1131 entry->gchandle = mono_gchandle_new_weakref (obj, TRUE);
1132 mono_object_register_finalizer (obj, &error);
1133 mono_error_assert_ok (&error);
1135 ref_list_push (&queue->queue, entry);
1140 * mono_gc_reference_queue_free:
1141 * @queue the queue that should be freed.
1143 * This operation signals that @queue should be freed. This operation is deferred
1144 * as it happens on the finalizer thread.
1146 * After this call, no further objects can be queued. It's the responsibility of the
1147 * caller to make sure that no further attempt to access queue will be made.
1150 mono_gc_reference_queue_free (MonoReferenceQueue *queue)
1152 queue->should_be_deleted = TRUE;