2 * metadata/gc.c: GC icalls.
4 * Author: Paolo Molaro <lupus@ximian.com>
6 * Copyright 2002-2003 Ximian, Inc (http://www.ximian.com)
7 * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
8 * Copyright 2012 Xamarin Inc (http://www.xamarin.com)
15 #include <mono/metadata/gc-internal.h>
16 #include <mono/metadata/mono-gc.h>
17 #include <mono/metadata/threads.h>
18 #include <mono/metadata/tabledefs.h>
19 #include <mono/metadata/exception.h>
20 #include <mono/metadata/profiler-private.h>
21 #include <mono/metadata/domain-internals.h>
22 #include <mono/metadata/class-internals.h>
23 #include <mono/metadata/metadata-internals.h>
24 #include <mono/metadata/mono-mlist.h>
25 #include <mono/metadata/threadpool.h>
26 #include <mono/metadata/threadpool-internals.h>
27 #include <mono/metadata/threads-types.h>
28 #include <mono/sgen/sgen-conf.h>
29 #include <mono/utils/mono-logger-internal.h>
30 #include <mono/metadata/gc-internal.h>
31 #include <mono/metadata/marshal.h> /* for mono_delegate_free_ftnptr () */
32 #include <mono/metadata/attach.h>
33 #include <mono/metadata/console-io.h>
34 #include <mono/utils/mono-semaphore.h>
35 #include <mono/utils/mono-memory-model.h>
36 #include <mono/utils/mono-counters.h>
37 #include <mono/utils/mono-time.h>
38 #include <mono/utils/dtrace.h>
39 #include <mono/utils/mono-threads.h>
40 #include <mono/utils/atomic.h>
46 typedef struct DomainFinalizationReq {
49 } DomainFinalizationReq;
51 static gboolean gc_disabled = FALSE;
53 static gboolean finalizing_root_domain = FALSE;
55 gboolean log_finalizers = FALSE;
56 gboolean do_not_finalize = FALSE;
58 #define mono_finalizer_lock() mono_mutex_lock (&finalizer_mutex)
59 #define mono_finalizer_unlock() mono_mutex_unlock (&finalizer_mutex)
60 static mono_mutex_t finalizer_mutex;
61 static mono_mutex_t reference_queue_mutex;
63 static GSList *domains_to_finalize= NULL;
64 static MonoMList *threads_to_finalize = NULL;
66 static gboolean finalizer_thread_exited;
67 /* Uses finalizer_mutex */
68 static mono_cond_t exited_cond;
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);
79 static HANDLE pending_done_event;
82 guarded_wait (HANDLE handle, guint32 timeout, gboolean alertable)
87 result = WaitForSingleObjectEx (handle, timeout, alertable);
94 add_thread_to_finalize (MonoInternalThread *thread)
96 mono_finalizer_lock ();
97 if (!threads_to_finalize)
98 MONO_GC_REGISTER_ROOT_SINGLE (threads_to_finalize);
99 threads_to_finalize = mono_mlist_append (threads_to_finalize, (MonoObject*)thread);
100 mono_finalizer_unlock ();
103 static gboolean suspend_finalizers = FALSE;
105 * actually, we might want to queue the finalize requests in a separate thread,
106 * but we need to be careful about the execution domain of the thread...
109 mono_gc_run_finalize (void *obj, void *data)
114 MonoObject *exc = NULL;
119 MonoMethod* finalizer = NULL;
120 MonoDomain *caller_domain = mono_domain_get ();
122 RuntimeInvokeFunction runtime_invoke;
124 o = (MonoObject*)((char*)obj + GPOINTER_TO_UINT (data));
127 g_log ("mono-gc-finalizers", G_LOG_LEVEL_DEBUG, "<%s at %p> Starting finalizer checks.", o->vtable->klass->name, o);
129 if (suspend_finalizers)
132 domain = o->vtable->domain;
135 mono_domain_finalizers_lock (domain);
137 o2 = g_hash_table_lookup (domain->finalizable_objects_hash, o);
139 mono_domain_finalizers_unlock (domain);
142 /* Already finalized somehow */
146 /* make sure the finalizer is not called again if the object is resurrected */
147 object_register_finalizer (obj, NULL);
150 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Registered finalizer as processed.", o->vtable->klass->name, o);
152 if (o->vtable->klass == mono_defaults.internal_thread_class) {
153 MonoInternalThread *t = (MonoInternalThread*)o;
155 if (mono_gc_is_finalizer_internal_thread (t))
156 /* Avoid finalizing ourselves */
159 if (t->threadpool_thread && finalizing_root_domain) {
160 /* Don't finalize threadpool threads when
161 shutting down - they're finalized when the
162 threadpool shuts down. */
163 add_thread_to_finalize (t);
168 if (o->vtable->klass->image == mono_defaults.corlib && !strcmp (o->vtable->klass->name, "DynamicMethod") && finalizing_root_domain) {
170 * These can't be finalized during unloading/shutdown, since that would
171 * free the native code which can still be referenced by other
173 * FIXME: This is not perfect, objects dying at the same time as
174 * dynamic methods can still reference them even when !shutdown.
179 if (mono_runtime_get_no_exec ())
182 /* speedup later... and use a timeout */
183 /* g_print ("Finalize run on %p %s.%s\n", o, mono_object_class (o)->name_space, mono_object_class (o)->name); */
185 /* Use _internal here, since this thread can enter a doomed appdomain */
186 mono_domain_set_internal (mono_object_domain (o));
188 /* delegates that have a native function pointer allocated are
189 * registered for finalization, but they don't have a Finalize
190 * method, because in most cases it's not needed and it's just a waste.
192 if (o->vtable->klass->delegate) {
193 MonoDelegate* del = (MonoDelegate*)o;
194 if (del->delegate_trampoline)
195 mono_delegate_free_ftnptr ((MonoDelegate*)o);
196 mono_domain_set_internal (caller_domain);
200 finalizer = mono_class_get_finalizer (o->vtable->klass);
202 /* If object has a CCW but has no finalizer, it was only
203 * registered for finalization in order to free the CCW.
204 * Else it needs the regular finalizer run.
205 * FIXME: what to do about ressurection and suppression
206 * of finalizer on object with CCW.
208 if (mono_marshal_free_ccw (o) && !finalizer) {
209 mono_domain_set_internal (caller_domain);
214 * To avoid the locking plus the other overhead of mono_runtime_invoke (),
215 * create and precompile a wrapper which calls the finalize method using
219 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Compiling finalizer.", o->vtable->klass->name, o);
221 if (!domain->finalize_runtime_invoke) {
222 MonoMethod *invoke = mono_marshal_get_runtime_invoke (mono_class_get_method_from_name_flags (mono_defaults.object_class, "Finalize", 0, 0), TRUE);
224 domain->finalize_runtime_invoke = mono_compile_method (invoke);
227 runtime_invoke = domain->finalize_runtime_invoke;
229 mono_runtime_class_init (o->vtable);
231 if (G_UNLIKELY (MONO_GC_FINALIZE_INVOKE_ENABLED ())) {
232 MONO_GC_FINALIZE_INVOKE ((unsigned long)o, mono_object_get_size (o),
233 o->vtable->klass->name_space, o->vtable->klass->name);
237 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Calling finalizer.", o->vtable->klass->name, o);
239 runtime_invoke (o, NULL, &exc, NULL);
242 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Returned from finalizer.", o->vtable->klass->name, o);
245 mono_internal_thread_unhandled_exception (exc);
247 mono_domain_set_internal (caller_domain);
251 mono_gc_finalize_threadpool_threads (void)
253 while (threads_to_finalize) {
254 MonoInternalThread *thread = (MonoInternalThread*) mono_mlist_get_data (threads_to_finalize);
256 /* Force finalization of the thread. */
257 thread->threadpool_thread = FALSE;
258 mono_object_register_finalizer ((MonoObject*)thread);
260 mono_gc_run_finalize (thread, NULL);
262 threads_to_finalize = mono_mlist_next (threads_to_finalize);
267 mono_gc_out_of_memory (size_t size)
270 * we could allocate at program startup some memory that we could release
271 * back to the system at this point if we're really low on memory (ie, size is
272 * lower than the memory we set apart)
274 mono_raise_exception (mono_domain_get ()->out_of_memory_ex);
280 * Some of our objects may point to a different address than the address returned by GC_malloc()
281 * (because of the GetHashCode hack), but we need to pass the real address to register_finalizer.
282 * This also means that in the callback we need to adjust the pointer to get back the real
284 * We also need to be consistent in the use of the GC_debug* variants of malloc and register_finalizer,
285 * since that, too, can cause the underlying pointer to be offset.
288 object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*))
293 mono_raise_exception (mono_get_exception_argument_null ("obj"));
295 domain = obj->vtable->domain;
298 if (mono_domain_is_unloading (domain) && (callback != NULL))
300 * Can't register finalizers in a dying appdomain, since they
301 * could be invoked after the appdomain has been unloaded.
305 mono_domain_finalizers_lock (domain);
308 g_hash_table_insert (domain->finalizable_objects_hash, obj, obj);
310 g_hash_table_remove (domain->finalizable_objects_hash, obj);
312 mono_domain_finalizers_unlock (domain);
314 mono_gc_register_for_finalization (obj, callback);
315 #elif defined(HAVE_SGEN_GC)
317 * If we register finalizers for domains that are unloading we might
318 * end up running them while or after the domain is being cleared, so
319 * the objects will not be valid anymore.
321 if (!mono_domain_is_unloading (domain))
322 mono_gc_register_for_finalization (obj, callback);
327 * mono_object_register_finalizer:
328 * @obj: object to register
330 * Records that object @obj has a finalizer, this will call the
331 * Finalize method when the garbage collector disposes the object.
335 mono_object_register_finalizer (MonoObject *obj)
337 /* g_print ("Registered finalizer on %p %s.%s\n", obj, mono_object_class (obj)->name_space, mono_object_class (obj)->name); */
338 object_register_finalizer (obj, mono_gc_run_finalize);
342 * mono_domain_finalize:
343 * @domain: the domain to finalize
344 * @timeout: msects to wait for the finalization to complete, -1 to wait indefinitely
346 * Request finalization of all finalizable objects inside @domain. Wait
347 * @timeout msecs for the finalization to complete.
349 * Returns: TRUE if succeeded, FALSE if there was a timeout
353 mono_domain_finalize (MonoDomain *domain, guint32 timeout)
355 DomainFinalizationReq *req;
358 MonoInternalThread *thread = mono_thread_internal_current ();
360 #if defined(__native_client__)
364 if (mono_thread_internal_current () == gc_thread)
365 /* We are called from inside a finalizer, not much we can do here */
369 * No need to create another thread 'cause the finalizer thread
370 * is still working and will take care of running the finalizers
376 /* We don't support domain finalization without a GC */
377 if (mono_gc_is_null ())
380 mono_gc_collect (mono_gc_max_generation ());
382 done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
383 if (done_event == NULL) {
387 req = g_new0 (DomainFinalizationReq, 1);
388 req->domain = domain;
389 req->done_event = done_event;
391 if (domain == mono_get_root_domain ())
392 finalizing_root_domain = TRUE;
394 mono_finalizer_lock ();
396 domains_to_finalize = g_slist_append (domains_to_finalize, req);
398 mono_finalizer_unlock ();
400 /* Tell the finalizer thread to finalize this appdomain */
401 mono_gc_finalize_notify ();
407 res = guarded_wait (done_event, timeout, TRUE);
408 /* printf ("WAIT RES: %d.\n", res); */
410 if (res == WAIT_IO_COMPLETION) {
411 if ((thread->state & (ThreadState_StopRequested | ThreadState_SuspendRequested)) != 0)
413 } else if (res == WAIT_TIMEOUT) {
414 /* We leak the handle here */
421 CloseHandle (done_event);
423 if (domain == mono_get_root_domain ()) {
424 mono_thread_pool_cleanup ();
425 mono_gc_finalize_threadpool_threads ();
432 ves_icall_System_GC_InternalCollect (int generation)
434 mono_gc_collect (generation);
438 ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection)
441 mono_gc_collect (mono_gc_max_generation ());
442 return mono_gc_get_used_size ();
446 ves_icall_System_GC_KeepAlive (MonoObject *obj)
454 ves_icall_System_GC_ReRegisterForFinalize (MonoObject *obj)
456 MONO_CHECK_ARG_NULL (obj,);
458 object_register_finalizer (obj, mono_gc_run_finalize);
462 ves_icall_System_GC_SuppressFinalize (MonoObject *obj)
464 MONO_CHECK_ARG_NULL (obj,);
466 /* delegates have no finalizers, but we register them to deal with the
467 * unmanaged->managed trampoline. We don't let the user suppress it
468 * otherwise we'd leak it.
470 if (obj->vtable->klass->delegate)
473 /* FIXME: Need to handle case where obj has COM Callable Wrapper
474 * generated for it that needs cleaned up, but user wants to suppress
475 * their derived object finalizer. */
477 object_register_finalizer (obj, NULL);
481 ves_icall_System_GC_WaitForPendingFinalizers (void)
483 if (mono_gc_is_null ())
486 if (!mono_gc_pending_finalizers ())
489 if (mono_thread_internal_current () == gc_thread)
490 /* Avoid deadlocks */
494 If the finalizer thread is not live, lets pretend no finalizers are pending since the current thread might
495 be the one responsible for starting it up.
497 if (gc_thread == NULL)
500 ResetEvent (pending_done_event);
501 mono_gc_finalize_notify ();
502 /* g_print ("Waiting for pending finalizers....\n"); */
503 guarded_wait (pending_done_event, INFINITE, TRUE);
504 /* g_print ("Done pending....\n"); */
508 ves_icall_System_GC_register_ephemeron_array (MonoObject *array)
511 if (!mono_gc_ephemeron_array_add (array)) {
512 mono_set_pending_exception (mono_object_domain (array)->out_of_memory_ex);
519 ves_icall_System_GC_get_ephemeron_tombstone (void)
521 return mono_domain_get ()->ephemeron_tombstone;
524 #define mono_allocator_lock() mono_mutex_lock (&allocator_section)
525 #define mono_allocator_unlock() mono_mutex_unlock (&allocator_section)
526 static mono_mutex_t allocator_section;
527 static mono_mutex_t handle_section;
536 static HandleType mono_gchandle_get_type (guint32 gchandle);
539 ves_icall_System_GCHandle_GetTarget (guint32 handle)
541 return mono_gchandle_get_target (handle);
545 * if type == -1, change the target of the handle, otherwise allocate a new handle.
548 ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type)
551 mono_gchandle_set_target (handle, obj);
552 /* the handle doesn't change */
557 return mono_gchandle_new_weakref (obj, FALSE);
558 case HANDLE_WEAK_TRACK:
559 return mono_gchandle_new_weakref (obj, TRUE);
561 return mono_gchandle_new (obj, FALSE);
563 return mono_gchandle_new (obj, TRUE);
565 g_assert_not_reached ();
571 ves_icall_System_GCHandle_FreeHandle (guint32 handle)
573 mono_gchandle_free (handle);
577 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
581 if (mono_gchandle_get_type (handle) != HANDLE_PINNED)
583 obj = mono_gchandle_get_target (handle);
585 MonoClass *klass = mono_object_class (obj);
586 if (klass == mono_defaults.string_class) {
587 return mono_string_chars ((MonoString*)obj);
588 } else if (klass->rank) {
589 return mono_array_addr ((MonoArray*)obj, char, 0);
591 /* the C# code will check and throw the exception */
592 /* FIXME: missing !klass->blittable test, see bug #61134 */
593 if ((klass->flags & TYPE_ATTRIBUTE_LAYOUT_MASK) == TYPE_ATTRIBUTE_AUTO_LAYOUT)
595 return (char*)obj + sizeof (MonoObject);
602 ves_icall_Mono_Runtime_SetGCAllowSynchronousMajor (MonoBoolean flag)
604 return mono_gc_set_allow_synchronous_major (flag);
612 guint slot_hint : 24; /* starting slot for search */
613 /* 2^16 appdomains should be enough for everyone (though I know I'll regret this in 20 years) */
614 /* we alloc this only for weak refs, since we can get the domain directly in the other cases */
618 /* weak and weak-track arrays will be allocated in malloc memory
620 static HandleData gc_handles [] = {
621 {NULL, NULL, 0, HANDLE_WEAK, 0},
622 {NULL, NULL, 0, HANDLE_WEAK_TRACK, 0},
623 {NULL, NULL, 0, HANDLE_NORMAL, 0},
624 {NULL, NULL, 0, HANDLE_PINNED, 0}
627 #define lock_handles(handles) mono_mutex_lock (&handle_section)
628 #define unlock_handles(handles) mono_mutex_unlock (&handle_section)
631 find_first_unset (guint32 bitmap)
634 for (i = 0; i < 32; ++i) {
635 if (!(bitmap & (1 << i)))
642 make_root_descr_all_refs (int numbits, gboolean pinned)
648 return mono_gc_make_root_descr_all_refs (numbits);
652 alloc_handle (HandleData *handles, MonoObject *obj, gboolean track)
656 lock_handles (handles);
657 if (!handles->size) {
659 if (handles->type > HANDLE_WEAK_TRACK) {
660 handles->entries = mono_gc_alloc_fixed (sizeof (gpointer) * handles->size, make_root_descr_all_refs (handles->size, handles->type == HANDLE_PINNED));
662 handles->entries = g_malloc0 (sizeof (gpointer) * handles->size);
663 handles->domain_ids = g_malloc0 (sizeof (guint16) * handles->size);
665 handles->bitmap = g_malloc0 (handles->size / 8);
668 for (slot = handles->slot_hint; slot < handles->size / 32; ++slot) {
669 if (handles->bitmap [slot] != 0xffffffff) {
670 i = find_first_unset (handles->bitmap [slot]);
671 handles->slot_hint = slot;
675 if (i == -1 && handles->slot_hint != 0) {
676 for (slot = 0; slot < handles->slot_hint; ++slot) {
677 if (handles->bitmap [slot] != 0xffffffff) {
678 i = find_first_unset (handles->bitmap [slot]);
679 handles->slot_hint = slot;
686 guint32 new_size = handles->size * 2; /* always double: we memset to 0 based on this below */
688 /* resize and copy the bitmap */
689 new_bitmap = g_malloc0 (new_size / 8);
690 memcpy (new_bitmap, handles->bitmap, handles->size / 8);
691 g_free (handles->bitmap);
692 handles->bitmap = new_bitmap;
694 /* resize and copy the entries */
695 if (handles->type > HANDLE_WEAK_TRACK) {
698 entries = mono_gc_alloc_fixed (sizeof (gpointer) * new_size, make_root_descr_all_refs (new_size, handles->type == HANDLE_PINNED));
699 mono_gc_memmove_aligned (entries, handles->entries, sizeof (gpointer) * handles->size);
701 mono_gc_free_fixed (handles->entries);
702 handles->entries = entries;
706 domain_ids = g_malloc0 (sizeof (guint16) * new_size);
707 entries = g_malloc0 (sizeof (gpointer) * new_size);
708 memcpy (domain_ids, handles->domain_ids, sizeof (guint16) * handles->size);
709 for (i = 0; i < handles->size; ++i) {
710 MonoObject *obj = mono_gc_weak_link_get (&(handles->entries [i]));
712 mono_gc_weak_link_add (&(entries [i]), obj, track);
713 mono_gc_weak_link_remove (&(handles->entries [i]), track);
715 g_assert (!handles->entries [i]);
718 g_free (handles->entries);
719 g_free (handles->domain_ids);
720 handles->entries = entries;
721 handles->domain_ids = domain_ids;
724 /* set i and slot to the next free position */
726 slot = (handles->size + 1) / 32;
727 handles->slot_hint = handles->size + 1;
728 handles->size = new_size;
730 handles->bitmap [slot] |= 1 << i;
731 slot = slot * 32 + i;
732 handles->entries [slot] = NULL;
733 if (handles->type <= HANDLE_WEAK_TRACK) {
734 /*FIXME, what to use when obj == null?*/
735 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
737 mono_gc_weak_link_add (&(handles->entries [slot]), obj, track);
739 handles->entries [slot] = obj;
742 #ifndef DISABLE_PERFCOUNTERS
743 mono_perfcounters->gc_num_handles++;
745 unlock_handles (handles);
746 /*g_print ("allocated entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
747 res = (slot << 3) | (handles->type + 1);
748 mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_CREATED, handles->type, res, obj);
754 * @obj: managed object to get a handle for
755 * @pinned: whether the object should be pinned
757 * This returns a handle that wraps the object, this is used to keep a
758 * reference to a managed object from the unmanaged world and preventing the
759 * object from being disposed.
761 * If @pinned is false the address of the object can not be obtained, if it is
762 * true the address of the object can be obtained. This will also pin the
763 * object so it will not be possible by a moving garbage collector to move the
766 * Returns: a handle that can be used to access the object from
770 mono_gchandle_new (MonoObject *obj, gboolean pinned)
772 return alloc_handle (&gc_handles [pinned? HANDLE_PINNED: HANDLE_NORMAL], obj, FALSE);
776 * mono_gchandle_new_weakref:
777 * @obj: managed object to get a handle for
778 * @pinned: whether the object should be pinned
780 * This returns a weak handle that wraps the object, this is used to
781 * keep a reference to a managed object from the unmanaged world.
782 * Unlike the mono_gchandle_new the object can be reclaimed by the
783 * garbage collector. In this case the value of the GCHandle will be
786 * If @pinned is false the address of the object can not be obtained, if it is
787 * true the address of the object can be obtained. This will also pin the
788 * object so it will not be possible by a moving garbage collector to move the
791 * Returns: a handle that can be used to access the object from
795 mono_gchandle_new_weakref (MonoObject *obj, gboolean track_resurrection)
797 guint32 handle = alloc_handle (&gc_handles [track_resurrection? HANDLE_WEAK_TRACK: HANDLE_WEAK], obj, track_resurrection);
803 mono_gchandle_get_type (guint32 gchandle)
805 guint type = (gchandle & 7) - 1;
811 * mono_gchandle_get_target:
812 * @gchandle: a GCHandle's handle.
814 * The handle was previously created by calling mono_gchandle_new or
815 * mono_gchandle_new_weakref.
817 * Returns a pointer to the MonoObject represented by the handle or
818 * NULL for a collected object if using a weakref handle.
821 mono_gchandle_get_target (guint32 gchandle)
823 guint slot = gchandle >> 3;
824 guint type = (gchandle & 7) - 1;
825 HandleData *handles = &gc_handles [type];
826 MonoObject *obj = NULL;
829 lock_handles (handles);
830 if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
831 if (handles->type <= HANDLE_WEAK_TRACK) {
832 obj = mono_gc_weak_link_get (&handles->entries [slot]);
834 obj = handles->entries [slot];
837 /* print a warning? */
839 unlock_handles (handles);
840 /*g_print ("get target of entry %d of type %d: %p\n", slot, handles->type, obj);*/
845 mono_gchandle_set_target (guint32 gchandle, MonoObject *obj)
847 guint slot = gchandle >> 3;
848 guint type = (gchandle & 7) - 1;
849 HandleData *handles = &gc_handles [type];
853 lock_handles (handles);
854 if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
855 if (handles->type <= HANDLE_WEAK_TRACK) {
856 if (handles->entries [slot])
857 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
859 mono_gc_weak_link_add (&handles->entries [slot], obj, handles->type == HANDLE_WEAK_TRACK);
860 /*FIXME, what to use when obj == null?*/
861 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
863 handles->entries [slot] = obj;
866 /* print a warning? */
868 /*g_print ("changed entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
869 unlock_handles (handles);
873 * mono_gchandle_is_in_domain:
874 * @gchandle: a GCHandle's handle.
875 * @domain: An application domain.
877 * Returns: true if the object wrapped by the @gchandle belongs to the specific @domain.
880 mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain)
882 guint slot = gchandle >> 3;
883 guint type = (gchandle & 7) - 1;
884 HandleData *handles = &gc_handles [type];
885 gboolean result = FALSE;
888 lock_handles (handles);
889 if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
890 if (handles->type <= HANDLE_WEAK_TRACK) {
891 result = domain->domain_id == handles->domain_ids [slot];
894 obj = handles->entries [slot];
898 result = domain == mono_object_domain (obj);
901 /* print a warning? */
903 unlock_handles (handles);
908 * mono_gchandle_free:
909 * @gchandle: a GCHandle's handle.
911 * Frees the @gchandle handle. If there are no outstanding
912 * references, the garbage collector can reclaim the memory of the
916 mono_gchandle_free (guint32 gchandle)
918 guint slot = gchandle >> 3;
919 guint type = (gchandle & 7) - 1;
920 HandleData *handles = &gc_handles [type];
924 lock_handles (handles);
925 if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
926 if (handles->type <= HANDLE_WEAK_TRACK) {
927 if (handles->entries [slot])
928 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
930 handles->entries [slot] = NULL;
932 handles->bitmap [slot / 32] &= ~(1 << (slot % 32));
934 /* print a warning? */
936 #ifndef DISABLE_PERFCOUNTERS
937 mono_perfcounters->gc_num_handles--;
939 /*g_print ("freed entry %d of type %d\n", slot, handles->type);*/
940 unlock_handles (handles);
941 mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_DESTROYED, handles->type, gchandle, NULL);
945 * mono_gchandle_free_domain:
946 * @domain: domain that is unloading
948 * Function used internally to cleanup any GC handle for objects belonging
949 * to the specified domain during appdomain unload.
952 mono_gchandle_free_domain (MonoDomain *domain)
956 for (type = 0; type < 3; ++type) {
958 HandleData *handles = &gc_handles [type];
959 lock_handles (handles);
960 for (slot = 0; slot < handles->size; ++slot) {
961 if (!(handles->bitmap [slot / 32] & (1 << (slot % 32))))
963 if (type <= HANDLE_WEAK_TRACK) {
964 if (domain->domain_id == handles->domain_ids [slot]) {
965 handles->bitmap [slot / 32] &= ~(1 << (slot % 32));
966 if (handles->entries [slot])
967 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
970 if (handles->entries [slot] && mono_object_domain (handles->entries [slot]) == domain) {
971 handles->bitmap [slot / 32] &= ~(1 << (slot % 32));
972 handles->entries [slot] = NULL;
976 unlock_handles (handles);
982 mono_gc_GCHandle_CheckCurrentDomain (guint32 gchandle)
984 return mono_gchandle_is_in_domain (gchandle, mono_domain_get ());
987 #ifdef MONO_HAS_SEMAPHORES
988 static MonoSemType finalizer_sem;
990 static HANDLE finalizer_event;
991 static volatile gboolean finished=FALSE;
994 mono_gc_finalize_notify (void)
997 g_message ( "%s: prodding finalizer", __func__);
1000 if (mono_gc_is_null ())
1003 #ifdef MONO_HAS_SEMAPHORES
1004 MONO_SEM_POST (&finalizer_sem);
1006 SetEvent (finalizer_event);
1010 #ifdef HAVE_BOEHM_GC
1013 collect_objects (gpointer key, gpointer value, gpointer user_data)
1015 GPtrArray *arr = (GPtrArray*)user_data;
1016 g_ptr_array_add (arr, key);
1022 * finalize_domain_objects:
1024 * Run the finalizers of all finalizable objects in req->domain.
1027 finalize_domain_objects (DomainFinalizationReq *req)
1029 MonoDomain *domain = req->domain;
1032 #define NUM_FOBJECTS 64
1033 MonoObject *to_finalize [NUM_FOBJECTS];
1037 /* Process finalizers which are already in the queue */
1038 mono_gc_invoke_finalizers ();
1040 #ifdef HAVE_BOEHM_GC
1041 while (g_hash_table_size (domain->finalizable_objects_hash) > 0) {
1045 * Since the domain is unloading, nobody is allowed to put
1046 * new entries into the hash table. But finalize_object might
1047 * remove entries from the hash table, so we make a copy.
1049 objs = g_ptr_array_new ();
1050 g_hash_table_foreach (domain->finalizable_objects_hash, collect_objects, objs);
1051 /* printf ("FINALIZING %d OBJECTS.\n", objs->len); */
1053 for (i = 0; i < objs->len; ++i) {
1054 MonoObject *o = (MonoObject*)g_ptr_array_index (objs, i);
1055 /* FIXME: Avoid finalizing threads, etc */
1056 mono_gc_run_finalize (o, 0);
1059 g_ptr_array_free (objs, TRUE);
1061 #elif defined(HAVE_SGEN_GC)
1062 while ((count = mono_gc_finalizers_for_domain (domain, to_finalize, NUM_FOBJECTS))) {
1064 for (i = 0; i < count; ++i) {
1065 mono_gc_run_finalize (to_finalize [i], 0);
1070 /* cleanup the reference queue */
1071 reference_queue_clear_for_domain (domain);
1073 /* printf ("DONE.\n"); */
1074 SetEvent (req->done_event);
1076 /* The event is closed in mono_domain_finalize if we get here */
1081 finalizer_thread (gpointer unused)
1083 gboolean wait = TRUE;
1086 /* Wait to be notified that there's at least one
1090 g_assert (mono_domain_get () == mono_get_root_domain ());
1091 mono_gc_set_skip_thread (TRUE);
1092 MONO_PREPARE_BLOCKING
1095 /* An alertable wait is required so this thread can be suspended on windows */
1096 #ifdef MONO_HAS_SEMAPHORES
1097 MONO_SEM_WAIT_ALERTABLE (&finalizer_sem, TRUE);
1099 WaitForSingleObjectEx (finalizer_event, INFINITE, TRUE);
1103 MONO_FINISH_BLOCKING
1104 mono_gc_set_skip_thread (FALSE);
1106 mono_threads_perform_thread_dump ();
1108 mono_console_handle_async_ops ();
1110 mono_attach_maybe_start ();
1112 if (domains_to_finalize) {
1113 mono_finalizer_lock ();
1114 if (domains_to_finalize) {
1115 DomainFinalizationReq *req = domains_to_finalize->data;
1116 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
1117 mono_finalizer_unlock ();
1119 finalize_domain_objects (req);
1121 mono_finalizer_unlock ();
1125 /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup),
1126 * before the domain is unloaded.
1128 mono_gc_invoke_finalizers ();
1130 mono_threads_join_threads ();
1132 reference_queue_proccess_all ();
1134 #ifdef MONO_HAS_SEMAPHORES
1135 /* Avoid posting the pending done event until there are pending finalizers */
1136 if (MONO_SEM_TIMEDWAIT (&finalizer_sem, 0) == 0)
1137 /* Don't wait again at the start of the loop */
1140 SetEvent (pending_done_event);
1142 SetEvent (pending_done_event);
1146 mono_finalizer_lock ();
1147 finalizer_thread_exited = TRUE;
1148 mono_cond_signal (&exited_cond);
1149 mono_finalizer_unlock ();
1154 #ifndef LAZY_GC_THREAD_CREATION
1158 mono_gc_init_finalizer_thread (void)
1160 gc_thread = mono_thread_create_internal (mono_domain_get (), finalizer_thread, NULL, FALSE, 0);
1161 ves_icall_System_Threading_Thread_SetName_internal (gc_thread, mono_string_new (mono_domain_get (), "Finalizer"));
1167 mono_mutex_init_recursive (&handle_section);
1168 mono_mutex_init_recursive (&allocator_section);
1170 mono_mutex_init_recursive (&finalizer_mutex);
1171 mono_mutex_init_recursive (&reference_queue_mutex);
1173 MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_NORMAL].entries);
1174 MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_PINNED].entries);
1176 mono_counters_register ("Minor GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.minor_gc_count);
1177 mono_counters_register ("Major GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.major_gc_count);
1178 mono_counters_register ("Minor GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.minor_gc_time);
1179 mono_counters_register ("Major GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time);
1180 mono_counters_register ("Major GC time concurrent", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time_concurrent);
1182 mono_gc_base_init ();
1184 if (mono_gc_is_disabled ()) {
1189 finalizer_event = CreateEvent (NULL, FALSE, FALSE, NULL);
1190 g_assert (finalizer_event);
1191 pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
1192 g_assert (pending_done_event);
1193 mono_cond_init (&exited_cond, 0);
1194 #ifdef MONO_HAS_SEMAPHORES
1195 MONO_SEM_INIT (&finalizer_sem, 0);
1198 #ifndef LAZY_GC_THREAD_CREATION
1199 mono_gc_init_finalizer_thread ();
1204 mono_gc_cleanup (void)
1207 g_message ("%s: cleaning up finalizer", __func__);
1210 if (mono_gc_is_null ())
1215 if (mono_thread_internal_current () != gc_thread) {
1216 gboolean timed_out = FALSE;
1217 guint32 start_ticks = mono_msec_ticks ();
1218 guint32 end_ticks = start_ticks + 2000;
1220 mono_gc_finalize_notify ();
1221 /* Finishing the finalizer thread, so wait a little bit... */
1222 /* MS seems to wait for about 2 seconds */
1223 while (!finalizer_thread_exited) {
1224 guint32 current_ticks = mono_msec_ticks ();
1227 if (current_ticks >= end_ticks)
1230 timeout = end_ticks - current_ticks;
1231 MONO_PREPARE_BLOCKING;
1232 mono_finalizer_lock ();
1233 if (!finalizer_thread_exited)
1234 mono_cond_timedwait_ms (&exited_cond, &finalizer_mutex, timeout);
1235 mono_finalizer_unlock ();
1236 MONO_FINISH_BLOCKING;
1239 if (!finalizer_thread_exited) {
1242 /* Set a flag which the finalizer thread can check */
1243 suspend_finalizers = TRUE;
1245 /* Try to abort the thread, in the hope that it is running managed code */
1246 mono_thread_internal_stop (gc_thread);
1248 /* Wait for it to stop */
1249 ret = guarded_wait (gc_thread->handle, 100, TRUE);
1251 if (ret == WAIT_TIMEOUT) {
1253 * The finalizer thread refused to die. There is not much we
1254 * can do here, since the runtime is shutting down so the
1255 * state the finalizer thread depends on will vanish.
1257 g_warning ("Shutting down finalizer thread timed out.");
1265 /* Wait for the thread to actually exit */
1266 ret = guarded_wait (gc_thread->handle, INFINITE, TRUE);
1267 g_assert (ret == WAIT_OBJECT_0);
1269 mono_thread_join (GUINT_TO_POINTER (gc_thread->tid));
1273 mono_gc_base_cleanup ();
1276 mono_reference_queue_cleanup ();
1278 mono_mutex_destroy (&handle_section);
1279 mono_mutex_destroy (&allocator_section);
1280 mono_mutex_destroy (&finalizer_mutex);
1281 mono_mutex_destroy (&reference_queue_mutex);
1285 mono_gc_is_finalizer_internal_thread (MonoInternalThread *thread)
1287 return thread == gc_thread;
1291 * mono_gc_is_finalizer_thread:
1292 * @thread: the thread to test.
1294 * In Mono objects are finalized asynchronously on a separate thread.
1295 * This routine tests whether the @thread argument represents the
1296 * finalization thread.
1298 * Returns true if @thread is the finalization thread.
1301 mono_gc_is_finalizer_thread (MonoThread *thread)
1303 return mono_gc_is_finalizer_internal_thread (thread->internal_thread);
1306 #if defined(__MACH__)
1307 static pthread_t mach_exception_thread;
1310 mono_gc_register_mach_exception_thread (pthread_t thread)
1312 mach_exception_thread = thread;
1316 mono_gc_get_mach_exception_thread (void)
1318 return mach_exception_thread;
1322 #ifndef HAVE_SGEN_GC
1324 mono_gc_alloc_mature (MonoVTable *vtable)
1326 return mono_object_new_specific (vtable);
1331 static MonoReferenceQueue *ref_queues;
1334 ref_list_remove_element (RefQueueEntry **prev, RefQueueEntry *element)
1337 /* Guard if head is changed concurrently. */
1338 while (*prev != element)
1339 prev = &(*prev)->next;
1340 } while (prev && InterlockedCompareExchangePointer ((void*)prev, element->next, element) != element);
1344 ref_list_push (RefQueueEntry **head, RefQueueEntry *value)
1346 RefQueueEntry *current;
1349 value->next = current;
1350 STORE_STORE_FENCE; /*Must make sure the previous store is visible before the CAS. */
1351 } while (InterlockedCompareExchangePointer ((void*)head, value, current) != current);
1355 reference_queue_proccess (MonoReferenceQueue *queue)
1357 RefQueueEntry **iter = &queue->queue;
1358 RefQueueEntry *entry;
1359 while ((entry = *iter)) {
1361 if (queue->should_be_deleted || !mono_gc_weak_link_get (&entry->dis_link)) {
1362 mono_gc_weak_link_remove (&entry->dis_link, TRUE);
1364 if (queue->should_be_deleted || !mono_gchandle_get_target (entry->gchandle)) {
1365 mono_gchandle_free ((guint32)entry->gchandle);
1367 ref_list_remove_element (iter, entry);
1368 queue->callback (entry->user_data);
1371 iter = &entry->next;
1377 reference_queue_proccess_all (void)
1379 MonoReferenceQueue **iter;
1380 MonoReferenceQueue *queue = ref_queues;
1381 for (; queue; queue = queue->next)
1382 reference_queue_proccess (queue);
1385 mono_mutex_lock (&reference_queue_mutex);
1386 for (iter = &ref_queues; *iter;) {
1388 if (!queue->should_be_deleted) {
1389 iter = &queue->next;
1393 mono_mutex_unlock (&reference_queue_mutex);
1394 reference_queue_proccess (queue);
1397 *iter = queue->next;
1400 mono_mutex_unlock (&reference_queue_mutex);
1404 mono_reference_queue_cleanup (void)
1406 MonoReferenceQueue *queue = ref_queues;
1407 for (; queue; queue = queue->next)
1408 queue->should_be_deleted = TRUE;
1409 reference_queue_proccess_all ();
1413 reference_queue_clear_for_domain (MonoDomain *domain)
1415 MonoReferenceQueue *queue = ref_queues;
1416 for (; queue; queue = queue->next) {
1417 RefQueueEntry **iter = &queue->queue;
1418 RefQueueEntry *entry;
1419 while ((entry = *iter)) {
1420 if (entry->domain == domain) {
1422 mono_gc_weak_link_remove (&entry->dis_link, TRUE);
1424 mono_gchandle_free ((guint32)entry->gchandle);
1426 ref_list_remove_element (iter, entry);
1427 queue->callback (entry->user_data);
1430 iter = &entry->next;
1436 * mono_gc_reference_queue_new:
1437 * @callback callback used when processing collected entries.
1439 * Create a new reference queue used to process collected objects.
1440 * A reference queue let you add a pair of (managed object, user data)
1441 * using the mono_gc_reference_queue_add method.
1443 * Once the managed object is collected @callback will be called
1444 * in the finalizer thread with 'user data' as argument.
1446 * The callback is called from the finalizer thread without any locks held.
1447 * When a AppDomain is unloaded, all callbacks for objects belonging to it
1450 * @returns the new queue.
1453 mono_gc_reference_queue_new (mono_reference_queue_callback callback)
1455 MonoReferenceQueue *res = g_new0 (MonoReferenceQueue, 1);
1456 res->callback = callback;
1458 mono_mutex_lock (&reference_queue_mutex);
1459 res->next = ref_queues;
1461 mono_mutex_unlock (&reference_queue_mutex);
1467 * mono_gc_reference_queue_add:
1468 * @queue the queue to add the reference to.
1469 * @obj the object to be watched for collection
1470 * @user_data parameter to be passed to the queue callback
1472 * Queue an object to be watched for collection, when the @obj is
1473 * collected, the callback that was registered for the @queue will
1474 * be invoked with @user_data as argument.
1476 * @returns false if the queue is scheduled to be freed.
1479 mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *user_data)
1481 RefQueueEntry *entry;
1482 if (queue->should_be_deleted)
1485 entry = g_new0 (RefQueueEntry, 1);
1486 entry->user_data = user_data;
1487 entry->domain = mono_object_domain (obj);
1490 mono_gc_weak_link_add (&entry->dis_link, obj, TRUE);
1492 entry->gchandle = mono_gchandle_new_weakref (obj, TRUE);
1493 mono_object_register_finalizer (obj);
1496 ref_list_push (&queue->queue, entry);
1501 * mono_gc_reference_queue_free:
1502 * @queue the queue that should be freed.
1504 * This operation signals that @queue should be freed. This operation is deferred
1505 * as it happens on the finalizer thread.
1507 * After this call, no further objects can be queued. It's the responsibility of the
1508 * caller to make sure that no further attempt to access queue will be made.
1511 mono_gc_reference_queue_free (MonoReferenceQueue *queue)
1513 queue->should_be_deleted = TRUE;