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)
15 #include <mono/metadata/gc-internals.h>
16 #include <mono/metadata/mono-gc.h>
17 #include <mono/metadata/threads.h>
18 #include <mono/metadata/tabledefs.h>
19 #include <mono/metadata/exception.h>
20 #include <mono/metadata/profiler-private.h>
21 #include <mono/metadata/domain-internals.h>
22 #include <mono/metadata/class-internals.h>
23 #include <mono/metadata/metadata-internals.h>
24 #include <mono/metadata/mono-mlist.h>
25 #include <mono/metadata/threads-types.h>
26 #include <mono/metadata/threadpool-ms.h>
27 #include <mono/sgen/sgen-conf.h>
28 #include <mono/sgen/sgen-gc.h>
29 #include <mono/utils/mono-logger-internals.h>
30 #include <mono/metadata/gc-internals.h>
31 #include <mono/metadata/marshal.h> /* for mono_delegate_free_ftnptr () */
32 #include <mono/metadata/attach.h>
33 #include <mono/metadata/console-io.h>
34 #include <mono/utils/mono-os-semaphore.h>
35 #include <mono/utils/mono-memory-model.h>
36 #include <mono/utils/mono-counters.h>
37 #include <mono/utils/mono-time.h>
38 #include <mono/utils/dtrace.h>
39 #include <mono/utils/mono-threads.h>
40 #include <mono/utils/atomic.h>
46 typedef struct DomainFinalizationReq {
49 } DomainFinalizationReq;
51 static gboolean gc_disabled = FALSE;
53 static gboolean finalizing_root_domain = FALSE;
55 gboolean log_finalizers = FALSE;
56 gboolean mono_do_not_finalize = FALSE;
57 gchar **mono_do_not_finalize_class_names = NULL;
59 #define mono_finalizer_lock() mono_os_mutex_lock (&finalizer_mutex)
60 #define mono_finalizer_unlock() mono_os_mutex_unlock (&finalizer_mutex)
61 static mono_mutex_t finalizer_mutex;
62 static mono_mutex_t reference_queue_mutex;
64 static GSList *domains_to_finalize= NULL;
65 static MonoMList *threads_to_finalize = NULL;
67 static gboolean finalizer_thread_exited;
68 /* Uses finalizer_mutex */
69 static mono_cond_t exited_cond;
71 static MonoInternalThread *gc_thread;
73 static void object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*));
75 static void reference_queue_proccess_all (void);
76 static void mono_reference_queue_cleanup (void);
77 static void reference_queue_clear_for_domain (MonoDomain *domain);
78 static HANDLE pending_done_event;
81 guarded_wait (HANDLE handle, guint32 timeout, gboolean alertable)
85 MONO_PREPARE_BLOCKING;
86 result = WaitForSingleObjectEx (handle, timeout, alertable);
93 add_thread_to_finalize (MonoInternalThread *thread)
95 mono_finalizer_lock ();
96 if (!threads_to_finalize)
97 MONO_GC_REGISTER_ROOT_SINGLE (threads_to_finalize, MONO_ROOT_SOURCE_FINALIZER_QUEUE, "finalizable threads list");
98 threads_to_finalize = mono_mlist_append (threads_to_finalize, (MonoObject*)thread);
99 mono_finalizer_unlock ();
102 static gboolean suspend_finalizers = FALSE;
104 * actually, we might want to queue the finalize requests in a separate thread,
105 * but we need to be careful about the execution domain of the thread...
108 mono_gc_run_finalize (void *obj, void *data)
110 MonoObject *exc = NULL;
115 MonoMethod* finalizer = NULL;
116 MonoDomain *caller_domain = mono_domain_get ();
118 RuntimeInvokeFunction runtime_invoke;
120 // This function is called from the innards of the GC, so our best alternative for now is to do polling here
121 mono_threads_safepoint ();
123 o = (MonoObject*)((char*)obj + GPOINTER_TO_UINT (data));
125 if (mono_do_not_finalize) {
126 if (!mono_do_not_finalize_class_names)
129 size_t namespace_len = strlen (o->vtable->klass->name_space);
130 for (int i = 0; mono_do_not_finalize_class_names [i]; ++i) {
131 const char *name = mono_do_not_finalize_class_names [i];
132 if (strncmp (name, o->vtable->klass->name_space, namespace_len))
134 if (name [namespace_len] != '.')
136 if (strcmp (name + namespace_len + 1, o->vtable->klass->name))
143 g_log ("mono-gc-finalizers", G_LOG_LEVEL_DEBUG, "<%s at %p> Starting finalizer checks.", o->vtable->klass->name, o);
145 if (suspend_finalizers)
148 domain = o->vtable->domain;
151 mono_domain_finalizers_lock (domain);
153 o2 = g_hash_table_lookup (domain->finalizable_objects_hash, o);
155 mono_domain_finalizers_unlock (domain);
158 /* Already finalized somehow */
162 /* make sure the finalizer is not called again if the object is resurrected */
163 object_register_finalizer (obj, NULL);
166 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Registered finalizer as processed.", o->vtable->klass->name, o);
168 if (o->vtable->klass == mono_defaults.internal_thread_class) {
169 MonoInternalThread *t = (MonoInternalThread*)o;
171 if (mono_gc_is_finalizer_internal_thread (t))
172 /* Avoid finalizing ourselves */
175 if (t->threadpool_thread && finalizing_root_domain) {
176 /* Don't finalize threadpool threads when
177 shutting down - they're finalized when the
178 threadpool shuts down. */
179 add_thread_to_finalize (t);
184 if (o->vtable->klass->image == mono_defaults.corlib && !strcmp (o->vtable->klass->name, "DynamicMethod") && finalizing_root_domain) {
186 * These can't be finalized during unloading/shutdown, since that would
187 * free the native code which can still be referenced by other
189 * FIXME: This is not perfect, objects dying at the same time as
190 * dynamic methods can still reference them even when !shutdown.
195 if (mono_runtime_get_no_exec ())
198 /* speedup later... and use a timeout */
199 /* g_print ("Finalize run on %p %s.%s\n", o, mono_object_class (o)->name_space, mono_object_class (o)->name); */
201 /* Use _internal here, since this thread can enter a doomed appdomain */
202 mono_domain_set_internal (mono_object_domain (o));
204 /* delegates that have a native function pointer allocated are
205 * registered for finalization, but they don't have a Finalize
206 * method, because in most cases it's not needed and it's just a waste.
208 if (o->vtable->klass->delegate) {
209 MonoDelegate* del = (MonoDelegate*)o;
210 if (del->delegate_trampoline)
211 mono_delegate_free_ftnptr ((MonoDelegate*)o);
212 mono_domain_set_internal (caller_domain);
216 finalizer = mono_class_get_finalizer (o->vtable->klass);
218 /* If object has a CCW but has no finalizer, it was only
219 * registered for finalization in order to free the CCW.
220 * Else it needs the regular finalizer run.
221 * FIXME: what to do about ressurection and suppression
222 * of finalizer on object with CCW.
224 if (mono_marshal_free_ccw (o) && !finalizer) {
225 mono_domain_set_internal (caller_domain);
230 * To avoid the locking plus the other overhead of mono_runtime_invoke (),
231 * create and precompile a wrapper which calls the finalize method using
235 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Compiling finalizer.", o->vtable->klass->name, o);
237 if (!domain->finalize_runtime_invoke) {
238 MonoMethod *invoke = mono_marshal_get_runtime_invoke (mono_class_get_method_from_name_flags (mono_defaults.object_class, "Finalize", 0, 0), TRUE, FALSE);
240 domain->finalize_runtime_invoke = mono_compile_method (invoke);
243 runtime_invoke = domain->finalize_runtime_invoke;
245 mono_runtime_class_init (o->vtable);
247 if (G_UNLIKELY (MONO_GC_FINALIZE_INVOKE_ENABLED ())) {
248 MONO_GC_FINALIZE_INVOKE ((unsigned long)o, mono_object_get_size (o),
249 o->vtable->klass->name_space, o->vtable->klass->name);
253 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Calling finalizer.", o->vtable->klass->name, o);
255 runtime_invoke (o, NULL, &exc, NULL);
258 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Returned from finalizer.", o->vtable->klass->name, o);
261 mono_thread_internal_unhandled_exception (exc);
263 mono_domain_set_internal (caller_domain);
267 mono_gc_finalize_threadpool_threads (void)
269 while (threads_to_finalize) {
270 MonoInternalThread *thread = (MonoInternalThread*) mono_mlist_get_data (threads_to_finalize);
272 /* Force finalization of the thread. */
273 thread->threadpool_thread = FALSE;
274 mono_object_register_finalizer ((MonoObject*)thread);
276 mono_gc_run_finalize (thread, NULL);
278 threads_to_finalize = mono_mlist_next (threads_to_finalize);
283 mono_gc_out_of_memory (size_t size)
286 * we could allocate at program startup some memory that we could release
287 * back to the system at this point if we're really low on memory (ie, size is
288 * lower than the memory we set apart)
290 mono_raise_exception (mono_domain_get ()->out_of_memory_ex);
296 * Some of our objects may point to a different address than the address returned by GC_malloc()
297 * (because of the GetHashCode hack), but we need to pass the real address to register_finalizer.
298 * This also means that in the callback we need to adjust the pointer to get back the real
300 * We also need to be consistent in the use of the GC_debug* variants of malloc and register_finalizer,
301 * since that, too, can cause the underlying pointer to be offset.
304 object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*))
309 mono_raise_exception (mono_get_exception_argument_null ("obj"));
311 domain = obj->vtable->domain;
314 if (mono_domain_is_unloading (domain) && (callback != NULL))
316 * Can't register finalizers in a dying appdomain, since they
317 * could be invoked after the appdomain has been unloaded.
321 mono_domain_finalizers_lock (domain);
324 g_hash_table_insert (domain->finalizable_objects_hash, obj, obj);
326 g_hash_table_remove (domain->finalizable_objects_hash, obj);
328 mono_domain_finalizers_unlock (domain);
330 mono_gc_register_for_finalization (obj, callback);
331 #elif defined(HAVE_SGEN_GC)
333 * If we register finalizers for domains that are unloading we might
334 * end up running them while or after the domain is being cleared, so
335 * the objects will not be valid anymore.
337 if (!mono_domain_is_unloading (domain)) {
339 mono_gc_register_for_finalization (obj, callback);
340 MONO_FINISH_TRY_BLOCKING;
346 * mono_object_register_finalizer:
347 * @obj: object to register
349 * Records that object @obj has a finalizer, this will call the
350 * Finalize method when the garbage collector disposes the object.
354 mono_object_register_finalizer (MonoObject *obj)
356 /* g_print ("Registered finalizer on %p %s.%s\n", obj, mono_object_class (obj)->name_space, mono_object_class (obj)->name); */
357 object_register_finalizer (obj, mono_gc_run_finalize);
361 * mono_domain_finalize:
362 * @domain: the domain to finalize
363 * @timeout: msects to wait for the finalization to complete, -1 to wait indefinitely
365 * Request finalization of all finalizable objects inside @domain. Wait
366 * @timeout msecs for the finalization to complete.
368 * Returns: TRUE if succeeded, FALSE if there was a timeout
372 mono_domain_finalize (MonoDomain *domain, guint32 timeout)
374 DomainFinalizationReq *req;
377 MonoInternalThread *thread = mono_thread_internal_current ();
379 #if defined(__native_client__)
383 if (mono_thread_internal_current () == gc_thread)
384 /* We are called from inside a finalizer, not much we can do here */
388 * No need to create another thread 'cause the finalizer thread
389 * is still working and will take care of running the finalizers
395 /* We don't support domain finalization without a GC */
396 if (mono_gc_is_null ())
399 mono_gc_collect (mono_gc_max_generation ());
401 done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
402 if (done_event == NULL) {
406 req = g_new0 (DomainFinalizationReq, 1);
407 req->domain = domain;
408 req->done_event = done_event;
410 if (domain == mono_get_root_domain ())
411 finalizing_root_domain = TRUE;
413 mono_finalizer_lock ();
415 domains_to_finalize = g_slist_append (domains_to_finalize, req);
417 mono_finalizer_unlock ();
419 /* Tell the finalizer thread to finalize this appdomain */
420 mono_gc_finalize_notify ();
426 res = guarded_wait (done_event, timeout, TRUE);
427 /* printf ("WAIT RES: %d.\n", res); */
429 if (res == WAIT_IO_COMPLETION) {
430 if ((thread->state & (ThreadState_StopRequested | ThreadState_SuspendRequested)) != 0)
432 } else if (res == WAIT_TIMEOUT) {
433 /* We leak the handle here */
440 CloseHandle (done_event);
442 if (domain == mono_get_root_domain ()) {
443 mono_threadpool_ms_cleanup ();
444 mono_gc_finalize_threadpool_threads ();
451 ves_icall_System_GC_InternalCollect (int generation)
453 mono_gc_collect (generation);
457 ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection)
460 mono_gc_collect (mono_gc_max_generation ());
461 return mono_gc_get_used_size ();
465 ves_icall_System_GC_KeepAlive (MonoObject *obj)
473 ves_icall_System_GC_ReRegisterForFinalize (MonoObject *obj)
475 MONO_CHECK_ARG_NULL (obj,);
477 object_register_finalizer (obj, mono_gc_run_finalize);
481 ves_icall_System_GC_SuppressFinalize (MonoObject *obj)
483 MONO_CHECK_ARG_NULL (obj,);
485 /* delegates have no finalizers, but we register them to deal with the
486 * unmanaged->managed trampoline. We don't let the user suppress it
487 * otherwise we'd leak it.
489 if (obj->vtable->klass->delegate)
492 /* FIXME: Need to handle case where obj has COM Callable Wrapper
493 * generated for it that needs cleaned up, but user wants to suppress
494 * their derived object finalizer. */
496 object_register_finalizer (obj, NULL);
500 ves_icall_System_GC_WaitForPendingFinalizers (void)
502 if (mono_gc_is_null ())
505 if (!mono_gc_pending_finalizers ())
508 if (mono_thread_internal_current () == gc_thread)
509 /* Avoid deadlocks */
513 If the finalizer thread is not live, lets pretend no finalizers are pending since the current thread might
514 be the one responsible for starting it up.
516 if (gc_thread == NULL)
519 ResetEvent (pending_done_event);
520 mono_gc_finalize_notify ();
521 /* g_print ("Waiting for pending finalizers....\n"); */
522 guarded_wait (pending_done_event, INFINITE, TRUE);
523 /* g_print ("Done pending....\n"); */
527 ves_icall_System_GC_register_ephemeron_array (MonoObject *array)
530 if (!mono_gc_ephemeron_array_add (array)) {
531 mono_set_pending_exception (mono_object_domain (array)->out_of_memory_ex);
538 ves_icall_System_GC_get_ephemeron_tombstone (void)
540 return mono_domain_get ()->ephemeron_tombstone;
543 #define mono_allocator_lock() mono_os_mutex_lock (&allocator_section)
544 #define mono_allocator_unlock() mono_os_mutex_unlock (&allocator_section)
545 static mono_mutex_t allocator_section;
548 ves_icall_System_GCHandle_GetTarget (guint32 handle)
550 return mono_gchandle_get_target (handle);
554 * if type == -1, change the target of the handle, otherwise allocate a new handle.
557 ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type)
560 mono_gchandle_set_target (handle, obj);
561 /* the handle doesn't change */
566 return mono_gchandle_new_weakref (obj, FALSE);
567 case HANDLE_WEAK_TRACK:
568 return mono_gchandle_new_weakref (obj, TRUE);
570 return mono_gchandle_new (obj, FALSE);
572 return mono_gchandle_new (obj, TRUE);
574 g_assert_not_reached ();
580 ves_icall_System_GCHandle_FreeHandle (guint32 handle)
582 mono_gchandle_free (handle);
586 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
590 if (MONO_GC_HANDLE_TYPE (handle) != HANDLE_PINNED)
592 obj = mono_gchandle_get_target (handle);
594 MonoClass *klass = mono_object_class (obj);
595 if (klass == mono_defaults.string_class) {
596 return mono_string_chars ((MonoString*)obj);
597 } else if (klass->rank) {
598 return mono_array_addr ((MonoArray*)obj, char, 0);
600 /* the C# code will check and throw the exception */
601 /* FIXME: missing !klass->blittable test, see bug #61134 */
602 if ((klass->flags & TYPE_ATTRIBUTE_LAYOUT_MASK) == TYPE_ATTRIBUTE_AUTO_LAYOUT)
604 return (char*)obj + sizeof (MonoObject);
611 ves_icall_Mono_Runtime_SetGCAllowSynchronousMajor (MonoBoolean flag)
613 return mono_gc_set_allow_synchronous_major (flag);
617 mono_gc_GCHandle_CheckCurrentDomain (guint32 gchandle)
619 return mono_gchandle_is_in_domain (gchandle, mono_domain_get ());
622 #ifdef MONO_HAS_SEMAPHORES
623 static MonoSemType finalizer_sem;
625 static HANDLE finalizer_event;
626 static volatile gboolean finished=FALSE;
629 mono_gc_finalize_notify (void)
632 g_message ( "%s: prodding finalizer", __func__);
635 if (mono_gc_is_null ())
638 #ifdef MONO_HAS_SEMAPHORES
639 mono_os_sem_post (&finalizer_sem);
641 SetEvent (finalizer_event);
648 collect_objects (gpointer key, gpointer value, gpointer user_data)
650 GPtrArray *arr = (GPtrArray*)user_data;
651 g_ptr_array_add (arr, key);
657 * finalize_domain_objects:
659 * Run the finalizers of all finalizable objects in req->domain.
662 finalize_domain_objects (DomainFinalizationReq *req)
664 MonoDomain *domain = req->domain;
667 #define NUM_FOBJECTS 64
668 MonoObject *to_finalize [NUM_FOBJECTS];
672 /* Process finalizers which are already in the queue */
673 mono_gc_invoke_finalizers ();
676 while (g_hash_table_size (domain->finalizable_objects_hash) > 0) {
680 * Since the domain is unloading, nobody is allowed to put
681 * new entries into the hash table. But finalize_object might
682 * remove entries from the hash table, so we make a copy.
684 objs = g_ptr_array_new ();
685 g_hash_table_foreach (domain->finalizable_objects_hash, collect_objects, objs);
686 /* printf ("FINALIZING %d OBJECTS.\n", objs->len); */
688 for (i = 0; i < objs->len; ++i) {
689 MonoObject *o = (MonoObject*)g_ptr_array_index (objs, i);
690 /* FIXME: Avoid finalizing threads, etc */
691 mono_gc_run_finalize (o, 0);
694 g_ptr_array_free (objs, TRUE);
696 #elif defined(HAVE_SGEN_GC)
697 while ((count = mono_gc_finalizers_for_domain (domain, to_finalize, NUM_FOBJECTS))) {
699 for (i = 0; i < count; ++i) {
700 mono_gc_run_finalize (to_finalize [i], 0);
705 /* cleanup the reference queue */
706 reference_queue_clear_for_domain (domain);
708 /* printf ("DONE.\n"); */
709 SetEvent (req->done_event);
711 /* The event is closed in mono_domain_finalize if we get here */
716 finalizer_thread (gpointer unused)
718 gboolean wait = TRUE;
721 /* Wait to be notified that there's at least one
725 g_assert (mono_domain_get () == mono_get_root_domain ());
726 mono_gc_set_skip_thread (TRUE);
727 MONO_PREPARE_BLOCKING;
730 /* An alertable wait is required so this thread can be suspended on windows */
731 #ifdef MONO_HAS_SEMAPHORES
732 mono_os_sem_wait (&finalizer_sem, MONO_SEM_FLAGS_ALERTABLE);
734 WaitForSingleObjectEx (finalizer_event, INFINITE, TRUE);
738 MONO_FINISH_BLOCKING;
739 mono_gc_set_skip_thread (FALSE);
741 mono_threads_perform_thread_dump ();
743 mono_console_handle_async_ops ();
745 mono_attach_maybe_start ();
747 if (domains_to_finalize) {
748 mono_finalizer_lock ();
749 if (domains_to_finalize) {
750 DomainFinalizationReq *req = domains_to_finalize->data;
751 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
752 mono_finalizer_unlock ();
754 finalize_domain_objects (req);
756 mono_finalizer_unlock ();
760 /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup),
761 * before the domain is unloaded.
763 mono_gc_invoke_finalizers ();
765 mono_threads_join_threads ();
767 reference_queue_proccess_all ();
769 #ifdef MONO_HAS_SEMAPHORES
770 /* Avoid posting the pending done event until there are pending finalizers */
771 if (mono_os_sem_timedwait (&finalizer_sem, 0, MONO_SEM_FLAGS_NONE) == 0)
772 /* Don't wait again at the start of the loop */
775 SetEvent (pending_done_event);
777 SetEvent (pending_done_event);
781 mono_finalizer_lock ();
782 finalizer_thread_exited = TRUE;
783 mono_os_cond_signal (&exited_cond);
784 mono_finalizer_unlock ();
789 #ifndef LAZY_GC_THREAD_CREATION
793 mono_gc_init_finalizer_thread (void)
795 gc_thread = mono_thread_create_internal (mono_domain_get (), finalizer_thread, NULL, FALSE, 0);
796 ves_icall_System_Threading_Thread_SetName_internal (gc_thread, mono_string_new (mono_domain_get (), "Finalizer"));
802 mono_os_mutex_init_recursive (&allocator_section);
804 mono_os_mutex_init_recursive (&finalizer_mutex);
805 mono_os_mutex_init_recursive (&reference_queue_mutex);
807 mono_counters_register ("Minor GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.minor_gc_count);
808 mono_counters_register ("Major GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.major_gc_count);
809 mono_counters_register ("Minor GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.minor_gc_time);
810 mono_counters_register ("Major GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time);
811 mono_counters_register ("Major GC time concurrent", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time_concurrent);
813 mono_gc_base_init ();
815 if (mono_gc_is_disabled ()) {
820 finalizer_event = CreateEvent (NULL, FALSE, FALSE, NULL);
821 g_assert (finalizer_event);
822 pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
823 g_assert (pending_done_event);
824 mono_os_cond_init (&exited_cond);
825 #ifdef MONO_HAS_SEMAPHORES
826 mono_os_sem_init (&finalizer_sem, 0);
829 #ifndef LAZY_GC_THREAD_CREATION
830 mono_gc_init_finalizer_thread ();
835 mono_gc_cleanup (void)
838 g_message ("%s: cleaning up finalizer", __func__);
841 if (mono_gc_is_null ())
846 if (mono_thread_internal_current () != gc_thread) {
847 gboolean timed_out = FALSE;
848 guint32 start_ticks = mono_msec_ticks ();
849 guint32 end_ticks = start_ticks + 2000;
851 mono_gc_finalize_notify ();
852 /* Finishing the finalizer thread, so wait a little bit... */
853 /* MS seems to wait for about 2 seconds */
854 while (!finalizer_thread_exited) {
855 guint32 current_ticks = mono_msec_ticks ();
858 if (current_ticks >= end_ticks)
861 timeout = end_ticks - current_ticks;
862 MONO_PREPARE_BLOCKING;
863 mono_finalizer_lock ();
864 if (!finalizer_thread_exited)
865 mono_os_cond_timedwait_ms (&exited_cond, &finalizer_mutex, timeout);
866 mono_finalizer_unlock ();
867 MONO_FINISH_BLOCKING;
870 if (!finalizer_thread_exited) {
873 /* Set a flag which the finalizer thread can check */
874 suspend_finalizers = TRUE;
876 /* Try to abort the thread, in the hope that it is running managed code */
877 mono_thread_internal_stop (gc_thread);
879 /* Wait for it to stop */
880 ret = guarded_wait (gc_thread->handle, 100, TRUE);
882 if (ret == WAIT_TIMEOUT) {
884 * The finalizer thread refused to die. There is not much we
885 * can do here, since the runtime is shutting down so the
886 * state the finalizer thread depends on will vanish.
888 g_warning ("Shutting down finalizer thread timed out.");
896 /* Wait for the thread to actually exit */
897 ret = guarded_wait (gc_thread->handle, INFINITE, TRUE);
898 g_assert (ret == WAIT_OBJECT_0);
900 mono_thread_join (GUINT_TO_POINTER (gc_thread->tid));
904 mono_gc_base_cleanup ();
907 mono_reference_queue_cleanup ();
909 mono_os_mutex_destroy (&allocator_section);
910 mono_os_mutex_destroy (&finalizer_mutex);
911 mono_os_mutex_destroy (&reference_queue_mutex);
915 mono_gc_is_finalizer_internal_thread (MonoInternalThread *thread)
917 return thread == gc_thread;
921 * mono_gc_is_finalizer_thread:
922 * @thread: the thread to test.
924 * In Mono objects are finalized asynchronously on a separate thread.
925 * This routine tests whether the @thread argument represents the
926 * finalization thread.
928 * Returns true if @thread is the finalization thread.
931 mono_gc_is_finalizer_thread (MonoThread *thread)
933 return mono_gc_is_finalizer_internal_thread (thread->internal_thread);
936 #if defined(__MACH__)
937 static pthread_t mach_exception_thread;
940 mono_gc_register_mach_exception_thread (pthread_t thread)
942 mach_exception_thread = thread;
946 mono_gc_get_mach_exception_thread (void)
948 return mach_exception_thread;
954 mono_gc_alloc_mature (MonoVTable *vtable)
956 return mono_object_new_specific (vtable);
961 static MonoReferenceQueue *ref_queues;
964 ref_list_remove_element (RefQueueEntry **prev, RefQueueEntry *element)
967 /* Guard if head is changed concurrently. */
968 while (*prev != element)
969 prev = &(*prev)->next;
970 } while (prev && InterlockedCompareExchangePointer ((void*)prev, element->next, element) != element);
974 ref_list_push (RefQueueEntry **head, RefQueueEntry *value)
976 RefQueueEntry *current;
979 value->next = current;
980 STORE_STORE_FENCE; /*Must make sure the previous store is visible before the CAS. */
981 } while (InterlockedCompareExchangePointer ((void*)head, value, current) != current);
985 reference_queue_proccess (MonoReferenceQueue *queue)
987 RefQueueEntry **iter = &queue->queue;
988 RefQueueEntry *entry;
989 while ((entry = *iter)) {
990 if (queue->should_be_deleted || !mono_gchandle_get_target (entry->gchandle)) {
991 mono_gchandle_free ((guint32)entry->gchandle);
992 ref_list_remove_element (iter, entry);
993 queue->callback (entry->user_data);
1002 reference_queue_proccess_all (void)
1004 MonoReferenceQueue **iter;
1005 MonoReferenceQueue *queue = ref_queues;
1006 for (; queue; queue = queue->next)
1007 reference_queue_proccess (queue);
1010 mono_os_mutex_lock (&reference_queue_mutex);
1011 for (iter = &ref_queues; *iter;) {
1013 if (!queue->should_be_deleted) {
1014 iter = &queue->next;
1018 mono_os_mutex_unlock (&reference_queue_mutex);
1019 reference_queue_proccess (queue);
1022 *iter = queue->next;
1025 mono_os_mutex_unlock (&reference_queue_mutex);
1029 mono_reference_queue_cleanup (void)
1031 MonoReferenceQueue *queue = ref_queues;
1032 for (; queue; queue = queue->next)
1033 queue->should_be_deleted = TRUE;
1034 reference_queue_proccess_all ();
1038 reference_queue_clear_for_domain (MonoDomain *domain)
1040 MonoReferenceQueue *queue = ref_queues;
1041 for (; queue; queue = queue->next) {
1042 RefQueueEntry **iter = &queue->queue;
1043 RefQueueEntry *entry;
1044 while ((entry = *iter)) {
1045 if (entry->domain == domain) {
1046 mono_gchandle_free ((guint32)entry->gchandle);
1047 ref_list_remove_element (iter, entry);
1048 queue->callback (entry->user_data);
1051 iter = &entry->next;
1057 * mono_gc_reference_queue_new:
1058 * @callback callback used when processing collected entries.
1060 * Create a new reference queue used to process collected objects.
1061 * A reference queue let you add a pair of (managed object, user data)
1062 * using the mono_gc_reference_queue_add method.
1064 * Once the managed object is collected @callback will be called
1065 * in the finalizer thread with 'user data' as argument.
1067 * The callback is called from the finalizer thread without any locks held.
1068 * When a AppDomain is unloaded, all callbacks for objects belonging to it
1071 * @returns the new queue.
1074 mono_gc_reference_queue_new (mono_reference_queue_callback callback)
1076 MonoReferenceQueue *res = g_new0 (MonoReferenceQueue, 1);
1077 res->callback = callback;
1079 mono_os_mutex_lock (&reference_queue_mutex);
1080 res->next = ref_queues;
1082 mono_os_mutex_unlock (&reference_queue_mutex);
1088 * mono_gc_reference_queue_add:
1089 * @queue the queue to add the reference to.
1090 * @obj the object to be watched for collection
1091 * @user_data parameter to be passed to the queue callback
1093 * Queue an object to be watched for collection, when the @obj is
1094 * collected, the callback that was registered for the @queue will
1095 * be invoked with @user_data as argument.
1097 * @returns false if the queue is scheduled to be freed.
1100 mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *user_data)
1102 RefQueueEntry *entry;
1103 if (queue->should_be_deleted)
1106 entry = g_new0 (RefQueueEntry, 1);
1107 entry->user_data = user_data;
1108 entry->domain = mono_object_domain (obj);
1110 entry->gchandle = mono_gchandle_new_weakref (obj, TRUE);
1111 mono_object_register_finalizer (obj);
1113 ref_list_push (&queue->queue, entry);
1118 * mono_gc_reference_queue_free:
1119 * @queue the queue that should be freed.
1121 * This operation signals that @queue should be freed. This operation is deferred
1122 * as it happens on the finalizer thread.
1124 * After this call, no further objects can be queued. It's the responsibility of the
1125 * caller to make sure that no further attempt to access queue will be made.
1128 mono_gc_reference_queue_free (MonoReferenceQueue *queue)
1130 queue->should_be_deleted = TRUE;