2 * metadata/gc.c: GC icalls.
4 * Author: Paolo Molaro <lupus@ximian.com>
6 * Copyright 2002-2003 Ximian, Inc (http://www.ximian.com)
7 * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
8 * Copyright 2012 Xamarin Inc (http://www.xamarin.com)
15 #include <mono/metadata/gc-internal.h>
16 #include <mono/metadata/mono-gc.h>
17 #include <mono/metadata/threads.h>
18 #include <mono/metadata/tabledefs.h>
19 #include <mono/metadata/exception.h>
20 #include <mono/metadata/profiler-private.h>
21 #include <mono/metadata/domain-internals.h>
22 #include <mono/metadata/class-internals.h>
23 #include <mono/metadata/metadata-internals.h>
24 #include <mono/metadata/mono-mlist.h>
25 #include <mono/metadata/threadpool.h>
26 #include <mono/metadata/threadpool-internals.h>
27 #include <mono/metadata/threads-types.h>
28 #include <mono/sgen/sgen-conf.h>
29 #include <mono/utils/mono-logger-internal.h>
30 #include <mono/metadata/gc-internal.h>
31 #include <mono/metadata/marshal.h> /* for mono_delegate_free_ftnptr () */
32 #include <mono/metadata/attach.h>
33 #include <mono/metadata/console-io.h>
34 #include <mono/utils/mono-semaphore.h>
35 #include <mono/utils/mono-memory-model.h>
36 #include <mono/utils/mono-counters.h>
37 #include <mono/utils/mono-time.h>
38 #include <mono/utils/dtrace.h>
39 #include <mono/utils/mono-threads.h>
40 #include <mono/utils/atomic.h>
46 typedef struct DomainFinalizationReq {
49 } DomainFinalizationReq;
51 #ifdef PLATFORM_WINCE /* FIXME: add accessors to gc.dll API */
52 extern void (*__imp_GC_finalizer_notifier)(void);
53 #define GC_finalizer_notifier __imp_GC_finalizer_notifier
54 extern int __imp_GC_finalize_on_demand;
55 #define GC_finalize_on_demand __imp_GC_finalize_on_demand
58 static gboolean gc_disabled = FALSE;
60 static gboolean finalizing_root_domain = FALSE;
62 gboolean log_finalizers = FALSE;
63 gboolean do_not_finalize = FALSE;
65 #define mono_finalizer_lock() mono_mutex_lock (&finalizer_mutex)
66 #define mono_finalizer_unlock() mono_mutex_unlock (&finalizer_mutex)
67 static mono_mutex_t finalizer_mutex;
68 static mono_mutex_t reference_queue_mutex;
70 static GSList *domains_to_finalize= NULL;
71 static MonoMList *threads_to_finalize = NULL;
73 static gboolean finalizer_thread_exited;
74 /* Uses finalizer_mutex */
75 static mono_cond_t exited_cond;
77 static MonoInternalThread *gc_thread;
79 static void object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*));
81 static void mono_gchandle_set_target (guint32 gchandle, MonoObject *obj);
83 static void reference_queue_proccess_all (void);
84 static void mono_reference_queue_cleanup (void);
85 static void reference_queue_clear_for_domain (MonoDomain *domain);
87 static HANDLE pending_done_event;
91 guarded_wait (HANDLE handle, guint32 timeout, gboolean alertable)
96 result = WaitForSingleObjectEx (handle, timeout, alertable);
103 add_thread_to_finalize (MonoInternalThread *thread)
105 mono_finalizer_lock ();
106 if (!threads_to_finalize)
107 MONO_GC_REGISTER_ROOT_SINGLE (threads_to_finalize);
108 threads_to_finalize = mono_mlist_append (threads_to_finalize, (MonoObject*)thread);
109 mono_finalizer_unlock ();
112 static gboolean suspend_finalizers = FALSE;
114 * actually, we might want to queue the finalize requests in a separate thread,
115 * but we need to be careful about the execution domain of the thread...
118 mono_gc_run_finalize (void *obj, void *data)
123 MonoObject *exc = NULL;
128 MonoMethod* finalizer = NULL;
129 MonoDomain *caller_domain = mono_domain_get ();
131 RuntimeInvokeFunction runtime_invoke;
133 o = (MonoObject*)((char*)obj + GPOINTER_TO_UINT (data));
136 g_log ("mono-gc-finalizers", G_LOG_LEVEL_DEBUG, "<%s at %p> Starting finalizer checks.", o->vtable->klass->name, o);
138 if (suspend_finalizers)
141 domain = o->vtable->domain;
144 mono_domain_finalizers_lock (domain);
146 o2 = g_hash_table_lookup (domain->finalizable_objects_hash, o);
148 mono_domain_finalizers_unlock (domain);
151 /* Already finalized somehow */
155 /* make sure the finalizer is not called again if the object is resurrected */
156 object_register_finalizer (obj, NULL);
159 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Registered finalizer as processed.", o->vtable->klass->name, o);
161 if (o->vtable->klass == mono_defaults.internal_thread_class) {
162 MonoInternalThread *t = (MonoInternalThread*)o;
164 if (mono_gc_is_finalizer_internal_thread (t))
165 /* Avoid finalizing ourselves */
168 if (t->threadpool_thread && finalizing_root_domain) {
169 /* Don't finalize threadpool threads when
170 shutting down - they're finalized when the
171 threadpool shuts down. */
172 add_thread_to_finalize (t);
177 if (o->vtable->klass->image == mono_defaults.corlib && !strcmp (o->vtable->klass->name, "DynamicMethod") && finalizing_root_domain) {
179 * These can't be finalized during unloading/shutdown, since that would
180 * free the native code which can still be referenced by other
182 * FIXME: This is not perfect, objects dying at the same time as
183 * dynamic methods can still reference them even when !shutdown.
188 if (mono_runtime_get_no_exec ())
191 /* speedup later... and use a timeout */
192 /* g_print ("Finalize run on %p %s.%s\n", o, mono_object_class (o)->name_space, mono_object_class (o)->name); */
194 /* Use _internal here, since this thread can enter a doomed appdomain */
195 mono_domain_set_internal (mono_object_domain (o));
197 /* delegates that have a native function pointer allocated are
198 * registered for finalization, but they don't have a Finalize
199 * method, because in most cases it's not needed and it's just a waste.
201 if (o->vtable->klass->delegate) {
202 MonoDelegate* del = (MonoDelegate*)o;
203 if (del->delegate_trampoline)
204 mono_delegate_free_ftnptr ((MonoDelegate*)o);
205 mono_domain_set_internal (caller_domain);
209 finalizer = mono_class_get_finalizer (o->vtable->klass);
212 /* If object has a CCW but has no finalizer, it was only
213 * registered for finalization in order to free the CCW.
214 * Else it needs the regular finalizer run.
215 * FIXME: what to do about ressurection and suppression
216 * of finalizer on object with CCW.
218 if (mono_marshal_free_ccw (o) && !finalizer) {
219 mono_domain_set_internal (caller_domain);
225 * To avoid the locking plus the other overhead of mono_runtime_invoke (),
226 * create and precompile a wrapper which calls the finalize method using
230 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Compiling finalizer.", o->vtable->klass->name, o);
232 if (!domain->finalize_runtime_invoke) {
233 MonoMethod *invoke = mono_marshal_get_runtime_invoke (mono_class_get_method_from_name_flags (mono_defaults.object_class, "Finalize", 0, 0), TRUE);
235 domain->finalize_runtime_invoke = mono_compile_method (invoke);
238 runtime_invoke = domain->finalize_runtime_invoke;
240 mono_runtime_class_init (o->vtable);
242 if (G_UNLIKELY (MONO_GC_FINALIZE_INVOKE_ENABLED ())) {
243 MONO_GC_FINALIZE_INVOKE ((unsigned long)o, mono_object_get_size (o),
244 o->vtable->klass->name_space, o->vtable->klass->name);
248 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Calling finalizer.", o->vtable->klass->name, o);
250 runtime_invoke (o, NULL, &exc, NULL);
253 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Returned from finalizer.", o->vtable->klass->name, o);
256 mono_internal_thread_unhandled_exception (exc);
258 mono_domain_set_internal (caller_domain);
262 mono_gc_finalize_threadpool_threads (void)
264 while (threads_to_finalize) {
265 MonoInternalThread *thread = (MonoInternalThread*) mono_mlist_get_data (threads_to_finalize);
267 /* Force finalization of the thread. */
268 thread->threadpool_thread = FALSE;
269 mono_object_register_finalizer ((MonoObject*)thread);
271 mono_gc_run_finalize (thread, NULL);
273 threads_to_finalize = mono_mlist_next (threads_to_finalize);
278 mono_gc_out_of_memory (size_t size)
281 * we could allocate at program startup some memory that we could release
282 * back to the system at this point if we're really low on memory (ie, size is
283 * lower than the memory we set apart)
285 mono_raise_exception (mono_domain_get ()->out_of_memory_ex);
291 * Some of our objects may point to a different address than the address returned by GC_malloc()
292 * (because of the GetHashCode hack), but we need to pass the real address to register_finalizer.
293 * This also means that in the callback we need to adjust the pointer to get back the real
295 * We also need to be consistent in the use of the GC_debug* variants of malloc and register_finalizer,
296 * since that, too, can cause the underlying pointer to be offset.
299 object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*))
304 mono_raise_exception (mono_get_exception_argument_null ("obj"));
306 domain = obj->vtable->domain;
309 if (mono_domain_is_unloading (domain) && (callback != NULL))
311 * Can't register finalizers in a dying appdomain, since they
312 * could be invoked after the appdomain has been unloaded.
316 mono_domain_finalizers_lock (domain);
319 g_hash_table_insert (domain->finalizable_objects_hash, obj, obj);
321 g_hash_table_remove (domain->finalizable_objects_hash, obj);
323 mono_domain_finalizers_unlock (domain);
325 mono_gc_register_for_finalization (obj, callback);
326 #elif defined(HAVE_SGEN_GC)
328 * If we register finalizers for domains that are unloading we might
329 * end up running them while or after the domain is being cleared, so
330 * the objects will not be valid anymore.
332 if (!mono_domain_is_unloading (domain))
333 mono_gc_register_for_finalization (obj, callback);
338 * mono_object_register_finalizer:
339 * @obj: object to register
341 * Records that object @obj has a finalizer, this will call the
342 * Finalize method when the garbage collector disposes the object.
346 mono_object_register_finalizer (MonoObject *obj)
348 /* g_print ("Registered finalizer on %p %s.%s\n", obj, mono_object_class (obj)->name_space, mono_object_class (obj)->name); */
349 object_register_finalizer (obj, mono_gc_run_finalize);
353 * mono_domain_finalize:
354 * @domain: the domain to finalize
355 * @timeout: msects to wait for the finalization to complete, -1 to wait indefinitely
357 * Request finalization of all finalizable objects inside @domain. Wait
358 * @timeout msecs for the finalization to complete.
360 * Returns: TRUE if succeeded, FALSE if there was a timeout
364 mono_domain_finalize (MonoDomain *domain, guint32 timeout)
366 DomainFinalizationReq *req;
369 MonoInternalThread *thread = mono_thread_internal_current ();
371 #if defined(__native_client__)
375 if (mono_thread_internal_current () == gc_thread)
376 /* We are called from inside a finalizer, not much we can do here */
380 * No need to create another thread 'cause the finalizer thread
381 * is still working and will take care of running the finalizers
388 mono_gc_collect (mono_gc_max_generation ());
390 done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
391 if (done_event == NULL) {
395 req = g_new0 (DomainFinalizationReq, 1);
396 req->domain = domain;
397 req->done_event = done_event;
399 if (domain == mono_get_root_domain ())
400 finalizing_root_domain = TRUE;
402 mono_finalizer_lock ();
404 domains_to_finalize = g_slist_append (domains_to_finalize, req);
406 mono_finalizer_unlock ();
408 /* Tell the finalizer thread to finalize this appdomain */
409 mono_gc_finalize_notify ();
415 res = guarded_wait (done_event, timeout, TRUE);
416 /* printf ("WAIT RES: %d.\n", res); */
418 if (res == WAIT_IO_COMPLETION) {
419 if ((thread->state & (ThreadState_StopRequested | ThreadState_SuspendRequested)) != 0)
421 } else if (res == WAIT_TIMEOUT) {
422 /* We leak the handle here */
429 CloseHandle (done_event);
431 if (domain == mono_get_root_domain ()) {
432 mono_thread_pool_cleanup ();
433 mono_gc_finalize_threadpool_threads ();
438 /* We don't support domain finalization without a GC */
444 ves_icall_System_GC_InternalCollect (int generation)
446 mono_gc_collect (generation);
450 ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection)
453 mono_gc_collect (mono_gc_max_generation ());
454 return mono_gc_get_used_size ();
458 ves_icall_System_GC_KeepAlive (MonoObject *obj)
466 ves_icall_System_GC_ReRegisterForFinalize (MonoObject *obj)
468 MONO_CHECK_ARG_NULL (obj,);
470 object_register_finalizer (obj, mono_gc_run_finalize);
474 ves_icall_System_GC_SuppressFinalize (MonoObject *obj)
476 MONO_CHECK_ARG_NULL (obj,);
478 /* delegates have no finalizers, but we register them to deal with the
479 * unmanaged->managed trampoline. We don't let the user suppress it
480 * otherwise we'd leak it.
482 if (obj->vtable->klass->delegate)
485 /* FIXME: Need to handle case where obj has COM Callable Wrapper
486 * generated for it that needs cleaned up, but user wants to suppress
487 * their derived object finalizer. */
489 object_register_finalizer (obj, NULL);
493 ves_icall_System_GC_WaitForPendingFinalizers (void)
496 if (!mono_gc_pending_finalizers ())
499 if (mono_thread_internal_current () == gc_thread)
500 /* Avoid deadlocks */
504 If the finalizer thread is not live, lets pretend no finalizers are pending since the current thread might
505 be the one responsible for starting it up.
507 if (gc_thread == NULL)
510 ResetEvent (pending_done_event);
511 mono_gc_finalize_notify ();
512 /* g_print ("Waiting for pending finalizers....\n"); */
513 guarded_wait (pending_done_event, INFINITE, TRUE);
514 /* g_print ("Done pending....\n"); */
519 ves_icall_System_GC_register_ephemeron_array (MonoObject *array)
522 if (!mono_gc_ephemeron_array_add (array)) {
523 mono_set_pending_exception (mono_object_domain (array)->out_of_memory_ex);
530 ves_icall_System_GC_get_ephemeron_tombstone (void)
532 return mono_domain_get ()->ephemeron_tombstone;
535 #define mono_allocator_lock() mono_mutex_lock (&allocator_section)
536 #define mono_allocator_unlock() mono_mutex_unlock (&allocator_section)
537 static mono_mutex_t allocator_section;
538 static mono_mutex_t handle_section;
547 static HandleType mono_gchandle_get_type (guint32 gchandle);
550 ves_icall_System_GCHandle_GetTarget (guint32 handle)
552 return mono_gchandle_get_target (handle);
556 * if type == -1, change the target of the handle, otherwise allocate a new handle.
559 ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type)
562 mono_gchandle_set_target (handle, obj);
563 /* the handle doesn't change */
568 return mono_gchandle_new_weakref (obj, FALSE);
569 case HANDLE_WEAK_TRACK:
570 return mono_gchandle_new_weakref (obj, TRUE);
572 return mono_gchandle_new (obj, FALSE);
574 return mono_gchandle_new (obj, TRUE);
576 g_assert_not_reached ();
582 ves_icall_System_GCHandle_FreeHandle (guint32 handle)
584 mono_gchandle_free (handle);
588 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
592 if (mono_gchandle_get_type (handle) != HANDLE_PINNED)
594 obj = mono_gchandle_get_target (handle);
596 MonoClass *klass = mono_object_class (obj);
597 if (klass == mono_defaults.string_class) {
598 return mono_string_chars ((MonoString*)obj);
599 } else if (klass->rank) {
600 return mono_array_addr ((MonoArray*)obj, char, 0);
602 /* the C# code will check and throw the exception */
603 /* FIXME: missing !klass->blittable test, see bug #61134 */
604 if ((klass->flags & TYPE_ATTRIBUTE_LAYOUT_MASK) == TYPE_ATTRIBUTE_AUTO_LAYOUT)
606 return (char*)obj + sizeof (MonoObject);
613 ves_icall_Mono_Runtime_SetGCAllowSynchronousMajor (MonoBoolean flag)
615 return mono_gc_set_allow_synchronous_major (flag);
623 guint slot_hint : 24; /* starting slot for search */
624 /* 2^16 appdomains should be enough for everyone (though I know I'll regret this in 20 years) */
625 /* we alloc this only for weak refs, since we can get the domain directly in the other cases */
629 /* weak and weak-track arrays will be allocated in malloc memory
631 static HandleData gc_handles [] = {
632 {NULL, NULL, 0, HANDLE_WEAK, 0},
633 {NULL, NULL, 0, HANDLE_WEAK_TRACK, 0},
634 {NULL, NULL, 0, HANDLE_NORMAL, 0},
635 {NULL, NULL, 0, HANDLE_PINNED, 0}
638 #define lock_handles(handles) mono_mutex_lock (&handle_section)
639 #define unlock_handles(handles) mono_mutex_unlock (&handle_section)
642 find_first_unset (guint32 bitmap)
645 for (i = 0; i < 32; ++i) {
646 if (!(bitmap & (1 << i)))
653 make_root_descr_all_refs (int numbits, gboolean pinned)
659 return mono_gc_make_root_descr_all_refs (numbits);
663 alloc_handle (HandleData *handles, MonoObject *obj, gboolean track)
667 lock_handles (handles);
668 if (!handles->size) {
670 if (handles->type > HANDLE_WEAK_TRACK) {
671 handles->entries = mono_gc_alloc_fixed (sizeof (gpointer) * handles->size, make_root_descr_all_refs (handles->size, handles->type == HANDLE_PINNED));
673 handles->entries = g_malloc0 (sizeof (gpointer) * handles->size);
674 handles->domain_ids = g_malloc0 (sizeof (guint16) * handles->size);
676 handles->bitmap = g_malloc0 (handles->size / 8);
679 for (slot = handles->slot_hint; slot < handles->size / 32; ++slot) {
680 if (handles->bitmap [slot] != 0xffffffff) {
681 i = find_first_unset (handles->bitmap [slot]);
682 handles->slot_hint = slot;
686 if (i == -1 && handles->slot_hint != 0) {
687 for (slot = 0; slot < handles->slot_hint; ++slot) {
688 if (handles->bitmap [slot] != 0xffffffff) {
689 i = find_first_unset (handles->bitmap [slot]);
690 handles->slot_hint = slot;
697 guint32 new_size = handles->size * 2; /* always double: we memset to 0 based on this below */
699 /* resize and copy the bitmap */
700 new_bitmap = g_malloc0 (new_size / 8);
701 memcpy (new_bitmap, handles->bitmap, handles->size / 8);
702 g_free (handles->bitmap);
703 handles->bitmap = new_bitmap;
705 /* resize and copy the entries */
706 if (handles->type > HANDLE_WEAK_TRACK) {
709 entries = mono_gc_alloc_fixed (sizeof (gpointer) * new_size, make_root_descr_all_refs (new_size, handles->type == HANDLE_PINNED));
710 mono_gc_memmove_aligned (entries, handles->entries, sizeof (gpointer) * handles->size);
712 mono_gc_free_fixed (handles->entries);
713 handles->entries = entries;
717 domain_ids = g_malloc0 (sizeof (guint16) * new_size);
718 entries = g_malloc0 (sizeof (gpointer) * new_size);
719 memcpy (domain_ids, handles->domain_ids, sizeof (guint16) * handles->size);
720 for (i = 0; i < handles->size; ++i) {
721 MonoObject *obj = mono_gc_weak_link_get (&(handles->entries [i]));
723 mono_gc_weak_link_add (&(entries [i]), obj, track);
724 mono_gc_weak_link_remove (&(handles->entries [i]), track);
726 g_assert (!handles->entries [i]);
729 g_free (handles->entries);
730 g_free (handles->domain_ids);
731 handles->entries = entries;
732 handles->domain_ids = domain_ids;
735 /* set i and slot to the next free position */
737 slot = (handles->size + 1) / 32;
738 handles->slot_hint = handles->size + 1;
739 handles->size = new_size;
741 handles->bitmap [slot] |= 1 << i;
742 slot = slot * 32 + i;
743 handles->entries [slot] = NULL;
744 if (handles->type <= HANDLE_WEAK_TRACK) {
745 /*FIXME, what to use when obj == null?*/
746 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
748 mono_gc_weak_link_add (&(handles->entries [slot]), obj, track);
750 handles->entries [slot] = obj;
753 #ifndef DISABLE_PERFCOUNTERS
754 mono_perfcounters->gc_num_handles++;
756 unlock_handles (handles);
757 /*g_print ("allocated entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
758 res = (slot << 3) | (handles->type + 1);
759 mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_CREATED, handles->type, res, obj);
765 * @obj: managed object to get a handle for
766 * @pinned: whether the object should be pinned
768 * This returns a handle that wraps the object, this is used to keep a
769 * reference to a managed object from the unmanaged world and preventing the
770 * object from being disposed.
772 * If @pinned is false the address of the object can not be obtained, if it is
773 * true the address of the object can be obtained. This will also pin the
774 * object so it will not be possible by a moving garbage collector to move the
777 * Returns: a handle that can be used to access the object from
781 mono_gchandle_new (MonoObject *obj, gboolean pinned)
783 return alloc_handle (&gc_handles [pinned? HANDLE_PINNED: HANDLE_NORMAL], obj, FALSE);
787 * mono_gchandle_new_weakref:
788 * @obj: managed object to get a handle for
789 * @pinned: whether the object should be pinned
791 * This returns a weak handle that wraps the object, this is used to
792 * keep a reference to a managed object from the unmanaged world.
793 * Unlike the mono_gchandle_new the object can be reclaimed by the
794 * garbage collector. In this case the value of the GCHandle will be
797 * If @pinned is false the address of the object can not be obtained, if it is
798 * true the address of the object can be obtained. This will also pin the
799 * object so it will not be possible by a moving garbage collector to move the
802 * Returns: a handle that can be used to access the object from
806 mono_gchandle_new_weakref (MonoObject *obj, gboolean track_resurrection)
808 guint32 handle = alloc_handle (&gc_handles [track_resurrection? HANDLE_WEAK_TRACK: HANDLE_WEAK], obj, track_resurrection);
814 mono_gchandle_get_type (guint32 gchandle)
816 guint type = (gchandle & 7) - 1;
822 * mono_gchandle_get_target:
823 * @gchandle: a GCHandle's handle.
825 * The handle was previously created by calling mono_gchandle_new or
826 * mono_gchandle_new_weakref.
828 * Returns a pointer to the MonoObject represented by the handle or
829 * NULL for a collected object if using a weakref handle.
832 mono_gchandle_get_target (guint32 gchandle)
834 guint slot = gchandle >> 3;
835 guint type = (gchandle & 7) - 1;
836 HandleData *handles = &gc_handles [type];
837 MonoObject *obj = NULL;
840 lock_handles (handles);
841 if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
842 if (handles->type <= HANDLE_WEAK_TRACK) {
843 obj = mono_gc_weak_link_get (&handles->entries [slot]);
845 obj = handles->entries [slot];
848 /* print a warning? */
850 unlock_handles (handles);
851 /*g_print ("get target of entry %d of type %d: %p\n", slot, handles->type, obj);*/
856 mono_gchandle_set_target (guint32 gchandle, MonoObject *obj)
858 guint slot = gchandle >> 3;
859 guint type = (gchandle & 7) - 1;
860 HandleData *handles = &gc_handles [type];
864 lock_handles (handles);
865 if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
866 if (handles->type <= HANDLE_WEAK_TRACK) {
867 if (handles->entries [slot])
868 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
870 mono_gc_weak_link_add (&handles->entries [slot], obj, handles->type == HANDLE_WEAK_TRACK);
871 /*FIXME, what to use when obj == null?*/
872 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
874 handles->entries [slot] = obj;
877 /* print a warning? */
879 /*g_print ("changed entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
880 unlock_handles (handles);
884 * mono_gchandle_is_in_domain:
885 * @gchandle: a GCHandle's handle.
886 * @domain: An application domain.
888 * Returns: true if the object wrapped by the @gchandle belongs to the specific @domain.
891 mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain)
893 guint slot = gchandle >> 3;
894 guint type = (gchandle & 7) - 1;
895 HandleData *handles = &gc_handles [type];
896 gboolean result = FALSE;
899 lock_handles (handles);
900 if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
901 if (handles->type <= HANDLE_WEAK_TRACK) {
902 result = domain->domain_id == handles->domain_ids [slot];
905 obj = handles->entries [slot];
909 result = domain == mono_object_domain (obj);
912 /* print a warning? */
914 unlock_handles (handles);
919 * mono_gchandle_free:
920 * @gchandle: a GCHandle's handle.
922 * Frees the @gchandle handle. If there are no outstanding
923 * references, the garbage collector can reclaim the memory of the
927 mono_gchandle_free (guint32 gchandle)
929 guint slot = gchandle >> 3;
930 guint type = (gchandle & 7) - 1;
931 HandleData *handles = &gc_handles [type];
935 lock_handles (handles);
936 if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
937 if (handles->type <= HANDLE_WEAK_TRACK) {
938 if (handles->entries [slot])
939 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
941 handles->entries [slot] = NULL;
943 handles->bitmap [slot / 32] &= ~(1 << (slot % 32));
945 /* print a warning? */
947 #ifndef DISABLE_PERFCOUNTERS
948 mono_perfcounters->gc_num_handles--;
950 /*g_print ("freed entry %d of type %d\n", slot, handles->type);*/
951 unlock_handles (handles);
952 mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_DESTROYED, handles->type, gchandle, NULL);
956 * mono_gchandle_free_domain:
957 * @domain: domain that is unloading
959 * Function used internally to cleanup any GC handle for objects belonging
960 * to the specified domain during appdomain unload.
963 mono_gchandle_free_domain (MonoDomain *domain)
967 for (type = 0; type < 3; ++type) {
969 HandleData *handles = &gc_handles [type];
970 lock_handles (handles);
971 for (slot = 0; slot < handles->size; ++slot) {
972 if (!(handles->bitmap [slot / 32] & (1 << (slot % 32))))
974 if (type <= HANDLE_WEAK_TRACK) {
975 if (domain->domain_id == handles->domain_ids [slot]) {
976 handles->bitmap [slot / 32] &= ~(1 << (slot % 32));
977 if (handles->entries [slot])
978 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
981 if (handles->entries [slot] && mono_object_domain (handles->entries [slot]) == domain) {
982 handles->bitmap [slot / 32] &= ~(1 << (slot % 32));
983 handles->entries [slot] = NULL;
987 unlock_handles (handles);
993 mono_gc_GCHandle_CheckCurrentDomain (guint32 gchandle)
995 return mono_gchandle_is_in_domain (gchandle, mono_domain_get ());
1000 #ifdef MONO_HAS_SEMAPHORES
1001 static MonoSemType finalizer_sem;
1003 static HANDLE finalizer_event;
1004 static volatile gboolean finished=FALSE;
1007 mono_gc_finalize_notify (void)
1010 g_message ( "%s: prodding finalizer", __func__);
1013 #ifdef MONO_HAS_SEMAPHORES
1014 MONO_SEM_POST (&finalizer_sem);
1016 SetEvent (finalizer_event);
1020 #ifdef HAVE_BOEHM_GC
1023 collect_objects (gpointer key, gpointer value, gpointer user_data)
1025 GPtrArray *arr = (GPtrArray*)user_data;
1026 g_ptr_array_add (arr, key);
1032 * finalize_domain_objects:
1034 * Run the finalizers of all finalizable objects in req->domain.
1037 finalize_domain_objects (DomainFinalizationReq *req)
1039 MonoDomain *domain = req->domain;
1042 #define NUM_FOBJECTS 64
1043 MonoObject *to_finalize [NUM_FOBJECTS];
1047 /* Process finalizers which are already in the queue */
1048 mono_gc_invoke_finalizers ();
1050 #ifdef HAVE_BOEHM_GC
1051 while (g_hash_table_size (domain->finalizable_objects_hash) > 0) {
1055 * Since the domain is unloading, nobody is allowed to put
1056 * new entries into the hash table. But finalize_object might
1057 * remove entries from the hash table, so we make a copy.
1059 objs = g_ptr_array_new ();
1060 g_hash_table_foreach (domain->finalizable_objects_hash, collect_objects, objs);
1061 /* printf ("FINALIZING %d OBJECTS.\n", objs->len); */
1063 for (i = 0; i < objs->len; ++i) {
1064 MonoObject *o = (MonoObject*)g_ptr_array_index (objs, i);
1065 /* FIXME: Avoid finalizing threads, etc */
1066 mono_gc_run_finalize (o, 0);
1069 g_ptr_array_free (objs, TRUE);
1071 #elif defined(HAVE_SGEN_GC)
1072 while ((count = mono_gc_finalizers_for_domain (domain, to_finalize, NUM_FOBJECTS))) {
1074 for (i = 0; i < count; ++i) {
1075 mono_gc_run_finalize (to_finalize [i], 0);
1080 /* cleanup the reference queue */
1081 reference_queue_clear_for_domain (domain);
1083 /* printf ("DONE.\n"); */
1084 SetEvent (req->done_event);
1086 /* The event is closed in mono_domain_finalize if we get here */
1091 finalizer_thread (gpointer unused)
1093 gboolean wait = TRUE;
1096 /* Wait to be notified that there's at least one
1100 g_assert (mono_domain_get () == mono_get_root_domain ());
1101 mono_gc_set_skip_thread (TRUE);
1102 MONO_PREPARE_BLOCKING
1105 /* An alertable wait is required so this thread can be suspended on windows */
1106 #ifdef MONO_HAS_SEMAPHORES
1107 MONO_SEM_WAIT_ALERTABLE (&finalizer_sem, TRUE);
1109 WaitForSingleObjectEx (finalizer_event, INFINITE, TRUE);
1113 MONO_FINISH_BLOCKING
1114 mono_gc_set_skip_thread (FALSE);
1116 mono_threads_perform_thread_dump ();
1118 mono_console_handle_async_ops ();
1120 #ifndef DISABLE_ATTACH
1121 mono_attach_maybe_start ();
1124 if (domains_to_finalize) {
1125 mono_finalizer_lock ();
1126 if (domains_to_finalize) {
1127 DomainFinalizationReq *req = domains_to_finalize->data;
1128 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
1129 mono_finalizer_unlock ();
1131 finalize_domain_objects (req);
1133 mono_finalizer_unlock ();
1137 /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup),
1138 * before the domain is unloaded.
1140 mono_gc_invoke_finalizers ();
1142 mono_threads_join_threads ();
1144 reference_queue_proccess_all ();
1146 #ifdef MONO_HAS_SEMAPHORES
1147 /* Avoid posting the pending done event until there are pending finalizers */
1148 if (MONO_SEM_TIMEDWAIT (&finalizer_sem, 0) == 0)
1149 /* Don't wait again at the start of the loop */
1152 SetEvent (pending_done_event);
1154 SetEvent (pending_done_event);
1158 mono_finalizer_lock ();
1159 finalizer_thread_exited = TRUE;
1160 mono_cond_signal (&exited_cond);
1161 mono_finalizer_unlock ();
1166 #ifndef LAZY_GC_THREAD_CREATION
1170 mono_gc_init_finalizer_thread (void)
1172 gc_thread = mono_thread_create_internal (mono_domain_get (), finalizer_thread, NULL, FALSE, 0);
1173 ves_icall_System_Threading_Thread_SetName_internal (gc_thread, mono_string_new (mono_domain_get (), "Finalizer"));
1179 mono_mutex_init_recursive (&handle_section);
1180 mono_mutex_init_recursive (&allocator_section);
1182 mono_mutex_init_recursive (&finalizer_mutex);
1183 mono_mutex_init_recursive (&reference_queue_mutex);
1185 MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_NORMAL].entries);
1186 MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_PINNED].entries);
1188 mono_counters_register ("Minor GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.minor_gc_count);
1189 mono_counters_register ("Major GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.major_gc_count);
1190 mono_counters_register ("Minor GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.minor_gc_time);
1191 mono_counters_register ("Major GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time);
1192 mono_counters_register ("Major GC time concurrent", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time_concurrent);
1194 mono_gc_base_init ();
1196 if (mono_gc_is_disabled ()) {
1201 finalizer_event = CreateEvent (NULL, FALSE, FALSE, NULL);
1202 g_assert (finalizer_event);
1203 pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
1204 g_assert (pending_done_event);
1205 mono_cond_init (&exited_cond, 0);
1206 #ifdef MONO_HAS_SEMAPHORES
1207 MONO_SEM_INIT (&finalizer_sem, 0);
1210 #ifndef LAZY_GC_THREAD_CREATION
1211 mono_gc_init_finalizer_thread ();
1216 mono_gc_cleanup (void)
1219 g_message ("%s: cleaning up finalizer", __func__);
1224 if (mono_thread_internal_current () != gc_thread) {
1225 gboolean timed_out = FALSE;
1226 guint32 start_ticks = mono_msec_ticks ();
1227 guint32 end_ticks = start_ticks + 2000;
1229 mono_gc_finalize_notify ();
1230 /* Finishing the finalizer thread, so wait a little bit... */
1231 /* MS seems to wait for about 2 seconds */
1232 while (!finalizer_thread_exited) {
1233 guint32 current_ticks = mono_msec_ticks ();
1236 if (current_ticks >= end_ticks)
1239 timeout = end_ticks - current_ticks;
1240 MONO_PREPARE_BLOCKING;
1241 mono_finalizer_lock ();
1242 if (!finalizer_thread_exited)
1243 mono_cond_timedwait_ms (&exited_cond, &finalizer_mutex, timeout);
1244 mono_finalizer_unlock ();
1245 MONO_FINISH_BLOCKING;
1248 if (!finalizer_thread_exited) {
1251 /* Set a flag which the finalizer thread can check */
1252 suspend_finalizers = TRUE;
1254 /* Try to abort the thread, in the hope that it is running managed code */
1255 mono_thread_internal_stop (gc_thread);
1257 /* Wait for it to stop */
1258 ret = guarded_wait (gc_thread->handle, 100, TRUE);
1260 if (ret == WAIT_TIMEOUT) {
1262 * The finalizer thread refused to die. There is not much we
1263 * can do here, since the runtime is shutting down so the
1264 * state the finalizer thread depends on will vanish.
1266 g_warning ("Shutting down finalizer thread timed out.");
1274 /* Wait for the thread to actually exit */
1275 ret = guarded_wait (gc_thread->handle, INFINITE, TRUE);
1276 g_assert (ret == WAIT_OBJECT_0);
1278 mono_thread_join (GUINT_TO_POINTER (gc_thread->tid));
1282 #ifdef HAVE_BOEHM_GC
1283 GC_finalizer_notifier = NULL;
1287 mono_reference_queue_cleanup ();
1289 mono_mutex_destroy (&handle_section);
1290 mono_mutex_destroy (&allocator_section);
1291 mono_mutex_destroy (&finalizer_mutex);
1292 mono_mutex_destroy (&reference_queue_mutex);
1297 /* Null GC dummy functions */
1299 mono_gc_finalize_notify (void)
1303 void mono_gc_init (void)
1305 mono_mutex_init_recursive (&handle_section);
1308 void mono_gc_cleanup (void)
1315 mono_gc_is_finalizer_internal_thread (MonoInternalThread *thread)
1317 return thread == gc_thread;
1321 * mono_gc_is_finalizer_thread:
1322 * @thread: the thread to test.
1324 * In Mono objects are finalized asynchronously on a separate thread.
1325 * This routine tests whether the @thread argument represents the
1326 * finalization thread.
1328 * Returns true if @thread is the finalization thread.
1331 mono_gc_is_finalizer_thread (MonoThread *thread)
1333 return mono_gc_is_finalizer_internal_thread (thread->internal_thread);
1336 #if defined(__MACH__)
1337 static pthread_t mach_exception_thread;
1340 mono_gc_register_mach_exception_thread (pthread_t thread)
1342 mach_exception_thread = thread;
1346 mono_gc_get_mach_exception_thread (void)
1348 return mach_exception_thread;
1352 #ifndef HAVE_SGEN_GC
1354 mono_gc_alloc_mature (MonoVTable *vtable)
1356 return mono_object_new_specific (vtable);
1361 static MonoReferenceQueue *ref_queues;
1364 ref_list_remove_element (RefQueueEntry **prev, RefQueueEntry *element)
1367 /* Guard if head is changed concurrently. */
1368 while (*prev != element)
1369 prev = &(*prev)->next;
1370 } while (prev && InterlockedCompareExchangePointer ((void*)prev, element->next, element) != element);
1374 ref_list_push (RefQueueEntry **head, RefQueueEntry *value)
1376 RefQueueEntry *current;
1379 value->next = current;
1380 STORE_STORE_FENCE; /*Must make sure the previous store is visible before the CAS. */
1381 } while (InterlockedCompareExchangePointer ((void*)head, value, current) != current);
1385 reference_queue_proccess (MonoReferenceQueue *queue)
1387 RefQueueEntry **iter = &queue->queue;
1388 RefQueueEntry *entry;
1389 while ((entry = *iter)) {
1391 if (queue->should_be_deleted || !mono_gc_weak_link_get (&entry->dis_link)) {
1392 mono_gc_weak_link_remove (&entry->dis_link, TRUE);
1394 if (queue->should_be_deleted || !mono_gchandle_get_target (entry->gchandle)) {
1395 mono_gchandle_free ((guint32)entry->gchandle);
1397 ref_list_remove_element (iter, entry);
1398 queue->callback (entry->user_data);
1401 iter = &entry->next;
1407 reference_queue_proccess_all (void)
1409 MonoReferenceQueue **iter;
1410 MonoReferenceQueue *queue = ref_queues;
1411 for (; queue; queue = queue->next)
1412 reference_queue_proccess (queue);
1415 mono_mutex_lock (&reference_queue_mutex);
1416 for (iter = &ref_queues; *iter;) {
1418 if (!queue->should_be_deleted) {
1419 iter = &queue->next;
1423 mono_mutex_unlock (&reference_queue_mutex);
1424 reference_queue_proccess (queue);
1427 *iter = queue->next;
1430 mono_mutex_unlock (&reference_queue_mutex);
1434 mono_reference_queue_cleanup (void)
1436 MonoReferenceQueue *queue = ref_queues;
1437 for (; queue; queue = queue->next)
1438 queue->should_be_deleted = TRUE;
1439 reference_queue_proccess_all ();
1443 reference_queue_clear_for_domain (MonoDomain *domain)
1445 MonoReferenceQueue *queue = ref_queues;
1446 for (; queue; queue = queue->next) {
1447 RefQueueEntry **iter = &queue->queue;
1448 RefQueueEntry *entry;
1449 while ((entry = *iter)) {
1450 if (entry->domain == domain) {
1452 mono_gc_weak_link_remove (&entry->dis_link, TRUE);
1454 mono_gchandle_free ((guint32)entry->gchandle);
1456 ref_list_remove_element (iter, entry);
1457 queue->callback (entry->user_data);
1460 iter = &entry->next;
1466 * mono_gc_reference_queue_new:
1467 * @callback callback used when processing collected entries.
1469 * Create a new reference queue used to process collected objects.
1470 * A reference queue let you add a pair of (managed object, user data)
1471 * using the mono_gc_reference_queue_add method.
1473 * Once the managed object is collected @callback will be called
1474 * in the finalizer thread with 'user data' as argument.
1476 * The callback is called from the finalizer thread without any locks held.
1477 * When a AppDomain is unloaded, all callbacks for objects belonging to it
1480 * @returns the new queue.
1483 mono_gc_reference_queue_new (mono_reference_queue_callback callback)
1485 MonoReferenceQueue *res = g_new0 (MonoReferenceQueue, 1);
1486 res->callback = callback;
1488 mono_mutex_lock (&reference_queue_mutex);
1489 res->next = ref_queues;
1491 mono_mutex_unlock (&reference_queue_mutex);
1497 * mono_gc_reference_queue_add:
1498 * @queue the queue to add the reference to.
1499 * @obj the object to be watched for collection
1500 * @user_data parameter to be passed to the queue callback
1502 * Queue an object to be watched for collection, when the @obj is
1503 * collected, the callback that was registered for the @queue will
1504 * be invoked with @user_data as argument.
1506 * @returns false if the queue is scheduled to be freed.
1509 mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *user_data)
1511 RefQueueEntry *entry;
1512 if (queue->should_be_deleted)
1515 entry = g_new0 (RefQueueEntry, 1);
1516 entry->user_data = user_data;
1517 entry->domain = mono_object_domain (obj);
1520 mono_gc_weak_link_add (&entry->dis_link, obj, TRUE);
1522 entry->gchandle = mono_gchandle_new_weakref (obj, TRUE);
1523 mono_object_register_finalizer (obj);
1526 ref_list_push (&queue->queue, entry);
1531 * mono_gc_reference_queue_free:
1532 * @queue the queue that should be freed.
1534 * This operation signals that @queue should be freed. This operation is deferred
1535 * as it happens on the finalizer thread.
1537 * After this call, no further objects can be queued. It's the responsibility of the
1538 * caller to make sure that no further attempt to access queue will be made.
1541 mono_gc_reference_queue_free (MonoReferenceQueue *queue)
1543 queue->should_be_deleted = TRUE;