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/mono-threads-coop.h>
42 #include <mono/utils/atomic.h>
43 #include <mono/utils/mono-coop-semaphore.h>
44 #include <mono/utils/hazard-pointer.h>
50 typedef struct DomainFinalizationReq {
54 } DomainFinalizationReq;
56 static gboolean gc_disabled = FALSE;
58 static gboolean finalizing_root_domain = FALSE;
60 gboolean log_finalizers = FALSE;
61 gboolean mono_do_not_finalize = FALSE;
62 volatile gboolean suspend_finalizers = FALSE;
63 gchar **mono_do_not_finalize_class_names = NULL;
65 #define mono_finalizer_lock() mono_coop_mutex_lock (&finalizer_mutex)
66 #define mono_finalizer_unlock() mono_coop_mutex_unlock (&finalizer_mutex)
67 static MonoCoopMutex finalizer_mutex;
68 static MonoCoopMutex reference_queue_mutex;
70 static GSList *domains_to_finalize= NULL;
71 static MonoMList *threads_to_finalize = NULL;
73 static gboolean finalizer_thread_exited;
74 /* Uses finalizer_mutex */
75 static MonoCoopCond exited_cond;
77 static MonoInternalThread *gc_thread;
79 static void object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*));
81 static void reference_queue_proccess_all (void);
82 static void mono_reference_queue_cleanup (void);
83 static void reference_queue_clear_for_domain (MonoDomain *domain);
84 static HANDLE pending_done_event;
87 guarded_wait (HANDLE handle, guint32 timeout, gboolean alertable)
92 result = WaitForSingleObjectEx (handle, timeout, alertable);
99 add_thread_to_finalize (MonoInternalThread *thread, MonoError *error)
101 mono_error_init (error);
102 mono_finalizer_lock ();
103 if (!threads_to_finalize)
104 MONO_GC_REGISTER_ROOT_SINGLE (threads_to_finalize, MONO_ROOT_SOURCE_FINALIZER_QUEUE, "finalizable threads list");
105 threads_to_finalize = mono_mlist_append_checked (threads_to_finalize, (MonoObject*)thread, error);
106 mono_finalizer_unlock ();
107 return is_ok (error);
111 * actually, we might want to queue the finalize requests in a separate thread,
112 * but we need to be careful about the execution domain of the thread...
115 mono_gc_run_finalize (void *obj, void *data)
118 MonoObject *exc = NULL;
123 MonoMethod* finalizer = NULL;
124 MonoDomain *caller_domain = mono_domain_get ();
126 RuntimeInvokeFunction runtime_invoke;
128 // This function is called from the innards of the GC, so our best alternative for now is to do polling here
129 mono_threads_safepoint ();
131 o = (MonoObject*)((char*)obj + GPOINTER_TO_UINT (data));
133 if (mono_do_not_finalize) {
134 if (!mono_do_not_finalize_class_names)
137 size_t namespace_len = strlen (o->vtable->klass->name_space);
138 for (int i = 0; mono_do_not_finalize_class_names [i]; ++i) {
139 const char *name = mono_do_not_finalize_class_names [i];
140 if (strncmp (name, o->vtable->klass->name_space, namespace_len))
142 if (name [namespace_len] != '.')
144 if (strcmp (name + namespace_len + 1, o->vtable->klass->name))
151 g_log ("mono-gc-finalizers", G_LOG_LEVEL_DEBUG, "<%s at %p> Starting finalizer checks.", o->vtable->klass->name, o);
153 if (suspend_finalizers)
156 domain = o->vtable->domain;
159 mono_domain_finalizers_lock (domain);
161 o2 = (MonoObject *)g_hash_table_lookup (domain->finalizable_objects_hash, o);
163 mono_domain_finalizers_unlock (domain);
166 /* Already finalized somehow */
170 /* make sure the finalizer is not called again if the object is resurrected */
171 object_register_finalizer ((MonoObject *)obj, NULL);
174 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Registered finalizer as processed.", o->vtable->klass->name, o);
176 if (o->vtable->klass == mono_defaults.internal_thread_class) {
177 MonoInternalThread *t = (MonoInternalThread*)o;
179 if (mono_gc_is_finalizer_internal_thread (t))
180 /* Avoid finalizing ourselves */
183 if (t->threadpool_thread && finalizing_root_domain) {
184 /* Don't finalize threadpool threads when
185 shutting down - they're finalized when the
186 threadpool shuts down. */
187 if (!add_thread_to_finalize (t, &error))
188 goto unhandled_error;
193 if (o->vtable->klass->image == mono_defaults.corlib && !strcmp (o->vtable->klass->name, "DynamicMethod") && finalizing_root_domain) {
195 * These can't be finalized during unloading/shutdown, since that would
196 * free the native code which can still be referenced by other
198 * FIXME: This is not perfect, objects dying at the same time as
199 * dynamic methods can still reference them even when !shutdown.
204 if (mono_runtime_get_no_exec ())
207 /* speedup later... and use a timeout */
208 /* g_print ("Finalize run on %p %s.%s\n", o, mono_object_class (o)->name_space, mono_object_class (o)->name); */
210 /* Use _internal here, since this thread can enter a doomed appdomain */
211 mono_domain_set_internal (mono_object_domain (o));
213 /* delegates that have a native function pointer allocated are
214 * registered for finalization, but they don't have a Finalize
215 * method, because in most cases it's not needed and it's just a waste.
217 if (o->vtable->klass->delegate) {
218 MonoDelegate* del = (MonoDelegate*)o;
219 if (del->delegate_trampoline)
220 mono_delegate_free_ftnptr ((MonoDelegate*)o);
221 mono_domain_set_internal (caller_domain);
225 finalizer = mono_class_get_finalizer (o->vtable->klass);
227 /* If object has a CCW but has no finalizer, it was only
228 * registered for finalization in order to free the CCW.
229 * Else it needs the regular finalizer run.
230 * FIXME: what to do about ressurection and suppression
231 * of finalizer on object with CCW.
233 if (mono_marshal_free_ccw (o) && !finalizer) {
234 mono_domain_set_internal (caller_domain);
239 * To avoid the locking plus the other overhead of mono_runtime_invoke_checked (),
240 * create and precompile a wrapper which calls the finalize method using
244 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Compiling finalizer.", o->vtable->klass->name, o);
246 if (!domain->finalize_runtime_invoke) {
247 MonoMethod *invoke = mono_marshal_get_runtime_invoke (mono_class_get_method_from_name_flags (mono_defaults.object_class, "Finalize", 0, 0), TRUE);
249 domain->finalize_runtime_invoke = mono_compile_method_checked (invoke, &error);
250 mono_error_assert_ok (&error); /* expect this not to fail */
253 runtime_invoke = (RuntimeInvokeFunction)domain->finalize_runtime_invoke;
255 mono_runtime_class_init_full (o->vtable, &error);
257 goto unhandled_error;
259 if (G_UNLIKELY (MONO_GC_FINALIZE_INVOKE_ENABLED ())) {
260 MONO_GC_FINALIZE_INVOKE ((unsigned long)o, mono_object_get_size (o),
261 o->vtable->klass->name_space, o->vtable->klass->name);
265 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Calling finalizer.", o->vtable->klass->name, o);
267 runtime_invoke (o, NULL, &exc, NULL);
270 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Returned from finalizer.", o->vtable->klass->name, o);
274 exc = (MonoObject*)mono_error_convert_to_exception (&error);
276 mono_thread_internal_unhandled_exception (exc);
278 mono_domain_set_internal (caller_domain);
282 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);
291 mono_gc_run_finalize (thread, NULL);
293 threads_to_finalize = mono_mlist_next (threads_to_finalize);
298 mono_gc_out_of_memory (size_t size)
301 * we could allocate at program startup some memory that we could release
302 * back to the system at this point if we're really low on memory (ie, size is
303 * lower than the memory we set apart)
305 mono_raise_exception (mono_domain_get ()->out_of_memory_ex);
311 * Some of our objects may point to a different address than the address returned by GC_malloc()
312 * (because of the GetHashCode hack), but we need to pass the real address to register_finalizer.
313 * This also means that in the callback we need to adjust the pointer to get back the real
315 * We also need to be consistent in the use of the GC_debug* variants of malloc and register_finalizer,
316 * since that, too, can cause the underlying pointer to be offset.
319 object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*))
323 g_assert (obj != NULL);
325 domain = obj->vtable->domain;
328 if (mono_domain_is_unloading (domain) && (callback != NULL))
330 * Can't register finalizers in a dying appdomain, since they
331 * could be invoked after the appdomain has been unloaded.
335 mono_domain_finalizers_lock (domain);
338 g_hash_table_insert (domain->finalizable_objects_hash, obj, obj);
340 g_hash_table_remove (domain->finalizable_objects_hash, obj);
342 mono_domain_finalizers_unlock (domain);
344 mono_gc_register_for_finalization (obj, callback);
345 #elif defined(HAVE_SGEN_GC)
347 * If we register finalizers for domains that are unloading we might
348 * end up running them while or after the domain is being cleared, so
349 * the objects will not be valid anymore.
351 if (!mono_domain_is_unloading (domain))
352 mono_gc_register_for_finalization (obj, callback);
357 * mono_object_register_finalizer:
358 * @obj: object to register
360 * Records that object @obj has a finalizer, this will call the
361 * Finalize method when the garbage collector disposes the object.
365 mono_object_register_finalizer (MonoObject *obj)
367 /* g_print ("Registered finalizer on %p %s.%s\n", obj, mono_object_class (obj)->name_space, mono_object_class (obj)->name); */
368 object_register_finalizer (obj, mono_gc_run_finalize);
372 * mono_domain_finalize:
373 * @domain: the domain to finalize
374 * @timeout: msects to wait for the finalization to complete, -1 to wait indefinitely
376 * Request finalization of all finalizable objects inside @domain. Wait
377 * @timeout msecs for the finalization to complete.
379 * Returns: TRUE if succeeded, FALSE if there was a timeout
383 mono_domain_finalize (MonoDomain *domain, guint32 timeout)
385 DomainFinalizationReq *req;
386 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 req = g_new0 (DomainFinalizationReq, 1);
415 req->domain = domain;
416 mono_coop_sem_init (&req->done, 0);
418 if (domain == mono_get_root_domain ())
419 finalizing_root_domain = TRUE;
421 mono_finalizer_lock ();
423 domains_to_finalize = g_slist_append (domains_to_finalize, req);
425 mono_finalizer_unlock ();
427 /* Tell the finalizer thread to finalize this appdomain */
428 mono_gc_finalize_notify ();
432 if (timeout != INFINITE)
433 start = mono_msec_ticks ();
438 if (timeout == INFINITE) {
439 res = mono_coop_sem_wait (&req->done, MONO_SEM_FLAGS_ALERTABLE);
441 gint64 elapsed = mono_msec_ticks () - start;
442 if (elapsed >= timeout) {
447 res = mono_coop_sem_timedwait (&req->done, timeout - elapsed, MONO_SEM_FLAGS_ALERTABLE);
450 if (res == MONO_SEM_TIMEDWAIT_RET_SUCCESS) {
452 } else if (res == MONO_SEM_TIMEDWAIT_RET_ALERTED) {
453 if ((thread->state & (ThreadState_StopRequested | ThreadState_SuspendRequested)) != 0) {
457 } else if (res == MONO_SEM_TIMEDWAIT_RET_TIMEDOUT) {
461 g_error ("%s: unknown result %d", __func__, res);
466 /* Try removing the req from domains_to_finalize:
467 * - if it's not found: the domain is being finalized,
468 * so we the ref count is already decremented
469 * - if it's found: the domain is not yet being finalized,
470 * so we can safely decrement the ref */
474 mono_finalizer_lock ();
476 found = g_slist_index (domains_to_finalize, req) != -1;
478 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
480 mono_finalizer_unlock ();
483 /* We have to decrement it wherever we
484 * remove it from domains_to_finalize */
485 if (InterlockedDecrement (&req->ref) != 1)
486 g_error ("%s: req->ref should be 1, as we are the first one to decrement it", __func__);
492 if (domain == mono_get_root_domain ()) {
493 mono_threadpool_ms_cleanup ();
494 mono_gc_finalize_threadpool_threads ();
497 mono_profiler_appdomain_event (domain, MONO_PROFILE_END_UNLOAD);
500 if (InterlockedDecrement (&req->ref) == 0) {
501 mono_coop_sem_destroy (&req->done);
509 ves_icall_System_GC_InternalCollect (int generation)
511 mono_gc_collect (generation);
515 ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection)
518 mono_gc_collect (mono_gc_max_generation ());
519 return mono_gc_get_used_size ();
523 ves_icall_System_GC_KeepAlive (MonoObject *obj)
531 ves_icall_System_GC_ReRegisterForFinalize (MonoObject *obj)
533 MONO_CHECK_ARG_NULL (obj,);
535 object_register_finalizer (obj, mono_gc_run_finalize);
539 ves_icall_System_GC_SuppressFinalize (MonoObject *obj)
541 MONO_CHECK_ARG_NULL (obj,);
543 /* delegates have no finalizers, but we register them to deal with the
544 * unmanaged->managed trampoline. We don't let the user suppress it
545 * otherwise we'd leak it.
547 if (obj->vtable->klass->delegate)
550 /* FIXME: Need to handle case where obj has COM Callable Wrapper
551 * generated for it that needs cleaned up, but user wants to suppress
552 * their derived object finalizer. */
554 object_register_finalizer (obj, NULL);
558 ves_icall_System_GC_WaitForPendingFinalizers (void)
560 if (mono_gc_is_null ())
563 if (!mono_gc_pending_finalizers ())
566 if (mono_thread_internal_current () == gc_thread)
567 /* Avoid deadlocks */
571 If the finalizer thread is not live, lets pretend no finalizers are pending since the current thread might
572 be the one responsible for starting it up.
574 if (gc_thread == NULL)
577 ResetEvent (pending_done_event);
578 mono_gc_finalize_notify ();
579 /* g_print ("Waiting for pending finalizers....\n"); */
580 guarded_wait (pending_done_event, INFINITE, TRUE);
581 /* g_print ("Done pending....\n"); */
585 ves_icall_System_GC_register_ephemeron_array (MonoObject *array)
588 if (!mono_gc_ephemeron_array_add (array)) {
589 mono_set_pending_exception (mono_object_domain (array)->out_of_memory_ex);
596 ves_icall_System_GC_get_ephemeron_tombstone (void)
598 return mono_domain_get ()->ephemeron_tombstone;
602 ves_icall_System_GCHandle_GetTarget (guint32 handle)
604 return mono_gchandle_get_target (handle);
608 * if type == -1, change the target of the handle, otherwise allocate a new handle.
611 ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type)
614 mono_gchandle_set_target (handle, obj);
615 /* the handle doesn't change */
620 return mono_gchandle_new_weakref (obj, FALSE);
621 case HANDLE_WEAK_TRACK:
622 return mono_gchandle_new_weakref (obj, TRUE);
624 return mono_gchandle_new (obj, FALSE);
626 return mono_gchandle_new (obj, TRUE);
628 g_assert_not_reached ();
634 ves_icall_System_GCHandle_FreeHandle (guint32 handle)
636 mono_gchandle_free (handle);
640 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
644 if (MONO_GC_HANDLE_TYPE (handle) != HANDLE_PINNED)
646 obj = mono_gchandle_get_target (handle);
648 MonoClass *klass = mono_object_class (obj);
649 if (klass == mono_defaults.string_class) {
650 return mono_string_chars ((MonoString*)obj);
651 } else if (klass->rank) {
652 return mono_array_addr ((MonoArray*)obj, char, 0);
654 /* the C# code will check and throw the exception */
655 /* FIXME: missing !klass->blittable test, see bug #61134 */
656 if ((klass->flags & TYPE_ATTRIBUTE_LAYOUT_MASK) == TYPE_ATTRIBUTE_AUTO_LAYOUT)
658 return (char*)obj + sizeof (MonoObject);
665 mono_gc_GCHandle_CheckCurrentDomain (guint32 gchandle)
667 return mono_gchandle_is_in_domain (gchandle, mono_domain_get ());
670 static MonoCoopSem finalizer_sem;
671 static volatile gboolean finished=FALSE;
674 mono_gc_finalize_notify (void)
677 g_message ( "%s: prodding finalizer", __func__);
680 if (mono_gc_is_null ())
683 mono_coop_sem_post (&finalizer_sem);
687 This is the number of entries allowed in the hazard free queue before
688 we explicitly cycle the finalizer thread to trigger pumping the queue.
690 It was picked empirically by running the corlib test suite in a stress
691 scenario where all hazard entries are queued.
693 In this extreme scenario we double the number of times we cycle the finalizer
694 thread compared to just GC calls.
696 Entries are usually in the order of 100's of bytes each, so we're limiting
697 floating garbage to be in the order of a dozen kb.
699 static gboolean finalizer_thread_pulsed;
700 #define HAZARD_QUEUE_OVERFLOW_SIZE 20
703 hazard_free_queue_is_too_big (size_t size)
705 if (size < HAZARD_QUEUE_OVERFLOW_SIZE)
708 if (finalizer_thread_pulsed || InterlockedCompareExchange (&finalizer_thread_pulsed, TRUE, FALSE))
711 mono_gc_finalize_notify ();
715 hazard_free_queue_pump (void)
717 mono_thread_hazardous_try_free_all ();
718 finalizer_thread_pulsed = FALSE;
724 collect_objects (gpointer key, gpointer value, gpointer user_data)
726 GPtrArray *arr = (GPtrArray*)user_data;
727 g_ptr_array_add (arr, key);
733 * finalize_domain_objects:
735 * Run the finalizers of all finalizable objects in req->domain.
738 finalize_domain_objects (void)
740 DomainFinalizationReq *req = NULL;
743 if (domains_to_finalize) {
744 mono_finalizer_lock ();
745 if (domains_to_finalize) {
746 req = (DomainFinalizationReq *)domains_to_finalize->data;
747 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
749 mono_finalizer_unlock ();
755 domain = req->domain;
757 /* Process finalizers which are already in the queue */
758 mono_gc_invoke_finalizers ();
761 while (g_hash_table_size (domain->finalizable_objects_hash) > 0) {
765 * Since the domain is unloading, nobody is allowed to put
766 * new entries into the hash table. But finalize_object might
767 * remove entries from the hash table, so we make a copy.
769 objs = g_ptr_array_new ();
770 g_hash_table_foreach (domain->finalizable_objects_hash, collect_objects, objs);
771 /* printf ("FINALIZING %d OBJECTS.\n", objs->len); */
773 for (i = 0; i < objs->len; ++i) {
774 MonoObject *o = (MonoObject*)g_ptr_array_index (objs, i);
775 /* FIXME: Avoid finalizing threads, etc */
776 mono_gc_run_finalize (o, 0);
779 g_ptr_array_free (objs, TRUE);
781 #elif defined(HAVE_SGEN_GC)
782 mono_gc_finalize_domain (domain);
783 mono_gc_invoke_finalizers ();
786 /* cleanup the reference queue */
787 reference_queue_clear_for_domain (domain);
789 /* printf ("DONE.\n"); */
790 mono_coop_sem_post (&req->done);
792 if (InterlockedDecrement (&req->ref) == 0) {
793 /* mono_domain_finalize already returned, and
794 * doesn't hold a reference to req anymore. */
795 mono_coop_sem_destroy (&req->done);
801 finalizer_thread (gpointer unused)
804 mono_thread_set_name_internal (mono_thread_internal_current (), mono_string_new (mono_get_root_domain (), "Finalizer"), FALSE, &error);
805 mono_error_assert_ok (&error);
807 gboolean wait = TRUE;
809 /* Register a hazard free queue pump callback */
810 mono_hazard_pointer_install_free_queue_size_callback (hazard_free_queue_is_too_big);
813 /* Wait to be notified that there's at least one
817 g_assert (mono_domain_get () == mono_get_root_domain ());
818 mono_gc_set_skip_thread (TRUE);
821 /* An alertable wait is required so this thread can be suspended on windows */
822 mono_coop_sem_wait (&finalizer_sem, MONO_SEM_FLAGS_ALERTABLE);
826 mono_gc_set_skip_thread (FALSE);
828 mono_threads_perform_thread_dump ();
830 mono_console_handle_async_ops ();
832 mono_attach_maybe_start ();
834 finalize_domain_objects ();
836 /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup),
837 * before the domain is unloaded.
839 mono_gc_invoke_finalizers ();
841 mono_threads_join_threads ();
843 reference_queue_proccess_all ();
845 hazard_free_queue_pump ();
847 /* Avoid posting the pending done event until there are pending finalizers */
848 if (mono_coop_sem_timedwait (&finalizer_sem, 0, MONO_SEM_FLAGS_NONE) == MONO_SEM_TIMEDWAIT_RET_SUCCESS) {
849 /* Don't wait again at the start of the loop */
852 SetEvent (pending_done_event);
856 mono_finalizer_lock ();
857 finalizer_thread_exited = TRUE;
858 mono_coop_cond_signal (&exited_cond);
859 mono_finalizer_unlock ();
864 #ifndef LAZY_GC_THREAD_CREATION
868 mono_gc_init_finalizer_thread (void)
871 gc_thread = mono_thread_create_internal (mono_domain_get (), finalizer_thread, NULL, FALSE, 0, &error);
872 mono_error_assert_ok (&error);
878 mono_coop_mutex_init_recursive (&finalizer_mutex);
879 mono_coop_mutex_init_recursive (&reference_queue_mutex);
881 mono_counters_register ("Minor GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.minor_gc_count);
882 mono_counters_register ("Major GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.major_gc_count);
883 mono_counters_register ("Minor GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.minor_gc_time);
884 mono_counters_register ("Major GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time);
885 mono_counters_register ("Major GC time concurrent", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time_concurrent);
887 mono_gc_base_init ();
889 if (mono_gc_is_disabled ()) {
894 pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
895 g_assert (pending_done_event);
896 mono_coop_cond_init (&exited_cond);
897 mono_coop_sem_init (&finalizer_sem, 0);
899 #ifndef LAZY_GC_THREAD_CREATION
900 mono_gc_init_finalizer_thread ();
905 mono_gc_cleanup (void)
908 g_message ("%s: cleaning up finalizer", __func__);
911 if (mono_gc_is_null ())
916 if (mono_thread_internal_current () != gc_thread) {
917 gint64 start_ticks = mono_msec_ticks ();
918 gint64 end_ticks = start_ticks + 2000;
920 mono_gc_finalize_notify ();
921 /* Finishing the finalizer thread, so wait a little bit... */
922 /* MS seems to wait for about 2 seconds */
923 while (!finalizer_thread_exited) {
924 gint64 current_ticks = mono_msec_ticks ();
927 if (current_ticks >= end_ticks)
930 timeout = end_ticks - current_ticks;
931 mono_finalizer_lock ();
932 if (!finalizer_thread_exited)
933 mono_coop_cond_timedwait (&exited_cond, &finalizer_mutex, timeout);
934 mono_finalizer_unlock ();
937 if (!finalizer_thread_exited) {
940 /* Set a flag which the finalizer thread can check */
941 suspend_finalizers = TRUE;
942 mono_gc_suspend_finalizers ();
944 /* Try to abort the thread, in the hope that it is running managed code */
945 mono_thread_internal_abort (gc_thread);
947 /* Wait for it to stop */
948 ret = guarded_wait (gc_thread->handle, 100, TRUE);
950 if (ret == WAIT_TIMEOUT) {
952 * The finalizer thread refused to exit. Make it stop.
954 mono_thread_internal_stop (gc_thread);
955 ret = guarded_wait (gc_thread->handle, 100, TRUE);
956 g_assert (ret != WAIT_TIMEOUT);
957 /* The thread can't set this flag */
958 finalizer_thread_exited = TRUE;
964 /* Wait for the thread to actually exit */
965 ret = guarded_wait (gc_thread->handle, INFINITE, TRUE);
966 g_assert (ret == WAIT_OBJECT_0);
968 mono_thread_join (GUINT_TO_POINTER (gc_thread->tid));
969 g_assert (finalizer_thread_exited);
972 mono_gc_base_cleanup ();
975 mono_reference_queue_cleanup ();
977 mono_coop_mutex_destroy (&finalizer_mutex);
978 mono_coop_mutex_destroy (&reference_queue_mutex);
982 mono_gc_is_finalizer_internal_thread (MonoInternalThread *thread)
984 return thread == gc_thread;
988 * mono_gc_is_finalizer_thread:
989 * @thread: the thread to test.
991 * In Mono objects are finalized asynchronously on a separate thread.
992 * This routine tests whether the @thread argument represents the
993 * finalization thread.
995 * Returns: TRUE if @thread is the finalization thread.
998 mono_gc_is_finalizer_thread (MonoThread *thread)
1000 return mono_gc_is_finalizer_internal_thread (thread->internal_thread);
1003 #if defined(__MACH__)
1004 static pthread_t mach_exception_thread;
1007 mono_gc_register_mach_exception_thread (pthread_t thread)
1009 mach_exception_thread = thread;
1013 mono_gc_get_mach_exception_thread (void)
1015 return mach_exception_thread;
1019 static MonoReferenceQueue *ref_queues;
1022 ref_list_remove_element (RefQueueEntry **prev, RefQueueEntry *element)
1025 /* Guard if head is changed concurrently. */
1026 while (*prev != element)
1027 prev = &(*prev)->next;
1028 } while (prev && InterlockedCompareExchangePointer ((volatile gpointer *)prev, element->next, element) != element);
1032 ref_list_push (RefQueueEntry **head, RefQueueEntry *value)
1034 RefQueueEntry *current;
1037 value->next = current;
1038 STORE_STORE_FENCE; /*Must make sure the previous store is visible before the CAS. */
1039 } while (InterlockedCompareExchangePointer ((volatile gpointer *)head, value, current) != current);
1043 reference_queue_proccess (MonoReferenceQueue *queue)
1045 RefQueueEntry **iter = &queue->queue;
1046 RefQueueEntry *entry;
1047 while ((entry = *iter)) {
1048 if (queue->should_be_deleted || !mono_gchandle_get_target (entry->gchandle)) {
1049 mono_gchandle_free ((guint32)entry->gchandle);
1050 ref_list_remove_element (iter, entry);
1051 queue->callback (entry->user_data);
1054 iter = &entry->next;
1060 reference_queue_proccess_all (void)
1062 MonoReferenceQueue **iter;
1063 MonoReferenceQueue *queue = ref_queues;
1064 for (; queue; queue = queue->next)
1065 reference_queue_proccess (queue);
1068 mono_coop_mutex_lock (&reference_queue_mutex);
1069 for (iter = &ref_queues; *iter;) {
1071 if (!queue->should_be_deleted) {
1072 iter = &queue->next;
1076 mono_coop_mutex_unlock (&reference_queue_mutex);
1077 reference_queue_proccess (queue);
1080 *iter = queue->next;
1083 mono_coop_mutex_unlock (&reference_queue_mutex);
1087 mono_reference_queue_cleanup (void)
1089 MonoReferenceQueue *queue = ref_queues;
1090 for (; queue; queue = queue->next)
1091 queue->should_be_deleted = TRUE;
1092 reference_queue_proccess_all ();
1096 reference_queue_clear_for_domain (MonoDomain *domain)
1098 MonoReferenceQueue *queue = ref_queues;
1099 for (; queue; queue = queue->next) {
1100 RefQueueEntry **iter = &queue->queue;
1101 RefQueueEntry *entry;
1102 while ((entry = *iter)) {
1103 if (entry->domain == domain) {
1104 mono_gchandle_free ((guint32)entry->gchandle);
1105 ref_list_remove_element (iter, entry);
1106 queue->callback (entry->user_data);
1109 iter = &entry->next;
1115 * mono_gc_reference_queue_new:
1116 * @callback callback used when processing collected entries.
1118 * Create a new reference queue used to process collected objects.
1119 * A reference queue let you add a pair of (managed object, user data)
1120 * using the mono_gc_reference_queue_add method.
1122 * Once the managed object is collected @callback will be called
1123 * in the finalizer thread with 'user data' as argument.
1125 * The callback is called from the finalizer thread without any locks held.
1126 * When a AppDomain is unloaded, all callbacks for objects belonging to it
1129 * @returns the new queue.
1132 mono_gc_reference_queue_new (mono_reference_queue_callback callback)
1134 MonoReferenceQueue *res = g_new0 (MonoReferenceQueue, 1);
1135 res->callback = callback;
1137 mono_coop_mutex_lock (&reference_queue_mutex);
1138 res->next = ref_queues;
1140 mono_coop_mutex_unlock (&reference_queue_mutex);
1146 * mono_gc_reference_queue_add:
1147 * @queue the queue to add the reference to.
1148 * @obj the object to be watched for collection
1149 * @user_data parameter to be passed to the queue callback
1151 * Queue an object to be watched for collection, when the @obj is
1152 * collected, the callback that was registered for the @queue will
1153 * be invoked with @user_data as argument.
1155 * @returns false if the queue is scheduled to be freed.
1158 mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *user_data)
1160 RefQueueEntry *entry;
1161 if (queue->should_be_deleted)
1164 g_assert (obj != NULL);
1166 entry = g_new0 (RefQueueEntry, 1);
1167 entry->user_data = user_data;
1168 entry->domain = mono_object_domain (obj);
1170 entry->gchandle = mono_gchandle_new_weakref (obj, TRUE);
1171 mono_object_register_finalizer (obj);
1173 ref_list_push (&queue->queue, entry);
1178 * mono_gc_reference_queue_free:
1179 * @queue the queue that should be freed.
1181 * This operation signals that @queue should be freed. This operation is deferred
1182 * as it happens on the finalizer thread.
1184 * After this call, no further objects can be queued. It's the responsibility of the
1185 * caller to make sure that no further attempt to access queue will be made.
1188 mono_gc_reference_queue_free (MonoReferenceQueue *queue)
1190 queue->should_be_deleted = TRUE;