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.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/marshal.h> /* for mono_delegate_free_ftnptr () */
32 #include <mono/metadata/attach.h>
33 #include <mono/metadata/console-io.h>
34 #include <mono/utils/mono-os-semaphore.h>
35 #include <mono/utils/mono-memory-model.h>
36 #include <mono/utils/mono-counters.h>
37 #include <mono/utils/mono-time.h>
38 #include <mono/utils/dtrace.h>
39 #include <mono/utils/mono-threads.h>
40 #include <mono/utils/mono-threads-coop.h>
41 #include <mono/utils/atomic.h>
42 #include <mono/utils/mono-coop-semaphore.h>
43 #include <mono/utils/hazard-pointer.h>
44 #include <mono/utils/w32api.h>
50 typedef struct DomainFinalizationReq {
54 } DomainFinalizationReq;
56 static gboolean gc_disabled;
58 static gboolean finalizing_root_domain;
60 gboolean log_finalizers;
61 gboolean mono_do_not_finalize;
62 volatile gboolean suspend_finalizers;
63 gchar **mono_do_not_finalize_class_names ;
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;
72 static gboolean finalizer_thread_exited;
73 /* Uses finalizer_mutex */
74 static MonoCoopCond exited_cond;
76 static MonoInternalThread *gc_thread;
79 static HANDLE pending_done_event;
81 static gboolean pending_done;
82 static MonoCoopCond pending_done_cond;
83 static MonoCoopMutex pending_done_mutex;
86 static void object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*));
88 static void reference_queue_proccess_all (void);
89 static void mono_reference_queue_cleanup (void);
90 static void reference_queue_clear_for_domain (MonoDomain *domain);
93 static MonoThreadInfoWaitRet
94 guarded_wait (MonoThreadHandle *thread_handle, guint32 timeout, gboolean alertable)
96 MonoThreadInfoWaitRet result;
99 result = mono_thread_info_wait_one_handle (thread_handle, timeout, alertable);
107 MonoCoopMutex *mutex;
108 } BreakCoopAlertableWaitUD;
111 break_coop_alertable_wait (gpointer user_data)
113 BreakCoopAlertableWaitUD *ud = (BreakCoopAlertableWaitUD*)user_data;
115 mono_coop_mutex_lock (ud->mutex);
116 mono_coop_cond_signal (ud->cond);
117 mono_coop_mutex_unlock (ud->mutex);
123 * coop_cond_timedwait_alertable:
125 * Wait on COND/MUTEX. If ALERTABLE is non-null, the wait can be interrupted.
126 * In that case, *ALERTABLE will be set to TRUE, and 0 is returned.
129 coop_cond_timedwait_alertable (MonoCoopCond *cond, MonoCoopMutex *mutex, guint32 timeout_ms, gboolean *alertable)
131 BreakCoopAlertableWaitUD *ud;
135 ud = g_new0 (BreakCoopAlertableWaitUD, 1);
139 mono_thread_info_install_interrupt (break_coop_alertable_wait, ud, alertable);
145 res = mono_coop_cond_timedwait (cond, mutex, timeout_ms);
147 mono_thread_info_uninstall_interrupt (alertable);
151 /* the interrupt token has not been taken by another
152 * thread, so it's our responsability to free it up. */
160 * actually, we might want to queue the finalize requests in a separate thread,
161 * but we need to be careful about the execution domain of the thread...
164 mono_gc_run_finalize (void *obj, void *data)
167 MonoObject *exc = NULL;
172 MonoMethod* finalizer = NULL;
173 MonoDomain *caller_domain = mono_domain_get ();
175 RuntimeInvokeFunction runtime_invoke;
177 // This function is called from the innards of the GC, so our best alternative for now is to do polling here
178 mono_threads_safepoint ();
180 o = (MonoObject*)((char*)obj + GPOINTER_TO_UINT (data));
182 if (mono_do_not_finalize) {
183 if (!mono_do_not_finalize_class_names)
186 size_t namespace_len = strlen (o->vtable->klass->name_space);
187 for (int i = 0; mono_do_not_finalize_class_names [i]; ++i) {
188 const char *name = mono_do_not_finalize_class_names [i];
189 if (strncmp (name, o->vtable->klass->name_space, namespace_len))
191 if (name [namespace_len] != '.')
193 if (strcmp (name + namespace_len + 1, o->vtable->klass->name))
200 g_log ("mono-gc-finalizers", G_LOG_LEVEL_DEBUG, "<%s at %p> Starting finalizer checks.", o->vtable->klass->name, o);
202 if (suspend_finalizers)
205 domain = o->vtable->domain;
208 mono_domain_finalizers_lock (domain);
210 o2 = (MonoObject *)g_hash_table_lookup (domain->finalizable_objects_hash, o);
212 mono_domain_finalizers_unlock (domain);
215 /* Already finalized somehow */
219 /* make sure the finalizer is not called again if the object is resurrected */
220 object_register_finalizer ((MonoObject *)obj, NULL);
223 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Registered finalizer as processed.", o->vtable->klass->name, o);
225 if (o->vtable->klass == mono_defaults.internal_thread_class) {
226 MonoInternalThread *t = (MonoInternalThread*)o;
228 if (mono_gc_is_finalizer_internal_thread (t))
229 /* Avoid finalizing ourselves */
233 if (o->vtable->klass->image == mono_defaults.corlib && !strcmp (o->vtable->klass->name, "DynamicMethod") && finalizing_root_domain) {
235 * These can't be finalized during unloading/shutdown, since that would
236 * free the native code which can still be referenced by other
238 * FIXME: This is not perfect, objects dying at the same time as
239 * dynamic methods can still reference them even when !shutdown.
244 if (mono_runtime_get_no_exec ())
247 /* speedup later... and use a timeout */
248 /* g_print ("Finalize run on %p %s.%s\n", o, mono_object_class (o)->name_space, mono_object_class (o)->name); */
250 /* Use _internal here, since this thread can enter a doomed appdomain */
251 mono_domain_set_internal (mono_object_domain (o));
253 /* delegates that have a native function pointer allocated are
254 * registered for finalization, but they don't have a Finalize
255 * method, because in most cases it's not needed and it's just a waste.
257 if (o->vtable->klass->delegate) {
258 MonoDelegate* del = (MonoDelegate*)o;
259 if (del->delegate_trampoline)
260 mono_delegate_free_ftnptr ((MonoDelegate*)o);
261 mono_domain_set_internal (caller_domain);
265 finalizer = mono_class_get_finalizer (o->vtable->klass);
267 /* If object has a CCW but has no finalizer, it was only
268 * registered for finalization in order to free the CCW.
269 * Else it needs the regular finalizer run.
270 * FIXME: what to do about ressurection and suppression
271 * of finalizer on object with CCW.
273 if (mono_marshal_free_ccw (o) && !finalizer) {
274 mono_domain_set_internal (caller_domain);
279 * To avoid the locking plus the other overhead of mono_runtime_invoke_checked (),
280 * create and precompile a wrapper which calls the finalize method using
284 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Compiling finalizer.", o->vtable->klass->name, o);
286 if (!domain->finalize_runtime_invoke) {
287 MonoMethod *invoke = mono_marshal_get_runtime_invoke (mono_class_get_method_from_name_flags (mono_defaults.object_class, "Finalize", 0, 0), TRUE);
289 domain->finalize_runtime_invoke = mono_compile_method_checked (invoke, &error);
290 mono_error_assert_ok (&error); /* expect this not to fail */
293 runtime_invoke = (RuntimeInvokeFunction)domain->finalize_runtime_invoke;
295 mono_runtime_class_init_full (o->vtable, &error);
297 goto unhandled_error;
299 if (G_UNLIKELY (MONO_GC_FINALIZE_INVOKE_ENABLED ())) {
300 MONO_GC_FINALIZE_INVOKE ((unsigned long)o, mono_object_get_size (o),
301 o->vtable->klass->name_space, o->vtable->klass->name);
305 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Calling finalizer.", o->vtable->klass->name, o);
307 mono_profiler_gc_finalize_object_begin (o);
309 runtime_invoke (o, NULL, &exc, NULL);
311 mono_profiler_gc_finalize_object_end (o);
314 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Returned from finalizer.", o->vtable->klass->name, o);
318 exc = (MonoObject*)mono_error_convert_to_exception (&error);
320 mono_thread_internal_unhandled_exception (exc);
322 mono_domain_set_internal (caller_domain);
326 mono_gc_out_of_memory (size_t size)
329 * we could allocate at program startup some memory that we could release
330 * back to the system at this point if we're really low on memory (ie, size is
331 * lower than the memory we set apart)
333 mono_raise_exception (mono_domain_get ()->out_of_memory_ex);
339 * Some of our objects may point to a different address than the address returned by GC_malloc()
340 * (because of the GetHashCode hack), but we need to pass the real address to register_finalizer.
341 * This also means that in the callback we need to adjust the pointer to get back the real
343 * We also need to be consistent in the use of the GC_debug* variants of malloc and register_finalizer,
344 * since that, too, can cause the underlying pointer to be offset.
347 object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*))
351 g_assert (obj != NULL);
353 domain = obj->vtable->domain;
356 if (mono_domain_is_unloading (domain) && (callback != NULL))
358 * Can't register finalizers in a dying appdomain, since they
359 * could be invoked after the appdomain has been unloaded.
363 mono_domain_finalizers_lock (domain);
366 g_hash_table_insert (domain->finalizable_objects_hash, obj, obj);
368 g_hash_table_remove (domain->finalizable_objects_hash, obj);
370 mono_domain_finalizers_unlock (domain);
372 mono_gc_register_for_finalization (obj, callback);
373 #elif defined(HAVE_SGEN_GC)
375 * If we register finalizers for domains that are unloading we might
376 * end up running them while or after the domain is being cleared, so
377 * the objects will not be valid anymore.
379 if (!mono_domain_is_unloading (domain))
380 mono_gc_register_for_finalization (obj, callback);
385 * mono_object_register_finalizer:
386 * @obj: object to register
388 * Records that object @obj has a finalizer, this will call the
389 * Finalize method when the garbage collector disposes the object.
393 mono_object_register_finalizer (MonoObject *obj)
395 /* g_print ("Registered finalizer on %p %s.%s\n", obj, mono_object_class (obj)->name_space, mono_object_class (obj)->name); */
396 object_register_finalizer (obj, mono_gc_run_finalize);
400 * mono_domain_finalize:
401 * @domain: the domain to finalize
402 * @timeout: msects to wait for the finalization to complete, -1 to wait indefinitely
404 * Request finalization of all finalizable objects inside @domain. Wait
405 * @timeout msecs for the finalization to complete.
407 * Returns: TRUE if succeeded, FALSE if there was a timeout
411 mono_domain_finalize (MonoDomain *domain, guint32 timeout)
413 DomainFinalizationReq *req;
414 MonoInternalThread *thread = mono_thread_internal_current ();
419 #if defined(__native_client__)
423 if (mono_thread_internal_current () == gc_thread)
424 /* We are called from inside a finalizer, not much we can do here */
428 * No need to create another thread 'cause the finalizer thread
429 * is still working and will take care of running the finalizers
435 /* We don't support domain finalization without a GC */
436 if (mono_gc_is_null ())
439 mono_gc_collect (mono_gc_max_generation ());
441 req = g_new0 (DomainFinalizationReq, 1);
443 req->domain = domain;
444 mono_coop_sem_init (&req->done, 0);
446 if (domain == mono_get_root_domain ())
447 finalizing_root_domain = TRUE;
449 mono_finalizer_lock ();
451 domains_to_finalize = g_slist_append (domains_to_finalize, req);
453 mono_finalizer_unlock ();
455 /* Tell the finalizer thread to finalize this appdomain */
456 mono_gc_finalize_notify ();
459 timeout = MONO_INFINITE_WAIT;
460 if (timeout != MONO_INFINITE_WAIT)
461 start = mono_msec_ticks ();
466 if (timeout == MONO_INFINITE_WAIT) {
467 res = mono_coop_sem_wait (&req->done, MONO_SEM_FLAGS_ALERTABLE);
469 gint64 elapsed = mono_msec_ticks () - start;
470 if (elapsed >= timeout) {
475 res = mono_coop_sem_timedwait (&req->done, timeout - elapsed, MONO_SEM_FLAGS_ALERTABLE);
478 if (res == MONO_SEM_TIMEDWAIT_RET_SUCCESS) {
480 } else if (res == MONO_SEM_TIMEDWAIT_RET_ALERTED) {
481 if ((thread->state & (ThreadState_StopRequested | ThreadState_SuspendRequested)) != 0) {
485 } else if (res == MONO_SEM_TIMEDWAIT_RET_TIMEDOUT) {
489 g_error ("%s: unknown result %d", __func__, res);
494 /* Try removing the req from domains_to_finalize:
495 * - if it's not found: the domain is being finalized,
496 * so we the ref count is already decremented
497 * - if it's found: the domain is not yet being finalized,
498 * so we can safely decrement the ref */
502 mono_finalizer_lock ();
504 found = g_slist_index (domains_to_finalize, req) != -1;
506 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
508 mono_finalizer_unlock ();
511 /* We have to decrement it wherever we
512 * remove it from domains_to_finalize */
513 if (InterlockedDecrement (&req->ref) != 1)
514 g_error ("%s: req->ref should be 1, as we are the first one to decrement it", __func__);
521 if (InterlockedDecrement (&req->ref) == 0) {
522 mono_coop_sem_destroy (&req->done);
530 ves_icall_System_GC_InternalCollect (int generation)
532 mono_gc_collect (generation);
536 ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection)
539 mono_gc_collect (mono_gc_max_generation ());
540 return mono_gc_get_used_size ();
544 ves_icall_System_GC_KeepAlive (MonoObject *obj)
552 ves_icall_System_GC_ReRegisterForFinalize (MonoObject *obj)
554 MONO_CHECK_ARG_NULL (obj,);
556 object_register_finalizer (obj, mono_gc_run_finalize);
560 ves_icall_System_GC_SuppressFinalize (MonoObject *obj)
562 MONO_CHECK_ARG_NULL (obj,);
564 /* delegates have no finalizers, but we register them to deal with the
565 * unmanaged->managed trampoline. We don't let the user suppress it
566 * otherwise we'd leak it.
568 if (obj->vtable->klass->delegate)
571 /* FIXME: Need to handle case where obj has COM Callable Wrapper
572 * generated for it that needs cleaned up, but user wants to suppress
573 * their derived object finalizer. */
575 object_register_finalizer (obj, NULL);
579 ves_icall_System_GC_WaitForPendingFinalizers (void)
581 if (mono_gc_is_null ())
584 if (!mono_gc_pending_finalizers ())
587 if (mono_thread_internal_current () == gc_thread)
588 /* Avoid deadlocks */
592 If the finalizer thread is not live, lets pretend no finalizers are pending since the current thread might
593 be the one responsible for starting it up.
595 if (gc_thread == NULL)
599 ResetEvent (pending_done_event);
600 mono_gc_finalize_notify ();
601 /* g_print ("Waiting for pending finalizers....\n"); */
603 WaitForSingleObjectEx (pending_done_event, INFINITE, TRUE);
605 /* g_print ("Done pending....\n"); */
607 gboolean alerted = FALSE;
608 mono_coop_mutex_lock (&pending_done_mutex);
609 pending_done = FALSE;
610 mono_gc_finalize_notify ();
611 while (!pending_done) {
612 coop_cond_timedwait_alertable (&pending_done_cond, &pending_done_mutex, MONO_INFINITE_WAIT, &alerted);
616 mono_coop_mutex_unlock (&pending_done_mutex);
621 ves_icall_System_GC_register_ephemeron_array (MonoObject *array)
624 if (!mono_gc_ephemeron_array_add (array)) {
625 mono_set_pending_exception (mono_object_domain (array)->out_of_memory_ex);
632 ves_icall_System_GC_get_ephemeron_tombstone (void)
634 return mono_domain_get ()->ephemeron_tombstone;
638 ves_icall_System_GCHandle_GetTarget (guint32 handle)
640 return mono_gchandle_get_target (handle);
644 * if type == -1, change the target of the handle, otherwise allocate a new handle.
647 ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type)
650 mono_gchandle_set_target (handle, obj);
651 /* the handle doesn't change */
656 return mono_gchandle_new_weakref (obj, FALSE);
657 case HANDLE_WEAK_TRACK:
658 return mono_gchandle_new_weakref (obj, TRUE);
660 return mono_gchandle_new (obj, FALSE);
662 return mono_gchandle_new (obj, TRUE);
664 g_assert_not_reached ();
670 ves_icall_System_GCHandle_FreeHandle (guint32 handle)
672 mono_gchandle_free (handle);
676 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
680 if (MONO_GC_HANDLE_TYPE (handle) != HANDLE_PINNED)
682 obj = mono_gchandle_get_target (handle);
684 MonoClass *klass = mono_object_class (obj);
685 if (klass == mono_defaults.string_class) {
686 return mono_string_chars ((MonoString*)obj);
687 } else if (klass->rank) {
688 return mono_array_addr ((MonoArray*)obj, char, 0);
690 /* the C# code will check and throw the exception */
691 /* FIXME: missing !klass->blittable test, see bug #61134 */
692 if (mono_class_is_auto_layout (klass))
694 return (char*)obj + sizeof (MonoObject);
701 mono_gc_GCHandle_CheckCurrentDomain (guint32 gchandle)
703 return mono_gchandle_is_in_domain (gchandle, mono_domain_get ());
706 static MonoCoopSem finalizer_sem;
707 static volatile gboolean finished;
710 * mono_gc_finalize_notify:
712 * Notify the finalizer thread that finalizers etc.
713 * are available to be processed.
716 mono_gc_finalize_notify (void)
719 g_message ( "%s: prodding finalizer", __func__);
722 if (mono_gc_is_null ())
725 mono_coop_sem_post (&finalizer_sem);
729 This is the number of entries allowed in the hazard free queue before
730 we explicitly cycle the finalizer thread to trigger pumping the queue.
732 It was picked empirically by running the corlib test suite in a stress
733 scenario where all hazard entries are queued.
735 In this extreme scenario we double the number of times we cycle the finalizer
736 thread compared to just GC calls.
738 Entries are usually in the order of 100's of bytes each, so we're limiting
739 floating garbage to be in the order of a dozen kb.
741 static gboolean finalizer_thread_pulsed;
742 #define HAZARD_QUEUE_OVERFLOW_SIZE 20
745 hazard_free_queue_is_too_big (size_t size)
747 if (size < HAZARD_QUEUE_OVERFLOW_SIZE)
750 if (finalizer_thread_pulsed || InterlockedCompareExchange (&finalizer_thread_pulsed, TRUE, FALSE))
753 mono_gc_finalize_notify ();
757 hazard_free_queue_pump (void)
759 mono_thread_hazardous_try_free_all ();
760 finalizer_thread_pulsed = FALSE;
766 collect_objects (gpointer key, gpointer value, gpointer user_data)
768 GPtrArray *arr = (GPtrArray*)user_data;
769 g_ptr_array_add (arr, key);
775 * finalize_domain_objects:
777 * Run the finalizers of all finalizable objects in req->domain.
780 finalize_domain_objects (void)
782 DomainFinalizationReq *req = NULL;
785 if (domains_to_finalize) {
786 mono_finalizer_lock ();
787 if (domains_to_finalize) {
788 req = (DomainFinalizationReq *)domains_to_finalize->data;
789 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
791 mono_finalizer_unlock ();
797 domain = req->domain;
799 /* Process finalizers which are already in the queue */
800 mono_gc_invoke_finalizers ();
803 while (g_hash_table_size (domain->finalizable_objects_hash) > 0) {
807 * Since the domain is unloading, nobody is allowed to put
808 * new entries into the hash table. But finalize_object might
809 * remove entries from the hash table, so we make a copy.
811 objs = g_ptr_array_new ();
812 g_hash_table_foreach (domain->finalizable_objects_hash, collect_objects, objs);
813 /* printf ("FINALIZING %d OBJECTS.\n", objs->len); */
815 for (i = 0; i < objs->len; ++i) {
816 MonoObject *o = (MonoObject*)g_ptr_array_index (objs, i);
817 /* FIXME: Avoid finalizing threads, etc */
818 mono_gc_run_finalize (o, 0);
821 g_ptr_array_free (objs, TRUE);
823 #elif defined(HAVE_SGEN_GC)
824 mono_gc_finalize_domain (domain);
825 mono_gc_invoke_finalizers ();
828 /* cleanup the reference queue */
829 reference_queue_clear_for_domain (domain);
831 /* printf ("DONE.\n"); */
832 mono_coop_sem_post (&req->done);
834 if (InterlockedDecrement (&req->ref) == 0) {
835 /* mono_domain_finalize already returned, and
836 * doesn't hold a reference to req anymore. */
837 mono_coop_sem_destroy (&req->done);
843 finalizer_thread (gpointer unused)
846 gboolean wait = TRUE;
848 mono_thread_set_name_internal (mono_thread_internal_current (), mono_string_new (mono_get_root_domain (), "Finalizer"), FALSE, FALSE, &error);
849 mono_error_assert_ok (&error);
851 /* Register a hazard free queue pump callback */
852 mono_hazard_pointer_install_free_queue_size_callback (hazard_free_queue_is_too_big);
855 /* Wait to be notified that there's at least one
859 g_assert (mono_domain_get () == mono_get_root_domain ());
860 mono_gc_set_skip_thread (TRUE);
863 /* An alertable wait is required so this thread can be suspended on windows */
864 mono_coop_sem_wait (&finalizer_sem, MONO_SEM_FLAGS_ALERTABLE);
868 mono_gc_set_skip_thread (FALSE);
870 mono_threads_perform_thread_dump ();
872 mono_console_handle_async_ops ();
874 mono_attach_maybe_start ();
876 finalize_domain_objects ();
878 mono_profiler_gc_finalize_begin ();
880 /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup),
881 * before the domain is unloaded.
883 mono_gc_invoke_finalizers ();
885 mono_profiler_gc_finalize_end ();
887 mono_threads_join_threads ();
889 reference_queue_proccess_all ();
891 hazard_free_queue_pump ();
893 /* Avoid posting the pending done event until there are pending finalizers */
894 if (mono_coop_sem_timedwait (&finalizer_sem, 0, MONO_SEM_FLAGS_NONE) == MONO_SEM_TIMEDWAIT_RET_SUCCESS) {
895 /* Don't wait again at the start of the loop */
899 SetEvent (pending_done_event);
901 mono_coop_mutex_lock (&pending_done_mutex);
903 mono_coop_cond_signal (&pending_done_cond);
904 mono_coop_mutex_unlock (&pending_done_mutex);
909 mono_finalizer_lock ();
910 finalizer_thread_exited = TRUE;
911 mono_coop_cond_signal (&exited_cond);
912 mono_finalizer_unlock ();
917 #ifndef LAZY_GC_THREAD_CREATION
921 mono_gc_init_finalizer_thread (void)
924 gc_thread = mono_thread_create_internal (mono_domain_get (), finalizer_thread, NULL, FALSE, 0, &error);
925 mono_error_assert_ok (&error);
931 mono_coop_mutex_init_recursive (&finalizer_mutex);
932 mono_coop_mutex_init_recursive (&reference_queue_mutex);
934 mono_counters_register ("Minor GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.minor_gc_count);
935 mono_counters_register ("Major GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.major_gc_count);
936 mono_counters_register ("Minor GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.minor_gc_time);
937 mono_counters_register ("Major GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time);
938 mono_counters_register ("Major GC time concurrent", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time_concurrent);
940 mono_gc_base_init ();
942 if (mono_gc_is_disabled ()) {
948 pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
949 g_assert (pending_done_event);
951 mono_coop_cond_init (&pending_done_cond);
952 mono_coop_mutex_init (&pending_done_mutex);
955 mono_coop_cond_init (&exited_cond);
956 mono_coop_sem_init (&finalizer_sem, 0);
958 #ifndef LAZY_GC_THREAD_CREATION
959 mono_gc_init_finalizer_thread ();
964 mono_gc_cleanup (void)
967 g_message ("%s: cleaning up finalizer", __func__);
970 if (mono_gc_is_null ())
975 if (mono_thread_internal_current () != gc_thread) {
978 const gint64 timeout = 40 * 1000;
980 mono_gc_finalize_notify ();
982 start = mono_msec_ticks ();
984 /* Finishing the finalizer thread, so wait a little bit... */
985 /* MS seems to wait for about 2 seconds per finalizer thread */
986 /* and 40 seconds for all finalizers to finish */
990 if (finalizer_thread_exited) {
991 /* Wait for the thread to actually exit. We don't want the wait
992 * to be alertable, because we assert on the result to be SUCCESS_0 */
993 ret = guarded_wait (gc_thread->handle, MONO_INFINITE_WAIT, FALSE);
994 g_assert (ret == MONO_THREAD_INFO_WAIT_RET_SUCCESS_0);
996 mono_thread_join (GUINT_TO_POINTER (gc_thread->tid));
1000 elapsed = mono_msec_ticks () - start;
1001 if (elapsed >= timeout) {
1004 /* Set a flag which the finalizer thread can check */
1005 suspend_finalizers = TRUE;
1006 mono_gc_suspend_finalizers ();
1008 /* Try to abort the thread, in the hope that it is running managed code */
1009 mono_thread_internal_abort (gc_thread);
1011 /* Wait for it to stop */
1012 ret = guarded_wait (gc_thread->handle, 100, FALSE);
1013 if (ret == MONO_THREAD_INFO_WAIT_RET_TIMEOUT) {
1014 /* The finalizer thread refused to exit, suspend it forever. */
1015 mono_thread_internal_suspend_for_shutdown (gc_thread);
1019 g_assert (ret == MONO_THREAD_INFO_WAIT_RET_SUCCESS_0);
1021 mono_thread_join (GUINT_TO_POINTER (gc_thread->tid));
1025 mono_finalizer_lock ();
1026 if (!finalizer_thread_exited)
1027 mono_coop_cond_timedwait (&exited_cond, &finalizer_mutex, timeout - elapsed);
1028 mono_finalizer_unlock ();
1032 mono_gc_base_cleanup ();
1035 mono_reference_queue_cleanup ();
1037 mono_coop_mutex_destroy (&finalizer_mutex);
1038 mono_coop_mutex_destroy (&reference_queue_mutex);
1042 mono_gc_is_finalizer_internal_thread (MonoInternalThread *thread)
1044 return thread == gc_thread;
1048 * mono_gc_is_finalizer_thread:
1049 * @thread: the thread to test.
1051 * In Mono objects are finalized asynchronously on a separate thread.
1052 * This routine tests whether the @thread argument represents the
1053 * finalization thread.
1055 * Returns: TRUE if @thread is the finalization thread.
1058 mono_gc_is_finalizer_thread (MonoThread *thread)
1060 return mono_gc_is_finalizer_internal_thread (thread->internal_thread);
1063 #if defined(__MACH__)
1064 static pthread_t mach_exception_thread;
1067 mono_gc_register_mach_exception_thread (pthread_t thread)
1069 mach_exception_thread = thread;
1073 mono_gc_get_mach_exception_thread (void)
1075 return mach_exception_thread;
1079 static MonoReferenceQueue *ref_queues;
1082 ref_list_remove_element (RefQueueEntry **prev, RefQueueEntry *element)
1085 /* Guard if head is changed concurrently. */
1086 while (*prev != element)
1087 prev = &(*prev)->next;
1088 } while (prev && InterlockedCompareExchangePointer ((volatile gpointer *)prev, element->next, element) != element);
1092 ref_list_push (RefQueueEntry **head, RefQueueEntry *value)
1094 RefQueueEntry *current;
1097 value->next = current;
1098 STORE_STORE_FENCE; /*Must make sure the previous store is visible before the CAS. */
1099 } while (InterlockedCompareExchangePointer ((volatile gpointer *)head, value, current) != current);
1103 reference_queue_proccess (MonoReferenceQueue *queue)
1105 RefQueueEntry **iter = &queue->queue;
1106 RefQueueEntry *entry;
1107 while ((entry = *iter)) {
1108 if (queue->should_be_deleted || !mono_gchandle_get_target (entry->gchandle)) {
1109 mono_gchandle_free ((guint32)entry->gchandle);
1110 ref_list_remove_element (iter, entry);
1111 queue->callback (entry->user_data);
1114 iter = &entry->next;
1120 reference_queue_proccess_all (void)
1122 MonoReferenceQueue **iter;
1123 MonoReferenceQueue *queue = ref_queues;
1124 for (; queue; queue = queue->next)
1125 reference_queue_proccess (queue);
1128 mono_coop_mutex_lock (&reference_queue_mutex);
1129 for (iter = &ref_queues; *iter;) {
1131 if (!queue->should_be_deleted) {
1132 iter = &queue->next;
1136 mono_coop_mutex_unlock (&reference_queue_mutex);
1137 reference_queue_proccess (queue);
1140 *iter = queue->next;
1143 mono_coop_mutex_unlock (&reference_queue_mutex);
1147 mono_reference_queue_cleanup (void)
1149 MonoReferenceQueue *queue = ref_queues;
1150 for (; queue; queue = queue->next)
1151 queue->should_be_deleted = TRUE;
1152 reference_queue_proccess_all ();
1156 reference_queue_clear_for_domain (MonoDomain *domain)
1158 MonoReferenceQueue *queue = ref_queues;
1159 for (; queue; queue = queue->next) {
1160 RefQueueEntry **iter = &queue->queue;
1161 RefQueueEntry *entry;
1162 while ((entry = *iter)) {
1163 if (entry->domain == domain) {
1164 mono_gchandle_free ((guint32)entry->gchandle);
1165 ref_list_remove_element (iter, entry);
1166 queue->callback (entry->user_data);
1169 iter = &entry->next;
1175 * mono_gc_reference_queue_new:
1176 * @callback callback used when processing collected entries.
1178 * Create a new reference queue used to process collected objects.
1179 * A reference queue let you add a pair of (managed object, user data)
1180 * using the mono_gc_reference_queue_add method.
1182 * Once the managed object is collected @callback will be called
1183 * in the finalizer thread with 'user data' as argument.
1185 * The callback is called from the finalizer thread without any locks held.
1186 * When a AppDomain is unloaded, all callbacks for objects belonging to it
1189 * @returns the new queue.
1192 mono_gc_reference_queue_new (mono_reference_queue_callback callback)
1194 MonoReferenceQueue *res = g_new0 (MonoReferenceQueue, 1);
1195 res->callback = callback;
1197 mono_coop_mutex_lock (&reference_queue_mutex);
1198 res->next = ref_queues;
1200 mono_coop_mutex_unlock (&reference_queue_mutex);
1206 * mono_gc_reference_queue_add:
1207 * @queue the queue to add the reference to.
1208 * @obj the object to be watched for collection
1209 * @user_data parameter to be passed to the queue callback
1211 * Queue an object to be watched for collection, when the @obj is
1212 * collected, the callback that was registered for the @queue will
1213 * be invoked with @user_data as argument.
1215 * @returns false if the queue is scheduled to be freed.
1218 mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *user_data)
1220 RefQueueEntry *entry;
1221 if (queue->should_be_deleted)
1224 g_assert (obj != NULL);
1226 entry = g_new0 (RefQueueEntry, 1);
1227 entry->user_data = user_data;
1228 entry->domain = mono_object_domain (obj);
1230 entry->gchandle = mono_gchandle_new_weakref (obj, TRUE);
1231 mono_object_register_finalizer (obj);
1233 ref_list_push (&queue->queue, entry);
1238 * mono_gc_reference_queue_free:
1239 * @queue the queue that should be freed.
1241 * This operation signals that @queue should be freed. This operation is deferred
1242 * as it happens on the finalizer thread.
1244 * After this call, no further objects can be queued. It's the responsibility of the
1245 * caller to make sure that no further attempt to access queue will be made.
1248 mono_gc_reference_queue_free (MonoReferenceQueue *queue)
1250 queue->should_be_deleted = TRUE;