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)
16 #include <mono/metadata/gc-internal.h>
17 #include <mono/metadata/mono-gc.h>
18 #include <mono/metadata/threads.h>
19 #include <mono/metadata/tabledefs.h>
20 #include <mono/metadata/exception.h>
21 #include <mono/metadata/profiler-private.h>
22 #include <mono/metadata/domain-internals.h>
23 #include <mono/metadata/class-internals.h>
24 #include <mono/metadata/metadata-internals.h>
25 #include <mono/metadata/mono-mlist.h>
26 #include <mono/metadata/threadpool.h>
27 #include <mono/metadata/threadpool-internals.h>
28 #include <mono/metadata/threads-types.h>
29 #include <mono/metadata/sgen-conf.h>
30 #include <mono/utils/mono-logger-internal.h>
31 #include <mono/metadata/gc-internal.h>
32 #include <mono/metadata/marshal.h> /* for mono_delegate_free_ftnptr () */
33 #include <mono/metadata/attach.h>
34 #include <mono/metadata/console-io.h>
35 #include <mono/utils/mono-semaphore.h>
36 #include <mono/utils/mono-memory-model.h>
37 #include <mono/utils/mono-counters.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 #define mono_finalizer_lock() mono_mutex_lock (&finalizer_mutex)
63 #define mono_finalizer_unlock() mono_mutex_unlock (&finalizer_mutex)
64 static mono_mutex_t finalizer_mutex;
65 static mono_mutex_t reference_queue_mutex;
67 static GSList *domains_to_finalize= NULL;
68 static MonoMList *threads_to_finalize = NULL;
70 static MonoInternalThread *gc_thread;
72 static void object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*));
74 static void mono_gchandle_set_target (guint32 gchandle, MonoObject *obj);
76 static void reference_queue_proccess_all (void);
77 static void mono_reference_queue_cleanup (void);
78 static void reference_queue_clear_for_domain (MonoDomain *domain);
80 static HANDLE pending_done_event;
81 static HANDLE shutdown_event;
87 add_thread_to_finalize (MonoInternalThread *thread)
89 mono_finalizer_lock ();
90 if (!threads_to_finalize)
91 MONO_GC_REGISTER_ROOT_SINGLE (threads_to_finalize);
92 threads_to_finalize = mono_mlist_append (threads_to_finalize, (MonoObject*)thread);
93 mono_finalizer_unlock ();
96 static gboolean suspend_finalizers = FALSE;
98 * actually, we might want to queue the finalize requests in a separate thread,
99 * but we need to be careful about the execution domain of the thread...
102 mono_gc_run_finalize (void *obj, void *data)
104 MonoObject *exc = NULL;
109 MonoMethod* finalizer = NULL;
110 MonoDomain *caller_domain = mono_domain_get ();
112 RuntimeInvokeFunction runtime_invoke;
114 o = (MonoObject*)((char*)obj + GPOINTER_TO_UINT (data));
116 if (suspend_finalizers)
119 domain = o->vtable->domain;
122 mono_domain_finalizers_lock (domain);
124 o2 = g_hash_table_lookup (domain->finalizable_objects_hash, o);
126 mono_domain_finalizers_unlock (domain);
129 /* Already finalized somehow */
133 /* make sure the finalizer is not called again if the object is resurrected */
134 object_register_finalizer (obj, NULL);
136 if (o->vtable->klass == mono_defaults.internal_thread_class) {
137 MonoInternalThread *t = (MonoInternalThread*)o;
139 if (mono_gc_is_finalizer_internal_thread (t))
140 /* Avoid finalizing ourselves */
143 if (t->threadpool_thread && finalizing_root_domain) {
144 /* Don't finalize threadpool threads when
145 shutting down - they're finalized when the
146 threadpool shuts down. */
147 add_thread_to_finalize (t);
152 if (o->vtable->klass->image == mono_defaults.corlib && !strcmp (o->vtable->klass->name, "DynamicMethod") && finalizing_root_domain) {
154 * These can't be finalized during unloading/shutdown, since that would
155 * free the native code which can still be referenced by other
157 * FIXME: This is not perfect, objects dying at the same time as
158 * dynamic methods can still reference them even when !shutdown.
163 if (mono_runtime_get_no_exec ())
166 /* speedup later... and use a timeout */
167 /* g_print ("Finalize run on %p %s.%s\n", o, mono_object_class (o)->name_space, mono_object_class (o)->name); */
169 /* Use _internal here, since this thread can enter a doomed appdomain */
170 mono_domain_set_internal (mono_object_domain (o));
172 /* delegates that have a native function pointer allocated are
173 * registered for finalization, but they don't have a Finalize
174 * method, because in most cases it's not needed and it's just a waste.
176 if (o->vtable->klass->delegate) {
177 MonoDelegate* del = (MonoDelegate*)o;
178 if (del->delegate_trampoline)
179 mono_delegate_free_ftnptr ((MonoDelegate*)o);
180 mono_domain_set_internal (caller_domain);
184 finalizer = mono_class_get_finalizer (o->vtable->klass);
187 /* If object has a CCW but has no finalizer, it was only
188 * registered for finalization in order to free the CCW.
189 * Else it needs the regular finalizer run.
190 * FIXME: what to do about ressurection and suppression
191 * of finalizer on object with CCW.
193 if (mono_marshal_free_ccw (o) && !finalizer) {
194 mono_domain_set_internal (caller_domain);
200 * To avoid the locking plus the other overhead of mono_runtime_invoke (),
201 * create and precompile a wrapper which calls the finalize method using
204 if (!domain->finalize_runtime_invoke) {
205 MonoMethod *invoke = mono_marshal_get_runtime_invoke (mono_class_get_method_from_name_flags (mono_defaults.object_class, "Finalize", 0, 0), TRUE);
207 domain->finalize_runtime_invoke = mono_compile_method (invoke);
210 runtime_invoke = domain->finalize_runtime_invoke;
212 mono_runtime_class_init (o->vtable);
214 if (G_UNLIKELY (MONO_GC_FINALIZE_INVOKE_ENABLED ())) {
215 MONO_GC_FINALIZE_INVOKE ((unsigned long)o, mono_object_get_size (o),
216 o->vtable->klass->name_space, o->vtable->klass->name);
219 runtime_invoke (o, NULL, &exc, NULL);
222 mono_internal_thread_unhandled_exception (exc);
224 mono_domain_set_internal (caller_domain);
228 mono_gc_finalize_threadpool_threads (void)
230 while (threads_to_finalize) {
231 MonoInternalThread *thread = (MonoInternalThread*) mono_mlist_get_data (threads_to_finalize);
233 /* Force finalization of the thread. */
234 thread->threadpool_thread = FALSE;
235 mono_object_register_finalizer ((MonoObject*)thread);
237 mono_gc_run_finalize (thread, NULL);
239 threads_to_finalize = mono_mlist_next (threads_to_finalize);
244 mono_gc_out_of_memory (size_t size)
247 * we could allocate at program startup some memory that we could release
248 * back to the system at this point if we're really low on memory (ie, size is
249 * lower than the memory we set apart)
251 mono_raise_exception (mono_domain_get ()->out_of_memory_ex);
257 * Some of our objects may point to a different address than the address returned by GC_malloc()
258 * (because of the GetHashCode hack), but we need to pass the real address to register_finalizer.
259 * This also means that in the callback we need to adjust the pointer to get back the real
261 * We also need to be consistent in the use of the GC_debug* variants of malloc and register_finalizer,
262 * since that, too, can cause the underlying pointer to be offset.
265 object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*))
270 mono_raise_exception (mono_get_exception_argument_null ("obj"));
272 domain = obj->vtable->domain;
275 if (mono_domain_is_unloading (domain) && (callback != NULL))
277 * Can't register finalizers in a dying appdomain, since they
278 * could be invoked after the appdomain has been unloaded.
282 mono_domain_finalizers_lock (domain);
285 g_hash_table_insert (domain->finalizable_objects_hash, obj, obj);
287 g_hash_table_remove (domain->finalizable_objects_hash, obj);
289 mono_domain_finalizers_unlock (domain);
291 mono_gc_register_for_finalization (obj, callback);
292 #elif defined(HAVE_SGEN_GC)
294 * If we register finalizers for domains that are unloading we might
295 * end up running them while or after the domain is being cleared, so
296 * the objects will not be valid anymore.
298 if (!mono_domain_is_unloading (domain))
299 mono_gc_register_for_finalization (obj, callback);
304 * mono_object_register_finalizer:
305 * @obj: object to register
307 * Records that object @obj has a finalizer, this will call the
308 * Finalize method when the garbage collector disposes the object.
312 mono_object_register_finalizer (MonoObject *obj)
314 /* g_print ("Registered finalizer on %p %s.%s\n", obj, mono_object_class (obj)->name_space, mono_object_class (obj)->name); */
315 object_register_finalizer (obj, mono_gc_run_finalize);
319 * mono_domain_finalize:
320 * @domain: the domain to finalize
321 * @timeout: msects to wait for the finalization to complete, -1 to wait indefinitely
323 * Request finalization of all finalizable objects inside @domain. Wait
324 * @timeout msecs for the finalization to complete.
326 * Returns: TRUE if succeeded, FALSE if there was a timeout
330 mono_domain_finalize (MonoDomain *domain, guint32 timeout)
332 DomainFinalizationReq *req;
335 MonoInternalThread *thread = mono_thread_internal_current ();
337 #if defined(__native_client__)
341 if (mono_thread_internal_current () == gc_thread)
342 /* We are called from inside a finalizer, not much we can do here */
346 * No need to create another thread 'cause the finalizer thread
347 * is still working and will take care of running the finalizers
354 mono_gc_collect (mono_gc_max_generation ());
356 done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
357 if (done_event == NULL) {
361 req = g_new0 (DomainFinalizationReq, 1);
362 req->domain = domain;
363 req->done_event = done_event;
365 if (domain == mono_get_root_domain ())
366 finalizing_root_domain = TRUE;
368 mono_finalizer_lock ();
370 domains_to_finalize = g_slist_append (domains_to_finalize, req);
372 mono_finalizer_unlock ();
374 /* Tell the finalizer thread to finalize this appdomain */
375 mono_gc_finalize_notify ();
381 res = WaitForSingleObjectEx (done_event, timeout, TRUE);
382 /* printf ("WAIT RES: %d.\n", res); */
384 if (res == WAIT_IO_COMPLETION) {
385 if ((thread->state & (ThreadState_StopRequested | ThreadState_SuspendRequested)) != 0)
387 } else if (res == WAIT_TIMEOUT) {
388 /* We leak the handle here */
395 CloseHandle (done_event);
397 if (domain == mono_get_root_domain ()) {
398 mono_thread_pool_cleanup ();
399 mono_gc_finalize_threadpool_threads ();
404 /* We don't support domain finalization without a GC */
410 ves_icall_System_GC_InternalCollect (int generation)
412 mono_gc_collect (generation);
416 ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection)
419 mono_gc_collect (mono_gc_max_generation ());
420 return mono_gc_get_used_size ();
424 ves_icall_System_GC_KeepAlive (MonoObject *obj)
432 ves_icall_System_GC_ReRegisterForFinalize (MonoObject *obj)
434 MONO_CHECK_ARG_NULL (obj,);
436 object_register_finalizer (obj, mono_gc_run_finalize);
440 ves_icall_System_GC_SuppressFinalize (MonoObject *obj)
442 MONO_CHECK_ARG_NULL (obj,);
444 /* delegates have no finalizers, but we register them to deal with the
445 * unmanaged->managed trampoline. We don't let the user suppress it
446 * otherwise we'd leak it.
448 if (obj->vtable->klass->delegate)
451 /* FIXME: Need to handle case where obj has COM Callable Wrapper
452 * generated for it that needs cleaned up, but user wants to suppress
453 * their derived object finalizer. */
455 object_register_finalizer (obj, NULL);
459 ves_icall_System_GC_WaitForPendingFinalizers (void)
462 if (!mono_gc_pending_finalizers ())
465 if (mono_thread_internal_current () == gc_thread)
466 /* Avoid deadlocks */
470 If the finalizer thread is not live, lets pretend no finalizers are pending since the current thread might
471 be the one responsible for starting it up.
473 if (gc_thread == NULL)
476 ResetEvent (pending_done_event);
477 mono_gc_finalize_notify ();
478 /* g_print ("Waiting for pending finalizers....\n"); */
479 WaitForSingleObjectEx (pending_done_event, INFINITE, TRUE);
480 /* g_print ("Done pending....\n"); */
485 ves_icall_System_GC_register_ephemeron_array (MonoObject *array)
488 if (!mono_gc_ephemeron_array_add (array)) {
489 mono_set_pending_exception (mono_object_domain (array)->out_of_memory_ex);
496 ves_icall_System_GC_get_ephemeron_tombstone (void)
498 return mono_domain_get ()->ephemeron_tombstone;
501 #define mono_allocator_lock() mono_mutex_lock (&allocator_section)
502 #define mono_allocator_unlock() mono_mutex_unlock (&allocator_section)
503 static mono_mutex_t allocator_section;
504 static mono_mutex_t handle_section;
513 static HandleType mono_gchandle_get_type (guint32 gchandle);
516 ves_icall_System_GCHandle_GetTarget (guint32 handle)
518 return mono_gchandle_get_target (handle);
522 * if type == -1, change the target of the handle, otherwise allocate a new handle.
525 ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type)
528 mono_gchandle_set_target (handle, obj);
529 /* the handle doesn't change */
534 return mono_gchandle_new_weakref (obj, FALSE);
535 case HANDLE_WEAK_TRACK:
536 return mono_gchandle_new_weakref (obj, TRUE);
538 return mono_gchandle_new (obj, FALSE);
540 return mono_gchandle_new (obj, TRUE);
542 g_assert_not_reached ();
548 ves_icall_System_GCHandle_FreeHandle (guint32 handle)
550 mono_gchandle_free (handle);
554 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
558 if (mono_gchandle_get_type (handle) != HANDLE_PINNED)
560 obj = mono_gchandle_get_target (handle);
562 MonoClass *klass = mono_object_class (obj);
563 if (klass == mono_defaults.string_class) {
564 return mono_string_chars ((MonoString*)obj);
565 } else if (klass->rank) {
566 return mono_array_addr ((MonoArray*)obj, char, 0);
568 /* the C# code will check and throw the exception */
569 /* FIXME: missing !klass->blittable test, see bug #61134 */
570 if ((klass->flags & TYPE_ATTRIBUTE_LAYOUT_MASK) == TYPE_ATTRIBUTE_AUTO_LAYOUT)
572 return (char*)obj + sizeof (MonoObject);
579 ves_icall_Mono_Runtime_SetGCAllowSynchronousMajor (MonoBoolean flag)
581 return mono_gc_set_allow_synchronous_major (flag);
589 guint slot_hint : 24; /* starting slot for search */
590 /* 2^16 appdomains should be enough for everyone (though I know I'll regret this in 20 years) */
591 /* we alloc this only for weak refs, since we can get the domain directly in the other cases */
595 /* weak and weak-track arrays will be allocated in malloc memory
597 static HandleData gc_handles [] = {
598 {NULL, NULL, 0, HANDLE_WEAK, 0},
599 {NULL, NULL, 0, HANDLE_WEAK_TRACK, 0},
600 {NULL, NULL, 0, HANDLE_NORMAL, 0},
601 {NULL, NULL, 0, HANDLE_PINNED, 0}
604 #define lock_handles(handles) mono_mutex_lock (&handle_section)
605 #define unlock_handles(handles) mono_mutex_unlock (&handle_section)
608 find_first_unset (guint32 bitmap)
611 for (i = 0; i < 32; ++i) {
612 if (!(bitmap & (1 << i)))
619 make_root_descr_all_refs (int numbits, gboolean pinned)
625 return mono_gc_make_root_descr_all_refs (numbits);
629 alloc_handle (HandleData *handles, MonoObject *obj, gboolean track)
633 lock_handles (handles);
634 if (!handles->size) {
636 if (handles->type > HANDLE_WEAK_TRACK) {
637 handles->entries = mono_gc_alloc_fixed (sizeof (gpointer) * handles->size, make_root_descr_all_refs (handles->size, handles->type == HANDLE_PINNED));
639 handles->entries = g_malloc0 (sizeof (gpointer) * handles->size);
640 handles->domain_ids = g_malloc0 (sizeof (guint16) * handles->size);
642 handles->bitmap = g_malloc0 (handles->size / 8);
645 for (slot = handles->slot_hint; slot < handles->size / 32; ++slot) {
646 if (handles->bitmap [slot] != 0xffffffff) {
647 i = find_first_unset (handles->bitmap [slot]);
648 handles->slot_hint = slot;
652 if (i == -1 && handles->slot_hint != 0) {
653 for (slot = 0; slot < handles->slot_hint; ++slot) {
654 if (handles->bitmap [slot] != 0xffffffff) {
655 i = find_first_unset (handles->bitmap [slot]);
656 handles->slot_hint = slot;
663 guint32 new_size = handles->size * 2; /* always double: we memset to 0 based on this below */
665 /* resize and copy the bitmap */
666 new_bitmap = g_malloc0 (new_size / 8);
667 memcpy (new_bitmap, handles->bitmap, handles->size / 8);
668 g_free (handles->bitmap);
669 handles->bitmap = new_bitmap;
671 /* resize and copy the entries */
672 if (handles->type > HANDLE_WEAK_TRACK) {
675 entries = mono_gc_alloc_fixed (sizeof (gpointer) * new_size, make_root_descr_all_refs (new_size, handles->type == HANDLE_PINNED));
676 mono_gc_memmove_aligned (entries, handles->entries, sizeof (gpointer) * handles->size);
678 mono_gc_free_fixed (handles->entries);
679 handles->entries = entries;
683 domain_ids = g_malloc0 (sizeof (guint16) * new_size);
684 entries = g_malloc0 (sizeof (gpointer) * new_size);
685 memcpy (domain_ids, handles->domain_ids, sizeof (guint16) * handles->size);
686 for (i = 0; i < handles->size; ++i) {
687 MonoObject *obj = mono_gc_weak_link_get (&(handles->entries [i]));
689 mono_gc_weak_link_add (&(entries [i]), obj, track);
690 mono_gc_weak_link_remove (&(handles->entries [i]), track);
692 g_assert (!handles->entries [i]);
695 g_free (handles->entries);
696 g_free (handles->domain_ids);
697 handles->entries = entries;
698 handles->domain_ids = domain_ids;
701 /* set i and slot to the next free position */
703 slot = (handles->size + 1) / 32;
704 handles->slot_hint = handles->size + 1;
705 handles->size = new_size;
707 handles->bitmap [slot] |= 1 << i;
708 slot = slot * 32 + i;
709 handles->entries [slot] = NULL;
710 if (handles->type <= HANDLE_WEAK_TRACK) {
711 /*FIXME, what to use when obj == null?*/
712 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
714 mono_gc_weak_link_add (&(handles->entries [slot]), obj, track);
716 handles->entries [slot] = obj;
719 #ifndef DISABLE_PERFCOUNTERS
720 mono_perfcounters->gc_num_handles++;
722 unlock_handles (handles);
723 /*g_print ("allocated entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
724 res = (slot << 3) | (handles->type + 1);
725 mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_CREATED, handles->type, res, obj);
731 * @obj: managed object to get a handle for
732 * @pinned: whether the object should be pinned
734 * This returns a handle that wraps the object, this is used to keep a
735 * reference to a managed object from the unmanaged world and preventing the
736 * object from being disposed.
738 * If @pinned is false the address of the object can not be obtained, if it is
739 * true the address of the object can be obtained. This will also pin the
740 * object so it will not be possible by a moving garbage collector to move the
743 * Returns: a handle that can be used to access the object from
747 mono_gchandle_new (MonoObject *obj, gboolean pinned)
749 return alloc_handle (&gc_handles [pinned? HANDLE_PINNED: HANDLE_NORMAL], obj, FALSE);
753 * mono_gchandle_new_weakref:
754 * @obj: managed object to get a handle for
755 * @pinned: whether the object should be pinned
757 * This returns a weak handle that wraps the object, this is used to
758 * keep a reference to a managed object from the unmanaged world.
759 * Unlike the mono_gchandle_new the object can be reclaimed by the
760 * garbage collector. In this case the value of the GCHandle will be
763 * If @pinned is false the address of the object can not be obtained, if it is
764 * true the address of the object can be obtained. This will also pin the
765 * object so it will not be possible by a moving garbage collector to move the
768 * Returns: a handle that can be used to access the object from
772 mono_gchandle_new_weakref (MonoObject *obj, gboolean track_resurrection)
774 guint32 handle = alloc_handle (&gc_handles [track_resurrection? HANDLE_WEAK_TRACK: HANDLE_WEAK], obj, track_resurrection);
780 mono_gchandle_get_type (guint32 gchandle)
782 guint type = (gchandle & 7) - 1;
788 * mono_gchandle_get_target:
789 * @gchandle: a GCHandle's handle.
791 * The handle was previously created by calling mono_gchandle_new or
792 * mono_gchandle_new_weakref.
794 * Returns a pointer to the MonoObject represented by the handle or
795 * NULL for a collected object if using a weakref handle.
798 mono_gchandle_get_target (guint32 gchandle)
800 guint slot = gchandle >> 3;
801 guint type = (gchandle & 7) - 1;
802 HandleData *handles = &gc_handles [type];
803 MonoObject *obj = NULL;
806 lock_handles (handles);
807 if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
808 if (handles->type <= HANDLE_WEAK_TRACK) {
809 obj = mono_gc_weak_link_get (&handles->entries [slot]);
811 obj = handles->entries [slot];
814 /* print a warning? */
816 unlock_handles (handles);
817 /*g_print ("get target of entry %d of type %d: %p\n", slot, handles->type, obj);*/
822 mono_gchandle_set_target (guint32 gchandle, MonoObject *obj)
824 guint slot = gchandle >> 3;
825 guint type = (gchandle & 7) - 1;
826 HandleData *handles = &gc_handles [type];
827 MonoObject *old_obj = NULL;
831 lock_handles (handles);
832 if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
833 if (handles->type <= HANDLE_WEAK_TRACK) {
834 old_obj = handles->entries [slot];
835 if (handles->entries [slot])
836 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
838 mono_gc_weak_link_add (&handles->entries [slot], obj, handles->type == HANDLE_WEAK_TRACK);
839 /*FIXME, what to use when obj == null?*/
840 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
842 handles->entries [slot] = obj;
845 /* print a warning? */
847 /*g_print ("changed entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
848 unlock_handles (handles);
852 * mono_gchandle_is_in_domain:
853 * @gchandle: a GCHandle's handle.
854 * @domain: An application domain.
856 * Returns: true if the object wrapped by the @gchandle belongs to the specific @domain.
859 mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain)
861 guint slot = gchandle >> 3;
862 guint type = (gchandle & 7) - 1;
863 HandleData *handles = &gc_handles [type];
864 gboolean result = FALSE;
867 lock_handles (handles);
868 if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
869 if (handles->type <= HANDLE_WEAK_TRACK) {
870 result = domain->domain_id == handles->domain_ids [slot];
873 obj = handles->entries [slot];
877 result = domain == mono_object_domain (obj);
880 /* print a warning? */
882 unlock_handles (handles);
887 * mono_gchandle_free:
888 * @gchandle: a GCHandle's handle.
890 * Frees the @gchandle handle. If there are no outstanding
891 * references, the garbage collector can reclaim the memory of the
895 mono_gchandle_free (guint32 gchandle)
897 guint slot = gchandle >> 3;
898 guint type = (gchandle & 7) - 1;
899 HandleData *handles = &gc_handles [type];
903 lock_handles (handles);
904 if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
905 if (handles->type <= HANDLE_WEAK_TRACK) {
906 if (handles->entries [slot])
907 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
909 handles->entries [slot] = NULL;
911 handles->bitmap [slot / 32] &= ~(1 << (slot % 32));
913 /* print a warning? */
915 #ifndef DISABLE_PERFCOUNTERS
916 mono_perfcounters->gc_num_handles--;
918 /*g_print ("freed entry %d of type %d\n", slot, handles->type);*/
919 unlock_handles (handles);
920 mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_DESTROYED, handles->type, gchandle, NULL);
924 * mono_gchandle_free_domain:
925 * @domain: domain that is unloading
927 * Function used internally to cleanup any GC handle for objects belonging
928 * to the specified domain during appdomain unload.
931 mono_gchandle_free_domain (MonoDomain *domain)
935 for (type = 0; type < 3; ++type) {
937 HandleData *handles = &gc_handles [type];
938 lock_handles (handles);
939 for (slot = 0; slot < handles->size; ++slot) {
940 if (!(handles->bitmap [slot / 32] & (1 << (slot % 32))))
942 if (type <= HANDLE_WEAK_TRACK) {
943 if (domain->domain_id == handles->domain_ids [slot]) {
944 handles->bitmap [slot / 32] &= ~(1 << (slot % 32));
945 if (handles->entries [slot])
946 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
949 if (handles->entries [slot] && mono_object_domain (handles->entries [slot]) == domain) {
950 handles->bitmap [slot / 32] &= ~(1 << (slot % 32));
951 handles->entries [slot] = NULL;
955 unlock_handles (handles);
961 GCHandle_CheckCurrentDomain (guint32 gchandle)
963 return mono_gchandle_is_in_domain (gchandle, mono_domain_get ());
968 #ifdef MONO_HAS_SEMAPHORES
969 static MonoSemType finalizer_sem;
971 static HANDLE finalizer_event;
972 static volatile gboolean finished=FALSE;
975 mono_gc_finalize_notify (void)
978 g_message ( "%s: prodding finalizer", __func__);
981 #ifdef MONO_HAS_SEMAPHORES
982 MONO_SEM_POST (&finalizer_sem);
984 SetEvent (finalizer_event);
991 collect_objects (gpointer key, gpointer value, gpointer user_data)
993 GPtrArray *arr = (GPtrArray*)user_data;
994 g_ptr_array_add (arr, key);
1000 * finalize_domain_objects:
1002 * Run the finalizers of all finalizable objects in req->domain.
1005 finalize_domain_objects (DomainFinalizationReq *req)
1007 MonoDomain *domain = req->domain;
1010 #define NUM_FOBJECTS 64
1011 MonoObject *to_finalize [NUM_FOBJECTS];
1015 /* Process finalizers which are already in the queue */
1016 mono_gc_invoke_finalizers ();
1018 #ifdef HAVE_BOEHM_GC
1019 while (g_hash_table_size (domain->finalizable_objects_hash) > 0) {
1023 * Since the domain is unloading, nobody is allowed to put
1024 * new entries into the hash table. But finalize_object might
1025 * remove entries from the hash table, so we make a copy.
1027 objs = g_ptr_array_new ();
1028 g_hash_table_foreach (domain->finalizable_objects_hash, collect_objects, objs);
1029 /* printf ("FINALIZING %d OBJECTS.\n", objs->len); */
1031 for (i = 0; i < objs->len; ++i) {
1032 MonoObject *o = (MonoObject*)g_ptr_array_index (objs, i);
1033 /* FIXME: Avoid finalizing threads, etc */
1034 mono_gc_run_finalize (o, 0);
1037 g_ptr_array_free (objs, TRUE);
1039 #elif defined(HAVE_SGEN_GC)
1040 while ((count = mono_gc_finalizers_for_domain (domain, to_finalize, NUM_FOBJECTS))) {
1042 for (i = 0; i < count; ++i) {
1043 mono_gc_run_finalize (to_finalize [i], 0);
1048 /* cleanup the reference queue */
1049 reference_queue_clear_for_domain (domain);
1051 /* printf ("DONE.\n"); */
1052 SetEvent (req->done_event);
1054 /* The event is closed in mono_domain_finalize if we get here */
1059 finalizer_thread (gpointer unused)
1061 gboolean wait = TRUE;
1064 /* Wait to be notified that there's at least one
1068 g_assert (mono_domain_get () == mono_get_root_domain ());
1069 mono_gc_set_skip_thread (TRUE);
1072 /* An alertable wait is required so this thread can be suspended on windows */
1073 #ifdef MONO_HAS_SEMAPHORES
1074 MONO_SEM_WAIT_ALERTABLE (&finalizer_sem, TRUE);
1076 WaitForSingleObjectEx (finalizer_event, INFINITE, TRUE);
1080 mono_gc_set_skip_thread (FALSE);
1082 mono_threads_perform_thread_dump ();
1084 mono_console_handle_async_ops ();
1086 #ifndef DISABLE_ATTACH
1087 mono_attach_maybe_start ();
1090 if (domains_to_finalize) {
1091 mono_finalizer_lock ();
1092 if (domains_to_finalize) {
1093 DomainFinalizationReq *req = domains_to_finalize->data;
1094 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
1095 mono_finalizer_unlock ();
1097 finalize_domain_objects (req);
1099 mono_finalizer_unlock ();
1103 /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup),
1104 * before the domain is unloaded.
1106 mono_gc_invoke_finalizers ();
1108 mono_threads_join_threads ();
1110 reference_queue_proccess_all ();
1112 #ifdef MONO_HAS_SEMAPHORES
1113 /* Avoid posting the pending done event until there are pending finalizers */
1114 if (MONO_SEM_TIMEDWAIT (&finalizer_sem, 0) == 0)
1115 /* Don't wait again at the start of the loop */
1118 SetEvent (pending_done_event);
1120 SetEvent (pending_done_event);
1124 SetEvent (shutdown_event);
1128 #ifndef LAZY_GC_THREAD_CREATION
1132 mono_gc_init_finalizer_thread (void)
1134 gc_thread = mono_thread_create_internal (mono_domain_get (), finalizer_thread, NULL, FALSE, 0);
1135 ves_icall_System_Threading_Thread_SetName_internal (gc_thread, mono_string_new (mono_domain_get (), "Finalizer"));
1141 mono_mutex_init_recursive (&handle_section);
1142 mono_mutex_init_recursive (&allocator_section);
1144 mono_mutex_init_recursive (&finalizer_mutex);
1145 mono_mutex_init_recursive (&reference_queue_mutex);
1147 MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_NORMAL].entries);
1148 MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_PINNED].entries);
1150 mono_counters_register ("Minor GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.minor_gc_count);
1151 mono_counters_register ("Major GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.major_gc_count);
1152 mono_counters_register ("Minor GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.minor_gc_time);
1153 mono_counters_register ("Major GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time);
1154 mono_counters_register ("Major GC time concurrent", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time_concurrent);
1156 mono_gc_base_init ();
1158 if (mono_gc_is_disabled ()) {
1163 finalizer_event = CreateEvent (NULL, FALSE, FALSE, NULL);
1164 pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
1165 shutdown_event = CreateEvent (NULL, TRUE, FALSE, NULL);
1166 if (finalizer_event == NULL || pending_done_event == NULL || shutdown_event == NULL) {
1167 g_assert_not_reached ();
1169 #ifdef MONO_HAS_SEMAPHORES
1170 MONO_SEM_INIT (&finalizer_sem, 0);
1173 #ifndef LAZY_GC_THREAD_CREATION
1174 mono_gc_init_finalizer_thread ();
1179 mono_gc_cleanup (void)
1182 g_message ("%s: cleaning up finalizer", __func__);
1186 ResetEvent (shutdown_event);
1188 if (mono_thread_internal_current () != gc_thread) {
1189 gboolean timed_out = FALSE;
1191 mono_gc_finalize_notify ();
1192 /* Finishing the finalizer thread, so wait a little bit... */
1193 /* MS seems to wait for about 2 seconds */
1194 if (WaitForSingleObjectEx (shutdown_event, 2000, FALSE) == WAIT_TIMEOUT) {
1197 /* Set a flag which the finalizer thread can check */
1198 suspend_finalizers = TRUE;
1200 /* Try to abort the thread, in the hope that it is running managed code */
1201 mono_thread_internal_stop (gc_thread);
1203 /* Wait for it to stop */
1204 ret = WaitForSingleObjectEx (gc_thread->handle, 100, TRUE);
1206 if (ret == WAIT_TIMEOUT) {
1208 * The finalizer thread refused to die. There is not much we
1209 * can do here, since the runtime is shutting down so the
1210 * state the finalizer thread depends on will vanish.
1212 g_warning ("Shutting down finalizer thread timed out.");
1220 /* Wait for the thread to actually exit */
1221 ret = WaitForSingleObjectEx (gc_thread->handle, INFINITE, TRUE);
1222 g_assert (ret == WAIT_OBJECT_0);
1224 mono_thread_join (GUINT_TO_POINTER (gc_thread->tid));
1228 #ifdef HAVE_BOEHM_GC
1229 GC_finalizer_notifier = NULL;
1233 mono_reference_queue_cleanup ();
1235 mono_mutex_destroy (&handle_section);
1236 mono_mutex_destroy (&allocator_section);
1237 mono_mutex_destroy (&finalizer_mutex);
1238 mono_mutex_destroy (&reference_queue_mutex);
1243 /* Null GC dummy functions */
1245 mono_gc_finalize_notify (void)
1249 void mono_gc_init (void)
1251 mono_mutex_init_recursive (&handle_section);
1254 void mono_gc_cleanup (void)
1261 mono_gc_is_finalizer_internal_thread (MonoInternalThread *thread)
1263 return thread == gc_thread;
1267 * mono_gc_is_finalizer_thread:
1268 * @thread: the thread to test.
1270 * In Mono objects are finalized asynchronously on a separate thread.
1271 * This routine tests whether the @thread argument represents the
1272 * finalization thread.
1274 * Returns true if @thread is the finalization thread.
1277 mono_gc_is_finalizer_thread (MonoThread *thread)
1279 return mono_gc_is_finalizer_internal_thread (thread->internal_thread);
1282 #if defined(__MACH__)
1283 static pthread_t mach_exception_thread;
1286 mono_gc_register_mach_exception_thread (pthread_t thread)
1288 mach_exception_thread = thread;
1292 mono_gc_get_mach_exception_thread (void)
1294 return mach_exception_thread;
1299 * mono_gc_parse_environment_string_extract_number:
1301 * @str: points to the first digit of the number
1302 * @out: pointer to the variable that will receive the value
1304 * Tries to extract a number from the passed string, taking in to account m, k
1307 * Returns true if passing was successful
1310 mono_gc_parse_environment_string_extract_number (const char *str, size_t *out)
1313 int len = strlen (str), shift = 0;
1315 gboolean is_suffix = FALSE;
1321 suffix = str [len - 1];
1336 if (!isdigit (suffix))
1342 val = strtol (str, &endptr, 10);
1344 if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN))
1345 || (errno != 0 && val == 0) || (endptr == str))
1351 if (val < 0) /* negative numbers cannot be suffixed */
1353 if (*(endptr + 1)) /* Invalid string. */
1356 unshifted = (size_t)val;
1358 if (val < 0) /* overflow */
1360 if (((size_t)val >> shift) != unshifted) /* value too large */
1368 #ifndef HAVE_SGEN_GC
1370 mono_gc_alloc_mature (MonoVTable *vtable)
1372 return mono_object_new_specific (vtable);
1377 static MonoReferenceQueue *ref_queues;
1380 ref_list_remove_element (RefQueueEntry **prev, RefQueueEntry *element)
1383 /* Guard if head is changed concurrently. */
1384 while (*prev != element)
1385 prev = &(*prev)->next;
1386 } while (prev && InterlockedCompareExchangePointer ((void*)prev, element->next, element) != element);
1390 ref_list_push (RefQueueEntry **head, RefQueueEntry *value)
1392 RefQueueEntry *current;
1395 value->next = current;
1396 STORE_STORE_FENCE; /*Must make sure the previous store is visible before the CAS. */
1397 } while (InterlockedCompareExchangePointer ((void*)head, value, current) != current);
1401 reference_queue_proccess (MonoReferenceQueue *queue)
1403 RefQueueEntry **iter = &queue->queue;
1404 RefQueueEntry *entry;
1405 while ((entry = *iter)) {
1407 if (queue->should_be_deleted || !mono_gc_weak_link_get (&entry->dis_link)) {
1408 mono_gc_weak_link_remove (&entry->dis_link, TRUE);
1410 if (queue->should_be_deleted || !mono_gchandle_get_target (entry->gchandle)) {
1411 mono_gchandle_free ((guint32)entry->gchandle);
1413 ref_list_remove_element (iter, entry);
1414 queue->callback (entry->user_data);
1417 iter = &entry->next;
1423 reference_queue_proccess_all (void)
1425 MonoReferenceQueue **iter;
1426 MonoReferenceQueue *queue = ref_queues;
1427 for (; queue; queue = queue->next)
1428 reference_queue_proccess (queue);
1431 mono_mutex_lock (&reference_queue_mutex);
1432 for (iter = &ref_queues; *iter;) {
1434 if (!queue->should_be_deleted) {
1435 iter = &queue->next;
1439 mono_mutex_unlock (&reference_queue_mutex);
1440 reference_queue_proccess (queue);
1443 *iter = queue->next;
1446 mono_mutex_unlock (&reference_queue_mutex);
1450 mono_reference_queue_cleanup (void)
1452 MonoReferenceQueue *queue = ref_queues;
1453 for (; queue; queue = queue->next)
1454 queue->should_be_deleted = TRUE;
1455 reference_queue_proccess_all ();
1459 reference_queue_clear_for_domain (MonoDomain *domain)
1461 MonoReferenceQueue *queue = ref_queues;
1462 for (; queue; queue = queue->next) {
1463 RefQueueEntry **iter = &queue->queue;
1464 RefQueueEntry *entry;
1465 while ((entry = *iter)) {
1466 if (entry->domain == domain) {
1468 mono_gc_weak_link_remove (&entry->dis_link, TRUE);
1470 mono_gchandle_free ((guint32)entry->gchandle);
1472 ref_list_remove_element (iter, entry);
1473 queue->callback (entry->user_data);
1476 iter = &entry->next;
1482 * mono_gc_reference_queue_new:
1483 * @callback callback used when processing collected entries.
1485 * Create a new reference queue used to process collected objects.
1486 * A reference queue let you add a pair of (managed object, user data)
1487 * using the mono_gc_reference_queue_add method.
1489 * Once the managed object is collected @callback will be called
1490 * in the finalizer thread with 'user data' as argument.
1492 * The callback is called from the finalizer thread without any locks held.
1493 * When a AppDomain is unloaded, all callbacks for objects belonging to it
1496 * @returns the new queue.
1499 mono_gc_reference_queue_new (mono_reference_queue_callback callback)
1501 MonoReferenceQueue *res = g_new0 (MonoReferenceQueue, 1);
1502 res->callback = callback;
1504 mono_mutex_lock (&reference_queue_mutex);
1505 res->next = ref_queues;
1507 mono_mutex_unlock (&reference_queue_mutex);
1513 * mono_gc_reference_queue_add:
1514 * @queue the queue to add the reference to.
1515 * @obj the object to be watched for collection
1516 * @user_data parameter to be passed to the queue callback
1518 * Queue an object to be watched for collection, when the @obj is
1519 * collected, the callback that was registered for the @queue will
1520 * be invoked with @user_data as argument.
1522 * @returns false if the queue is scheduled to be freed.
1525 mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *user_data)
1527 RefQueueEntry *entry;
1528 if (queue->should_be_deleted)
1531 entry = g_new0 (RefQueueEntry, 1);
1532 entry->user_data = user_data;
1533 entry->domain = mono_object_domain (obj);
1536 mono_gc_weak_link_add (&entry->dis_link, obj, TRUE);
1538 entry->gchandle = mono_gchandle_new_weakref (obj, TRUE);
1539 mono_object_register_finalizer (obj);
1542 ref_list_push (&queue->queue, entry);
1547 * mono_gc_reference_queue_free:
1548 * @queue the queue that should be freed.
1550 * This operation signals that @queue should be freed. This operation is deferred
1551 * as it happens on the finalizer thread.
1553 * After this call, no further objects can be queued. It's the responsibility of the
1554 * caller to make sure that no further attempt to access queue will be made.
1557 mono_gc_reference_queue_free (MonoReferenceQueue *queue)
1559 queue->should_be_deleted = TRUE;