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 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 MonoInternalThread *gc_thread;
75 static void object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*));
77 static void mono_gchandle_set_target (guint32 gchandle, MonoObject *obj);
79 static void reference_queue_proccess_all (void);
80 static void mono_reference_queue_cleanup (void);
81 static void reference_queue_clear_for_domain (MonoDomain *domain);
83 static HANDLE pending_done_event;
84 static HANDLE shutdown_event;
90 guarded_wait (HANDLE handle, guint32 timeout, gboolean alertable)
95 result = WaitForSingleObjectEx (handle, timeout, alertable);
102 add_thread_to_finalize (MonoInternalThread *thread)
104 mono_finalizer_lock ();
105 if (!threads_to_finalize)
106 MONO_GC_REGISTER_ROOT_SINGLE (threads_to_finalize);
107 threads_to_finalize = mono_mlist_append (threads_to_finalize, (MonoObject*)thread);
108 mono_finalizer_unlock ();
111 static gboolean suspend_finalizers = FALSE;
113 * actually, we might want to queue the finalize requests in a separate thread,
114 * but we need to be careful about the execution domain of the thread...
117 mono_gc_run_finalize (void *obj, void *data)
122 MonoObject *exc = NULL;
127 MonoMethod* finalizer = NULL;
128 MonoDomain *caller_domain = mono_domain_get ();
130 RuntimeInvokeFunction runtime_invoke;
132 o = (MonoObject*)((char*)obj + GPOINTER_TO_UINT (data));
135 g_log ("mono-gc-finalizers", G_LOG_LEVEL_DEBUG, "<%s at %p> Starting finalizer checks.", o->vtable->klass->name, o);
137 if (suspend_finalizers)
140 domain = o->vtable->domain;
143 mono_domain_finalizers_lock (domain);
145 o2 = g_hash_table_lookup (domain->finalizable_objects_hash, o);
147 mono_domain_finalizers_unlock (domain);
150 /* Already finalized somehow */
154 /* make sure the finalizer is not called again if the object is resurrected */
155 object_register_finalizer (obj, NULL);
158 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Registered finalizer as processed.", o->vtable->klass->name, o);
160 if (o->vtable->klass == mono_defaults.internal_thread_class) {
161 MonoInternalThread *t = (MonoInternalThread*)o;
163 if (mono_gc_is_finalizer_internal_thread (t))
164 /* Avoid finalizing ourselves */
167 if (t->threadpool_thread && finalizing_root_domain) {
168 /* Don't finalize threadpool threads when
169 shutting down - they're finalized when the
170 threadpool shuts down. */
171 add_thread_to_finalize (t);
176 if (o->vtable->klass->image == mono_defaults.corlib && !strcmp (o->vtable->klass->name, "DynamicMethod") && finalizing_root_domain) {
178 * These can't be finalized during unloading/shutdown, since that would
179 * free the native code which can still be referenced by other
181 * FIXME: This is not perfect, objects dying at the same time as
182 * dynamic methods can still reference them even when !shutdown.
187 if (mono_runtime_get_no_exec ())
190 /* speedup later... and use a timeout */
191 /* g_print ("Finalize run on %p %s.%s\n", o, mono_object_class (o)->name_space, mono_object_class (o)->name); */
193 /* Use _internal here, since this thread can enter a doomed appdomain */
194 mono_domain_set_internal (mono_object_domain (o));
196 /* delegates that have a native function pointer allocated are
197 * registered for finalization, but they don't have a Finalize
198 * method, because in most cases it's not needed and it's just a waste.
200 if (o->vtable->klass->delegate) {
201 MonoDelegate* del = (MonoDelegate*)o;
202 if (del->delegate_trampoline)
203 mono_delegate_free_ftnptr ((MonoDelegate*)o);
204 mono_domain_set_internal (caller_domain);
208 finalizer = mono_class_get_finalizer (o->vtable->klass);
211 /* If object has a CCW but has no finalizer, it was only
212 * registered for finalization in order to free the CCW.
213 * Else it needs the regular finalizer run.
214 * FIXME: what to do about ressurection and suppression
215 * of finalizer on object with CCW.
217 if (mono_marshal_free_ccw (o) && !finalizer) {
218 mono_domain_set_internal (caller_domain);
224 * To avoid the locking plus the other overhead of mono_runtime_invoke (),
225 * create and precompile a wrapper which calls the finalize method using
229 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Compiling finalizer.", o->vtable->klass->name, o);
231 if (!domain->finalize_runtime_invoke) {
232 MonoMethod *invoke = mono_marshal_get_runtime_invoke (mono_class_get_method_from_name_flags (mono_defaults.object_class, "Finalize", 0, 0), TRUE);
234 domain->finalize_runtime_invoke = mono_compile_method (invoke);
237 runtime_invoke = domain->finalize_runtime_invoke;
239 mono_runtime_class_init (o->vtable);
241 if (G_UNLIKELY (MONO_GC_FINALIZE_INVOKE_ENABLED ())) {
242 MONO_GC_FINALIZE_INVOKE ((unsigned long)o, mono_object_get_size (o),
243 o->vtable->klass->name_space, o->vtable->klass->name);
247 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Calling finalizer.", o->vtable->klass->name, o);
249 runtime_invoke (o, NULL, &exc, NULL);
252 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Returned from finalizer.", o->vtable->klass->name, o);
255 mono_internal_thread_unhandled_exception (exc);
257 mono_domain_set_internal (caller_domain);
261 mono_gc_finalize_threadpool_threads (void)
263 while (threads_to_finalize) {
264 MonoInternalThread *thread = (MonoInternalThread*) mono_mlist_get_data (threads_to_finalize);
266 /* Force finalization of the thread. */
267 thread->threadpool_thread = FALSE;
268 mono_object_register_finalizer ((MonoObject*)thread);
270 mono_gc_run_finalize (thread, NULL);
272 threads_to_finalize = mono_mlist_next (threads_to_finalize);
277 mono_gc_out_of_memory (size_t size)
280 * we could allocate at program startup some memory that we could release
281 * back to the system at this point if we're really low on memory (ie, size is
282 * lower than the memory we set apart)
284 mono_raise_exception (mono_domain_get ()->out_of_memory_ex);
290 * Some of our objects may point to a different address than the address returned by GC_malloc()
291 * (because of the GetHashCode hack), but we need to pass the real address to register_finalizer.
292 * This also means that in the callback we need to adjust the pointer to get back the real
294 * We also need to be consistent in the use of the GC_debug* variants of malloc and register_finalizer,
295 * since that, too, can cause the underlying pointer to be offset.
298 object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*))
303 mono_raise_exception (mono_get_exception_argument_null ("obj"));
305 domain = obj->vtable->domain;
308 if (mono_domain_is_unloading (domain) && (callback != NULL))
310 * Can't register finalizers in a dying appdomain, since they
311 * could be invoked after the appdomain has been unloaded.
315 mono_domain_finalizers_lock (domain);
318 g_hash_table_insert (domain->finalizable_objects_hash, obj, obj);
320 g_hash_table_remove (domain->finalizable_objects_hash, obj);
322 mono_domain_finalizers_unlock (domain);
324 mono_gc_register_for_finalization (obj, callback);
325 #elif defined(HAVE_SGEN_GC)
327 * If we register finalizers for domains that are unloading we might
328 * end up running them while or after the domain is being cleared, so
329 * the objects will not be valid anymore.
331 if (!mono_domain_is_unloading (domain))
332 mono_gc_register_for_finalization (obj, callback);
337 * mono_object_register_finalizer:
338 * @obj: object to register
340 * Records that object @obj has a finalizer, this will call the
341 * Finalize method when the garbage collector disposes the object.
345 mono_object_register_finalizer (MonoObject *obj)
347 /* g_print ("Registered finalizer on %p %s.%s\n", obj, mono_object_class (obj)->name_space, mono_object_class (obj)->name); */
348 object_register_finalizer (obj, mono_gc_run_finalize);
352 * mono_domain_finalize:
353 * @domain: the domain to finalize
354 * @timeout: msects to wait for the finalization to complete, -1 to wait indefinitely
356 * Request finalization of all finalizable objects inside @domain. Wait
357 * @timeout msecs for the finalization to complete.
359 * Returns: TRUE if succeeded, FALSE if there was a timeout
363 mono_domain_finalize (MonoDomain *domain, guint32 timeout)
365 DomainFinalizationReq *req;
368 MonoInternalThread *thread = mono_thread_internal_current ();
370 #if defined(__native_client__)
374 if (mono_thread_internal_current () == gc_thread)
375 /* We are called from inside a finalizer, not much we can do here */
379 * No need to create another thread 'cause the finalizer thread
380 * is still working and will take care of running the finalizers
387 mono_gc_collect (mono_gc_max_generation ());
389 done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
390 if (done_event == NULL) {
394 req = g_new0 (DomainFinalizationReq, 1);
395 req->domain = domain;
396 req->done_event = done_event;
398 if (domain == mono_get_root_domain ())
399 finalizing_root_domain = TRUE;
401 mono_finalizer_lock ();
403 domains_to_finalize = g_slist_append (domains_to_finalize, req);
405 mono_finalizer_unlock ();
407 /* Tell the finalizer thread to finalize this appdomain */
408 mono_gc_finalize_notify ();
414 res = guarded_wait (done_event, timeout, TRUE);
415 /* printf ("WAIT RES: %d.\n", res); */
417 if (res == WAIT_IO_COMPLETION) {
418 if ((thread->state & (ThreadState_StopRequested | ThreadState_SuspendRequested)) != 0)
420 } else if (res == WAIT_TIMEOUT) {
421 /* We leak the handle here */
428 CloseHandle (done_event);
430 if (domain == mono_get_root_domain ()) {
431 mono_thread_pool_cleanup ();
432 mono_gc_finalize_threadpool_threads ();
437 /* We don't support domain finalization without a GC */
443 ves_icall_System_GC_InternalCollect (int generation)
445 mono_gc_collect (generation);
449 ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection)
452 mono_gc_collect (mono_gc_max_generation ());
453 return mono_gc_get_used_size ();
457 ves_icall_System_GC_KeepAlive (MonoObject *obj)
465 ves_icall_System_GC_ReRegisterForFinalize (MonoObject *obj)
467 MONO_CHECK_ARG_NULL (obj,);
469 object_register_finalizer (obj, mono_gc_run_finalize);
473 ves_icall_System_GC_SuppressFinalize (MonoObject *obj)
475 MONO_CHECK_ARG_NULL (obj,);
477 /* delegates have no finalizers, but we register them to deal with the
478 * unmanaged->managed trampoline. We don't let the user suppress it
479 * otherwise we'd leak it.
481 if (obj->vtable->klass->delegate)
484 /* FIXME: Need to handle case where obj has COM Callable Wrapper
485 * generated for it that needs cleaned up, but user wants to suppress
486 * their derived object finalizer. */
488 object_register_finalizer (obj, NULL);
492 ves_icall_System_GC_WaitForPendingFinalizers (void)
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"); */
518 ves_icall_System_GC_register_ephemeron_array (MonoObject *array)
521 if (!mono_gc_ephemeron_array_add (array)) {
522 mono_set_pending_exception (mono_object_domain (array)->out_of_memory_ex);
529 ves_icall_System_GC_get_ephemeron_tombstone (void)
531 return mono_domain_get ()->ephemeron_tombstone;
534 #define mono_allocator_lock() mono_mutex_lock (&allocator_section)
535 #define mono_allocator_unlock() mono_mutex_unlock (&allocator_section)
536 static mono_mutex_t allocator_section;
537 static mono_mutex_t handle_section;
546 static HandleType mono_gchandle_get_type (guint32 gchandle);
549 ves_icall_System_GCHandle_GetTarget (guint32 handle)
551 return mono_gchandle_get_target (handle);
555 * if type == -1, change the target of the handle, otherwise allocate a new handle.
558 ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type)
561 mono_gchandle_set_target (handle, obj);
562 /* the handle doesn't change */
567 return mono_gchandle_new_weakref (obj, FALSE);
568 case HANDLE_WEAK_TRACK:
569 return mono_gchandle_new_weakref (obj, TRUE);
571 return mono_gchandle_new (obj, FALSE);
573 return mono_gchandle_new (obj, TRUE);
575 g_assert_not_reached ();
581 ves_icall_System_GCHandle_FreeHandle (guint32 handle)
583 mono_gchandle_free (handle);
587 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
591 if (mono_gchandle_get_type (handle) != HANDLE_PINNED)
593 obj = mono_gchandle_get_target (handle);
595 MonoClass *klass = mono_object_class (obj);
596 if (klass == mono_defaults.string_class) {
597 return mono_string_chars ((MonoString*)obj);
598 } else if (klass->rank) {
599 return mono_array_addr ((MonoArray*)obj, char, 0);
601 /* the C# code will check and throw the exception */
602 /* FIXME: missing !klass->blittable test, see bug #61134 */
603 if ((klass->flags & TYPE_ATTRIBUTE_LAYOUT_MASK) == TYPE_ATTRIBUTE_AUTO_LAYOUT)
605 return (char*)obj + sizeof (MonoObject);
612 ves_icall_Mono_Runtime_SetGCAllowSynchronousMajor (MonoBoolean flag)
614 return mono_gc_set_allow_synchronous_major (flag);
622 guint slot_hint : 24; /* starting slot for search */
623 /* 2^16 appdomains should be enough for everyone (though I know I'll regret this in 20 years) */
624 /* we alloc this only for weak refs, since we can get the domain directly in the other cases */
628 /* weak and weak-track arrays will be allocated in malloc memory
630 static HandleData gc_handles [] = {
631 {NULL, NULL, 0, HANDLE_WEAK, 0},
632 {NULL, NULL, 0, HANDLE_WEAK_TRACK, 0},
633 {NULL, NULL, 0, HANDLE_NORMAL, 0},
634 {NULL, NULL, 0, HANDLE_PINNED, 0}
637 #define lock_handles(handles) mono_mutex_lock (&handle_section)
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)))
652 make_root_descr_all_refs (int numbits, gboolean pinned)
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));
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));
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 GCHandle_CheckCurrentDomain (guint32 gchandle)
994 return mono_gchandle_is_in_domain (gchandle, mono_domain_get ());
999 #ifdef MONO_HAS_SEMAPHORES
1000 static MonoSemType finalizer_sem;
1002 static HANDLE finalizer_event;
1003 static volatile gboolean finished=FALSE;
1006 mono_gc_finalize_notify (void)
1009 g_message ( "%s: prodding finalizer", __func__);
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);
1103 /* An alertable wait is required so this thread can be suspended on windows */
1104 #ifdef MONO_HAS_SEMAPHORES
1105 MONO_SEM_WAIT_ALERTABLE (&finalizer_sem, TRUE);
1107 WaitForSingleObjectEx (finalizer_event, INFINITE, TRUE);
1111 mono_gc_set_skip_thread (FALSE);
1113 mono_threads_perform_thread_dump ();
1115 mono_console_handle_async_ops ();
1117 #ifndef DISABLE_ATTACH
1118 mono_attach_maybe_start ();
1121 if (domains_to_finalize) {
1122 mono_finalizer_lock ();
1123 if (domains_to_finalize) {
1124 DomainFinalizationReq *req = domains_to_finalize->data;
1125 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
1126 mono_finalizer_unlock ();
1128 finalize_domain_objects (req);
1130 mono_finalizer_unlock ();
1134 /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup),
1135 * before the domain is unloaded.
1137 mono_gc_invoke_finalizers ();
1139 mono_threads_join_threads ();
1141 reference_queue_proccess_all ();
1143 #ifdef MONO_HAS_SEMAPHORES
1144 /* Avoid posting the pending done event until there are pending finalizers */
1145 if (MONO_SEM_TIMEDWAIT (&finalizer_sem, 0) == 0)
1146 /* Don't wait again at the start of the loop */
1149 SetEvent (pending_done_event);
1151 SetEvent (pending_done_event);
1155 SetEvent (shutdown_event);
1159 #ifndef LAZY_GC_THREAD_CREATION
1163 mono_gc_init_finalizer_thread (void)
1165 gc_thread = mono_thread_create_internal (mono_domain_get (), finalizer_thread, NULL, FALSE, 0);
1166 ves_icall_System_Threading_Thread_SetName_internal (gc_thread, mono_string_new (mono_domain_get (), "Finalizer"));
1172 mono_mutex_init_recursive (&handle_section);
1173 mono_mutex_init_recursive (&allocator_section);
1175 mono_mutex_init_recursive (&finalizer_mutex);
1176 mono_mutex_init_recursive (&reference_queue_mutex);
1178 MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_NORMAL].entries);
1179 MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_PINNED].entries);
1181 mono_counters_register ("Minor GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.minor_gc_count);
1182 mono_counters_register ("Major GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.major_gc_count);
1183 mono_counters_register ("Minor GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.minor_gc_time);
1184 mono_counters_register ("Major GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time);
1185 mono_counters_register ("Major GC time concurrent", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time_concurrent);
1187 mono_gc_base_init ();
1189 if (mono_gc_is_disabled ()) {
1194 finalizer_event = CreateEvent (NULL, FALSE, FALSE, NULL);
1195 pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
1196 shutdown_event = CreateEvent (NULL, TRUE, FALSE, NULL);
1197 if (finalizer_event == NULL || pending_done_event == NULL || shutdown_event == NULL) {
1198 g_assert_not_reached ();
1200 #ifdef MONO_HAS_SEMAPHORES
1201 MONO_SEM_INIT (&finalizer_sem, 0);
1204 #ifndef LAZY_GC_THREAD_CREATION
1205 mono_gc_init_finalizer_thread ();
1210 mono_gc_cleanup (void)
1213 g_message ("%s: cleaning up finalizer", __func__);
1217 ResetEvent (shutdown_event);
1219 if (mono_thread_internal_current () != gc_thread) {
1220 gboolean timed_out = FALSE;
1222 mono_gc_finalize_notify ();
1223 /* Finishing the finalizer thread, so wait a little bit... */
1224 /* MS seems to wait for about 2 seconds */
1225 if (guarded_wait (shutdown_event, 2000, FALSE) == WAIT_TIMEOUT) {
1228 /* Set a flag which the finalizer thread can check */
1229 suspend_finalizers = TRUE;
1231 /* Try to abort the thread, in the hope that it is running managed code */
1232 mono_thread_internal_stop (gc_thread);
1234 /* Wait for it to stop */
1235 ret = guarded_wait (gc_thread->handle, 100, TRUE);
1237 if (ret == WAIT_TIMEOUT) {
1239 * The finalizer thread refused to die. There is not much we
1240 * can do here, since the runtime is shutting down so the
1241 * state the finalizer thread depends on will vanish.
1243 g_warning ("Shutting down finalizer thread timed out.");
1251 /* Wait for the thread to actually exit */
1252 ret = guarded_wait (gc_thread->handle, INFINITE, TRUE);
1253 g_assert (ret == WAIT_OBJECT_0);
1255 mono_thread_join (GUINT_TO_POINTER (gc_thread->tid));
1259 #ifdef HAVE_BOEHM_GC
1260 GC_finalizer_notifier = NULL;
1264 mono_reference_queue_cleanup ();
1266 mono_mutex_destroy (&handle_section);
1267 mono_mutex_destroy (&allocator_section);
1268 mono_mutex_destroy (&finalizer_mutex);
1269 mono_mutex_destroy (&reference_queue_mutex);
1274 /* Null GC dummy functions */
1276 mono_gc_finalize_notify (void)
1280 void mono_gc_init (void)
1282 mono_mutex_init_recursive (&handle_section);
1285 void mono_gc_cleanup (void)
1292 mono_gc_is_finalizer_internal_thread (MonoInternalThread *thread)
1294 return thread == gc_thread;
1298 * mono_gc_is_finalizer_thread:
1299 * @thread: the thread to test.
1301 * In Mono objects are finalized asynchronously on a separate thread.
1302 * This routine tests whether the @thread argument represents the
1303 * finalization thread.
1305 * Returns true if @thread is the finalization thread.
1308 mono_gc_is_finalizer_thread (MonoThread *thread)
1310 return mono_gc_is_finalizer_internal_thread (thread->internal_thread);
1313 #if defined(__MACH__)
1314 static pthread_t mach_exception_thread;
1317 mono_gc_register_mach_exception_thread (pthread_t thread)
1319 mach_exception_thread = thread;
1323 mono_gc_get_mach_exception_thread (void)
1325 return mach_exception_thread;
1330 * mono_gc_parse_environment_string_extract_number:
1332 * @str: points to the first digit of the number
1333 * @out: pointer to the variable that will receive the value
1335 * Tries to extract a number from the passed string, taking in to account m, k
1338 * Returns true if passing was successful
1341 mono_gc_parse_environment_string_extract_number (const char *str, size_t *out)
1344 int len = strlen (str), shift = 0;
1346 gboolean is_suffix = FALSE;
1352 suffix = str [len - 1];
1367 if (!isdigit (suffix))
1373 val = strtol (str, &endptr, 10);
1375 if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN))
1376 || (errno != 0 && val == 0) || (endptr == str))
1382 if (val < 0) /* negative numbers cannot be suffixed */
1384 if (*(endptr + 1)) /* Invalid string. */
1387 unshifted = (size_t)val;
1389 if (val < 0) /* overflow */
1391 if (((size_t)val >> shift) != unshifted) /* value too large */
1399 #ifndef HAVE_SGEN_GC
1401 mono_gc_alloc_mature (MonoVTable *vtable)
1403 return mono_object_new_specific (vtable);
1408 static MonoReferenceQueue *ref_queues;
1411 ref_list_remove_element (RefQueueEntry **prev, RefQueueEntry *element)
1414 /* Guard if head is changed concurrently. */
1415 while (*prev != element)
1416 prev = &(*prev)->next;
1417 } while (prev && InterlockedCompareExchangePointer ((void*)prev, element->next, element) != element);
1421 ref_list_push (RefQueueEntry **head, RefQueueEntry *value)
1423 RefQueueEntry *current;
1426 value->next = current;
1427 STORE_STORE_FENCE; /*Must make sure the previous store is visible before the CAS. */
1428 } while (InterlockedCompareExchangePointer ((void*)head, value, current) != current);
1432 reference_queue_proccess (MonoReferenceQueue *queue)
1434 RefQueueEntry **iter = &queue->queue;
1435 RefQueueEntry *entry;
1436 while ((entry = *iter)) {
1438 if (queue->should_be_deleted || !mono_gc_weak_link_get (&entry->dis_link)) {
1439 mono_gc_weak_link_remove (&entry->dis_link, TRUE);
1441 if (queue->should_be_deleted || !mono_gchandle_get_target (entry->gchandle)) {
1442 mono_gchandle_free ((guint32)entry->gchandle);
1444 ref_list_remove_element (iter, entry);
1445 queue->callback (entry->user_data);
1448 iter = &entry->next;
1454 reference_queue_proccess_all (void)
1456 MonoReferenceQueue **iter;
1457 MonoReferenceQueue *queue = ref_queues;
1458 for (; queue; queue = queue->next)
1459 reference_queue_proccess (queue);
1462 mono_mutex_lock (&reference_queue_mutex);
1463 for (iter = &ref_queues; *iter;) {
1465 if (!queue->should_be_deleted) {
1466 iter = &queue->next;
1470 mono_mutex_unlock (&reference_queue_mutex);
1471 reference_queue_proccess (queue);
1474 *iter = queue->next;
1477 mono_mutex_unlock (&reference_queue_mutex);
1481 mono_reference_queue_cleanup (void)
1483 MonoReferenceQueue *queue = ref_queues;
1484 for (; queue; queue = queue->next)
1485 queue->should_be_deleted = TRUE;
1486 reference_queue_proccess_all ();
1490 reference_queue_clear_for_domain (MonoDomain *domain)
1492 MonoReferenceQueue *queue = ref_queues;
1493 for (; queue; queue = queue->next) {
1494 RefQueueEntry **iter = &queue->queue;
1495 RefQueueEntry *entry;
1496 while ((entry = *iter)) {
1497 if (entry->domain == domain) {
1499 mono_gc_weak_link_remove (&entry->dis_link, TRUE);
1501 mono_gchandle_free ((guint32)entry->gchandle);
1503 ref_list_remove_element (iter, entry);
1504 queue->callback (entry->user_data);
1507 iter = &entry->next;
1513 * mono_gc_reference_queue_new:
1514 * @callback callback used when processing collected entries.
1516 * Create a new reference queue used to process collected objects.
1517 * A reference queue let you add a pair of (managed object, user data)
1518 * using the mono_gc_reference_queue_add method.
1520 * Once the managed object is collected @callback will be called
1521 * in the finalizer thread with 'user data' as argument.
1523 * The callback is called from the finalizer thread without any locks held.
1524 * When a AppDomain is unloaded, all callbacks for objects belonging to it
1527 * @returns the new queue.
1530 mono_gc_reference_queue_new (mono_reference_queue_callback callback)
1532 MonoReferenceQueue *res = g_new0 (MonoReferenceQueue, 1);
1533 res->callback = callback;
1535 mono_mutex_lock (&reference_queue_mutex);
1536 res->next = ref_queues;
1538 mono_mutex_unlock (&reference_queue_mutex);
1544 * mono_gc_reference_queue_add:
1545 * @queue the queue to add the reference to.
1546 * @obj the object to be watched for collection
1547 * @user_data parameter to be passed to the queue callback
1549 * Queue an object to be watched for collection, when the @obj is
1550 * collected, the callback that was registered for the @queue will
1551 * be invoked with @user_data as argument.
1553 * @returns false if the queue is scheduled to be freed.
1556 mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *user_data)
1558 RefQueueEntry *entry;
1559 if (queue->should_be_deleted)
1562 entry = g_new0 (RefQueueEntry, 1);
1563 entry->user_data = user_data;
1564 entry->domain = mono_object_domain (obj);
1567 mono_gc_weak_link_add (&entry->dis_link, obj, TRUE);
1569 entry->gchandle = mono_gchandle_new_weakref (obj, TRUE);
1570 mono_object_register_finalizer (obj);
1573 ref_list_push (&queue->queue, entry);
1578 * mono_gc_reference_queue_free:
1579 * @queue the queue that should be freed.
1581 * This operation signals that @queue should be freed. This operation is deferred
1582 * as it happens on the finalizer thread.
1584 * After this call, no further objects can be queued. It's the responsibility of the
1585 * caller to make sure that no further attempt to access queue will be made.
1588 mono_gc_reference_queue_free (MonoReferenceQueue *queue)
1590 queue->should_be_deleted = TRUE;