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 #ifdef PLATFORM_WINCE /* FIXME: add accessors to gc.dll API */
52 extern void (*__imp_GC_finalizer_notifier)(void);
53 #define GC_finalizer_notifier __imp_GC_finalizer_notifier
54 extern int __imp_GC_finalize_on_demand;
55 #define GC_finalize_on_demand __imp_GC_finalize_on_demand
58 static gboolean gc_disabled = FALSE;
60 static gboolean finalizing_root_domain = FALSE;
62 gboolean log_finalizers = FALSE;
63 gboolean do_not_finalize = FALSE;
65 #define mono_finalizer_lock() mono_mutex_lock (&finalizer_mutex)
66 #define mono_finalizer_unlock() mono_mutex_unlock (&finalizer_mutex)
67 static mono_mutex_t finalizer_mutex;
68 static mono_mutex_t reference_queue_mutex;
70 static GSList *domains_to_finalize= NULL;
71 static MonoMList *threads_to_finalize = NULL;
73 static gboolean finalizer_thread_exited;
74 /* Uses finalizer_mutex */
75 static mono_cond_t exited_cond;
77 static MonoInternalThread *gc_thread;
79 static void object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*));
81 static void mono_gchandle_set_target (guint32 gchandle, MonoObject *obj);
83 static void reference_queue_proccess_all (void);
84 static void mono_reference_queue_cleanup (void);
85 static void reference_queue_clear_for_domain (MonoDomain *domain);
86 static HANDLE pending_done_event;
89 guarded_wait (HANDLE handle, guint32 timeout, gboolean alertable)
94 result = WaitForSingleObjectEx (handle, timeout, alertable);
101 add_thread_to_finalize (MonoInternalThread *thread)
103 mono_finalizer_lock ();
104 if (!threads_to_finalize)
105 MONO_GC_REGISTER_ROOT_SINGLE (threads_to_finalize);
106 threads_to_finalize = mono_mlist_append (threads_to_finalize, (MonoObject*)thread);
107 mono_finalizer_unlock ();
110 static gboolean suspend_finalizers = FALSE;
112 * actually, we might want to queue the finalize requests in a separate thread,
113 * but we need to be careful about the execution domain of the thread...
116 mono_gc_run_finalize (void *obj, void *data)
121 MonoObject *exc = NULL;
126 MonoMethod* finalizer = NULL;
127 MonoDomain *caller_domain = mono_domain_get ();
129 RuntimeInvokeFunction runtime_invoke;
131 o = (MonoObject*)((char*)obj + GPOINTER_TO_UINT (data));
134 g_log ("mono-gc-finalizers", G_LOG_LEVEL_DEBUG, "<%s at %p> Starting finalizer checks.", o->vtable->klass->name, o);
136 if (suspend_finalizers)
139 domain = o->vtable->domain;
142 mono_domain_finalizers_lock (domain);
144 o2 = g_hash_table_lookup (domain->finalizable_objects_hash, o);
146 mono_domain_finalizers_unlock (domain);
149 /* Already finalized somehow */
153 /* make sure the finalizer is not called again if the object is resurrected */
154 object_register_finalizer (obj, NULL);
157 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Registered finalizer as processed.", o->vtable->klass->name, o);
159 if (o->vtable->klass == mono_defaults.internal_thread_class) {
160 MonoInternalThread *t = (MonoInternalThread*)o;
162 if (mono_gc_is_finalizer_internal_thread (t))
163 /* Avoid finalizing ourselves */
166 if (t->threadpool_thread && finalizing_root_domain) {
167 /* Don't finalize threadpool threads when
168 shutting down - they're finalized when the
169 threadpool shuts down. */
170 add_thread_to_finalize (t);
175 if (o->vtable->klass->image == mono_defaults.corlib && !strcmp (o->vtable->klass->name, "DynamicMethod") && finalizing_root_domain) {
177 * These can't be finalized during unloading/shutdown, since that would
178 * free the native code which can still be referenced by other
180 * FIXME: This is not perfect, objects dying at the same time as
181 * dynamic methods can still reference them even when !shutdown.
186 if (mono_runtime_get_no_exec ())
189 /* speedup later... and use a timeout */
190 /* g_print ("Finalize run on %p %s.%s\n", o, mono_object_class (o)->name_space, mono_object_class (o)->name); */
192 /* Use _internal here, since this thread can enter a doomed appdomain */
193 mono_domain_set_internal (mono_object_domain (o));
195 /* delegates that have a native function pointer allocated are
196 * registered for finalization, but they don't have a Finalize
197 * method, because in most cases it's not needed and it's just a waste.
199 if (o->vtable->klass->delegate) {
200 MonoDelegate* del = (MonoDelegate*)o;
201 if (del->delegate_trampoline)
202 mono_delegate_free_ftnptr ((MonoDelegate*)o);
203 mono_domain_set_internal (caller_domain);
207 finalizer = mono_class_get_finalizer (o->vtable->klass);
210 /* If object has a CCW but has no finalizer, it was only
211 * registered for finalization in order to free the CCW.
212 * Else it needs the regular finalizer run.
213 * FIXME: what to do about ressurection and suppression
214 * of finalizer on object with CCW.
216 if (mono_marshal_free_ccw (o) && !finalizer) {
217 mono_domain_set_internal (caller_domain);
223 * To avoid the locking plus the other overhead of mono_runtime_invoke (),
224 * create and precompile a wrapper which calls the finalize method using
228 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Compiling finalizer.", o->vtable->klass->name, o);
230 if (!domain->finalize_runtime_invoke) {
231 MonoMethod *invoke = mono_marshal_get_runtime_invoke (mono_class_get_method_from_name_flags (mono_defaults.object_class, "Finalize", 0, 0), TRUE);
233 domain->finalize_runtime_invoke = mono_compile_method (invoke);
236 runtime_invoke = domain->finalize_runtime_invoke;
238 mono_runtime_class_init (o->vtable);
240 if (G_UNLIKELY (MONO_GC_FINALIZE_INVOKE_ENABLED ())) {
241 MONO_GC_FINALIZE_INVOKE ((unsigned long)o, mono_object_get_size (o),
242 o->vtable->klass->name_space, o->vtable->klass->name);
246 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Calling finalizer.", o->vtable->klass->name, o);
248 runtime_invoke (o, NULL, &exc, NULL);
251 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Returned from finalizer.", o->vtable->klass->name, o);
254 mono_internal_thread_unhandled_exception (exc);
256 mono_domain_set_internal (caller_domain);
260 mono_gc_finalize_threadpool_threads (void)
262 while (threads_to_finalize) {
263 MonoInternalThread *thread = (MonoInternalThread*) mono_mlist_get_data (threads_to_finalize);
265 /* Force finalization of the thread. */
266 thread->threadpool_thread = FALSE;
267 mono_object_register_finalizer ((MonoObject*)thread);
269 mono_gc_run_finalize (thread, NULL);
271 threads_to_finalize = mono_mlist_next (threads_to_finalize);
276 mono_gc_out_of_memory (size_t size)
279 * we could allocate at program startup some memory that we could release
280 * back to the system at this point if we're really low on memory (ie, size is
281 * lower than the memory we set apart)
283 mono_raise_exception (mono_domain_get ()->out_of_memory_ex);
289 * Some of our objects may point to a different address than the address returned by GC_malloc()
290 * (because of the GetHashCode hack), but we need to pass the real address to register_finalizer.
291 * This also means that in the callback we need to adjust the pointer to get back the real
293 * We also need to be consistent in the use of the GC_debug* variants of malloc and register_finalizer,
294 * since that, too, can cause the underlying pointer to be offset.
297 object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*))
302 mono_raise_exception (mono_get_exception_argument_null ("obj"));
304 domain = obj->vtable->domain;
307 if (mono_domain_is_unloading (domain) && (callback != NULL))
309 * Can't register finalizers in a dying appdomain, since they
310 * could be invoked after the appdomain has been unloaded.
314 mono_domain_finalizers_lock (domain);
317 g_hash_table_insert (domain->finalizable_objects_hash, obj, obj);
319 g_hash_table_remove (domain->finalizable_objects_hash, obj);
321 mono_domain_finalizers_unlock (domain);
323 mono_gc_register_for_finalization (obj, callback);
324 #elif defined(HAVE_SGEN_GC)
326 * If we register finalizers for domains that are unloading we might
327 * end up running them while or after the domain is being cleared, so
328 * the objects will not be valid anymore.
330 if (!mono_domain_is_unloading (domain))
331 mono_gc_register_for_finalization (obj, callback);
336 * mono_object_register_finalizer:
337 * @obj: object to register
339 * Records that object @obj has a finalizer, this will call the
340 * Finalize method when the garbage collector disposes the object.
344 mono_object_register_finalizer (MonoObject *obj)
346 /* g_print ("Registered finalizer on %p %s.%s\n", obj, mono_object_class (obj)->name_space, mono_object_class (obj)->name); */
347 object_register_finalizer (obj, mono_gc_run_finalize);
351 * mono_domain_finalize:
352 * @domain: the domain to finalize
353 * @timeout: msects to wait for the finalization to complete, -1 to wait indefinitely
355 * Request finalization of all finalizable objects inside @domain. Wait
356 * @timeout msecs for the finalization to complete.
358 * Returns: TRUE if succeeded, FALSE if there was a timeout
362 mono_domain_finalize (MonoDomain *domain, guint32 timeout)
364 DomainFinalizationReq *req;
367 MonoInternalThread *thread = mono_thread_internal_current ();
369 #if defined(__native_client__)
373 if (mono_thread_internal_current () == gc_thread)
374 /* We are called from inside a finalizer, not much we can do here */
378 * No need to create another thread 'cause the finalizer thread
379 * is still working and will take care of running the finalizers
385 /* We don't support domain finalization without a GC */
386 if (mono_gc_is_null ())
389 mono_gc_collect (mono_gc_max_generation ());
391 done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
392 if (done_event == NULL) {
396 req = g_new0 (DomainFinalizationReq, 1);
397 req->domain = domain;
398 req->done_event = done_event;
400 if (domain == mono_get_root_domain ())
401 finalizing_root_domain = TRUE;
403 mono_finalizer_lock ();
405 domains_to_finalize = g_slist_append (domains_to_finalize, req);
407 mono_finalizer_unlock ();
409 /* Tell the finalizer thread to finalize this appdomain */
410 mono_gc_finalize_notify ();
416 res = guarded_wait (done_event, timeout, TRUE);
417 /* printf ("WAIT RES: %d.\n", res); */
419 if (res == WAIT_IO_COMPLETION) {
420 if ((thread->state & (ThreadState_StopRequested | ThreadState_SuspendRequested)) != 0)
422 } else if (res == WAIT_TIMEOUT) {
423 /* We leak the handle here */
430 CloseHandle (done_event);
432 if (domain == mono_get_root_domain ()) {
433 mono_thread_pool_cleanup ();
434 mono_gc_finalize_threadpool_threads ();
441 ves_icall_System_GC_InternalCollect (int generation)
443 mono_gc_collect (generation);
447 ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection)
450 mono_gc_collect (mono_gc_max_generation ());
451 return mono_gc_get_used_size ();
455 ves_icall_System_GC_KeepAlive (MonoObject *obj)
463 ves_icall_System_GC_ReRegisterForFinalize (MonoObject *obj)
465 MONO_CHECK_ARG_NULL (obj,);
467 object_register_finalizer (obj, mono_gc_run_finalize);
471 ves_icall_System_GC_SuppressFinalize (MonoObject *obj)
473 MONO_CHECK_ARG_NULL (obj,);
475 /* delegates have no finalizers, but we register them to deal with the
476 * unmanaged->managed trampoline. We don't let the user suppress it
477 * otherwise we'd leak it.
479 if (obj->vtable->klass->delegate)
482 /* FIXME: Need to handle case where obj has COM Callable Wrapper
483 * generated for it that needs cleaned up, but user wants to suppress
484 * their derived object finalizer. */
486 object_register_finalizer (obj, NULL);
490 ves_icall_System_GC_WaitForPendingFinalizers (void)
492 if (mono_gc_is_null ())
495 if (!mono_gc_pending_finalizers ())
498 if (mono_thread_internal_current () == gc_thread)
499 /* Avoid deadlocks */
503 If the finalizer thread is not live, lets pretend no finalizers are pending since the current thread might
504 be the one responsible for starting it up.
506 if (gc_thread == NULL)
509 ResetEvent (pending_done_event);
510 mono_gc_finalize_notify ();
511 /* g_print ("Waiting for pending finalizers....\n"); */
512 guarded_wait (pending_done_event, INFINITE, TRUE);
513 /* g_print ("Done pending....\n"); */
517 ves_icall_System_GC_register_ephemeron_array (MonoObject *array)
520 if (!mono_gc_ephemeron_array_add (array)) {
521 mono_set_pending_exception (mono_object_domain (array)->out_of_memory_ex);
528 ves_icall_System_GC_get_ephemeron_tombstone (void)
530 return mono_domain_get ()->ephemeron_tombstone;
533 #define mono_allocator_lock() mono_mutex_lock (&allocator_section)
534 #define mono_allocator_unlock() mono_mutex_unlock (&allocator_section)
535 static mono_mutex_t allocator_section;
536 static mono_mutex_t handle_section;
545 static HandleType mono_gchandle_get_type (guint32 gchandle);
548 ves_icall_System_GCHandle_GetTarget (guint32 handle)
550 return mono_gchandle_get_target (handle);
554 * if type == -1, change the target of the handle, otherwise allocate a new handle.
557 ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type)
560 mono_gchandle_set_target (handle, obj);
561 /* the handle doesn't change */
566 return mono_gchandle_new_weakref (obj, FALSE);
567 case HANDLE_WEAK_TRACK:
568 return mono_gchandle_new_weakref (obj, TRUE);
570 return mono_gchandle_new (obj, FALSE);
572 return mono_gchandle_new (obj, TRUE);
574 g_assert_not_reached ();
580 ves_icall_System_GCHandle_FreeHandle (guint32 handle)
582 mono_gchandle_free (handle);
586 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
590 if (mono_gchandle_get_type (handle) != HANDLE_PINNED)
592 obj = mono_gchandle_get_target (handle);
594 MonoClass *klass = mono_object_class (obj);
595 if (klass == mono_defaults.string_class) {
596 return mono_string_chars ((MonoString*)obj);
597 } else if (klass->rank) {
598 return mono_array_addr ((MonoArray*)obj, char, 0);
600 /* the C# code will check and throw the exception */
601 /* FIXME: missing !klass->blittable test, see bug #61134 */
602 if ((klass->flags & TYPE_ATTRIBUTE_LAYOUT_MASK) == TYPE_ATTRIBUTE_AUTO_LAYOUT)
604 return (char*)obj + sizeof (MonoObject);
611 ves_icall_Mono_Runtime_SetGCAllowSynchronousMajor (MonoBoolean flag)
613 return mono_gc_set_allow_synchronous_major (flag);
621 guint slot_hint : 24; /* starting slot for search */
622 /* 2^16 appdomains should be enough for everyone (though I know I'll regret this in 20 years) */
623 /* we alloc this only for weak refs, since we can get the domain directly in the other cases */
627 /* weak and weak-track arrays will be allocated in malloc memory
629 static HandleData gc_handles [] = {
630 {NULL, NULL, 0, HANDLE_WEAK, 0},
631 {NULL, NULL, 0, HANDLE_WEAK_TRACK, 0},
632 {NULL, NULL, 0, HANDLE_NORMAL, 0},
633 {NULL, NULL, 0, HANDLE_PINNED, 0}
636 #define lock_handles(handles) mono_mutex_lock (&handle_section)
637 #define unlock_handles(handles) mono_mutex_unlock (&handle_section)
640 find_first_unset (guint32 bitmap)
643 for (i = 0; i < 32; ++i) {
644 if (!(bitmap & (1 << i)))
651 make_root_descr_all_refs (int numbits, gboolean pinned)
657 return mono_gc_make_root_descr_all_refs (numbits);
661 alloc_handle (HandleData *handles, MonoObject *obj, gboolean track)
665 lock_handles (handles);
666 if (!handles->size) {
668 if (handles->type > HANDLE_WEAK_TRACK) {
669 handles->entries = mono_gc_alloc_fixed (sizeof (gpointer) * handles->size, make_root_descr_all_refs (handles->size, handles->type == HANDLE_PINNED));
671 handles->entries = g_malloc0 (sizeof (gpointer) * handles->size);
672 handles->domain_ids = g_malloc0 (sizeof (guint16) * handles->size);
674 handles->bitmap = g_malloc0 (handles->size / 8);
677 for (slot = handles->slot_hint; slot < handles->size / 32; ++slot) {
678 if (handles->bitmap [slot] != 0xffffffff) {
679 i = find_first_unset (handles->bitmap [slot]);
680 handles->slot_hint = slot;
684 if (i == -1 && handles->slot_hint != 0) {
685 for (slot = 0; slot < handles->slot_hint; ++slot) {
686 if (handles->bitmap [slot] != 0xffffffff) {
687 i = find_first_unset (handles->bitmap [slot]);
688 handles->slot_hint = slot;
695 guint32 new_size = handles->size * 2; /* always double: we memset to 0 based on this below */
697 /* resize and copy the bitmap */
698 new_bitmap = g_malloc0 (new_size / 8);
699 memcpy (new_bitmap, handles->bitmap, handles->size / 8);
700 g_free (handles->bitmap);
701 handles->bitmap = new_bitmap;
703 /* resize and copy the entries */
704 if (handles->type > HANDLE_WEAK_TRACK) {
707 entries = mono_gc_alloc_fixed (sizeof (gpointer) * new_size, make_root_descr_all_refs (new_size, handles->type == HANDLE_PINNED));
708 mono_gc_memmove_aligned (entries, handles->entries, sizeof (gpointer) * handles->size);
710 mono_gc_free_fixed (handles->entries);
711 handles->entries = entries;
715 domain_ids = g_malloc0 (sizeof (guint16) * new_size);
716 entries = g_malloc0 (sizeof (gpointer) * new_size);
717 memcpy (domain_ids, handles->domain_ids, sizeof (guint16) * handles->size);
718 for (i = 0; i < handles->size; ++i) {
719 MonoObject *obj = mono_gc_weak_link_get (&(handles->entries [i]));
721 mono_gc_weak_link_add (&(entries [i]), obj, track);
722 mono_gc_weak_link_remove (&(handles->entries [i]), track);
724 g_assert (!handles->entries [i]);
727 g_free (handles->entries);
728 g_free (handles->domain_ids);
729 handles->entries = entries;
730 handles->domain_ids = domain_ids;
733 /* set i and slot to the next free position */
735 slot = (handles->size + 1) / 32;
736 handles->slot_hint = handles->size + 1;
737 handles->size = new_size;
739 handles->bitmap [slot] |= 1 << i;
740 slot = slot * 32 + i;
741 handles->entries [slot] = NULL;
742 if (handles->type <= HANDLE_WEAK_TRACK) {
743 /*FIXME, what to use when obj == null?*/
744 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
746 mono_gc_weak_link_add (&(handles->entries [slot]), obj, track);
748 handles->entries [slot] = obj;
751 #ifndef DISABLE_PERFCOUNTERS
752 mono_perfcounters->gc_num_handles++;
754 unlock_handles (handles);
755 /*g_print ("allocated entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
756 res = (slot << 3) | (handles->type + 1);
757 mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_CREATED, handles->type, res, obj);
763 * @obj: managed object to get a handle for
764 * @pinned: whether the object should be pinned
766 * This returns a handle that wraps the object, this is used to keep a
767 * reference to a managed object from the unmanaged world and preventing the
768 * object from being disposed.
770 * If @pinned is false the address of the object can not be obtained, if it is
771 * true the address of the object can be obtained. This will also pin the
772 * object so it will not be possible by a moving garbage collector to move the
775 * Returns: a handle that can be used to access the object from
779 mono_gchandle_new (MonoObject *obj, gboolean pinned)
781 return alloc_handle (&gc_handles [pinned? HANDLE_PINNED: HANDLE_NORMAL], obj, FALSE);
785 * mono_gchandle_new_weakref:
786 * @obj: managed object to get a handle for
787 * @pinned: whether the object should be pinned
789 * This returns a weak handle that wraps the object, this is used to
790 * keep a reference to a managed object from the unmanaged world.
791 * Unlike the mono_gchandle_new the object can be reclaimed by the
792 * garbage collector. In this case the value of the GCHandle will be
795 * If @pinned is false the address of the object can not be obtained, if it is
796 * true the address of the object can be obtained. This will also pin the
797 * object so it will not be possible by a moving garbage collector to move the
800 * Returns: a handle that can be used to access the object from
804 mono_gchandle_new_weakref (MonoObject *obj, gboolean track_resurrection)
806 guint32 handle = alloc_handle (&gc_handles [track_resurrection? HANDLE_WEAK_TRACK: HANDLE_WEAK], obj, track_resurrection);
812 mono_gchandle_get_type (guint32 gchandle)
814 guint type = (gchandle & 7) - 1;
820 * mono_gchandle_get_target:
821 * @gchandle: a GCHandle's handle.
823 * The handle was previously created by calling mono_gchandle_new or
824 * mono_gchandle_new_weakref.
826 * Returns a pointer to the MonoObject represented by the handle or
827 * NULL for a collected object if using a weakref handle.
830 mono_gchandle_get_target (guint32 gchandle)
832 guint slot = gchandle >> 3;
833 guint type = (gchandle & 7) - 1;
834 HandleData *handles = &gc_handles [type];
835 MonoObject *obj = NULL;
838 lock_handles (handles);
839 if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
840 if (handles->type <= HANDLE_WEAK_TRACK) {
841 obj = mono_gc_weak_link_get (&handles->entries [slot]);
843 obj = handles->entries [slot];
846 /* print a warning? */
848 unlock_handles (handles);
849 /*g_print ("get target of entry %d of type %d: %p\n", slot, handles->type, obj);*/
854 mono_gchandle_set_target (guint32 gchandle, MonoObject *obj)
856 guint slot = gchandle >> 3;
857 guint type = (gchandle & 7) - 1;
858 HandleData *handles = &gc_handles [type];
862 lock_handles (handles);
863 if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
864 if (handles->type <= HANDLE_WEAK_TRACK) {
865 if (handles->entries [slot])
866 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
868 mono_gc_weak_link_add (&handles->entries [slot], obj, handles->type == HANDLE_WEAK_TRACK);
869 /*FIXME, what to use when obj == null?*/
870 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
872 handles->entries [slot] = obj;
875 /* print a warning? */
877 /*g_print ("changed entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
878 unlock_handles (handles);
882 * mono_gchandle_is_in_domain:
883 * @gchandle: a GCHandle's handle.
884 * @domain: An application domain.
886 * Returns: true if the object wrapped by the @gchandle belongs to the specific @domain.
889 mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain)
891 guint slot = gchandle >> 3;
892 guint type = (gchandle & 7) - 1;
893 HandleData *handles = &gc_handles [type];
894 gboolean result = FALSE;
897 lock_handles (handles);
898 if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
899 if (handles->type <= HANDLE_WEAK_TRACK) {
900 result = domain->domain_id == handles->domain_ids [slot];
903 obj = handles->entries [slot];
907 result = domain == mono_object_domain (obj);
910 /* print a warning? */
912 unlock_handles (handles);
917 * mono_gchandle_free:
918 * @gchandle: a GCHandle's handle.
920 * Frees the @gchandle handle. If there are no outstanding
921 * references, the garbage collector can reclaim the memory of the
925 mono_gchandle_free (guint32 gchandle)
927 guint slot = gchandle >> 3;
928 guint type = (gchandle & 7) - 1;
929 HandleData *handles = &gc_handles [type];
933 lock_handles (handles);
934 if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
935 if (handles->type <= HANDLE_WEAK_TRACK) {
936 if (handles->entries [slot])
937 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
939 handles->entries [slot] = NULL;
941 handles->bitmap [slot / 32] &= ~(1 << (slot % 32));
943 /* print a warning? */
945 #ifndef DISABLE_PERFCOUNTERS
946 mono_perfcounters->gc_num_handles--;
948 /*g_print ("freed entry %d of type %d\n", slot, handles->type);*/
949 unlock_handles (handles);
950 mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_DESTROYED, handles->type, gchandle, NULL);
954 * mono_gchandle_free_domain:
955 * @domain: domain that is unloading
957 * Function used internally to cleanup any GC handle for objects belonging
958 * to the specified domain during appdomain unload.
961 mono_gchandle_free_domain (MonoDomain *domain)
965 for (type = 0; type < 3; ++type) {
967 HandleData *handles = &gc_handles [type];
968 lock_handles (handles);
969 for (slot = 0; slot < handles->size; ++slot) {
970 if (!(handles->bitmap [slot / 32] & (1 << (slot % 32))))
972 if (type <= HANDLE_WEAK_TRACK) {
973 if (domain->domain_id == handles->domain_ids [slot]) {
974 handles->bitmap [slot / 32] &= ~(1 << (slot % 32));
975 if (handles->entries [slot])
976 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
979 if (handles->entries [slot] && mono_object_domain (handles->entries [slot]) == domain) {
980 handles->bitmap [slot / 32] &= ~(1 << (slot % 32));
981 handles->entries [slot] = NULL;
985 unlock_handles (handles);
991 mono_gc_GCHandle_CheckCurrentDomain (guint32 gchandle)
993 return mono_gchandle_is_in_domain (gchandle, mono_domain_get ());
996 #ifdef MONO_HAS_SEMAPHORES
997 static MonoSemType finalizer_sem;
999 static HANDLE finalizer_event;
1000 static volatile gboolean finished=FALSE;
1003 mono_gc_finalize_notify (void)
1006 g_message ( "%s: prodding finalizer", __func__);
1009 if (mono_gc_is_null ())
1012 #ifdef MONO_HAS_SEMAPHORES
1013 MONO_SEM_POST (&finalizer_sem);
1015 SetEvent (finalizer_event);
1019 #ifdef HAVE_BOEHM_GC
1022 collect_objects (gpointer key, gpointer value, gpointer user_data)
1024 GPtrArray *arr = (GPtrArray*)user_data;
1025 g_ptr_array_add (arr, key);
1031 * finalize_domain_objects:
1033 * Run the finalizers of all finalizable objects in req->domain.
1036 finalize_domain_objects (DomainFinalizationReq *req)
1038 MonoDomain *domain = req->domain;
1041 #define NUM_FOBJECTS 64
1042 MonoObject *to_finalize [NUM_FOBJECTS];
1046 /* Process finalizers which are already in the queue */
1047 mono_gc_invoke_finalizers ();
1049 #ifdef HAVE_BOEHM_GC
1050 while (g_hash_table_size (domain->finalizable_objects_hash) > 0) {
1054 * Since the domain is unloading, nobody is allowed to put
1055 * new entries into the hash table. But finalize_object might
1056 * remove entries from the hash table, so we make a copy.
1058 objs = g_ptr_array_new ();
1059 g_hash_table_foreach (domain->finalizable_objects_hash, collect_objects, objs);
1060 /* printf ("FINALIZING %d OBJECTS.\n", objs->len); */
1062 for (i = 0; i < objs->len; ++i) {
1063 MonoObject *o = (MonoObject*)g_ptr_array_index (objs, i);
1064 /* FIXME: Avoid finalizing threads, etc */
1065 mono_gc_run_finalize (o, 0);
1068 g_ptr_array_free (objs, TRUE);
1070 #elif defined(HAVE_SGEN_GC)
1071 while ((count = mono_gc_finalizers_for_domain (domain, to_finalize, NUM_FOBJECTS))) {
1073 for (i = 0; i < count; ++i) {
1074 mono_gc_run_finalize (to_finalize [i], 0);
1079 /* cleanup the reference queue */
1080 reference_queue_clear_for_domain (domain);
1082 /* printf ("DONE.\n"); */
1083 SetEvent (req->done_event);
1085 /* The event is closed in mono_domain_finalize if we get here */
1090 finalizer_thread (gpointer unused)
1092 gboolean wait = TRUE;
1095 /* Wait to be notified that there's at least one
1099 g_assert (mono_domain_get () == mono_get_root_domain ());
1100 mono_gc_set_skip_thread (TRUE);
1101 MONO_PREPARE_BLOCKING
1104 /* An alertable wait is required so this thread can be suspended on windows */
1105 #ifdef MONO_HAS_SEMAPHORES
1106 MONO_SEM_WAIT_ALERTABLE (&finalizer_sem, TRUE);
1108 WaitForSingleObjectEx (finalizer_event, INFINITE, TRUE);
1112 MONO_FINISH_BLOCKING
1113 mono_gc_set_skip_thread (FALSE);
1115 mono_threads_perform_thread_dump ();
1117 mono_console_handle_async_ops ();
1119 #ifndef DISABLE_ATTACH
1120 mono_attach_maybe_start ();
1123 if (domains_to_finalize) {
1124 mono_finalizer_lock ();
1125 if (domains_to_finalize) {
1126 DomainFinalizationReq *req = domains_to_finalize->data;
1127 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
1128 mono_finalizer_unlock ();
1130 finalize_domain_objects (req);
1132 mono_finalizer_unlock ();
1136 /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup),
1137 * before the domain is unloaded.
1139 mono_gc_invoke_finalizers ();
1141 mono_threads_join_threads ();
1143 reference_queue_proccess_all ();
1145 #ifdef MONO_HAS_SEMAPHORES
1146 /* Avoid posting the pending done event until there are pending finalizers */
1147 if (MONO_SEM_TIMEDWAIT (&finalizer_sem, 0) == 0)
1148 /* Don't wait again at the start of the loop */
1151 SetEvent (pending_done_event);
1153 SetEvent (pending_done_event);
1157 mono_finalizer_lock ();
1158 finalizer_thread_exited = TRUE;
1159 mono_cond_signal (&exited_cond);
1160 mono_finalizer_unlock ();
1165 #ifndef LAZY_GC_THREAD_CREATION
1169 mono_gc_init_finalizer_thread (void)
1171 gc_thread = mono_thread_create_internal (mono_domain_get (), finalizer_thread, NULL, FALSE, 0);
1172 ves_icall_System_Threading_Thread_SetName_internal (gc_thread, mono_string_new (mono_domain_get (), "Finalizer"));
1178 mono_mutex_init_recursive (&handle_section);
1179 mono_mutex_init_recursive (&allocator_section);
1181 mono_mutex_init_recursive (&finalizer_mutex);
1182 mono_mutex_init_recursive (&reference_queue_mutex);
1184 MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_NORMAL].entries);
1185 MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_PINNED].entries);
1187 mono_counters_register ("Minor GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.minor_gc_count);
1188 mono_counters_register ("Major GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.major_gc_count);
1189 mono_counters_register ("Minor GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.minor_gc_time);
1190 mono_counters_register ("Major GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time);
1191 mono_counters_register ("Major GC time concurrent", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time_concurrent);
1193 mono_gc_base_init ();
1195 if (mono_gc_is_disabled ()) {
1200 finalizer_event = CreateEvent (NULL, FALSE, FALSE, NULL);
1201 g_assert (finalizer_event);
1202 pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
1203 g_assert (pending_done_event);
1204 mono_cond_init (&exited_cond, 0);
1205 #ifdef MONO_HAS_SEMAPHORES
1206 MONO_SEM_INIT (&finalizer_sem, 0);
1209 #ifndef LAZY_GC_THREAD_CREATION
1210 mono_gc_init_finalizer_thread ();
1215 mono_gc_cleanup (void)
1218 g_message ("%s: cleaning up finalizer", __func__);
1221 if (mono_gc_is_null ())
1226 if (mono_thread_internal_current () != gc_thread) {
1227 gboolean timed_out = FALSE;
1228 guint32 start_ticks = mono_msec_ticks ();
1229 guint32 end_ticks = start_ticks + 2000;
1231 mono_gc_finalize_notify ();
1232 /* Finishing the finalizer thread, so wait a little bit... */
1233 /* MS seems to wait for about 2 seconds */
1234 while (!finalizer_thread_exited) {
1235 guint32 current_ticks = mono_msec_ticks ();
1238 if (current_ticks >= end_ticks)
1241 timeout = end_ticks - current_ticks;
1242 MONO_PREPARE_BLOCKING;
1243 mono_finalizer_lock ();
1244 if (!finalizer_thread_exited)
1245 mono_cond_timedwait_ms (&exited_cond, &finalizer_mutex, timeout);
1246 mono_finalizer_unlock ();
1247 MONO_FINISH_BLOCKING;
1250 if (!finalizer_thread_exited) {
1253 /* Set a flag which the finalizer thread can check */
1254 suspend_finalizers = TRUE;
1256 /* Try to abort the thread, in the hope that it is running managed code */
1257 mono_thread_internal_stop (gc_thread);
1259 /* Wait for it to stop */
1260 ret = guarded_wait (gc_thread->handle, 100, TRUE);
1262 if (ret == WAIT_TIMEOUT) {
1264 * The finalizer thread refused to die. There is not much we
1265 * can do here, since the runtime is shutting down so the
1266 * state the finalizer thread depends on will vanish.
1268 g_warning ("Shutting down finalizer thread timed out.");
1276 /* Wait for the thread to actually exit */
1277 ret = guarded_wait (gc_thread->handle, INFINITE, TRUE);
1278 g_assert (ret == WAIT_OBJECT_0);
1280 mono_thread_join (GUINT_TO_POINTER (gc_thread->tid));
1284 #ifdef HAVE_BOEHM_GC
1285 GC_finalizer_notifier = NULL;
1289 mono_reference_queue_cleanup ();
1291 mono_mutex_destroy (&handle_section);
1292 mono_mutex_destroy (&allocator_section);
1293 mono_mutex_destroy (&finalizer_mutex);
1294 mono_mutex_destroy (&reference_queue_mutex);
1298 mono_gc_is_finalizer_internal_thread (MonoInternalThread *thread)
1300 return thread == gc_thread;
1304 * mono_gc_is_finalizer_thread:
1305 * @thread: the thread to test.
1307 * In Mono objects are finalized asynchronously on a separate thread.
1308 * This routine tests whether the @thread argument represents the
1309 * finalization thread.
1311 * Returns true if @thread is the finalization thread.
1314 mono_gc_is_finalizer_thread (MonoThread *thread)
1316 return mono_gc_is_finalizer_internal_thread (thread->internal_thread);
1319 #if defined(__MACH__)
1320 static pthread_t mach_exception_thread;
1323 mono_gc_register_mach_exception_thread (pthread_t thread)
1325 mach_exception_thread = thread;
1329 mono_gc_get_mach_exception_thread (void)
1331 return mach_exception_thread;
1335 #ifndef HAVE_SGEN_GC
1337 mono_gc_alloc_mature (MonoVTable *vtable)
1339 return mono_object_new_specific (vtable);
1344 static MonoReferenceQueue *ref_queues;
1347 ref_list_remove_element (RefQueueEntry **prev, RefQueueEntry *element)
1350 /* Guard if head is changed concurrently. */
1351 while (*prev != element)
1352 prev = &(*prev)->next;
1353 } while (prev && InterlockedCompareExchangePointer ((void*)prev, element->next, element) != element);
1357 ref_list_push (RefQueueEntry **head, RefQueueEntry *value)
1359 RefQueueEntry *current;
1362 value->next = current;
1363 STORE_STORE_FENCE; /*Must make sure the previous store is visible before the CAS. */
1364 } while (InterlockedCompareExchangePointer ((void*)head, value, current) != current);
1368 reference_queue_proccess (MonoReferenceQueue *queue)
1370 RefQueueEntry **iter = &queue->queue;
1371 RefQueueEntry *entry;
1372 while ((entry = *iter)) {
1374 if (queue->should_be_deleted || !mono_gc_weak_link_get (&entry->dis_link)) {
1375 mono_gc_weak_link_remove (&entry->dis_link, TRUE);
1377 if (queue->should_be_deleted || !mono_gchandle_get_target (entry->gchandle)) {
1378 mono_gchandle_free ((guint32)entry->gchandle);
1380 ref_list_remove_element (iter, entry);
1381 queue->callback (entry->user_data);
1384 iter = &entry->next;
1390 reference_queue_proccess_all (void)
1392 MonoReferenceQueue **iter;
1393 MonoReferenceQueue *queue = ref_queues;
1394 for (; queue; queue = queue->next)
1395 reference_queue_proccess (queue);
1398 mono_mutex_lock (&reference_queue_mutex);
1399 for (iter = &ref_queues; *iter;) {
1401 if (!queue->should_be_deleted) {
1402 iter = &queue->next;
1406 mono_mutex_unlock (&reference_queue_mutex);
1407 reference_queue_proccess (queue);
1410 *iter = queue->next;
1413 mono_mutex_unlock (&reference_queue_mutex);
1417 mono_reference_queue_cleanup (void)
1419 MonoReferenceQueue *queue = ref_queues;
1420 for (; queue; queue = queue->next)
1421 queue->should_be_deleted = TRUE;
1422 reference_queue_proccess_all ();
1426 reference_queue_clear_for_domain (MonoDomain *domain)
1428 MonoReferenceQueue *queue = ref_queues;
1429 for (; queue; queue = queue->next) {
1430 RefQueueEntry **iter = &queue->queue;
1431 RefQueueEntry *entry;
1432 while ((entry = *iter)) {
1433 if (entry->domain == domain) {
1435 mono_gc_weak_link_remove (&entry->dis_link, TRUE);
1437 mono_gchandle_free ((guint32)entry->gchandle);
1439 ref_list_remove_element (iter, entry);
1440 queue->callback (entry->user_data);
1443 iter = &entry->next;
1449 * mono_gc_reference_queue_new:
1450 * @callback callback used when processing collected entries.
1452 * Create a new reference queue used to process collected objects.
1453 * A reference queue let you add a pair of (managed object, user data)
1454 * using the mono_gc_reference_queue_add method.
1456 * Once the managed object is collected @callback will be called
1457 * in the finalizer thread with 'user data' as argument.
1459 * The callback is called from the finalizer thread without any locks held.
1460 * When a AppDomain is unloaded, all callbacks for objects belonging to it
1463 * @returns the new queue.
1466 mono_gc_reference_queue_new (mono_reference_queue_callback callback)
1468 MonoReferenceQueue *res = g_new0 (MonoReferenceQueue, 1);
1469 res->callback = callback;
1471 mono_mutex_lock (&reference_queue_mutex);
1472 res->next = ref_queues;
1474 mono_mutex_unlock (&reference_queue_mutex);
1480 * mono_gc_reference_queue_add:
1481 * @queue the queue to add the reference to.
1482 * @obj the object to be watched for collection
1483 * @user_data parameter to be passed to the queue callback
1485 * Queue an object to be watched for collection, when the @obj is
1486 * collected, the callback that was registered for the @queue will
1487 * be invoked with @user_data as argument.
1489 * @returns false if the queue is scheduled to be freed.
1492 mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *user_data)
1494 RefQueueEntry *entry;
1495 if (queue->should_be_deleted)
1498 entry = g_new0 (RefQueueEntry, 1);
1499 entry->user_data = user_data;
1500 entry->domain = mono_object_domain (obj);
1503 mono_gc_weak_link_add (&entry->dis_link, obj, TRUE);
1505 entry->gchandle = mono_gchandle_new_weakref (obj, TRUE);
1506 mono_object_register_finalizer (obj);
1509 ref_list_push (&queue->queue, entry);
1514 * mono_gc_reference_queue_free:
1515 * @queue the queue that should be freed.
1517 * This operation signals that @queue should be freed. This operation is deferred
1518 * as it happens on the finalizer thread.
1520 * After this call, no further objects can be queued. It's the responsibility of the
1521 * caller to make sure that no further attempt to access queue will be made.
1524 mono_gc_reference_queue_free (MonoReferenceQueue *queue)
1526 queue->should_be_deleted = TRUE;