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)
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 mono_error_raise_exception (&error); /* FIXME don't raise here */
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 add_thread_to_finalize (t);
191 if (o->vtable->klass->image == mono_defaults.corlib && !strcmp (o->vtable->klass->name, "DynamicMethod") && finalizing_root_domain) {
193 * These can't be finalized during unloading/shutdown, since that would
194 * free the native code which can still be referenced by other
196 * FIXME: This is not perfect, objects dying at the same time as
197 * dynamic methods can still reference them even when !shutdown.
202 if (mono_runtime_get_no_exec ())
205 /* speedup later... and use a timeout */
206 /* g_print ("Finalize run on %p %s.%s\n", o, mono_object_class (o)->name_space, mono_object_class (o)->name); */
208 /* Use _internal here, since this thread can enter a doomed appdomain */
209 mono_domain_set_internal (mono_object_domain (o));
211 /* delegates that have a native function pointer allocated are
212 * registered for finalization, but they don't have a Finalize
213 * method, because in most cases it's not needed and it's just a waste.
215 if (o->vtable->klass->delegate) {
216 MonoDelegate* del = (MonoDelegate*)o;
217 if (del->delegate_trampoline)
218 mono_delegate_free_ftnptr ((MonoDelegate*)o);
219 mono_domain_set_internal (caller_domain);
223 finalizer = mono_class_get_finalizer (o->vtable->klass);
225 /* If object has a CCW but has no finalizer, it was only
226 * registered for finalization in order to free the CCW.
227 * Else it needs the regular finalizer run.
228 * FIXME: what to do about ressurection and suppression
229 * of finalizer on object with CCW.
231 if (mono_marshal_free_ccw (o) && !finalizer) {
232 mono_domain_set_internal (caller_domain);
237 * To avoid the locking plus the other overhead of mono_runtime_invoke_checked (),
238 * create and precompile a wrapper which calls the finalize method using
242 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Compiling finalizer.", o->vtable->klass->name, o);
244 if (!domain->finalize_runtime_invoke) {
245 MonoMethod *invoke = mono_marshal_get_runtime_invoke (mono_class_get_method_from_name_flags (mono_defaults.object_class, "Finalize", 0, 0), TRUE);
247 domain->finalize_runtime_invoke = mono_compile_method_checked (invoke, &error);
248 mono_error_assert_ok (&error); /* expect this not to fail */
251 runtime_invoke = (RuntimeInvokeFunction)domain->finalize_runtime_invoke;
253 mono_runtime_class_init_full (o->vtable, &error);
254 mono_error_raise_exception (&error); /* FIXME don't raise here */
256 if (G_UNLIKELY (MONO_GC_FINALIZE_INVOKE_ENABLED ())) {
257 MONO_GC_FINALIZE_INVOKE ((unsigned long)o, mono_object_get_size (o),
258 o->vtable->klass->name_space, o->vtable->klass->name);
262 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Calling finalizer.", o->vtable->klass->name, o);
264 runtime_invoke (o, NULL, &exc, NULL);
267 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Returned from finalizer.", o->vtable->klass->name, o);
270 mono_thread_internal_unhandled_exception (exc);
272 mono_domain_set_internal (caller_domain);
276 mono_gc_finalize_threadpool_threads (void)
279 while (threads_to_finalize) {
280 MonoInternalThread *thread = (MonoInternalThread*) mono_mlist_get_data (threads_to_finalize);
282 /* Force finalization of the thread. */
283 thread->threadpool_thread = FALSE;
284 mono_object_register_finalizer ((MonoObject*)thread, &error);
285 mono_error_assert_ok (&error); /* FIXME don't swallow the error */
287 mono_gc_run_finalize (thread, NULL);
289 threads_to_finalize = mono_mlist_next (threads_to_finalize);
294 mono_gc_out_of_memory (size_t size)
297 * we could allocate at program startup some memory that we could release
298 * back to the system at this point if we're really low on memory (ie, size is
299 * lower than the memory we set apart)
301 mono_raise_exception (mono_domain_get ()->out_of_memory_ex);
307 * Some of our objects may point to a different address than the address returned by GC_malloc()
308 * (because of the GetHashCode hack), but we need to pass the real address to register_finalizer.
309 * This also means that in the callback we need to adjust the pointer to get back the real
311 * We also need to be consistent in the use of the GC_debug* variants of malloc and register_finalizer,
312 * since that, too, can cause the underlying pointer to be offset.
315 object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*), MonoError *error)
319 mono_error_init (error);
322 mono_error_set_argument_null (error, "obj", "");
326 domain = obj->vtable->domain;
329 if (mono_domain_is_unloading (domain) && (callback != NULL))
331 * Can't register finalizers in a dying appdomain, since they
332 * could be invoked after the appdomain has been unloaded.
336 mono_domain_finalizers_lock (domain);
339 g_hash_table_insert (domain->finalizable_objects_hash, obj, obj);
341 g_hash_table_remove (domain->finalizable_objects_hash, obj);
343 mono_domain_finalizers_unlock (domain);
345 mono_gc_register_for_finalization (obj, callback);
346 #elif defined(HAVE_SGEN_GC)
348 * If we register finalizers for domains that are unloading we might
349 * end up running them while or after the domain is being cleared, so
350 * the objects will not be valid anymore.
352 if (!mono_domain_is_unloading (domain))
353 mono_gc_register_for_finalization (obj, callback);
358 * mono_object_register_finalizer:
359 * @obj: object to register
361 * Records that object @obj has a finalizer, this will call the
362 * Finalize method when the garbage collector disposes the object.
366 mono_object_register_finalizer (MonoObject *obj, MonoError *error)
368 /* g_print ("Registered finalizer on %p %s.%s\n", obj, mono_object_class (obj)->name_space, mono_object_class (obj)->name); */
369 object_register_finalizer (obj, mono_gc_run_finalize, error);
373 * mono_domain_finalize:
374 * @domain: the domain to finalize
375 * @timeout: msects to wait for the finalization to complete, -1 to wait indefinitely
377 * Request finalization of all finalizable objects inside @domain. Wait
378 * @timeout msecs for the finalization to complete.
380 * Returns: TRUE if succeeded, FALSE if there was a timeout
384 mono_domain_finalize (MonoDomain *domain, guint32 timeout)
386 DomainFinalizationReq *req;
389 MonoInternalThread *thread = mono_thread_internal_current ();
391 #if defined(__native_client__)
395 if (mono_thread_internal_current () == gc_thread)
396 /* We are called from inside a finalizer, not much we can do here */
400 * No need to create another thread 'cause the finalizer thread
401 * is still working and will take care of running the finalizers
407 /* We don't support domain finalization without a GC */
408 if (mono_gc_is_null ())
411 mono_gc_collect (mono_gc_max_generation ());
413 done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
414 if (done_event == NULL) {
418 req = g_new0 (DomainFinalizationReq, 1);
419 req->domain = domain;
420 req->done_event = done_event;
422 if (domain == mono_get_root_domain ())
423 finalizing_root_domain = TRUE;
425 mono_finalizer_lock ();
427 domains_to_finalize = g_slist_append (domains_to_finalize, req);
429 mono_finalizer_unlock ();
431 /* Tell the finalizer thread to finalize this appdomain */
432 mono_gc_finalize_notify ();
438 res = guarded_wait (done_event, timeout, TRUE);
439 /* printf ("WAIT RES: %d.\n", res); */
441 if (res == WAIT_IO_COMPLETION) {
442 if ((thread->state & (ThreadState_StopRequested | ThreadState_SuspendRequested)) != 0)
444 } else if (res == WAIT_TIMEOUT) {
445 /* We leak the handle here */
452 CloseHandle (done_event);
454 if (domain == mono_get_root_domain ()) {
455 mono_threadpool_ms_cleanup ();
456 mono_gc_finalize_threadpool_threads ();
459 mono_profiler_appdomain_event (domain, MONO_PROFILE_END_UNLOAD);
465 ves_icall_System_GC_InternalCollect (int generation)
467 mono_gc_collect (generation);
471 ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection)
474 mono_gc_collect (mono_gc_max_generation ());
475 return mono_gc_get_used_size ();
479 ves_icall_System_GC_KeepAlive (MonoObject *obj)
487 ves_icall_System_GC_ReRegisterForFinalize (MonoObject *obj)
491 MONO_CHECK_ARG_NULL (obj,);
493 object_register_finalizer (obj, mono_gc_run_finalize, &error);
494 mono_error_set_pending_exception (&error);
498 ves_icall_System_GC_SuppressFinalize (MonoObject *obj)
502 MONO_CHECK_ARG_NULL (obj,);
504 /* delegates have no finalizers, but we register them to deal with the
505 * unmanaged->managed trampoline. We don't let the user suppress it
506 * otherwise we'd leak it.
508 if (obj->vtable->klass->delegate)
511 /* FIXME: Need to handle case where obj has COM Callable Wrapper
512 * generated for it that needs cleaned up, but user wants to suppress
513 * their derived object finalizer. */
515 object_register_finalizer (obj, NULL, &error);
516 mono_error_set_pending_exception (&error);
520 ves_icall_System_GC_WaitForPendingFinalizers (void)
522 if (mono_gc_is_null ())
525 if (!mono_gc_pending_finalizers ())
528 if (mono_thread_internal_current () == gc_thread)
529 /* Avoid deadlocks */
533 If the finalizer thread is not live, lets pretend no finalizers are pending since the current thread might
534 be the one responsible for starting it up.
536 if (gc_thread == NULL)
539 ResetEvent (pending_done_event);
540 mono_gc_finalize_notify ();
541 /* g_print ("Waiting for pending finalizers....\n"); */
542 guarded_wait (pending_done_event, INFINITE, TRUE);
543 /* g_print ("Done pending....\n"); */
547 ves_icall_System_GC_register_ephemeron_array (MonoObject *array)
550 if (!mono_gc_ephemeron_array_add (array)) {
551 mono_set_pending_exception (mono_object_domain (array)->out_of_memory_ex);
558 ves_icall_System_GC_get_ephemeron_tombstone (void)
560 return mono_domain_get ()->ephemeron_tombstone;
564 ves_icall_System_GCHandle_GetTarget (guint32 handle)
566 return mono_gchandle_get_target (handle);
570 * if type == -1, change the target of the handle, otherwise allocate a new handle.
573 ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type)
576 mono_gchandle_set_target (handle, obj);
577 /* the handle doesn't change */
582 return mono_gchandle_new_weakref (obj, FALSE);
583 case HANDLE_WEAK_TRACK:
584 return mono_gchandle_new_weakref (obj, TRUE);
586 return mono_gchandle_new (obj, FALSE);
588 return mono_gchandle_new (obj, TRUE);
590 g_assert_not_reached ();
596 ves_icall_System_GCHandle_FreeHandle (guint32 handle)
598 mono_gchandle_free (handle);
602 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
606 if (MONO_GC_HANDLE_TYPE (handle) != HANDLE_PINNED)
608 obj = mono_gchandle_get_target (handle);
610 MonoClass *klass = mono_object_class (obj);
611 if (klass == mono_defaults.string_class) {
612 return mono_string_chars ((MonoString*)obj);
613 } else if (klass->rank) {
614 return mono_array_addr ((MonoArray*)obj, char, 0);
616 /* the C# code will check and throw the exception */
617 /* FIXME: missing !klass->blittable test, see bug #61134 */
618 if ((klass->flags & TYPE_ATTRIBUTE_LAYOUT_MASK) == TYPE_ATTRIBUTE_AUTO_LAYOUT)
620 return (char*)obj + sizeof (MonoObject);
627 mono_gc_GCHandle_CheckCurrentDomain (guint32 gchandle)
629 return mono_gchandle_is_in_domain (gchandle, mono_domain_get ());
632 static MonoCoopSem finalizer_sem;
633 static volatile gboolean finished=FALSE;
636 mono_gc_finalize_notify (void)
639 g_message ( "%s: prodding finalizer", __func__);
642 if (mono_gc_is_null ())
645 mono_coop_sem_post (&finalizer_sem);
649 This is the number of entries allowed in the hazard free queue before
650 we explicitly cycle the finalizer thread to trigger pumping the queue.
652 It was picked empirically by running the corlib test suite in a stress
653 scenario where all hazard entries are queued.
655 In this extreme scenario we double the number of times we cycle the finalizer
656 thread compared to just GC calls.
658 Entries are usually in the order of 100's of bytes each, so we're limiting
659 floating garbage to be in the order of a dozen kb.
661 static gboolean finalizer_thread_pulsed;
662 #define HAZARD_QUEUE_OVERFLOW_SIZE 20
665 hazard_free_queue_is_too_big (size_t size)
667 if (size < HAZARD_QUEUE_OVERFLOW_SIZE)
670 if (finalizer_thread_pulsed || InterlockedCompareExchange (&finalizer_thread_pulsed, TRUE, FALSE))
673 mono_gc_finalize_notify ();
677 hazard_free_queue_pump (void)
679 mono_thread_hazardous_try_free_all ();
680 finalizer_thread_pulsed = FALSE;
686 collect_objects (gpointer key, gpointer value, gpointer user_data)
688 GPtrArray *arr = (GPtrArray*)user_data;
689 g_ptr_array_add (arr, key);
695 * finalize_domain_objects:
697 * Run the finalizers of all finalizable objects in req->domain.
700 finalize_domain_objects (DomainFinalizationReq *req)
702 MonoDomain *domain = req->domain;
705 #define NUM_FOBJECTS 64
706 MonoObject *to_finalize [NUM_FOBJECTS];
710 /* Process finalizers which are already in the queue */
711 mono_gc_invoke_finalizers ();
714 while (g_hash_table_size (domain->finalizable_objects_hash) > 0) {
718 * Since the domain is unloading, nobody is allowed to put
719 * new entries into the hash table. But finalize_object might
720 * remove entries from the hash table, so we make a copy.
722 objs = g_ptr_array_new ();
723 g_hash_table_foreach (domain->finalizable_objects_hash, collect_objects, objs);
724 /* printf ("FINALIZING %d OBJECTS.\n", objs->len); */
726 for (i = 0; i < objs->len; ++i) {
727 MonoObject *o = (MonoObject*)g_ptr_array_index (objs, i);
728 /* FIXME: Avoid finalizing threads, etc */
729 mono_gc_run_finalize (o, 0);
732 g_ptr_array_free (objs, TRUE);
734 #elif defined(HAVE_SGEN_GC)
735 while ((count = mono_gc_finalizers_for_domain (domain, to_finalize, NUM_FOBJECTS))) {
737 for (i = 0; i < count; ++i) {
738 mono_gc_run_finalize (to_finalize [i], 0);
743 /* cleanup the reference queue */
744 reference_queue_clear_for_domain (domain);
746 /* printf ("DONE.\n"); */
747 SetEvent (req->done_event);
749 /* The event is closed in mono_domain_finalize if we get here */
754 finalizer_thread (gpointer unused)
757 mono_thread_set_name_internal (mono_thread_internal_current (), mono_string_new (mono_get_root_domain (), "Finalizer"), FALSE, &error);
758 mono_error_assert_ok (&error);
760 gboolean wait = TRUE;
762 /* Register a hazard free queue pump callback */
763 mono_hazard_pointer_install_free_queue_size_callback (hazard_free_queue_is_too_big);
766 /* Wait to be notified that there's at least one
770 g_assert (mono_domain_get () == mono_get_root_domain ());
771 mono_gc_set_skip_thread (TRUE);
774 /* An alertable wait is required so this thread can be suspended on windows */
775 mono_coop_sem_wait (&finalizer_sem, MONO_SEM_FLAGS_ALERTABLE);
779 mono_gc_set_skip_thread (FALSE);
781 mono_threads_perform_thread_dump ();
783 mono_console_handle_async_ops ();
785 mono_attach_maybe_start ();
787 if (domains_to_finalize) {
788 mono_finalizer_lock ();
789 if (domains_to_finalize) {
790 DomainFinalizationReq *req = (DomainFinalizationReq *)domains_to_finalize->data;
791 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
792 mono_finalizer_unlock ();
794 finalize_domain_objects (req);
796 mono_finalizer_unlock ();
800 /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup),
801 * before the domain is unloaded.
803 mono_gc_invoke_finalizers ();
805 mono_threads_join_threads ();
807 reference_queue_proccess_all ();
809 hazard_free_queue_pump ();
811 /* Avoid posting the pending done event until there are pending finalizers */
812 if (mono_coop_sem_timedwait (&finalizer_sem, 0, MONO_SEM_FLAGS_NONE) == 0) {
813 /* Don't wait again at the start of the loop */
816 SetEvent (pending_done_event);
820 mono_finalizer_lock ();
821 finalizer_thread_exited = TRUE;
822 mono_coop_cond_signal (&exited_cond);
823 mono_finalizer_unlock ();
828 #ifndef LAZY_GC_THREAD_CREATION
832 mono_gc_init_finalizer_thread (void)
835 gc_thread = mono_thread_create_internal (mono_domain_get (), finalizer_thread, NULL, FALSE, 0, &error);
836 mono_error_assert_ok (&error);
842 mono_coop_mutex_init_recursive (&finalizer_mutex);
843 mono_coop_mutex_init_recursive (&reference_queue_mutex);
845 mono_counters_register ("Minor GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.minor_gc_count);
846 mono_counters_register ("Major GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.major_gc_count);
847 mono_counters_register ("Minor GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.minor_gc_time);
848 mono_counters_register ("Major GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time);
849 mono_counters_register ("Major GC time concurrent", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time_concurrent);
851 mono_gc_base_init ();
853 if (mono_gc_is_disabled ()) {
858 pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
859 g_assert (pending_done_event);
860 mono_coop_cond_init (&exited_cond);
861 mono_coop_sem_init (&finalizer_sem, 0);
863 #ifndef LAZY_GC_THREAD_CREATION
864 mono_gc_init_finalizer_thread ();
869 mono_gc_cleanup (void)
872 g_message ("%s: cleaning up finalizer", __func__);
875 if (mono_gc_is_null ())
880 if (mono_thread_internal_current () != gc_thread) {
881 gboolean timed_out = FALSE;
882 gint64 start_ticks = mono_msec_ticks ();
883 gint64 end_ticks = start_ticks + 2000;
885 mono_gc_finalize_notify ();
886 /* Finishing the finalizer thread, so wait a little bit... */
887 /* MS seems to wait for about 2 seconds */
888 while (!finalizer_thread_exited) {
889 gint64 current_ticks = mono_msec_ticks ();
892 if (current_ticks >= end_ticks)
895 timeout = end_ticks - current_ticks;
896 mono_finalizer_lock ();
897 if (!finalizer_thread_exited)
898 mono_coop_cond_timedwait (&exited_cond, &finalizer_mutex, timeout);
899 mono_finalizer_unlock ();
902 if (!finalizer_thread_exited) {
905 /* Set a flag which the finalizer thread can check */
906 suspend_finalizers = TRUE;
908 /* Try to abort the thread, in the hope that it is running managed code */
909 mono_thread_internal_stop (gc_thread);
911 /* Wait for it to stop */
912 ret = guarded_wait (gc_thread->handle, 100, TRUE);
914 if (ret == WAIT_TIMEOUT) {
916 * The finalizer thread refused to die. There is not much we
917 * can do here, since the runtime is shutting down so the
918 * state the finalizer thread depends on will vanish.
920 g_warning ("Shutting down finalizer thread timed out.");
928 /* Wait for the thread to actually exit */
929 ret = guarded_wait (gc_thread->handle, INFINITE, TRUE);
930 g_assert (ret == WAIT_OBJECT_0);
932 mono_thread_join (GUINT_TO_POINTER (gc_thread->tid));
934 g_assert (finalizer_thread_exited);
937 mono_gc_base_cleanup ();
940 mono_reference_queue_cleanup ();
942 mono_coop_mutex_destroy (&finalizer_mutex);
943 mono_coop_mutex_destroy (&reference_queue_mutex);
947 mono_gc_is_finalizer_internal_thread (MonoInternalThread *thread)
949 return thread == gc_thread;
953 * mono_gc_is_finalizer_thread:
954 * @thread: the thread to test.
956 * In Mono objects are finalized asynchronously on a separate thread.
957 * This routine tests whether the @thread argument represents the
958 * finalization thread.
960 * Returns: TRUE if @thread is the finalization thread.
963 mono_gc_is_finalizer_thread (MonoThread *thread)
965 return mono_gc_is_finalizer_internal_thread (thread->internal_thread);
968 #if defined(__MACH__)
969 static pthread_t mach_exception_thread;
972 mono_gc_register_mach_exception_thread (pthread_t thread)
974 mach_exception_thread = thread;
978 mono_gc_get_mach_exception_thread (void)
980 return mach_exception_thread;
984 static MonoReferenceQueue *ref_queues;
987 ref_list_remove_element (RefQueueEntry **prev, RefQueueEntry *element)
990 /* Guard if head is changed concurrently. */
991 while (*prev != element)
992 prev = &(*prev)->next;
993 } while (prev && InterlockedCompareExchangePointer ((volatile gpointer *)prev, element->next, element) != element);
997 ref_list_push (RefQueueEntry **head, RefQueueEntry *value)
999 RefQueueEntry *current;
1002 value->next = current;
1003 STORE_STORE_FENCE; /*Must make sure the previous store is visible before the CAS. */
1004 } while (InterlockedCompareExchangePointer ((volatile gpointer *)head, value, current) != current);
1008 reference_queue_proccess (MonoReferenceQueue *queue)
1010 RefQueueEntry **iter = &queue->queue;
1011 RefQueueEntry *entry;
1012 while ((entry = *iter)) {
1013 if (queue->should_be_deleted || !mono_gchandle_get_target (entry->gchandle)) {
1014 mono_gchandle_free ((guint32)entry->gchandle);
1015 ref_list_remove_element (iter, entry);
1016 queue->callback (entry->user_data);
1019 iter = &entry->next;
1025 reference_queue_proccess_all (void)
1027 MonoReferenceQueue **iter;
1028 MonoReferenceQueue *queue = ref_queues;
1029 for (; queue; queue = queue->next)
1030 reference_queue_proccess (queue);
1033 mono_coop_mutex_lock (&reference_queue_mutex);
1034 for (iter = &ref_queues; *iter;) {
1036 if (!queue->should_be_deleted) {
1037 iter = &queue->next;
1041 mono_coop_mutex_unlock (&reference_queue_mutex);
1042 reference_queue_proccess (queue);
1045 *iter = queue->next;
1048 mono_coop_mutex_unlock (&reference_queue_mutex);
1052 mono_reference_queue_cleanup (void)
1054 MonoReferenceQueue *queue = ref_queues;
1055 for (; queue; queue = queue->next)
1056 queue->should_be_deleted = TRUE;
1057 reference_queue_proccess_all ();
1061 reference_queue_clear_for_domain (MonoDomain *domain)
1063 MonoReferenceQueue *queue = ref_queues;
1064 for (; queue; queue = queue->next) {
1065 RefQueueEntry **iter = &queue->queue;
1066 RefQueueEntry *entry;
1067 while ((entry = *iter)) {
1068 if (entry->domain == domain) {
1069 mono_gchandle_free ((guint32)entry->gchandle);
1070 ref_list_remove_element (iter, entry);
1071 queue->callback (entry->user_data);
1074 iter = &entry->next;
1080 * mono_gc_reference_queue_new:
1081 * @callback callback used when processing collected entries.
1083 * Create a new reference queue used to process collected objects.
1084 * A reference queue let you add a pair of (managed object, user data)
1085 * using the mono_gc_reference_queue_add method.
1087 * Once the managed object is collected @callback will be called
1088 * in the finalizer thread with 'user data' as argument.
1090 * The callback is called from the finalizer thread without any locks held.
1091 * When a AppDomain is unloaded, all callbacks for objects belonging to it
1094 * @returns the new queue.
1097 mono_gc_reference_queue_new (mono_reference_queue_callback callback)
1099 MonoReferenceQueue *res = g_new0 (MonoReferenceQueue, 1);
1100 res->callback = callback;
1102 mono_coop_mutex_lock (&reference_queue_mutex);
1103 res->next = ref_queues;
1105 mono_coop_mutex_unlock (&reference_queue_mutex);
1111 * mono_gc_reference_queue_add:
1112 * @queue the queue to add the reference to.
1113 * @obj the object to be watched for collection
1114 * @user_data parameter to be passed to the queue callback
1116 * Queue an object to be watched for collection, when the @obj is
1117 * collected, the callback that was registered for the @queue will
1118 * be invoked with @user_data as argument.
1120 * @returns false if the queue is scheduled to be freed.
1123 mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *user_data)
1126 RefQueueEntry *entry;
1127 if (queue->should_be_deleted)
1130 entry = g_new0 (RefQueueEntry, 1);
1131 entry->user_data = user_data;
1132 entry->domain = mono_object_domain (obj);
1134 entry->gchandle = mono_gchandle_new_weakref (obj, TRUE);
1135 mono_object_register_finalizer (obj, &error);
1136 mono_error_assert_ok (&error);
1138 ref_list_push (&queue->queue, entry);
1143 * mono_gc_reference_queue_free:
1144 * @queue the queue that should be freed.
1146 * This operation signals that @queue should be freed. This operation is deferred
1147 * as it happens on the finalizer thread.
1149 * After this call, no further objects can be queued. It's the responsibility of the
1150 * caller to make sure that no further attempt to access queue will be made.
1153 mono_gc_reference_queue_free (MonoReferenceQueue *queue)
1155 queue->should_be_deleted = TRUE;