update readme (#21797)
[mono-project.git] / mono / metadata / gc.c
blob6914262b4a6e3ccb33403a5f4e85d21c1c1ec278
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 #if _MSC_VER
57 #pragma warning(disable:4312) // FIXME pointer cast to different size
58 #endif
60 typedef struct DomainFinalizationReq {
61 gint32 ref;
62 MonoDomain *domain;
63 MonoCoopSem done;
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;
89 #ifdef TARGET_WIN32
90 static HANDLE pending_done_event;
91 #else
92 static gboolean pending_done;
93 static MonoCoopCond pending_done_cond;
94 static MonoCoopMutex pending_done_mutex;
95 #endif
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;
109 MONO_ENTER_GC_SAFE;
110 result = mono_thread_info_wait_one_handle (thread_handle, timeout, alertable);
111 MONO_EXIT_GC_SAFE;
113 return result;
116 typedef struct {
117 MonoCoopCond *cond;
118 MonoCoopMutex *mutex;
119 } BreakCoopAlertableWaitUD;
121 static void
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);
130 g_free (ud);
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.
139 static gint
140 coop_cond_timedwait_alertable (MonoCoopCond *cond, MonoCoopMutex *mutex, guint32 timeout_ms, gboolean *alertable)
142 BreakCoopAlertableWaitUD *ud;
143 int res;
145 if (alertable) {
146 ud = g_new0 (BreakCoopAlertableWaitUD, 1);
147 ud->cond = cond;
148 ud->mutex = mutex;
150 mono_thread_info_install_interrupt (break_coop_alertable_wait, ud, alertable);
151 if (*alertable) {
152 g_free (ud);
153 return 0;
156 res = mono_coop_cond_timedwait (cond, mutex, timeout_ms);
157 if (alertable) {
158 mono_thread_info_uninstall_interrupt (alertable);
159 if (*alertable)
160 return 0;
161 else {
162 /* the interrupt token has not been taken by another
163 * thread, so it's our responsability to free it up. */
164 g_free (ud);
167 return res;
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...
174 void
175 mono_gc_run_finalize (void *obj, void *data)
177 ERROR_DECL (error);
178 MonoObject *exc = NULL;
179 MonoObject *o;
180 #ifndef HAVE_SGEN_GC
181 MonoObject *o2;
182 #endif
183 MonoMethod* finalizer = NULL;
184 MonoDomain *caller_domain = mono_domain_get ();
185 MonoDomain *domain;
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)
197 return;
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))
203 break;
204 if (name [namespace_len] != '.')
205 break;
206 if (strcmp (name + namespace_len + 1, o_name))
207 break;
208 return;
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)
216 return;
218 domain = o->vtable->domain;
220 #ifndef HAVE_SGEN_GC
221 mono_domain_finalizers_lock (domain);
223 o2 = (MonoObject *)g_hash_table_lookup (domain->finalizable_objects_hash, o);
225 mono_domain_finalizers_unlock (domain);
227 if (!o2)
228 /* Already finalized somehow */
229 return;
230 #endif
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 */
243 return;
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
250 * finalizers.
251 * FIXME: This is not perfect, objects dying at the same time as
252 * dynamic methods can still reference them even when !shutdown.
254 return;
257 if (mono_runtime_get_no_exec ())
258 return;
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);
275 return;
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);
288 return;
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
294 * a CALLVIRT.
296 if (mono_log_finalizers)
297 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Compiling finalizer.", o_name, o);
299 #ifndef HOST_WASM
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;
310 #endif
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),
317 o_ns, o_name);
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));
325 #ifdef HOST_WASM
326 if (finalizer) { // null finalizers work fine when using the vcall invoke as Object has an empty one
327 gpointer params [1];
328 params [0] = NULL;
329 mono_runtime_try_invoke (finalizer, o, params, &exc, error);
331 #else
332 runtime_invoke (o, NULL, &exc, NULL);
333 #endif
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);
340 unhandled_error:
341 if (!is_ok (error))
342 exc = (MonoObject*)mono_error_convert_to_exception (error);
343 if (exc)
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
353 * MonoObject*.
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.
357 static void
358 object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*))
360 MonoDomain *domain;
362 g_assert (obj != NULL);
364 domain = obj->vtable->domain;
366 #if HAVE_BOEHM_GC
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.
372 return;
374 mono_domain_finalizers_lock (domain);
376 if (callback)
377 g_hash_table_insert (domain->finalizable_objects_hash, obj, obj);
378 else
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);
392 #endif
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.
403 void
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);
410 static void
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);
417 void
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
434 gboolean
435 mono_domain_finalize (MonoDomain *domain, guint32 timeout)
437 DomainFinalizationReq *req;
438 MonoInternalThread *thread = mono_thread_internal_current ();
439 gint res;
440 gboolean ret;
441 gint64 start;
443 if (mono_thread_internal_current () == gc_thread)
444 /* We are called from inside a finalizer, not much we can do here */
445 return FALSE;
448 * No need to create another thread 'cause the finalizer thread
449 * is still working and will take care of running the finalizers
452 if (gc_disabled)
453 return TRUE;
455 /* We don't support domain finalization without a GC */
456 if (mono_gc_is_null ())
457 return FALSE;
459 mono_gc_collect (mono_gc_max_generation ());
461 req = g_new0 (DomainFinalizationReq, 1);
462 req->ref = 2;
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 ();
478 if (timeout == -1)
479 timeout = MONO_INFINITE_WAIT;
480 if (timeout != MONO_INFINITE_WAIT)
481 start = mono_msec_ticks ();
483 ret = TRUE;
485 for (;;) {
486 if (timeout == MONO_INFINITE_WAIT) {
487 res = mono_coop_sem_wait (&req->done, MONO_SEM_FLAGS_ALERTABLE);
488 } else {
489 gint64 elapsed = mono_msec_ticks () - start;
490 if (elapsed >= timeout) {
491 ret = FALSE;
492 break;
495 res = mono_coop_sem_timedwait (&req->done, timeout - elapsed, MONO_SEM_FLAGS_ALERTABLE);
498 if (res == MONO_SEM_TIMEDWAIT_RET_SUCCESS) {
499 break;
500 } else if (res == MONO_SEM_TIMEDWAIT_RET_ALERTED) {
501 if ((thread->state & (ThreadState_AbortRequested | ThreadState_SuspendRequested)) != 0) {
502 ret = FALSE;
503 break;
505 } else if (res == MONO_SEM_TIMEDWAIT_RET_TIMEDOUT) {
506 ret = FALSE;
507 break;
508 } else {
509 g_error ("%s: unknown result %d", __func__, res);
513 if (!ret) {
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 */
520 gboolean found;
522 mono_finalizer_lock ();
524 found = g_slist_index (domains_to_finalize, req) != -1;
525 if (found)
526 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
528 mono_finalizer_unlock ();
530 if (found) {
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__);
537 goto done;
540 done:
541 if (mono_atomic_dec_i32 (&req->ref) == 0) {
542 mono_coop_sem_destroy (&req->done);
543 g_free (req);
546 return ret;
549 void
550 ves_icall_System_GC_InternalCollect (int generation)
552 mono_gc_collect (generation);
555 gint64
556 ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection)
558 if (forceCollection)
559 mono_gc_collect (mono_gc_max_generation ());
560 return mono_gc_get_used_size ();
563 void
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);
573 void
574 ves_icall_System_GC_ReRegisterForFinalize (MonoObjectHandle obj, MonoError *error)
576 MONO_CHECK_ARG_NULL_HANDLE (obj,);
578 mono_object_register_finalizer_handle (obj);
581 void
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)))
591 return;
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);
600 void
601 ves_icall_System_GC_WaitForPendingFinalizers (void)
603 if (mono_gc_is_null ())
604 return;
606 if (!mono_gc_pending_finalizers ())
607 return;
609 if (mono_thread_internal_current () == gc_thread)
610 /* Avoid deadlocks */
611 return;
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)
618 return;
620 #ifdef TARGET_WIN32
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"); */
626 #else
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);
633 if (alerted)
634 break;
636 mono_coop_mutex_unlock (&pending_done_mutex);
637 #endif
640 void
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, "");
647 MonoObjectHandle
648 ves_icall_System_GC_get_ephemeron_tombstone (MonoError *error)
650 return MONO_HANDLE_NEW (MonoObject, mono_domain_get ()->ephemeron_tombstone);
654 MonoObjectHandle
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.
663 MonoGCHandle
664 ves_icall_System_GCHandle_GetTargetHandle (MonoObjectHandle obj, MonoGCHandle handle, gint32 type, MonoError *error)
666 if (type == -1) {
667 mono_gchandle_set_target_handle (handle, obj);
668 /* the handle doesn't change */
669 return handle;
671 switch (type) {
672 case HANDLE_WEAK:
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);
676 case HANDLE_NORMAL:
677 return mono_gchandle_from_handle (obj, FALSE);
678 case HANDLE_PINNED:
679 return mono_gchandle_from_handle (obj, TRUE);
680 default:
681 g_assert_not_reached ();
683 return NULL;
686 void
687 ves_icall_System_GCHandle_FreeHandle (MonoGCHandle handle)
689 mono_gchandle_free_internal (handle);
692 gpointer
693 ves_icall_System_GCHandle_GetAddrOfPinnedObject (MonoGCHandle handle)
695 // Handles seem to only be in the way here, and the object is pinned.
697 MonoObject *obj;
698 guint32 gch = MONO_GC_HANDLE_TO_UINT (handle);
700 if (MONO_GC_HANDLE_TYPE (gch) != HANDLE_PINNED)
701 return (gpointer)-2;
703 obj = mono_gchandle_get_target_internal (handle);
704 if (obj) {
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);
715 } else {
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))
719 return (gpointer)-1;
720 return mono_object_get_data (obj);
723 return NULL;
726 MonoBoolean
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.
743 void
744 mono_gc_finalize_notify (void)
746 #ifdef DEBUG
747 g_message ( "%s: prodding finalizer", __func__);
748 #endif
750 if (mono_gc_is_null ())
751 return;
753 #ifdef HOST_WASM
754 mono_threads_schedule_background_job (mono_runtime_do_background_work);
755 #else
756 mono_coop_sem_post (&finalizer_sem);
757 #endif
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
776 static void
777 hazard_free_queue_is_too_big (size_t size)
779 if (size < HAZARD_QUEUE_OVERFLOW_SIZE)
780 return;
782 if (finalizer_thread_pulsed || mono_atomic_cas_i32 (&finalizer_thread_pulsed, TRUE, FALSE))
783 return;
785 mono_gc_finalize_notify ();
788 static void
789 hazard_free_queue_pump (void)
791 mono_thread_hazardous_try_free_all ();
792 finalizer_thread_pulsed = FALSE;
795 #ifdef HAVE_BOEHM_GC
797 static void
798 collect_objects (gpointer key, gpointer value, gpointer user_data)
800 GPtrArray *arr = (GPtrArray*)user_data;
801 g_ptr_array_add (arr, key);
804 #endif
807 * finalize_domain_objects:
809 * Run the finalizers of all finalizable objects in req->domain.
811 static void
812 finalize_domain_objects (void)
814 DomainFinalizationReq *req = NULL;
815 MonoDomain *domain;
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 ();
826 if (!req)
827 return;
829 domain = req->domain;
831 /* Process finalizers which are already in the queue */
832 mono_gc_invoke_finalizers ();
834 #ifdef HAVE_BOEHM_GC
835 while (g_hash_table_size (domain->finalizable_objects_hash) > 0) {
836 int i;
837 GPtrArray *objs;
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 ();
858 #endif
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);
870 g_free (req);
875 static void
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 ();
904 static gsize WINAPI
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);
914 while (!finished) {
915 /* Wait to be notified that there's at least one
916 * finaliser to run
919 g_assert (mono_domain_get () == mono_get_root_domain ());
920 mono_thread_info_set_flags (MONO_THREAD_INFO_FLAGS_NO_GC);
922 if (wait) {
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);
926 wait = TRUE;
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 */
935 wait = FALSE;
936 } else {
937 #ifdef TARGET_WIN32
938 SetEvent (pending_done_event);
939 #else
940 mono_coop_mutex_lock (&pending_done_mutex);
941 pending_done = TRUE;
942 mono_coop_cond_signal (&pending_done_cond);
943 mono_coop_mutex_unlock (&pending_done_mutex);
944 #endif
948 mono_finalizer_lock ();
949 finalizer_thread_exited = TRUE;
950 mono_coop_cond_signal (&exited_cond);
951 mono_finalizer_unlock ();
953 return 0;
956 static void
957 init_finalizer_thread (void)
959 ERROR_DECL (error);
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
970 * automatically.
972 void
973 mono_gc_init_finalizer_thread (void)
975 #ifndef LAZY_GC_THREAD_CREATION
976 /* do nothing */
977 #else
978 init_finalizer_thread ();
979 #endif
982 static void
983 reference_queue_mutex_init (void)
985 mono_coop_mutex_init_recursive (&reference_queue_mutex);
988 void
989 mono_gc_init (void)
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 ()) {
1003 gc_disabled = TRUE;
1004 return;
1007 #ifdef TARGET_WIN32
1008 pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
1009 g_assert (pending_done_event);
1010 #else
1011 mono_coop_cond_init (&pending_done_cond);
1012 mono_coop_mutex_init (&pending_done_mutex);
1013 #endif
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 ();
1021 #endif
1024 void
1025 mono_gc_cleanup (void)
1027 #ifdef DEBUG
1028 g_message ("%s: cleaning up finalizer", __func__);
1029 #endif
1031 if (mono_gc_is_null ())
1032 return;
1034 if (!gc_disabled) {
1035 finished = TRUE;
1036 if (mono_thread_internal_current () != gc_thread) {
1037 int ret;
1038 gint64 start;
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 */
1048 for (;;) {
1049 gint64 elapsed;
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));
1058 break;
1061 elapsed = mono_msec_ticks () - start;
1062 if (elapsed >= timeout) {
1063 /* 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);
1077 break;
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)));
1083 break;
1086 mono_finalizer_lock ();
1087 if (!finalizer_thread_exited)
1088 mono_coop_cond_timedwait (&exited_cond, &finalizer_mutex, timeout - elapsed);
1089 mono_finalizer_unlock ();
1092 gc_thread = NULL;
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);
1102 gboolean
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.
1118 gboolean
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;
1127 void
1128 mono_gc_register_mach_exception_thread (pthread_t thread)
1130 mach_exception_thread = thread;
1133 pthread_t
1134 mono_gc_get_mach_exception_thread (void)
1136 return mach_exception_thread;
1138 #endif
1140 static MonoReferenceQueue *ref_queues;
1142 static void
1143 ref_list_remove_element (RefQueueEntry **prev, RefQueueEntry *element)
1145 do {
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);
1152 static void
1153 ref_list_push (RefQueueEntry **head, RefQueueEntry *value)
1155 RefQueueEntry *current;
1156 do {
1157 current = *head;
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);
1163 static void
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);
1173 g_free (entry);
1174 } else {
1175 iter = &entry->next;
1180 static void
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);
1188 restart:
1189 mono_coop_mutex_lock (&reference_queue_mutex);
1190 for (iter = &ref_queues; *iter;) {
1191 queue = *iter;
1192 if (!queue->should_be_deleted) {
1193 iter = &queue->next;
1194 continue;
1196 if (queue->queue) {
1197 mono_coop_mutex_unlock (&reference_queue_mutex);
1198 reference_queue_proccess (queue);
1199 goto restart;
1201 *iter = queue->next;
1202 g_free (queue);
1204 mono_coop_mutex_unlock (&reference_queue_mutex);
1207 static void
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 ();
1216 static void
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);
1228 g_free (entry);
1229 } else {
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
1248 * will be invoked.
1250 * \returns the new queue.
1252 MonoReferenceQueue*
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));
1258 MonoReferenceQueue*
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;
1267 ref_queues = res;
1268 mono_coop_mutex_unlock (&reference_queue_mutex);
1270 return res;
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.
1285 gboolean
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));
1291 gboolean
1292 mono_gc_reference_queue_add_internal (MonoReferenceQueue *queue, MonoObject *obj, void *user_data)
1294 RefQueueEntry *entry;
1295 if (queue->should_be_deleted)
1296 return FALSE;
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);
1307 #endif
1309 ref_list_push (&queue->queue, entry);
1310 return TRUE;
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.
1323 void
1324 mono_gc_reference_queue_free (MonoReferenceQueue *queue)
1326 queue->should_be_deleted = TRUE;
1329 MonoObjectHandle
1330 mono_gc_alloc_handle_pinned_obj (MonoVTable *vtable, gsize size)
1332 return MONO_HANDLE_NEW (MonoObject, mono_gc_alloc_pinned_obj (vtable, size));
1335 MonoObjectHandle
1336 mono_gc_alloc_handle_obj (MonoVTable *vtable, gsize size)
1338 return MONO_HANDLE_NEW (MonoObject, mono_gc_alloc_obj (vtable, size));
1341 MonoArrayHandle
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));
1347 MonoArrayHandle
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));
1353 MonoStringHandle
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));
1359 MonoObjectHandle
1360 mono_gc_alloc_handle_mature (MonoVTable *vtable, gsize size)
1362 return MONO_HANDLE_NEW (MonoObject, mono_gc_alloc_mature (vtable, size));
1365 void
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.
1376 void
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));