5 * Author: Paolo Molaro <lupus@ximian.com>
7 * Copyright 2002-2003 Ximian, Inc (http://www.ximian.com)
8 * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
9 * Copyright 2012 Xamarin Inc (http://www.xamarin.com)
10 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
17 #include <mono/metadata/gc-internals.h>
18 #include <mono/metadata/mono-gc.h>
19 #include <mono/metadata/threads.h>
20 #include <mono/metadata/tabledefs.h>
21 #include <mono/metadata/exception.h>
22 #include <mono/metadata/profiler-private.h>
23 #include <mono/metadata/domain-internals.h>
24 #include <mono/metadata/class-internals.h>
25 #include <mono/metadata/metadata-internals.h>
26 #include <mono/metadata/mono-mlist.h>
27 #include <mono/metadata/threads-types.h>
28 #include <mono/metadata/threadpool.h>
29 #include <mono/sgen/sgen-conf.h>
30 #include <mono/sgen/sgen-gc.h>
31 #include <mono/utils/mono-logger-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/metadata/w32process.h>
36 #include <mono/utils/mono-os-semaphore.h>
37 #include <mono/utils/mono-memory-model.h>
38 #include <mono/utils/mono-counters.h>
39 #include <mono/utils/mono-time.h>
40 #include <mono/utils/dtrace.h>
41 #include <mono/utils/mono-threads.h>
42 #include <mono/utils/mono-threads-coop.h>
43 #include <mono/utils/atomic.h>
44 #include <mono/utils/mono-coop-semaphore.h>
45 #include <mono/utils/hazard-pointer.h>
46 #include <mono/utils/w32api.h>
47 #include <mono/utils/unlocked.h>
48 #include <mono/utils/mono-os-wait.h>
49 #include <mono/utils/mono-lazy-init.h>
53 #include "external-only.h"
54 #include "icall-decl.h"
56 typedef struct DomainFinalizationReq
{
60 } DomainFinalizationReq
;
62 static gboolean gc_disabled
;
64 static gboolean finalizing_root_domain
;
66 gboolean mono_log_finalizers
;
67 gboolean mono_do_not_finalize
;
68 static volatile gboolean suspend_finalizers
;
69 gchar
**mono_do_not_finalize_class_names
;
71 #define mono_finalizer_lock() mono_coop_mutex_lock (&finalizer_mutex)
72 #define mono_finalizer_unlock() mono_coop_mutex_unlock (&finalizer_mutex)
73 static MonoCoopMutex finalizer_mutex
;
74 static MonoCoopMutex reference_queue_mutex
;
75 static mono_lazy_init_t reference_queue_mutex_inited
= MONO_LAZY_INIT_STATUS_NOT_INITIALIZED
;
77 static GSList
*domains_to_finalize
;
79 static gboolean finalizer_thread_exited
;
80 /* Uses finalizer_mutex */
81 static MonoCoopCond exited_cond
;
83 static MonoInternalThread
*gc_thread
;
86 static HANDLE pending_done_event
;
88 static gboolean pending_done
;
89 static MonoCoopCond pending_done_cond
;
90 static MonoCoopMutex pending_done_mutex
;
93 static void object_register_finalizer (MonoObject
*obj
, void (*callback
)(void *, void*));
95 static void reference_queue_proccess_all (void);
96 static void mono_reference_queue_cleanup (void);
97 static void reference_queue_clear_for_domain (MonoDomain
*domain
);
98 static void mono_runtime_do_background_work (void);
100 static MonoThreadInfoWaitRet
101 guarded_wait (MonoThreadHandle
*thread_handle
, guint32 timeout
, gboolean alertable
)
103 MonoThreadInfoWaitRet result
;
106 result
= mono_thread_info_wait_one_handle (thread_handle
, timeout
, alertable
);
114 MonoCoopMutex
*mutex
;
115 } BreakCoopAlertableWaitUD
;
118 break_coop_alertable_wait (gpointer user_data
)
120 BreakCoopAlertableWaitUD
*ud
= (BreakCoopAlertableWaitUD
*)user_data
;
122 mono_coop_mutex_lock (ud
->mutex
);
123 mono_coop_cond_signal (ud
->cond
);
124 mono_coop_mutex_unlock (ud
->mutex
);
130 * coop_cond_timedwait_alertable:
132 * Wait on COND/MUTEX. If ALERTABLE is non-null, the wait can be interrupted.
133 * In that case, *ALERTABLE will be set to TRUE, and 0 is returned.
136 coop_cond_timedwait_alertable (MonoCoopCond
*cond
, MonoCoopMutex
*mutex
, guint32 timeout_ms
, gboolean
*alertable
)
138 BreakCoopAlertableWaitUD
*ud
;
142 ud
= g_new0 (BreakCoopAlertableWaitUD
, 1);
146 mono_thread_info_install_interrupt (break_coop_alertable_wait
, ud
, alertable
);
152 res
= mono_coop_cond_timedwait (cond
, mutex
, timeout_ms
);
154 mono_thread_info_uninstall_interrupt (alertable
);
158 /* the interrupt token has not been taken by another
159 * thread, so it's our responsability to free it up. */
167 * actually, we might want to queue the finalize requests in a separate thread,
168 * but we need to be careful about the execution domain of the thread...
171 mono_gc_run_finalize (void *obj
, void *data
)
174 MonoObject
*exc
= NULL
;
179 MonoMethod
* finalizer
= NULL
;
180 MonoDomain
*caller_domain
= mono_domain_get ();
183 // This function is called from the innards of the GC, so our best alternative for now is to do polling here
184 mono_threads_safepoint ();
186 o
= (MonoObject
*)((char*)obj
+ GPOINTER_TO_UINT (data
));
188 const char *o_ns
= m_class_get_name_space (mono_object_class (o
));
189 const char *o_name
= m_class_get_name (mono_object_class (o
));
191 if (mono_do_not_finalize
) {
192 if (!mono_do_not_finalize_class_names
)
195 size_t namespace_len
= strlen (o_ns
);
196 for (int i
= 0; mono_do_not_finalize_class_names
[i
]; ++i
) {
197 const char *name
= mono_do_not_finalize_class_names
[i
];
198 if (strncmp (name
, o_ns
, namespace_len
))
200 if (name
[namespace_len
] != '.')
202 if (strcmp (name
+ namespace_len
+ 1, o_name
))
208 if (mono_log_finalizers
)
209 g_log ("mono-gc-finalizers", G_LOG_LEVEL_DEBUG
, "<%s at %p> Starting finalizer checks.", o_name
, o
);
211 if (suspend_finalizers
)
214 domain
= o
->vtable
->domain
;
217 mono_domain_finalizers_lock (domain
);
219 o2
= (MonoObject
*)g_hash_table_lookup (domain
->finalizable_objects_hash
, o
);
221 mono_domain_finalizers_unlock (domain
);
224 /* Already finalized somehow */
228 /* make sure the finalizer is not called again if the object is resurrected */
229 object_register_finalizer ((MonoObject
*)obj
, NULL
);
231 if (mono_log_finalizers
)
232 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE
, "<%s at %p> Registered finalizer as processed.", o_name
, o
);
234 if (o
->vtable
->klass
== mono_defaults
.internal_thread_class
) {
235 MonoInternalThread
*t
= (MonoInternalThread
*)o
;
237 if (mono_gc_is_finalizer_internal_thread (t
))
238 /* Avoid finalizing ourselves */
242 if (m_class_get_image (mono_object_class (o
)) == mono_defaults
.corlib
&& !strcmp (o_name
, "DynamicMethod") && finalizing_root_domain
) {
244 * These can't be finalized during unloading/shutdown, since that would
245 * free the native code which can still be referenced by other
247 * FIXME: This is not perfect, objects dying at the same time as
248 * dynamic methods can still reference them even when !shutdown.
253 if (mono_runtime_get_no_exec ())
256 /* speedup later... and use a timeout */
257 /* g_print ("Finalize run on %p %s.%s\n", o, mono_object_class (o)->name_space, mono_object_class (o)->name); */
259 /* Use _internal here, since this thread can enter a doomed appdomain */
260 mono_domain_set_internal (mono_object_domain (o
));
262 /* delegates that have a native function pointer allocated are
263 * registered for finalization, but they don't have a Finalize
264 * method, because in most cases it's not needed and it's just a waste.
266 if (m_class_is_delegate (mono_object_class (o
))) {
267 MonoDelegate
* del
= (MonoDelegate
*)o
;
268 if (del
->delegate_trampoline
)
269 mono_delegate_free_ftnptr ((MonoDelegate
*)o
);
270 mono_domain_set_internal (caller_domain
);
274 finalizer
= mono_class_get_finalizer (o
->vtable
->klass
);
276 /* If object has a CCW but has no finalizer, it was only
277 * registered for finalization in order to free the CCW.
278 * Else it needs the regular finalizer run.
279 * FIXME: what to do about ressurection and suppression
280 * of finalizer on object with CCW.
282 if (mono_marshal_free_ccw (o
) && !finalizer
) {
283 mono_domain_set_internal (caller_domain
);
288 * To avoid the locking plus the other overhead of mono_runtime_invoke_checked (),
289 * create and precompile a wrapper which calls the finalize method using
292 if (mono_log_finalizers
)
293 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE
, "<%s at %p> Compiling finalizer.", o_name
, o
);
296 if (!domain
->finalize_runtime_invoke
) {
297 MonoMethod
*finalize_method
= mono_class_get_method_from_name_checked (mono_defaults
.object_class
, "Finalize", 0, 0, error
);
298 mono_error_assert_ok (error
);
299 MonoMethod
*invoke
= mono_marshal_get_runtime_invoke (finalize_method
, TRUE
);
301 domain
->finalize_runtime_invoke
= mono_compile_method_checked (invoke
, error
);
302 mono_error_assert_ok (error
); /* expect this not to fail */
305 RuntimeInvokeFunction runtime_invoke
= (RuntimeInvokeFunction
)domain
->finalize_runtime_invoke
;
308 mono_runtime_class_init_full (o
->vtable
, error
);
309 goto_if_nok (error
, unhandled_error
);
311 if (G_UNLIKELY (MONO_GC_FINALIZE_INVOKE_ENABLED ())) {
312 MONO_GC_FINALIZE_INVOKE ((unsigned long)o
, mono_object_get_size_internal (o
),
316 if (mono_log_finalizers
)
317 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE
, "<%s at %p> Calling finalizer.", o_name
, o
);
319 MONO_PROFILER_RAISE (gc_finalizing_object
, (o
));
322 if (finalizer
) { // null finalizers work fine when using the vcall invoke as Object has an empty one
325 mono_runtime_try_invoke (finalizer
, o
, params
, &exc
, error
);
328 runtime_invoke (o
, NULL
, &exc
, NULL
);
331 MONO_PROFILER_RAISE (gc_finalized_object
, (o
));
333 if (mono_log_finalizers
)
334 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE
, "<%s at %p> Returned from finalizer.", o_name
, o
);
338 exc
= (MonoObject
*)mono_error_convert_to_exception (error
);
340 mono_thread_internal_unhandled_exception (exc
);
342 mono_domain_set_internal (caller_domain
);
346 * Some of our objects may point to a different address than the address returned by GC_malloc()
347 * (because of the GetHashCode hack), but we need to pass the real address to register_finalizer.
348 * This also means that in the callback we need to adjust the pointer to get back the real
350 * We also need to be consistent in the use of the GC_debug* variants of malloc and register_finalizer,
351 * since that, too, can cause the underlying pointer to be offset.
354 object_register_finalizer (MonoObject
*obj
, void (*callback
)(void *, void*))
358 g_assert (obj
!= NULL
);
360 domain
= obj
->vtable
->domain
;
363 if (mono_domain_is_unloading (domain
) && (callback
!= NULL
))
365 * Can't register finalizers in a dying appdomain, since they
366 * could be invoked after the appdomain has been unloaded.
370 mono_domain_finalizers_lock (domain
);
373 g_hash_table_insert (domain
->finalizable_objects_hash
, obj
, obj
);
375 g_hash_table_remove (domain
->finalizable_objects_hash
, obj
);
377 mono_domain_finalizers_unlock (domain
);
379 mono_gc_register_for_finalization (obj
, callback
);
380 #elif defined(HAVE_SGEN_GC)
382 * If we register finalizers for domains that are unloading we might
383 * end up running them while or after the domain is being cleared, so
384 * the objects will not be valid anymore.
386 if (!mono_domain_is_unloading (domain
))
387 mono_gc_register_for_finalization (obj
, callback
);
392 * mono_object_register_finalizer:
393 * \param obj object to register
395 * Records that object \p obj has a finalizer, this will call the
396 * Finalize method when the garbage collector disposes the object.
400 mono_object_register_finalizer_handle (MonoObjectHandle obj
)
402 /* g_print ("Registered finalizer on %p %s.%s\n", obj, mono_handle_class (obj)->name_space, mono_handle_class (obj)->name); */
403 object_register_finalizer (MONO_HANDLE_RAW (obj
), mono_gc_run_finalize
);
407 mono_object_unregister_finalizer_handle (MonoObjectHandle obj
)
409 /* g_print ("Unregistered finalizer on %p %s.%s\n", obj, mono_handle_class (obj)->name_space, mono_handle_class (obj)->name); */
410 object_register_finalizer (MONO_HANDLE_RAW (obj
), NULL
);
414 mono_object_register_finalizer (MonoObject
*obj
)
416 /* g_print ("Registered finalizer on %p %s.%s\n", obj, mono_object_class (obj)->name_space, mono_object_class (obj)->name); */
417 object_register_finalizer (obj
, mono_gc_run_finalize
);
421 * mono_domain_finalize:
422 * \param domain the domain to finalize
423 * \param timeout msecs to wait for the finalization to complete, \c -1 to wait indefinitely
425 * Request finalization of all finalizable objects inside \p domain. Wait
426 * \p timeout msecs for the finalization to complete.
428 * \returns TRUE if succeeded, FALSE if there was a timeout
431 mono_domain_finalize (MonoDomain
*domain
, guint32 timeout
)
433 DomainFinalizationReq
*req
;
434 MonoInternalThread
*thread
= mono_thread_internal_current ();
439 if (mono_thread_internal_current () == gc_thread
)
440 /* We are called from inside a finalizer, not much we can do here */
444 * No need to create another thread 'cause the finalizer thread
445 * is still working and will take care of running the finalizers
451 /* We don't support domain finalization without a GC */
452 if (mono_gc_is_null ())
455 mono_gc_collect (mono_gc_max_generation ());
457 req
= g_new0 (DomainFinalizationReq
, 1);
459 req
->domain
= domain
;
460 mono_coop_sem_init (&req
->done
, 0);
462 if (domain
== mono_get_root_domain ())
463 finalizing_root_domain
= TRUE
;
465 mono_finalizer_lock ();
467 domains_to_finalize
= g_slist_append (domains_to_finalize
, req
);
469 mono_finalizer_unlock ();
471 /* Tell the finalizer thread to finalize this appdomain */
472 mono_gc_finalize_notify ();
475 timeout
= MONO_INFINITE_WAIT
;
476 if (timeout
!= MONO_INFINITE_WAIT
)
477 start
= mono_msec_ticks ();
482 if (timeout
== MONO_INFINITE_WAIT
) {
483 res
= mono_coop_sem_wait (&req
->done
, MONO_SEM_FLAGS_ALERTABLE
);
485 gint64 elapsed
= mono_msec_ticks () - start
;
486 if (elapsed
>= timeout
) {
491 res
= mono_coop_sem_timedwait (&req
->done
, timeout
- elapsed
, MONO_SEM_FLAGS_ALERTABLE
);
494 if (res
== MONO_SEM_TIMEDWAIT_RET_SUCCESS
) {
496 } else if (res
== MONO_SEM_TIMEDWAIT_RET_ALERTED
) {
497 if ((thread
->state
& (ThreadState_AbortRequested
| ThreadState_SuspendRequested
)) != 0) {
501 } else if (res
== MONO_SEM_TIMEDWAIT_RET_TIMEDOUT
) {
505 g_error ("%s: unknown result %d", __func__
, res
);
510 /* Try removing the req from domains_to_finalize:
511 * - if it's not found: the domain is being finalized,
512 * so we the ref count is already decremented
513 * - if it's found: the domain is not yet being finalized,
514 * so we can safely decrement the ref */
518 mono_finalizer_lock ();
520 found
= g_slist_index (domains_to_finalize
, req
) != -1;
522 domains_to_finalize
= g_slist_remove (domains_to_finalize
, req
);
524 mono_finalizer_unlock ();
527 /* We have to decrement it wherever we
528 * remove it from domains_to_finalize */
529 if (mono_atomic_dec_i32 (&req
->ref
) != 1)
530 g_error ("%s: req->ref should be 1, as we are the first one to decrement it", __func__
);
537 if (mono_atomic_dec_i32 (&req
->ref
) == 0) {
538 mono_coop_sem_destroy (&req
->done
);
546 ves_icall_System_GC_InternalCollect (int generation
)
548 mono_gc_collect (generation
);
552 ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection
)
555 mono_gc_collect (mono_gc_max_generation ());
556 return mono_gc_get_used_size ();
560 ves_icall_System_GC_KeepAlive (MonoObjectHandle obj
, MonoError
*error
)
568 ves_icall_System_GC_ReRegisterForFinalize (MonoObjectHandle obj
, MonoError
*error
)
570 MONO_CHECK_ARG_NULL_HANDLE (obj
,);
572 mono_object_register_finalizer_handle (obj
);
576 ves_icall_System_GC_SuppressFinalize (MonoObjectHandle obj
, MonoError
*error
)
578 MONO_CHECK_ARG_NULL_HANDLE (obj
,);
580 /* delegates have no finalizers, but we register them to deal with the
581 * unmanaged->managed trampoline. We don't let the user suppress it
582 * otherwise we'd leak it.
584 if (m_class_is_delegate (mono_handle_class (obj
)))
587 /* FIXME: Need to handle case where obj has COM Callable Wrapper
588 * generated for it that needs cleaned up, but user wants to suppress
589 * their derived object finalizer. */
591 mono_object_unregister_finalizer_handle (obj
);
595 ves_icall_System_GC_WaitForPendingFinalizers (void)
597 if (mono_gc_is_null ())
600 if (!mono_gc_pending_finalizers ())
603 if (mono_thread_internal_current () == gc_thread
)
604 /* Avoid deadlocks */
608 If the finalizer thread is not live, lets pretend no finalizers are pending since the current thread might
609 be the one responsible for starting it up.
611 if (gc_thread
== NULL
)
615 ResetEvent (pending_done_event
);
616 mono_gc_finalize_notify ();
617 /* g_print ("Waiting for pending finalizers....\n"); */
619 mono_win32_wait_for_single_object_ex (pending_done_event
, INFINITE
, TRUE
);
621 /* g_print ("Done pending....\n"); */
623 gboolean alerted
= FALSE
;
624 mono_coop_mutex_lock (&pending_done_mutex
);
625 pending_done
= FALSE
;
626 mono_gc_finalize_notify ();
627 while (!pending_done
) {
628 coop_cond_timedwait_alertable (&pending_done_cond
, &pending_done_mutex
, MONO_INFINITE_WAIT
, &alerted
);
632 mono_coop_mutex_unlock (&pending_done_mutex
);
637 ves_icall_System_GC_register_ephemeron_array (MonoObjectHandle array
, MonoError
*error
)
639 if (!mono_gc_ephemeron_array_add (MONO_HANDLE_RAW (array
)))
640 mono_error_set_out_of_memory (error
, "");
644 ves_icall_System_GC_get_ephemeron_tombstone (MonoError
*error
)
646 return MONO_HANDLE_NEW (MonoObject
, mono_domain_get ()->ephemeron_tombstone
);
652 ves_icall_System_GCHandle_Alloc (MonoObjectHandle obj
, gint32 type
, MonoError
*error
)
658 handle
= mono_gchandle_new_weakref_from_handle (obj
);
660 case HANDLE_WEAK_TRACK
:
661 handle
= mono_gchandle_new_weakref_from_handle_track_resurrection (obj
);
664 handle
= mono_gchandle_from_handle (obj
, FALSE
);
667 handle
= mono_gchandle_from_handle (obj
, TRUE
);
670 g_assert_not_reached ();
672 /* The lowest bit is used to mark pinned handles by netcore's GCHandle class */
673 return GUINT_TO_POINTER (handle
<< 1);
677 ves_icall_System_GCHandle_Free (gpointer handle
, MonoError
*error
)
679 mono_gchandle_free_internal (GPOINTER_TO_UINT (handle
) >> 1);
683 ves_icall_System_GCHandle_Get (gpointer handle
, MonoError
*error
)
685 return mono_gchandle_get_target_handle (GPOINTER_TO_UINT (handle
) >> 1);
689 ves_icall_System_GCHandle_Set (gpointer handle
, MonoObjectHandle obj
, MonoError
*error
)
691 mono_gchandle_set_target_handle (GPOINTER_TO_UINT (handle
) >> 1, obj
);
697 ves_icall_System_GCHandle_GetTarget (guint32 handle
, MonoError
*error
)
699 return mono_gchandle_get_target_handle (handle
);
703 * if type == -1, change the target of the handle, otherwise allocate a new handle.
706 ves_icall_System_GCHandle_GetTargetHandle (MonoObjectHandle obj
, guint32 handle
, gint32 type
, MonoError
*error
)
709 mono_gchandle_set_target_handle (handle
, obj
);
710 /* the handle doesn't change */
715 return mono_gchandle_new_weakref_from_handle (obj
);
716 case HANDLE_WEAK_TRACK
:
717 return mono_gchandle_new_weakref_from_handle_track_resurrection (obj
);
719 return mono_gchandle_from_handle (obj
, FALSE
);
721 return mono_gchandle_from_handle (obj
, TRUE
);
723 g_assert_not_reached ();
729 ves_icall_System_GCHandle_FreeHandle (guint32 handle
)
731 mono_gchandle_free_internal (handle
);
735 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle
)
737 // Handles seem to only be in the way here, and the object is pinned.
741 if (MONO_GC_HANDLE_TYPE (handle
) != HANDLE_PINNED
)
743 obj
= mono_gchandle_get_target_internal (handle
);
745 MonoClass
*klass
= mono_object_class (obj
);
747 // FIXME This would be a good place for
748 // object->GetAddrOfPinnedObject()
749 // or klass->GetAddrOfPinnedObject(obj);
751 if (klass
== mono_defaults
.string_class
) {
752 return mono_string_chars_internal ((MonoString
*)obj
);
753 } else if (m_class_get_rank (klass
)) {
754 return mono_array_addr_internal ((MonoArray
*)obj
, char, 0);
756 /* the C# code will check and throw the exception */
757 /* FIXME: missing !klass->blittable test, see bug #61134 */
758 if (mono_class_is_auto_layout (klass
))
760 return mono_object_get_data (obj
);
767 ves_icall_System_GCHandle_CheckCurrentDomain (guint32 gchandle
)
769 return mono_gchandle_is_in_domain (gchandle
, mono_domain_get ());
774 static MonoCoopSem finalizer_sem
;
775 static volatile gboolean finished
;
778 * mono_gc_finalize_notify:
780 * Notify the finalizer thread that finalizers etc.
781 * are available to be processed.
782 * This is async signal safe.
785 mono_gc_finalize_notify (void)
788 g_message ( "%s: prodding finalizer", __func__
);
791 if (mono_gc_is_null ())
795 mono_threads_schedule_background_job (mono_runtime_do_background_work
);
797 mono_coop_sem_post (&finalizer_sem
);
802 This is the number of entries allowed in the hazard free queue before
803 we explicitly cycle the finalizer thread to trigger pumping the queue.
805 It was picked empirically by running the corlib test suite in a stress
806 scenario where all hazard entries are queued.
808 In this extreme scenario we double the number of times we cycle the finalizer
809 thread compared to just GC calls.
811 Entries are usually in the order of 100's of bytes each, so we're limiting
812 floating garbage to be in the order of a dozen kb.
814 static gboolean finalizer_thread_pulsed
;
815 #define HAZARD_QUEUE_OVERFLOW_SIZE 20
818 hazard_free_queue_is_too_big (size_t size
)
820 if (size
< HAZARD_QUEUE_OVERFLOW_SIZE
)
823 if (finalizer_thread_pulsed
|| mono_atomic_cas_i32 (&finalizer_thread_pulsed
, TRUE
, FALSE
))
826 mono_gc_finalize_notify ();
830 hazard_free_queue_pump (void)
832 mono_thread_hazardous_try_free_all ();
833 finalizer_thread_pulsed
= FALSE
;
839 collect_objects (gpointer key
, gpointer value
, gpointer user_data
)
841 GPtrArray
*arr
= (GPtrArray
*)user_data
;
842 g_ptr_array_add (arr
, key
);
848 * finalize_domain_objects:
850 * Run the finalizers of all finalizable objects in req->domain.
853 finalize_domain_objects (void)
855 DomainFinalizationReq
*req
= NULL
;
858 if (UnlockedReadPointer ((gpointer
volatile*)&domains_to_finalize
)) {
859 mono_finalizer_lock ();
860 if (domains_to_finalize
) {
861 req
= (DomainFinalizationReq
*)domains_to_finalize
->data
;
862 domains_to_finalize
= g_slist_remove (domains_to_finalize
, req
);
864 mono_finalizer_unlock ();
870 domain
= req
->domain
;
872 /* Process finalizers which are already in the queue */
873 mono_gc_invoke_finalizers ();
876 while (g_hash_table_size (domain
->finalizable_objects_hash
) > 0) {
880 * Since the domain is unloading, nobody is allowed to put
881 * new entries into the hash table. But finalize_object might
882 * remove entries from the hash table, so we make a copy.
884 objs
= g_ptr_array_new ();
885 g_hash_table_foreach (domain
->finalizable_objects_hash
, collect_objects
, objs
);
886 /* printf ("FINALIZING %d OBJECTS.\n", objs->len); */
888 for (i
= 0; i
< objs
->len
; ++i
) {
889 MonoObject
*o
= (MonoObject
*)g_ptr_array_index (objs
, i
);
890 /* FIXME: Avoid finalizing threads, etc */
891 mono_gc_run_finalize (o
, 0);
894 g_ptr_array_free (objs
, TRUE
);
896 #elif defined(HAVE_SGEN_GC)
897 mono_gc_finalize_domain (domain
);
898 mono_gc_invoke_finalizers ();
901 /* cleanup the reference queue */
902 reference_queue_clear_for_domain (domain
);
904 /* printf ("DONE.\n"); */
905 mono_coop_sem_post (&req
->done
);
907 if (mono_atomic_dec_i32 (&req
->ref
) == 0) {
908 /* mono_domain_finalize already returned, and
909 * doesn't hold a reference to req anymore. */
910 mono_coop_sem_destroy (&req
->done
);
917 mono_runtime_do_background_work (void)
919 mono_threads_perform_thread_dump ();
921 mono_console_handle_async_ops ();
923 mono_attach_maybe_start ();
925 finalize_domain_objects ();
927 MONO_PROFILER_RAISE (gc_finalizing
, ());
929 /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup),
930 * before the domain is unloaded.
932 mono_gc_invoke_finalizers ();
934 MONO_PROFILER_RAISE (gc_finalized
, ());
936 mono_threads_join_threads ();
938 reference_queue_proccess_all ();
940 mono_w32process_signal_finished ();
942 hazard_free_queue_pump ();
946 finalizer_thread (gpointer unused
)
949 gboolean wait
= TRUE
;
951 MonoString
*finalizer
= mono_string_new_checked (mono_get_root_domain (), "Finalizer", error
);
952 mono_error_assert_ok (error
);
953 mono_thread_set_name_internal (mono_thread_internal_current (), finalizer
, FALSE
, FALSE
, error
);
954 mono_error_assert_ok (error
);
956 /* Register a hazard free queue pump callback */
957 mono_hazard_pointer_install_free_queue_size_callback (hazard_free_queue_is_too_big
);
960 /* Wait to be notified that there's at least one
964 g_assert (mono_domain_get () == mono_get_root_domain ());
965 mono_thread_info_set_flags (MONO_THREAD_INFO_FLAGS_NO_GC
);
968 /* An alertable wait is required so this thread can be suspended on windows */
969 mono_coop_sem_wait (&finalizer_sem
, MONO_SEM_FLAGS_ALERTABLE
);
973 mono_thread_info_set_flags (MONO_THREAD_INFO_FLAGS_NONE
);
975 mono_runtime_do_background_work ();
977 /* Avoid posting the pending done event until there are pending finalizers */
978 if (mono_coop_sem_timedwait (&finalizer_sem
, 0, MONO_SEM_FLAGS_NONE
) == MONO_SEM_TIMEDWAIT_RET_SUCCESS
) {
979 /* Don't wait again at the start of the loop */
983 SetEvent (pending_done_event
);
985 mono_coop_mutex_lock (&pending_done_mutex
);
987 mono_coop_cond_signal (&pending_done_cond
);
988 mono_coop_mutex_unlock (&pending_done_mutex
);
993 mono_finalizer_lock ();
994 finalizer_thread_exited
= TRUE
;
995 mono_coop_cond_signal (&exited_cond
);
996 mono_finalizer_unlock ();
1001 #ifndef LAZY_GC_THREAD_CREATION
1005 mono_gc_init_finalizer_thread (void)
1008 gc_thread
= mono_thread_create_internal (mono_domain_get (), (gpointer
)finalizer_thread
, NULL
, MONO_THREAD_CREATE_FLAGS_NONE
, error
);
1009 mono_error_assert_ok (error
);
1013 reference_queue_mutex_init (void)
1015 mono_coop_mutex_init_recursive (&reference_queue_mutex
);
1021 mono_lazy_initialize (&reference_queue_mutex_inited
, reference_queue_mutex_init
);
1022 mono_coop_mutex_init_recursive (&finalizer_mutex
);
1024 mono_counters_register ("Minor GC collections", MONO_COUNTER_GC
| MONO_COUNTER_INT
, &mono_gc_stats
.minor_gc_count
);
1025 mono_counters_register ("Major GC collections", MONO_COUNTER_GC
| MONO_COUNTER_INT
, &mono_gc_stats
.major_gc_count
);
1026 mono_counters_register ("Minor GC time", MONO_COUNTER_GC
| MONO_COUNTER_ULONG
| MONO_COUNTER_TIME
, &mono_gc_stats
.minor_gc_time
);
1027 mono_counters_register ("Major GC time", MONO_COUNTER_GC
| MONO_COUNTER_LONG
| MONO_COUNTER_TIME
, &mono_gc_stats
.major_gc_time
);
1028 mono_counters_register ("Major GC time concurrent", MONO_COUNTER_GC
| MONO_COUNTER_LONG
| MONO_COUNTER_TIME
, &mono_gc_stats
.major_gc_time_concurrent
);
1030 mono_gc_base_init ();
1032 if (mono_gc_is_disabled ()) {
1038 pending_done_event
= CreateEvent (NULL
, TRUE
, FALSE
, NULL
);
1039 g_assert (pending_done_event
);
1041 mono_coop_cond_init (&pending_done_cond
);
1042 mono_coop_mutex_init (&pending_done_mutex
);
1045 mono_coop_cond_init (&exited_cond
);
1046 mono_coop_sem_init (&finalizer_sem
, 0);
1048 #ifndef LAZY_GC_THREAD_CREATION
1049 if (!mono_runtime_get_no_exec ())
1050 mono_gc_init_finalizer_thread ();
1055 mono_gc_cleanup (void)
1058 g_message ("%s: cleaning up finalizer", __func__
);
1061 if (mono_gc_is_null ())
1066 if (mono_thread_internal_current () != gc_thread
) {
1069 const gint64 timeout
= 40 * 1000;
1071 mono_gc_finalize_notify ();
1073 start
= mono_msec_ticks ();
1075 /* Finishing the finalizer thread, so wait a little bit... */
1076 /* MS seems to wait for about 2 seconds per finalizer thread */
1077 /* and 40 seconds for all finalizers to finish */
1081 if (finalizer_thread_exited
) {
1082 /* Wait for the thread to actually exit. We don't want the wait
1083 * to be alertable, because we assert on the result to be SUCCESS_0 */
1084 ret
= guarded_wait (gc_thread
->handle
, MONO_INFINITE_WAIT
, FALSE
);
1085 g_assert (ret
== MONO_THREAD_INFO_WAIT_RET_SUCCESS_0
);
1087 mono_threads_add_joinable_thread ((gpointer
)(MONO_UINT_TO_NATIVE_THREAD_ID (gc_thread
->tid
)));
1091 elapsed
= mono_msec_ticks () - start
;
1092 if (elapsed
>= timeout
) {
1095 /* Set a flag which the finalizer thread can check */
1096 suspend_finalizers
= TRUE
;
1097 mono_gc_suspend_finalizers ();
1099 /* Try to abort the thread, in the hope that it is running managed code */
1100 mono_thread_internal_abort (gc_thread
, FALSE
);
1102 /* Wait for it to stop */
1103 ret
= guarded_wait (gc_thread
->handle
, 100, FALSE
);
1104 if (ret
== MONO_THREAD_INFO_WAIT_RET_TIMEOUT
) {
1105 /* The finalizer thread refused to exit, suspend it forever. */
1106 mono_thread_internal_suspend_for_shutdown (gc_thread
);
1110 g_assert (ret
== MONO_THREAD_INFO_WAIT_RET_SUCCESS_0
);
1112 mono_threads_add_joinable_thread ((gpointer
)(MONO_UINT_TO_NATIVE_THREAD_ID (gc_thread
->tid
)));
1116 mono_finalizer_lock ();
1117 if (!finalizer_thread_exited
)
1118 mono_coop_cond_timedwait (&exited_cond
, &finalizer_mutex
, timeout
- elapsed
);
1119 mono_finalizer_unlock ();
1123 mono_gc_base_cleanup ();
1126 mono_reference_queue_cleanup ();
1128 mono_coop_mutex_destroy (&finalizer_mutex
);
1129 mono_coop_mutex_destroy (&reference_queue_mutex
);
1133 mono_gc_is_finalizer_internal_thread (MonoInternalThread
*thread
)
1135 return thread
== gc_thread
;
1139 * mono_gc_is_finalizer_thread:
1140 * \param thread the thread to test.
1142 * In Mono objects are finalized asynchronously on a separate thread.
1143 * This routine tests whether the \p thread argument represents the
1144 * finalization thread.
1146 * \returns TRUE if \p thread is the finalization thread.
1149 mono_gc_is_finalizer_thread (MonoThread
*thread
)
1151 return mono_gc_is_finalizer_internal_thread (thread
->internal_thread
);
1154 #if defined(__MACH__)
1155 static pthread_t mach_exception_thread
;
1158 mono_gc_register_mach_exception_thread (pthread_t thread
)
1160 mach_exception_thread
= thread
;
1164 mono_gc_get_mach_exception_thread (void)
1166 return mach_exception_thread
;
1170 static MonoReferenceQueue
*ref_queues
;
1173 ref_list_remove_element (RefQueueEntry
**prev
, RefQueueEntry
*element
)
1176 /* Guard if head is changed concurrently. */
1177 while (*prev
!= element
)
1178 prev
= &(*prev
)->next
;
1179 } while (prev
&& mono_atomic_cas_ptr ((volatile gpointer
*)prev
, element
->next
, element
) != element
);
1183 ref_list_push (RefQueueEntry
**head
, RefQueueEntry
*value
)
1185 RefQueueEntry
*current
;
1188 value
->next
= current
;
1189 STORE_STORE_FENCE
; /*Must make sure the previous store is visible before the CAS. */
1190 } while (mono_atomic_cas_ptr ((volatile gpointer
*)head
, value
, current
) != current
);
1194 reference_queue_proccess (MonoReferenceQueue
*queue
)
1196 RefQueueEntry
**iter
= &queue
->queue
;
1197 RefQueueEntry
*entry
;
1198 while ((entry
= *iter
)) {
1199 if (queue
->should_be_deleted
|| !mono_gchandle_get_target_internal (entry
->gchandle
)) {
1200 mono_gchandle_free_internal ((guint32
)entry
->gchandle
);
1201 ref_list_remove_element (iter
, entry
);
1202 queue
->callback (entry
->user_data
);
1205 iter
= &entry
->next
;
1211 reference_queue_proccess_all (void)
1213 MonoReferenceQueue
**iter
;
1214 MonoReferenceQueue
*queue
= ref_queues
;
1215 for (; queue
; queue
= queue
->next
)
1216 reference_queue_proccess (queue
);
1219 mono_coop_mutex_lock (&reference_queue_mutex
);
1220 for (iter
= &ref_queues
; *iter
;) {
1222 if (!queue
->should_be_deleted
) {
1223 iter
= &queue
->next
;
1227 mono_coop_mutex_unlock (&reference_queue_mutex
);
1228 reference_queue_proccess (queue
);
1231 *iter
= queue
->next
;
1234 mono_coop_mutex_unlock (&reference_queue_mutex
);
1238 mono_reference_queue_cleanup (void)
1240 MonoReferenceQueue
*queue
= ref_queues
;
1241 for (; queue
; queue
= queue
->next
)
1242 queue
->should_be_deleted
= TRUE
;
1243 reference_queue_proccess_all ();
1247 reference_queue_clear_for_domain (MonoDomain
*domain
)
1249 MonoReferenceQueue
*queue
= ref_queues
;
1250 for (; queue
; queue
= queue
->next
) {
1251 RefQueueEntry
**iter
= &queue
->queue
;
1252 RefQueueEntry
*entry
;
1253 while ((entry
= *iter
)) {
1254 if (entry
->domain
== domain
) {
1255 mono_gchandle_free_internal ((guint32
)entry
->gchandle
);
1256 ref_list_remove_element (iter
, entry
);
1257 queue
->callback (entry
->user_data
);
1260 iter
= &entry
->next
;
1266 * mono_gc_reference_queue_new:
1267 * \param callback callback used when processing collected entries.
1269 * Create a new reference queue used to process collected objects.
1270 * A reference queue let you add a pair of (managed object, user data)
1271 * using the \c mono_gc_reference_queue_add method.
1273 * Once the managed object is collected \p callback will be called
1274 * in the finalizer thread with 'user data' as argument.
1276 * The callback is called from the finalizer thread without any locks held.
1277 * When an AppDomain is unloaded, all callbacks for objects belonging to it
1280 * \returns the new queue.
1283 mono_gc_reference_queue_new (mono_reference_queue_callback callback
)
1285 MONO_EXTERNAL_ONLY_GC_UNSAFE (MonoReferenceQueue
*, mono_gc_reference_queue_new_internal (callback
));
1289 mono_gc_reference_queue_new_internal (mono_reference_queue_callback callback
)
1291 MonoReferenceQueue
*res
= g_new0 (MonoReferenceQueue
, 1);
1292 res
->callback
= callback
;
1294 mono_lazy_initialize (&reference_queue_mutex_inited
, reference_queue_mutex_init
);
1295 mono_coop_mutex_lock (&reference_queue_mutex
);
1296 res
->next
= ref_queues
;
1298 mono_coop_mutex_unlock (&reference_queue_mutex
);
1304 * mono_gc_reference_queue_add:
1305 * \param queue the queue to add the reference to.
1306 * \param obj the object to be watched for collection
1307 * \param user_data parameter to be passed to the queue callback
1309 * Queue an object to be watched for collection, when the \p obj is
1310 * collected, the callback that was registered for the \p queue will
1311 * be invoked with \p user_data as argument.
1313 * \returns FALSE if the queue is scheduled to be freed.
1316 mono_gc_reference_queue_add (MonoReferenceQueue
*queue
, MonoObject
*obj
, void *user_data
)
1318 MONO_EXTERNAL_ONLY_GC_UNSAFE (gboolean
, mono_gc_reference_queue_add_internal (queue
, obj
, user_data
));
1322 mono_gc_reference_queue_add_internal (MonoReferenceQueue
*queue
, MonoObject
*obj
, void *user_data
)
1324 RefQueueEntry
*entry
;
1325 if (queue
->should_be_deleted
)
1328 g_assert (obj
!= NULL
);
1330 entry
= g_new0 (RefQueueEntry
, 1);
1331 entry
->user_data
= user_data
;
1332 entry
->domain
= mono_object_domain (obj
);
1334 entry
->gchandle
= mono_gchandle_new_weakref_internal (obj
, TRUE
);
1335 #ifndef HAVE_SGEN_GC
1336 mono_object_register_finalizer (obj
);
1339 ref_list_push (&queue
->queue
, entry
);
1344 * mono_gc_reference_queue_free:
1345 * \param queue the queue that should be freed.
1347 * This operation signals that \p queue should be freed. This operation is deferred
1348 * as it happens on the finalizer thread.
1350 * After this call, no further objects can be queued. It's the responsibility of the
1351 * caller to make sure that no further attempt to access queue will be made.
1354 mono_gc_reference_queue_free (MonoReferenceQueue
*queue
)
1356 queue
->should_be_deleted
= TRUE
;
1360 mono_gc_alloc_handle_pinned_obj (MonoVTable
*vtable
, gsize size
)
1362 return MONO_HANDLE_NEW (MonoObject
, mono_gc_alloc_pinned_obj (vtable
, size
));
1366 mono_gc_alloc_handle_obj (MonoVTable
*vtable
, gsize size
)
1368 return MONO_HANDLE_NEW (MonoObject
, mono_gc_alloc_obj (vtable
, size
));
1372 mono_gc_alloc_handle_vector (MonoVTable
*vtable
, gsize size
, gsize max_length
)
1374 return MONO_HANDLE_NEW (MonoArray
, mono_gc_alloc_vector (vtable
, size
, max_length
));
1378 mono_gc_alloc_handle_array (MonoVTable
*vtable
, gsize size
, gsize max_length
, gsize bounds_size
)
1380 return MONO_HANDLE_NEW (MonoArray
, mono_gc_alloc_array (vtable
, size
, max_length
, bounds_size
));
1384 mono_gc_alloc_handle_string (MonoVTable
*vtable
, gsize size
, gint32 len
)
1386 return MONO_HANDLE_NEW (MonoString
, mono_gc_alloc_string (vtable
, size
, len
));
1390 mono_gc_alloc_handle_mature (MonoVTable
*vtable
, gsize size
)
1392 return MONO_HANDLE_NEW (MonoObject
, mono_gc_alloc_mature (vtable
, size
));
1396 mono_gc_register_object_with_weak_fields (MonoObjectHandle obj
)
1398 mono_gc_register_obj_with_weak_fields (MONO_HANDLE_RAW (obj
));
1402 * mono_gc_wbarrier_object_copy_handle:
1404 * Write barrier to call when \p obj is the result of a clone or copy of an object.
1407 mono_gc_wbarrier_object_copy_handle (MonoObjectHandle obj
, MonoObjectHandle src
)
1409 mono_gc_wbarrier_object_copy_internal (MONO_HANDLE_RAW (obj
), MONO_HANDLE_RAW (src
));