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>
47 typedef struct DomainFinalizationReq {
50 } DomainFinalizationReq;
52 static gboolean gc_disabled = FALSE;
54 static gboolean finalizing_root_domain = FALSE;
56 gboolean log_finalizers = FALSE;
57 gboolean mono_do_not_finalize = FALSE;
58 gchar **mono_do_not_finalize_class_names = NULL;
60 #define mono_finalizer_lock() mono_coop_mutex_lock (&finalizer_mutex)
61 #define mono_finalizer_unlock() mono_coop_mutex_unlock (&finalizer_mutex)
62 static MonoCoopMutex finalizer_mutex;
63 static MonoCoopMutex reference_queue_mutex;
65 static GSList *domains_to_finalize= NULL;
66 static MonoMList *threads_to_finalize = NULL;
68 static gboolean finalizer_thread_exited;
69 /* Uses finalizer_mutex */
70 static MonoCoopCond exited_cond;
72 static MonoInternalThread *gc_thread;
74 static void object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*));
76 static void reference_queue_proccess_all (void);
77 static void mono_reference_queue_cleanup (void);
78 static void reference_queue_clear_for_domain (MonoDomain *domain);
79 static HANDLE pending_done_event;
82 guarded_wait (HANDLE handle, guint32 timeout, gboolean alertable)
86 MONO_PREPARE_BLOCKING;
87 result = WaitForSingleObjectEx (handle, timeout, alertable);
94 add_thread_to_finalize (MonoInternalThread *thread)
96 mono_finalizer_lock ();
97 if (!threads_to_finalize)
98 MONO_GC_REGISTER_ROOT_SINGLE (threads_to_finalize, MONO_ROOT_SOURCE_FINALIZER_QUEUE, "finalizable threads list");
99 threads_to_finalize = mono_mlist_append (threads_to_finalize, (MonoObject*)thread);
100 mono_finalizer_unlock ();
103 static gboolean suspend_finalizers = FALSE;
105 * actually, we might want to queue the finalize requests in a separate thread,
106 * but we need to be careful about the execution domain of the thread...
109 mono_gc_run_finalize (void *obj, void *data)
111 MonoObject *exc = NULL;
116 MonoMethod* finalizer = NULL;
117 MonoDomain *caller_domain = mono_domain_get ();
119 RuntimeInvokeFunction runtime_invoke;
121 // This function is called from the innards of the GC, so our best alternative for now is to do polling here
122 mono_threads_safepoint ();
124 o = (MonoObject*)((char*)obj + GPOINTER_TO_UINT (data));
126 if (mono_do_not_finalize) {
127 if (!mono_do_not_finalize_class_names)
130 size_t namespace_len = strlen (o->vtable->klass->name_space);
131 for (int i = 0; mono_do_not_finalize_class_names [i]; ++i) {
132 const char *name = mono_do_not_finalize_class_names [i];
133 if (strncmp (name, o->vtable->klass->name_space, namespace_len))
135 if (name [namespace_len] != '.')
137 if (strcmp (name + namespace_len + 1, o->vtable->klass->name))
144 g_log ("mono-gc-finalizers", G_LOG_LEVEL_DEBUG, "<%s at %p> Starting finalizer checks.", o->vtable->klass->name, o);
146 if (suspend_finalizers)
149 domain = o->vtable->domain;
152 mono_domain_finalizers_lock (domain);
154 o2 = g_hash_table_lookup (domain->finalizable_objects_hash, o);
156 mono_domain_finalizers_unlock (domain);
159 /* Already finalized somehow */
163 /* make sure the finalizer is not called again if the object is resurrected */
164 object_register_finalizer (obj, NULL);
167 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Registered finalizer as processed.", o->vtable->klass->name, o);
169 if (o->vtable->klass == mono_defaults.internal_thread_class) {
170 MonoInternalThread *t = (MonoInternalThread*)o;
172 if (mono_gc_is_finalizer_internal_thread (t))
173 /* Avoid finalizing ourselves */
176 if (t->threadpool_thread && finalizing_root_domain) {
177 /* Don't finalize threadpool threads when
178 shutting down - they're finalized when the
179 threadpool shuts down. */
180 add_thread_to_finalize (t);
185 if (o->vtable->klass->image == mono_defaults.corlib && !strcmp (o->vtable->klass->name, "DynamicMethod") && finalizing_root_domain) {
187 * These can't be finalized during unloading/shutdown, since that would
188 * free the native code which can still be referenced by other
190 * FIXME: This is not perfect, objects dying at the same time as
191 * dynamic methods can still reference them even when !shutdown.
196 if (mono_runtime_get_no_exec ())
199 /* speedup later... and use a timeout */
200 /* g_print ("Finalize run on %p %s.%s\n", o, mono_object_class (o)->name_space, mono_object_class (o)->name); */
202 /* Use _internal here, since this thread can enter a doomed appdomain */
203 mono_domain_set_internal (mono_object_domain (o));
205 /* delegates that have a native function pointer allocated are
206 * registered for finalization, but they don't have a Finalize
207 * method, because in most cases it's not needed and it's just a waste.
209 if (o->vtable->klass->delegate) {
210 MonoDelegate* del = (MonoDelegate*)o;
211 if (del->delegate_trampoline)
212 mono_delegate_free_ftnptr ((MonoDelegate*)o);
213 mono_domain_set_internal (caller_domain);
217 finalizer = mono_class_get_finalizer (o->vtable->klass);
219 /* If object has a CCW but has no finalizer, it was only
220 * registered for finalization in order to free the CCW.
221 * Else it needs the regular finalizer run.
222 * FIXME: what to do about ressurection and suppression
223 * of finalizer on object with CCW.
225 if (mono_marshal_free_ccw (o) && !finalizer) {
226 mono_domain_set_internal (caller_domain);
231 * To avoid the locking plus the other overhead of mono_runtime_invoke (),
232 * create and precompile a wrapper which calls the finalize method using
236 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Compiling finalizer.", o->vtable->klass->name, o);
238 if (!domain->finalize_runtime_invoke) {
239 MonoMethod *invoke = mono_marshal_get_runtime_invoke (mono_class_get_method_from_name_flags (mono_defaults.object_class, "Finalize", 0, 0), TRUE, FALSE);
241 domain->finalize_runtime_invoke = mono_compile_method (invoke);
244 runtime_invoke = domain->finalize_runtime_invoke;
246 mono_runtime_class_init (o->vtable);
248 if (G_UNLIKELY (MONO_GC_FINALIZE_INVOKE_ENABLED ())) {
249 MONO_GC_FINALIZE_INVOKE ((unsigned long)o, mono_object_get_size (o),
250 o->vtable->klass->name_space, o->vtable->klass->name);
254 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Calling finalizer.", o->vtable->klass->name, o);
256 runtime_invoke (o, NULL, &exc, NULL);
259 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Returned from finalizer.", o->vtable->klass->name, o);
262 mono_thread_internal_unhandled_exception (exc);
264 mono_domain_set_internal (caller_domain);
268 mono_gc_finalize_threadpool_threads (void)
270 while (threads_to_finalize) {
271 MonoInternalThread *thread = (MonoInternalThread*) mono_mlist_get_data (threads_to_finalize);
273 /* Force finalization of the thread. */
274 thread->threadpool_thread = FALSE;
275 mono_object_register_finalizer ((MonoObject*)thread);
277 mono_gc_run_finalize (thread, NULL);
279 threads_to_finalize = mono_mlist_next (threads_to_finalize);
284 mono_gc_out_of_memory (size_t size)
287 * we could allocate at program startup some memory that we could release
288 * back to the system at this point if we're really low on memory (ie, size is
289 * lower than the memory we set apart)
291 mono_raise_exception (mono_domain_get ()->out_of_memory_ex);
297 * Some of our objects may point to a different address than the address returned by GC_malloc()
298 * (because of the GetHashCode hack), but we need to pass the real address to register_finalizer.
299 * This also means that in the callback we need to adjust the pointer to get back the real
301 * We also need to be consistent in the use of the GC_debug* variants of malloc and register_finalizer,
302 * since that, too, can cause the underlying pointer to be offset.
305 object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*))
310 mono_raise_exception (mono_get_exception_argument_null ("obj"));
312 domain = obj->vtable->domain;
315 if (mono_domain_is_unloading (domain) && (callback != NULL))
317 * Can't register finalizers in a dying appdomain, since they
318 * could be invoked after the appdomain has been unloaded.
322 mono_domain_finalizers_lock (domain);
325 g_hash_table_insert (domain->finalizable_objects_hash, obj, obj);
327 g_hash_table_remove (domain->finalizable_objects_hash, obj);
329 mono_domain_finalizers_unlock (domain);
331 mono_gc_register_for_finalization (obj, callback);
332 #elif defined(HAVE_SGEN_GC)
334 * If we register finalizers for domains that are unloading we might
335 * end up running them while or after the domain is being cleared, so
336 * the objects will not be valid anymore.
338 if (!mono_domain_is_unloading (domain))
339 mono_gc_register_for_finalization (obj, callback);
344 * mono_object_register_finalizer:
345 * @obj: object to register
347 * Records that object @obj has a finalizer, this will call the
348 * Finalize method when the garbage collector disposes the object.
352 mono_object_register_finalizer (MonoObject *obj)
354 /* g_print ("Registered finalizer on %p %s.%s\n", obj, mono_object_class (obj)->name_space, mono_object_class (obj)->name); */
355 object_register_finalizer (obj, mono_gc_run_finalize);
359 * mono_domain_finalize:
360 * @domain: the domain to finalize
361 * @timeout: msects to wait for the finalization to complete, -1 to wait indefinitely
363 * Request finalization of all finalizable objects inside @domain. Wait
364 * @timeout msecs for the finalization to complete.
366 * Returns: TRUE if succeeded, FALSE if there was a timeout
370 mono_domain_finalize (MonoDomain *domain, guint32 timeout)
372 DomainFinalizationReq *req;
375 MonoInternalThread *thread = mono_thread_internal_current ();
377 #if defined(__native_client__)
381 if (mono_thread_internal_current () == gc_thread)
382 /* We are called from inside a finalizer, not much we can do here */
386 * No need to create another thread 'cause the finalizer thread
387 * is still working and will take care of running the finalizers
393 /* We don't support domain finalization without a GC */
394 if (mono_gc_is_null ())
397 mono_gc_collect (mono_gc_max_generation ());
399 done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
400 if (done_event == NULL) {
404 req = g_new0 (DomainFinalizationReq, 1);
405 req->domain = domain;
406 req->done_event = done_event;
408 if (domain == mono_get_root_domain ())
409 finalizing_root_domain = TRUE;
411 mono_finalizer_lock ();
413 domains_to_finalize = g_slist_append (domains_to_finalize, req);
415 mono_finalizer_unlock ();
417 /* Tell the finalizer thread to finalize this appdomain */
418 mono_gc_finalize_notify ();
424 res = guarded_wait (done_event, timeout, TRUE);
425 /* printf ("WAIT RES: %d.\n", res); */
427 if (res == WAIT_IO_COMPLETION) {
428 if ((thread->state & (ThreadState_StopRequested | ThreadState_SuspendRequested)) != 0)
430 } else if (res == WAIT_TIMEOUT) {
431 /* We leak the handle here */
438 CloseHandle (done_event);
440 if (domain == mono_get_root_domain ()) {
441 mono_threadpool_ms_cleanup ();
442 mono_gc_finalize_threadpool_threads ();
449 ves_icall_System_GC_InternalCollect (int generation)
451 mono_gc_collect (generation);
455 ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection)
458 mono_gc_collect (mono_gc_max_generation ());
459 return mono_gc_get_used_size ();
463 ves_icall_System_GC_KeepAlive (MonoObject *obj)
471 ves_icall_System_GC_ReRegisterForFinalize (MonoObject *obj)
473 MONO_CHECK_ARG_NULL (obj,);
475 object_register_finalizer (obj, mono_gc_run_finalize);
479 ves_icall_System_GC_SuppressFinalize (MonoObject *obj)
481 MONO_CHECK_ARG_NULL (obj,);
483 /* delegates have no finalizers, but we register them to deal with the
484 * unmanaged->managed trampoline. We don't let the user suppress it
485 * otherwise we'd leak it.
487 if (obj->vtable->klass->delegate)
490 /* FIXME: Need to handle case where obj has COM Callable Wrapper
491 * generated for it that needs cleaned up, but user wants to suppress
492 * their derived object finalizer. */
494 object_register_finalizer (obj, NULL);
498 ves_icall_System_GC_WaitForPendingFinalizers (void)
500 if (mono_gc_is_null ())
503 if (!mono_gc_pending_finalizers ())
506 if (mono_thread_internal_current () == gc_thread)
507 /* Avoid deadlocks */
511 If the finalizer thread is not live, lets pretend no finalizers are pending since the current thread might
512 be the one responsible for starting it up.
514 if (gc_thread == NULL)
517 ResetEvent (pending_done_event);
518 mono_gc_finalize_notify ();
519 /* g_print ("Waiting for pending finalizers....\n"); */
520 guarded_wait (pending_done_event, INFINITE, TRUE);
521 /* g_print ("Done pending....\n"); */
525 ves_icall_System_GC_register_ephemeron_array (MonoObject *array)
528 if (!mono_gc_ephemeron_array_add (array)) {
529 mono_set_pending_exception (mono_object_domain (array)->out_of_memory_ex);
536 ves_icall_System_GC_get_ephemeron_tombstone (void)
538 return mono_domain_get ()->ephemeron_tombstone;
541 #define mono_allocator_lock() mono_os_mutex_lock (&allocator_section)
542 #define mono_allocator_unlock() mono_os_mutex_unlock (&allocator_section)
543 static mono_mutex_t allocator_section;
546 ves_icall_System_GCHandle_GetTarget (guint32 handle)
548 return mono_gchandle_get_target (handle);
552 * if type == -1, change the target of the handle, otherwise allocate a new handle.
555 ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type)
558 mono_gchandle_set_target (handle, obj);
559 /* the handle doesn't change */
564 return mono_gchandle_new_weakref (obj, FALSE);
565 case HANDLE_WEAK_TRACK:
566 return mono_gchandle_new_weakref (obj, TRUE);
568 return mono_gchandle_new (obj, FALSE);
570 return mono_gchandle_new (obj, TRUE);
572 g_assert_not_reached ();
578 ves_icall_System_GCHandle_FreeHandle (guint32 handle)
580 mono_gchandle_free (handle);
584 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
588 if (MONO_GC_HANDLE_TYPE (handle) != HANDLE_PINNED)
590 obj = mono_gchandle_get_target (handle);
592 MonoClass *klass = mono_object_class (obj);
593 if (klass == mono_defaults.string_class) {
594 return mono_string_chars ((MonoString*)obj);
595 } else if (klass->rank) {
596 return mono_array_addr ((MonoArray*)obj, char, 0);
598 /* the C# code will check and throw the exception */
599 /* FIXME: missing !klass->blittable test, see bug #61134 */
600 if ((klass->flags & TYPE_ATTRIBUTE_LAYOUT_MASK) == TYPE_ATTRIBUTE_AUTO_LAYOUT)
602 return (char*)obj + sizeof (MonoObject);
609 ves_icall_Mono_Runtime_SetGCAllowSynchronousMajor (MonoBoolean flag)
611 return mono_gc_set_allow_synchronous_major (flag);
615 mono_gc_GCHandle_CheckCurrentDomain (guint32 gchandle)
617 return mono_gchandle_is_in_domain (gchandle, mono_domain_get ());
620 static MonoCoopSem finalizer_sem;
621 static volatile gboolean finished=FALSE;
624 mono_gc_finalize_notify (void)
627 g_message ( "%s: prodding finalizer", __func__);
630 if (mono_gc_is_null ())
633 mono_coop_sem_post (&finalizer_sem);
639 collect_objects (gpointer key, gpointer value, gpointer user_data)
641 GPtrArray *arr = (GPtrArray*)user_data;
642 g_ptr_array_add (arr, key);
648 * finalize_domain_objects:
650 * Run the finalizers of all finalizable objects in req->domain.
653 finalize_domain_objects (DomainFinalizationReq *req)
655 MonoDomain *domain = req->domain;
658 #define NUM_FOBJECTS 64
659 MonoObject *to_finalize [NUM_FOBJECTS];
663 /* Process finalizers which are already in the queue */
664 mono_gc_invoke_finalizers ();
667 while (g_hash_table_size (domain->finalizable_objects_hash) > 0) {
671 * Since the domain is unloading, nobody is allowed to put
672 * new entries into the hash table. But finalize_object might
673 * remove entries from the hash table, so we make a copy.
675 objs = g_ptr_array_new ();
676 g_hash_table_foreach (domain->finalizable_objects_hash, collect_objects, objs);
677 /* printf ("FINALIZING %d OBJECTS.\n", objs->len); */
679 for (i = 0; i < objs->len; ++i) {
680 MonoObject *o = (MonoObject*)g_ptr_array_index (objs, i);
681 /* FIXME: Avoid finalizing threads, etc */
682 mono_gc_run_finalize (o, 0);
685 g_ptr_array_free (objs, TRUE);
687 #elif defined(HAVE_SGEN_GC)
688 while ((count = mono_gc_finalizers_for_domain (domain, to_finalize, NUM_FOBJECTS))) {
690 for (i = 0; i < count; ++i) {
691 mono_gc_run_finalize (to_finalize [i], 0);
696 /* cleanup the reference queue */
697 reference_queue_clear_for_domain (domain);
699 /* printf ("DONE.\n"); */
700 SetEvent (req->done_event);
702 /* The event is closed in mono_domain_finalize if we get here */
707 finalizer_thread (gpointer unused)
709 gboolean wait = TRUE;
712 /* Wait to be notified that there's at least one
716 g_assert (mono_domain_get () == mono_get_root_domain ());
717 mono_gc_set_skip_thread (TRUE);
720 /* An alertable wait is required so this thread can be suspended on windows */
721 mono_coop_sem_wait (&finalizer_sem, MONO_SEM_FLAGS_ALERTABLE);
725 mono_gc_set_skip_thread (FALSE);
727 mono_threads_perform_thread_dump ();
729 mono_console_handle_async_ops ();
731 mono_attach_maybe_start ();
733 if (domains_to_finalize) {
734 mono_finalizer_lock ();
735 if (domains_to_finalize) {
736 DomainFinalizationReq *req = domains_to_finalize->data;
737 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
738 mono_finalizer_unlock ();
740 finalize_domain_objects (req);
742 mono_finalizer_unlock ();
746 /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup),
747 * before the domain is unloaded.
749 mono_gc_invoke_finalizers ();
751 mono_threads_join_threads ();
753 reference_queue_proccess_all ();
755 /* Avoid posting the pending done event until there are pending finalizers */
756 if (mono_coop_sem_timedwait (&finalizer_sem, 0, MONO_SEM_FLAGS_NONE) == 0) {
757 /* Don't wait again at the start of the loop */
760 SetEvent (pending_done_event);
764 mono_finalizer_lock ();
765 finalizer_thread_exited = TRUE;
766 mono_coop_cond_signal (&exited_cond);
767 mono_finalizer_unlock ();
772 #ifndef LAZY_GC_THREAD_CREATION
776 mono_gc_init_finalizer_thread (void)
778 gc_thread = mono_thread_create_internal (mono_domain_get (), finalizer_thread, NULL, FALSE, 0);
779 ves_icall_System_Threading_Thread_SetName_internal (gc_thread, mono_string_new (mono_domain_get (), "Finalizer"));
785 mono_os_mutex_init_recursive (&allocator_section);
787 mono_coop_mutex_init_recursive (&finalizer_mutex);
788 mono_coop_mutex_init_recursive (&reference_queue_mutex);
790 mono_counters_register ("Minor GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.minor_gc_count);
791 mono_counters_register ("Major GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.major_gc_count);
792 mono_counters_register ("Minor GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.minor_gc_time);
793 mono_counters_register ("Major GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time);
794 mono_counters_register ("Major GC time concurrent", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time_concurrent);
796 mono_gc_base_init ();
798 if (mono_gc_is_disabled ()) {
803 pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
804 g_assert (pending_done_event);
805 mono_coop_cond_init (&exited_cond);
806 mono_coop_sem_init (&finalizer_sem, 0);
808 #ifndef LAZY_GC_THREAD_CREATION
809 mono_gc_init_finalizer_thread ();
814 mono_gc_cleanup (void)
817 g_message ("%s: cleaning up finalizer", __func__);
820 if (mono_gc_is_null ())
825 if (mono_thread_internal_current () != gc_thread) {
826 gboolean timed_out = FALSE;
827 guint32 start_ticks = mono_msec_ticks ();
828 guint32 end_ticks = start_ticks + 2000;
830 mono_gc_finalize_notify ();
831 /* Finishing the finalizer thread, so wait a little bit... */
832 /* MS seems to wait for about 2 seconds */
833 while (!finalizer_thread_exited) {
834 guint32 current_ticks = mono_msec_ticks ();
837 if (current_ticks >= end_ticks)
840 timeout = end_ticks - current_ticks;
841 mono_finalizer_lock ();
842 if (!finalizer_thread_exited)
843 mono_coop_cond_timedwait (&exited_cond, &finalizer_mutex, timeout);
844 mono_finalizer_unlock ();
847 if (!finalizer_thread_exited) {
850 /* Set a flag which the finalizer thread can check */
851 suspend_finalizers = TRUE;
853 /* Try to abort the thread, in the hope that it is running managed code */
854 mono_thread_internal_stop (gc_thread);
856 /* Wait for it to stop */
857 ret = guarded_wait (gc_thread->handle, 100, TRUE);
859 if (ret == WAIT_TIMEOUT) {
861 * The finalizer thread refused to die. There is not much we
862 * can do here, since the runtime is shutting down so the
863 * state the finalizer thread depends on will vanish.
865 g_warning ("Shutting down finalizer thread timed out.");
873 /* Wait for the thread to actually exit */
874 ret = guarded_wait (gc_thread->handle, INFINITE, TRUE);
875 g_assert (ret == WAIT_OBJECT_0);
877 mono_thread_join (GUINT_TO_POINTER (gc_thread->tid));
881 mono_gc_base_cleanup ();
884 mono_reference_queue_cleanup ();
886 mono_os_mutex_destroy (&allocator_section);
887 mono_coop_mutex_destroy (&finalizer_mutex);
888 mono_coop_mutex_destroy (&reference_queue_mutex);
892 mono_gc_is_finalizer_internal_thread (MonoInternalThread *thread)
894 return thread == gc_thread;
898 * mono_gc_is_finalizer_thread:
899 * @thread: the thread to test.
901 * In Mono objects are finalized asynchronously on a separate thread.
902 * This routine tests whether the @thread argument represents the
903 * finalization thread.
905 * Returns true if @thread is the finalization thread.
908 mono_gc_is_finalizer_thread (MonoThread *thread)
910 return mono_gc_is_finalizer_internal_thread (thread->internal_thread);
913 #if defined(__MACH__)
914 static pthread_t mach_exception_thread;
917 mono_gc_register_mach_exception_thread (pthread_t thread)
919 mach_exception_thread = thread;
923 mono_gc_get_mach_exception_thread (void)
925 return mach_exception_thread;
931 mono_gc_alloc_mature (MonoVTable *vtable)
933 return mono_object_new_specific (vtable);
938 static MonoReferenceQueue *ref_queues;
941 ref_list_remove_element (RefQueueEntry **prev, RefQueueEntry *element)
944 /* Guard if head is changed concurrently. */
945 while (*prev != element)
946 prev = &(*prev)->next;
947 } while (prev && InterlockedCompareExchangePointer ((void*)prev, element->next, element) != element);
951 ref_list_push (RefQueueEntry **head, RefQueueEntry *value)
953 RefQueueEntry *current;
956 value->next = current;
957 STORE_STORE_FENCE; /*Must make sure the previous store is visible before the CAS. */
958 } while (InterlockedCompareExchangePointer ((void*)head, value, current) != current);
962 reference_queue_proccess (MonoReferenceQueue *queue)
964 RefQueueEntry **iter = &queue->queue;
965 RefQueueEntry *entry;
966 while ((entry = *iter)) {
967 if (queue->should_be_deleted || !mono_gchandle_get_target (entry->gchandle)) {
968 mono_gchandle_free ((guint32)entry->gchandle);
969 ref_list_remove_element (iter, entry);
970 queue->callback (entry->user_data);
979 reference_queue_proccess_all (void)
981 MonoReferenceQueue **iter;
982 MonoReferenceQueue *queue = ref_queues;
983 for (; queue; queue = queue->next)
984 reference_queue_proccess (queue);
987 mono_coop_mutex_lock (&reference_queue_mutex);
988 for (iter = &ref_queues; *iter;) {
990 if (!queue->should_be_deleted) {
995 mono_coop_mutex_unlock (&reference_queue_mutex);
996 reference_queue_proccess (queue);
1002 mono_coop_mutex_unlock (&reference_queue_mutex);
1006 mono_reference_queue_cleanup (void)
1008 MonoReferenceQueue *queue = ref_queues;
1009 for (; queue; queue = queue->next)
1010 queue->should_be_deleted = TRUE;
1011 reference_queue_proccess_all ();
1015 reference_queue_clear_for_domain (MonoDomain *domain)
1017 MonoReferenceQueue *queue = ref_queues;
1018 for (; queue; queue = queue->next) {
1019 RefQueueEntry **iter = &queue->queue;
1020 RefQueueEntry *entry;
1021 while ((entry = *iter)) {
1022 if (entry->domain == domain) {
1023 mono_gchandle_free ((guint32)entry->gchandle);
1024 ref_list_remove_element (iter, entry);
1025 queue->callback (entry->user_data);
1028 iter = &entry->next;
1034 * mono_gc_reference_queue_new:
1035 * @callback callback used when processing collected entries.
1037 * Create a new reference queue used to process collected objects.
1038 * A reference queue let you add a pair of (managed object, user data)
1039 * using the mono_gc_reference_queue_add method.
1041 * Once the managed object is collected @callback will be called
1042 * in the finalizer thread with 'user data' as argument.
1044 * The callback is called from the finalizer thread without any locks held.
1045 * When a AppDomain is unloaded, all callbacks for objects belonging to it
1048 * @returns the new queue.
1051 mono_gc_reference_queue_new (mono_reference_queue_callback callback)
1053 MonoReferenceQueue *res = g_new0 (MonoReferenceQueue, 1);
1054 res->callback = callback;
1056 mono_coop_mutex_lock (&reference_queue_mutex);
1057 res->next = ref_queues;
1059 mono_coop_mutex_unlock (&reference_queue_mutex);
1065 * mono_gc_reference_queue_add:
1066 * @queue the queue to add the reference to.
1067 * @obj the object to be watched for collection
1068 * @user_data parameter to be passed to the queue callback
1070 * Queue an object to be watched for collection, when the @obj is
1071 * collected, the callback that was registered for the @queue will
1072 * be invoked with @user_data as argument.
1074 * @returns false if the queue is scheduled to be freed.
1077 mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *user_data)
1079 RefQueueEntry *entry;
1080 if (queue->should_be_deleted)
1083 entry = g_new0 (RefQueueEntry, 1);
1084 entry->user_data = user_data;
1085 entry->domain = mono_object_domain (obj);
1087 entry->gchandle = mono_gchandle_new_weakref (obj, TRUE);
1088 mono_object_register_finalizer (obj);
1090 ref_list_push (&queue->queue, entry);
1095 * mono_gc_reference_queue_free:
1096 * @queue the queue that should be freed.
1098 * This operation signals that @queue should be freed. This operation is deferred
1099 * as it happens on the finalizer thread.
1101 * After this call, no further objects can be queued. It's the responsibility of the
1102 * caller to make sure that no further attempt to access queue will be made.
1105 mono_gc_reference_queue_free (MonoReferenceQueue *queue)
1107 queue->should_be_deleted = TRUE;