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-internal.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-internal.h>
30 #include <mono/metadata/gc-internal.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-semaphore.h>
35 #include <mono/utils/mono-memory-model.h>
36 #include <mono/utils/mono-counters.h>
37 #include <mono/utils/mono-time.h>
38 #include <mono/utils/dtrace.h>
39 #include <mono/utils/mono-threads.h>
40 #include <mono/utils/atomic.h>
46 typedef struct DomainFinalizationReq {
49 } DomainFinalizationReq;
51 static gboolean gc_disabled = FALSE;
53 static gboolean finalizing_root_domain = FALSE;
55 gboolean log_finalizers = FALSE;
56 gboolean do_not_finalize = FALSE;
58 #define mono_finalizer_lock() mono_mutex_lock (&finalizer_mutex)
59 #define mono_finalizer_unlock() mono_mutex_unlock (&finalizer_mutex)
60 static mono_mutex_t finalizer_mutex;
61 static mono_mutex_t reference_queue_mutex;
63 static GSList *domains_to_finalize= NULL;
64 static MonoMList *threads_to_finalize = NULL;
66 static gboolean finalizer_thread_exited;
67 /* Uses finalizer_mutex */
68 static mono_cond_t exited_cond;
70 static MonoInternalThread *gc_thread;
72 static void object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*));
74 static void reference_queue_proccess_all (void);
75 static void mono_reference_queue_cleanup (void);
76 static void reference_queue_clear_for_domain (MonoDomain *domain);
77 static HANDLE pending_done_event;
80 guarded_wait (HANDLE handle, guint32 timeout, gboolean alertable)
84 MONO_PREPARE_BLOCKING;
85 result = WaitForSingleObjectEx (handle, timeout, alertable);
92 add_thread_to_finalize (MonoInternalThread *thread)
94 mono_finalizer_lock ();
95 if (!threads_to_finalize)
96 MONO_GC_REGISTER_ROOT_SINGLE (threads_to_finalize, MONO_ROOT_SOURCE_FINALIZER_QUEUE, "finalizable threads list");
97 threads_to_finalize = mono_mlist_append (threads_to_finalize, (MonoObject*)thread);
98 mono_finalizer_unlock ();
101 static gboolean suspend_finalizers = FALSE;
103 * actually, we might want to queue the finalize requests in a separate thread,
104 * but we need to be careful about the execution domain of the thread...
107 mono_gc_run_finalize (void *obj, void *data)
112 MonoObject *exc = NULL;
117 MonoMethod* finalizer = NULL;
118 MonoDomain *caller_domain = mono_domain_get ();
120 RuntimeInvokeFunction runtime_invoke;
122 // This function is called from the innards of the GC, so our best alternative for now is to do polling here
123 MONO_SUSPEND_CHECK ();
125 o = (MonoObject*)((char*)obj + GPOINTER_TO_UINT (data));
128 g_log ("mono-gc-finalizers", G_LOG_LEVEL_DEBUG, "<%s at %p> Starting finalizer checks.", o->vtable->klass->name, o);
130 if (suspend_finalizers)
133 domain = o->vtable->domain;
136 mono_domain_finalizers_lock (domain);
138 o2 = g_hash_table_lookup (domain->finalizable_objects_hash, o);
140 mono_domain_finalizers_unlock (domain);
143 /* Already finalized somehow */
147 /* make sure the finalizer is not called again if the object is resurrected */
148 object_register_finalizer (obj, NULL);
151 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Registered finalizer as processed.", o->vtable->klass->name, o);
153 if (o->vtable->klass == mono_defaults.internal_thread_class) {
154 MonoInternalThread *t = (MonoInternalThread*)o;
156 if (mono_gc_is_finalizer_internal_thread (t))
157 /* Avoid finalizing ourselves */
160 if (t->threadpool_thread && finalizing_root_domain) {
161 /* Don't finalize threadpool threads when
162 shutting down - they're finalized when the
163 threadpool shuts down. */
164 add_thread_to_finalize (t);
169 if (o->vtable->klass->image == mono_defaults.corlib && !strcmp (o->vtable->klass->name, "DynamicMethod") && finalizing_root_domain) {
171 * These can't be finalized during unloading/shutdown, since that would
172 * free the native code which can still be referenced by other
174 * FIXME: This is not perfect, objects dying at the same time as
175 * dynamic methods can still reference them even when !shutdown.
180 if (mono_runtime_get_no_exec ())
183 /* speedup later... and use a timeout */
184 /* g_print ("Finalize run on %p %s.%s\n", o, mono_object_class (o)->name_space, mono_object_class (o)->name); */
186 /* Use _internal here, since this thread can enter a doomed appdomain */
187 mono_domain_set_internal (mono_object_domain (o));
189 /* delegates that have a native function pointer allocated are
190 * registered for finalization, but they don't have a Finalize
191 * method, because in most cases it's not needed and it's just a waste.
193 if (o->vtable->klass->delegate) {
194 MonoDelegate* del = (MonoDelegate*)o;
195 if (del->delegate_trampoline)
196 mono_delegate_free_ftnptr ((MonoDelegate*)o);
197 mono_domain_set_internal (caller_domain);
201 finalizer = mono_class_get_finalizer (o->vtable->klass);
203 /* If object has a CCW but has no finalizer, it was only
204 * registered for finalization in order to free the CCW.
205 * Else it needs the regular finalizer run.
206 * FIXME: what to do about ressurection and suppression
207 * of finalizer on object with CCW.
209 if (mono_marshal_free_ccw (o) && !finalizer) {
210 mono_domain_set_internal (caller_domain);
215 * To avoid the locking plus the other overhead of mono_runtime_invoke (),
216 * create and precompile a wrapper which calls the finalize method using
220 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Compiling finalizer.", o->vtable->klass->name, o);
222 if (!domain->finalize_runtime_invoke) {
223 MonoMethod *invoke = mono_marshal_get_runtime_invoke (mono_class_get_method_from_name_flags (mono_defaults.object_class, "Finalize", 0, 0), TRUE);
225 domain->finalize_runtime_invoke = mono_compile_method (invoke);
228 runtime_invoke = domain->finalize_runtime_invoke;
230 mono_runtime_class_init (o->vtable);
232 if (G_UNLIKELY (MONO_GC_FINALIZE_INVOKE_ENABLED ())) {
233 MONO_GC_FINALIZE_INVOKE ((unsigned long)o, mono_object_get_size (o),
234 o->vtable->klass->name_space, o->vtable->klass->name);
238 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Calling finalizer.", o->vtable->klass->name, o);
240 runtime_invoke (o, NULL, &exc, NULL);
243 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Returned from finalizer.", o->vtable->klass->name, o);
246 mono_thread_internal_unhandled_exception (exc);
248 mono_domain_set_internal (caller_domain);
252 mono_gc_finalize_threadpool_threads (void)
254 while (threads_to_finalize) {
255 MonoInternalThread *thread = (MonoInternalThread*) mono_mlist_get_data (threads_to_finalize);
257 /* Force finalization of the thread. */
258 thread->threadpool_thread = FALSE;
259 mono_object_register_finalizer ((MonoObject*)thread);
261 mono_gc_run_finalize (thread, NULL);
263 threads_to_finalize = mono_mlist_next (threads_to_finalize);
268 mono_gc_out_of_memory (size_t size)
271 * we could allocate at program startup some memory that we could release
272 * back to the system at this point if we're really low on memory (ie, size is
273 * lower than the memory we set apart)
275 mono_raise_exception (mono_domain_get ()->out_of_memory_ex);
281 * Some of our objects may point to a different address than the address returned by GC_malloc()
282 * (because of the GetHashCode hack), but we need to pass the real address to register_finalizer.
283 * This also means that in the callback we need to adjust the pointer to get back the real
285 * We also need to be consistent in the use of the GC_debug* variants of malloc and register_finalizer,
286 * since that, too, can cause the underlying pointer to be offset.
289 object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*))
294 mono_raise_exception (mono_get_exception_argument_null ("obj"));
296 domain = obj->vtable->domain;
299 if (mono_domain_is_unloading (domain) && (callback != NULL))
301 * Can't register finalizers in a dying appdomain, since they
302 * could be invoked after the appdomain has been unloaded.
306 mono_domain_finalizers_lock (domain);
309 g_hash_table_insert (domain->finalizable_objects_hash, obj, obj);
311 g_hash_table_remove (domain->finalizable_objects_hash, obj);
313 mono_domain_finalizers_unlock (domain);
315 mono_gc_register_for_finalization (obj, callback);
316 #elif defined(HAVE_SGEN_GC)
318 * If we register finalizers for domains that are unloading we might
319 * end up running them while or after the domain is being cleared, so
320 * the objects will not be valid anymore.
322 if (!mono_domain_is_unloading (domain)) {
324 mono_gc_register_for_finalization (obj, callback);
325 MONO_FINISH_TRY_BLOCKING;
331 * mono_object_register_finalizer:
332 * @obj: object to register
334 * Records that object @obj has a finalizer, this will call the
335 * Finalize method when the garbage collector disposes the object.
339 mono_object_register_finalizer (MonoObject *obj)
341 /* g_print ("Registered finalizer on %p %s.%s\n", obj, mono_object_class (obj)->name_space, mono_object_class (obj)->name); */
342 object_register_finalizer (obj, mono_gc_run_finalize);
346 * mono_domain_finalize:
347 * @domain: the domain to finalize
348 * @timeout: msects to wait for the finalization to complete, -1 to wait indefinitely
350 * Request finalization of all finalizable objects inside @domain. Wait
351 * @timeout msecs for the finalization to complete.
353 * Returns: TRUE if succeeded, FALSE if there was a timeout
357 mono_domain_finalize (MonoDomain *domain, guint32 timeout)
359 DomainFinalizationReq *req;
362 MonoInternalThread *thread = mono_thread_internal_current ();
364 #if defined(__native_client__)
368 if (mono_thread_internal_current () == gc_thread)
369 /* We are called from inside a finalizer, not much we can do here */
373 * No need to create another thread 'cause the finalizer thread
374 * is still working and will take care of running the finalizers
380 /* We don't support domain finalization without a GC */
381 if (mono_gc_is_null ())
384 mono_gc_collect (mono_gc_max_generation ());
386 done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
387 if (done_event == NULL) {
391 req = g_new0 (DomainFinalizationReq, 1);
392 req->domain = domain;
393 req->done_event = done_event;
395 if (domain == mono_get_root_domain ())
396 finalizing_root_domain = TRUE;
398 mono_finalizer_lock ();
400 domains_to_finalize = g_slist_append (domains_to_finalize, req);
402 mono_finalizer_unlock ();
404 /* Tell the finalizer thread to finalize this appdomain */
405 mono_gc_finalize_notify ();
411 res = guarded_wait (done_event, timeout, TRUE);
412 /* printf ("WAIT RES: %d.\n", res); */
414 if (res == WAIT_IO_COMPLETION) {
415 if ((thread->state & (ThreadState_StopRequested | ThreadState_SuspendRequested)) != 0)
417 } else if (res == WAIT_TIMEOUT) {
418 /* We leak the handle here */
425 CloseHandle (done_event);
427 if (domain == mono_get_root_domain ()) {
428 mono_threadpool_ms_cleanup ();
429 mono_gc_finalize_threadpool_threads ();
436 ves_icall_System_GC_InternalCollect (int generation)
438 mono_gc_collect (generation);
442 ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection)
445 mono_gc_collect (mono_gc_max_generation ());
446 return mono_gc_get_used_size ();
450 ves_icall_System_GC_KeepAlive (MonoObject *obj)
458 ves_icall_System_GC_ReRegisterForFinalize (MonoObject *obj)
460 MONO_CHECK_ARG_NULL (obj,);
462 object_register_finalizer (obj, mono_gc_run_finalize);
466 ves_icall_System_GC_SuppressFinalize (MonoObject *obj)
468 MONO_CHECK_ARG_NULL (obj,);
470 /* delegates have no finalizers, but we register them to deal with the
471 * unmanaged->managed trampoline. We don't let the user suppress it
472 * otherwise we'd leak it.
474 if (obj->vtable->klass->delegate)
477 /* FIXME: Need to handle case where obj has COM Callable Wrapper
478 * generated for it that needs cleaned up, but user wants to suppress
479 * their derived object finalizer. */
481 object_register_finalizer (obj, NULL);
485 ves_icall_System_GC_WaitForPendingFinalizers (void)
487 if (mono_gc_is_null ())
490 if (!mono_gc_pending_finalizers ())
493 if (mono_thread_internal_current () == gc_thread)
494 /* Avoid deadlocks */
498 If the finalizer thread is not live, lets pretend no finalizers are pending since the current thread might
499 be the one responsible for starting it up.
501 if (gc_thread == NULL)
504 ResetEvent (pending_done_event);
505 mono_gc_finalize_notify ();
506 /* g_print ("Waiting for pending finalizers....\n"); */
507 guarded_wait (pending_done_event, INFINITE, TRUE);
508 /* g_print ("Done pending....\n"); */
512 ves_icall_System_GC_register_ephemeron_array (MonoObject *array)
515 if (!mono_gc_ephemeron_array_add (array)) {
516 mono_set_pending_exception (mono_object_domain (array)->out_of_memory_ex);
523 ves_icall_System_GC_get_ephemeron_tombstone (void)
525 return mono_domain_get ()->ephemeron_tombstone;
528 #define mono_allocator_lock() mono_mutex_lock (&allocator_section)
529 #define mono_allocator_unlock() mono_mutex_unlock (&allocator_section)
530 static mono_mutex_t allocator_section;
533 ves_icall_System_GCHandle_GetTarget (guint32 handle)
535 return mono_gchandle_get_target (handle);
539 * if type == -1, change the target of the handle, otherwise allocate a new handle.
542 ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type)
545 mono_gchandle_set_target (handle, obj);
546 /* the handle doesn't change */
551 return mono_gchandle_new_weakref (obj, FALSE);
552 case HANDLE_WEAK_TRACK:
553 return mono_gchandle_new_weakref (obj, TRUE);
555 return mono_gchandle_new (obj, FALSE);
557 return mono_gchandle_new (obj, TRUE);
559 g_assert_not_reached ();
565 ves_icall_System_GCHandle_FreeHandle (guint32 handle)
567 mono_gchandle_free (handle);
571 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
575 if (MONO_GC_HANDLE_TYPE (handle) != HANDLE_PINNED)
577 obj = mono_gchandle_get_target (handle);
579 MonoClass *klass = mono_object_class (obj);
580 if (klass == mono_defaults.string_class) {
581 return mono_string_chars ((MonoString*)obj);
582 } else if (klass->rank) {
583 return mono_array_addr ((MonoArray*)obj, char, 0);
585 /* the C# code will check and throw the exception */
586 /* FIXME: missing !klass->blittable test, see bug #61134 */
587 if ((klass->flags & TYPE_ATTRIBUTE_LAYOUT_MASK) == TYPE_ATTRIBUTE_AUTO_LAYOUT)
589 return (char*)obj + sizeof (MonoObject);
596 ves_icall_Mono_Runtime_SetGCAllowSynchronousMajor (MonoBoolean flag)
598 return mono_gc_set_allow_synchronous_major (flag);
602 mono_gc_GCHandle_CheckCurrentDomain (guint32 gchandle)
604 return mono_gchandle_is_in_domain (gchandle, mono_domain_get ());
607 #ifdef MONO_HAS_SEMAPHORES
608 static MonoSemType finalizer_sem;
610 static HANDLE finalizer_event;
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 #ifdef MONO_HAS_SEMAPHORES
624 MONO_SEM_POST (&finalizer_sem);
626 SetEvent (finalizer_event);
633 collect_objects (gpointer key, gpointer value, gpointer user_data)
635 GPtrArray *arr = (GPtrArray*)user_data;
636 g_ptr_array_add (arr, key);
642 * finalize_domain_objects:
644 * Run the finalizers of all finalizable objects in req->domain.
647 finalize_domain_objects (DomainFinalizationReq *req)
649 MonoDomain *domain = req->domain;
652 #define NUM_FOBJECTS 64
653 MonoObject *to_finalize [NUM_FOBJECTS];
657 /* Process finalizers which are already in the queue */
658 mono_gc_invoke_finalizers ();
661 while (g_hash_table_size (domain->finalizable_objects_hash) > 0) {
665 * Since the domain is unloading, nobody is allowed to put
666 * new entries into the hash table. But finalize_object might
667 * remove entries from the hash table, so we make a copy.
669 objs = g_ptr_array_new ();
670 g_hash_table_foreach (domain->finalizable_objects_hash, collect_objects, objs);
671 /* printf ("FINALIZING %d OBJECTS.\n", objs->len); */
673 for (i = 0; i < objs->len; ++i) {
674 MonoObject *o = (MonoObject*)g_ptr_array_index (objs, i);
675 /* FIXME: Avoid finalizing threads, etc */
676 mono_gc_run_finalize (o, 0);
679 g_ptr_array_free (objs, TRUE);
681 #elif defined(HAVE_SGEN_GC)
682 while ((count = mono_gc_finalizers_for_domain (domain, to_finalize, NUM_FOBJECTS))) {
684 for (i = 0; i < count; ++i) {
685 mono_gc_run_finalize (to_finalize [i], 0);
690 /* cleanup the reference queue */
691 reference_queue_clear_for_domain (domain);
693 /* printf ("DONE.\n"); */
694 SetEvent (req->done_event);
696 /* The event is closed in mono_domain_finalize if we get here */
701 finalizer_thread (gpointer unused)
703 gboolean wait = TRUE;
706 /* Wait to be notified that there's at least one
710 g_assert (mono_domain_get () == mono_get_root_domain ());
711 mono_gc_set_skip_thread (TRUE);
712 MONO_PREPARE_BLOCKING;
715 /* An alertable wait is required so this thread can be suspended on windows */
716 #ifdef MONO_HAS_SEMAPHORES
717 MONO_SEM_WAIT_ALERTABLE (&finalizer_sem, TRUE);
719 WaitForSingleObjectEx (finalizer_event, INFINITE, TRUE);
723 MONO_FINISH_BLOCKING;
724 mono_gc_set_skip_thread (FALSE);
726 mono_threads_perform_thread_dump ();
728 mono_console_handle_async_ops ();
730 mono_attach_maybe_start ();
732 if (domains_to_finalize) {
733 mono_finalizer_lock ();
734 if (domains_to_finalize) {
735 DomainFinalizationReq *req = domains_to_finalize->data;
736 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
737 mono_finalizer_unlock ();
739 finalize_domain_objects (req);
741 mono_finalizer_unlock ();
745 /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup),
746 * before the domain is unloaded.
748 mono_gc_invoke_finalizers ();
750 mono_threads_join_threads ();
752 reference_queue_proccess_all ();
754 #ifdef MONO_HAS_SEMAPHORES
755 /* Avoid posting the pending done event until there are pending finalizers */
756 if (MONO_SEM_TIMEDWAIT (&finalizer_sem, 0) == 0)
757 /* Don't wait again at the start of the loop */
760 SetEvent (pending_done_event);
762 SetEvent (pending_done_event);
766 mono_finalizer_lock ();
767 finalizer_thread_exited = TRUE;
768 mono_cond_signal (&exited_cond);
769 mono_finalizer_unlock ();
774 #ifndef LAZY_GC_THREAD_CREATION
778 mono_gc_init_finalizer_thread (void)
780 gc_thread = mono_thread_create_internal (mono_domain_get (), finalizer_thread, NULL, FALSE, 0);
781 ves_icall_System_Threading_Thread_SetName_internal (gc_thread, mono_string_new (mono_domain_get (), "Finalizer"));
787 mono_mutex_init_recursive (&allocator_section);
789 mono_mutex_init_recursive (&finalizer_mutex);
790 mono_mutex_init_recursive (&reference_queue_mutex);
792 mono_counters_register ("Minor GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.minor_gc_count);
793 mono_counters_register ("Major GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.major_gc_count);
794 mono_counters_register ("Minor GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.minor_gc_time);
795 mono_counters_register ("Major GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time);
796 mono_counters_register ("Major GC time concurrent", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time_concurrent);
798 mono_gc_base_init ();
800 if (mono_gc_is_disabled ()) {
805 finalizer_event = CreateEvent (NULL, FALSE, FALSE, NULL);
806 g_assert (finalizer_event);
807 pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
808 g_assert (pending_done_event);
809 mono_cond_init (&exited_cond, 0);
810 #ifdef MONO_HAS_SEMAPHORES
811 MONO_SEM_INIT (&finalizer_sem, 0);
814 #ifndef LAZY_GC_THREAD_CREATION
815 mono_gc_init_finalizer_thread ();
820 mono_gc_cleanup (void)
823 g_message ("%s: cleaning up finalizer", __func__);
826 if (mono_gc_is_null ())
831 if (mono_thread_internal_current () != gc_thread) {
832 gboolean timed_out = FALSE;
833 guint32 start_ticks = mono_msec_ticks ();
834 guint32 end_ticks = start_ticks + 2000;
836 mono_gc_finalize_notify ();
837 /* Finishing the finalizer thread, so wait a little bit... */
838 /* MS seems to wait for about 2 seconds */
839 while (!finalizer_thread_exited) {
840 guint32 current_ticks = mono_msec_ticks ();
843 if (current_ticks >= end_ticks)
846 timeout = end_ticks - current_ticks;
847 MONO_PREPARE_BLOCKING;
848 mono_finalizer_lock ();
849 if (!finalizer_thread_exited)
850 mono_cond_timedwait_ms (&exited_cond, &finalizer_mutex, timeout);
851 mono_finalizer_unlock ();
852 MONO_FINISH_BLOCKING;
855 if (!finalizer_thread_exited) {
858 /* Set a flag which the finalizer thread can check */
859 suspend_finalizers = TRUE;
861 /* Try to abort the thread, in the hope that it is running managed code */
862 mono_thread_internal_stop (gc_thread);
864 /* Wait for it to stop */
865 ret = guarded_wait (gc_thread->handle, 100, TRUE);
867 if (ret == WAIT_TIMEOUT) {
869 * The finalizer thread refused to die. There is not much we
870 * can do here, since the runtime is shutting down so the
871 * state the finalizer thread depends on will vanish.
873 g_warning ("Shutting down finalizer thread timed out.");
881 /* Wait for the thread to actually exit */
882 ret = guarded_wait (gc_thread->handle, INFINITE, TRUE);
883 g_assert (ret == WAIT_OBJECT_0);
885 mono_thread_join (GUINT_TO_POINTER (gc_thread->tid));
889 mono_gc_base_cleanup ();
892 mono_reference_queue_cleanup ();
894 mono_mutex_destroy (&allocator_section);
895 mono_mutex_destroy (&finalizer_mutex);
896 mono_mutex_destroy (&reference_queue_mutex);
900 mono_gc_is_finalizer_internal_thread (MonoInternalThread *thread)
902 return thread == gc_thread;
906 * mono_gc_is_finalizer_thread:
907 * @thread: the thread to test.
909 * In Mono objects are finalized asynchronously on a separate thread.
910 * This routine tests whether the @thread argument represents the
911 * finalization thread.
913 * Returns true if @thread is the finalization thread.
916 mono_gc_is_finalizer_thread (MonoThread *thread)
918 return mono_gc_is_finalizer_internal_thread (thread->internal_thread);
921 #if defined(__MACH__)
922 static pthread_t mach_exception_thread;
925 mono_gc_register_mach_exception_thread (pthread_t thread)
927 mach_exception_thread = thread;
931 mono_gc_get_mach_exception_thread (void)
933 return mach_exception_thread;
939 mono_gc_alloc_mature (MonoVTable *vtable)
941 return mono_object_new_specific (vtable);
946 static MonoReferenceQueue *ref_queues;
949 ref_list_remove_element (RefQueueEntry **prev, RefQueueEntry *element)
952 /* Guard if head is changed concurrently. */
953 while (*prev != element)
954 prev = &(*prev)->next;
955 } while (prev && InterlockedCompareExchangePointer ((void*)prev, element->next, element) != element);
959 ref_list_push (RefQueueEntry **head, RefQueueEntry *value)
961 RefQueueEntry *current;
964 value->next = current;
965 STORE_STORE_FENCE; /*Must make sure the previous store is visible before the CAS. */
966 } while (InterlockedCompareExchangePointer ((void*)head, value, current) != current);
970 reference_queue_proccess (MonoReferenceQueue *queue)
972 RefQueueEntry **iter = &queue->queue;
973 RefQueueEntry *entry;
974 while ((entry = *iter)) {
975 if (queue->should_be_deleted || !mono_gchandle_get_target (entry->gchandle)) {
976 mono_gchandle_free ((guint32)entry->gchandle);
977 ref_list_remove_element (iter, entry);
978 queue->callback (entry->user_data);
987 reference_queue_proccess_all (void)
989 MonoReferenceQueue **iter;
990 MonoReferenceQueue *queue = ref_queues;
991 for (; queue; queue = queue->next)
992 reference_queue_proccess (queue);
995 mono_mutex_lock (&reference_queue_mutex);
996 for (iter = &ref_queues; *iter;) {
998 if (!queue->should_be_deleted) {
1003 mono_mutex_unlock (&reference_queue_mutex);
1004 reference_queue_proccess (queue);
1007 *iter = queue->next;
1010 mono_mutex_unlock (&reference_queue_mutex);
1014 mono_reference_queue_cleanup (void)
1016 MonoReferenceQueue *queue = ref_queues;
1017 for (; queue; queue = queue->next)
1018 queue->should_be_deleted = TRUE;
1019 reference_queue_proccess_all ();
1023 reference_queue_clear_for_domain (MonoDomain *domain)
1025 MonoReferenceQueue *queue = ref_queues;
1026 for (; queue; queue = queue->next) {
1027 RefQueueEntry **iter = &queue->queue;
1028 RefQueueEntry *entry;
1029 while ((entry = *iter)) {
1030 if (entry->domain == domain) {
1031 mono_gchandle_free ((guint32)entry->gchandle);
1032 ref_list_remove_element (iter, entry);
1033 queue->callback (entry->user_data);
1036 iter = &entry->next;
1042 * mono_gc_reference_queue_new:
1043 * @callback callback used when processing collected entries.
1045 * Create a new reference queue used to process collected objects.
1046 * A reference queue let you add a pair of (managed object, user data)
1047 * using the mono_gc_reference_queue_add method.
1049 * Once the managed object is collected @callback will be called
1050 * in the finalizer thread with 'user data' as argument.
1052 * The callback is called from the finalizer thread without any locks held.
1053 * When a AppDomain is unloaded, all callbacks for objects belonging to it
1056 * @returns the new queue.
1059 mono_gc_reference_queue_new (mono_reference_queue_callback callback)
1061 MonoReferenceQueue *res = g_new0 (MonoReferenceQueue, 1);
1062 res->callback = callback;
1064 mono_mutex_lock (&reference_queue_mutex);
1065 res->next = ref_queues;
1067 mono_mutex_unlock (&reference_queue_mutex);
1073 * mono_gc_reference_queue_add:
1074 * @queue the queue to add the reference to.
1075 * @obj the object to be watched for collection
1076 * @user_data parameter to be passed to the queue callback
1078 * Queue an object to be watched for collection, when the @obj is
1079 * collected, the callback that was registered for the @queue will
1080 * be invoked with @user_data as argument.
1082 * @returns false if the queue is scheduled to be freed.
1085 mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *user_data)
1087 RefQueueEntry *entry;
1088 if (queue->should_be_deleted)
1091 entry = g_new0 (RefQueueEntry, 1);
1092 entry->user_data = user_data;
1093 entry->domain = mono_object_domain (obj);
1095 entry->gchandle = mono_gchandle_new_weakref (obj, TRUE);
1096 mono_object_register_finalizer (obj);
1098 ref_list_push (&queue->queue, entry);
1103 * mono_gc_reference_queue_free:
1104 * @queue the queue that should be freed.
1106 * This operation signals that @queue should be freed. This operation is deferred
1107 * as it happens on the finalizer thread.
1109 * After this call, no further objects can be queued. It's the responsibility of the
1110 * caller to make sure that no further attempt to access queue will be made.
1113 mono_gc_reference_queue_free (MonoReferenceQueue *queue)
1115 queue->should_be_deleted = TRUE;