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"
57 #pragma warning(disable:4312) // FIXME pointer cast to different size
60 typedef struct DomainFinalizationReq
{
64 } DomainFinalizationReq
;
66 static gboolean gc_disabled
;
68 static gboolean finalizing_root_domain
;
70 gboolean mono_log_finalizers
;
71 gboolean mono_do_not_finalize
;
72 static volatile gboolean suspend_finalizers
;
73 gchar
**mono_do_not_finalize_class_names
;
75 #define mono_finalizer_lock() mono_coop_mutex_lock (&finalizer_mutex)
76 #define mono_finalizer_unlock() mono_coop_mutex_unlock (&finalizer_mutex)
77 static MonoCoopMutex finalizer_mutex
;
78 static MonoCoopMutex reference_queue_mutex
;
79 static mono_lazy_init_t reference_queue_mutex_inited
= MONO_LAZY_INIT_STATUS_NOT_INITIALIZED
;
81 static GSList
*domains_to_finalize
;
83 static gboolean finalizer_thread_exited
;
84 /* Uses finalizer_mutex */
85 static MonoCoopCond exited_cond
;
87 static MonoInternalThread
*gc_thread
;
90 static HANDLE pending_done_event
;
92 static gboolean pending_done
;
93 static MonoCoopCond pending_done_cond
;
94 static MonoCoopMutex pending_done_mutex
;
97 static void object_register_finalizer (MonoObject
*obj
, void (*callback
)(void *, void*));
99 static void reference_queue_proccess_all (void);
100 static void mono_reference_queue_cleanup (void);
101 static void reference_queue_clear_for_domain (MonoDomain
*domain
);
102 static void mono_runtime_do_background_work (void);
104 static MonoThreadInfoWaitRet
105 guarded_wait (MonoThreadHandle
*thread_handle
, guint32 timeout
, gboolean alertable
)
107 MonoThreadInfoWaitRet result
;
110 result
= mono_thread_info_wait_one_handle (thread_handle
, timeout
, alertable
);
118 MonoCoopMutex
*mutex
;
119 } BreakCoopAlertableWaitUD
;
122 break_coop_alertable_wait (gpointer user_data
)
124 BreakCoopAlertableWaitUD
*ud
= (BreakCoopAlertableWaitUD
*)user_data
;
126 mono_coop_mutex_lock (ud
->mutex
);
127 mono_coop_cond_signal (ud
->cond
);
128 mono_coop_mutex_unlock (ud
->mutex
);
134 * coop_cond_timedwait_alertable:
136 * Wait on COND/MUTEX. If ALERTABLE is non-null, the wait can be interrupted.
137 * In that case, *ALERTABLE will be set to TRUE, and 0 is returned.
140 coop_cond_timedwait_alertable (MonoCoopCond
*cond
, MonoCoopMutex
*mutex
, guint32 timeout_ms
, gboolean
*alertable
)
142 BreakCoopAlertableWaitUD
*ud
;
146 ud
= g_new0 (BreakCoopAlertableWaitUD
, 1);
150 mono_thread_info_install_interrupt (break_coop_alertable_wait
, ud
, alertable
);
156 res
= mono_coop_cond_timedwait (cond
, mutex
, timeout_ms
);
158 mono_thread_info_uninstall_interrupt (alertable
);
162 /* the interrupt token has not been taken by another
163 * thread, so it's our responsability to free it up. */
171 * actually, we might want to queue the finalize requests in a separate thread,
172 * but we need to be careful about the execution domain of the thread...
175 mono_gc_run_finalize (void *obj
, void *data
)
178 MonoObject
*exc
= NULL
;
183 MonoMethod
* finalizer
= NULL
;
184 MonoDomain
*caller_domain
= mono_domain_get ();
187 // This function is called from the innards of the GC, so our best alternative for now is to do polling here
188 mono_threads_safepoint ();
190 o
= (MonoObject
*)((char*)obj
+ GPOINTER_TO_UINT (data
));
192 const char *o_ns
= m_class_get_name_space (mono_object_class (o
));
193 const char *o_name
= m_class_get_name (mono_object_class (o
));
195 if (mono_do_not_finalize
) {
196 if (!mono_do_not_finalize_class_names
)
199 size_t namespace_len
= strlen (o_ns
);
200 for (int i
= 0; mono_do_not_finalize_class_names
[i
]; ++i
) {
201 const char *name
= mono_do_not_finalize_class_names
[i
];
202 if (strncmp (name
, o_ns
, namespace_len
))
204 if (name
[namespace_len
] != '.')
206 if (strcmp (name
+ namespace_len
+ 1, o_name
))
212 if (mono_log_finalizers
)
213 g_log ("mono-gc-finalizers", G_LOG_LEVEL_DEBUG
, "<%s at %p> Starting finalizer checks.", o_name
, o
);
215 if (suspend_finalizers
)
218 domain
= o
->vtable
->domain
;
221 mono_domain_finalizers_lock (domain
);
223 o2
= (MonoObject
*)g_hash_table_lookup (domain
->finalizable_objects_hash
, o
);
225 mono_domain_finalizers_unlock (domain
);
228 /* Already finalized somehow */
232 /* make sure the finalizer is not called again if the object is resurrected */
233 object_register_finalizer ((MonoObject
*)obj
, NULL
);
235 if (mono_log_finalizers
)
236 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE
, "<%s at %p> Registered finalizer as processed.", o_name
, o
);
238 if (o
->vtable
->klass
== mono_defaults
.internal_thread_class
) {
239 MonoInternalThread
*t
= (MonoInternalThread
*)o
;
241 if (mono_gc_is_finalizer_internal_thread (t
))
242 /* Avoid finalizing ourselves */
246 if (m_class_get_image (mono_object_class (o
)) == mono_defaults
.corlib
&& !strcmp (o_name
, "DynamicMethod") && finalizing_root_domain
) {
248 * These can't be finalized during unloading/shutdown, since that would
249 * free the native code which can still be referenced by other
251 * FIXME: This is not perfect, objects dying at the same time as
252 * dynamic methods can still reference them even when !shutdown.
257 if (mono_runtime_get_no_exec ())
260 /* speedup later... and use a timeout */
261 /* g_print ("Finalize run on %p %s.%s\n", o, mono_object_class (o)->name_space, mono_object_class (o)->name); */
263 /* Use _internal here, since this thread can enter a doomed appdomain */
264 mono_domain_set_internal_with_options (mono_object_domain (o
), TRUE
);
266 /* delegates that have a native function pointer allocated are
267 * registered for finalization, but they don't have a Finalize
268 * method, because in most cases it's not needed and it's just a waste.
270 if (m_class_is_delegate (mono_object_class (o
))) {
271 MonoDelegate
* del
= (MonoDelegate
*)o
;
272 if (del
->delegate_trampoline
)
273 mono_delegate_free_ftnptr ((MonoDelegate
*)o
);
274 mono_domain_set_internal_with_options (caller_domain
, TRUE
);
278 finalizer
= mono_class_get_finalizer (o
->vtable
->klass
);
280 /* If object has a CCW but has no finalizer, it was only
281 * registered for finalization in order to free the CCW.
282 * Else it needs the regular finalizer run.
283 * FIXME: what to do about ressurection and suppression
284 * of finalizer on object with CCW.
286 if (mono_marshal_free_ccw (o
) && !finalizer
) {
287 mono_domain_set_internal_with_options (caller_domain
, TRUE
);
292 * To avoid the locking plus the other overhead of mono_runtime_invoke_checked (),
293 * create and precompile a wrapper which calls the finalize method using
296 if (mono_log_finalizers
)
297 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE
, "<%s at %p> Compiling finalizer.", o_name
, o
);
300 if (!domain
->finalize_runtime_invoke
) {
301 MonoMethod
*finalize_method
= mono_class_get_method_from_name_checked (mono_defaults
.object_class
, "Finalize", 0, 0, error
);
302 mono_error_assert_ok (error
);
303 MonoMethod
*invoke
= mono_marshal_get_runtime_invoke (finalize_method
, TRUE
);
305 domain
->finalize_runtime_invoke
= mono_compile_method_checked (invoke
, error
);
306 mono_error_assert_ok (error
); /* expect this not to fail */
309 RuntimeInvokeFunction runtime_invoke
= (RuntimeInvokeFunction
)domain
->finalize_runtime_invoke
;
312 mono_runtime_class_init_full (o
->vtable
, error
);
313 goto_if_nok (error
, unhandled_error
);
315 if (G_UNLIKELY (MONO_GC_FINALIZE_INVOKE_ENABLED ())) {
316 MONO_GC_FINALIZE_INVOKE ((unsigned long)o
, mono_object_get_size_internal (o
),
320 if (mono_log_finalizers
)
321 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE
, "<%s at %p> Calling finalizer.", o_name
, o
);
323 MONO_PROFILER_RAISE (gc_finalizing_object
, (o
));
326 if (finalizer
) { // null finalizers work fine when using the vcall invoke as Object has an empty one
329 mono_runtime_try_invoke (finalizer
, o
, params
, &exc
, error
);
332 runtime_invoke (o
, NULL
, &exc
, NULL
);
335 MONO_PROFILER_RAISE (gc_finalized_object
, (o
));
337 if (mono_log_finalizers
)
338 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE
, "<%s at %p> Returned from finalizer.", o_name
, o
);
342 exc
= (MonoObject
*)mono_error_convert_to_exception (error
);
344 mono_thread_internal_unhandled_exception (exc
);
346 mono_domain_set_internal_with_options (caller_domain
, TRUE
);
350 * Some of our objects may point to a different address than the address returned by GC_malloc()
351 * (because of the GetHashCode hack), but we need to pass the real address to register_finalizer.
352 * This also means that in the callback we need to adjust the pointer to get back the real
354 * We also need to be consistent in the use of the GC_debug* variants of malloc and register_finalizer,
355 * since that, too, can cause the underlying pointer to be offset.
358 object_register_finalizer (MonoObject
*obj
, void (*callback
)(void *, void*))
362 g_assert (obj
!= NULL
);
364 domain
= obj
->vtable
->domain
;
367 if (mono_domain_is_unloading (domain
) && (callback
!= NULL
))
369 * Can't register finalizers in a dying appdomain, since they
370 * could be invoked after the appdomain has been unloaded.
374 mono_domain_finalizers_lock (domain
);
377 g_hash_table_insert (domain
->finalizable_objects_hash
, obj
, obj
);
379 g_hash_table_remove (domain
->finalizable_objects_hash
, obj
);
381 mono_domain_finalizers_unlock (domain
);
383 mono_gc_register_for_finalization (obj
, callback
);
384 #elif defined(HAVE_SGEN_GC)
386 * If we register finalizers for domains that are unloading we might
387 * end up running them while or after the domain is being cleared, so
388 * the objects will not be valid anymore.
390 if (!mono_domain_is_unloading (domain
))
391 mono_gc_register_for_finalization (obj
, callback
);
396 * mono_object_register_finalizer:
397 * \param obj object to register
399 * Records that object \p obj has a finalizer, this will call the
400 * Finalize method when the garbage collector disposes the object.
404 mono_object_register_finalizer_handle (MonoObjectHandle obj
)
406 /* g_print ("Registered finalizer on %p %s.%s\n", obj, mono_handle_class (obj)->name_space, mono_handle_class (obj)->name); */
407 object_register_finalizer (MONO_HANDLE_RAW (obj
), mono_gc_run_finalize
);
411 mono_object_unregister_finalizer_handle (MonoObjectHandle obj
)
413 /* g_print ("Unregistered finalizer on %p %s.%s\n", obj, mono_handle_class (obj)->name_space, mono_handle_class (obj)->name); */
414 object_register_finalizer (MONO_HANDLE_RAW (obj
), NULL
);
418 mono_object_register_finalizer (MonoObject
*obj
)
420 /* g_print ("Registered finalizer on %p %s.%s\n", obj, mono_object_class (obj)->name_space, mono_object_class (obj)->name); */
421 object_register_finalizer (obj
, mono_gc_run_finalize
);
425 * mono_domain_finalize:
426 * \param domain the domain to finalize
427 * \param timeout msecs to wait for the finalization to complete, \c -1 to wait indefinitely
429 * Request finalization of all finalizable objects inside \p domain. Wait
430 * \p timeout msecs for the finalization to complete.
432 * \returns TRUE if succeeded, FALSE if there was a timeout
435 mono_domain_finalize (MonoDomain
*domain
, guint32 timeout
)
437 DomainFinalizationReq
*req
;
438 MonoInternalThread
*thread
= mono_thread_internal_current ();
443 if (mono_thread_internal_current () == gc_thread
)
444 /* We are called from inside a finalizer, not much we can do here */
448 * No need to create another thread 'cause the finalizer thread
449 * is still working and will take care of running the finalizers
455 /* We don't support domain finalization without a GC */
456 if (mono_gc_is_null ())
459 mono_gc_collect (mono_gc_max_generation ());
461 req
= g_new0 (DomainFinalizationReq
, 1);
463 req
->domain
= domain
;
464 mono_coop_sem_init (&req
->done
, 0);
466 if (domain
== mono_get_root_domain ())
467 finalizing_root_domain
= TRUE
;
469 mono_finalizer_lock ();
471 domains_to_finalize
= g_slist_append (domains_to_finalize
, req
);
473 mono_finalizer_unlock ();
475 /* Tell the finalizer thread to finalize this appdomain */
476 mono_gc_finalize_notify ();
479 timeout
= MONO_INFINITE_WAIT
;
480 if (timeout
!= MONO_INFINITE_WAIT
)
481 start
= mono_msec_ticks ();
486 if (timeout
== MONO_INFINITE_WAIT
) {
487 res
= mono_coop_sem_wait (&req
->done
, MONO_SEM_FLAGS_ALERTABLE
);
489 gint64 elapsed
= mono_msec_ticks () - start
;
490 if (elapsed
>= timeout
) {
495 res
= mono_coop_sem_timedwait (&req
->done
, timeout
- elapsed
, MONO_SEM_FLAGS_ALERTABLE
);
498 if (res
== MONO_SEM_TIMEDWAIT_RET_SUCCESS
) {
500 } else if (res
== MONO_SEM_TIMEDWAIT_RET_ALERTED
) {
501 if ((thread
->state
& (ThreadState_AbortRequested
| ThreadState_SuspendRequested
)) != 0) {
505 } else if (res
== MONO_SEM_TIMEDWAIT_RET_TIMEDOUT
) {
509 g_error ("%s: unknown result %d", __func__
, res
);
514 /* Try removing the req from domains_to_finalize:
515 * - if it's not found: the domain is being finalized,
516 * so we the ref count is already decremented
517 * - if it's found: the domain is not yet being finalized,
518 * so we can safely decrement the ref */
522 mono_finalizer_lock ();
524 found
= g_slist_index (domains_to_finalize
, req
) != -1;
526 domains_to_finalize
= g_slist_remove (domains_to_finalize
, req
);
528 mono_finalizer_unlock ();
531 /* We have to decrement it wherever we
532 * remove it from domains_to_finalize */
533 if (mono_atomic_dec_i32 (&req
->ref
) != 1)
534 g_error ("%s: req->ref should be 1, as we are the first one to decrement it", __func__
);
541 if (mono_atomic_dec_i32 (&req
->ref
) == 0) {
542 mono_coop_sem_destroy (&req
->done
);
550 ves_icall_System_GC_InternalCollect (int generation
)
552 mono_gc_collect (generation
);
556 ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection
)
559 mono_gc_collect (mono_gc_max_generation ());
560 return mono_gc_get_used_size ();
564 ves_icall_System_GC_GetGCMemoryInfo (gint64
* high_memory_load_threshold_bytes
,
565 gint64
* memory_load_bytes
,
566 gint64
* total_available_memory_bytes
,
567 gint64
* heap_size_bytes
,
568 gint64
* fragmented_bytes
)
570 mono_gc_get_gcmemoryinfo (high_memory_load_threshold_bytes
, memory_load_bytes
, total_available_memory_bytes
, heap_size_bytes
, fragmented_bytes
);
574 ves_icall_System_GC_ReRegisterForFinalize (MonoObjectHandle obj
, MonoError
*error
)
576 MONO_CHECK_ARG_NULL_HANDLE (obj
,);
578 mono_object_register_finalizer_handle (obj
);
582 ves_icall_System_GC_SuppressFinalize (MonoObjectHandle obj
, MonoError
*error
)
584 MONO_CHECK_ARG_NULL_HANDLE (obj
,);
586 /* delegates have no finalizers, but we register them to deal with the
587 * unmanaged->managed trampoline. We don't let the user suppress it
588 * otherwise we'd leak it.
590 if (m_class_is_delegate (mono_handle_class (obj
)))
593 /* FIXME: Need to handle case where obj has COM Callable Wrapper
594 * generated for it that needs cleaned up, but user wants to suppress
595 * their derived object finalizer. */
597 mono_object_unregister_finalizer_handle (obj
);
601 ves_icall_System_GC_WaitForPendingFinalizers (void)
603 if (mono_gc_is_null ())
606 if (!mono_gc_pending_finalizers ())
609 if (mono_thread_internal_current () == gc_thread
)
610 /* Avoid deadlocks */
614 If the finalizer thread is not live, lets pretend no finalizers are pending since the current thread might
615 be the one responsible for starting it up.
617 if (gc_thread
== NULL
)
621 ResetEvent (pending_done_event
);
622 mono_gc_finalize_notify ();
623 /* g_print ("Waiting for pending finalizers....\n"); */
624 mono_coop_win32_wait_for_single_object_ex (pending_done_event
, INFINITE
, TRUE
);
625 /* g_print ("Done pending....\n"); */
627 gboolean alerted
= FALSE
;
628 mono_coop_mutex_lock (&pending_done_mutex
);
629 pending_done
= FALSE
;
630 mono_gc_finalize_notify ();
631 while (!pending_done
) {
632 coop_cond_timedwait_alertable (&pending_done_cond
, &pending_done_mutex
, MONO_INFINITE_WAIT
, &alerted
);
636 mono_coop_mutex_unlock (&pending_done_mutex
);
641 ves_icall_System_GC_register_ephemeron_array (MonoObjectHandle array
, MonoError
*error
)
643 if (!mono_gc_ephemeron_array_add (MONO_HANDLE_RAW (array
)))
644 mono_error_set_out_of_memory (error
, "");
648 ves_icall_System_GC_get_ephemeron_tombstone (MonoError
*error
)
650 return MONO_HANDLE_NEW (MonoObject
, mono_domain_get ()->ephemeron_tombstone
);
655 ves_icall_System_GCHandle_GetTarget (MonoGCHandle handle
, MonoError
*error
)
657 return mono_gchandle_get_target_handle (handle
);
661 * if type == -1, change the target of the handle, otherwise allocate a new handle.
664 ves_icall_System_GCHandle_GetTargetHandle (MonoObjectHandle obj
, MonoGCHandle handle
, gint32 type
, MonoError
*error
)
667 mono_gchandle_set_target_handle (handle
, obj
);
668 /* the handle doesn't change */
673 return mono_gchandle_new_weakref_from_handle (obj
);
674 case HANDLE_WEAK_TRACK
:
675 return mono_gchandle_new_weakref_from_handle_track_resurrection (obj
);
677 return mono_gchandle_from_handle (obj
, FALSE
);
679 return mono_gchandle_from_handle (obj
, TRUE
);
681 g_assert_not_reached ();
687 ves_icall_System_GCHandle_FreeHandle (MonoGCHandle handle
)
689 mono_gchandle_free_internal (handle
);
693 ves_icall_System_GCHandle_GetAddrOfPinnedObject (MonoGCHandle handle
)
695 // Handles seem to only be in the way here, and the object is pinned.
698 guint32 gch
= MONO_GC_HANDLE_TO_UINT (handle
);
700 if (MONO_GC_HANDLE_TYPE (gch
) != HANDLE_PINNED
)
703 obj
= mono_gchandle_get_target_internal (handle
);
705 MonoClass
*klass
= mono_object_class (obj
);
707 // FIXME This would be a good place for
708 // object->GetAddrOfPinnedObject()
709 // or klass->GetAddrOfPinnedObject(obj);
711 if (klass
== mono_defaults
.string_class
) {
712 return mono_string_chars_internal ((MonoString
*)obj
);
713 } else if (m_class_get_rank (klass
)) {
714 return mono_array_addr_internal ((MonoArray
*)obj
, char, 0);
716 /* the C# code will check and throw the exception */
717 /* FIXME: missing !klass->blittable test, see bug #61134 */
718 if (mono_class_is_auto_layout (klass
))
720 return mono_object_get_data (obj
);
727 ves_icall_System_GCHandle_CheckCurrentDomain (MonoGCHandle gchandle
)
729 return mono_gchandle_is_in_domain (gchandle
, mono_domain_get ());
733 static MonoCoopSem finalizer_sem
;
734 static volatile gboolean finished
;
737 * mono_gc_finalize_notify:
739 * Notify the finalizer thread that finalizers etc.
740 * are available to be processed.
741 * This is async signal safe.
744 mono_gc_finalize_notify (void)
747 g_message ( "%s: prodding finalizer", __func__
);
750 if (mono_gc_is_null ())
754 mono_threads_schedule_background_job (mono_runtime_do_background_work
);
756 mono_coop_sem_post (&finalizer_sem
);
761 This is the number of entries allowed in the hazard free queue before
762 we explicitly cycle the finalizer thread to trigger pumping the queue.
764 It was picked empirically by running the corlib test suite in a stress
765 scenario where all hazard entries are queued.
767 In this extreme scenario we double the number of times we cycle the finalizer
768 thread compared to just GC calls.
770 Entries are usually in the order of 100's of bytes each, so we're limiting
771 floating garbage to be in the order of a dozen kb.
773 static gboolean finalizer_thread_pulsed
;
774 #define HAZARD_QUEUE_OVERFLOW_SIZE 20
777 hazard_free_queue_is_too_big (size_t size
)
779 if (size
< HAZARD_QUEUE_OVERFLOW_SIZE
)
782 if (finalizer_thread_pulsed
|| mono_atomic_cas_i32 (&finalizer_thread_pulsed
, TRUE
, FALSE
))
785 mono_gc_finalize_notify ();
789 hazard_free_queue_pump (void)
791 mono_thread_hazardous_try_free_all ();
792 finalizer_thread_pulsed
= FALSE
;
798 collect_objects (gpointer key
, gpointer value
, gpointer user_data
)
800 GPtrArray
*arr
= (GPtrArray
*)user_data
;
801 g_ptr_array_add (arr
, key
);
807 * finalize_domain_objects:
809 * Run the finalizers of all finalizable objects in req->domain.
812 finalize_domain_objects (void)
814 DomainFinalizationReq
*req
= NULL
;
817 if (UnlockedReadPointer ((gpointer
volatile*)&domains_to_finalize
)) {
818 mono_finalizer_lock ();
819 if (domains_to_finalize
) {
820 req
= (DomainFinalizationReq
*)domains_to_finalize
->data
;
821 domains_to_finalize
= g_slist_remove (domains_to_finalize
, req
);
823 mono_finalizer_unlock ();
829 domain
= req
->domain
;
831 /* Process finalizers which are already in the queue */
832 mono_gc_invoke_finalizers ();
835 while (g_hash_table_size (domain
->finalizable_objects_hash
) > 0) {
839 * Since the domain is unloading, nobody is allowed to put
840 * new entries into the hash table. But finalize_object might
841 * remove entries from the hash table, so we make a copy.
843 objs
= g_ptr_array_new ();
844 g_hash_table_foreach (domain
->finalizable_objects_hash
, collect_objects
, objs
);
845 /* printf ("FINALIZING %d OBJECTS.\n", objs->len); */
847 for (i
= 0; i
< objs
->len
; ++i
) {
848 MonoObject
*o
= (MonoObject
*)g_ptr_array_index (objs
, i
);
849 /* FIXME: Avoid finalizing threads, etc */
850 mono_gc_run_finalize (o
, 0);
853 g_ptr_array_free (objs
, TRUE
);
855 #elif defined(HAVE_SGEN_GC)
856 mono_gc_finalize_domain (domain
);
857 mono_gc_invoke_finalizers ();
860 /* cleanup the reference queue */
861 reference_queue_clear_for_domain (domain
);
863 /* printf ("DONE.\n"); */
864 mono_coop_sem_post (&req
->done
);
866 if (mono_atomic_dec_i32 (&req
->ref
) == 0) {
867 /* mono_domain_finalize already returned, and
868 * doesn't hold a reference to req anymore. */
869 mono_coop_sem_destroy (&req
->done
);
876 mono_runtime_do_background_work (void)
878 mono_threads_perform_thread_dump ();
880 mono_console_handle_async_ops ();
882 mono_attach_maybe_start ();
884 finalize_domain_objects ();
886 MONO_PROFILER_RAISE (gc_finalizing
, ());
888 /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup),
889 * before the domain is unloaded.
891 mono_gc_invoke_finalizers ();
893 MONO_PROFILER_RAISE (gc_finalized
, ());
895 mono_threads_join_threads ();
897 reference_queue_proccess_all ();
899 mono_w32process_signal_finished ();
901 hazard_free_queue_pump ();
905 finalizer_thread (gpointer unused
)
907 gboolean wait
= TRUE
;
909 mono_thread_set_name_constant_ignore_error (mono_thread_internal_current (), "Finalizer", MonoSetThreadNameFlag_None
);
911 /* Register a hazard free queue pump callback */
912 mono_hazard_pointer_install_free_queue_size_callback (hazard_free_queue_is_too_big
);
915 /* Wait to be notified that there's at least one
919 g_assert (mono_domain_get () == mono_get_root_domain ());
920 mono_thread_info_set_flags (MONO_THREAD_INFO_FLAGS_NO_GC
);
923 /* An alertable wait is required so this thread can be suspended on windows */
924 mono_coop_sem_wait (&finalizer_sem
, MONO_SEM_FLAGS_ALERTABLE
);
928 mono_thread_info_set_flags (MONO_THREAD_INFO_FLAGS_NONE
);
930 mono_runtime_do_background_work ();
932 /* Avoid posting the pending done event until there are pending finalizers */
933 if (mono_coop_sem_timedwait (&finalizer_sem
, 0, MONO_SEM_FLAGS_NONE
) == MONO_SEM_TIMEDWAIT_RET_SUCCESS
) {
934 /* Don't wait again at the start of the loop */
938 SetEvent (pending_done_event
);
940 mono_coop_mutex_lock (&pending_done_mutex
);
942 mono_coop_cond_signal (&pending_done_cond
);
943 mono_coop_mutex_unlock (&pending_done_mutex
);
948 mono_finalizer_lock ();
949 finalizer_thread_exited
= TRUE
;
950 mono_coop_cond_signal (&exited_cond
);
951 mono_finalizer_unlock ();
957 init_finalizer_thread (void)
960 gc_thread
= mono_thread_create_internal (mono_domain_get (), (gpointer
)finalizer_thread
, NULL
, MONO_THREAD_CREATE_FLAGS_NONE
, error
);
961 mono_error_assert_ok (error
);
965 * mono_gc_init_finalizer_thread:
967 * If the runtime is compiled with --with-lazy-gc-thread-creation, this
968 * function must be called by embedders to create the finalizer. Otherwise, the
969 * function does nothing and the runtime creates the finalizer thread
973 mono_gc_init_finalizer_thread (void)
975 #ifndef LAZY_GC_THREAD_CREATION
978 init_finalizer_thread ();
983 reference_queue_mutex_init (void)
985 mono_coop_mutex_init_recursive (&reference_queue_mutex
);
991 mono_lazy_initialize (&reference_queue_mutex_inited
, reference_queue_mutex_init
);
992 mono_coop_mutex_init_recursive (&finalizer_mutex
);
994 mono_counters_register ("Minor GC collections", MONO_COUNTER_GC
| MONO_COUNTER_INT
, &mono_gc_stats
.minor_gc_count
);
995 mono_counters_register ("Major GC collections", MONO_COUNTER_GC
| MONO_COUNTER_INT
, &mono_gc_stats
.major_gc_count
);
996 mono_counters_register ("Minor GC time", MONO_COUNTER_GC
| MONO_COUNTER_ULONG
| MONO_COUNTER_TIME
, &mono_gc_stats
.minor_gc_time
);
997 mono_counters_register ("Major GC time", MONO_COUNTER_GC
| MONO_COUNTER_LONG
| MONO_COUNTER_TIME
, &mono_gc_stats
.major_gc_time
);
998 mono_counters_register ("Major GC time concurrent", MONO_COUNTER_GC
| MONO_COUNTER_LONG
| MONO_COUNTER_TIME
, &mono_gc_stats
.major_gc_time_concurrent
);
1000 mono_gc_base_init ();
1002 if (mono_gc_is_disabled ()) {
1008 pending_done_event
= CreateEvent (NULL
, TRUE
, FALSE
, NULL
);
1009 g_assert (pending_done_event
);
1011 mono_coop_cond_init (&pending_done_cond
);
1012 mono_coop_mutex_init (&pending_done_mutex
);
1015 mono_coop_cond_init (&exited_cond
);
1016 mono_coop_sem_init (&finalizer_sem
, 0);
1018 #ifndef LAZY_GC_THREAD_CREATION
1019 if (!mono_runtime_get_no_exec ())
1020 init_finalizer_thread ();
1025 mono_gc_cleanup (void)
1028 g_message ("%s: cleaning up finalizer", __func__
);
1031 if (mono_gc_is_null ())
1036 if (mono_thread_internal_current () != gc_thread
) {
1039 const gint64 timeout
= 40 * 1000;
1041 mono_gc_finalize_notify ();
1043 start
= mono_msec_ticks ();
1045 /* Finishing the finalizer thread, so wait a little bit... */
1046 /* MS seems to wait for about 2 seconds per finalizer thread */
1047 /* and 40 seconds for all finalizers to finish */
1051 if (finalizer_thread_exited
) {
1052 /* Wait for the thread to actually exit. We don't want the wait
1053 * to be alertable, because we assert on the result to be SUCCESS_0 */
1054 ret
= guarded_wait (gc_thread
->handle
, MONO_INFINITE_WAIT
, FALSE
);
1055 g_assert (ret
== MONO_THREAD_INFO_WAIT_RET_SUCCESS_0
);
1057 mono_threads_add_joinable_thread ((gpointer
)(gsize
)MONO_UINT_TO_NATIVE_THREAD_ID (gc_thread
->tid
));
1061 elapsed
= mono_msec_ticks () - start
;
1062 if (elapsed
>= timeout
) {
1065 /* Set a flag which the finalizer thread can check */
1066 suspend_finalizers
= TRUE
;
1067 mono_gc_suspend_finalizers ();
1069 /* Try to abort the thread, in the hope that it is running managed code */
1070 mono_thread_internal_abort (gc_thread
, FALSE
);
1072 /* Wait for it to stop */
1073 ret
= guarded_wait (gc_thread
->handle
, 100, FALSE
);
1074 if (ret
== MONO_THREAD_INFO_WAIT_RET_TIMEOUT
) {
1075 /* The finalizer thread refused to exit, suspend it forever. */
1076 mono_thread_internal_suspend_for_shutdown (gc_thread
);
1080 g_assert (ret
== MONO_THREAD_INFO_WAIT_RET_SUCCESS_0
);
1082 mono_threads_add_joinable_thread ((gpointer
)(MONO_UINT_TO_NATIVE_THREAD_ID (gc_thread
->tid
)));
1086 mono_finalizer_lock ();
1087 if (!finalizer_thread_exited
)
1088 mono_coop_cond_timedwait (&exited_cond
, &finalizer_mutex
, timeout
- elapsed
);
1089 mono_finalizer_unlock ();
1093 mono_gc_base_cleanup ();
1096 mono_reference_queue_cleanup ();
1098 mono_coop_mutex_destroy (&finalizer_mutex
);
1099 mono_coop_mutex_destroy (&reference_queue_mutex
);
1103 mono_gc_is_finalizer_internal_thread (MonoInternalThread
*thread
)
1105 return thread
== gc_thread
;
1109 * mono_gc_is_finalizer_thread:
1110 * \param thread the thread to test.
1112 * In Mono objects are finalized asynchronously on a separate thread.
1113 * This routine tests whether the \p thread argument represents the
1114 * finalization thread.
1116 * \returns TRUE if \p thread is the finalization thread.
1119 mono_gc_is_finalizer_thread (MonoThread
*thread
)
1121 return mono_gc_is_finalizer_internal_thread (thread
->internal_thread
);
1124 #if defined(__MACH__)
1125 static pthread_t mach_exception_thread
;
1128 mono_gc_register_mach_exception_thread (pthread_t thread
)
1130 mach_exception_thread
= thread
;
1134 mono_gc_get_mach_exception_thread (void)
1136 return mach_exception_thread
;
1140 static MonoReferenceQueue
*ref_queues
;
1143 ref_list_remove_element (RefQueueEntry
**prev
, RefQueueEntry
*element
)
1146 /* Guard if head is changed concurrently. */
1147 while (*prev
!= element
)
1148 prev
= &(*prev
)->next
;
1149 } while (prev
&& mono_atomic_cas_ptr ((volatile gpointer
*)prev
, element
->next
, element
) != element
);
1153 ref_list_push (RefQueueEntry
**head
, RefQueueEntry
*value
)
1155 RefQueueEntry
*current
;
1158 value
->next
= current
;
1159 STORE_STORE_FENCE
; /*Must make sure the previous store is visible before the CAS. */
1160 } while (mono_atomic_cas_ptr ((volatile gpointer
*)head
, value
, current
) != current
);
1164 reference_queue_proccess (MonoReferenceQueue
*queue
)
1166 RefQueueEntry
**iter
= &queue
->queue
;
1167 RefQueueEntry
*entry
;
1168 while ((entry
= *iter
)) {
1169 if (queue
->should_be_deleted
|| !mono_gchandle_get_target_internal (entry
->gchandle
)) {
1170 mono_gchandle_free_internal (entry
->gchandle
);
1171 ref_list_remove_element (iter
, entry
);
1172 queue
->callback (entry
->user_data
);
1175 iter
= &entry
->next
;
1181 reference_queue_proccess_all (void)
1183 MonoReferenceQueue
**iter
;
1184 MonoReferenceQueue
*queue
= ref_queues
;
1185 for (; queue
; queue
= queue
->next
)
1186 reference_queue_proccess (queue
);
1189 mono_coop_mutex_lock (&reference_queue_mutex
);
1190 for (iter
= &ref_queues
; *iter
;) {
1192 if (!queue
->should_be_deleted
) {
1193 iter
= &queue
->next
;
1197 mono_coop_mutex_unlock (&reference_queue_mutex
);
1198 reference_queue_proccess (queue
);
1201 *iter
= queue
->next
;
1204 mono_coop_mutex_unlock (&reference_queue_mutex
);
1208 mono_reference_queue_cleanup (void)
1210 MonoReferenceQueue
*queue
= ref_queues
;
1211 for (; queue
; queue
= queue
->next
)
1212 queue
->should_be_deleted
= TRUE
;
1213 reference_queue_proccess_all ();
1217 reference_queue_clear_for_domain (MonoDomain
*domain
)
1219 MonoReferenceQueue
*queue
= ref_queues
;
1220 for (; queue
; queue
= queue
->next
) {
1221 RefQueueEntry
**iter
= &queue
->queue
;
1222 RefQueueEntry
*entry
;
1223 while ((entry
= *iter
)) {
1224 if (entry
->domain
== domain
) {
1225 mono_gchandle_free_internal (entry
->gchandle
);
1226 ref_list_remove_element (iter
, entry
);
1227 queue
->callback (entry
->user_data
);
1230 iter
= &entry
->next
;
1236 * mono_gc_reference_queue_new:
1237 * \param callback callback used when processing collected entries.
1239 * Create a new reference queue used to process collected objects.
1240 * A reference queue let you add a pair of (managed object, user data)
1241 * using the \c mono_gc_reference_queue_add method.
1243 * Once the managed object is collected \p callback will be called
1244 * in the finalizer thread with 'user data' as argument.
1246 * The callback is called from the finalizer thread without any locks held.
1247 * When an AppDomain is unloaded, all callbacks for objects belonging to it
1250 * \returns the new queue.
1253 mono_gc_reference_queue_new (mono_reference_queue_callback callback
)
1255 MONO_EXTERNAL_ONLY_GC_UNSAFE (MonoReferenceQueue
*, mono_gc_reference_queue_new_internal (callback
));
1259 mono_gc_reference_queue_new_internal (mono_reference_queue_callback callback
)
1261 MonoReferenceQueue
*res
= g_new0 (MonoReferenceQueue
, 1);
1262 res
->callback
= callback
;
1264 mono_lazy_initialize (&reference_queue_mutex_inited
, reference_queue_mutex_init
);
1265 mono_coop_mutex_lock (&reference_queue_mutex
);
1266 res
->next
= ref_queues
;
1268 mono_coop_mutex_unlock (&reference_queue_mutex
);
1274 * mono_gc_reference_queue_add:
1275 * \param queue the queue to add the reference to.
1276 * \param obj the object to be watched for collection
1277 * \param user_data parameter to be passed to the queue callback
1279 * Queue an object to be watched for collection, when the \p obj is
1280 * collected, the callback that was registered for the \p queue will
1281 * be invoked with \p user_data as argument.
1283 * \returns FALSE if the queue is scheduled to be freed.
1286 mono_gc_reference_queue_add (MonoReferenceQueue
*queue
, MonoObject
*obj
, void *user_data
)
1288 MONO_EXTERNAL_ONLY_GC_UNSAFE (gboolean
, mono_gc_reference_queue_add_internal (queue
, obj
, user_data
));
1292 mono_gc_reference_queue_add_internal (MonoReferenceQueue
*queue
, MonoObject
*obj
, void *user_data
)
1294 RefQueueEntry
*entry
;
1295 if (queue
->should_be_deleted
)
1298 g_assert (obj
!= NULL
);
1300 entry
= g_new0 (RefQueueEntry
, 1);
1301 entry
->user_data
= user_data
;
1302 entry
->domain
= mono_object_domain (obj
);
1304 entry
->gchandle
= mono_gchandle_new_weakref_internal (obj
, TRUE
);
1305 #ifndef HAVE_SGEN_GC
1306 mono_object_register_finalizer (obj
);
1309 ref_list_push (&queue
->queue
, entry
);
1314 * mono_gc_reference_queue_free:
1315 * \param queue the queue that should be freed.
1317 * This operation signals that \p queue should be freed. This operation is deferred
1318 * as it happens on the finalizer thread.
1320 * After this call, no further objects can be queued. It's the responsibility of the
1321 * caller to make sure that no further attempt to access queue will be made.
1324 mono_gc_reference_queue_free (MonoReferenceQueue
*queue
)
1326 queue
->should_be_deleted
= TRUE
;
1330 mono_gc_alloc_handle_pinned_obj (MonoVTable
*vtable
, gsize size
)
1332 return MONO_HANDLE_NEW (MonoObject
, mono_gc_alloc_pinned_obj (vtable
, size
));
1336 mono_gc_alloc_handle_obj (MonoVTable
*vtable
, gsize size
)
1338 return MONO_HANDLE_NEW (MonoObject
, mono_gc_alloc_obj (vtable
, size
));
1342 mono_gc_alloc_handle_vector (MonoVTable
*vtable
, gsize size
, gsize max_length
)
1344 return MONO_HANDLE_NEW (MonoArray
, mono_gc_alloc_vector (vtable
, size
, max_length
));
1348 mono_gc_alloc_handle_array (MonoVTable
*vtable
, gsize size
, gsize max_length
, gsize bounds_size
)
1350 return MONO_HANDLE_NEW (MonoArray
, mono_gc_alloc_array (vtable
, size
, max_length
, bounds_size
));
1354 mono_gc_alloc_handle_string (MonoVTable
*vtable
, gsize size
, gint32 len
)
1356 return MONO_HANDLE_NEW (MonoString
, mono_gc_alloc_string (vtable
, size
, len
));
1360 mono_gc_alloc_handle_mature (MonoVTable
*vtable
, gsize size
)
1362 return MONO_HANDLE_NEW (MonoObject
, mono_gc_alloc_mature (vtable
, size
));
1366 mono_gc_register_object_with_weak_fields (MonoObjectHandle obj
)
1368 mono_gc_register_obj_with_weak_fields (MONO_HANDLE_RAW (obj
));
1372 * mono_gc_wbarrier_object_copy_handle:
1374 * Write barrier to call when \p obj is the result of a clone or copy of an object.
1377 mono_gc_wbarrier_object_copy_handle (MonoObjectHandle obj
, MonoObjectHandle src
)
1379 mono_gc_wbarrier_object_copy_internal (MONO_HANDLE_RAW (obj
), MONO_HANDLE_RAW (src
));