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/threads-types.h>
26 #include <mono/metadata/threadpool-ms.h>
27 #include <mono/sgen/sgen-conf.h>
28 #include <mono/utils/mono-logger-internal.h>
29 #include <mono/metadata/gc-internal.h>
30 #include <mono/metadata/marshal.h> /* for mono_delegate_free_ftnptr () */
31 #include <mono/metadata/attach.h>
32 #include <mono/metadata/console-io.h>
33 #include <mono/utils/mono-semaphore.h>
34 #include <mono/utils/mono-memory-model.h>
35 #include <mono/utils/mono-counters.h>
36 #include <mono/utils/mono-time.h>
37 #include <mono/utils/dtrace.h>
38 #include <mono/utils/mono-threads.h>
39 #include <mono/utils/atomic.h>
45 typedef struct DomainFinalizationReq {
48 } DomainFinalizationReq;
50 static gboolean gc_disabled = FALSE;
52 static gboolean finalizing_root_domain = FALSE;
54 gboolean log_finalizers = FALSE;
55 gboolean do_not_finalize = FALSE;
57 #define mono_finalizer_lock() mono_mutex_lock (&finalizer_mutex)
58 #define mono_finalizer_unlock() mono_mutex_unlock (&finalizer_mutex)
59 static mono_mutex_t finalizer_mutex;
60 static mono_mutex_t reference_queue_mutex;
62 static GSList *domains_to_finalize= NULL;
63 static MonoMList *threads_to_finalize = NULL;
65 static gboolean finalizer_thread_exited;
66 /* Uses finalizer_mutex */
67 static mono_cond_t exited_cond;
69 static MonoInternalThread *gc_thread;
71 static void object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*));
73 static void mono_gchandle_set_target (guint32 gchandle, MonoObject *obj);
75 static void reference_queue_proccess_all (void);
76 static void mono_reference_queue_cleanup (void);
77 static void reference_queue_clear_for_domain (MonoDomain *domain);
78 static HANDLE pending_done_event;
81 guarded_wait (HANDLE handle, guint32 timeout, gboolean alertable)
85 MONO_PREPARE_BLOCKING;
86 result = WaitForSingleObjectEx (handle, timeout, alertable);
93 add_thread_to_finalize (MonoInternalThread *thread)
95 mono_finalizer_lock ();
96 if (!threads_to_finalize)
97 MONO_GC_REGISTER_ROOT_SINGLE (threads_to_finalize);
98 threads_to_finalize = mono_mlist_append (threads_to_finalize, (MonoObject*)thread);
99 mono_finalizer_unlock ();
102 static gboolean suspend_finalizers = FALSE;
104 * actually, we might want to queue the finalize requests in a separate thread,
105 * but we need to be careful about the execution domain of the thread...
108 mono_gc_run_finalize (void *obj, void *data)
113 MonoObject *exc = NULL;
118 MonoMethod* finalizer = NULL;
119 MonoDomain *caller_domain = mono_domain_get ();
121 RuntimeInvokeFunction runtime_invoke;
123 o = (MonoObject*)((char*)obj + GPOINTER_TO_UINT (data));
126 g_log ("mono-gc-finalizers", G_LOG_LEVEL_DEBUG, "<%s at %p> Starting finalizer checks.", o->vtable->klass->name, o);
128 if (suspend_finalizers)
131 domain = o->vtable->domain;
134 mono_domain_finalizers_lock (domain);
136 o2 = g_hash_table_lookup (domain->finalizable_objects_hash, o);
138 mono_domain_finalizers_unlock (domain);
141 /* Already finalized somehow */
145 /* make sure the finalizer is not called again if the object is resurrected */
146 object_register_finalizer (obj, NULL);
149 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Registered finalizer as processed.", o->vtable->klass->name, o);
151 if (o->vtable->klass == mono_defaults.internal_thread_class) {
152 MonoInternalThread *t = (MonoInternalThread*)o;
154 if (mono_gc_is_finalizer_internal_thread (t))
155 /* Avoid finalizing ourselves */
158 if (t->threadpool_thread && finalizing_root_domain) {
159 /* Don't finalize threadpool threads when
160 shutting down - they're finalized when the
161 threadpool shuts down. */
162 add_thread_to_finalize (t);
167 if (o->vtable->klass->image == mono_defaults.corlib && !strcmp (o->vtable->klass->name, "DynamicMethod") && finalizing_root_domain) {
169 * These can't be finalized during unloading/shutdown, since that would
170 * free the native code which can still be referenced by other
172 * FIXME: This is not perfect, objects dying at the same time as
173 * dynamic methods can still reference them even when !shutdown.
178 if (mono_runtime_get_no_exec ())
181 /* speedup later... and use a timeout */
182 /* g_print ("Finalize run on %p %s.%s\n", o, mono_object_class (o)->name_space, mono_object_class (o)->name); */
184 /* Use _internal here, since this thread can enter a doomed appdomain */
185 mono_domain_set_internal (mono_object_domain (o));
187 /* delegates that have a native function pointer allocated are
188 * registered for finalization, but they don't have a Finalize
189 * method, because in most cases it's not needed and it's just a waste.
191 if (o->vtable->klass->delegate) {
192 MonoDelegate* del = (MonoDelegate*)o;
193 if (del->delegate_trampoline)
194 mono_delegate_free_ftnptr ((MonoDelegate*)o);
195 mono_domain_set_internal (caller_domain);
199 finalizer = mono_class_get_finalizer (o->vtable->klass);
201 /* If object has a CCW but has no finalizer, it was only
202 * registered for finalization in order to free the CCW.
203 * Else it needs the regular finalizer run.
204 * FIXME: what to do about ressurection and suppression
205 * of finalizer on object with CCW.
207 if (mono_marshal_free_ccw (o) && !finalizer) {
208 mono_domain_set_internal (caller_domain);
213 * To avoid the locking plus the other overhead of mono_runtime_invoke (),
214 * create and precompile a wrapper which calls the finalize method using
218 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Compiling finalizer.", o->vtable->klass->name, o);
220 if (!domain->finalize_runtime_invoke) {
221 MonoMethod *invoke = mono_marshal_get_runtime_invoke (mono_class_get_method_from_name_flags (mono_defaults.object_class, "Finalize", 0, 0), TRUE);
223 domain->finalize_runtime_invoke = mono_compile_method (invoke);
226 runtime_invoke = domain->finalize_runtime_invoke;
228 mono_runtime_class_init (o->vtable);
230 if (G_UNLIKELY (MONO_GC_FINALIZE_INVOKE_ENABLED ())) {
231 MONO_GC_FINALIZE_INVOKE ((unsigned long)o, mono_object_get_size (o),
232 o->vtable->klass->name_space, o->vtable->klass->name);
236 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Calling finalizer.", o->vtable->klass->name, o);
238 runtime_invoke (o, NULL, &exc, NULL);
241 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Returned from finalizer.", o->vtable->klass->name, o);
244 mono_thread_internal_unhandled_exception (exc);
246 mono_domain_set_internal (caller_domain);
250 mono_gc_finalize_threadpool_threads (void)
252 while (threads_to_finalize) {
253 MonoInternalThread *thread = (MonoInternalThread*) mono_mlist_get_data (threads_to_finalize);
255 /* Force finalization of the thread. */
256 thread->threadpool_thread = FALSE;
257 mono_object_register_finalizer ((MonoObject*)thread);
259 mono_gc_run_finalize (thread, NULL);
261 threads_to_finalize = mono_mlist_next (threads_to_finalize);
266 mono_gc_out_of_memory (size_t size)
269 * we could allocate at program startup some memory that we could release
270 * back to the system at this point if we're really low on memory (ie, size is
271 * lower than the memory we set apart)
273 mono_raise_exception (mono_domain_get ()->out_of_memory_ex);
279 * Some of our objects may point to a different address than the address returned by GC_malloc()
280 * (because of the GetHashCode hack), but we need to pass the real address to register_finalizer.
281 * This also means that in the callback we need to adjust the pointer to get back the real
283 * We also need to be consistent in the use of the GC_debug* variants of malloc and register_finalizer,
284 * since that, too, can cause the underlying pointer to be offset.
287 object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*))
292 mono_raise_exception (mono_get_exception_argument_null ("obj"));
294 domain = obj->vtable->domain;
297 if (mono_domain_is_unloading (domain) && (callback != NULL))
299 * Can't register finalizers in a dying appdomain, since they
300 * could be invoked after the appdomain has been unloaded.
304 mono_domain_finalizers_lock (domain);
307 g_hash_table_insert (domain->finalizable_objects_hash, obj, obj);
309 g_hash_table_remove (domain->finalizable_objects_hash, obj);
311 mono_domain_finalizers_unlock (domain);
313 mono_gc_register_for_finalization (obj, callback);
314 #elif defined(HAVE_SGEN_GC)
316 * If we register finalizers for domains that are unloading we might
317 * end up running them while or after the domain is being cleared, so
318 * the objects will not be valid anymore.
320 if (!mono_domain_is_unloading (domain))
321 mono_gc_register_for_finalization (obj, callback);
326 * mono_object_register_finalizer:
327 * @obj: object to register
329 * Records that object @obj has a finalizer, this will call the
330 * Finalize method when the garbage collector disposes the object.
334 mono_object_register_finalizer (MonoObject *obj)
336 /* g_print ("Registered finalizer on %p %s.%s\n", obj, mono_object_class (obj)->name_space, mono_object_class (obj)->name); */
337 object_register_finalizer (obj, mono_gc_run_finalize);
341 * mono_domain_finalize:
342 * @domain: the domain to finalize
343 * @timeout: msects to wait for the finalization to complete, -1 to wait indefinitely
345 * Request finalization of all finalizable objects inside @domain. Wait
346 * @timeout msecs for the finalization to complete.
348 * Returns: TRUE if succeeded, FALSE if there was a timeout
352 mono_domain_finalize (MonoDomain *domain, guint32 timeout)
354 DomainFinalizationReq *req;
357 MonoInternalThread *thread = mono_thread_internal_current ();
359 #if defined(__native_client__)
363 if (mono_thread_internal_current () == gc_thread)
364 /* We are called from inside a finalizer, not much we can do here */
368 * No need to create another thread 'cause the finalizer thread
369 * is still working and will take care of running the finalizers
375 /* We don't support domain finalization without a GC */
376 if (mono_gc_is_null ())
379 mono_gc_collect (mono_gc_max_generation ());
381 done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
382 if (done_event == NULL) {
386 req = g_new0 (DomainFinalizationReq, 1);
387 req->domain = domain;
388 req->done_event = done_event;
390 if (domain == mono_get_root_domain ())
391 finalizing_root_domain = TRUE;
393 mono_finalizer_lock ();
395 domains_to_finalize = g_slist_append (domains_to_finalize, req);
397 mono_finalizer_unlock ();
399 /* Tell the finalizer thread to finalize this appdomain */
400 mono_gc_finalize_notify ();
406 res = guarded_wait (done_event, timeout, TRUE);
407 /* printf ("WAIT RES: %d.\n", res); */
409 if (res == WAIT_IO_COMPLETION) {
410 if ((thread->state & (ThreadState_StopRequested | ThreadState_SuspendRequested)) != 0)
412 } else if (res == WAIT_TIMEOUT) {
413 /* We leak the handle here */
420 CloseHandle (done_event);
422 if (domain == mono_get_root_domain ()) {
423 mono_threadpool_ms_cleanup ();
424 mono_gc_finalize_threadpool_threads ();
431 ves_icall_System_GC_InternalCollect (int generation)
433 mono_gc_collect (generation);
437 ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection)
440 mono_gc_collect (mono_gc_max_generation ());
441 return mono_gc_get_used_size ();
445 ves_icall_System_GC_KeepAlive (MonoObject *obj)
453 ves_icall_System_GC_ReRegisterForFinalize (MonoObject *obj)
455 MONO_CHECK_ARG_NULL (obj,);
457 object_register_finalizer (obj, mono_gc_run_finalize);
461 ves_icall_System_GC_SuppressFinalize (MonoObject *obj)
463 MONO_CHECK_ARG_NULL (obj,);
465 /* delegates have no finalizers, but we register them to deal with the
466 * unmanaged->managed trampoline. We don't let the user suppress it
467 * otherwise we'd leak it.
469 if (obj->vtable->klass->delegate)
472 /* FIXME: Need to handle case where obj has COM Callable Wrapper
473 * generated for it that needs cleaned up, but user wants to suppress
474 * their derived object finalizer. */
476 object_register_finalizer (obj, NULL);
480 ves_icall_System_GC_WaitForPendingFinalizers (void)
482 if (mono_gc_is_null ())
485 if (!mono_gc_pending_finalizers ())
488 if (mono_thread_internal_current () == gc_thread)
489 /* Avoid deadlocks */
493 If the finalizer thread is not live, lets pretend no finalizers are pending since the current thread might
494 be the one responsible for starting it up.
496 if (gc_thread == NULL)
499 ResetEvent (pending_done_event);
500 mono_gc_finalize_notify ();
501 /* g_print ("Waiting for pending finalizers....\n"); */
502 guarded_wait (pending_done_event, INFINITE, TRUE);
503 /* g_print ("Done pending....\n"); */
507 ves_icall_System_GC_register_ephemeron_array (MonoObject *array)
510 if (!mono_gc_ephemeron_array_add (array)) {
511 mono_set_pending_exception (mono_object_domain (array)->out_of_memory_ex);
518 ves_icall_System_GC_get_ephemeron_tombstone (void)
520 return mono_domain_get ()->ephemeron_tombstone;
523 #define mono_allocator_lock() mono_mutex_lock (&allocator_section)
524 #define mono_allocator_unlock() mono_mutex_unlock (&allocator_section)
525 static mono_mutex_t allocator_section;
526 static mono_mutex_t handle_section;
535 static HandleType mono_gchandle_get_type (guint32 gchandle);
538 ves_icall_System_GCHandle_GetTarget (guint32 handle)
540 return mono_gchandle_get_target (handle);
544 * if type == -1, change the target of the handle, otherwise allocate a new handle.
547 ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type)
550 mono_gchandle_set_target (handle, obj);
551 /* the handle doesn't change */
556 return mono_gchandle_new_weakref (obj, FALSE);
557 case HANDLE_WEAK_TRACK:
558 return mono_gchandle_new_weakref (obj, TRUE);
560 return mono_gchandle_new (obj, FALSE);
562 return mono_gchandle_new (obj, TRUE);
564 g_assert_not_reached ();
570 ves_icall_System_GCHandle_FreeHandle (guint32 handle)
572 mono_gchandle_free (handle);
576 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
580 if (mono_gchandle_get_type (handle) != HANDLE_PINNED)
582 obj = mono_gchandle_get_target (handle);
584 MonoClass *klass = mono_object_class (obj);
585 if (klass == mono_defaults.string_class) {
586 return mono_string_chars ((MonoString*)obj);
587 } else if (klass->rank) {
588 return mono_array_addr ((MonoArray*)obj, char, 0);
590 /* the C# code will check and throw the exception */
591 /* FIXME: missing !klass->blittable test, see bug #61134 */
592 if ((klass->flags & TYPE_ATTRIBUTE_LAYOUT_MASK) == TYPE_ATTRIBUTE_AUTO_LAYOUT)
594 return (char*)obj + sizeof (MonoObject);
601 ves_icall_Mono_Runtime_SetGCAllowSynchronousMajor (MonoBoolean flag)
603 return mono_gc_set_allow_synchronous_major (flag);
611 guint slot_hint : 24; /* starting slot for search */
612 /* 2^16 appdomains should be enough for everyone (though I know I'll regret this in 20 years) */
613 /* we alloc this only for weak refs, since we can get the domain directly in the other cases */
617 /* weak and weak-track arrays will be allocated in malloc memory
619 static HandleData gc_handles [] = {
620 {NULL, NULL, 0, HANDLE_WEAK, 0},
621 {NULL, NULL, 0, HANDLE_WEAK_TRACK, 0},
622 {NULL, NULL, 0, HANDLE_NORMAL, 0},
623 {NULL, NULL, 0, HANDLE_PINNED, 0}
626 #define lock_handles(handles) mono_mutex_lock (&handle_section)
627 #define unlock_handles(handles) mono_mutex_unlock (&handle_section)
630 find_first_unset (guint32 bitmap)
633 for (i = 0; i < 32; ++i) {
634 if (!(bitmap & (1 << i)))
640 static MonoGCDescriptor
641 make_root_descr_all_refs (int numbits, gboolean pinned)
645 return MONO_GC_DESCRIPTOR_NULL;
647 return mono_gc_make_root_descr_all_refs (numbits);
651 alloc_handle (HandleData *handles, MonoObject *obj, gboolean track)
655 lock_handles (handles);
656 if (!handles->size) {
658 if (handles->type > HANDLE_WEAK_TRACK) {
659 handles->entries = mono_gc_alloc_fixed (sizeof (gpointer) * handles->size, make_root_descr_all_refs (handles->size, handles->type == HANDLE_PINNED));
661 handles->entries = g_malloc0 (sizeof (gpointer) * handles->size);
662 handles->domain_ids = g_malloc0 (sizeof (guint16) * handles->size);
664 handles->bitmap = g_malloc0 (handles->size / 8);
667 for (slot = handles->slot_hint; slot < handles->size / 32; ++slot) {
668 if (handles->bitmap [slot] != 0xffffffff) {
669 i = find_first_unset (handles->bitmap [slot]);
670 handles->slot_hint = slot;
674 if (i == -1 && handles->slot_hint != 0) {
675 for (slot = 0; slot < handles->slot_hint; ++slot) {
676 if (handles->bitmap [slot] != 0xffffffff) {
677 i = find_first_unset (handles->bitmap [slot]);
678 handles->slot_hint = slot;
685 guint32 new_size = handles->size * 2; /* always double: we memset to 0 based on this below */
687 /* resize and copy the bitmap */
688 new_bitmap = g_malloc0 (new_size / 8);
689 memcpy (new_bitmap, handles->bitmap, handles->size / 8);
690 g_free (handles->bitmap);
691 handles->bitmap = new_bitmap;
693 /* resize and copy the entries */
694 if (handles->type > HANDLE_WEAK_TRACK) {
697 entries = mono_gc_alloc_fixed (sizeof (gpointer) * new_size, make_root_descr_all_refs (new_size, handles->type == HANDLE_PINNED));
698 mono_gc_memmove_aligned (entries, handles->entries, sizeof (gpointer) * handles->size);
700 mono_gc_free_fixed (handles->entries);
701 handles->entries = entries;
705 domain_ids = g_malloc0 (sizeof (guint16) * new_size);
706 entries = g_malloc0 (sizeof (gpointer) * new_size);
707 memcpy (domain_ids, handles->domain_ids, sizeof (guint16) * handles->size);
708 for (i = 0; i < handles->size; ++i) {
709 MonoObject *obj = mono_gc_weak_link_get (&(handles->entries [i]));
711 mono_gc_weak_link_add (&(entries [i]), obj, track);
712 mono_gc_weak_link_remove (&(handles->entries [i]), track);
714 g_assert (!handles->entries [i]);
717 g_free (handles->entries);
718 g_free (handles->domain_ids);
719 handles->entries = entries;
720 handles->domain_ids = domain_ids;
723 /* set i and slot to the next free position */
725 slot = (handles->size + 1) / 32;
726 handles->slot_hint = handles->size + 1;
727 handles->size = new_size;
729 handles->bitmap [slot] |= 1 << i;
730 slot = slot * 32 + i;
731 handles->entries [slot] = NULL;
732 if (handles->type <= HANDLE_WEAK_TRACK) {
733 /*FIXME, what to use when obj == null?*/
734 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
736 mono_gc_weak_link_add (&(handles->entries [slot]), obj, track);
738 handles->entries [slot] = obj;
741 #ifndef DISABLE_PERFCOUNTERS
742 mono_perfcounters->gc_num_handles++;
744 unlock_handles (handles);
745 /*g_print ("allocated entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
746 res = (slot << 3) | (handles->type + 1);
747 mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_CREATED, handles->type, res, obj);
753 * @obj: managed object to get a handle for
754 * @pinned: whether the object should be pinned
756 * This returns a handle that wraps the object, this is used to keep a
757 * reference to a managed object from the unmanaged world and preventing the
758 * object from being disposed.
760 * If @pinned is false the address of the object can not be obtained, if it is
761 * true the address of the object can be obtained. This will also pin the
762 * object so it will not be possible by a moving garbage collector to move the
765 * Returns: a handle that can be used to access the object from
769 mono_gchandle_new (MonoObject *obj, gboolean pinned)
771 return alloc_handle (&gc_handles [pinned? HANDLE_PINNED: HANDLE_NORMAL], obj, FALSE);
775 * mono_gchandle_new_weakref:
776 * @obj: managed object to get a handle for
777 * @pinned: whether the object should be pinned
779 * This returns a weak handle that wraps the object, this is used to
780 * keep a reference to a managed object from the unmanaged world.
781 * Unlike the mono_gchandle_new the object can be reclaimed by the
782 * garbage collector. In this case the value of the GCHandle will be
785 * If @pinned is false the address of the object can not be obtained, if it is
786 * true the address of the object can be obtained. This will also pin the
787 * object so it will not be possible by a moving garbage collector to move the
790 * Returns: a handle that can be used to access the object from
794 mono_gchandle_new_weakref (MonoObject *obj, gboolean track_resurrection)
796 guint32 handle = alloc_handle (&gc_handles [track_resurrection? HANDLE_WEAK_TRACK: HANDLE_WEAK], obj, track_resurrection);
802 mono_gchandle_get_type (guint32 gchandle)
804 guint type = (gchandle & 7) - 1;
810 * mono_gchandle_get_target:
811 * @gchandle: a GCHandle's handle.
813 * The handle was previously created by calling mono_gchandle_new or
814 * mono_gchandle_new_weakref.
816 * Returns a pointer to the MonoObject represented by the handle or
817 * NULL for a collected object if using a weakref handle.
820 mono_gchandle_get_target (guint32 gchandle)
822 guint slot = gchandle >> 3;
823 guint type = (gchandle & 7) - 1;
824 HandleData *handles = &gc_handles [type];
825 MonoObject *obj = NULL;
828 lock_handles (handles);
829 if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
830 if (handles->type <= HANDLE_WEAK_TRACK) {
831 obj = mono_gc_weak_link_get (&handles->entries [slot]);
833 obj = handles->entries [slot];
836 /* print a warning? */
838 unlock_handles (handles);
839 /*g_print ("get target of entry %d of type %d: %p\n", slot, handles->type, obj);*/
844 mono_gchandle_set_target (guint32 gchandle, MonoObject *obj)
846 guint slot = gchandle >> 3;
847 guint type = (gchandle & 7) - 1;
848 HandleData *handles = &gc_handles [type];
852 lock_handles (handles);
853 if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
854 if (handles->type <= HANDLE_WEAK_TRACK) {
855 if (handles->entries [slot])
856 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
858 mono_gc_weak_link_add (&handles->entries [slot], obj, handles->type == HANDLE_WEAK_TRACK);
859 /*FIXME, what to use when obj == null?*/
860 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
862 handles->entries [slot] = obj;
865 /* print a warning? */
867 /*g_print ("changed entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
868 unlock_handles (handles);
872 * mono_gchandle_is_in_domain:
873 * @gchandle: a GCHandle's handle.
874 * @domain: An application domain.
876 * Returns: true if the object wrapped by the @gchandle belongs to the specific @domain.
879 mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain)
881 guint slot = gchandle >> 3;
882 guint type = (gchandle & 7) - 1;
883 HandleData *handles = &gc_handles [type];
884 gboolean result = FALSE;
887 lock_handles (handles);
888 if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
889 if (handles->type <= HANDLE_WEAK_TRACK) {
890 result = domain->domain_id == handles->domain_ids [slot];
893 obj = handles->entries [slot];
897 result = domain == mono_object_domain (obj);
900 /* print a warning? */
902 unlock_handles (handles);
907 * mono_gchandle_free:
908 * @gchandle: a GCHandle's handle.
910 * Frees the @gchandle handle. If there are no outstanding
911 * references, the garbage collector can reclaim the memory of the
915 mono_gchandle_free (guint32 gchandle)
917 guint slot = gchandle >> 3;
918 guint type = (gchandle & 7) - 1;
919 HandleData *handles = &gc_handles [type];
923 lock_handles (handles);
924 if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
925 if (handles->type <= HANDLE_WEAK_TRACK) {
926 if (handles->entries [slot])
927 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
929 handles->entries [slot] = NULL;
931 handles->bitmap [slot / 32] &= ~(1 << (slot % 32));
933 /* print a warning? */
935 #ifndef DISABLE_PERFCOUNTERS
936 mono_perfcounters->gc_num_handles--;
938 /*g_print ("freed entry %d of type %d\n", slot, handles->type);*/
939 unlock_handles (handles);
940 mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_DESTROYED, handles->type, gchandle, NULL);
944 * mono_gchandle_free_domain:
945 * @domain: domain that is unloading
947 * Function used internally to cleanup any GC handle for objects belonging
948 * to the specified domain during appdomain unload.
951 mono_gchandle_free_domain (MonoDomain *domain)
955 for (type = 0; type < 3; ++type) {
957 HandleData *handles = &gc_handles [type];
958 lock_handles (handles);
959 for (slot = 0; slot < handles->size; ++slot) {
960 if (!(handles->bitmap [slot / 32] & (1 << (slot % 32))))
962 if (type <= HANDLE_WEAK_TRACK) {
963 if (domain->domain_id == handles->domain_ids [slot]) {
964 handles->bitmap [slot / 32] &= ~(1 << (slot % 32));
965 if (handles->entries [slot])
966 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
969 if (handles->entries [slot] && mono_object_domain (handles->entries [slot]) == domain) {
970 handles->bitmap [slot / 32] &= ~(1 << (slot % 32));
971 handles->entries [slot] = NULL;
975 unlock_handles (handles);
981 mono_gc_GCHandle_CheckCurrentDomain (guint32 gchandle)
983 return mono_gchandle_is_in_domain (gchandle, mono_domain_get ());
986 #ifdef MONO_HAS_SEMAPHORES
987 static MonoSemType finalizer_sem;
989 static HANDLE finalizer_event;
990 static volatile gboolean finished=FALSE;
993 mono_gc_finalize_notify (void)
996 g_message ( "%s: prodding finalizer", __func__);
999 if (mono_gc_is_null ())
1002 #ifdef MONO_HAS_SEMAPHORES
1003 MONO_SEM_POST (&finalizer_sem);
1005 SetEvent (finalizer_event);
1009 #ifdef HAVE_BOEHM_GC
1012 collect_objects (gpointer key, gpointer value, gpointer user_data)
1014 GPtrArray *arr = (GPtrArray*)user_data;
1015 g_ptr_array_add (arr, key);
1021 * finalize_domain_objects:
1023 * Run the finalizers of all finalizable objects in req->domain.
1026 finalize_domain_objects (DomainFinalizationReq *req)
1028 MonoDomain *domain = req->domain;
1031 #define NUM_FOBJECTS 64
1032 MonoObject *to_finalize [NUM_FOBJECTS];
1036 /* Process finalizers which are already in the queue */
1037 mono_gc_invoke_finalizers ();
1039 #ifdef HAVE_BOEHM_GC
1040 while (g_hash_table_size (domain->finalizable_objects_hash) > 0) {
1044 * Since the domain is unloading, nobody is allowed to put
1045 * new entries into the hash table. But finalize_object might
1046 * remove entries from the hash table, so we make a copy.
1048 objs = g_ptr_array_new ();
1049 g_hash_table_foreach (domain->finalizable_objects_hash, collect_objects, objs);
1050 /* printf ("FINALIZING %d OBJECTS.\n", objs->len); */
1052 for (i = 0; i < objs->len; ++i) {
1053 MonoObject *o = (MonoObject*)g_ptr_array_index (objs, i);
1054 /* FIXME: Avoid finalizing threads, etc */
1055 mono_gc_run_finalize (o, 0);
1058 g_ptr_array_free (objs, TRUE);
1060 #elif defined(HAVE_SGEN_GC)
1061 while ((count = mono_gc_finalizers_for_domain (domain, to_finalize, NUM_FOBJECTS))) {
1063 for (i = 0; i < count; ++i) {
1064 mono_gc_run_finalize (to_finalize [i], 0);
1069 /* cleanup the reference queue */
1070 reference_queue_clear_for_domain (domain);
1072 /* printf ("DONE.\n"); */
1073 SetEvent (req->done_event);
1075 /* The event is closed in mono_domain_finalize if we get here */
1080 finalizer_thread (gpointer unused)
1082 gboolean wait = TRUE;
1085 /* Wait to be notified that there's at least one
1089 g_assert (mono_domain_get () == mono_get_root_domain ());
1090 mono_gc_set_skip_thread (TRUE);
1091 MONO_PREPARE_BLOCKING;
1094 /* An alertable wait is required so this thread can be suspended on windows */
1095 #ifdef MONO_HAS_SEMAPHORES
1096 MONO_SEM_WAIT_ALERTABLE (&finalizer_sem, TRUE);
1098 WaitForSingleObjectEx (finalizer_event, INFINITE, TRUE);
1102 MONO_FINISH_BLOCKING;
1103 mono_gc_set_skip_thread (FALSE);
1105 mono_threads_perform_thread_dump ();
1107 mono_console_handle_async_ops ();
1109 mono_attach_maybe_start ();
1111 if (domains_to_finalize) {
1112 mono_finalizer_lock ();
1113 if (domains_to_finalize) {
1114 DomainFinalizationReq *req = domains_to_finalize->data;
1115 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
1116 mono_finalizer_unlock ();
1118 finalize_domain_objects (req);
1120 mono_finalizer_unlock ();
1124 /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup),
1125 * before the domain is unloaded.
1127 mono_gc_invoke_finalizers ();
1129 mono_threads_join_threads ();
1131 reference_queue_proccess_all ();
1133 #ifdef MONO_HAS_SEMAPHORES
1134 /* Avoid posting the pending done event until there are pending finalizers */
1135 if (MONO_SEM_TIMEDWAIT (&finalizer_sem, 0) == 0)
1136 /* Don't wait again at the start of the loop */
1139 SetEvent (pending_done_event);
1141 SetEvent (pending_done_event);
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 finalizer_event = CreateEvent (NULL, FALSE, FALSE, NULL);
1189 g_assert (finalizer_event);
1190 pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
1191 g_assert (pending_done_event);
1192 mono_cond_init (&exited_cond, 0);
1193 #ifdef MONO_HAS_SEMAPHORES
1194 MONO_SEM_INIT (&finalizer_sem, 0);
1197 #ifndef LAZY_GC_THREAD_CREATION
1198 mono_gc_init_finalizer_thread ();
1203 mono_gc_cleanup (void)
1206 g_message ("%s: cleaning up finalizer", __func__);
1209 if (mono_gc_is_null ())
1214 if (mono_thread_internal_current () != gc_thread) {
1215 gboolean timed_out = FALSE;
1216 guint32 start_ticks = mono_msec_ticks ();
1217 guint32 end_ticks = start_ticks + 2000;
1219 mono_gc_finalize_notify ();
1220 /* Finishing the finalizer thread, so wait a little bit... */
1221 /* MS seems to wait for about 2 seconds */
1222 while (!finalizer_thread_exited) {
1223 guint32 current_ticks = mono_msec_ticks ();
1226 if (current_ticks >= end_ticks)
1229 timeout = end_ticks - current_ticks;
1230 MONO_PREPARE_BLOCKING;
1231 mono_finalizer_lock ();
1232 if (!finalizer_thread_exited)
1233 mono_cond_timedwait_ms (&exited_cond, &finalizer_mutex, timeout);
1234 mono_finalizer_unlock ();
1235 MONO_FINISH_BLOCKING;
1238 if (!finalizer_thread_exited) {
1241 /* Set a flag which the finalizer thread can check */
1242 suspend_finalizers = TRUE;
1244 /* Try to abort the thread, in the hope that it is running managed code */
1245 mono_thread_internal_stop (gc_thread);
1247 /* Wait for it to stop */
1248 ret = guarded_wait (gc_thread->handle, 100, TRUE);
1250 if (ret == WAIT_TIMEOUT) {
1252 * The finalizer thread refused to die. There is not much we
1253 * can do here, since the runtime is shutting down so the
1254 * state the finalizer thread depends on will vanish.
1256 g_warning ("Shutting down finalizer thread timed out.");
1264 /* Wait for the thread to actually exit */
1265 ret = guarded_wait (gc_thread->handle, INFINITE, TRUE);
1266 g_assert (ret == WAIT_OBJECT_0);
1268 mono_thread_join (GUINT_TO_POINTER (gc_thread->tid));
1272 mono_gc_base_cleanup ();
1275 mono_reference_queue_cleanup ();
1277 mono_mutex_destroy (&allocator_section);
1278 mono_mutex_destroy (&finalizer_mutex);
1279 mono_mutex_destroy (&reference_queue_mutex);
1283 * mono_gc_mutex_cleanup:
1285 * Destroy the mutexes that may still be used after the main cleanup routine.
1288 mono_gc_mutex_cleanup (void)
1290 mono_mutex_destroy (&handle_section);
1294 mono_gc_is_finalizer_internal_thread (MonoInternalThread *thread)
1296 return thread == gc_thread;
1300 * mono_gc_is_finalizer_thread:
1301 * @thread: the thread to test.
1303 * In Mono objects are finalized asynchronously on a separate thread.
1304 * This routine tests whether the @thread argument represents the
1305 * finalization thread.
1307 * Returns true if @thread is the finalization thread.
1310 mono_gc_is_finalizer_thread (MonoThread *thread)
1312 return mono_gc_is_finalizer_internal_thread (thread->internal_thread);
1315 #if defined(__MACH__)
1316 static pthread_t mach_exception_thread;
1319 mono_gc_register_mach_exception_thread (pthread_t thread)
1321 mach_exception_thread = thread;
1325 mono_gc_get_mach_exception_thread (void)
1327 return mach_exception_thread;
1331 #ifndef HAVE_SGEN_GC
1333 mono_gc_alloc_mature (MonoVTable *vtable)
1335 return mono_object_new_specific (vtable);
1340 static MonoReferenceQueue *ref_queues;
1343 ref_list_remove_element (RefQueueEntry **prev, RefQueueEntry *element)
1346 /* Guard if head is changed concurrently. */
1347 while (*prev != element)
1348 prev = &(*prev)->next;
1349 } while (prev && InterlockedCompareExchangePointer ((void*)prev, element->next, element) != element);
1353 ref_list_push (RefQueueEntry **head, RefQueueEntry *value)
1355 RefQueueEntry *current;
1358 value->next = current;
1359 STORE_STORE_FENCE; /*Must make sure the previous store is visible before the CAS. */
1360 } while (InterlockedCompareExchangePointer ((void*)head, value, current) != current);
1364 reference_queue_proccess (MonoReferenceQueue *queue)
1366 RefQueueEntry **iter = &queue->queue;
1367 RefQueueEntry *entry;
1368 while ((entry = *iter)) {
1370 if (queue->should_be_deleted || !mono_gc_weak_link_get (&entry->dis_link)) {
1371 mono_gc_weak_link_remove (&entry->dis_link, TRUE);
1373 if (queue->should_be_deleted || !mono_gchandle_get_target (entry->gchandle)) {
1374 mono_gchandle_free ((guint32)entry->gchandle);
1376 ref_list_remove_element (iter, entry);
1377 queue->callback (entry->user_data);
1380 iter = &entry->next;
1386 reference_queue_proccess_all (void)
1388 MonoReferenceQueue **iter;
1389 MonoReferenceQueue *queue = ref_queues;
1390 for (; queue; queue = queue->next)
1391 reference_queue_proccess (queue);
1394 mono_mutex_lock (&reference_queue_mutex);
1395 for (iter = &ref_queues; *iter;) {
1397 if (!queue->should_be_deleted) {
1398 iter = &queue->next;
1402 mono_mutex_unlock (&reference_queue_mutex);
1403 reference_queue_proccess (queue);
1406 *iter = queue->next;
1409 mono_mutex_unlock (&reference_queue_mutex);
1413 mono_reference_queue_cleanup (void)
1415 MonoReferenceQueue *queue = ref_queues;
1416 for (; queue; queue = queue->next)
1417 queue->should_be_deleted = TRUE;
1418 reference_queue_proccess_all ();
1422 reference_queue_clear_for_domain (MonoDomain *domain)
1424 MonoReferenceQueue *queue = ref_queues;
1425 for (; queue; queue = queue->next) {
1426 RefQueueEntry **iter = &queue->queue;
1427 RefQueueEntry *entry;
1428 while ((entry = *iter)) {
1429 if (entry->domain == domain) {
1431 mono_gc_weak_link_remove (&entry->dis_link, TRUE);
1433 mono_gchandle_free ((guint32)entry->gchandle);
1435 ref_list_remove_element (iter, entry);
1436 queue->callback (entry->user_data);
1439 iter = &entry->next;
1445 * mono_gc_reference_queue_new:
1446 * @callback callback used when processing collected entries.
1448 * Create a new reference queue used to process collected objects.
1449 * A reference queue let you add a pair of (managed object, user data)
1450 * using the mono_gc_reference_queue_add method.
1452 * Once the managed object is collected @callback will be called
1453 * in the finalizer thread with 'user data' as argument.
1455 * The callback is called from the finalizer thread without any locks held.
1456 * When a AppDomain is unloaded, all callbacks for objects belonging to it
1459 * @returns the new queue.
1462 mono_gc_reference_queue_new (mono_reference_queue_callback callback)
1464 MonoReferenceQueue *res = g_new0 (MonoReferenceQueue, 1);
1465 res->callback = callback;
1467 mono_mutex_lock (&reference_queue_mutex);
1468 res->next = ref_queues;
1470 mono_mutex_unlock (&reference_queue_mutex);
1476 * mono_gc_reference_queue_add:
1477 * @queue the queue to add the reference to.
1478 * @obj the object to be watched for collection
1479 * @user_data parameter to be passed to the queue callback
1481 * Queue an object to be watched for collection, when the @obj is
1482 * collected, the callback that was registered for the @queue will
1483 * be invoked with @user_data as argument.
1485 * @returns false if the queue is scheduled to be freed.
1488 mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *user_data)
1490 RefQueueEntry *entry;
1491 if (queue->should_be_deleted)
1494 entry = g_new0 (RefQueueEntry, 1);
1495 entry->user_data = user_data;
1496 entry->domain = mono_object_domain (obj);
1499 mono_gc_weak_link_add (&entry->dis_link, obj, TRUE);
1501 entry->gchandle = mono_gchandle_new_weakref (obj, TRUE);
1502 mono_object_register_finalizer (obj);
1505 ref_list_push (&queue->queue, entry);
1510 * mono_gc_reference_queue_free:
1511 * @queue the queue that should be freed.
1513 * This operation signals that @queue should be freed. This operation is deferred
1514 * as it happens on the finalizer thread.
1516 * After this call, no further objects can be queued. It's the responsibility of the
1517 * caller to make sure that no further attempt to access queue will be made.
1520 mono_gc_reference_queue_free (MonoReferenceQueue *queue)
1522 queue->should_be_deleted = TRUE;