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, MONO_ROOT_SOURCE_FINALIZER_QUEUE, "finalizable threads list");
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 // This function is called from the innards of the GC, so our best alternative for now is to do polling here
124 MONO_SUSPEND_CHECK ();
126 o = (MonoObject*)((char*)obj + GPOINTER_TO_UINT (data));
129 g_log ("mono-gc-finalizers", G_LOG_LEVEL_DEBUG, "<%s at %p> Starting finalizer checks.", o->vtable->klass->name, o);
131 if (suspend_finalizers)
134 domain = o->vtable->domain;
137 mono_domain_finalizers_lock (domain);
139 o2 = g_hash_table_lookup (domain->finalizable_objects_hash, o);
141 mono_domain_finalizers_unlock (domain);
144 /* Already finalized somehow */
148 /* make sure the finalizer is not called again if the object is resurrected */
149 object_register_finalizer (obj, NULL);
152 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Registered finalizer as processed.", o->vtable->klass->name, o);
154 if (o->vtable->klass == mono_defaults.internal_thread_class) {
155 MonoInternalThread *t = (MonoInternalThread*)o;
157 if (mono_gc_is_finalizer_internal_thread (t))
158 /* Avoid finalizing ourselves */
161 if (t->threadpool_thread && finalizing_root_domain) {
162 /* Don't finalize threadpool threads when
163 shutting down - they're finalized when the
164 threadpool shuts down. */
165 add_thread_to_finalize (t);
170 if (o->vtable->klass->image == mono_defaults.corlib && !strcmp (o->vtable->klass->name, "DynamicMethod") && finalizing_root_domain) {
172 * These can't be finalized during unloading/shutdown, since that would
173 * free the native code which can still be referenced by other
175 * FIXME: This is not perfect, objects dying at the same time as
176 * dynamic methods can still reference them even when !shutdown.
181 if (mono_runtime_get_no_exec ())
184 /* speedup later... and use a timeout */
185 /* g_print ("Finalize run on %p %s.%s\n", o, mono_object_class (o)->name_space, mono_object_class (o)->name); */
187 /* Use _internal here, since this thread can enter a doomed appdomain */
188 mono_domain_set_internal (mono_object_domain (o));
190 /* delegates that have a native function pointer allocated are
191 * registered for finalization, but they don't have a Finalize
192 * method, because in most cases it's not needed and it's just a waste.
194 if (o->vtable->klass->delegate) {
195 MonoDelegate* del = (MonoDelegate*)o;
196 if (del->delegate_trampoline)
197 mono_delegate_free_ftnptr ((MonoDelegate*)o);
198 mono_domain_set_internal (caller_domain);
202 finalizer = mono_class_get_finalizer (o->vtable->klass);
204 /* If object has a CCW but has no finalizer, it was only
205 * registered for finalization in order to free the CCW.
206 * Else it needs the regular finalizer run.
207 * FIXME: what to do about ressurection and suppression
208 * of finalizer on object with CCW.
210 if (mono_marshal_free_ccw (o) && !finalizer) {
211 mono_domain_set_internal (caller_domain);
216 * To avoid the locking plus the other overhead of mono_runtime_invoke (),
217 * create and precompile a wrapper which calls the finalize method using
221 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Compiling finalizer.", o->vtable->klass->name, o);
223 if (!domain->finalize_runtime_invoke) {
224 MonoMethod *invoke = mono_marshal_get_runtime_invoke (mono_class_get_method_from_name_flags (mono_defaults.object_class, "Finalize", 0, 0), TRUE);
226 domain->finalize_runtime_invoke = mono_compile_method (invoke);
229 runtime_invoke = domain->finalize_runtime_invoke;
231 mono_runtime_class_init (o->vtable);
233 if (G_UNLIKELY (MONO_GC_FINALIZE_INVOKE_ENABLED ())) {
234 MONO_GC_FINALIZE_INVOKE ((unsigned long)o, mono_object_get_size (o),
235 o->vtable->klass->name_space, o->vtable->klass->name);
239 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Calling finalizer.", o->vtable->klass->name, o);
241 runtime_invoke (o, NULL, &exc, NULL);
244 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Returned from finalizer.", o->vtable->klass->name, o);
247 mono_thread_internal_unhandled_exception (exc);
249 mono_domain_set_internal (caller_domain);
253 mono_gc_finalize_threadpool_threads (void)
255 while (threads_to_finalize) {
256 MonoInternalThread *thread = (MonoInternalThread*) mono_mlist_get_data (threads_to_finalize);
258 /* Force finalization of the thread. */
259 thread->threadpool_thread = FALSE;
260 mono_object_register_finalizer ((MonoObject*)thread);
262 mono_gc_run_finalize (thread, NULL);
264 threads_to_finalize = mono_mlist_next (threads_to_finalize);
269 mono_gc_out_of_memory (size_t size)
272 * we could allocate at program startup some memory that we could release
273 * back to the system at this point if we're really low on memory (ie, size is
274 * lower than the memory we set apart)
276 mono_raise_exception (mono_domain_get ()->out_of_memory_ex);
282 * Some of our objects may point to a different address than the address returned by GC_malloc()
283 * (because of the GetHashCode hack), but we need to pass the real address to register_finalizer.
284 * This also means that in the callback we need to adjust the pointer to get back the real
286 * We also need to be consistent in the use of the GC_debug* variants of malloc and register_finalizer,
287 * since that, too, can cause the underlying pointer to be offset.
290 object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*))
295 mono_raise_exception (mono_get_exception_argument_null ("obj"));
297 domain = obj->vtable->domain;
300 if (mono_domain_is_unloading (domain) && (callback != NULL))
302 * Can't register finalizers in a dying appdomain, since they
303 * could be invoked after the appdomain has been unloaded.
307 mono_domain_finalizers_lock (domain);
310 g_hash_table_insert (domain->finalizable_objects_hash, obj, obj);
312 g_hash_table_remove (domain->finalizable_objects_hash, obj);
314 mono_domain_finalizers_unlock (domain);
316 mono_gc_register_for_finalization (obj, callback);
317 #elif defined(HAVE_SGEN_GC)
319 * If we register finalizers for domains that are unloading we might
320 * end up running them while or after the domain is being cleared, so
321 * the objects will not be valid anymore.
323 if (!mono_domain_is_unloading (domain)) {
325 mono_gc_register_for_finalization (obj, callback);
326 MONO_FINISH_TRY_BLOCKING;
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_threadpool_ms_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 ResetEvent (pending_done_event);
506 mono_gc_finalize_notify ();
507 /* g_print ("Waiting for pending finalizers....\n"); */
508 guarded_wait (pending_done_event, INFINITE, TRUE);
509 /* g_print ("Done pending....\n"); */
513 ves_icall_System_GC_register_ephemeron_array (MonoObject *array)
516 if (!mono_gc_ephemeron_array_add (array)) {
517 mono_set_pending_exception (mono_object_domain (array)->out_of_memory_ex);
524 ves_icall_System_GC_get_ephemeron_tombstone (void)
526 return mono_domain_get ()->ephemeron_tombstone;
529 #define mono_allocator_lock() mono_mutex_lock (&allocator_section)
530 #define mono_allocator_unlock() mono_mutex_unlock (&allocator_section)
531 static mono_mutex_t allocator_section;
532 static mono_mutex_t handle_section;
541 static HandleType mono_gchandle_get_type (guint32 gchandle);
544 ves_icall_System_GCHandle_GetTarget (guint32 handle)
546 return mono_gchandle_get_target (handle);
550 * if type == -1, change the target of the handle, otherwise allocate a new handle.
553 ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type)
556 mono_gchandle_set_target (handle, obj);
557 /* the handle doesn't change */
562 return mono_gchandle_new_weakref (obj, FALSE);
563 case HANDLE_WEAK_TRACK:
564 return mono_gchandle_new_weakref (obj, TRUE);
566 return mono_gchandle_new (obj, FALSE);
568 return mono_gchandle_new (obj, TRUE);
570 g_assert_not_reached ();
576 ves_icall_System_GCHandle_FreeHandle (guint32 handle)
578 mono_gchandle_free (handle);
582 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
586 if (mono_gchandle_get_type (handle) != HANDLE_PINNED)
588 obj = mono_gchandle_get_target (handle);
590 MonoClass *klass = mono_object_class (obj);
591 if (klass == mono_defaults.string_class) {
592 return mono_string_chars ((MonoString*)obj);
593 } else if (klass->rank) {
594 return mono_array_addr ((MonoArray*)obj, char, 0);
596 /* the C# code will check and throw the exception */
597 /* FIXME: missing !klass->blittable test, see bug #61134 */
598 if ((klass->flags & TYPE_ATTRIBUTE_LAYOUT_MASK) == TYPE_ATTRIBUTE_AUTO_LAYOUT)
600 return (char*)obj + sizeof (MonoObject);
607 ves_icall_Mono_Runtime_SetGCAllowSynchronousMajor (MonoBoolean flag)
609 return mono_gc_set_allow_synchronous_major (flag);
617 guint slot_hint : 24; /* starting slot for search */
618 /* 2^16 appdomains should be enough for everyone (though I know I'll regret this in 20 years) */
619 /* we alloc this only for weak refs, since we can get the domain directly in the other cases */
623 /* weak and weak-track arrays will be allocated in malloc memory
625 static HandleData gc_handles [] = {
626 {NULL, NULL, 0, HANDLE_WEAK, 0},
627 {NULL, NULL, 0, HANDLE_WEAK_TRACK, 0},
628 {NULL, NULL, 0, HANDLE_NORMAL, 0},
629 {NULL, NULL, 0, HANDLE_PINNED, 0}
632 #define lock_handles(handles) do { \
634 mono_mutex_lock (&handle_section); \
635 MONO_FINISH_TRY_BLOCKING; \
638 #define unlock_handles(handles) mono_mutex_unlock (&handle_section)
641 find_first_unset (guint32 bitmap)
644 for (i = 0; i < 32; ++i) {
645 if (!(bitmap & (1 << i)))
651 static MonoGCDescriptor
652 make_root_descr_all_refs (int numbits, gboolean pinned)
656 return MONO_GC_DESCRIPTOR_NULL;
658 return mono_gc_make_root_descr_all_refs (numbits);
662 alloc_handle (HandleData *handles, MonoObject *obj, gboolean track)
666 lock_handles (handles);
667 if (!handles->size) {
669 if (handles->type > HANDLE_WEAK_TRACK) {
670 handles->entries = mono_gc_alloc_fixed (sizeof (gpointer) * handles->size, make_root_descr_all_refs (handles->size, handles->type == HANDLE_PINNED), MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
672 handles->entries = g_malloc0 (sizeof (gpointer) * handles->size);
673 handles->domain_ids = g_malloc0 (sizeof (guint16) * handles->size);
675 handles->bitmap = g_malloc0 (handles->size / 8);
678 for (slot = handles->slot_hint; slot < handles->size / 32; ++slot) {
679 if (handles->bitmap [slot] != 0xffffffff) {
680 i = find_first_unset (handles->bitmap [slot]);
681 handles->slot_hint = slot;
685 if (i == -1 && handles->slot_hint != 0) {
686 for (slot = 0; slot < handles->slot_hint; ++slot) {
687 if (handles->bitmap [slot] != 0xffffffff) {
688 i = find_first_unset (handles->bitmap [slot]);
689 handles->slot_hint = slot;
696 guint32 new_size = handles->size * 2; /* always double: we memset to 0 based on this below */
698 /* resize and copy the bitmap */
699 new_bitmap = g_malloc0 (new_size / 8);
700 memcpy (new_bitmap, handles->bitmap, handles->size / 8);
701 g_free (handles->bitmap);
702 handles->bitmap = new_bitmap;
704 /* resize and copy the entries */
705 if (handles->type > HANDLE_WEAK_TRACK) {
708 entries = mono_gc_alloc_fixed (sizeof (gpointer) * new_size, make_root_descr_all_refs (new_size, handles->type == HANDLE_PINNED), MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
709 mono_gc_memmove_aligned (entries, handles->entries, sizeof (gpointer) * handles->size);
711 mono_gc_free_fixed (handles->entries);
712 handles->entries = entries;
716 domain_ids = g_malloc0 (sizeof (guint16) * new_size);
717 entries = g_malloc0 (sizeof (gpointer) * new_size);
718 memcpy (domain_ids, handles->domain_ids, sizeof (guint16) * handles->size);
719 for (i = 0; i < handles->size; ++i) {
720 MonoObject *obj = mono_gc_weak_link_get (&(handles->entries [i]));
722 mono_gc_weak_link_add (&(entries [i]), obj, track);
723 mono_gc_weak_link_remove (&(handles->entries [i]), track);
725 g_assert (!handles->entries [i]);
728 g_free (handles->entries);
729 g_free (handles->domain_ids);
730 handles->entries = entries;
731 handles->domain_ids = domain_ids;
734 /* set i and slot to the next free position */
736 slot = (handles->size + 1) / 32;
737 handles->slot_hint = handles->size + 1;
738 handles->size = new_size;
740 handles->bitmap [slot] |= 1 << i;
741 slot = slot * 32 + i;
742 handles->entries [slot] = NULL;
743 if (handles->type <= HANDLE_WEAK_TRACK) {
744 /*FIXME, what to use when obj == null?*/
745 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
747 mono_gc_weak_link_add (&(handles->entries [slot]), obj, track);
749 handles->entries [slot] = obj;
752 #ifndef DISABLE_PERFCOUNTERS
753 mono_perfcounters->gc_num_handles++;
755 unlock_handles (handles);
756 /*g_print ("allocated entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
757 res = (slot << 3) | (handles->type + 1);
758 mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_CREATED, handles->type, res, obj);
764 * @obj: managed object to get a handle for
765 * @pinned: whether the object should be pinned
767 * This returns a handle that wraps the object, this is used to keep a
768 * reference to a managed object from the unmanaged world and preventing the
769 * object from being disposed.
771 * If @pinned is false the address of the object can not be obtained, if it is
772 * true the address of the object can be obtained. This will also pin the
773 * object so it will not be possible by a moving garbage collector to move the
776 * Returns: a handle that can be used to access the object from
780 mono_gchandle_new (MonoObject *obj, gboolean pinned)
782 return alloc_handle (&gc_handles [pinned? HANDLE_PINNED: HANDLE_NORMAL], obj, FALSE);
786 * mono_gchandle_new_weakref:
787 * @obj: managed object to get a handle for
788 * @pinned: whether the object should be pinned
790 * This returns a weak handle that wraps the object, this is used to
791 * keep a reference to a managed object from the unmanaged world.
792 * Unlike the mono_gchandle_new the object can be reclaimed by the
793 * garbage collector. In this case the value of the GCHandle will be
796 * If @pinned is false the address of the object can not be obtained, if it is
797 * true the address of the object can be obtained. This will also pin the
798 * object so it will not be possible by a moving garbage collector to move the
801 * Returns: a handle that can be used to access the object from
805 mono_gchandle_new_weakref (MonoObject *obj, gboolean track_resurrection)
807 guint32 handle = alloc_handle (&gc_handles [track_resurrection? HANDLE_WEAK_TRACK: HANDLE_WEAK], obj, track_resurrection);
813 mono_gchandle_get_type (guint32 gchandle)
815 guint type = (gchandle & 7) - 1;
821 * mono_gchandle_get_target:
822 * @gchandle: a GCHandle's handle.
824 * The handle was previously created by calling mono_gchandle_new or
825 * mono_gchandle_new_weakref.
827 * Returns a pointer to the MonoObject represented by the handle or
828 * NULL for a collected object if using a weakref handle.
831 mono_gchandle_get_target (guint32 gchandle)
833 guint slot = gchandle >> 3;
834 guint type = (gchandle & 7) - 1;
835 HandleData *handles = &gc_handles [type];
836 MonoObject *obj = NULL;
839 lock_handles (handles);
840 if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
841 if (handles->type <= HANDLE_WEAK_TRACK) {
842 obj = mono_gc_weak_link_get (&handles->entries [slot]);
844 obj = handles->entries [slot];
847 /* print a warning? */
849 unlock_handles (handles);
850 /*g_print ("get target of entry %d of type %d: %p\n", slot, handles->type, obj);*/
855 mono_gchandle_set_target (guint32 gchandle, MonoObject *obj)
857 guint slot = gchandle >> 3;
858 guint type = (gchandle & 7) - 1;
859 HandleData *handles = &gc_handles [type];
863 lock_handles (handles);
864 if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
865 if (handles->type <= HANDLE_WEAK_TRACK) {
866 if (handles->entries [slot])
867 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
869 mono_gc_weak_link_add (&handles->entries [slot], obj, handles->type == HANDLE_WEAK_TRACK);
870 /*FIXME, what to use when obj == null?*/
871 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
873 handles->entries [slot] = obj;
876 /* print a warning? */
878 /*g_print ("changed entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
879 unlock_handles (handles);
883 * mono_gchandle_is_in_domain:
884 * @gchandle: a GCHandle's handle.
885 * @domain: An application domain.
887 * Returns: true if the object wrapped by the @gchandle belongs to the specific @domain.
890 mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain)
892 guint slot = gchandle >> 3;
893 guint type = (gchandle & 7) - 1;
894 HandleData *handles = &gc_handles [type];
895 gboolean result = FALSE;
898 lock_handles (handles);
899 if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
900 if (handles->type <= HANDLE_WEAK_TRACK) {
901 result = domain->domain_id == handles->domain_ids [slot];
904 obj = handles->entries [slot];
908 result = domain == mono_object_domain (obj);
911 /* print a warning? */
913 unlock_handles (handles);
918 * mono_gchandle_free:
919 * @gchandle: a GCHandle's handle.
921 * Frees the @gchandle handle. If there are no outstanding
922 * references, the garbage collector can reclaim the memory of the
926 mono_gchandle_free (guint32 gchandle)
928 guint slot = gchandle >> 3;
929 guint type = (gchandle & 7) - 1;
930 HandleData *handles = &gc_handles [type];
934 lock_handles (handles);
935 if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
936 if (handles->type <= HANDLE_WEAK_TRACK) {
937 if (handles->entries [slot])
938 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
940 handles->entries [slot] = NULL;
942 handles->bitmap [slot / 32] &= ~(1 << (slot % 32));
944 /* print a warning? */
946 #ifndef DISABLE_PERFCOUNTERS
947 mono_perfcounters->gc_num_handles--;
949 /*g_print ("freed entry %d of type %d\n", slot, handles->type);*/
950 unlock_handles (handles);
951 mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_DESTROYED, handles->type, gchandle, NULL);
955 * mono_gchandle_free_domain:
956 * @domain: domain that is unloading
958 * Function used internally to cleanup any GC handle for objects belonging
959 * to the specified domain during appdomain unload.
962 mono_gchandle_free_domain (MonoDomain *domain)
966 for (type = 0; type < 3; ++type) {
968 HandleData *handles = &gc_handles [type];
969 lock_handles (handles);
970 for (slot = 0; slot < handles->size; ++slot) {
971 if (!(handles->bitmap [slot / 32] & (1 << (slot % 32))))
973 if (type <= HANDLE_WEAK_TRACK) {
974 if (domain->domain_id == handles->domain_ids [slot]) {
975 handles->bitmap [slot / 32] &= ~(1 << (slot % 32));
976 if (handles->entries [slot])
977 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
980 if (handles->entries [slot] && mono_object_domain (handles->entries [slot]) == domain) {
981 handles->bitmap [slot / 32] &= ~(1 << (slot % 32));
982 handles->entries [slot] = NULL;
986 unlock_handles (handles);
992 mono_gc_GCHandle_CheckCurrentDomain (guint32 gchandle)
994 return mono_gchandle_is_in_domain (gchandle, mono_domain_get ());
997 #ifdef MONO_HAS_SEMAPHORES
998 static MonoSemType finalizer_sem;
1000 static HANDLE finalizer_event;
1001 static volatile gboolean finished=FALSE;
1004 mono_gc_finalize_notify (void)
1007 g_message ( "%s: prodding finalizer", __func__);
1010 if (mono_gc_is_null ())
1013 #ifdef MONO_HAS_SEMAPHORES
1014 MONO_SEM_POST (&finalizer_sem);
1016 SetEvent (finalizer_event);
1020 #ifdef HAVE_BOEHM_GC
1023 collect_objects (gpointer key, gpointer value, gpointer user_data)
1025 GPtrArray *arr = (GPtrArray*)user_data;
1026 g_ptr_array_add (arr, key);
1032 * finalize_domain_objects:
1034 * Run the finalizers of all finalizable objects in req->domain.
1037 finalize_domain_objects (DomainFinalizationReq *req)
1039 MonoDomain *domain = req->domain;
1042 #define NUM_FOBJECTS 64
1043 MonoObject *to_finalize [NUM_FOBJECTS];
1047 /* Process finalizers which are already in the queue */
1048 mono_gc_invoke_finalizers ();
1050 #ifdef HAVE_BOEHM_GC
1051 while (g_hash_table_size (domain->finalizable_objects_hash) > 0) {
1055 * Since the domain is unloading, nobody is allowed to put
1056 * new entries into the hash table. But finalize_object might
1057 * remove entries from the hash table, so we make a copy.
1059 objs = g_ptr_array_new ();
1060 g_hash_table_foreach (domain->finalizable_objects_hash, collect_objects, objs);
1061 /* printf ("FINALIZING %d OBJECTS.\n", objs->len); */
1063 for (i = 0; i < objs->len; ++i) {
1064 MonoObject *o = (MonoObject*)g_ptr_array_index (objs, i);
1065 /* FIXME: Avoid finalizing threads, etc */
1066 mono_gc_run_finalize (o, 0);
1069 g_ptr_array_free (objs, TRUE);
1071 #elif defined(HAVE_SGEN_GC)
1072 while ((count = mono_gc_finalizers_for_domain (domain, to_finalize, NUM_FOBJECTS))) {
1074 for (i = 0; i < count; ++i) {
1075 mono_gc_run_finalize (to_finalize [i], 0);
1080 /* cleanup the reference queue */
1081 reference_queue_clear_for_domain (domain);
1083 /* printf ("DONE.\n"); */
1084 SetEvent (req->done_event);
1086 /* The event is closed in mono_domain_finalize if we get here */
1091 finalizer_thread (gpointer unused)
1093 gboolean wait = TRUE;
1096 /* Wait to be notified that there's at least one
1100 g_assert (mono_domain_get () == mono_get_root_domain ());
1101 mono_gc_set_skip_thread (TRUE);
1102 MONO_PREPARE_BLOCKING;
1105 /* An alertable wait is required so this thread can be suspended on windows */
1106 #ifdef MONO_HAS_SEMAPHORES
1107 MONO_SEM_WAIT_ALERTABLE (&finalizer_sem, TRUE);
1109 WaitForSingleObjectEx (finalizer_event, INFINITE, TRUE);
1113 MONO_FINISH_BLOCKING;
1114 mono_gc_set_skip_thread (FALSE);
1116 mono_threads_perform_thread_dump ();
1118 mono_console_handle_async_ops ();
1120 mono_attach_maybe_start ();
1122 if (domains_to_finalize) {
1123 mono_finalizer_lock ();
1124 if (domains_to_finalize) {
1125 DomainFinalizationReq *req = domains_to_finalize->data;
1126 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
1127 mono_finalizer_unlock ();
1129 finalize_domain_objects (req);
1131 mono_finalizer_unlock ();
1135 /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup),
1136 * before the domain is unloaded.
1138 mono_gc_invoke_finalizers ();
1140 mono_threads_join_threads ();
1142 reference_queue_proccess_all ();
1144 #ifdef MONO_HAS_SEMAPHORES
1145 /* Avoid posting the pending done event until there are pending finalizers */
1146 if (MONO_SEM_TIMEDWAIT (&finalizer_sem, 0) == 0)
1147 /* Don't wait again at the start of the loop */
1150 SetEvent (pending_done_event);
1152 SetEvent (pending_done_event);
1156 mono_finalizer_lock ();
1157 finalizer_thread_exited = TRUE;
1158 mono_cond_signal (&exited_cond);
1159 mono_finalizer_unlock ();
1164 #ifndef LAZY_GC_THREAD_CREATION
1168 mono_gc_init_finalizer_thread (void)
1170 gc_thread = mono_thread_create_internal (mono_domain_get (), finalizer_thread, NULL, FALSE, 0);
1171 ves_icall_System_Threading_Thread_SetName_internal (gc_thread, mono_string_new (mono_domain_get (), "Finalizer"));
1177 mono_mutex_init_recursive (&handle_section);
1178 mono_mutex_init_recursive (&allocator_section);
1180 mono_mutex_init_recursive (&finalizer_mutex);
1181 mono_mutex_init_recursive (&reference_queue_mutex);
1183 MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_NORMAL].entries, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
1184 MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_PINNED].entries, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
1186 mono_counters_register ("Minor GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.minor_gc_count);
1187 mono_counters_register ("Major GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.major_gc_count);
1188 mono_counters_register ("Minor GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.minor_gc_time);
1189 mono_counters_register ("Major GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time);
1190 mono_counters_register ("Major GC time concurrent", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time_concurrent);
1192 mono_gc_base_init ();
1194 if (mono_gc_is_disabled ()) {
1199 finalizer_event = CreateEvent (NULL, FALSE, FALSE, NULL);
1200 g_assert (finalizer_event);
1201 pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
1202 g_assert (pending_done_event);
1203 mono_cond_init (&exited_cond, 0);
1204 #ifdef MONO_HAS_SEMAPHORES
1205 MONO_SEM_INIT (&finalizer_sem, 0);
1208 #ifndef LAZY_GC_THREAD_CREATION
1209 mono_gc_init_finalizer_thread ();
1214 mono_gc_cleanup (void)
1217 g_message ("%s: cleaning up finalizer", __func__);
1220 if (mono_gc_is_null ())
1225 if (mono_thread_internal_current () != gc_thread) {
1226 gboolean timed_out = FALSE;
1227 guint32 start_ticks = mono_msec_ticks ();
1228 guint32 end_ticks = start_ticks + 2000;
1230 mono_gc_finalize_notify ();
1231 /* Finishing the finalizer thread, so wait a little bit... */
1232 /* MS seems to wait for about 2 seconds */
1233 while (!finalizer_thread_exited) {
1234 guint32 current_ticks = mono_msec_ticks ();
1237 if (current_ticks >= end_ticks)
1240 timeout = end_ticks - current_ticks;
1241 MONO_PREPARE_BLOCKING;
1242 mono_finalizer_lock ();
1243 if (!finalizer_thread_exited)
1244 mono_cond_timedwait_ms (&exited_cond, &finalizer_mutex, timeout);
1245 mono_finalizer_unlock ();
1246 MONO_FINISH_BLOCKING;
1249 if (!finalizer_thread_exited) {
1252 /* Set a flag which the finalizer thread can check */
1253 suspend_finalizers = TRUE;
1255 /* Try to abort the thread, in the hope that it is running managed code */
1256 mono_thread_internal_stop (gc_thread);
1258 /* Wait for it to stop */
1259 ret = guarded_wait (gc_thread->handle, 100, TRUE);
1261 if (ret == WAIT_TIMEOUT) {
1263 * The finalizer thread refused to die. There is not much we
1264 * can do here, since the runtime is shutting down so the
1265 * state the finalizer thread depends on will vanish.
1267 g_warning ("Shutting down finalizer thread timed out.");
1275 /* Wait for the thread to actually exit */
1276 ret = guarded_wait (gc_thread->handle, INFINITE, TRUE);
1277 g_assert (ret == WAIT_OBJECT_0);
1279 mono_thread_join (GUINT_TO_POINTER (gc_thread->tid));
1283 mono_gc_base_cleanup ();
1286 mono_reference_queue_cleanup ();
1288 mono_mutex_destroy (&allocator_section);
1289 mono_mutex_destroy (&finalizer_mutex);
1290 mono_mutex_destroy (&reference_queue_mutex);
1294 * mono_gc_mutex_cleanup:
1296 * Destroy the mutexes that may still be used after the main cleanup routine.
1299 mono_gc_mutex_cleanup (void)
1301 mono_mutex_destroy (&handle_section);
1305 mono_gc_is_finalizer_internal_thread (MonoInternalThread *thread)
1307 return thread == gc_thread;
1311 * mono_gc_is_finalizer_thread:
1312 * @thread: the thread to test.
1314 * In Mono objects are finalized asynchronously on a separate thread.
1315 * This routine tests whether the @thread argument represents the
1316 * finalization thread.
1318 * Returns true if @thread is the finalization thread.
1321 mono_gc_is_finalizer_thread (MonoThread *thread)
1323 return mono_gc_is_finalizer_internal_thread (thread->internal_thread);
1326 #if defined(__MACH__)
1327 static pthread_t mach_exception_thread;
1330 mono_gc_register_mach_exception_thread (pthread_t thread)
1332 mach_exception_thread = thread;
1336 mono_gc_get_mach_exception_thread (void)
1338 return mach_exception_thread;
1342 #ifndef HAVE_SGEN_GC
1344 mono_gc_alloc_mature (MonoVTable *vtable)
1346 return mono_object_new_specific (vtable);
1351 static MonoReferenceQueue *ref_queues;
1354 ref_list_remove_element (RefQueueEntry **prev, RefQueueEntry *element)
1357 /* Guard if head is changed concurrently. */
1358 while (*prev != element)
1359 prev = &(*prev)->next;
1360 } while (prev && InterlockedCompareExchangePointer ((void*)prev, element->next, element) != element);
1364 ref_list_push (RefQueueEntry **head, RefQueueEntry *value)
1366 RefQueueEntry *current;
1369 value->next = current;
1370 STORE_STORE_FENCE; /*Must make sure the previous store is visible before the CAS. */
1371 } while (InterlockedCompareExchangePointer ((void*)head, value, current) != current);
1375 reference_queue_proccess (MonoReferenceQueue *queue)
1377 RefQueueEntry **iter = &queue->queue;
1378 RefQueueEntry *entry;
1379 while ((entry = *iter)) {
1381 if (queue->should_be_deleted || !mono_gc_weak_link_get (&entry->dis_link)) {
1382 mono_gc_weak_link_remove (&entry->dis_link, TRUE);
1384 if (queue->should_be_deleted || !mono_gchandle_get_target (entry->gchandle)) {
1385 mono_gchandle_free ((guint32)entry->gchandle);
1387 ref_list_remove_element (iter, entry);
1388 queue->callback (entry->user_data);
1391 iter = &entry->next;
1397 reference_queue_proccess_all (void)
1399 MonoReferenceQueue **iter;
1400 MonoReferenceQueue *queue = ref_queues;
1401 for (; queue; queue = queue->next)
1402 reference_queue_proccess (queue);
1405 mono_mutex_lock (&reference_queue_mutex);
1406 for (iter = &ref_queues; *iter;) {
1408 if (!queue->should_be_deleted) {
1409 iter = &queue->next;
1413 mono_mutex_unlock (&reference_queue_mutex);
1414 reference_queue_proccess (queue);
1417 *iter = queue->next;
1420 mono_mutex_unlock (&reference_queue_mutex);
1424 mono_reference_queue_cleanup (void)
1426 MonoReferenceQueue *queue = ref_queues;
1427 for (; queue; queue = queue->next)
1428 queue->should_be_deleted = TRUE;
1429 reference_queue_proccess_all ();
1433 reference_queue_clear_for_domain (MonoDomain *domain)
1435 MonoReferenceQueue *queue = ref_queues;
1436 for (; queue; queue = queue->next) {
1437 RefQueueEntry **iter = &queue->queue;
1438 RefQueueEntry *entry;
1439 while ((entry = *iter)) {
1440 if (entry->domain == domain) {
1442 mono_gc_weak_link_remove (&entry->dis_link, TRUE);
1444 mono_gchandle_free ((guint32)entry->gchandle);
1446 ref_list_remove_element (iter, entry);
1447 queue->callback (entry->user_data);
1450 iter = &entry->next;
1456 * mono_gc_reference_queue_new:
1457 * @callback callback used when processing collected entries.
1459 * Create a new reference queue used to process collected objects.
1460 * A reference queue let you add a pair of (managed object, user data)
1461 * using the mono_gc_reference_queue_add method.
1463 * Once the managed object is collected @callback will be called
1464 * in the finalizer thread with 'user data' as argument.
1466 * The callback is called from the finalizer thread without any locks held.
1467 * When a AppDomain is unloaded, all callbacks for objects belonging to it
1470 * @returns the new queue.
1473 mono_gc_reference_queue_new (mono_reference_queue_callback callback)
1475 MonoReferenceQueue *res = g_new0 (MonoReferenceQueue, 1);
1476 res->callback = callback;
1478 mono_mutex_lock (&reference_queue_mutex);
1479 res->next = ref_queues;
1481 mono_mutex_unlock (&reference_queue_mutex);
1487 * mono_gc_reference_queue_add:
1488 * @queue the queue to add the reference to.
1489 * @obj the object to be watched for collection
1490 * @user_data parameter to be passed to the queue callback
1492 * Queue an object to be watched for collection, when the @obj is
1493 * collected, the callback that was registered for the @queue will
1494 * be invoked with @user_data as argument.
1496 * @returns false if the queue is scheduled to be freed.
1499 mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *user_data)
1501 RefQueueEntry *entry;
1502 if (queue->should_be_deleted)
1505 entry = g_new0 (RefQueueEntry, 1);
1506 entry->user_data = user_data;
1507 entry->domain = mono_object_domain (obj);
1510 mono_gc_weak_link_add (&entry->dis_link, obj, TRUE);
1512 entry->gchandle = mono_gchandle_new_weakref (obj, TRUE);
1513 mono_object_register_finalizer (obj);
1516 ref_list_push (&queue->queue, entry);
1521 * mono_gc_reference_queue_free:
1522 * @queue the queue that should be freed.
1524 * This operation signals that @queue should be freed. This operation is deferred
1525 * as it happens on the finalizer thread.
1527 * After this call, no further objects can be queued. It's the responsibility of the
1528 * caller to make sure that no further attempt to access queue will be made.
1531 mono_gc_reference_queue_free (MonoReferenceQueue *queue)
1533 queue->should_be_deleted = TRUE;