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)
9 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
16 #include <mono/metadata/gc-internals.h>
17 #include <mono/metadata/mono-gc.h>
18 #include <mono/metadata/threads.h>
19 #include <mono/metadata/tabledefs.h>
20 #include <mono/metadata/exception.h>
21 #include <mono/metadata/profiler-private.h>
22 #include <mono/metadata/domain-internals.h>
23 #include <mono/metadata/class-internals.h>
24 #include <mono/metadata/metadata-internals.h>
25 #include <mono/metadata/mono-mlist.h>
26 #include <mono/metadata/threads-types.h>
27 #include <mono/metadata/threadpool-ms.h>
28 #include <mono/sgen/sgen-conf.h>
29 #include <mono/sgen/sgen-gc.h>
30 #include <mono/utils/mono-logger-internals.h>
31 #include <mono/metadata/gc-internals.h>
32 #include <mono/metadata/marshal.h> /* for mono_delegate_free_ftnptr () */
33 #include <mono/metadata/attach.h>
34 #include <mono/metadata/console-io.h>
35 #include <mono/utils/mono-os-semaphore.h>
36 #include <mono/utils/mono-memory-model.h>
37 #include <mono/utils/mono-counters.h>
38 #include <mono/utils/mono-time.h>
39 #include <mono/utils/dtrace.h>
40 #include <mono/utils/mono-threads.h>
41 #include <mono/utils/atomic.h>
42 #include <mono/utils/mono-coop-semaphore.h>
43 #include <mono/utils/hazard-pointer.h>
49 typedef struct DomainFinalizationReq {
52 } DomainFinalizationReq;
54 static gboolean gc_disabled = FALSE;
56 static gboolean finalizing_root_domain = FALSE;
58 gboolean log_finalizers = FALSE;
59 gboolean mono_do_not_finalize = FALSE;
60 gchar **mono_do_not_finalize_class_names = NULL;
62 #define mono_finalizer_lock() mono_coop_mutex_lock (&finalizer_mutex)
63 #define mono_finalizer_unlock() mono_coop_mutex_unlock (&finalizer_mutex)
64 static MonoCoopMutex finalizer_mutex;
65 static MonoCoopMutex reference_queue_mutex;
67 static GSList *domains_to_finalize= NULL;
68 static MonoMList *threads_to_finalize = NULL;
70 static gboolean finalizer_thread_exited;
71 /* Uses finalizer_mutex */
72 static MonoCoopCond exited_cond;
74 static MonoInternalThread *gc_thread;
76 static void object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*), MonoError *error);
78 static void reference_queue_proccess_all (void);
79 static void mono_reference_queue_cleanup (void);
80 static void reference_queue_clear_for_domain (MonoDomain *domain);
81 static HANDLE pending_done_event;
84 guarded_wait (HANDLE handle, guint32 timeout, gboolean alertable)
89 result = WaitForSingleObjectEx (handle, timeout, alertable);
96 add_thread_to_finalize (MonoInternalThread *thread, MonoError *error)
98 mono_error_init (error);
99 mono_finalizer_lock ();
100 if (!threads_to_finalize)
101 MONO_GC_REGISTER_ROOT_SINGLE (threads_to_finalize, MONO_ROOT_SOURCE_FINALIZER_QUEUE, "finalizable threads list");
102 threads_to_finalize = mono_mlist_append_checked (threads_to_finalize, (MonoObject*)thread, error);
103 mono_finalizer_unlock ();
104 return is_ok (error);
107 static gboolean suspend_finalizers = FALSE;
109 * actually, we might want to queue the finalize requests in a separate thread,
110 * but we need to be careful about the execution domain of the thread...
113 mono_gc_run_finalize (void *obj, void *data)
116 MonoObject *exc = NULL;
121 MonoMethod* finalizer = NULL;
122 MonoDomain *caller_domain = mono_domain_get ();
124 RuntimeInvokeFunction runtime_invoke;
126 // This function is called from the innards of the GC, so our best alternative for now is to do polling here
127 mono_threads_safepoint ();
129 o = (MonoObject*)((char*)obj + GPOINTER_TO_UINT (data));
131 if (mono_do_not_finalize) {
132 if (!mono_do_not_finalize_class_names)
135 size_t namespace_len = strlen (o->vtable->klass->name_space);
136 for (int i = 0; mono_do_not_finalize_class_names [i]; ++i) {
137 const char *name = mono_do_not_finalize_class_names [i];
138 if (strncmp (name, o->vtable->klass->name_space, namespace_len))
140 if (name [namespace_len] != '.')
142 if (strcmp (name + namespace_len + 1, o->vtable->klass->name))
149 g_log ("mono-gc-finalizers", G_LOG_LEVEL_DEBUG, "<%s at %p> Starting finalizer checks.", o->vtable->klass->name, o);
151 if (suspend_finalizers)
154 domain = o->vtable->domain;
157 mono_domain_finalizers_lock (domain);
159 o2 = (MonoObject *)g_hash_table_lookup (domain->finalizable_objects_hash, o);
161 mono_domain_finalizers_unlock (domain);
164 /* Already finalized somehow */
168 /* make sure the finalizer is not called again if the object is resurrected */
169 object_register_finalizer ((MonoObject *)obj, NULL, &error);
170 mono_error_assert_ok (&error); /* FIXME don't swallow the error */
173 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Registered finalizer as processed.", o->vtable->klass->name, o);
175 if (o->vtable->klass == mono_defaults.internal_thread_class) {
176 MonoInternalThread *t = (MonoInternalThread*)o;
178 if (mono_gc_is_finalizer_internal_thread (t))
179 /* Avoid finalizing ourselves */
182 if (t->threadpool_thread && finalizing_root_domain) {
183 /* Don't finalize threadpool threads when
184 shutting down - they're finalized when the
185 threadpool shuts down. */
186 if (!add_thread_to_finalize (t, &error))
187 goto unhandled_error;
192 if (o->vtable->klass->image == mono_defaults.corlib && !strcmp (o->vtable->klass->name, "DynamicMethod") && finalizing_root_domain) {
194 * These can't be finalized during unloading/shutdown, since that would
195 * free the native code which can still be referenced by other
197 * FIXME: This is not perfect, objects dying at the same time as
198 * dynamic methods can still reference them even when !shutdown.
203 if (mono_runtime_get_no_exec ())
206 /* speedup later... and use a timeout */
207 /* g_print ("Finalize run on %p %s.%s\n", o, mono_object_class (o)->name_space, mono_object_class (o)->name); */
209 /* Use _internal here, since this thread can enter a doomed appdomain */
210 mono_domain_set_internal (mono_object_domain (o));
212 /* delegates that have a native function pointer allocated are
213 * registered for finalization, but they don't have a Finalize
214 * method, because in most cases it's not needed and it's just a waste.
216 if (o->vtable->klass->delegate) {
217 MonoDelegate* del = (MonoDelegate*)o;
218 if (del->delegate_trampoline)
219 mono_delegate_free_ftnptr ((MonoDelegate*)o);
220 mono_domain_set_internal (caller_domain);
224 finalizer = mono_class_get_finalizer (o->vtable->klass);
226 /* If object has a CCW but has no finalizer, it was only
227 * registered for finalization in order to free the CCW.
228 * Else it needs the regular finalizer run.
229 * FIXME: what to do about ressurection and suppression
230 * of finalizer on object with CCW.
232 if (mono_marshal_free_ccw (o) && !finalizer) {
233 mono_domain_set_internal (caller_domain);
238 * To avoid the locking plus the other overhead of mono_runtime_invoke_checked (),
239 * create and precompile a wrapper which calls the finalize method using
243 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Compiling finalizer.", o->vtable->klass->name, o);
245 if (!domain->finalize_runtime_invoke) {
246 MonoMethod *invoke = mono_marshal_get_runtime_invoke (mono_class_get_method_from_name_flags (mono_defaults.object_class, "Finalize", 0, 0), TRUE);
248 domain->finalize_runtime_invoke = mono_compile_method_checked (invoke, &error);
249 mono_error_assert_ok (&error); /* expect this not to fail */
252 runtime_invoke = (RuntimeInvokeFunction)domain->finalize_runtime_invoke;
254 mono_runtime_class_init_full (o->vtable, &error);
256 goto unhandled_error;
258 if (G_UNLIKELY (MONO_GC_FINALIZE_INVOKE_ENABLED ())) {
259 MONO_GC_FINALIZE_INVOKE ((unsigned long)o, mono_object_get_size (o),
260 o->vtable->klass->name_space, o->vtable->klass->name);
264 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Calling finalizer.", o->vtable->klass->name, o);
266 runtime_invoke (o, NULL, &exc, NULL);
269 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Returned from finalizer.", o->vtable->klass->name, o);
273 exc = (MonoObject*)mono_error_convert_to_exception (&error);
275 mono_thread_internal_unhandled_exception (exc);
277 mono_domain_set_internal (caller_domain);
281 mono_gc_finalize_threadpool_threads (void)
284 while (threads_to_finalize) {
285 MonoInternalThread *thread = (MonoInternalThread*) mono_mlist_get_data (threads_to_finalize);
287 /* Force finalization of the thread. */
288 thread->threadpool_thread = FALSE;
289 mono_object_register_finalizer ((MonoObject*)thread, &error);
290 mono_error_assert_ok (&error); /* FIXME don't swallow the error */
292 mono_gc_run_finalize (thread, NULL);
294 threads_to_finalize = mono_mlist_next (threads_to_finalize);
299 mono_gc_out_of_memory (size_t size)
302 * we could allocate at program startup some memory that we could release
303 * back to the system at this point if we're really low on memory (ie, size is
304 * lower than the memory we set apart)
306 mono_raise_exception (mono_domain_get ()->out_of_memory_ex);
312 * Some of our objects may point to a different address than the address returned by GC_malloc()
313 * (because of the GetHashCode hack), but we need to pass the real address to register_finalizer.
314 * This also means that in the callback we need to adjust the pointer to get back the real
316 * We also need to be consistent in the use of the GC_debug* variants of malloc and register_finalizer,
317 * since that, too, can cause the underlying pointer to be offset.
320 object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*), MonoError *error)
324 mono_error_init (error);
327 mono_error_set_argument_null (error, "obj", "");
331 domain = obj->vtable->domain;
334 if (mono_domain_is_unloading (domain) && (callback != NULL))
336 * Can't register finalizers in a dying appdomain, since they
337 * could be invoked after the appdomain has been unloaded.
341 mono_domain_finalizers_lock (domain);
344 g_hash_table_insert (domain->finalizable_objects_hash, obj, obj);
346 g_hash_table_remove (domain->finalizable_objects_hash, obj);
348 mono_domain_finalizers_unlock (domain);
350 mono_gc_register_for_finalization (obj, callback);
351 #elif defined(HAVE_SGEN_GC)
353 * If we register finalizers for domains that are unloading we might
354 * end up running them while or after the domain is being cleared, so
355 * the objects will not be valid anymore.
357 if (!mono_domain_is_unloading (domain))
358 mono_gc_register_for_finalization (obj, callback);
363 * mono_object_register_finalizer:
364 * @obj: object to register
366 * Records that object @obj has a finalizer, this will call the
367 * Finalize method when the garbage collector disposes the object.
371 mono_object_register_finalizer (MonoObject *obj, MonoError *error)
373 /* g_print ("Registered finalizer on %p %s.%s\n", obj, mono_object_class (obj)->name_space, mono_object_class (obj)->name); */
374 object_register_finalizer (obj, mono_gc_run_finalize, error);
378 * mono_domain_finalize:
379 * @domain: the domain to finalize
380 * @timeout: msects to wait for the finalization to complete, -1 to wait indefinitely
382 * Request finalization of all finalizable objects inside @domain. Wait
383 * @timeout msecs for the finalization to complete.
385 * Returns: TRUE if succeeded, FALSE if there was a timeout
389 mono_domain_finalize (MonoDomain *domain, guint32 timeout)
391 DomainFinalizationReq *req;
394 MonoInternalThread *thread = mono_thread_internal_current ();
396 #if defined(__native_client__)
400 if (mono_thread_internal_current () == gc_thread)
401 /* We are called from inside a finalizer, not much we can do here */
405 * No need to create another thread 'cause the finalizer thread
406 * is still working and will take care of running the finalizers
412 /* We don't support domain finalization without a GC */
413 if (mono_gc_is_null ())
416 mono_gc_collect (mono_gc_max_generation ());
418 done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
419 if (done_event == NULL) {
423 req = g_new0 (DomainFinalizationReq, 1);
424 req->domain = domain;
425 req->done_event = done_event;
427 if (domain == mono_get_root_domain ())
428 finalizing_root_domain = TRUE;
430 mono_finalizer_lock ();
432 domains_to_finalize = g_slist_append (domains_to_finalize, req);
434 mono_finalizer_unlock ();
436 /* Tell the finalizer thread to finalize this appdomain */
437 mono_gc_finalize_notify ();
443 res = guarded_wait (done_event, timeout, TRUE);
444 /* printf ("WAIT RES: %d.\n", res); */
446 if (res == WAIT_IO_COMPLETION) {
447 if ((thread->state & (ThreadState_StopRequested | ThreadState_SuspendRequested)) != 0)
449 } else if (res == WAIT_TIMEOUT) {
450 /* We leak the handle here */
457 CloseHandle (done_event);
459 if (domain == mono_get_root_domain ()) {
460 mono_threadpool_ms_cleanup ();
461 mono_gc_finalize_threadpool_threads ();
464 mono_profiler_appdomain_event (domain, MONO_PROFILE_END_UNLOAD);
470 ves_icall_System_GC_InternalCollect (int generation)
472 mono_gc_collect (generation);
476 ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection)
479 mono_gc_collect (mono_gc_max_generation ());
480 return mono_gc_get_used_size ();
484 ves_icall_System_GC_KeepAlive (MonoObject *obj)
492 ves_icall_System_GC_ReRegisterForFinalize (MonoObject *obj)
496 MONO_CHECK_ARG_NULL (obj,);
498 object_register_finalizer (obj, mono_gc_run_finalize, &error);
499 mono_error_set_pending_exception (&error);
503 ves_icall_System_GC_SuppressFinalize (MonoObject *obj)
507 MONO_CHECK_ARG_NULL (obj,);
509 /* delegates have no finalizers, but we register them to deal with the
510 * unmanaged->managed trampoline. We don't let the user suppress it
511 * otherwise we'd leak it.
513 if (obj->vtable->klass->delegate)
516 /* FIXME: Need to handle case where obj has COM Callable Wrapper
517 * generated for it that needs cleaned up, but user wants to suppress
518 * their derived object finalizer. */
520 object_register_finalizer (obj, NULL, &error);
521 mono_error_set_pending_exception (&error);
525 ves_icall_System_GC_WaitForPendingFinalizers (void)
527 if (mono_gc_is_null ())
530 if (!mono_gc_pending_finalizers ())
533 if (mono_thread_internal_current () == gc_thread)
534 /* Avoid deadlocks */
538 If the finalizer thread is not live, lets pretend no finalizers are pending since the current thread might
539 be the one responsible for starting it up.
541 if (gc_thread == NULL)
544 ResetEvent (pending_done_event);
545 mono_gc_finalize_notify ();
546 /* g_print ("Waiting for pending finalizers....\n"); */
547 guarded_wait (pending_done_event, INFINITE, TRUE);
548 /* g_print ("Done pending....\n"); */
552 ves_icall_System_GC_register_ephemeron_array (MonoObject *array)
555 if (!mono_gc_ephemeron_array_add (array)) {
556 mono_set_pending_exception (mono_object_domain (array)->out_of_memory_ex);
563 ves_icall_System_GC_get_ephemeron_tombstone (void)
565 return mono_domain_get ()->ephemeron_tombstone;
569 ves_icall_System_GCHandle_GetTarget (guint32 handle)
571 return mono_gchandle_get_target (handle);
575 * if type == -1, change the target of the handle, otherwise allocate a new handle.
578 ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type)
581 mono_gchandle_set_target (handle, obj);
582 /* the handle doesn't change */
587 return mono_gchandle_new_weakref (obj, FALSE);
588 case HANDLE_WEAK_TRACK:
589 return mono_gchandle_new_weakref (obj, TRUE);
591 return mono_gchandle_new (obj, FALSE);
593 return mono_gchandle_new (obj, TRUE);
595 g_assert_not_reached ();
601 ves_icall_System_GCHandle_FreeHandle (guint32 handle)
603 mono_gchandle_free (handle);
607 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
611 if (MONO_GC_HANDLE_TYPE (handle) != HANDLE_PINNED)
613 obj = mono_gchandle_get_target (handle);
615 MonoClass *klass = mono_object_class (obj);
616 if (klass == mono_defaults.string_class) {
617 return mono_string_chars ((MonoString*)obj);
618 } else if (klass->rank) {
619 return mono_array_addr ((MonoArray*)obj, char, 0);
621 /* the C# code will check and throw the exception */
622 /* FIXME: missing !klass->blittable test, see bug #61134 */
623 if ((klass->flags & TYPE_ATTRIBUTE_LAYOUT_MASK) == TYPE_ATTRIBUTE_AUTO_LAYOUT)
625 return (char*)obj + sizeof (MonoObject);
632 mono_gc_GCHandle_CheckCurrentDomain (guint32 gchandle)
634 return mono_gchandle_is_in_domain (gchandle, mono_domain_get ());
637 static MonoCoopSem finalizer_sem;
638 static volatile gboolean finished=FALSE;
641 mono_gc_finalize_notify (void)
644 g_message ( "%s: prodding finalizer", __func__);
647 if (mono_gc_is_null ())
650 mono_coop_sem_post (&finalizer_sem);
654 This is the number of entries allowed in the hazard free queue before
655 we explicitly cycle the finalizer thread to trigger pumping the queue.
657 It was picked empirically by running the corlib test suite in a stress
658 scenario where all hazard entries are queued.
660 In this extreme scenario we double the number of times we cycle the finalizer
661 thread compared to just GC calls.
663 Entries are usually in the order of 100's of bytes each, so we're limiting
664 floating garbage to be in the order of a dozen kb.
666 static gboolean finalizer_thread_pulsed;
667 #define HAZARD_QUEUE_OVERFLOW_SIZE 20
670 hazard_free_queue_is_too_big (size_t size)
672 if (size < HAZARD_QUEUE_OVERFLOW_SIZE)
675 if (finalizer_thread_pulsed || InterlockedCompareExchange (&finalizer_thread_pulsed, TRUE, FALSE))
678 mono_gc_finalize_notify ();
682 hazard_free_queue_pump (void)
684 mono_thread_hazardous_try_free_all ();
685 finalizer_thread_pulsed = FALSE;
691 collect_objects (gpointer key, gpointer value, gpointer user_data)
693 GPtrArray *arr = (GPtrArray*)user_data;
694 g_ptr_array_add (arr, key);
700 * finalize_domain_objects:
702 * Run the finalizers of all finalizable objects in req->domain.
705 finalize_domain_objects (DomainFinalizationReq *req)
707 MonoDomain *domain = req->domain;
710 #define NUM_FOBJECTS 64
711 MonoObject *to_finalize [NUM_FOBJECTS];
715 /* Process finalizers which are already in the queue */
716 mono_gc_invoke_finalizers ();
719 while (g_hash_table_size (domain->finalizable_objects_hash) > 0) {
723 * Since the domain is unloading, nobody is allowed to put
724 * new entries into the hash table. But finalize_object might
725 * remove entries from the hash table, so we make a copy.
727 objs = g_ptr_array_new ();
728 g_hash_table_foreach (domain->finalizable_objects_hash, collect_objects, objs);
729 /* printf ("FINALIZING %d OBJECTS.\n", objs->len); */
731 for (i = 0; i < objs->len; ++i) {
732 MonoObject *o = (MonoObject*)g_ptr_array_index (objs, i);
733 /* FIXME: Avoid finalizing threads, etc */
734 mono_gc_run_finalize (o, 0);
737 g_ptr_array_free (objs, TRUE);
739 #elif defined(HAVE_SGEN_GC)
740 while ((count = mono_gc_finalizers_for_domain (domain, to_finalize, NUM_FOBJECTS))) {
742 for (i = 0; i < count; ++i) {
743 mono_gc_run_finalize (to_finalize [i], 0);
748 /* cleanup the reference queue */
749 reference_queue_clear_for_domain (domain);
751 /* printf ("DONE.\n"); */
752 SetEvent (req->done_event);
754 /* The event is closed in mono_domain_finalize if we get here */
759 finalizer_thread (gpointer unused)
762 mono_thread_set_name_internal (mono_thread_internal_current (), mono_string_new (mono_get_root_domain (), "Finalizer"), FALSE, &error);
763 mono_error_assert_ok (&error);
765 gboolean wait = TRUE;
767 /* Register a hazard free queue pump callback */
768 mono_hazard_pointer_install_free_queue_size_callback (hazard_free_queue_is_too_big);
771 /* Wait to be notified that there's at least one
775 g_assert (mono_domain_get () == mono_get_root_domain ());
776 mono_gc_set_skip_thread (TRUE);
779 /* An alertable wait is required so this thread can be suspended on windows */
780 mono_coop_sem_wait (&finalizer_sem, MONO_SEM_FLAGS_ALERTABLE);
784 mono_gc_set_skip_thread (FALSE);
786 mono_threads_perform_thread_dump ();
788 mono_console_handle_async_ops ();
790 mono_attach_maybe_start ();
792 if (domains_to_finalize) {
793 mono_finalizer_lock ();
794 if (domains_to_finalize) {
795 DomainFinalizationReq *req = (DomainFinalizationReq *)domains_to_finalize->data;
796 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
797 mono_finalizer_unlock ();
799 finalize_domain_objects (req);
801 mono_finalizer_unlock ();
805 /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup),
806 * before the domain is unloaded.
808 mono_gc_invoke_finalizers ();
810 mono_threads_join_threads ();
812 reference_queue_proccess_all ();
814 hazard_free_queue_pump ();
816 /* Avoid posting the pending done event until there are pending finalizers */
817 if (mono_coop_sem_timedwait (&finalizer_sem, 0, MONO_SEM_FLAGS_NONE) == 0) {
818 /* Don't wait again at the start of the loop */
821 SetEvent (pending_done_event);
825 mono_finalizer_lock ();
826 finalizer_thread_exited = TRUE;
827 mono_coop_cond_signal (&exited_cond);
828 mono_finalizer_unlock ();
833 #ifndef LAZY_GC_THREAD_CREATION
837 mono_gc_init_finalizer_thread (void)
840 gc_thread = mono_thread_create_internal (mono_domain_get (), finalizer_thread, NULL, FALSE, 0, &error);
841 mono_error_assert_ok (&error);
847 mono_coop_mutex_init_recursive (&finalizer_mutex);
848 mono_coop_mutex_init_recursive (&reference_queue_mutex);
850 mono_counters_register ("Minor GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.minor_gc_count);
851 mono_counters_register ("Major GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.major_gc_count);
852 mono_counters_register ("Minor GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.minor_gc_time);
853 mono_counters_register ("Major GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time);
854 mono_counters_register ("Major GC time concurrent", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time_concurrent);
856 mono_gc_base_init ();
858 if (mono_gc_is_disabled ()) {
863 pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
864 g_assert (pending_done_event);
865 mono_coop_cond_init (&exited_cond);
866 mono_coop_sem_init (&finalizer_sem, 0);
868 #ifndef LAZY_GC_THREAD_CREATION
869 mono_gc_init_finalizer_thread ();
874 mono_gc_cleanup (void)
877 g_message ("%s: cleaning up finalizer", __func__);
880 if (mono_gc_is_null ())
885 if (mono_thread_internal_current () != gc_thread) {
886 gboolean timed_out = FALSE;
887 gint64 start_ticks = mono_msec_ticks ();
888 gint64 end_ticks = start_ticks + 2000;
890 mono_gc_finalize_notify ();
891 /* Finishing the finalizer thread, so wait a little bit... */
892 /* MS seems to wait for about 2 seconds */
893 while (!finalizer_thread_exited) {
894 gint64 current_ticks = mono_msec_ticks ();
897 if (current_ticks >= end_ticks)
900 timeout = end_ticks - current_ticks;
901 mono_finalizer_lock ();
902 if (!finalizer_thread_exited)
903 mono_coop_cond_timedwait (&exited_cond, &finalizer_mutex, timeout);
904 mono_finalizer_unlock ();
907 if (!finalizer_thread_exited) {
910 /* Set a flag which the finalizer thread can check */
911 suspend_finalizers = TRUE;
913 /* Try to abort the thread, in the hope that it is running managed code */
914 mono_thread_internal_stop (gc_thread);
916 /* Wait for it to stop */
917 ret = guarded_wait (gc_thread->handle, 100, TRUE);
919 if (ret == WAIT_TIMEOUT) {
921 * The finalizer thread refused to die. There is not much we
922 * can do here, since the runtime is shutting down so the
923 * state the finalizer thread depends on will vanish.
925 g_warning ("Shutting down finalizer thread timed out.");
933 /* Wait for the thread to actually exit */
934 ret = guarded_wait (gc_thread->handle, INFINITE, TRUE);
935 g_assert (ret == WAIT_OBJECT_0);
937 mono_thread_join (GUINT_TO_POINTER (gc_thread->tid));
939 g_assert (finalizer_thread_exited);
942 mono_gc_base_cleanup ();
945 mono_reference_queue_cleanup ();
947 mono_coop_mutex_destroy (&finalizer_mutex);
948 mono_coop_mutex_destroy (&reference_queue_mutex);
952 mono_gc_is_finalizer_internal_thread (MonoInternalThread *thread)
954 return thread == gc_thread;
958 * mono_gc_is_finalizer_thread:
959 * @thread: the thread to test.
961 * In Mono objects are finalized asynchronously on a separate thread.
962 * This routine tests whether the @thread argument represents the
963 * finalization thread.
965 * Returns: TRUE if @thread is the finalization thread.
968 mono_gc_is_finalizer_thread (MonoThread *thread)
970 return mono_gc_is_finalizer_internal_thread (thread->internal_thread);
973 #if defined(__MACH__)
974 static pthread_t mach_exception_thread;
977 mono_gc_register_mach_exception_thread (pthread_t thread)
979 mach_exception_thread = thread;
983 mono_gc_get_mach_exception_thread (void)
985 return mach_exception_thread;
989 static MonoReferenceQueue *ref_queues;
992 ref_list_remove_element (RefQueueEntry **prev, RefQueueEntry *element)
995 /* Guard if head is changed concurrently. */
996 while (*prev != element)
997 prev = &(*prev)->next;
998 } while (prev && InterlockedCompareExchangePointer ((volatile gpointer *)prev, element->next, element) != element);
1002 ref_list_push (RefQueueEntry **head, RefQueueEntry *value)
1004 RefQueueEntry *current;
1007 value->next = current;
1008 STORE_STORE_FENCE; /*Must make sure the previous store is visible before the CAS. */
1009 } while (InterlockedCompareExchangePointer ((volatile gpointer *)head, value, current) != current);
1013 reference_queue_proccess (MonoReferenceQueue *queue)
1015 RefQueueEntry **iter = &queue->queue;
1016 RefQueueEntry *entry;
1017 while ((entry = *iter)) {
1018 if (queue->should_be_deleted || !mono_gchandle_get_target (entry->gchandle)) {
1019 mono_gchandle_free ((guint32)entry->gchandle);
1020 ref_list_remove_element (iter, entry);
1021 queue->callback (entry->user_data);
1024 iter = &entry->next;
1030 reference_queue_proccess_all (void)
1032 MonoReferenceQueue **iter;
1033 MonoReferenceQueue *queue = ref_queues;
1034 for (; queue; queue = queue->next)
1035 reference_queue_proccess (queue);
1038 mono_coop_mutex_lock (&reference_queue_mutex);
1039 for (iter = &ref_queues; *iter;) {
1041 if (!queue->should_be_deleted) {
1042 iter = &queue->next;
1046 mono_coop_mutex_unlock (&reference_queue_mutex);
1047 reference_queue_proccess (queue);
1050 *iter = queue->next;
1053 mono_coop_mutex_unlock (&reference_queue_mutex);
1057 mono_reference_queue_cleanup (void)
1059 MonoReferenceQueue *queue = ref_queues;
1060 for (; queue; queue = queue->next)
1061 queue->should_be_deleted = TRUE;
1062 reference_queue_proccess_all ();
1066 reference_queue_clear_for_domain (MonoDomain *domain)
1068 MonoReferenceQueue *queue = ref_queues;
1069 for (; queue; queue = queue->next) {
1070 RefQueueEntry **iter = &queue->queue;
1071 RefQueueEntry *entry;
1072 while ((entry = *iter)) {
1073 if (entry->domain == domain) {
1074 mono_gchandle_free ((guint32)entry->gchandle);
1075 ref_list_remove_element (iter, entry);
1076 queue->callback (entry->user_data);
1079 iter = &entry->next;
1085 * mono_gc_reference_queue_new:
1086 * @callback callback used when processing collected entries.
1088 * Create a new reference queue used to process collected objects.
1089 * A reference queue let you add a pair of (managed object, user data)
1090 * using the mono_gc_reference_queue_add method.
1092 * Once the managed object is collected @callback will be called
1093 * in the finalizer thread with 'user data' as argument.
1095 * The callback is called from the finalizer thread without any locks held.
1096 * When a AppDomain is unloaded, all callbacks for objects belonging to it
1099 * @returns the new queue.
1102 mono_gc_reference_queue_new (mono_reference_queue_callback callback)
1104 MonoReferenceQueue *res = g_new0 (MonoReferenceQueue, 1);
1105 res->callback = callback;
1107 mono_coop_mutex_lock (&reference_queue_mutex);
1108 res->next = ref_queues;
1110 mono_coop_mutex_unlock (&reference_queue_mutex);
1116 * mono_gc_reference_queue_add:
1117 * @queue the queue to add the reference to.
1118 * @obj the object to be watched for collection
1119 * @user_data parameter to be passed to the queue callback
1121 * Queue an object to be watched for collection, when the @obj is
1122 * collected, the callback that was registered for the @queue will
1123 * be invoked with @user_data as argument.
1125 * @returns false if the queue is scheduled to be freed.
1128 mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *user_data)
1131 RefQueueEntry *entry;
1132 if (queue->should_be_deleted)
1135 entry = g_new0 (RefQueueEntry, 1);
1136 entry->user_data = user_data;
1137 entry->domain = mono_object_domain (obj);
1139 entry->gchandle = mono_gchandle_new_weakref (obj, TRUE);
1140 mono_object_register_finalizer (obj, &error);
1141 mono_error_assert_ok (&error);
1143 ref_list_push (&queue->queue, entry);
1148 * mono_gc_reference_queue_free:
1149 * @queue the queue that should be freed.
1151 * This operation signals that @queue should be freed. This operation is deferred
1152 * as it happens on the finalizer thread.
1154 * After this call, no further objects can be queued. It's the responsibility of the
1155 * caller to make sure that no further attempt to access queue will be made.
1158 mono_gc_reference_queue_free (MonoReferenceQueue *queue)
1160 queue->should_be_deleted = TRUE;