2 * metadata/gc.c: GC icalls.
4 * Author: Paolo Molaro <lupus@ximian.com>
6 * Copyright 2002-2003 Ximian, Inc (http://www.ximian.com)
7 * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
8 * Copyright 2012 Xamarin Inc (http://www.xamarin.com)
15 #include <mono/metadata/gc-internal.h>
16 #include <mono/metadata/mono-gc.h>
17 #include <mono/metadata/threads.h>
18 #include <mono/metadata/tabledefs.h>
19 #include <mono/metadata/exception.h>
20 #include <mono/metadata/profiler-private.h>
21 #include <mono/metadata/domain-internals.h>
22 #include <mono/metadata/class-internals.h>
23 #include <mono/metadata/metadata-internals.h>
24 #include <mono/metadata/mono-mlist.h>
25 #include <mono/metadata/threadpool.h>
26 #include <mono/metadata/threadpool-internals.h>
27 #include <mono/metadata/threads-types.h>
28 #include <mono/sgen/sgen-conf.h>
29 #include <mono/utils/mono-logger-internal.h>
30 #include <mono/metadata/gc-internal.h>
31 #include <mono/metadata/marshal.h> /* for mono_delegate_free_ftnptr () */
32 #include <mono/metadata/attach.h>
33 #include <mono/metadata/console-io.h>
34 #include <mono/utils/mono-semaphore.h>
35 #include <mono/utils/mono-memory-model.h>
36 #include <mono/utils/mono-counters.h>
37 #include <mono/utils/mono-time.h>
38 #include <mono/utils/dtrace.h>
39 #include <mono/utils/mono-threads.h>
40 #include <mono/utils/atomic.h>
46 typedef struct DomainFinalizationReq {
49 } DomainFinalizationReq;
51 static gboolean gc_disabled = FALSE;
53 static gboolean finalizing_root_domain = FALSE;
55 gboolean log_finalizers = FALSE;
56 gboolean do_not_finalize = FALSE;
58 #define mono_finalizer_lock() mono_mutex_lock (&finalizer_mutex)
59 #define mono_finalizer_unlock() mono_mutex_unlock (&finalizer_mutex)
60 static mono_mutex_t finalizer_mutex;
61 static mono_mutex_t reference_queue_mutex;
63 static GSList *domains_to_finalize= NULL;
64 static MonoMList *threads_to_finalize = NULL;
66 static gboolean finalizer_thread_exited;
67 static int num_to_finalize;
69 /* These use finalizer_mutex */
70 /* Used to wait for finalizer_thread_exited to become TRUE */
71 static mono_cond_t exited_cond;
72 /* Used to wait for num_to_finalize to become > 0 */
73 static mono_cond_t finalizer_cond;
75 static MonoInternalThread *gc_thread;
77 static void object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*));
79 static void mono_gchandle_set_target (guint32 gchandle, MonoObject *obj);
81 static void reference_queue_proccess_all (void);
82 static void mono_reference_queue_cleanup (void);
83 static void reference_queue_clear_for_domain (MonoDomain *domain);
84 static HANDLE pending_done_event;
87 guarded_wait (HANDLE handle, guint32 timeout, gboolean alertable)
92 result = WaitForSingleObjectEx (handle, timeout, alertable);
99 add_thread_to_finalize (MonoInternalThread *thread)
101 mono_finalizer_lock ();
102 if (!threads_to_finalize)
103 MONO_GC_REGISTER_ROOT_SINGLE (threads_to_finalize);
104 threads_to_finalize = mono_mlist_append (threads_to_finalize, (MonoObject*)thread);
105 mono_finalizer_unlock ();
108 static gboolean suspend_finalizers = FALSE;
110 * actually, we might want to queue the finalize requests in a separate thread,
111 * but we need to be careful about the execution domain of the thread...
114 mono_gc_run_finalize (void *obj, void *data)
119 MonoObject *exc = NULL;
124 MonoMethod* finalizer = NULL;
125 MonoDomain *caller_domain = mono_domain_get ();
127 RuntimeInvokeFunction runtime_invoke;
129 o = (MonoObject*)((char*)obj + GPOINTER_TO_UINT (data));
132 g_log ("mono-gc-finalizers", G_LOG_LEVEL_DEBUG, "<%s at %p> Starting finalizer checks.", o->vtable->klass->name, o);
134 if (suspend_finalizers)
137 domain = o->vtable->domain;
140 mono_domain_finalizers_lock (domain);
142 o2 = g_hash_table_lookup (domain->finalizable_objects_hash, o);
144 mono_domain_finalizers_unlock (domain);
147 /* Already finalized somehow */
151 /* make sure the finalizer is not called again if the object is resurrected */
152 object_register_finalizer (obj, NULL);
155 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Registered finalizer as processed.", o->vtable->klass->name, o);
157 if (o->vtable->klass == mono_defaults.internal_thread_class) {
158 MonoInternalThread *t = (MonoInternalThread*)o;
160 if (mono_gc_is_finalizer_internal_thread (t))
161 /* Avoid finalizing ourselves */
164 if (t->threadpool_thread && finalizing_root_domain) {
165 /* Don't finalize threadpool threads when
166 shutting down - they're finalized when the
167 threadpool shuts down. */
168 add_thread_to_finalize (t);
173 if (o->vtable->klass->image == mono_defaults.corlib && !strcmp (o->vtable->klass->name, "DynamicMethod") && finalizing_root_domain) {
175 * These can't be finalized during unloading/shutdown, since that would
176 * free the native code which can still be referenced by other
178 * FIXME: This is not perfect, objects dying at the same time as
179 * dynamic methods can still reference them even when !shutdown.
184 if (mono_runtime_get_no_exec ())
187 /* speedup later... and use a timeout */
188 /* g_print ("Finalize run on %p %s.%s\n", o, mono_object_class (o)->name_space, mono_object_class (o)->name); */
190 /* Use _internal here, since this thread can enter a doomed appdomain */
191 mono_domain_set_internal (mono_object_domain (o));
193 /* delegates that have a native function pointer allocated are
194 * registered for finalization, but they don't have a Finalize
195 * method, because in most cases it's not needed and it's just a waste.
197 if (o->vtable->klass->delegate) {
198 MonoDelegate* del = (MonoDelegate*)o;
199 if (del->delegate_trampoline)
200 mono_delegate_free_ftnptr ((MonoDelegate*)o);
201 mono_domain_set_internal (caller_domain);
205 finalizer = mono_class_get_finalizer (o->vtable->klass);
207 /* If object has a CCW but has no finalizer, it was only
208 * registered for finalization in order to free the CCW.
209 * Else it needs the regular finalizer run.
210 * FIXME: what to do about ressurection and suppression
211 * of finalizer on object with CCW.
213 if (mono_marshal_free_ccw (o) && !finalizer) {
214 mono_domain_set_internal (caller_domain);
219 * To avoid the locking plus the other overhead of mono_runtime_invoke (),
220 * create and precompile a wrapper which calls the finalize method using
224 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Compiling finalizer.", o->vtable->klass->name, o);
226 if (!domain->finalize_runtime_invoke) {
227 MonoMethod *invoke = mono_marshal_get_runtime_invoke (mono_class_get_method_from_name_flags (mono_defaults.object_class, "Finalize", 0, 0), TRUE);
229 domain->finalize_runtime_invoke = mono_compile_method (invoke);
232 runtime_invoke = domain->finalize_runtime_invoke;
234 mono_runtime_class_init (o->vtable);
236 if (G_UNLIKELY (MONO_GC_FINALIZE_INVOKE_ENABLED ())) {
237 MONO_GC_FINALIZE_INVOKE ((unsigned long)o, mono_object_get_size (o),
238 o->vtable->klass->name_space, o->vtable->klass->name);
242 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Calling finalizer.", o->vtable->klass->name, o);
244 runtime_invoke (o, NULL, &exc, NULL);
247 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Returned from finalizer.", o->vtable->klass->name, o);
250 mono_internal_thread_unhandled_exception (exc);
252 mono_domain_set_internal (caller_domain);
256 mono_gc_finalize_threadpool_threads (void)
258 while (threads_to_finalize) {
259 MonoInternalThread *thread = (MonoInternalThread*) mono_mlist_get_data (threads_to_finalize);
261 /* Force finalization of the thread. */
262 thread->threadpool_thread = FALSE;
263 mono_object_register_finalizer ((MonoObject*)thread);
265 mono_gc_run_finalize (thread, NULL);
267 threads_to_finalize = mono_mlist_next (threads_to_finalize);
272 mono_gc_out_of_memory (size_t size)
275 * we could allocate at program startup some memory that we could release
276 * back to the system at this point if we're really low on memory (ie, size is
277 * lower than the memory we set apart)
279 mono_raise_exception (mono_domain_get ()->out_of_memory_ex);
285 * Some of our objects may point to a different address than the address returned by GC_malloc()
286 * (because of the GetHashCode hack), but we need to pass the real address to register_finalizer.
287 * This also means that in the callback we need to adjust the pointer to get back the real
289 * We also need to be consistent in the use of the GC_debug* variants of malloc and register_finalizer,
290 * since that, too, can cause the underlying pointer to be offset.
293 object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*))
298 mono_raise_exception (mono_get_exception_argument_null ("obj"));
300 domain = obj->vtable->domain;
303 if (mono_domain_is_unloading (domain) && (callback != NULL))
305 * Can't register finalizers in a dying appdomain, since they
306 * could be invoked after the appdomain has been unloaded.
310 mono_domain_finalizers_lock (domain);
313 g_hash_table_insert (domain->finalizable_objects_hash, obj, obj);
315 g_hash_table_remove (domain->finalizable_objects_hash, obj);
317 mono_domain_finalizers_unlock (domain);
319 mono_gc_register_for_finalization (obj, callback);
320 #elif defined(HAVE_SGEN_GC)
322 * If we register finalizers for domains that are unloading we might
323 * end up running them while or after the domain is being cleared, so
324 * the objects will not be valid anymore.
326 if (!mono_domain_is_unloading (domain))
327 mono_gc_register_for_finalization (obj, callback);
332 * mono_object_register_finalizer:
333 * @obj: object to register
335 * Records that object @obj has a finalizer, this will call the
336 * Finalize method when the garbage collector disposes the object.
340 mono_object_register_finalizer (MonoObject *obj)
342 /* g_print ("Registered finalizer on %p %s.%s\n", obj, mono_object_class (obj)->name_space, mono_object_class (obj)->name); */
343 object_register_finalizer (obj, mono_gc_run_finalize);
347 * mono_domain_finalize:
348 * @domain: the domain to finalize
349 * @timeout: msects to wait for the finalization to complete, -1 to wait indefinitely
351 * Request finalization of all finalizable objects inside @domain. Wait
352 * @timeout msecs for the finalization to complete.
354 * Returns: TRUE if succeeded, FALSE if there was a timeout
358 mono_domain_finalize (MonoDomain *domain, guint32 timeout)
360 DomainFinalizationReq *req;
363 MonoInternalThread *thread = mono_thread_internal_current ();
365 #if defined(__native_client__)
369 if (mono_thread_internal_current () == gc_thread)
370 /* We are called from inside a finalizer, not much we can do here */
374 * No need to create another thread 'cause the finalizer thread
375 * is still working and will take care of running the finalizers
381 /* We don't support domain finalization without a GC */
382 if (mono_gc_is_null ())
385 mono_gc_collect (mono_gc_max_generation ());
387 done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
388 if (done_event == NULL) {
392 req = g_new0 (DomainFinalizationReq, 1);
393 req->domain = domain;
394 req->done_event = done_event;
396 if (domain == mono_get_root_domain ())
397 finalizing_root_domain = TRUE;
399 mono_finalizer_lock ();
401 domains_to_finalize = g_slist_append (domains_to_finalize, req);
403 mono_finalizer_unlock ();
405 /* Tell the finalizer thread to finalize this appdomain */
406 mono_gc_finalize_notify ();
412 res = guarded_wait (done_event, timeout, TRUE);
413 /* printf ("WAIT RES: %d.\n", res); */
415 if (res == WAIT_IO_COMPLETION) {
416 if ((thread->state & (ThreadState_StopRequested | ThreadState_SuspendRequested)) != 0)
418 } else if (res == WAIT_TIMEOUT) {
419 /* We leak the handle here */
426 CloseHandle (done_event);
428 if (domain == mono_get_root_domain ()) {
429 mono_thread_pool_cleanup ();
430 mono_gc_finalize_threadpool_threads ();
437 ves_icall_System_GC_InternalCollect (int generation)
439 mono_gc_collect (generation);
443 ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection)
446 mono_gc_collect (mono_gc_max_generation ());
447 return mono_gc_get_used_size ();
451 ves_icall_System_GC_KeepAlive (MonoObject *obj)
459 ves_icall_System_GC_ReRegisterForFinalize (MonoObject *obj)
461 MONO_CHECK_ARG_NULL (obj,);
463 object_register_finalizer (obj, mono_gc_run_finalize);
467 ves_icall_System_GC_SuppressFinalize (MonoObject *obj)
469 MONO_CHECK_ARG_NULL (obj,);
471 /* delegates have no finalizers, but we register them to deal with the
472 * unmanaged->managed trampoline. We don't let the user suppress it
473 * otherwise we'd leak it.
475 if (obj->vtable->klass->delegate)
478 /* FIXME: Need to handle case where obj has COM Callable Wrapper
479 * generated for it that needs cleaned up, but user wants to suppress
480 * their derived object finalizer. */
482 object_register_finalizer (obj, NULL);
486 ves_icall_System_GC_WaitForPendingFinalizers (void)
488 if (mono_gc_is_null ())
491 if (!mono_gc_pending_finalizers ())
494 if (mono_thread_internal_current () == gc_thread)
495 /* Avoid deadlocks */
499 If the finalizer thread is not live, lets pretend no finalizers are pending since the current thread might
500 be the one responsible for starting it up.
502 if (gc_thread == NULL)
505 /* Can't use cond vars here, since they don't contain the wapi interrupt logic */
507 ResetEvent (pending_done_event);
508 mono_gc_finalize_notify ();
509 /* g_print ("Waiting for pending finalizers....\n"); */
510 guarded_wait (pending_done_event, INFINITE, TRUE);
511 /* g_print ("Done pending....\n"); */
515 ves_icall_System_GC_register_ephemeron_array (MonoObject *array)
518 if (!mono_gc_ephemeron_array_add (array)) {
519 mono_set_pending_exception (mono_object_domain (array)->out_of_memory_ex);
526 ves_icall_System_GC_get_ephemeron_tombstone (void)
528 return mono_domain_get ()->ephemeron_tombstone;
531 #define mono_allocator_lock() mono_mutex_lock (&allocator_section)
532 #define mono_allocator_unlock() mono_mutex_unlock (&allocator_section)
533 static mono_mutex_t allocator_section;
534 static mono_mutex_t handle_section;
543 static HandleType mono_gchandle_get_type (guint32 gchandle);
546 ves_icall_System_GCHandle_GetTarget (guint32 handle)
548 return mono_gchandle_get_target (handle);
552 * if type == -1, change the target of the handle, otherwise allocate a new handle.
555 ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type)
558 mono_gchandle_set_target (handle, obj);
559 /* the handle doesn't change */
564 return mono_gchandle_new_weakref (obj, FALSE);
565 case HANDLE_WEAK_TRACK:
566 return mono_gchandle_new_weakref (obj, TRUE);
568 return mono_gchandle_new (obj, FALSE);
570 return mono_gchandle_new (obj, TRUE);
572 g_assert_not_reached ();
578 ves_icall_System_GCHandle_FreeHandle (guint32 handle)
580 mono_gchandle_free (handle);
584 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
588 if (mono_gchandle_get_type (handle) != HANDLE_PINNED)
590 obj = mono_gchandle_get_target (handle);
592 MonoClass *klass = mono_object_class (obj);
593 if (klass == mono_defaults.string_class) {
594 return mono_string_chars ((MonoString*)obj);
595 } else if (klass->rank) {
596 return mono_array_addr ((MonoArray*)obj, char, 0);
598 /* the C# code will check and throw the exception */
599 /* FIXME: missing !klass->blittable test, see bug #61134 */
600 if ((klass->flags & TYPE_ATTRIBUTE_LAYOUT_MASK) == TYPE_ATTRIBUTE_AUTO_LAYOUT)
602 return (char*)obj + sizeof (MonoObject);
609 ves_icall_Mono_Runtime_SetGCAllowSynchronousMajor (MonoBoolean flag)
611 return mono_gc_set_allow_synchronous_major (flag);
619 guint slot_hint : 24; /* starting slot for search */
620 /* 2^16 appdomains should be enough for everyone (though I know I'll regret this in 20 years) */
621 /* we alloc this only for weak refs, since we can get the domain directly in the other cases */
625 /* weak and weak-track arrays will be allocated in malloc memory
627 static HandleData gc_handles [] = {
628 {NULL, NULL, 0, HANDLE_WEAK, 0},
629 {NULL, NULL, 0, HANDLE_WEAK_TRACK, 0},
630 {NULL, NULL, 0, HANDLE_NORMAL, 0},
631 {NULL, NULL, 0, HANDLE_PINNED, 0}
634 #define lock_handles(handles) mono_mutex_lock (&handle_section)
635 #define unlock_handles(handles) mono_mutex_unlock (&handle_section)
638 find_first_unset (guint32 bitmap)
641 for (i = 0; i < 32; ++i) {
642 if (!(bitmap & (1 << i)))
649 make_root_descr_all_refs (int numbits, gboolean pinned)
655 return mono_gc_make_root_descr_all_refs (numbits);
659 alloc_handle (HandleData *handles, MonoObject *obj, gboolean track)
663 lock_handles (handles);
664 if (!handles->size) {
666 if (handles->type > HANDLE_WEAK_TRACK) {
667 handles->entries = mono_gc_alloc_fixed (sizeof (gpointer) * handles->size, make_root_descr_all_refs (handles->size, handles->type == HANDLE_PINNED));
669 handles->entries = g_malloc0 (sizeof (gpointer) * handles->size);
670 handles->domain_ids = g_malloc0 (sizeof (guint16) * handles->size);
672 handles->bitmap = g_malloc0 (handles->size / 8);
675 for (slot = handles->slot_hint; slot < handles->size / 32; ++slot) {
676 if (handles->bitmap [slot] != 0xffffffff) {
677 i = find_first_unset (handles->bitmap [slot]);
678 handles->slot_hint = slot;
682 if (i == -1 && handles->slot_hint != 0) {
683 for (slot = 0; slot < handles->slot_hint; ++slot) {
684 if (handles->bitmap [slot] != 0xffffffff) {
685 i = find_first_unset (handles->bitmap [slot]);
686 handles->slot_hint = slot;
693 guint32 new_size = handles->size * 2; /* always double: we memset to 0 based on this below */
695 /* resize and copy the bitmap */
696 new_bitmap = g_malloc0 (new_size / 8);
697 memcpy (new_bitmap, handles->bitmap, handles->size / 8);
698 g_free (handles->bitmap);
699 handles->bitmap = new_bitmap;
701 /* resize and copy the entries */
702 if (handles->type > HANDLE_WEAK_TRACK) {
705 entries = mono_gc_alloc_fixed (sizeof (gpointer) * new_size, make_root_descr_all_refs (new_size, handles->type == HANDLE_PINNED));
706 mono_gc_memmove_aligned (entries, handles->entries, sizeof (gpointer) * handles->size);
708 mono_gc_free_fixed (handles->entries);
709 handles->entries = entries;
713 domain_ids = g_malloc0 (sizeof (guint16) * new_size);
714 entries = g_malloc0 (sizeof (gpointer) * new_size);
715 memcpy (domain_ids, handles->domain_ids, sizeof (guint16) * handles->size);
716 for (i = 0; i < handles->size; ++i) {
717 MonoObject *obj = mono_gc_weak_link_get (&(handles->entries [i]));
719 mono_gc_weak_link_add (&(entries [i]), obj, track);
720 mono_gc_weak_link_remove (&(handles->entries [i]), track);
722 g_assert (!handles->entries [i]);
725 g_free (handles->entries);
726 g_free (handles->domain_ids);
727 handles->entries = entries;
728 handles->domain_ids = domain_ids;
731 /* set i and slot to the next free position */
733 slot = (handles->size + 1) / 32;
734 handles->slot_hint = handles->size + 1;
735 handles->size = new_size;
737 handles->bitmap [slot] |= 1 << i;
738 slot = slot * 32 + i;
739 handles->entries [slot] = NULL;
740 if (handles->type <= HANDLE_WEAK_TRACK) {
741 /*FIXME, what to use when obj == null?*/
742 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
744 mono_gc_weak_link_add (&(handles->entries [slot]), obj, track);
746 handles->entries [slot] = obj;
749 #ifndef DISABLE_PERFCOUNTERS
750 mono_perfcounters->gc_num_handles++;
752 unlock_handles (handles);
753 /*g_print ("allocated entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
754 res = (slot << 3) | (handles->type + 1);
755 mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_CREATED, handles->type, res, obj);
761 * @obj: managed object to get a handle for
762 * @pinned: whether the object should be pinned
764 * This returns a handle that wraps the object, this is used to keep a
765 * reference to a managed object from the unmanaged world and preventing the
766 * object from being disposed.
768 * If @pinned is false the address of the object can not be obtained, if it is
769 * true the address of the object can be obtained. This will also pin the
770 * object so it will not be possible by a moving garbage collector to move the
773 * Returns: a handle that can be used to access the object from
777 mono_gchandle_new (MonoObject *obj, gboolean pinned)
779 return alloc_handle (&gc_handles [pinned? HANDLE_PINNED: HANDLE_NORMAL], obj, FALSE);
783 * mono_gchandle_new_weakref:
784 * @obj: managed object to get a handle for
785 * @pinned: whether the object should be pinned
787 * This returns a weak handle that wraps the object, this is used to
788 * keep a reference to a managed object from the unmanaged world.
789 * Unlike the mono_gchandle_new the object can be reclaimed by the
790 * garbage collector. In this case the value of the GCHandle will be
793 * If @pinned is false the address of the object can not be obtained, if it is
794 * true the address of the object can be obtained. This will also pin the
795 * object so it will not be possible by a moving garbage collector to move the
798 * Returns: a handle that can be used to access the object from
802 mono_gchandle_new_weakref (MonoObject *obj, gboolean track_resurrection)
804 guint32 handle = alloc_handle (&gc_handles [track_resurrection? HANDLE_WEAK_TRACK: HANDLE_WEAK], obj, track_resurrection);
810 mono_gchandle_get_type (guint32 gchandle)
812 guint type = (gchandle & 7) - 1;
818 * mono_gchandle_get_target:
819 * @gchandle: a GCHandle's handle.
821 * The handle was previously created by calling mono_gchandle_new or
822 * mono_gchandle_new_weakref.
824 * Returns a pointer to the MonoObject represented by the handle or
825 * NULL for a collected object if using a weakref handle.
828 mono_gchandle_get_target (guint32 gchandle)
830 guint slot = gchandle >> 3;
831 guint type = (gchandle & 7) - 1;
832 HandleData *handles = &gc_handles [type];
833 MonoObject *obj = NULL;
836 lock_handles (handles);
837 if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
838 if (handles->type <= HANDLE_WEAK_TRACK) {
839 obj = mono_gc_weak_link_get (&handles->entries [slot]);
841 obj = handles->entries [slot];
844 /* print a warning? */
846 unlock_handles (handles);
847 /*g_print ("get target of entry %d of type %d: %p\n", slot, handles->type, obj);*/
852 mono_gchandle_set_target (guint32 gchandle, MonoObject *obj)
854 guint slot = gchandle >> 3;
855 guint type = (gchandle & 7) - 1;
856 HandleData *handles = &gc_handles [type];
860 lock_handles (handles);
861 if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
862 if (handles->type <= HANDLE_WEAK_TRACK) {
863 if (handles->entries [slot])
864 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
866 mono_gc_weak_link_add (&handles->entries [slot], obj, handles->type == HANDLE_WEAK_TRACK);
867 /*FIXME, what to use when obj == null?*/
868 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
870 handles->entries [slot] = obj;
873 /* print a warning? */
875 /*g_print ("changed entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
876 unlock_handles (handles);
880 * mono_gchandle_is_in_domain:
881 * @gchandle: a GCHandle's handle.
882 * @domain: An application domain.
884 * Returns: true if the object wrapped by the @gchandle belongs to the specific @domain.
887 mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain)
889 guint slot = gchandle >> 3;
890 guint type = (gchandle & 7) - 1;
891 HandleData *handles = &gc_handles [type];
892 gboolean result = FALSE;
895 lock_handles (handles);
896 if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
897 if (handles->type <= HANDLE_WEAK_TRACK) {
898 result = domain->domain_id == handles->domain_ids [slot];
901 obj = handles->entries [slot];
905 result = domain == mono_object_domain (obj);
908 /* print a warning? */
910 unlock_handles (handles);
915 * mono_gchandle_free:
916 * @gchandle: a GCHandle's handle.
918 * Frees the @gchandle handle. If there are no outstanding
919 * references, the garbage collector can reclaim the memory of the
923 mono_gchandle_free (guint32 gchandle)
925 guint slot = gchandle >> 3;
926 guint type = (gchandle & 7) - 1;
927 HandleData *handles = &gc_handles [type];
931 lock_handles (handles);
932 if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
933 if (handles->type <= HANDLE_WEAK_TRACK) {
934 if (handles->entries [slot])
935 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
937 handles->entries [slot] = NULL;
939 handles->bitmap [slot / 32] &= ~(1 << (slot % 32));
941 /* print a warning? */
943 #ifndef DISABLE_PERFCOUNTERS
944 mono_perfcounters->gc_num_handles--;
946 /*g_print ("freed entry %d of type %d\n", slot, handles->type);*/
947 unlock_handles (handles);
948 mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_DESTROYED, handles->type, gchandle, NULL);
952 * mono_gchandle_free_domain:
953 * @domain: domain that is unloading
955 * Function used internally to cleanup any GC handle for objects belonging
956 * to the specified domain during appdomain unload.
959 mono_gchandle_free_domain (MonoDomain *domain)
963 for (type = 0; type < 3; ++type) {
965 HandleData *handles = &gc_handles [type];
966 lock_handles (handles);
967 for (slot = 0; slot < handles->size; ++slot) {
968 if (!(handles->bitmap [slot / 32] & (1 << (slot % 32))))
970 if (type <= HANDLE_WEAK_TRACK) {
971 if (domain->domain_id == handles->domain_ids [slot]) {
972 handles->bitmap [slot / 32] &= ~(1 << (slot % 32));
973 if (handles->entries [slot])
974 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
977 if (handles->entries [slot] && mono_object_domain (handles->entries [slot]) == domain) {
978 handles->bitmap [slot / 32] &= ~(1 << (slot % 32));
979 handles->entries [slot] = NULL;
983 unlock_handles (handles);
989 mono_gc_GCHandle_CheckCurrentDomain (guint32 gchandle)
991 return mono_gchandle_is_in_domain (gchandle, mono_domain_get ());
994 static volatile gboolean finished=FALSE;
997 mono_gc_finalize_notify (void)
1000 g_message ( "%s: prodding finalizer", __func__);
1003 if (mono_gc_is_null ())
1006 mono_mutex_lock (&finalizer_mutex);
1008 mono_cond_signal (&finalizer_cond);
1009 mono_mutex_unlock (&finalizer_mutex);
1012 #ifdef HAVE_BOEHM_GC
1015 collect_objects (gpointer key, gpointer value, gpointer user_data)
1017 GPtrArray *arr = (GPtrArray*)user_data;
1018 g_ptr_array_add (arr, key);
1024 * finalize_domain_objects:
1026 * Run the finalizers of all finalizable objects in req->domain.
1029 finalize_domain_objects (DomainFinalizationReq *req)
1031 MonoDomain *domain = req->domain;
1034 #define NUM_FOBJECTS 64
1035 MonoObject *to_finalize [NUM_FOBJECTS];
1039 /* Process finalizers which are already in the queue */
1040 mono_gc_invoke_finalizers ();
1042 #ifdef HAVE_BOEHM_GC
1043 while (g_hash_table_size (domain->finalizable_objects_hash) > 0) {
1047 * Since the domain is unloading, nobody is allowed to put
1048 * new entries into the hash table. But finalize_object might
1049 * remove entries from the hash table, so we make a copy.
1051 objs = g_ptr_array_new ();
1052 g_hash_table_foreach (domain->finalizable_objects_hash, collect_objects, objs);
1053 /* printf ("FINALIZING %d OBJECTS.\n", objs->len); */
1055 for (i = 0; i < objs->len; ++i) {
1056 MonoObject *o = (MonoObject*)g_ptr_array_index (objs, i);
1057 /* FIXME: Avoid finalizing threads, etc */
1058 mono_gc_run_finalize (o, 0);
1061 g_ptr_array_free (objs, TRUE);
1063 #elif defined(HAVE_SGEN_GC)
1064 while ((count = mono_gc_finalizers_for_domain (domain, to_finalize, NUM_FOBJECTS))) {
1066 for (i = 0; i < count; ++i) {
1067 mono_gc_run_finalize (to_finalize [i], 0);
1072 /* cleanup the reference queue */
1073 reference_queue_clear_for_domain (domain);
1075 /* printf ("DONE.\n"); */
1076 SetEvent (req->done_event);
1078 /* The event is closed in mono_domain_finalize if we get here */
1083 finalizer_thread (gpointer unused)
1087 * Wait to be notified that there's at least one
1090 g_assert (mono_domain_get () == mono_get_root_domain ());
1092 gboolean has_work = FALSE;
1094 mono_gc_set_skip_thread (TRUE);
1095 MONO_PREPARE_BLOCKING;
1096 mono_mutex_lock (&finalizer_mutex);
1097 if (!num_to_finalize)
1098 mono_cond_wait (&finalizer_cond, &finalizer_mutex);
1101 mono_mutex_unlock (&finalizer_mutex);
1102 MONO_FINISH_BLOCKING;
1103 mono_gc_set_skip_thread (FALSE);
1105 if (has_work || finished)
1109 mono_threads_perform_thread_dump ();
1111 mono_console_handle_async_ops ();
1113 mono_attach_maybe_start ();
1115 if (domains_to_finalize) {
1116 mono_finalizer_lock ();
1117 if (domains_to_finalize) {
1118 DomainFinalizationReq *req = domains_to_finalize->data;
1119 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
1120 mono_finalizer_unlock ();
1122 finalize_domain_objects (req);
1124 mono_finalizer_unlock ();
1128 /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup),
1129 * before the domain is unloaded.
1131 mono_gc_invoke_finalizers ();
1133 mono_threads_join_threads ();
1135 reference_queue_proccess_all ();
1137 /* Avoid posting the pending done event until there are pending finalizers */
1138 mono_mutex_lock (&finalizer_mutex);
1140 if (num_to_finalize == 0)
1141 SetEvent (pending_done_event);
1142 mono_mutex_unlock (&finalizer_mutex);
1145 mono_finalizer_lock ();
1146 finalizer_thread_exited = TRUE;
1147 mono_cond_signal (&exited_cond);
1148 mono_finalizer_unlock ();
1153 #ifndef LAZY_GC_THREAD_CREATION
1157 mono_gc_init_finalizer_thread (void)
1159 gc_thread = mono_thread_create_internal (mono_domain_get (), finalizer_thread, NULL, FALSE, 0);
1160 ves_icall_System_Threading_Thread_SetName_internal (gc_thread, mono_string_new (mono_domain_get (), "Finalizer"));
1166 mono_mutex_init_recursive (&handle_section);
1167 mono_mutex_init_recursive (&allocator_section);
1169 mono_mutex_init_recursive (&finalizer_mutex);
1170 mono_mutex_init_recursive (&reference_queue_mutex);
1172 MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_NORMAL].entries);
1173 MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_PINNED].entries);
1175 mono_counters_register ("Minor GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.minor_gc_count);
1176 mono_counters_register ("Major GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.major_gc_count);
1177 mono_counters_register ("Minor GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.minor_gc_time);
1178 mono_counters_register ("Major GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time);
1179 mono_counters_register ("Major GC time concurrent", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time_concurrent);
1181 mono_gc_base_init ();
1183 if (mono_gc_is_disabled ()) {
1188 mono_cond_init (&finalizer_cond, 0);
1189 pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
1190 g_assert (pending_done_event);
1191 mono_cond_init (&exited_cond, 0);
1193 #ifndef LAZY_GC_THREAD_CREATION
1194 mono_gc_init_finalizer_thread ();
1199 mono_gc_cleanup (void)
1202 g_message ("%s: cleaning up finalizer", __func__);
1205 if (mono_gc_is_null ())
1210 if (mono_thread_internal_current () != gc_thread) {
1211 gboolean timed_out = FALSE;
1212 guint32 start_ticks = mono_msec_ticks ();
1213 guint32 end_ticks = start_ticks + 2000;
1215 mono_gc_finalize_notify ();
1216 /* Finishing the finalizer thread, so wait a little bit... */
1217 /* MS seems to wait for about 2 seconds */
1218 while (!finalizer_thread_exited) {
1219 guint32 current_ticks = mono_msec_ticks ();
1222 if (current_ticks >= end_ticks)
1225 timeout = end_ticks - current_ticks;
1226 MONO_PREPARE_BLOCKING;
1227 mono_finalizer_lock ();
1228 if (!finalizer_thread_exited)
1229 mono_cond_timedwait_ms (&exited_cond, &finalizer_mutex, timeout);
1230 mono_finalizer_unlock ();
1231 MONO_FINISH_BLOCKING;
1234 if (!finalizer_thread_exited) {
1237 /* Set a flag which the finalizer thread can check */
1238 suspend_finalizers = TRUE;
1240 /* Try to abort the thread, in the hope that it is running managed code */
1241 mono_thread_internal_stop (gc_thread);
1243 /* Wait for it to stop */
1244 ret = guarded_wait (gc_thread->handle, 100, TRUE);
1246 if (ret == WAIT_TIMEOUT) {
1248 * The finalizer thread refused to die. There is not much we
1249 * can do here, since the runtime is shutting down so the
1250 * state the finalizer thread depends on will vanish.
1252 g_warning ("Shutting down finalizer thread timed out.");
1260 /* Wait for the thread to actually exit */
1261 ret = guarded_wait (gc_thread->handle, INFINITE, TRUE);
1262 g_assert (ret == WAIT_OBJECT_0);
1264 mono_thread_join (GUINT_TO_POINTER (gc_thread->tid));
1268 mono_gc_base_cleanup ();
1271 mono_reference_queue_cleanup ();
1273 mono_mutex_destroy (&handle_section);
1274 mono_mutex_destroy (&allocator_section);
1275 mono_mutex_destroy (&finalizer_mutex);
1276 mono_mutex_destroy (&reference_queue_mutex);
1280 mono_gc_is_finalizer_internal_thread (MonoInternalThread *thread)
1282 return thread == gc_thread;
1286 * mono_gc_is_finalizer_thread:
1287 * @thread: the thread to test.
1289 * In Mono objects are finalized asynchronously on a separate thread.
1290 * This routine tests whether the @thread argument represents the
1291 * finalization thread.
1293 * Returns true if @thread is the finalization thread.
1296 mono_gc_is_finalizer_thread (MonoThread *thread)
1298 return mono_gc_is_finalizer_internal_thread (thread->internal_thread);
1301 #if defined(__MACH__)
1302 static pthread_t mach_exception_thread;
1305 mono_gc_register_mach_exception_thread (pthread_t thread)
1307 mach_exception_thread = thread;
1311 mono_gc_get_mach_exception_thread (void)
1313 return mach_exception_thread;
1317 #ifndef HAVE_SGEN_GC
1319 mono_gc_alloc_mature (MonoVTable *vtable)
1321 return mono_object_new_specific (vtable);
1326 static MonoReferenceQueue *ref_queues;
1329 ref_list_remove_element (RefQueueEntry **prev, RefQueueEntry *element)
1332 /* Guard if head is changed concurrently. */
1333 while (*prev != element)
1334 prev = &(*prev)->next;
1335 } while (prev && InterlockedCompareExchangePointer ((void*)prev, element->next, element) != element);
1339 ref_list_push (RefQueueEntry **head, RefQueueEntry *value)
1341 RefQueueEntry *current;
1344 value->next = current;
1345 STORE_STORE_FENCE; /*Must make sure the previous store is visible before the CAS. */
1346 } while (InterlockedCompareExchangePointer ((void*)head, value, current) != current);
1350 reference_queue_proccess (MonoReferenceQueue *queue)
1352 RefQueueEntry **iter = &queue->queue;
1353 RefQueueEntry *entry;
1354 while ((entry = *iter)) {
1356 if (queue->should_be_deleted || !mono_gc_weak_link_get (&entry->dis_link)) {
1357 mono_gc_weak_link_remove (&entry->dis_link, TRUE);
1359 if (queue->should_be_deleted || !mono_gchandle_get_target (entry->gchandle)) {
1360 mono_gchandle_free ((guint32)entry->gchandle);
1362 ref_list_remove_element (iter, entry);
1363 queue->callback (entry->user_data);
1366 iter = &entry->next;
1372 reference_queue_proccess_all (void)
1374 MonoReferenceQueue **iter;
1375 MonoReferenceQueue *queue = ref_queues;
1376 for (; queue; queue = queue->next)
1377 reference_queue_proccess (queue);
1380 mono_mutex_lock (&reference_queue_mutex);
1381 for (iter = &ref_queues; *iter;) {
1383 if (!queue->should_be_deleted) {
1384 iter = &queue->next;
1388 mono_mutex_unlock (&reference_queue_mutex);
1389 reference_queue_proccess (queue);
1392 *iter = queue->next;
1395 mono_mutex_unlock (&reference_queue_mutex);
1399 mono_reference_queue_cleanup (void)
1401 MonoReferenceQueue *queue = ref_queues;
1402 for (; queue; queue = queue->next)
1403 queue->should_be_deleted = TRUE;
1404 reference_queue_proccess_all ();
1408 reference_queue_clear_for_domain (MonoDomain *domain)
1410 MonoReferenceQueue *queue = ref_queues;
1411 for (; queue; queue = queue->next) {
1412 RefQueueEntry **iter = &queue->queue;
1413 RefQueueEntry *entry;
1414 while ((entry = *iter)) {
1415 if (entry->domain == domain) {
1417 mono_gc_weak_link_remove (&entry->dis_link, TRUE);
1419 mono_gchandle_free ((guint32)entry->gchandle);
1421 ref_list_remove_element (iter, entry);
1422 queue->callback (entry->user_data);
1425 iter = &entry->next;
1431 * mono_gc_reference_queue_new:
1432 * @callback callback used when processing collected entries.
1434 * Create a new reference queue used to process collected objects.
1435 * A reference queue let you add a pair of (managed object, user data)
1436 * using the mono_gc_reference_queue_add method.
1438 * Once the managed object is collected @callback will be called
1439 * in the finalizer thread with 'user data' as argument.
1441 * The callback is called from the finalizer thread without any locks held.
1442 * When a AppDomain is unloaded, all callbacks for objects belonging to it
1445 * @returns the new queue.
1448 mono_gc_reference_queue_new (mono_reference_queue_callback callback)
1450 MonoReferenceQueue *res = g_new0 (MonoReferenceQueue, 1);
1451 res->callback = callback;
1453 mono_mutex_lock (&reference_queue_mutex);
1454 res->next = ref_queues;
1456 mono_mutex_unlock (&reference_queue_mutex);
1462 * mono_gc_reference_queue_add:
1463 * @queue the queue to add the reference to.
1464 * @obj the object to be watched for collection
1465 * @user_data parameter to be passed to the queue callback
1467 * Queue an object to be watched for collection, when the @obj is
1468 * collected, the callback that was registered for the @queue will
1469 * be invoked with @user_data as argument.
1471 * @returns false if the queue is scheduled to be freed.
1474 mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *user_data)
1476 RefQueueEntry *entry;
1477 if (queue->should_be_deleted)
1480 entry = g_new0 (RefQueueEntry, 1);
1481 entry->user_data = user_data;
1482 entry->domain = mono_object_domain (obj);
1485 mono_gc_weak_link_add (&entry->dis_link, obj, TRUE);
1487 entry->gchandle = mono_gchandle_new_weakref (obj, TRUE);
1488 mono_object_register_finalizer (obj);
1491 ref_list_push (&queue->queue, entry);
1496 * mono_gc_reference_queue_free:
1497 * @queue the queue that should be freed.
1499 * This operation signals that @queue should be freed. This operation is deferred
1500 * as it happens on the finalizer thread.
1502 * After this call, no further objects can be queued. It's the responsibility of the
1503 * caller to make sure that no further attempt to access queue will be made.
1506 mono_gc_reference_queue_free (MonoReferenceQueue *queue)
1508 queue->should_be_deleted = TRUE;