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>
41 #include <mono/utils/mono-coop-semaphore.h>
42 #include <mono/utils/hazard-pointer.h>
48 typedef struct DomainFinalizationReq {
51 } DomainFinalizationReq;
53 static gboolean gc_disabled = FALSE;
55 static gboolean finalizing_root_domain = FALSE;
57 gboolean log_finalizers = FALSE;
58 gboolean mono_do_not_finalize = FALSE;
59 gchar **mono_do_not_finalize_class_names = NULL;
61 #define mono_finalizer_lock() mono_coop_mutex_lock (&finalizer_mutex)
62 #define mono_finalizer_unlock() mono_coop_mutex_unlock (&finalizer_mutex)
63 static MonoCoopMutex finalizer_mutex;
64 static MonoCoopMutex reference_queue_mutex;
66 static GSList *domains_to_finalize= NULL;
67 static MonoMList *threads_to_finalize = NULL;
69 static gboolean finalizer_thread_exited;
70 /* Uses finalizer_mutex */
71 static MonoCoopCond exited_cond;
73 static MonoInternalThread *gc_thread;
75 static void object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*), MonoError *error);
77 static void reference_queue_proccess_all (void);
78 static void mono_reference_queue_cleanup (void);
79 static void reference_queue_clear_for_domain (MonoDomain *domain);
80 static HANDLE pending_done_event;
83 guarded_wait (HANDLE handle, guint32 timeout, gboolean alertable)
87 MONO_PREPARE_BLOCKING;
88 result = WaitForSingleObjectEx (handle, timeout, alertable);
95 add_thread_to_finalize (MonoInternalThread *thread)
97 mono_finalizer_lock ();
98 if (!threads_to_finalize)
99 MONO_GC_REGISTER_ROOT_SINGLE (threads_to_finalize, MONO_ROOT_SOURCE_FINALIZER_QUEUE, "finalizable threads list");
100 threads_to_finalize = mono_mlist_append (threads_to_finalize, (MonoObject*)thread);
101 mono_finalizer_unlock ();
104 static gboolean suspend_finalizers = FALSE;
106 * actually, we might want to queue the finalize requests in a separate thread,
107 * but we need to be careful about the execution domain of the thread...
110 mono_gc_run_finalize (void *obj, void *data)
113 MonoObject *exc = NULL;
118 MonoMethod* finalizer = NULL;
119 MonoDomain *caller_domain = mono_domain_get ();
121 RuntimeInvokeFunction runtime_invoke;
123 // This function is called from the innards of the GC, so our best alternative for now is to do polling here
124 mono_threads_safepoint ();
126 o = (MonoObject*)((char*)obj + GPOINTER_TO_UINT (data));
128 if (mono_do_not_finalize) {
129 if (!mono_do_not_finalize_class_names)
132 size_t namespace_len = strlen (o->vtable->klass->name_space);
133 for (int i = 0; mono_do_not_finalize_class_names [i]; ++i) {
134 const char *name = mono_do_not_finalize_class_names [i];
135 if (strncmp (name, o->vtable->klass->name_space, namespace_len))
137 if (name [namespace_len] != '.')
139 if (strcmp (name + namespace_len + 1, o->vtable->klass->name))
146 g_log ("mono-gc-finalizers", G_LOG_LEVEL_DEBUG, "<%s at %p> Starting finalizer checks.", o->vtable->klass->name, o);
148 if (suspend_finalizers)
151 domain = o->vtable->domain;
154 mono_domain_finalizers_lock (domain);
156 o2 = (MonoObject *)g_hash_table_lookup (domain->finalizable_objects_hash, o);
158 mono_domain_finalizers_unlock (domain);
161 /* Already finalized somehow */
165 /* make sure the finalizer is not called again if the object is resurrected */
166 object_register_finalizer ((MonoObject *)obj, NULL, &error);
167 mono_error_assert_ok (&error); /* FIXME don't swallow the error */
170 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Registered finalizer as processed.", o->vtable->klass->name, o);
172 if (o->vtable->klass == mono_defaults.internal_thread_class) {
173 MonoInternalThread *t = (MonoInternalThread*)o;
175 if (mono_gc_is_finalizer_internal_thread (t))
176 /* Avoid finalizing ourselves */
179 if (t->threadpool_thread && finalizing_root_domain) {
180 /* Don't finalize threadpool threads when
181 shutting down - they're finalized when the
182 threadpool shuts down. */
183 add_thread_to_finalize (t);
188 if (o->vtable->klass->image == mono_defaults.corlib && !strcmp (o->vtable->klass->name, "DynamicMethod") && finalizing_root_domain) {
190 * These can't be finalized during unloading/shutdown, since that would
191 * free the native code which can still be referenced by other
193 * FIXME: This is not perfect, objects dying at the same time as
194 * dynamic methods can still reference them even when !shutdown.
199 if (mono_runtime_get_no_exec ())
202 /* speedup later... and use a timeout */
203 /* g_print ("Finalize run on %p %s.%s\n", o, mono_object_class (o)->name_space, mono_object_class (o)->name); */
205 /* Use _internal here, since this thread can enter a doomed appdomain */
206 mono_domain_set_internal (mono_object_domain (o));
208 /* delegates that have a native function pointer allocated are
209 * registered for finalization, but they don't have a Finalize
210 * method, because in most cases it's not needed and it's just a waste.
212 if (o->vtable->klass->delegate) {
213 MonoDelegate* del = (MonoDelegate*)o;
214 if (del->delegate_trampoline)
215 mono_delegate_free_ftnptr ((MonoDelegate*)o);
216 mono_domain_set_internal (caller_domain);
220 finalizer = mono_class_get_finalizer (o->vtable->klass);
222 /* If object has a CCW but has no finalizer, it was only
223 * registered for finalization in order to free the CCW.
224 * Else it needs the regular finalizer run.
225 * FIXME: what to do about ressurection and suppression
226 * of finalizer on object with CCW.
228 if (mono_marshal_free_ccw (o) && !finalizer) {
229 mono_domain_set_internal (caller_domain);
234 * To avoid the locking plus the other overhead of mono_runtime_invoke_checked (),
235 * create and precompile a wrapper which calls the finalize method using
239 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Compiling finalizer.", o->vtable->klass->name, o);
241 if (!domain->finalize_runtime_invoke) {
242 MonoMethod *invoke = mono_marshal_get_runtime_invoke (mono_class_get_method_from_name_flags (mono_defaults.object_class, "Finalize", 0, 0), TRUE);
244 domain->finalize_runtime_invoke = mono_compile_method (invoke);
247 runtime_invoke = (RuntimeInvokeFunction)domain->finalize_runtime_invoke;
249 mono_runtime_class_init_full (o->vtable, &error);
250 mono_error_raise_exception (&error); /* FIXME don't raise here */
252 if (G_UNLIKELY (MONO_GC_FINALIZE_INVOKE_ENABLED ())) {
253 MONO_GC_FINALIZE_INVOKE ((unsigned long)o, mono_object_get_size (o),
254 o->vtable->klass->name_space, o->vtable->klass->name);
258 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Calling finalizer.", o->vtable->klass->name, o);
260 runtime_invoke (o, NULL, &exc, NULL);
263 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Returned from finalizer.", o->vtable->klass->name, o);
266 mono_thread_internal_unhandled_exception (exc);
268 mono_domain_set_internal (caller_domain);
272 mono_gc_finalize_threadpool_threads (void)
275 while (threads_to_finalize) {
276 MonoInternalThread *thread = (MonoInternalThread*) mono_mlist_get_data (threads_to_finalize);
278 /* Force finalization of the thread. */
279 thread->threadpool_thread = FALSE;
280 mono_object_register_finalizer ((MonoObject*)thread, &error);
281 mono_error_assert_ok (&error); /* FIXME don't swallow the error */
283 mono_gc_run_finalize (thread, NULL);
285 threads_to_finalize = mono_mlist_next (threads_to_finalize);
290 mono_gc_out_of_memory (size_t size)
293 * we could allocate at program startup some memory that we could release
294 * back to the system at this point if we're really low on memory (ie, size is
295 * lower than the memory we set apart)
297 mono_raise_exception (mono_domain_get ()->out_of_memory_ex);
303 * Some of our objects may point to a different address than the address returned by GC_malloc()
304 * (because of the GetHashCode hack), but we need to pass the real address to register_finalizer.
305 * This also means that in the callback we need to adjust the pointer to get back the real
307 * We also need to be consistent in the use of the GC_debug* variants of malloc and register_finalizer,
308 * since that, too, can cause the underlying pointer to be offset.
311 object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*), MonoError *error)
315 mono_error_init (error);
318 mono_error_set_argument_null (error, "obj", "");
322 domain = obj->vtable->domain;
325 if (mono_domain_is_unloading (domain) && (callback != NULL))
327 * Can't register finalizers in a dying appdomain, since they
328 * could be invoked after the appdomain has been unloaded.
332 mono_domain_finalizers_lock (domain);
335 g_hash_table_insert (domain->finalizable_objects_hash, obj, obj);
337 g_hash_table_remove (domain->finalizable_objects_hash, obj);
339 mono_domain_finalizers_unlock (domain);
341 mono_gc_register_for_finalization (obj, callback);
342 #elif defined(HAVE_SGEN_GC)
344 * If we register finalizers for domains that are unloading we might
345 * end up running them while or after the domain is being cleared, so
346 * the objects will not be valid anymore.
348 if (!mono_domain_is_unloading (domain))
349 mono_gc_register_for_finalization (obj, callback);
354 * mono_object_register_finalizer:
355 * @obj: object to register
357 * Records that object @obj has a finalizer, this will call the
358 * Finalize method when the garbage collector disposes the object.
362 mono_object_register_finalizer (MonoObject *obj, MonoError *error)
364 /* g_print ("Registered finalizer on %p %s.%s\n", obj, mono_object_class (obj)->name_space, mono_object_class (obj)->name); */
365 object_register_finalizer (obj, mono_gc_run_finalize, error);
369 * mono_domain_finalize:
370 * @domain: the domain to finalize
371 * @timeout: msects to wait for the finalization to complete, -1 to wait indefinitely
373 * Request finalization of all finalizable objects inside @domain. Wait
374 * @timeout msecs for the finalization to complete.
376 * Returns: TRUE if succeeded, FALSE if there was a timeout
380 mono_domain_finalize (MonoDomain *domain, guint32 timeout)
382 DomainFinalizationReq *req;
385 MonoInternalThread *thread = mono_thread_internal_current ();
387 #if defined(__native_client__)
391 if (mono_thread_internal_current () == gc_thread)
392 /* We are called from inside a finalizer, not much we can do here */
396 * No need to create another thread 'cause the finalizer thread
397 * is still working and will take care of running the finalizers
403 /* We don't support domain finalization without a GC */
404 if (mono_gc_is_null ())
407 mono_gc_collect (mono_gc_max_generation ());
409 done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
410 if (done_event == NULL) {
414 req = g_new0 (DomainFinalizationReq, 1);
415 req->domain = domain;
416 req->done_event = done_event;
418 if (domain == mono_get_root_domain ())
419 finalizing_root_domain = TRUE;
421 mono_finalizer_lock ();
423 domains_to_finalize = g_slist_append (domains_to_finalize, req);
425 mono_finalizer_unlock ();
427 /* Tell the finalizer thread to finalize this appdomain */
428 mono_gc_finalize_notify ();
434 res = guarded_wait (done_event, timeout, TRUE);
435 /* printf ("WAIT RES: %d.\n", res); */
437 if (res == WAIT_IO_COMPLETION) {
438 if ((thread->state & (ThreadState_StopRequested | ThreadState_SuspendRequested)) != 0)
440 } else if (res == WAIT_TIMEOUT) {
441 /* We leak the handle here */
448 CloseHandle (done_event);
450 if (domain == mono_get_root_domain ()) {
451 mono_threadpool_ms_cleanup ();
452 mono_gc_finalize_threadpool_threads ();
459 ves_icall_System_GC_InternalCollect (int generation)
461 mono_gc_collect (generation);
465 ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection)
468 mono_gc_collect (mono_gc_max_generation ());
469 return mono_gc_get_used_size ();
473 ves_icall_System_GC_KeepAlive (MonoObject *obj)
481 ves_icall_System_GC_ReRegisterForFinalize (MonoObject *obj)
485 MONO_CHECK_ARG_NULL (obj,);
487 object_register_finalizer (obj, mono_gc_run_finalize, &error);
488 mono_error_set_pending_exception (&error);
492 ves_icall_System_GC_SuppressFinalize (MonoObject *obj)
496 MONO_CHECK_ARG_NULL (obj,);
498 /* delegates have no finalizers, but we register them to deal with the
499 * unmanaged->managed trampoline. We don't let the user suppress it
500 * otherwise we'd leak it.
502 if (obj->vtable->klass->delegate)
505 /* FIXME: Need to handle case where obj has COM Callable Wrapper
506 * generated for it that needs cleaned up, but user wants to suppress
507 * their derived object finalizer. */
509 object_register_finalizer (obj, NULL, &error);
510 mono_error_set_pending_exception (&error);
514 ves_icall_System_GC_WaitForPendingFinalizers (void)
516 if (mono_gc_is_null ())
519 if (!mono_gc_pending_finalizers ())
522 if (mono_thread_internal_current () == gc_thread)
523 /* Avoid deadlocks */
527 If the finalizer thread is not live, lets pretend no finalizers are pending since the current thread might
528 be the one responsible for starting it up.
530 if (gc_thread == NULL)
533 ResetEvent (pending_done_event);
534 mono_gc_finalize_notify ();
535 /* g_print ("Waiting for pending finalizers....\n"); */
536 guarded_wait (pending_done_event, INFINITE, TRUE);
537 /* g_print ("Done pending....\n"); */
541 ves_icall_System_GC_register_ephemeron_array (MonoObject *array)
544 if (!mono_gc_ephemeron_array_add (array)) {
545 mono_set_pending_exception (mono_object_domain (array)->out_of_memory_ex);
552 ves_icall_System_GC_get_ephemeron_tombstone (void)
554 return mono_domain_get ()->ephemeron_tombstone;
558 ves_icall_System_GCHandle_GetTarget (guint32 handle)
560 return mono_gchandle_get_target (handle);
564 * if type == -1, change the target of the handle, otherwise allocate a new handle.
567 ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type)
570 mono_gchandle_set_target (handle, obj);
571 /* the handle doesn't change */
576 return mono_gchandle_new_weakref (obj, FALSE);
577 case HANDLE_WEAK_TRACK:
578 return mono_gchandle_new_weakref (obj, TRUE);
580 return mono_gchandle_new (obj, FALSE);
582 return mono_gchandle_new (obj, TRUE);
584 g_assert_not_reached ();
590 ves_icall_System_GCHandle_FreeHandle (guint32 handle)
592 mono_gchandle_free (handle);
596 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
600 if (MONO_GC_HANDLE_TYPE (handle) != HANDLE_PINNED)
602 obj = mono_gchandle_get_target (handle);
604 MonoClass *klass = mono_object_class (obj);
605 if (klass == mono_defaults.string_class) {
606 return mono_string_chars ((MonoString*)obj);
607 } else if (klass->rank) {
608 return mono_array_addr ((MonoArray*)obj, char, 0);
610 /* the C# code will check and throw the exception */
611 /* FIXME: missing !klass->blittable test, see bug #61134 */
612 if ((klass->flags & TYPE_ATTRIBUTE_LAYOUT_MASK) == TYPE_ATTRIBUTE_AUTO_LAYOUT)
614 return (char*)obj + sizeof (MonoObject);
621 mono_gc_GCHandle_CheckCurrentDomain (guint32 gchandle)
623 return mono_gchandle_is_in_domain (gchandle, mono_domain_get ());
626 static MonoCoopSem finalizer_sem;
627 static volatile gboolean finished=FALSE;
630 mono_gc_finalize_notify (void)
633 g_message ( "%s: prodding finalizer", __func__);
636 if (mono_gc_is_null ())
639 mono_coop_sem_post (&finalizer_sem);
643 This is the number of entries allowed in the hazard free queue before
644 we explicitly cycle the finalizer thread to trigger pumping the queue.
646 It was picked empirically by running the corlib test suite in a stress
647 scenario where all hazard entries are queued.
649 In this extreme scenario we double the number of times we cycle the finalizer
650 thread compared to just GC calls.
652 Entries are usually in the order of 100's of bytes each, so we're limiting
653 floating garbage to be in the order of a dozen kb.
655 static gboolean finalizer_thread_pulsed;
656 #define HAZARD_QUEUE_OVERFLOW_SIZE 20
659 hazard_free_queue_is_too_big (size_t size)
661 if (size < HAZARD_QUEUE_OVERFLOW_SIZE)
664 if (finalizer_thread_pulsed || InterlockedCompareExchange (&finalizer_thread_pulsed, TRUE, FALSE))
667 mono_gc_finalize_notify ();
671 hazard_free_queue_pump (void)
673 mono_thread_hazardous_try_free_all ();
674 finalizer_thread_pulsed = FALSE;
680 collect_objects (gpointer key, gpointer value, gpointer user_data)
682 GPtrArray *arr = (GPtrArray*)user_data;
683 g_ptr_array_add (arr, key);
689 * finalize_domain_objects:
691 * Run the finalizers of all finalizable objects in req->domain.
694 finalize_domain_objects (DomainFinalizationReq *req)
696 MonoDomain *domain = req->domain;
699 #define NUM_FOBJECTS 64
700 MonoObject *to_finalize [NUM_FOBJECTS];
704 /* Process finalizers which are already in the queue */
705 mono_gc_invoke_finalizers ();
708 while (g_hash_table_size (domain->finalizable_objects_hash) > 0) {
712 * Since the domain is unloading, nobody is allowed to put
713 * new entries into the hash table. But finalize_object might
714 * remove entries from the hash table, so we make a copy.
716 objs = g_ptr_array_new ();
717 g_hash_table_foreach (domain->finalizable_objects_hash, collect_objects, objs);
718 /* printf ("FINALIZING %d OBJECTS.\n", objs->len); */
720 for (i = 0; i < objs->len; ++i) {
721 MonoObject *o = (MonoObject*)g_ptr_array_index (objs, i);
722 /* FIXME: Avoid finalizing threads, etc */
723 mono_gc_run_finalize (o, 0);
726 g_ptr_array_free (objs, TRUE);
728 #elif defined(HAVE_SGEN_GC)
729 while ((count = mono_gc_finalizers_for_domain (domain, to_finalize, NUM_FOBJECTS))) {
731 for (i = 0; i < count; ++i) {
732 mono_gc_run_finalize (to_finalize [i], 0);
737 /* cleanup the reference queue */
738 reference_queue_clear_for_domain (domain);
740 /* printf ("DONE.\n"); */
741 SetEvent (req->done_event);
743 /* The event is closed in mono_domain_finalize if we get here */
748 finalizer_thread (gpointer unused)
750 gboolean wait = TRUE;
752 /* Register a hazard free queue pump callback */
753 mono_hazard_pointer_install_free_queue_size_callback (hazard_free_queue_is_too_big);
756 /* Wait to be notified that there's at least one
760 g_assert (mono_domain_get () == mono_get_root_domain ());
761 mono_gc_set_skip_thread (TRUE);
764 /* An alertable wait is required so this thread can be suspended on windows */
765 mono_coop_sem_wait (&finalizer_sem, MONO_SEM_FLAGS_ALERTABLE);
769 mono_gc_set_skip_thread (FALSE);
771 mono_threads_perform_thread_dump ();
773 mono_console_handle_async_ops ();
775 mono_attach_maybe_start ();
777 if (domains_to_finalize) {
778 mono_finalizer_lock ();
779 if (domains_to_finalize) {
780 DomainFinalizationReq *req = (DomainFinalizationReq *)domains_to_finalize->data;
781 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
782 mono_finalizer_unlock ();
784 finalize_domain_objects (req);
786 mono_finalizer_unlock ();
790 /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup),
791 * before the domain is unloaded.
793 mono_gc_invoke_finalizers ();
795 mono_threads_join_threads ();
797 reference_queue_proccess_all ();
799 hazard_free_queue_pump ();
801 /* Avoid posting the pending done event until there are pending finalizers */
802 if (mono_coop_sem_timedwait (&finalizer_sem, 0, MONO_SEM_FLAGS_NONE) == 0) {
803 /* Don't wait again at the start of the loop */
806 SetEvent (pending_done_event);
810 mono_finalizer_lock ();
811 finalizer_thread_exited = TRUE;
812 mono_coop_cond_signal (&exited_cond);
813 mono_finalizer_unlock ();
818 #ifndef LAZY_GC_THREAD_CREATION
822 mono_gc_init_finalizer_thread (void)
824 gc_thread = mono_thread_create_internal (mono_domain_get (), finalizer_thread, NULL, FALSE, 0);
825 ves_icall_System_Threading_Thread_SetName_internal (gc_thread, mono_string_new (mono_domain_get (), "Finalizer"));
831 mono_coop_mutex_init_recursive (&finalizer_mutex);
832 mono_coop_mutex_init_recursive (&reference_queue_mutex);
834 mono_counters_register ("Minor GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.minor_gc_count);
835 mono_counters_register ("Major GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.major_gc_count);
836 mono_counters_register ("Minor GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.minor_gc_time);
837 mono_counters_register ("Major GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time);
838 mono_counters_register ("Major GC time concurrent", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time_concurrent);
840 mono_gc_base_init ();
842 if (mono_gc_is_disabled ()) {
847 pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
848 g_assert (pending_done_event);
849 mono_coop_cond_init (&exited_cond);
850 mono_coop_sem_init (&finalizer_sem, 0);
852 #ifndef LAZY_GC_THREAD_CREATION
853 mono_gc_init_finalizer_thread ();
858 mono_gc_cleanup (void)
861 g_message ("%s: cleaning up finalizer", __func__);
864 if (mono_gc_is_null ())
869 if (mono_thread_internal_current () != gc_thread) {
870 gboolean timed_out = FALSE;
871 guint32 start_ticks = mono_msec_ticks ();
872 guint32 end_ticks = start_ticks + 2000;
874 mono_gc_finalize_notify ();
875 /* Finishing the finalizer thread, so wait a little bit... */
876 /* MS seems to wait for about 2 seconds */
877 while (!finalizer_thread_exited) {
878 guint32 current_ticks = mono_msec_ticks ();
881 if (current_ticks >= end_ticks)
884 timeout = end_ticks - current_ticks;
885 mono_finalizer_lock ();
886 if (!finalizer_thread_exited)
887 mono_coop_cond_timedwait (&exited_cond, &finalizer_mutex, timeout);
888 mono_finalizer_unlock ();
891 if (!finalizer_thread_exited) {
894 /* Set a flag which the finalizer thread can check */
895 suspend_finalizers = TRUE;
897 /* Try to abort the thread, in the hope that it is running managed code */
898 mono_thread_internal_stop (gc_thread);
900 /* Wait for it to stop */
901 ret = guarded_wait (gc_thread->handle, 100, TRUE);
903 if (ret == WAIT_TIMEOUT) {
905 * The finalizer thread refused to die. There is not much we
906 * can do here, since the runtime is shutting down so the
907 * state the finalizer thread depends on will vanish.
909 g_warning ("Shutting down finalizer thread timed out.");
917 /* Wait for the thread to actually exit */
918 ret = guarded_wait (gc_thread->handle, INFINITE, TRUE);
919 g_assert (ret == WAIT_OBJECT_0);
921 mono_thread_join (GUINT_TO_POINTER (gc_thread->tid));
923 g_assert (finalizer_thread_exited);
926 mono_gc_base_cleanup ();
929 mono_reference_queue_cleanup ();
931 mono_coop_mutex_destroy (&finalizer_mutex);
932 mono_coop_mutex_destroy (&reference_queue_mutex);
936 mono_gc_is_finalizer_internal_thread (MonoInternalThread *thread)
938 return thread == gc_thread;
942 * mono_gc_is_finalizer_thread:
943 * @thread: the thread to test.
945 * In Mono objects are finalized asynchronously on a separate thread.
946 * This routine tests whether the @thread argument represents the
947 * finalization thread.
949 * Returns: TRUE if @thread is the finalization thread.
952 mono_gc_is_finalizer_thread (MonoThread *thread)
954 return mono_gc_is_finalizer_internal_thread (thread->internal_thread);
957 #if defined(__MACH__)
958 static pthread_t mach_exception_thread;
961 mono_gc_register_mach_exception_thread (pthread_t thread)
963 mach_exception_thread = thread;
967 mono_gc_get_mach_exception_thread (void)
969 return mach_exception_thread;
973 static MonoReferenceQueue *ref_queues;
976 ref_list_remove_element (RefQueueEntry **prev, RefQueueEntry *element)
979 /* Guard if head is changed concurrently. */
980 while (*prev != element)
981 prev = &(*prev)->next;
982 } while (prev && InterlockedCompareExchangePointer ((volatile gpointer *)prev, element->next, element) != element);
986 ref_list_push (RefQueueEntry **head, RefQueueEntry *value)
988 RefQueueEntry *current;
991 value->next = current;
992 STORE_STORE_FENCE; /*Must make sure the previous store is visible before the CAS. */
993 } while (InterlockedCompareExchangePointer ((volatile gpointer *)head, value, current) != current);
997 reference_queue_proccess (MonoReferenceQueue *queue)
999 RefQueueEntry **iter = &queue->queue;
1000 RefQueueEntry *entry;
1001 while ((entry = *iter)) {
1002 if (queue->should_be_deleted || !mono_gchandle_get_target (entry->gchandle)) {
1003 mono_gchandle_free ((guint32)entry->gchandle);
1004 ref_list_remove_element (iter, entry);
1005 queue->callback (entry->user_data);
1008 iter = &entry->next;
1014 reference_queue_proccess_all (void)
1016 MonoReferenceQueue **iter;
1017 MonoReferenceQueue *queue = ref_queues;
1018 for (; queue; queue = queue->next)
1019 reference_queue_proccess (queue);
1022 mono_coop_mutex_lock (&reference_queue_mutex);
1023 for (iter = &ref_queues; *iter;) {
1025 if (!queue->should_be_deleted) {
1026 iter = &queue->next;
1030 mono_coop_mutex_unlock (&reference_queue_mutex);
1031 reference_queue_proccess (queue);
1034 *iter = queue->next;
1037 mono_coop_mutex_unlock (&reference_queue_mutex);
1041 mono_reference_queue_cleanup (void)
1043 MonoReferenceQueue *queue = ref_queues;
1044 for (; queue; queue = queue->next)
1045 queue->should_be_deleted = TRUE;
1046 reference_queue_proccess_all ();
1050 reference_queue_clear_for_domain (MonoDomain *domain)
1052 MonoReferenceQueue *queue = ref_queues;
1053 for (; queue; queue = queue->next) {
1054 RefQueueEntry **iter = &queue->queue;
1055 RefQueueEntry *entry;
1056 while ((entry = *iter)) {
1057 if (entry->domain == domain) {
1058 mono_gchandle_free ((guint32)entry->gchandle);
1059 ref_list_remove_element (iter, entry);
1060 queue->callback (entry->user_data);
1063 iter = &entry->next;
1069 * mono_gc_reference_queue_new:
1070 * @callback callback used when processing collected entries.
1072 * Create a new reference queue used to process collected objects.
1073 * A reference queue let you add a pair of (managed object, user data)
1074 * using the mono_gc_reference_queue_add method.
1076 * Once the managed object is collected @callback will be called
1077 * in the finalizer thread with 'user data' as argument.
1079 * The callback is called from the finalizer thread without any locks held.
1080 * When a AppDomain is unloaded, all callbacks for objects belonging to it
1083 * @returns the new queue.
1086 mono_gc_reference_queue_new (mono_reference_queue_callback callback)
1088 MonoReferenceQueue *res = g_new0 (MonoReferenceQueue, 1);
1089 res->callback = callback;
1091 mono_coop_mutex_lock (&reference_queue_mutex);
1092 res->next = ref_queues;
1094 mono_coop_mutex_unlock (&reference_queue_mutex);
1100 * mono_gc_reference_queue_add:
1101 * @queue the queue to add the reference to.
1102 * @obj the object to be watched for collection
1103 * @user_data parameter to be passed to the queue callback
1105 * Queue an object to be watched for collection, when the @obj is
1106 * collected, the callback that was registered for the @queue will
1107 * be invoked with @user_data as argument.
1109 * @returns false if the queue is scheduled to be freed.
1112 mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *user_data)
1115 RefQueueEntry *entry;
1116 if (queue->should_be_deleted)
1119 entry = g_new0 (RefQueueEntry, 1);
1120 entry->user_data = user_data;
1121 entry->domain = mono_object_domain (obj);
1123 entry->gchandle = mono_gchandle_new_weakref (obj, TRUE);
1124 mono_object_register_finalizer (obj, &error);
1125 mono_error_assert_ok (&error);
1127 ref_list_push (&queue->queue, entry);
1132 * mono_gc_reference_queue_free:
1133 * @queue the queue that should be freed.
1135 * This operation signals that @queue should be freed. This operation is deferred
1136 * as it happens on the finalizer thread.
1138 * After this call, no further objects can be queued. It's the responsibility of the
1139 * caller to make sure that no further attempt to access queue will be made.
1142 mono_gc_reference_queue_free (MonoReferenceQueue *queue)
1144 queue->should_be_deleted = TRUE;