mono_thread_set_name_internal change unreadable boolean parameters to enum flags...
[mono-project.git] / mono / metadata / gc.c
blob09b588b30fb132717e3755c300d5237ede3a75d8
1 /**
2 * \file
3 * GC icalls.
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.
13 #include <config.h>
14 #include <glib.h>
15 #include <string.h>
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>
50 #ifndef HOST_WIN32
51 #include <pthread.h>
52 #endif
53 #include "external-only.h"
54 #include "icall-decl.h"
56 typedef struct DomainFinalizationReq {
57 gint32 ref;
58 MonoDomain *domain;
59 MonoCoopSem done;
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;
85 #ifdef TARGET_WIN32
86 static HANDLE pending_done_event;
87 #else
88 static gboolean pending_done;
89 static MonoCoopCond pending_done_cond;
90 static MonoCoopMutex pending_done_mutex;
91 #endif
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;
105 MONO_ENTER_GC_SAFE;
106 result = mono_thread_info_wait_one_handle (thread_handle, timeout, alertable);
107 MONO_EXIT_GC_SAFE;
109 return result;
112 typedef struct {
113 MonoCoopCond *cond;
114 MonoCoopMutex *mutex;
115 } BreakCoopAlertableWaitUD;
117 static inline void
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);
126 g_free (ud);
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.
135 static inline gint
136 coop_cond_timedwait_alertable (MonoCoopCond *cond, MonoCoopMutex *mutex, guint32 timeout_ms, gboolean *alertable)
138 BreakCoopAlertableWaitUD *ud;
139 int res;
141 if (alertable) {
142 ud = g_new0 (BreakCoopAlertableWaitUD, 1);
143 ud->cond = cond;
144 ud->mutex = mutex;
146 mono_thread_info_install_interrupt (break_coop_alertable_wait, ud, alertable);
147 if (*alertable) {
148 g_free (ud);
149 return 0;
152 res = mono_coop_cond_timedwait (cond, mutex, timeout_ms);
153 if (alertable) {
154 mono_thread_info_uninstall_interrupt (alertable);
155 if (*alertable)
156 return 0;
157 else {
158 /* the interrupt token has not been taken by another
159 * thread, so it's our responsability to free it up. */
160 g_free (ud);
163 return res;
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...
170 void
171 mono_gc_run_finalize (void *obj, void *data)
173 ERROR_DECL (error);
174 MonoObject *exc = NULL;
175 MonoObject *o;
176 #ifndef HAVE_SGEN_GC
177 MonoObject *o2;
178 #endif
179 MonoMethod* finalizer = NULL;
180 MonoDomain *caller_domain = mono_domain_get ();
181 MonoDomain *domain;
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)
193 return;
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))
199 break;
200 if (name [namespace_len] != '.')
201 break;
202 if (strcmp (name + namespace_len + 1, o_name))
203 break;
204 return;
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)
212 return;
214 domain = o->vtable->domain;
216 #ifndef HAVE_SGEN_GC
217 mono_domain_finalizers_lock (domain);
219 o2 = (MonoObject *)g_hash_table_lookup (domain->finalizable_objects_hash, o);
221 mono_domain_finalizers_unlock (domain);
223 if (!o2)
224 /* Already finalized somehow */
225 return;
226 #endif
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 */
239 return;
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
246 * finalizers.
247 * FIXME: This is not perfect, objects dying at the same time as
248 * dynamic methods can still reference them even when !shutdown.
250 return;
253 if (mono_runtime_get_no_exec ())
254 return;
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_with_options (mono_object_domain (o), TRUE);
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_with_options (caller_domain, TRUE);
271 return;
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_with_options (caller_domain, TRUE);
284 return;
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
290 * a CALLVIRT.
292 if (mono_log_finalizers)
293 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Compiling finalizer.", o_name, o);
295 #ifndef HOST_WASM
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;
306 #endif
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),
313 o_ns, o_name);
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));
321 #ifdef HOST_WASM
322 if (finalizer) { // null finalizers work fine when using the vcall invoke as Object has an empty one
323 gpointer params [1];
324 params [0] = NULL;
325 mono_runtime_try_invoke (finalizer, o, params, &exc, error);
327 #else
328 runtime_invoke (o, NULL, &exc, NULL);
329 #endif
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);
336 unhandled_error:
337 if (!is_ok (error))
338 exc = (MonoObject*)mono_error_convert_to_exception (error);
339 if (exc)
340 mono_thread_internal_unhandled_exception (exc);
342 mono_domain_set_internal_with_options (caller_domain, TRUE);
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
349 * MonoObject*.
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.
353 static void
354 object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*))
356 MonoDomain *domain;
358 g_assert (obj != NULL);
360 domain = obj->vtable->domain;
362 #if HAVE_BOEHM_GC
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.
368 return;
370 mono_domain_finalizers_lock (domain);
372 if (callback)
373 g_hash_table_insert (domain->finalizable_objects_hash, obj, obj);
374 else
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);
388 #endif
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.
399 void
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);
406 static void
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);
413 void
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
430 gboolean
431 mono_domain_finalize (MonoDomain *domain, guint32 timeout)
433 DomainFinalizationReq *req;
434 MonoInternalThread *thread = mono_thread_internal_current ();
435 gint res;
436 gboolean ret;
437 gint64 start;
439 if (mono_thread_internal_current () == gc_thread)
440 /* We are called from inside a finalizer, not much we can do here */
441 return FALSE;
444 * No need to create another thread 'cause the finalizer thread
445 * is still working and will take care of running the finalizers
448 if (gc_disabled)
449 return TRUE;
451 /* We don't support domain finalization without a GC */
452 if (mono_gc_is_null ())
453 return FALSE;
455 mono_gc_collect (mono_gc_max_generation ());
457 req = g_new0 (DomainFinalizationReq, 1);
458 req->ref = 2;
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 ();
474 if (timeout == -1)
475 timeout = MONO_INFINITE_WAIT;
476 if (timeout != MONO_INFINITE_WAIT)
477 start = mono_msec_ticks ();
479 ret = TRUE;
481 for (;;) {
482 if (timeout == MONO_INFINITE_WAIT) {
483 res = mono_coop_sem_wait (&req->done, MONO_SEM_FLAGS_ALERTABLE);
484 } else {
485 gint64 elapsed = mono_msec_ticks () - start;
486 if (elapsed >= timeout) {
487 ret = FALSE;
488 break;
491 res = mono_coop_sem_timedwait (&req->done, timeout - elapsed, MONO_SEM_FLAGS_ALERTABLE);
494 if (res == MONO_SEM_TIMEDWAIT_RET_SUCCESS) {
495 break;
496 } else if (res == MONO_SEM_TIMEDWAIT_RET_ALERTED) {
497 if ((thread->state & (ThreadState_AbortRequested | ThreadState_SuspendRequested)) != 0) {
498 ret = FALSE;
499 break;
501 } else if (res == MONO_SEM_TIMEDWAIT_RET_TIMEDOUT) {
502 ret = FALSE;
503 break;
504 } else {
505 g_error ("%s: unknown result %d", __func__, res);
509 if (!ret) {
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 */
516 gboolean found;
518 mono_finalizer_lock ();
520 found = g_slist_index (domains_to_finalize, req) != -1;
521 if (found)
522 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
524 mono_finalizer_unlock ();
526 if (found) {
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__);
533 goto done;
536 done:
537 if (mono_atomic_dec_i32 (&req->ref) == 0) {
538 mono_coop_sem_destroy (&req->done);
539 g_free (req);
542 return ret;
545 void
546 ves_icall_System_GC_InternalCollect (int generation)
548 mono_gc_collect (generation);
551 gint64
552 ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection)
554 if (forceCollection)
555 mono_gc_collect (mono_gc_max_generation ());
556 return mono_gc_get_used_size ();
559 void
560 ves_icall_System_GC_KeepAlive (MonoObjectHandle obj, MonoError *error)
563 * Does nothing.
567 void
568 ves_icall_System_GC_ReRegisterForFinalize (MonoObjectHandle obj, MonoError *error)
570 MONO_CHECK_ARG_NULL_HANDLE (obj,);
572 mono_object_register_finalizer_handle (obj);
575 void
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)))
585 return;
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);
594 void
595 ves_icall_System_GC_WaitForPendingFinalizers (void)
597 if (mono_gc_is_null ())
598 return;
600 if (!mono_gc_pending_finalizers ())
601 return;
603 if (mono_thread_internal_current () == gc_thread)
604 /* Avoid deadlocks */
605 return;
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)
612 return;
614 #ifdef TARGET_WIN32
615 ResetEvent (pending_done_event);
616 mono_gc_finalize_notify ();
617 /* g_print ("Waiting for pending finalizers....\n"); */
618 mono_coop_win32_wait_for_single_object_ex (pending_done_event, INFINITE, TRUE);
619 /* g_print ("Done pending....\n"); */
620 #else
621 gboolean alerted = FALSE;
622 mono_coop_mutex_lock (&pending_done_mutex);
623 pending_done = FALSE;
624 mono_gc_finalize_notify ();
625 while (!pending_done) {
626 coop_cond_timedwait_alertable (&pending_done_cond, &pending_done_mutex, MONO_INFINITE_WAIT, &alerted);
627 if (alerted)
628 break;
630 mono_coop_mutex_unlock (&pending_done_mutex);
631 #endif
634 void
635 ves_icall_System_GC_register_ephemeron_array (MonoObjectHandle array, MonoError *error)
637 if (!mono_gc_ephemeron_array_add (MONO_HANDLE_RAW (array)))
638 mono_error_set_out_of_memory (error, "");
641 MonoObjectHandle
642 ves_icall_System_GC_get_ephemeron_tombstone (MonoError *error)
644 return MONO_HANDLE_NEW (MonoObject, mono_domain_get ()->ephemeron_tombstone);
647 #if ENABLE_NETCORE
649 gpointer
650 ves_icall_System_GCHandle_InternalAlloc (MonoObjectHandle obj, gint32 type, MonoError *error)
652 guint32 handle = 0;
654 switch (type) {
655 case HANDLE_WEAK:
656 handle = mono_gchandle_new_weakref_from_handle (obj);
657 break;
658 case HANDLE_WEAK_TRACK:
659 handle = mono_gchandle_new_weakref_from_handle_track_resurrection (obj);
660 break;
661 case HANDLE_NORMAL:
662 handle = mono_gchandle_from_handle (obj, FALSE);
663 break;
664 case HANDLE_PINNED:
665 handle = mono_gchandle_from_handle (obj, TRUE);
666 break;
667 default:
668 g_assert_not_reached ();
670 /* The lowest bit is used to mark pinned handles by netcore's GCHandle class */
671 return GUINT_TO_POINTER (handle << 1);
674 void
675 ves_icall_System_GCHandle_InternalFree (gpointer handle, MonoError *error)
677 mono_gchandle_free_internal (GPOINTER_TO_UINT (handle) >> 1);
680 MonoObjectHandle
681 ves_icall_System_GCHandle_InternalGet (gpointer handle, MonoError *error)
683 return mono_gchandle_get_target_handle (GPOINTER_TO_UINT (handle) >> 1);
686 void
687 ves_icall_System_GCHandle_InternalSet (gpointer handle, MonoObjectHandle obj, MonoError *error)
689 mono_gchandle_set_target_handle (GPOINTER_TO_UINT (handle) >> 1, obj);
692 #else
694 MonoObjectHandle
695 ves_icall_System_GCHandle_GetTarget (guint32 handle, MonoError *error)
697 return mono_gchandle_get_target_handle (handle);
701 * if type == -1, change the target of the handle, otherwise allocate a new handle.
703 guint32
704 ves_icall_System_GCHandle_GetTargetHandle (MonoObjectHandle obj, guint32 handle, gint32 type, MonoError *error)
706 if (type == -1) {
707 mono_gchandle_set_target_handle (handle, obj);
708 /* the handle doesn't change */
709 return handle;
711 switch (type) {
712 case HANDLE_WEAK:
713 return mono_gchandle_new_weakref_from_handle (obj);
714 case HANDLE_WEAK_TRACK:
715 return mono_gchandle_new_weakref_from_handle_track_resurrection (obj);
716 case HANDLE_NORMAL:
717 return mono_gchandle_from_handle (obj, FALSE);
718 case HANDLE_PINNED:
719 return mono_gchandle_from_handle (obj, TRUE);
720 default:
721 g_assert_not_reached ();
723 return 0;
726 void
727 ves_icall_System_GCHandle_FreeHandle (guint32 handle)
729 mono_gchandle_free_internal (handle);
732 gpointer
733 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
735 // Handles seem to only be in the way here, and the object is pinned.
737 MonoObject *obj;
739 if (MONO_GC_HANDLE_TYPE (handle) != HANDLE_PINNED)
740 return (gpointer)-2;
741 obj = mono_gchandle_get_target_internal (handle);
742 if (obj) {
743 MonoClass *klass = mono_object_class (obj);
745 // FIXME This would be a good place for
746 // object->GetAddrOfPinnedObject()
747 // or klass->GetAddrOfPinnedObject(obj);
749 if (klass == mono_defaults.string_class) {
750 return mono_string_chars_internal ((MonoString*)obj);
751 } else if (m_class_get_rank (klass)) {
752 return mono_array_addr_internal ((MonoArray*)obj, char, 0);
753 } else {
754 /* the C# code will check and throw the exception */
755 /* FIXME: missing !klass->blittable test, see bug #61134 */
756 if (mono_class_is_auto_layout (klass))
757 return (gpointer)-1;
758 return mono_object_get_data (obj);
761 return NULL;
764 MonoBoolean
765 ves_icall_System_GCHandle_CheckCurrentDomain (guint32 gchandle)
767 return mono_gchandle_is_in_domain (gchandle, mono_domain_get ());
770 #endif
772 static MonoCoopSem finalizer_sem;
773 static volatile gboolean finished;
776 * mono_gc_finalize_notify:
778 * Notify the finalizer thread that finalizers etc.
779 * are available to be processed.
780 * This is async signal safe.
782 void
783 mono_gc_finalize_notify (void)
785 #ifdef DEBUG
786 g_message ( "%s: prodding finalizer", __func__);
787 #endif
789 if (mono_gc_is_null ())
790 return;
792 #ifdef HOST_WASM
793 mono_threads_schedule_background_job (mono_runtime_do_background_work);
794 #else
795 mono_coop_sem_post (&finalizer_sem);
796 #endif
800 This is the number of entries allowed in the hazard free queue before
801 we explicitly cycle the finalizer thread to trigger pumping the queue.
803 It was picked empirically by running the corlib test suite in a stress
804 scenario where all hazard entries are queued.
806 In this extreme scenario we double the number of times we cycle the finalizer
807 thread compared to just GC calls.
809 Entries are usually in the order of 100's of bytes each, so we're limiting
810 floating garbage to be in the order of a dozen kb.
812 static gboolean finalizer_thread_pulsed;
813 #define HAZARD_QUEUE_OVERFLOW_SIZE 20
815 static void
816 hazard_free_queue_is_too_big (size_t size)
818 if (size < HAZARD_QUEUE_OVERFLOW_SIZE)
819 return;
821 if (finalizer_thread_pulsed || mono_atomic_cas_i32 (&finalizer_thread_pulsed, TRUE, FALSE))
822 return;
824 mono_gc_finalize_notify ();
827 static void
828 hazard_free_queue_pump (void)
830 mono_thread_hazardous_try_free_all ();
831 finalizer_thread_pulsed = FALSE;
834 #ifdef HAVE_BOEHM_GC
836 static void
837 collect_objects (gpointer key, gpointer value, gpointer user_data)
839 GPtrArray *arr = (GPtrArray*)user_data;
840 g_ptr_array_add (arr, key);
843 #endif
846 * finalize_domain_objects:
848 * Run the finalizers of all finalizable objects in req->domain.
850 static void
851 finalize_domain_objects (void)
853 DomainFinalizationReq *req = NULL;
854 MonoDomain *domain;
856 if (UnlockedReadPointer ((gpointer volatile*)&domains_to_finalize)) {
857 mono_finalizer_lock ();
858 if (domains_to_finalize) {
859 req = (DomainFinalizationReq *)domains_to_finalize->data;
860 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
862 mono_finalizer_unlock ();
865 if (!req)
866 return;
868 domain = req->domain;
870 /* Process finalizers which are already in the queue */
871 mono_gc_invoke_finalizers ();
873 #ifdef HAVE_BOEHM_GC
874 while (g_hash_table_size (domain->finalizable_objects_hash) > 0) {
875 int i;
876 GPtrArray *objs;
878 * Since the domain is unloading, nobody is allowed to put
879 * new entries into the hash table. But finalize_object might
880 * remove entries from the hash table, so we make a copy.
882 objs = g_ptr_array_new ();
883 g_hash_table_foreach (domain->finalizable_objects_hash, collect_objects, objs);
884 /* printf ("FINALIZING %d OBJECTS.\n", objs->len); */
886 for (i = 0; i < objs->len; ++i) {
887 MonoObject *o = (MonoObject*)g_ptr_array_index (objs, i);
888 /* FIXME: Avoid finalizing threads, etc */
889 mono_gc_run_finalize (o, 0);
892 g_ptr_array_free (objs, TRUE);
894 #elif defined(HAVE_SGEN_GC)
895 mono_gc_finalize_domain (domain);
896 mono_gc_invoke_finalizers ();
897 #endif
899 /* cleanup the reference queue */
900 reference_queue_clear_for_domain (domain);
902 /* printf ("DONE.\n"); */
903 mono_coop_sem_post (&req->done);
905 if (mono_atomic_dec_i32 (&req->ref) == 0) {
906 /* mono_domain_finalize already returned, and
907 * doesn't hold a reference to req anymore. */
908 mono_coop_sem_destroy (&req->done);
909 g_free (req);
914 static void
915 mono_runtime_do_background_work (void)
917 mono_threads_perform_thread_dump ();
919 mono_console_handle_async_ops ();
921 mono_attach_maybe_start ();
923 finalize_domain_objects ();
925 MONO_PROFILER_RAISE (gc_finalizing, ());
927 /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup),
928 * before the domain is unloaded.
930 mono_gc_invoke_finalizers ();
932 MONO_PROFILER_RAISE (gc_finalized, ());
934 mono_threads_join_threads ();
936 reference_queue_proccess_all ();
938 mono_w32process_signal_finished ();
940 hazard_free_queue_pump ();
943 static gsize WINAPI
944 finalizer_thread (gpointer unused)
946 ERROR_DECL (error);
947 gboolean wait = TRUE;
949 MonoString *finalizer = mono_string_new_checked (mono_get_root_domain (), "Finalizer", error);
950 mono_error_assert_ok (error);
951 mono_thread_set_name_internal (mono_thread_internal_current (), finalizer, MonoSetThreadNameFlag_None, error);
952 mono_error_assert_ok (error);
954 /* Register a hazard free queue pump callback */
955 mono_hazard_pointer_install_free_queue_size_callback (hazard_free_queue_is_too_big);
957 while (!finished) {
958 /* Wait to be notified that there's at least one
959 * finaliser to run
962 g_assert (mono_domain_get () == mono_get_root_domain ());
963 mono_thread_info_set_flags (MONO_THREAD_INFO_FLAGS_NO_GC);
965 if (wait) {
966 /* An alertable wait is required so this thread can be suspended on windows */
967 mono_coop_sem_wait (&finalizer_sem, MONO_SEM_FLAGS_ALERTABLE);
969 wait = TRUE;
971 mono_thread_info_set_flags (MONO_THREAD_INFO_FLAGS_NONE);
973 mono_runtime_do_background_work ();
975 /* Avoid posting the pending done event until there are pending finalizers */
976 if (mono_coop_sem_timedwait (&finalizer_sem, 0, MONO_SEM_FLAGS_NONE) == MONO_SEM_TIMEDWAIT_RET_SUCCESS) {
977 /* Don't wait again at the start of the loop */
978 wait = FALSE;
979 } else {
980 #ifdef TARGET_WIN32
981 SetEvent (pending_done_event);
982 #else
983 mono_coop_mutex_lock (&pending_done_mutex);
984 pending_done = TRUE;
985 mono_coop_cond_signal (&pending_done_cond);
986 mono_coop_mutex_unlock (&pending_done_mutex);
987 #endif
991 mono_finalizer_lock ();
992 finalizer_thread_exited = TRUE;
993 mono_coop_cond_signal (&exited_cond);
994 mono_finalizer_unlock ();
996 return 0;
999 #ifndef LAZY_GC_THREAD_CREATION
1000 static
1001 #endif
1002 void
1003 mono_gc_init_finalizer_thread (void)
1005 ERROR_DECL (error);
1006 gc_thread = mono_thread_create_internal (mono_domain_get (), (gpointer)finalizer_thread, NULL, MONO_THREAD_CREATE_FLAGS_NONE, error);
1007 mono_error_assert_ok (error);
1010 static void
1011 reference_queue_mutex_init (void)
1013 mono_coop_mutex_init_recursive (&reference_queue_mutex);
1016 void
1017 mono_gc_init (void)
1019 mono_lazy_initialize (&reference_queue_mutex_inited, reference_queue_mutex_init);
1020 mono_coop_mutex_init_recursive (&finalizer_mutex);
1022 mono_counters_register ("Minor GC collections", MONO_COUNTER_GC | MONO_COUNTER_INT, &mono_gc_stats.minor_gc_count);
1023 mono_counters_register ("Major GC collections", MONO_COUNTER_GC | MONO_COUNTER_INT, &mono_gc_stats.major_gc_count);
1024 mono_counters_register ("Minor GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &mono_gc_stats.minor_gc_time);
1025 mono_counters_register ("Major GC time", MONO_COUNTER_GC | MONO_COUNTER_LONG | MONO_COUNTER_TIME, &mono_gc_stats.major_gc_time);
1026 mono_counters_register ("Major GC time concurrent", MONO_COUNTER_GC | MONO_COUNTER_LONG | MONO_COUNTER_TIME, &mono_gc_stats.major_gc_time_concurrent);
1028 mono_gc_base_init ();
1030 if (mono_gc_is_disabled ()) {
1031 gc_disabled = TRUE;
1032 return;
1035 #ifdef TARGET_WIN32
1036 pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
1037 g_assert (pending_done_event);
1038 #else
1039 mono_coop_cond_init (&pending_done_cond);
1040 mono_coop_mutex_init (&pending_done_mutex);
1041 #endif
1043 mono_coop_cond_init (&exited_cond);
1044 mono_coop_sem_init (&finalizer_sem, 0);
1046 #ifndef LAZY_GC_THREAD_CREATION
1047 if (!mono_runtime_get_no_exec ())
1048 mono_gc_init_finalizer_thread ();
1049 #endif
1052 void
1053 mono_gc_cleanup (void)
1055 #ifdef DEBUG
1056 g_message ("%s: cleaning up finalizer", __func__);
1057 #endif
1059 if (mono_gc_is_null ())
1060 return;
1062 if (!gc_disabled) {
1063 finished = TRUE;
1064 if (mono_thread_internal_current () != gc_thread) {
1065 int ret;
1066 gint64 start;
1067 const gint64 timeout = 40 * 1000;
1069 mono_gc_finalize_notify ();
1071 start = mono_msec_ticks ();
1073 /* Finishing the finalizer thread, so wait a little bit... */
1074 /* MS seems to wait for about 2 seconds per finalizer thread */
1075 /* and 40 seconds for all finalizers to finish */
1076 for (;;) {
1077 gint64 elapsed;
1079 if (finalizer_thread_exited) {
1080 /* Wait for the thread to actually exit. We don't want the wait
1081 * to be alertable, because we assert on the result to be SUCCESS_0 */
1082 ret = guarded_wait (gc_thread->handle, MONO_INFINITE_WAIT, FALSE);
1083 g_assert (ret == MONO_THREAD_INFO_WAIT_RET_SUCCESS_0);
1085 mono_threads_add_joinable_thread ((gpointer)(MONO_UINT_TO_NATIVE_THREAD_ID (gc_thread->tid)));
1086 break;
1089 elapsed = mono_msec_ticks () - start;
1090 if (elapsed >= timeout) {
1091 /* timeout */
1093 /* Set a flag which the finalizer thread can check */
1094 suspend_finalizers = TRUE;
1095 mono_gc_suspend_finalizers ();
1097 /* Try to abort the thread, in the hope that it is running managed code */
1098 mono_thread_internal_abort (gc_thread, FALSE);
1100 /* Wait for it to stop */
1101 ret = guarded_wait (gc_thread->handle, 100, FALSE);
1102 if (ret == MONO_THREAD_INFO_WAIT_RET_TIMEOUT) {
1103 /* The finalizer thread refused to exit, suspend it forever. */
1104 mono_thread_internal_suspend_for_shutdown (gc_thread);
1105 break;
1108 g_assert (ret == MONO_THREAD_INFO_WAIT_RET_SUCCESS_0);
1110 mono_threads_add_joinable_thread ((gpointer)(MONO_UINT_TO_NATIVE_THREAD_ID (gc_thread->tid)));
1111 break;
1114 mono_finalizer_lock ();
1115 if (!finalizer_thread_exited)
1116 mono_coop_cond_timedwait (&exited_cond, &finalizer_mutex, timeout - elapsed);
1117 mono_finalizer_unlock ();
1120 gc_thread = NULL;
1121 mono_gc_base_cleanup ();
1124 mono_reference_queue_cleanup ();
1126 mono_coop_mutex_destroy (&finalizer_mutex);
1127 mono_coop_mutex_destroy (&reference_queue_mutex);
1130 gboolean
1131 mono_gc_is_finalizer_internal_thread (MonoInternalThread *thread)
1133 return thread == gc_thread;
1137 * mono_gc_is_finalizer_thread:
1138 * \param thread the thread to test.
1140 * In Mono objects are finalized asynchronously on a separate thread.
1141 * This routine tests whether the \p thread argument represents the
1142 * finalization thread.
1144 * \returns TRUE if \p thread is the finalization thread.
1146 gboolean
1147 mono_gc_is_finalizer_thread (MonoThread *thread)
1149 return mono_gc_is_finalizer_internal_thread (thread->internal_thread);
1152 #if defined(__MACH__)
1153 static pthread_t mach_exception_thread;
1155 void
1156 mono_gc_register_mach_exception_thread (pthread_t thread)
1158 mach_exception_thread = thread;
1161 pthread_t
1162 mono_gc_get_mach_exception_thread (void)
1164 return mach_exception_thread;
1166 #endif
1168 static MonoReferenceQueue *ref_queues;
1170 static void
1171 ref_list_remove_element (RefQueueEntry **prev, RefQueueEntry *element)
1173 do {
1174 /* Guard if head is changed concurrently. */
1175 while (*prev != element)
1176 prev = &(*prev)->next;
1177 } while (prev && mono_atomic_cas_ptr ((volatile gpointer *)prev, element->next, element) != element);
1180 static void
1181 ref_list_push (RefQueueEntry **head, RefQueueEntry *value)
1183 RefQueueEntry *current;
1184 do {
1185 current = *head;
1186 value->next = current;
1187 STORE_STORE_FENCE; /*Must make sure the previous store is visible before the CAS. */
1188 } while (mono_atomic_cas_ptr ((volatile gpointer *)head, value, current) != current);
1191 static void
1192 reference_queue_proccess (MonoReferenceQueue *queue)
1194 RefQueueEntry **iter = &queue->queue;
1195 RefQueueEntry *entry;
1196 while ((entry = *iter)) {
1197 if (queue->should_be_deleted || !mono_gchandle_get_target_internal (entry->gchandle)) {
1198 mono_gchandle_free_internal ((guint32)entry->gchandle);
1199 ref_list_remove_element (iter, entry);
1200 queue->callback (entry->user_data);
1201 g_free (entry);
1202 } else {
1203 iter = &entry->next;
1208 static void
1209 reference_queue_proccess_all (void)
1211 MonoReferenceQueue **iter;
1212 MonoReferenceQueue *queue = ref_queues;
1213 for (; queue; queue = queue->next)
1214 reference_queue_proccess (queue);
1216 restart:
1217 mono_coop_mutex_lock (&reference_queue_mutex);
1218 for (iter = &ref_queues; *iter;) {
1219 queue = *iter;
1220 if (!queue->should_be_deleted) {
1221 iter = &queue->next;
1222 continue;
1224 if (queue->queue) {
1225 mono_coop_mutex_unlock (&reference_queue_mutex);
1226 reference_queue_proccess (queue);
1227 goto restart;
1229 *iter = queue->next;
1230 g_free (queue);
1232 mono_coop_mutex_unlock (&reference_queue_mutex);
1235 static void
1236 mono_reference_queue_cleanup (void)
1238 MonoReferenceQueue *queue = ref_queues;
1239 for (; queue; queue = queue->next)
1240 queue->should_be_deleted = TRUE;
1241 reference_queue_proccess_all ();
1244 static void
1245 reference_queue_clear_for_domain (MonoDomain *domain)
1247 MonoReferenceQueue *queue = ref_queues;
1248 for (; queue; queue = queue->next) {
1249 RefQueueEntry **iter = &queue->queue;
1250 RefQueueEntry *entry;
1251 while ((entry = *iter)) {
1252 if (entry->domain == domain) {
1253 mono_gchandle_free_internal ((guint32)entry->gchandle);
1254 ref_list_remove_element (iter, entry);
1255 queue->callback (entry->user_data);
1256 g_free (entry);
1257 } else {
1258 iter = &entry->next;
1264 * mono_gc_reference_queue_new:
1265 * \param callback callback used when processing collected entries.
1267 * Create a new reference queue used to process collected objects.
1268 * A reference queue let you add a pair of (managed object, user data)
1269 * using the \c mono_gc_reference_queue_add method.
1271 * Once the managed object is collected \p callback will be called
1272 * in the finalizer thread with 'user data' as argument.
1274 * The callback is called from the finalizer thread without any locks held.
1275 * When an AppDomain is unloaded, all callbacks for objects belonging to it
1276 * will be invoked.
1278 * \returns the new queue.
1280 MonoReferenceQueue*
1281 mono_gc_reference_queue_new (mono_reference_queue_callback callback)
1283 MONO_EXTERNAL_ONLY_GC_UNSAFE (MonoReferenceQueue*, mono_gc_reference_queue_new_internal (callback));
1286 MonoReferenceQueue*
1287 mono_gc_reference_queue_new_internal (mono_reference_queue_callback callback)
1289 MonoReferenceQueue *res = g_new0 (MonoReferenceQueue, 1);
1290 res->callback = callback;
1292 mono_lazy_initialize (&reference_queue_mutex_inited, reference_queue_mutex_init);
1293 mono_coop_mutex_lock (&reference_queue_mutex);
1294 res->next = ref_queues;
1295 ref_queues = res;
1296 mono_coop_mutex_unlock (&reference_queue_mutex);
1298 return res;
1302 * mono_gc_reference_queue_add:
1303 * \param queue the queue to add the reference to.
1304 * \param obj the object to be watched for collection
1305 * \param user_data parameter to be passed to the queue callback
1307 * Queue an object to be watched for collection, when the \p obj is
1308 * collected, the callback that was registered for the \p queue will
1309 * be invoked with \p user_data as argument.
1311 * \returns FALSE if the queue is scheduled to be freed.
1313 gboolean
1314 mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *user_data)
1316 MONO_EXTERNAL_ONLY_GC_UNSAFE (gboolean, mono_gc_reference_queue_add_internal (queue, obj, user_data));
1319 gboolean
1320 mono_gc_reference_queue_add_internal (MonoReferenceQueue *queue, MonoObject *obj, void *user_data)
1322 RefQueueEntry *entry;
1323 if (queue->should_be_deleted)
1324 return FALSE;
1326 g_assert (obj != NULL);
1328 entry = g_new0 (RefQueueEntry, 1);
1329 entry->user_data = user_data;
1330 entry->domain = mono_object_domain (obj);
1332 entry->gchandle = mono_gchandle_new_weakref_internal (obj, TRUE);
1333 #ifndef HAVE_SGEN_GC
1334 mono_object_register_finalizer (obj);
1335 #endif
1337 ref_list_push (&queue->queue, entry);
1338 return TRUE;
1342 * mono_gc_reference_queue_free:
1343 * \param queue the queue that should be freed.
1345 * This operation signals that \p queue should be freed. This operation is deferred
1346 * as it happens on the finalizer thread.
1348 * After this call, no further objects can be queued. It's the responsibility of the
1349 * caller to make sure that no further attempt to access queue will be made.
1351 void
1352 mono_gc_reference_queue_free (MonoReferenceQueue *queue)
1354 queue->should_be_deleted = TRUE;
1357 MonoObjectHandle
1358 mono_gc_alloc_handle_pinned_obj (MonoVTable *vtable, gsize size)
1360 return MONO_HANDLE_NEW (MonoObject, mono_gc_alloc_pinned_obj (vtable, size));
1363 MonoObjectHandle
1364 mono_gc_alloc_handle_obj (MonoVTable *vtable, gsize size)
1366 return MONO_HANDLE_NEW (MonoObject, mono_gc_alloc_obj (vtable, size));
1369 MonoArrayHandle
1370 mono_gc_alloc_handle_vector (MonoVTable *vtable, gsize size, gsize max_length)
1372 return MONO_HANDLE_NEW (MonoArray, mono_gc_alloc_vector (vtable, size, max_length));
1375 MonoArrayHandle
1376 mono_gc_alloc_handle_array (MonoVTable *vtable, gsize size, gsize max_length, gsize bounds_size)
1378 return MONO_HANDLE_NEW (MonoArray, mono_gc_alloc_array (vtable, size, max_length, bounds_size));
1381 MonoStringHandle
1382 mono_gc_alloc_handle_string (MonoVTable *vtable, gsize size, gint32 len)
1384 return MONO_HANDLE_NEW (MonoString, mono_gc_alloc_string (vtable, size, len));
1387 MonoObjectHandle
1388 mono_gc_alloc_handle_mature (MonoVTable *vtable, gsize size)
1390 return MONO_HANDLE_NEW (MonoObject, mono_gc_alloc_mature (vtable, size));
1393 void
1394 mono_gc_register_object_with_weak_fields (MonoObjectHandle obj)
1396 mono_gc_register_obj_with_weak_fields (MONO_HANDLE_RAW (obj));
1400 * mono_gc_wbarrier_object_copy_handle:
1402 * Write barrier to call when \p obj is the result of a clone or copy of an object.
1404 void
1405 mono_gc_wbarrier_object_copy_handle (MonoObjectHandle obj, MonoObjectHandle src)
1407 mono_gc_wbarrier_object_copy_internal (MONO_HANDLE_RAW (obj), MONO_HANDLE_RAW (src));