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;
542 ves_icall_System_GCHandle_GetTarget (guint32 handle)
544 return mono_gchandle_get_target (handle);
548 * if type == -1, change the target of the handle, otherwise allocate a new handle.
551 ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type)
554 mono_gchandle_set_target (handle, obj);
555 /* the handle doesn't change */
560 return mono_gchandle_new_weakref (obj, FALSE);
561 case HANDLE_WEAK_TRACK:
562 return mono_gchandle_new_weakref (obj, TRUE);
564 return mono_gchandle_new (obj, FALSE);
566 return mono_gchandle_new (obj, TRUE);
568 g_assert_not_reached ();
574 ves_icall_System_GCHandle_FreeHandle (guint32 handle)
576 mono_gchandle_free (handle);
580 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
584 if (MONO_GC_HANDLE_TYPE (handle) != HANDLE_PINNED)
586 obj = mono_gchandle_get_target (handle);
588 MonoClass *klass = mono_object_class (obj);
589 if (klass == mono_defaults.string_class) {
590 return mono_string_chars ((MonoString*)obj);
591 } else if (klass->rank) {
592 return mono_array_addr ((MonoArray*)obj, char, 0);
594 /* the C# code will check and throw the exception */
595 /* FIXME: missing !klass->blittable test, see bug #61134 */
596 if ((klass->flags & TYPE_ATTRIBUTE_LAYOUT_MASK) == TYPE_ATTRIBUTE_AUTO_LAYOUT)
598 return (char*)obj + sizeof (MonoObject);
605 ves_icall_Mono_Runtime_SetGCAllowSynchronousMajor (MonoBoolean flag)
607 return mono_gc_set_allow_synchronous_major (flag);
611 mono_gc_GCHandle_CheckCurrentDomain (guint32 gchandle)
613 return mono_gchandle_is_in_domain (gchandle, mono_domain_get ());
616 static MonoCoopSem finalizer_sem;
617 static volatile gboolean finished=FALSE;
620 mono_gc_finalize_notify (void)
623 g_message ( "%s: prodding finalizer", __func__);
626 if (mono_gc_is_null ())
629 mono_coop_sem_post (&finalizer_sem);
635 collect_objects (gpointer key, gpointer value, gpointer user_data)
637 GPtrArray *arr = (GPtrArray*)user_data;
638 g_ptr_array_add (arr, key);
644 * finalize_domain_objects:
646 * Run the finalizers of all finalizable objects in req->domain.
649 finalize_domain_objects (DomainFinalizationReq *req)
651 MonoDomain *domain = req->domain;
654 #define NUM_FOBJECTS 64
655 MonoObject *to_finalize [NUM_FOBJECTS];
659 /* Process finalizers which are already in the queue */
660 mono_gc_invoke_finalizers ();
663 while (g_hash_table_size (domain->finalizable_objects_hash) > 0) {
667 * Since the domain is unloading, nobody is allowed to put
668 * new entries into the hash table. But finalize_object might
669 * remove entries from the hash table, so we make a copy.
671 objs = g_ptr_array_new ();
672 g_hash_table_foreach (domain->finalizable_objects_hash, collect_objects, objs);
673 /* printf ("FINALIZING %d OBJECTS.\n", objs->len); */
675 for (i = 0; i < objs->len; ++i) {
676 MonoObject *o = (MonoObject*)g_ptr_array_index (objs, i);
677 /* FIXME: Avoid finalizing threads, etc */
678 mono_gc_run_finalize (o, 0);
681 g_ptr_array_free (objs, TRUE);
683 #elif defined(HAVE_SGEN_GC)
684 while ((count = mono_gc_finalizers_for_domain (domain, to_finalize, NUM_FOBJECTS))) {
686 for (i = 0; i < count; ++i) {
687 mono_gc_run_finalize (to_finalize [i], 0);
692 /* cleanup the reference queue */
693 reference_queue_clear_for_domain (domain);
695 /* printf ("DONE.\n"); */
696 SetEvent (req->done_event);
698 /* The event is closed in mono_domain_finalize if we get here */
703 finalizer_thread (gpointer unused)
705 gboolean wait = TRUE;
708 /* Wait to be notified that there's at least one
712 g_assert (mono_domain_get () == mono_get_root_domain ());
713 mono_gc_set_skip_thread (TRUE);
716 /* An alertable wait is required so this thread can be suspended on windows */
717 mono_coop_sem_wait (&finalizer_sem, MONO_SEM_FLAGS_ALERTABLE);
721 mono_gc_set_skip_thread (FALSE);
723 mono_threads_perform_thread_dump ();
725 mono_console_handle_async_ops ();
727 mono_attach_maybe_start ();
729 if (domains_to_finalize) {
730 mono_finalizer_lock ();
731 if (domains_to_finalize) {
732 DomainFinalizationReq *req = domains_to_finalize->data;
733 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
734 mono_finalizer_unlock ();
736 finalize_domain_objects (req);
738 mono_finalizer_unlock ();
742 /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup),
743 * before the domain is unloaded.
745 mono_gc_invoke_finalizers ();
747 mono_threads_join_threads ();
749 reference_queue_proccess_all ();
751 /* Avoid posting the pending done event until there are pending finalizers */
752 if (mono_coop_sem_timedwait (&finalizer_sem, 0, MONO_SEM_FLAGS_NONE) == 0) {
753 /* Don't wait again at the start of the loop */
756 SetEvent (pending_done_event);
760 mono_finalizer_lock ();
761 finalizer_thread_exited = TRUE;
762 mono_coop_cond_signal (&exited_cond);
763 mono_finalizer_unlock ();
768 #ifndef LAZY_GC_THREAD_CREATION
772 mono_gc_init_finalizer_thread (void)
774 gc_thread = mono_thread_create_internal (mono_domain_get (), finalizer_thread, NULL, FALSE, 0);
775 ves_icall_System_Threading_Thread_SetName_internal (gc_thread, mono_string_new (mono_domain_get (), "Finalizer"));
781 mono_coop_mutex_init_recursive (&finalizer_mutex);
782 mono_coop_mutex_init_recursive (&reference_queue_mutex);
784 mono_counters_register ("Minor GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.minor_gc_count);
785 mono_counters_register ("Major GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.major_gc_count);
786 mono_counters_register ("Minor GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.minor_gc_time);
787 mono_counters_register ("Major GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time);
788 mono_counters_register ("Major GC time concurrent", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time_concurrent);
790 mono_gc_base_init ();
792 if (mono_gc_is_disabled ()) {
797 pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
798 g_assert (pending_done_event);
799 mono_coop_cond_init (&exited_cond);
800 mono_coop_sem_init (&finalizer_sem, 0);
802 #ifndef LAZY_GC_THREAD_CREATION
803 mono_gc_init_finalizer_thread ();
808 mono_gc_cleanup (void)
811 g_message ("%s: cleaning up finalizer", __func__);
814 if (mono_gc_is_null ())
819 if (mono_thread_internal_current () != gc_thread) {
820 gboolean timed_out = FALSE;
821 guint32 start_ticks = mono_msec_ticks ();
822 guint32 end_ticks = start_ticks + 2000;
824 mono_gc_finalize_notify ();
825 /* Finishing the finalizer thread, so wait a little bit... */
826 /* MS seems to wait for about 2 seconds */
827 while (!finalizer_thread_exited) {
828 guint32 current_ticks = mono_msec_ticks ();
831 if (current_ticks >= end_ticks)
834 timeout = end_ticks - current_ticks;
835 mono_finalizer_lock ();
836 if (!finalizer_thread_exited)
837 mono_coop_cond_timedwait (&exited_cond, &finalizer_mutex, timeout);
838 mono_finalizer_unlock ();
841 if (!finalizer_thread_exited) {
844 /* Set a flag which the finalizer thread can check */
845 suspend_finalizers = TRUE;
847 /* Try to abort the thread, in the hope that it is running managed code */
848 mono_thread_internal_stop (gc_thread);
850 /* Wait for it to stop */
851 ret = guarded_wait (gc_thread->handle, 100, TRUE);
853 if (ret == WAIT_TIMEOUT) {
855 * The finalizer thread refused to die. There is not much we
856 * can do here, since the runtime is shutting down so the
857 * state the finalizer thread depends on will vanish.
859 g_warning ("Shutting down finalizer thread timed out.");
867 /* Wait for the thread to actually exit */
868 ret = guarded_wait (gc_thread->handle, INFINITE, TRUE);
869 g_assert (ret == WAIT_OBJECT_0);
871 mono_thread_join (GUINT_TO_POINTER (gc_thread->tid));
875 mono_gc_base_cleanup ();
878 mono_reference_queue_cleanup ();
880 mono_coop_mutex_destroy (&finalizer_mutex);
881 mono_coop_mutex_destroy (&reference_queue_mutex);
885 mono_gc_is_finalizer_internal_thread (MonoInternalThread *thread)
887 return thread == gc_thread;
891 * mono_gc_is_finalizer_thread:
892 * @thread: the thread to test.
894 * In Mono objects are finalized asynchronously on a separate thread.
895 * This routine tests whether the @thread argument represents the
896 * finalization thread.
898 * Returns true if @thread is the finalization thread.
901 mono_gc_is_finalizer_thread (MonoThread *thread)
903 return mono_gc_is_finalizer_internal_thread (thread->internal_thread);
906 #if defined(__MACH__)
907 static pthread_t mach_exception_thread;
910 mono_gc_register_mach_exception_thread (pthread_t thread)
912 mach_exception_thread = thread;
916 mono_gc_get_mach_exception_thread (void)
918 return mach_exception_thread;
924 mono_gc_alloc_mature (MonoVTable *vtable)
926 return mono_object_new_specific (vtable);
931 static MonoReferenceQueue *ref_queues;
934 ref_list_remove_element (RefQueueEntry **prev, RefQueueEntry *element)
937 /* Guard if head is changed concurrently. */
938 while (*prev != element)
939 prev = &(*prev)->next;
940 } while (prev && InterlockedCompareExchangePointer ((void*)prev, element->next, element) != element);
944 ref_list_push (RefQueueEntry **head, RefQueueEntry *value)
946 RefQueueEntry *current;
949 value->next = current;
950 STORE_STORE_FENCE; /*Must make sure the previous store is visible before the CAS. */
951 } while (InterlockedCompareExchangePointer ((void*)head, value, current) != current);
955 reference_queue_proccess (MonoReferenceQueue *queue)
957 RefQueueEntry **iter = &queue->queue;
958 RefQueueEntry *entry;
959 while ((entry = *iter)) {
960 if (queue->should_be_deleted || !mono_gchandle_get_target (entry->gchandle)) {
961 mono_gchandle_free ((guint32)entry->gchandle);
962 ref_list_remove_element (iter, entry);
963 queue->callback (entry->user_data);
972 reference_queue_proccess_all (void)
974 MonoReferenceQueue **iter;
975 MonoReferenceQueue *queue = ref_queues;
976 for (; queue; queue = queue->next)
977 reference_queue_proccess (queue);
980 mono_coop_mutex_lock (&reference_queue_mutex);
981 for (iter = &ref_queues; *iter;) {
983 if (!queue->should_be_deleted) {
988 mono_coop_mutex_unlock (&reference_queue_mutex);
989 reference_queue_proccess (queue);
995 mono_coop_mutex_unlock (&reference_queue_mutex);
999 mono_reference_queue_cleanup (void)
1001 MonoReferenceQueue *queue = ref_queues;
1002 for (; queue; queue = queue->next)
1003 queue->should_be_deleted = TRUE;
1004 reference_queue_proccess_all ();
1008 reference_queue_clear_for_domain (MonoDomain *domain)
1010 MonoReferenceQueue *queue = ref_queues;
1011 for (; queue; queue = queue->next) {
1012 RefQueueEntry **iter = &queue->queue;
1013 RefQueueEntry *entry;
1014 while ((entry = *iter)) {
1015 if (entry->domain == domain) {
1016 mono_gchandle_free ((guint32)entry->gchandle);
1017 ref_list_remove_element (iter, entry);
1018 queue->callback (entry->user_data);
1021 iter = &entry->next;
1027 * mono_gc_reference_queue_new:
1028 * @callback callback used when processing collected entries.
1030 * Create a new reference queue used to process collected objects.
1031 * A reference queue let you add a pair of (managed object, user data)
1032 * using the mono_gc_reference_queue_add method.
1034 * Once the managed object is collected @callback will be called
1035 * in the finalizer thread with 'user data' as argument.
1037 * The callback is called from the finalizer thread without any locks held.
1038 * When a AppDomain is unloaded, all callbacks for objects belonging to it
1041 * @returns the new queue.
1044 mono_gc_reference_queue_new (mono_reference_queue_callback callback)
1046 MonoReferenceQueue *res = g_new0 (MonoReferenceQueue, 1);
1047 res->callback = callback;
1049 mono_coop_mutex_lock (&reference_queue_mutex);
1050 res->next = ref_queues;
1052 mono_coop_mutex_unlock (&reference_queue_mutex);
1058 * mono_gc_reference_queue_add:
1059 * @queue the queue to add the reference to.
1060 * @obj the object to be watched for collection
1061 * @user_data parameter to be passed to the queue callback
1063 * Queue an object to be watched for collection, when the @obj is
1064 * collected, the callback that was registered for the @queue will
1065 * be invoked with @user_data as argument.
1067 * @returns false if the queue is scheduled to be freed.
1070 mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *user_data)
1072 RefQueueEntry *entry;
1073 if (queue->should_be_deleted)
1076 entry = g_new0 (RefQueueEntry, 1);
1077 entry->user_data = user_data;
1078 entry->domain = mono_object_domain (obj);
1080 entry->gchandle = mono_gchandle_new_weakref (obj, TRUE);
1081 mono_object_register_finalizer (obj);
1083 ref_list_push (&queue->queue, entry);
1088 * mono_gc_reference_queue_free:
1089 * @queue the queue that should be freed.
1091 * This operation signals that @queue should be freed. This operation is deferred
1092 * as it happens on the finalizer thread.
1094 * After this call, no further objects can be queued. It's the responsibility of the
1095 * caller to make sure that no further attempt to access queue will be made.
1098 mono_gc_reference_queue_free (MonoReferenceQueue *queue)
1100 queue->should_be_deleted = TRUE;