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 = (MonoObject *)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 ((MonoObject *)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_checked (),
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);
241 domain->finalize_runtime_invoke = mono_compile_method (invoke);
244 runtime_invoke = (RuntimeInvokeFunction)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 mono_gc_GCHandle_CheckCurrentDomain (guint32 gchandle)
607 return mono_gchandle_is_in_domain (gchandle, mono_domain_get ());
610 static MonoCoopSem finalizer_sem;
611 static volatile gboolean finished=FALSE;
614 mono_gc_finalize_notify (void)
617 g_message ( "%s: prodding finalizer", __func__);
620 if (mono_gc_is_null ())
623 mono_coop_sem_post (&finalizer_sem);
629 collect_objects (gpointer key, gpointer value, gpointer user_data)
631 GPtrArray *arr = (GPtrArray*)user_data;
632 g_ptr_array_add (arr, key);
638 * finalize_domain_objects:
640 * Run the finalizers of all finalizable objects in req->domain.
643 finalize_domain_objects (DomainFinalizationReq *req)
645 MonoDomain *domain = req->domain;
648 #define NUM_FOBJECTS 64
649 MonoObject *to_finalize [NUM_FOBJECTS];
653 /* Process finalizers which are already in the queue */
654 mono_gc_invoke_finalizers ();
657 while (g_hash_table_size (domain->finalizable_objects_hash) > 0) {
661 * Since the domain is unloading, nobody is allowed to put
662 * new entries into the hash table. But finalize_object might
663 * remove entries from the hash table, so we make a copy.
665 objs = g_ptr_array_new ();
666 g_hash_table_foreach (domain->finalizable_objects_hash, collect_objects, objs);
667 /* printf ("FINALIZING %d OBJECTS.\n", objs->len); */
669 for (i = 0; i < objs->len; ++i) {
670 MonoObject *o = (MonoObject*)g_ptr_array_index (objs, i);
671 /* FIXME: Avoid finalizing threads, etc */
672 mono_gc_run_finalize (o, 0);
675 g_ptr_array_free (objs, TRUE);
677 #elif defined(HAVE_SGEN_GC)
678 while ((count = mono_gc_finalizers_for_domain (domain, to_finalize, NUM_FOBJECTS))) {
680 for (i = 0; i < count; ++i) {
681 mono_gc_run_finalize (to_finalize [i], 0);
686 /* cleanup the reference queue */
687 reference_queue_clear_for_domain (domain);
689 /* printf ("DONE.\n"); */
690 SetEvent (req->done_event);
692 /* The event is closed in mono_domain_finalize if we get here */
697 finalizer_thread (gpointer unused)
699 gboolean wait = TRUE;
702 /* Wait to be notified that there's at least one
706 g_assert (mono_domain_get () == mono_get_root_domain ());
707 mono_gc_set_skip_thread (TRUE);
710 /* An alertable wait is required so this thread can be suspended on windows */
711 mono_coop_sem_wait (&finalizer_sem, MONO_SEM_FLAGS_ALERTABLE);
715 mono_gc_set_skip_thread (FALSE);
717 mono_threads_perform_thread_dump ();
719 mono_console_handle_async_ops ();
721 mono_attach_maybe_start ();
723 if (domains_to_finalize) {
724 mono_finalizer_lock ();
725 if (domains_to_finalize) {
726 DomainFinalizationReq *req = (DomainFinalizationReq *)domains_to_finalize->data;
727 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
728 mono_finalizer_unlock ();
730 finalize_domain_objects (req);
732 mono_finalizer_unlock ();
736 /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup),
737 * before the domain is unloaded.
739 mono_gc_invoke_finalizers ();
741 mono_threads_join_threads ();
743 reference_queue_proccess_all ();
745 /* Avoid posting the pending done event until there are pending finalizers */
746 if (mono_coop_sem_timedwait (&finalizer_sem, 0, MONO_SEM_FLAGS_NONE) == 0) {
747 /* Don't wait again at the start of the loop */
750 SetEvent (pending_done_event);
754 mono_finalizer_lock ();
755 finalizer_thread_exited = TRUE;
756 mono_coop_cond_signal (&exited_cond);
757 mono_finalizer_unlock ();
762 #ifndef LAZY_GC_THREAD_CREATION
766 mono_gc_init_finalizer_thread (void)
768 gc_thread = mono_thread_create_internal (mono_domain_get (), finalizer_thread, NULL, FALSE, 0);
769 ves_icall_System_Threading_Thread_SetName_internal (gc_thread, mono_string_new (mono_domain_get (), "Finalizer"));
775 mono_coop_mutex_init_recursive (&finalizer_mutex);
776 mono_coop_mutex_init_recursive (&reference_queue_mutex);
778 mono_counters_register ("Minor GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.minor_gc_count);
779 mono_counters_register ("Major GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.major_gc_count);
780 mono_counters_register ("Minor GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.minor_gc_time);
781 mono_counters_register ("Major GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time);
782 mono_counters_register ("Major GC time concurrent", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time_concurrent);
784 mono_gc_base_init ();
786 if (mono_gc_is_disabled ()) {
791 pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
792 g_assert (pending_done_event);
793 mono_coop_cond_init (&exited_cond);
794 mono_coop_sem_init (&finalizer_sem, 0);
796 #ifndef LAZY_GC_THREAD_CREATION
797 mono_gc_init_finalizer_thread ();
802 mono_gc_cleanup (void)
805 g_message ("%s: cleaning up finalizer", __func__);
808 if (mono_gc_is_null ())
813 if (mono_thread_internal_current () != gc_thread) {
814 gboolean timed_out = FALSE;
815 guint32 start_ticks = mono_msec_ticks ();
816 guint32 end_ticks = start_ticks + 2000;
818 mono_gc_finalize_notify ();
819 /* Finishing the finalizer thread, so wait a little bit... */
820 /* MS seems to wait for about 2 seconds */
821 while (!finalizer_thread_exited) {
822 guint32 current_ticks = mono_msec_ticks ();
825 if (current_ticks >= end_ticks)
828 timeout = end_ticks - current_ticks;
829 mono_finalizer_lock ();
830 if (!finalizer_thread_exited)
831 mono_coop_cond_timedwait (&exited_cond, &finalizer_mutex, timeout);
832 mono_finalizer_unlock ();
835 if (!finalizer_thread_exited) {
838 /* Set a flag which the finalizer thread can check */
839 suspend_finalizers = TRUE;
841 /* Try to abort the thread, in the hope that it is running managed code */
842 mono_thread_internal_stop (gc_thread);
844 /* Wait for it to stop */
845 ret = guarded_wait (gc_thread->handle, 100, TRUE);
847 if (ret == WAIT_TIMEOUT) {
849 * The finalizer thread refused to die. There is not much we
850 * can do here, since the runtime is shutting down so the
851 * state the finalizer thread depends on will vanish.
853 g_warning ("Shutting down finalizer thread timed out.");
861 /* Wait for the thread to actually exit */
862 ret = guarded_wait (gc_thread->handle, INFINITE, TRUE);
863 g_assert (ret == WAIT_OBJECT_0);
865 mono_thread_join (GUINT_TO_POINTER (gc_thread->tid));
867 g_assert (finalizer_thread_exited);
870 mono_gc_base_cleanup ();
873 mono_reference_queue_cleanup ();
875 mono_coop_mutex_destroy (&finalizer_mutex);
876 mono_coop_mutex_destroy (&reference_queue_mutex);
880 mono_gc_is_finalizer_internal_thread (MonoInternalThread *thread)
882 return thread == gc_thread;
886 * mono_gc_is_finalizer_thread:
887 * @thread: the thread to test.
889 * In Mono objects are finalized asynchronously on a separate thread.
890 * This routine tests whether the @thread argument represents the
891 * finalization thread.
893 * Returns: TRUE if @thread is the finalization thread.
896 mono_gc_is_finalizer_thread (MonoThread *thread)
898 return mono_gc_is_finalizer_internal_thread (thread->internal_thread);
901 #if defined(__MACH__)
902 static pthread_t mach_exception_thread;
905 mono_gc_register_mach_exception_thread (pthread_t thread)
907 mach_exception_thread = thread;
911 mono_gc_get_mach_exception_thread (void)
913 return mach_exception_thread;
917 static MonoReferenceQueue *ref_queues;
920 ref_list_remove_element (RefQueueEntry **prev, RefQueueEntry *element)
923 /* Guard if head is changed concurrently. */
924 while (*prev != element)
925 prev = &(*prev)->next;
926 } while (prev && InterlockedCompareExchangePointer ((volatile gpointer *)prev, element->next, element) != element);
930 ref_list_push (RefQueueEntry **head, RefQueueEntry *value)
932 RefQueueEntry *current;
935 value->next = current;
936 STORE_STORE_FENCE; /*Must make sure the previous store is visible before the CAS. */
937 } while (InterlockedCompareExchangePointer ((volatile gpointer *)head, value, current) != current);
941 reference_queue_proccess (MonoReferenceQueue *queue)
943 RefQueueEntry **iter = &queue->queue;
944 RefQueueEntry *entry;
945 while ((entry = *iter)) {
946 if (queue->should_be_deleted || !mono_gchandle_get_target (entry->gchandle)) {
947 mono_gchandle_free ((guint32)entry->gchandle);
948 ref_list_remove_element (iter, entry);
949 queue->callback (entry->user_data);
958 reference_queue_proccess_all (void)
960 MonoReferenceQueue **iter;
961 MonoReferenceQueue *queue = ref_queues;
962 for (; queue; queue = queue->next)
963 reference_queue_proccess (queue);
966 mono_coop_mutex_lock (&reference_queue_mutex);
967 for (iter = &ref_queues; *iter;) {
969 if (!queue->should_be_deleted) {
974 mono_coop_mutex_unlock (&reference_queue_mutex);
975 reference_queue_proccess (queue);
981 mono_coop_mutex_unlock (&reference_queue_mutex);
985 mono_reference_queue_cleanup (void)
987 MonoReferenceQueue *queue = ref_queues;
988 for (; queue; queue = queue->next)
989 queue->should_be_deleted = TRUE;
990 reference_queue_proccess_all ();
994 reference_queue_clear_for_domain (MonoDomain *domain)
996 MonoReferenceQueue *queue = ref_queues;
997 for (; queue; queue = queue->next) {
998 RefQueueEntry **iter = &queue->queue;
999 RefQueueEntry *entry;
1000 while ((entry = *iter)) {
1001 if (entry->domain == domain) {
1002 mono_gchandle_free ((guint32)entry->gchandle);
1003 ref_list_remove_element (iter, entry);
1004 queue->callback (entry->user_data);
1007 iter = &entry->next;
1013 * mono_gc_reference_queue_new:
1014 * @callback callback used when processing collected entries.
1016 * Create a new reference queue used to process collected objects.
1017 * A reference queue let you add a pair of (managed object, user data)
1018 * using the mono_gc_reference_queue_add method.
1020 * Once the managed object is collected @callback will be called
1021 * in the finalizer thread with 'user data' as argument.
1023 * The callback is called from the finalizer thread without any locks held.
1024 * When a AppDomain is unloaded, all callbacks for objects belonging to it
1027 * @returns the new queue.
1030 mono_gc_reference_queue_new (mono_reference_queue_callback callback)
1032 MonoReferenceQueue *res = g_new0 (MonoReferenceQueue, 1);
1033 res->callback = callback;
1035 mono_coop_mutex_lock (&reference_queue_mutex);
1036 res->next = ref_queues;
1038 mono_coop_mutex_unlock (&reference_queue_mutex);
1044 * mono_gc_reference_queue_add:
1045 * @queue the queue to add the reference to.
1046 * @obj the object to be watched for collection
1047 * @user_data parameter to be passed to the queue callback
1049 * Queue an object to be watched for collection, when the @obj is
1050 * collected, the callback that was registered for the @queue will
1051 * be invoked with @user_data as argument.
1053 * @returns false if the queue is scheduled to be freed.
1056 mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *user_data)
1058 RefQueueEntry *entry;
1059 if (queue->should_be_deleted)
1062 entry = g_new0 (RefQueueEntry, 1);
1063 entry->user_data = user_data;
1064 entry->domain = mono_object_domain (obj);
1066 entry->gchandle = mono_gchandle_new_weakref (obj, TRUE);
1067 mono_object_register_finalizer (obj);
1069 ref_list_push (&queue->queue, entry);
1074 * mono_gc_reference_queue_free:
1075 * @queue the queue that should be freed.
1077 * This operation signals that @queue should be freed. This operation is deferred
1078 * as it happens on the finalizer thread.
1080 * After this call, no further objects can be queued. It's the responsibility of the
1081 * caller to make sure that no further attempt to access queue will be made.
1084 mono_gc_reference_queue_free (MonoReferenceQueue *queue)
1086 queue->should_be_deleted = TRUE;