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*));
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)
89 result = WaitForSingleObjectEx (handle, timeout, alertable);
96 add_thread_to_finalize (MonoInternalThread *thread, MonoError *error)
98 mono_error_init (error);
99 mono_finalizer_lock ();
100 if (!threads_to_finalize)
101 MONO_GC_REGISTER_ROOT_SINGLE (threads_to_finalize, MONO_ROOT_SOURCE_FINALIZER_QUEUE, "finalizable threads list");
102 threads_to_finalize = mono_mlist_append_checked (threads_to_finalize, (MonoObject*)thread, error);
103 mono_finalizer_unlock ();
104 return is_ok (error);
107 static gboolean suspend_finalizers = FALSE;
109 * actually, we might want to queue the finalize requests in a separate thread,
110 * but we need to be careful about the execution domain of the thread...
113 mono_gc_run_finalize (void *obj, void *data)
116 MonoObject *exc = NULL;
121 MonoMethod* finalizer = NULL;
122 MonoDomain *caller_domain = mono_domain_get ();
124 RuntimeInvokeFunction runtime_invoke;
126 // This function is called from the innards of the GC, so our best alternative for now is to do polling here
127 mono_threads_safepoint ();
129 o = (MonoObject*)((char*)obj + GPOINTER_TO_UINT (data));
131 if (mono_do_not_finalize) {
132 if (!mono_do_not_finalize_class_names)
135 size_t namespace_len = strlen (o->vtable->klass->name_space);
136 for (int i = 0; mono_do_not_finalize_class_names [i]; ++i) {
137 const char *name = mono_do_not_finalize_class_names [i];
138 if (strncmp (name, o->vtable->klass->name_space, namespace_len))
140 if (name [namespace_len] != '.')
142 if (strcmp (name + namespace_len + 1, o->vtable->klass->name))
149 g_log ("mono-gc-finalizers", G_LOG_LEVEL_DEBUG, "<%s at %p> Starting finalizer checks.", o->vtable->klass->name, o);
151 if (suspend_finalizers)
154 domain = o->vtable->domain;
157 mono_domain_finalizers_lock (domain);
159 o2 = (MonoObject *)g_hash_table_lookup (domain->finalizable_objects_hash, o);
161 mono_domain_finalizers_unlock (domain);
164 /* Already finalized somehow */
168 /* make sure the finalizer is not called again if the object is resurrected */
169 object_register_finalizer ((MonoObject *)obj, NULL);
172 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Registered finalizer as processed.", o->vtable->klass->name, o);
174 if (o->vtable->klass == mono_defaults.internal_thread_class) {
175 MonoInternalThread *t = (MonoInternalThread*)o;
177 if (mono_gc_is_finalizer_internal_thread (t))
178 /* Avoid finalizing ourselves */
181 if (t->threadpool_thread && finalizing_root_domain) {
182 /* Don't finalize threadpool threads when
183 shutting down - they're finalized when the
184 threadpool shuts down. */
185 if (!add_thread_to_finalize (t, &error))
186 goto unhandled_error;
191 if (o->vtable->klass->image == mono_defaults.corlib && !strcmp (o->vtable->klass->name, "DynamicMethod") && finalizing_root_domain) {
193 * These can't be finalized during unloading/shutdown, since that would
194 * free the native code which can still be referenced by other
196 * FIXME: This is not perfect, objects dying at the same time as
197 * dynamic methods can still reference them even when !shutdown.
202 if (mono_runtime_get_no_exec ())
205 /* speedup later... and use a timeout */
206 /* g_print ("Finalize run on %p %s.%s\n", o, mono_object_class (o)->name_space, mono_object_class (o)->name); */
208 /* Use _internal here, since this thread can enter a doomed appdomain */
209 mono_domain_set_internal (mono_object_domain (o));
211 /* delegates that have a native function pointer allocated are
212 * registered for finalization, but they don't have a Finalize
213 * method, because in most cases it's not needed and it's just a waste.
215 if (o->vtable->klass->delegate) {
216 MonoDelegate* del = (MonoDelegate*)o;
217 if (del->delegate_trampoline)
218 mono_delegate_free_ftnptr ((MonoDelegate*)o);
219 mono_domain_set_internal (caller_domain);
223 finalizer = mono_class_get_finalizer (o->vtable->klass);
225 /* If object has a CCW but has no finalizer, it was only
226 * registered for finalization in order to free the CCW.
227 * Else it needs the regular finalizer run.
228 * FIXME: what to do about ressurection and suppression
229 * of finalizer on object with CCW.
231 if (mono_marshal_free_ccw (o) && !finalizer) {
232 mono_domain_set_internal (caller_domain);
237 * To avoid the locking plus the other overhead of mono_runtime_invoke_checked (),
238 * create and precompile a wrapper which calls the finalize method using
242 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Compiling finalizer.", o->vtable->klass->name, o);
244 if (!domain->finalize_runtime_invoke) {
245 MonoMethod *invoke = mono_marshal_get_runtime_invoke (mono_class_get_method_from_name_flags (mono_defaults.object_class, "Finalize", 0, 0), TRUE);
247 domain->finalize_runtime_invoke = mono_compile_method_checked (invoke, &error);
248 mono_error_assert_ok (&error); /* expect this not to fail */
251 runtime_invoke = (RuntimeInvokeFunction)domain->finalize_runtime_invoke;
253 mono_runtime_class_init_full (o->vtable, &error);
255 goto unhandled_error;
257 if (G_UNLIKELY (MONO_GC_FINALIZE_INVOKE_ENABLED ())) {
258 MONO_GC_FINALIZE_INVOKE ((unsigned long)o, mono_object_get_size (o),
259 o->vtable->klass->name_space, o->vtable->klass->name);
263 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Calling finalizer.", o->vtable->klass->name, o);
265 runtime_invoke (o, NULL, &exc, NULL);
268 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Returned from finalizer.", o->vtable->klass->name, o);
272 exc = (MonoObject*)mono_error_convert_to_exception (&error);
274 mono_thread_internal_unhandled_exception (exc);
276 mono_domain_set_internal (caller_domain);
280 mono_gc_finalize_threadpool_threads (void)
282 while (threads_to_finalize) {
283 MonoInternalThread *thread = (MonoInternalThread*) mono_mlist_get_data (threads_to_finalize);
285 /* Force finalization of the thread. */
286 thread->threadpool_thread = FALSE;
287 mono_object_register_finalizer ((MonoObject*)thread);
289 mono_gc_run_finalize (thread, NULL);
291 threads_to_finalize = mono_mlist_next (threads_to_finalize);
296 mono_gc_out_of_memory (size_t size)
299 * we could allocate at program startup some memory that we could release
300 * back to the system at this point if we're really low on memory (ie, size is
301 * lower than the memory we set apart)
303 mono_raise_exception (mono_domain_get ()->out_of_memory_ex);
309 * Some of our objects may point to a different address than the address returned by GC_malloc()
310 * (because of the GetHashCode hack), but we need to pass the real address to register_finalizer.
311 * This also means that in the callback we need to adjust the pointer to get back the real
313 * We also need to be consistent in the use of the GC_debug* variants of malloc and register_finalizer,
314 * since that, too, can cause the underlying pointer to be offset.
317 object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*))
321 g_assert (obj != NULL);
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)
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);
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)
486 MONO_CHECK_ARG_NULL (obj,);
488 object_register_finalizer (obj, mono_gc_run_finalize);
492 ves_icall_System_GC_SuppressFinalize (MonoObject *obj)
494 MONO_CHECK_ARG_NULL (obj,);
496 /* delegates have no finalizers, but we register them to deal with the
497 * unmanaged->managed trampoline. We don't let the user suppress it
498 * otherwise we'd leak it.
500 if (obj->vtable->klass->delegate)
503 /* FIXME: Need to handle case where obj has COM Callable Wrapper
504 * generated for it that needs cleaned up, but user wants to suppress
505 * their derived object finalizer. */
507 object_register_finalizer (obj, NULL);
511 ves_icall_System_GC_WaitForPendingFinalizers (void)
513 if (mono_gc_is_null ())
516 if (!mono_gc_pending_finalizers ())
519 if (mono_thread_internal_current () == gc_thread)
520 /* Avoid deadlocks */
524 If the finalizer thread is not live, lets pretend no finalizers are pending since the current thread might
525 be the one responsible for starting it up.
527 if (gc_thread == NULL)
530 ResetEvent (pending_done_event);
531 mono_gc_finalize_notify ();
532 /* g_print ("Waiting for pending finalizers....\n"); */
533 guarded_wait (pending_done_event, INFINITE, TRUE);
534 /* g_print ("Done pending....\n"); */
538 ves_icall_System_GC_register_ephemeron_array (MonoObject *array)
541 if (!mono_gc_ephemeron_array_add (array)) {
542 mono_set_pending_exception (mono_object_domain (array)->out_of_memory_ex);
549 ves_icall_System_GC_get_ephemeron_tombstone (void)
551 return mono_domain_get ()->ephemeron_tombstone;
555 ves_icall_System_GCHandle_GetTarget (guint32 handle)
557 return mono_gchandle_get_target (handle);
561 * if type == -1, change the target of the handle, otherwise allocate a new handle.
564 ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type)
567 mono_gchandle_set_target (handle, obj);
568 /* the handle doesn't change */
573 return mono_gchandle_new_weakref (obj, FALSE);
574 case HANDLE_WEAK_TRACK:
575 return mono_gchandle_new_weakref (obj, TRUE);
577 return mono_gchandle_new (obj, FALSE);
579 return mono_gchandle_new (obj, TRUE);
581 g_assert_not_reached ();
587 ves_icall_System_GCHandle_FreeHandle (guint32 handle)
589 mono_gchandle_free (handle);
593 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
597 if (MONO_GC_HANDLE_TYPE (handle) != HANDLE_PINNED)
599 obj = mono_gchandle_get_target (handle);
601 MonoClass *klass = mono_object_class (obj);
602 if (klass == mono_defaults.string_class) {
603 return mono_string_chars ((MonoString*)obj);
604 } else if (klass->rank) {
605 return mono_array_addr ((MonoArray*)obj, char, 0);
607 /* the C# code will check and throw the exception */
608 /* FIXME: missing !klass->blittable test, see bug #61134 */
609 if ((klass->flags & TYPE_ATTRIBUTE_LAYOUT_MASK) == TYPE_ATTRIBUTE_AUTO_LAYOUT)
611 return (char*)obj + sizeof (MonoObject);
618 mono_gc_GCHandle_CheckCurrentDomain (guint32 gchandle)
620 return mono_gchandle_is_in_domain (gchandle, mono_domain_get ());
623 static MonoCoopSem finalizer_sem;
624 static volatile gboolean finished=FALSE;
627 mono_gc_finalize_notify (void)
630 g_message ( "%s: prodding finalizer", __func__);
633 if (mono_gc_is_null ())
636 mono_coop_sem_post (&finalizer_sem);
640 This is the number of entries allowed in the hazard free queue before
641 we explicitly cycle the finalizer thread to trigger pumping the queue.
643 It was picked empirically by running the corlib test suite in a stress
644 scenario where all hazard entries are queued.
646 In this extreme scenario we double the number of times we cycle the finalizer
647 thread compared to just GC calls.
649 Entries are usually in the order of 100's of bytes each, so we're limiting
650 floating garbage to be in the order of a dozen kb.
652 static gboolean finalizer_thread_pulsed;
653 #define HAZARD_QUEUE_OVERFLOW_SIZE 20
656 hazard_free_queue_is_too_big (size_t size)
658 if (size < HAZARD_QUEUE_OVERFLOW_SIZE)
661 if (finalizer_thread_pulsed || InterlockedCompareExchange (&finalizer_thread_pulsed, TRUE, FALSE))
664 mono_gc_finalize_notify ();
668 hazard_free_queue_pump (void)
670 mono_thread_hazardous_try_free_all ();
671 finalizer_thread_pulsed = FALSE;
677 collect_objects (gpointer key, gpointer value, gpointer user_data)
679 GPtrArray *arr = (GPtrArray*)user_data;
680 g_ptr_array_add (arr, key);
686 * finalize_domain_objects:
688 * Run the finalizers of all finalizable objects in req->domain.
691 finalize_domain_objects (DomainFinalizationReq *req)
693 MonoDomain *domain = req->domain;
696 #define NUM_FOBJECTS 64
697 MonoObject *to_finalize [NUM_FOBJECTS];
701 /* Process finalizers which are already in the queue */
702 mono_gc_invoke_finalizers ();
705 while (g_hash_table_size (domain->finalizable_objects_hash) > 0) {
709 * Since the domain is unloading, nobody is allowed to put
710 * new entries into the hash table. But finalize_object might
711 * remove entries from the hash table, so we make a copy.
713 objs = g_ptr_array_new ();
714 g_hash_table_foreach (domain->finalizable_objects_hash, collect_objects, objs);
715 /* printf ("FINALIZING %d OBJECTS.\n", objs->len); */
717 for (i = 0; i < objs->len; ++i) {
718 MonoObject *o = (MonoObject*)g_ptr_array_index (objs, i);
719 /* FIXME: Avoid finalizing threads, etc */
720 mono_gc_run_finalize (o, 0);
723 g_ptr_array_free (objs, TRUE);
725 #elif defined(HAVE_SGEN_GC)
726 while ((count = mono_gc_finalizers_for_domain (domain, to_finalize, NUM_FOBJECTS))) {
728 for (i = 0; i < count; ++i) {
729 mono_gc_run_finalize (to_finalize [i], 0);
734 /* cleanup the reference queue */
735 reference_queue_clear_for_domain (domain);
737 /* printf ("DONE.\n"); */
738 SetEvent (req->done_event);
740 /* The event is closed in mono_domain_finalize if we get here */
745 finalizer_thread (gpointer unused)
748 mono_thread_set_name_internal (mono_thread_internal_current (), mono_string_new (mono_get_root_domain (), "Finalizer"), FALSE, &error);
749 mono_error_assert_ok (&error);
751 gboolean wait = TRUE;
753 /* Register a hazard free queue pump callback */
754 mono_hazard_pointer_install_free_queue_size_callback (hazard_free_queue_is_too_big);
757 /* Wait to be notified that there's at least one
761 g_assert (mono_domain_get () == mono_get_root_domain ());
762 mono_gc_set_skip_thread (TRUE);
765 /* An alertable wait is required so this thread can be suspended on windows */
766 mono_coop_sem_wait (&finalizer_sem, MONO_SEM_FLAGS_ALERTABLE);
770 mono_gc_set_skip_thread (FALSE);
772 mono_threads_perform_thread_dump ();
774 mono_console_handle_async_ops ();
776 mono_attach_maybe_start ();
778 if (domains_to_finalize) {
779 mono_finalizer_lock ();
780 if (domains_to_finalize) {
781 DomainFinalizationReq *req = (DomainFinalizationReq *)domains_to_finalize->data;
782 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
783 mono_finalizer_unlock ();
785 finalize_domain_objects (req);
787 mono_finalizer_unlock ();
791 /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup),
792 * before the domain is unloaded.
794 mono_gc_invoke_finalizers ();
796 mono_threads_join_threads ();
798 reference_queue_proccess_all ();
800 hazard_free_queue_pump ();
802 /* Avoid posting the pending done event until there are pending finalizers */
803 if (mono_coop_sem_timedwait (&finalizer_sem, 0, MONO_SEM_FLAGS_NONE) == 0) {
804 /* Don't wait again at the start of the loop */
807 SetEvent (pending_done_event);
811 mono_finalizer_lock ();
812 finalizer_thread_exited = TRUE;
813 mono_coop_cond_signal (&exited_cond);
814 mono_finalizer_unlock ();
819 #ifndef LAZY_GC_THREAD_CREATION
823 mono_gc_init_finalizer_thread (void)
826 gc_thread = mono_thread_create_internal (mono_domain_get (), finalizer_thread, NULL, FALSE, 0, &error);
827 mono_error_assert_ok (&error);
833 mono_coop_mutex_init_recursive (&finalizer_mutex);
834 mono_coop_mutex_init_recursive (&reference_queue_mutex);
836 mono_counters_register ("Minor GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.minor_gc_count);
837 mono_counters_register ("Major GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.major_gc_count);
838 mono_counters_register ("Minor GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.minor_gc_time);
839 mono_counters_register ("Major GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time);
840 mono_counters_register ("Major GC time concurrent", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time_concurrent);
842 mono_gc_base_init ();
844 if (mono_gc_is_disabled ()) {
849 pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
850 g_assert (pending_done_event);
851 mono_coop_cond_init (&exited_cond);
852 mono_coop_sem_init (&finalizer_sem, 0);
854 #ifndef LAZY_GC_THREAD_CREATION
855 mono_gc_init_finalizer_thread ();
860 mono_gc_cleanup (void)
863 g_message ("%s: cleaning up finalizer", __func__);
866 if (mono_gc_is_null ())
871 if (mono_thread_internal_current () != gc_thread) {
872 gboolean timed_out = FALSE;
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_stop (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 die. There is not much we
908 * can do here, since the runtime is shutting down so the
909 * state the finalizer thread depends on will vanish.
911 g_warning ("Shutting down finalizer thread timed out.");
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));
925 g_assert (finalizer_thread_exited);
928 mono_gc_base_cleanup ();
931 mono_reference_queue_cleanup ();
933 mono_coop_mutex_destroy (&finalizer_mutex);
934 mono_coop_mutex_destroy (&reference_queue_mutex);
938 mono_gc_is_finalizer_internal_thread (MonoInternalThread *thread)
940 return thread == gc_thread;
944 * mono_gc_is_finalizer_thread:
945 * @thread: the thread to test.
947 * In Mono objects are finalized asynchronously on a separate thread.
948 * This routine tests whether the @thread argument represents the
949 * finalization thread.
951 * Returns: TRUE if @thread is the finalization thread.
954 mono_gc_is_finalizer_thread (MonoThread *thread)
956 return mono_gc_is_finalizer_internal_thread (thread->internal_thread);
959 #if defined(__MACH__)
960 static pthread_t mach_exception_thread;
963 mono_gc_register_mach_exception_thread (pthread_t thread)
965 mach_exception_thread = thread;
969 mono_gc_get_mach_exception_thread (void)
971 return mach_exception_thread;
975 static MonoReferenceQueue *ref_queues;
978 ref_list_remove_element (RefQueueEntry **prev, RefQueueEntry *element)
981 /* Guard if head is changed concurrently. */
982 while (*prev != element)
983 prev = &(*prev)->next;
984 } while (prev && InterlockedCompareExchangePointer ((volatile gpointer *)prev, element->next, element) != element);
988 ref_list_push (RefQueueEntry **head, RefQueueEntry *value)
990 RefQueueEntry *current;
993 value->next = current;
994 STORE_STORE_FENCE; /*Must make sure the previous store is visible before the CAS. */
995 } while (InterlockedCompareExchangePointer ((volatile gpointer *)head, value, current) != current);
999 reference_queue_proccess (MonoReferenceQueue *queue)
1001 RefQueueEntry **iter = &queue->queue;
1002 RefQueueEntry *entry;
1003 while ((entry = *iter)) {
1004 if (queue->should_be_deleted || !mono_gchandle_get_target (entry->gchandle)) {
1005 mono_gchandle_free ((guint32)entry->gchandle);
1006 ref_list_remove_element (iter, entry);
1007 queue->callback (entry->user_data);
1010 iter = &entry->next;
1016 reference_queue_proccess_all (void)
1018 MonoReferenceQueue **iter;
1019 MonoReferenceQueue *queue = ref_queues;
1020 for (; queue; queue = queue->next)
1021 reference_queue_proccess (queue);
1024 mono_coop_mutex_lock (&reference_queue_mutex);
1025 for (iter = &ref_queues; *iter;) {
1027 if (!queue->should_be_deleted) {
1028 iter = &queue->next;
1032 mono_coop_mutex_unlock (&reference_queue_mutex);
1033 reference_queue_proccess (queue);
1036 *iter = queue->next;
1039 mono_coop_mutex_unlock (&reference_queue_mutex);
1043 mono_reference_queue_cleanup (void)
1045 MonoReferenceQueue *queue = ref_queues;
1046 for (; queue; queue = queue->next)
1047 queue->should_be_deleted = TRUE;
1048 reference_queue_proccess_all ();
1052 reference_queue_clear_for_domain (MonoDomain *domain)
1054 MonoReferenceQueue *queue = ref_queues;
1055 for (; queue; queue = queue->next) {
1056 RefQueueEntry **iter = &queue->queue;
1057 RefQueueEntry *entry;
1058 while ((entry = *iter)) {
1059 if (entry->domain == domain) {
1060 mono_gchandle_free ((guint32)entry->gchandle);
1061 ref_list_remove_element (iter, entry);
1062 queue->callback (entry->user_data);
1065 iter = &entry->next;
1071 * mono_gc_reference_queue_new:
1072 * @callback callback used when processing collected entries.
1074 * Create a new reference queue used to process collected objects.
1075 * A reference queue let you add a pair of (managed object, user data)
1076 * using the mono_gc_reference_queue_add method.
1078 * Once the managed object is collected @callback will be called
1079 * in the finalizer thread with 'user data' as argument.
1081 * The callback is called from the finalizer thread without any locks held.
1082 * When a AppDomain is unloaded, all callbacks for objects belonging to it
1085 * @returns the new queue.
1088 mono_gc_reference_queue_new (mono_reference_queue_callback callback)
1090 MonoReferenceQueue *res = g_new0 (MonoReferenceQueue, 1);
1091 res->callback = callback;
1093 mono_coop_mutex_lock (&reference_queue_mutex);
1094 res->next = ref_queues;
1096 mono_coop_mutex_unlock (&reference_queue_mutex);
1102 * mono_gc_reference_queue_add:
1103 * @queue the queue to add the reference to.
1104 * @obj the object to be watched for collection
1105 * @user_data parameter to be passed to the queue callback
1107 * Queue an object to be watched for collection, when the @obj is
1108 * collected, the callback that was registered for the @queue will
1109 * be invoked with @user_data as argument.
1111 * @returns false if the queue is scheduled to be freed.
1114 mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *user_data)
1116 RefQueueEntry *entry;
1117 if (queue->should_be_deleted)
1120 g_assert (obj != NULL);
1122 entry = g_new0 (RefQueueEntry, 1);
1123 entry->user_data = user_data;
1124 entry->domain = mono_object_domain (obj);
1126 entry->gchandle = mono_gchandle_new_weakref (obj, TRUE);
1127 mono_object_register_finalizer (obj);
1129 ref_list_push (&queue->queue, entry);
1134 * mono_gc_reference_queue_free:
1135 * @queue the queue that should be freed.
1137 * This operation signals that @queue should be freed. This operation is deferred
1138 * as it happens on the finalizer thread.
1140 * After this call, no further objects can be queued. It's the responsibility of the
1141 * caller to make sure that no further attempt to access queue will be made.
1144 mono_gc_reference_queue_free (MonoReferenceQueue *queue)
1146 queue->should_be_deleted = TRUE;