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 add_thread_to_finalize (MonoInternalThread *thread)
92 mono_finalizer_lock ();
93 if (!threads_to_finalize)
94 MONO_GC_REGISTER_ROOT_SINGLE (threads_to_finalize);
95 threads_to_finalize = mono_mlist_append (threads_to_finalize, (MonoObject*)thread);
96 mono_finalizer_unlock ();
99 static gboolean suspend_finalizers = FALSE;
101 * actually, we might want to queue the finalize requests in a separate thread,
102 * but we need to be careful about the execution domain of the thread...
105 mono_gc_run_finalize (void *obj, void *data)
110 MonoObject *exc = NULL;
115 MonoMethod* finalizer = NULL;
116 MonoDomain *caller_domain = mono_domain_get ();
118 RuntimeInvokeFunction runtime_invoke;
120 o = (MonoObject*)((char*)obj + GPOINTER_TO_UINT (data));
123 g_log ("mono-gc-finalizers", G_LOG_LEVEL_DEBUG, "<%s at %p> Starting finalizer checks.", o->vtable->klass->name, o);
125 if (suspend_finalizers)
128 domain = o->vtable->domain;
131 mono_domain_finalizers_lock (domain);
133 o2 = g_hash_table_lookup (domain->finalizable_objects_hash, o);
135 mono_domain_finalizers_unlock (domain);
138 /* Already finalized somehow */
142 /* make sure the finalizer is not called again if the object is resurrected */
143 object_register_finalizer (obj, NULL);
146 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Registered finalizer as processed.", o->vtable->klass->name, o);
148 if (o->vtable->klass == mono_defaults.internal_thread_class) {
149 MonoInternalThread *t = (MonoInternalThread*)o;
151 if (mono_gc_is_finalizer_internal_thread (t))
152 /* Avoid finalizing ourselves */
155 if (t->threadpool_thread && finalizing_root_domain) {
156 /* Don't finalize threadpool threads when
157 shutting down - they're finalized when the
158 threadpool shuts down. */
159 add_thread_to_finalize (t);
164 if (o->vtable->klass->image == mono_defaults.corlib && !strcmp (o->vtable->klass->name, "DynamicMethod") && finalizing_root_domain) {
166 * These can't be finalized during unloading/shutdown, since that would
167 * free the native code which can still be referenced by other
169 * FIXME: This is not perfect, objects dying at the same time as
170 * dynamic methods can still reference them even when !shutdown.
175 if (mono_runtime_get_no_exec ())
178 /* speedup later... and use a timeout */
179 /* g_print ("Finalize run on %p %s.%s\n", o, mono_object_class (o)->name_space, mono_object_class (o)->name); */
181 /* Use _internal here, since this thread can enter a doomed appdomain */
182 mono_domain_set_internal (mono_object_domain (o));
184 /* delegates that have a native function pointer allocated are
185 * registered for finalization, but they don't have a Finalize
186 * method, because in most cases it's not needed and it's just a waste.
188 if (o->vtable->klass->delegate) {
189 MonoDelegate* del = (MonoDelegate*)o;
190 if (del->delegate_trampoline)
191 mono_delegate_free_ftnptr ((MonoDelegate*)o);
192 mono_domain_set_internal (caller_domain);
196 finalizer = mono_class_get_finalizer (o->vtable->klass);
199 /* If object has a CCW but has no finalizer, it was only
200 * registered for finalization in order to free the CCW.
201 * Else it needs the regular finalizer run.
202 * FIXME: what to do about ressurection and suppression
203 * of finalizer on object with CCW.
205 if (mono_marshal_free_ccw (o) && !finalizer) {
206 mono_domain_set_internal (caller_domain);
212 * To avoid the locking plus the other overhead of mono_runtime_invoke (),
213 * create and precompile a wrapper which calls the finalize method using
217 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Compiling finalizer.", o->vtable->klass->name, o);
219 if (!domain->finalize_runtime_invoke) {
220 MonoMethod *invoke = mono_marshal_get_runtime_invoke (mono_class_get_method_from_name_flags (mono_defaults.object_class, "Finalize", 0, 0), TRUE);
222 domain->finalize_runtime_invoke = mono_compile_method (invoke);
225 runtime_invoke = domain->finalize_runtime_invoke;
227 mono_runtime_class_init (o->vtable);
229 if (G_UNLIKELY (MONO_GC_FINALIZE_INVOKE_ENABLED ())) {
230 MONO_GC_FINALIZE_INVOKE ((unsigned long)o, mono_object_get_size (o),
231 o->vtable->klass->name_space, o->vtable->klass->name);
235 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Calling finalizer.", o->vtable->klass->name, o);
237 runtime_invoke (o, NULL, &exc, NULL);
240 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Returned from finalizer.", o->vtable->klass->name, o);
243 mono_internal_thread_unhandled_exception (exc);
245 mono_domain_set_internal (caller_domain);
249 mono_gc_finalize_threadpool_threads (void)
251 while (threads_to_finalize) {
252 MonoInternalThread *thread = (MonoInternalThread*) mono_mlist_get_data (threads_to_finalize);
254 /* Force finalization of the thread. */
255 thread->threadpool_thread = FALSE;
256 mono_object_register_finalizer ((MonoObject*)thread);
258 mono_gc_run_finalize (thread, NULL);
260 threads_to_finalize = mono_mlist_next (threads_to_finalize);
265 mono_gc_out_of_memory (size_t size)
268 * we could allocate at program startup some memory that we could release
269 * back to the system at this point if we're really low on memory (ie, size is
270 * lower than the memory we set apart)
272 mono_raise_exception (mono_domain_get ()->out_of_memory_ex);
278 * Some of our objects may point to a different address than the address returned by GC_malloc()
279 * (because of the GetHashCode hack), but we need to pass the real address to register_finalizer.
280 * This also means that in the callback we need to adjust the pointer to get back the real
282 * We also need to be consistent in the use of the GC_debug* variants of malloc and register_finalizer,
283 * since that, too, can cause the underlying pointer to be offset.
286 object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*))
291 mono_raise_exception (mono_get_exception_argument_null ("obj"));
293 domain = obj->vtable->domain;
296 if (mono_domain_is_unloading (domain) && (callback != NULL))
298 * Can't register finalizers in a dying appdomain, since they
299 * could be invoked after the appdomain has been unloaded.
303 mono_domain_finalizers_lock (domain);
306 g_hash_table_insert (domain->finalizable_objects_hash, obj, obj);
308 g_hash_table_remove (domain->finalizable_objects_hash, obj);
310 mono_domain_finalizers_unlock (domain);
312 mono_gc_register_for_finalization (obj, callback);
313 #elif defined(HAVE_SGEN_GC)
315 * If we register finalizers for domains that are unloading we might
316 * end up running them while or after the domain is being cleared, so
317 * the objects will not be valid anymore.
319 if (!mono_domain_is_unloading (domain))
320 mono_gc_register_for_finalization (obj, callback);
325 * mono_object_register_finalizer:
326 * @obj: object to register
328 * Records that object @obj has a finalizer, this will call the
329 * Finalize method when the garbage collector disposes the object.
333 mono_object_register_finalizer (MonoObject *obj)
335 /* g_print ("Registered finalizer on %p %s.%s\n", obj, mono_object_class (obj)->name_space, mono_object_class (obj)->name); */
336 object_register_finalizer (obj, mono_gc_run_finalize);
340 * mono_domain_finalize:
341 * @domain: the domain to finalize
342 * @timeout: msects to wait for the finalization to complete, -1 to wait indefinitely
344 * Request finalization of all finalizable objects inside @domain. Wait
345 * @timeout msecs for the finalization to complete.
347 * Returns: TRUE if succeeded, FALSE if there was a timeout
351 mono_domain_finalize (MonoDomain *domain, guint32 timeout)
353 DomainFinalizationReq *req;
356 MonoInternalThread *thread = mono_thread_internal_current ();
358 #if defined(__native_client__)
362 if (mono_thread_internal_current () == gc_thread)
363 /* We are called from inside a finalizer, not much we can do here */
367 * No need to create another thread 'cause the finalizer thread
368 * is still working and will take care of running the finalizers
375 mono_gc_collect (mono_gc_max_generation ());
377 done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
378 if (done_event == NULL) {
382 req = g_new0 (DomainFinalizationReq, 1);
383 req->domain = domain;
384 req->done_event = done_event;
386 if (domain == mono_get_root_domain ())
387 finalizing_root_domain = TRUE;
389 mono_finalizer_lock ();
391 domains_to_finalize = g_slist_append (domains_to_finalize, req);
393 mono_finalizer_unlock ();
395 /* Tell the finalizer thread to finalize this appdomain */
396 mono_gc_finalize_notify ();
402 res = WaitForSingleObjectEx (done_event, timeout, TRUE);
403 /* printf ("WAIT RES: %d.\n", res); */
405 if (res == WAIT_IO_COMPLETION) {
406 if ((thread->state & (ThreadState_StopRequested | ThreadState_SuspendRequested)) != 0)
408 } else if (res == WAIT_TIMEOUT) {
409 /* We leak the handle here */
416 CloseHandle (done_event);
418 if (domain == mono_get_root_domain ()) {
419 mono_thread_pool_cleanup ();
420 mono_gc_finalize_threadpool_threads ();
425 /* We don't support domain finalization without a GC */
431 ves_icall_System_GC_InternalCollect (int generation)
433 mono_gc_collect (generation);
437 ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection)
440 mono_gc_collect (mono_gc_max_generation ());
441 return mono_gc_get_used_size ();
445 ves_icall_System_GC_KeepAlive (MonoObject *obj)
453 ves_icall_System_GC_ReRegisterForFinalize (MonoObject *obj)
455 MONO_CHECK_ARG_NULL (obj,);
457 object_register_finalizer (obj, mono_gc_run_finalize);
461 ves_icall_System_GC_SuppressFinalize (MonoObject *obj)
463 MONO_CHECK_ARG_NULL (obj,);
465 /* delegates have no finalizers, but we register them to deal with the
466 * unmanaged->managed trampoline. We don't let the user suppress it
467 * otherwise we'd leak it.
469 if (obj->vtable->klass->delegate)
472 /* FIXME: Need to handle case where obj has COM Callable Wrapper
473 * generated for it that needs cleaned up, but user wants to suppress
474 * their derived object finalizer. */
476 object_register_finalizer (obj, NULL);
480 ves_icall_System_GC_WaitForPendingFinalizers (void)
483 if (!mono_gc_pending_finalizers ())
486 if (mono_thread_internal_current () == gc_thread)
487 /* Avoid deadlocks */
491 If the finalizer thread is not live, lets pretend no finalizers are pending since the current thread might
492 be the one responsible for starting it up.
494 if (gc_thread == NULL)
497 ResetEvent (pending_done_event);
498 mono_gc_finalize_notify ();
499 /* g_print ("Waiting for pending finalizers....\n"); */
500 WaitForSingleObjectEx (pending_done_event, INFINITE, TRUE);
501 /* g_print ("Done pending....\n"); */
506 ves_icall_System_GC_register_ephemeron_array (MonoObject *array)
509 if (!mono_gc_ephemeron_array_add (array)) {
510 mono_set_pending_exception (mono_object_domain (array)->out_of_memory_ex);
517 ves_icall_System_GC_get_ephemeron_tombstone (void)
519 return mono_domain_get ()->ephemeron_tombstone;
522 #define mono_allocator_lock() mono_mutex_lock (&allocator_section)
523 #define mono_allocator_unlock() mono_mutex_unlock (&allocator_section)
524 static mono_mutex_t allocator_section;
525 static mono_mutex_t handle_section;
534 static HandleType mono_gchandle_get_type (guint32 gchandle);
537 ves_icall_System_GCHandle_GetTarget (guint32 handle)
539 return mono_gchandle_get_target (handle);
543 * if type == -1, change the target of the handle, otherwise allocate a new handle.
546 ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type)
549 mono_gchandle_set_target (handle, obj);
550 /* the handle doesn't change */
555 return mono_gchandle_new_weakref (obj, FALSE);
556 case HANDLE_WEAK_TRACK:
557 return mono_gchandle_new_weakref (obj, TRUE);
559 return mono_gchandle_new (obj, FALSE);
561 return mono_gchandle_new (obj, TRUE);
563 g_assert_not_reached ();
569 ves_icall_System_GCHandle_FreeHandle (guint32 handle)
571 mono_gchandle_free (handle);
575 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
579 if (mono_gchandle_get_type (handle) != HANDLE_PINNED)
581 obj = mono_gchandle_get_target (handle);
583 MonoClass *klass = mono_object_class (obj);
584 if (klass == mono_defaults.string_class) {
585 return mono_string_chars ((MonoString*)obj);
586 } else if (klass->rank) {
587 return mono_array_addr ((MonoArray*)obj, char, 0);
589 /* the C# code will check and throw the exception */
590 /* FIXME: missing !klass->blittable test, see bug #61134 */
591 if ((klass->flags & TYPE_ATTRIBUTE_LAYOUT_MASK) == TYPE_ATTRIBUTE_AUTO_LAYOUT)
593 return (char*)obj + sizeof (MonoObject);
600 ves_icall_Mono_Runtime_SetGCAllowSynchronousMajor (MonoBoolean flag)
602 return mono_gc_set_allow_synchronous_major (flag);
610 guint slot_hint : 24; /* starting slot for search */
611 /* 2^16 appdomains should be enough for everyone (though I know I'll regret this in 20 years) */
612 /* we alloc this only for weak refs, since we can get the domain directly in the other cases */
616 /* weak and weak-track arrays will be allocated in malloc memory
618 static HandleData gc_handles [] = {
619 {NULL, NULL, 0, HANDLE_WEAK, 0},
620 {NULL, NULL, 0, HANDLE_WEAK_TRACK, 0},
621 {NULL, NULL, 0, HANDLE_NORMAL, 0},
622 {NULL, NULL, 0, HANDLE_PINNED, 0}
625 #define lock_handles(handles) mono_mutex_lock (&handle_section)
626 #define unlock_handles(handles) mono_mutex_unlock (&handle_section)
629 find_first_unset (guint32 bitmap)
632 for (i = 0; i < 32; ++i) {
633 if (!(bitmap & (1 << i)))
640 make_root_descr_all_refs (int numbits, gboolean pinned)
646 return mono_gc_make_root_descr_all_refs (numbits);
650 alloc_handle (HandleData *handles, MonoObject *obj, gboolean track)
654 lock_handles (handles);
655 if (!handles->size) {
657 if (handles->type > HANDLE_WEAK_TRACK) {
658 handles->entries = mono_gc_alloc_fixed (sizeof (gpointer) * handles->size, make_root_descr_all_refs (handles->size, handles->type == HANDLE_PINNED));
660 handles->entries = g_malloc0 (sizeof (gpointer) * handles->size);
661 handles->domain_ids = g_malloc0 (sizeof (guint16) * handles->size);
663 handles->bitmap = g_malloc0 (handles->size / 8);
666 for (slot = handles->slot_hint; slot < handles->size / 32; ++slot) {
667 if (handles->bitmap [slot] != 0xffffffff) {
668 i = find_first_unset (handles->bitmap [slot]);
669 handles->slot_hint = slot;
673 if (i == -1 && handles->slot_hint != 0) {
674 for (slot = 0; slot < handles->slot_hint; ++slot) {
675 if (handles->bitmap [slot] != 0xffffffff) {
676 i = find_first_unset (handles->bitmap [slot]);
677 handles->slot_hint = slot;
684 guint32 new_size = handles->size * 2; /* always double: we memset to 0 based on this below */
686 /* resize and copy the bitmap */
687 new_bitmap = g_malloc0 (new_size / 8);
688 memcpy (new_bitmap, handles->bitmap, handles->size / 8);
689 g_free (handles->bitmap);
690 handles->bitmap = new_bitmap;
692 /* resize and copy the entries */
693 if (handles->type > HANDLE_WEAK_TRACK) {
696 entries = mono_gc_alloc_fixed (sizeof (gpointer) * new_size, make_root_descr_all_refs (new_size, handles->type == HANDLE_PINNED));
697 mono_gc_memmove_aligned (entries, handles->entries, sizeof (gpointer) * handles->size);
699 mono_gc_free_fixed (handles->entries);
700 handles->entries = entries;
704 domain_ids = g_malloc0 (sizeof (guint16) * new_size);
705 entries = g_malloc0 (sizeof (gpointer) * new_size);
706 memcpy (domain_ids, handles->domain_ids, sizeof (guint16) * handles->size);
707 for (i = 0; i < handles->size; ++i) {
708 MonoObject *obj = mono_gc_weak_link_get (&(handles->entries [i]));
710 mono_gc_weak_link_add (&(entries [i]), obj, track);
711 mono_gc_weak_link_remove (&(handles->entries [i]), track);
713 g_assert (!handles->entries [i]);
716 g_free (handles->entries);
717 g_free (handles->domain_ids);
718 handles->entries = entries;
719 handles->domain_ids = domain_ids;
722 /* set i and slot to the next free position */
724 slot = (handles->size + 1) / 32;
725 handles->slot_hint = handles->size + 1;
726 handles->size = new_size;
728 handles->bitmap [slot] |= 1 << i;
729 slot = slot * 32 + i;
730 handles->entries [slot] = NULL;
731 if (handles->type <= HANDLE_WEAK_TRACK) {
732 /*FIXME, what to use when obj == null?*/
733 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
735 mono_gc_weak_link_add (&(handles->entries [slot]), obj, track);
737 handles->entries [slot] = obj;
740 #ifndef DISABLE_PERFCOUNTERS
741 mono_perfcounters->gc_num_handles++;
743 unlock_handles (handles);
744 /*g_print ("allocated entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
745 res = (slot << 3) | (handles->type + 1);
746 mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_CREATED, handles->type, res, obj);
752 * @obj: managed object to get a handle for
753 * @pinned: whether the object should be pinned
755 * This returns a handle that wraps the object, this is used to keep a
756 * reference to a managed object from the unmanaged world and preventing the
757 * object from being disposed.
759 * If @pinned is false the address of the object can not be obtained, if it is
760 * true the address of the object can be obtained. This will also pin the
761 * object so it will not be possible by a moving garbage collector to move the
764 * Returns: a handle that can be used to access the object from
768 mono_gchandle_new (MonoObject *obj, gboolean pinned)
770 return alloc_handle (&gc_handles [pinned? HANDLE_PINNED: HANDLE_NORMAL], obj, FALSE);
774 * mono_gchandle_new_weakref:
775 * @obj: managed object to get a handle for
776 * @pinned: whether the object should be pinned
778 * This returns a weak handle that wraps the object, this is used to
779 * keep a reference to a managed object from the unmanaged world.
780 * Unlike the mono_gchandle_new the object can be reclaimed by the
781 * garbage collector. In this case the value of the GCHandle will be
784 * If @pinned is false the address of the object can not be obtained, if it is
785 * true the address of the object can be obtained. This will also pin the
786 * object so it will not be possible by a moving garbage collector to move the
789 * Returns: a handle that can be used to access the object from
793 mono_gchandle_new_weakref (MonoObject *obj, gboolean track_resurrection)
795 guint32 handle = alloc_handle (&gc_handles [track_resurrection? HANDLE_WEAK_TRACK: HANDLE_WEAK], obj, track_resurrection);
801 mono_gchandle_get_type (guint32 gchandle)
803 guint type = (gchandle & 7) - 1;
809 * mono_gchandle_get_target:
810 * @gchandle: a GCHandle's handle.
812 * The handle was previously created by calling mono_gchandle_new or
813 * mono_gchandle_new_weakref.
815 * Returns a pointer to the MonoObject represented by the handle or
816 * NULL for a collected object if using a weakref handle.
819 mono_gchandle_get_target (guint32 gchandle)
821 guint slot = gchandle >> 3;
822 guint type = (gchandle & 7) - 1;
823 HandleData *handles = &gc_handles [type];
824 MonoObject *obj = NULL;
827 lock_handles (handles);
828 if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
829 if (handles->type <= HANDLE_WEAK_TRACK) {
830 obj = mono_gc_weak_link_get (&handles->entries [slot]);
832 obj = handles->entries [slot];
835 /* print a warning? */
837 unlock_handles (handles);
838 /*g_print ("get target of entry %d of type %d: %p\n", slot, handles->type, obj);*/
843 mono_gchandle_set_target (guint32 gchandle, MonoObject *obj)
845 guint slot = gchandle >> 3;
846 guint type = (gchandle & 7) - 1;
847 HandleData *handles = &gc_handles [type];
851 lock_handles (handles);
852 if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
853 if (handles->type <= HANDLE_WEAK_TRACK) {
854 if (handles->entries [slot])
855 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
857 mono_gc_weak_link_add (&handles->entries [slot], obj, handles->type == HANDLE_WEAK_TRACK);
858 /*FIXME, what to use when obj == null?*/
859 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
861 handles->entries [slot] = obj;
864 /* print a warning? */
866 /*g_print ("changed entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
867 unlock_handles (handles);
871 * mono_gchandle_is_in_domain:
872 * @gchandle: a GCHandle's handle.
873 * @domain: An application domain.
875 * Returns: true if the object wrapped by the @gchandle belongs to the specific @domain.
878 mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain)
880 guint slot = gchandle >> 3;
881 guint type = (gchandle & 7) - 1;
882 HandleData *handles = &gc_handles [type];
883 gboolean result = FALSE;
886 lock_handles (handles);
887 if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
888 if (handles->type <= HANDLE_WEAK_TRACK) {
889 result = domain->domain_id == handles->domain_ids [slot];
892 obj = handles->entries [slot];
896 result = domain == mono_object_domain (obj);
899 /* print a warning? */
901 unlock_handles (handles);
906 * mono_gchandle_free:
907 * @gchandle: a GCHandle's handle.
909 * Frees the @gchandle handle. If there are no outstanding
910 * references, the garbage collector can reclaim the memory of the
914 mono_gchandle_free (guint32 gchandle)
916 guint slot = gchandle >> 3;
917 guint type = (gchandle & 7) - 1;
918 HandleData *handles = &gc_handles [type];
922 lock_handles (handles);
923 if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
924 if (handles->type <= HANDLE_WEAK_TRACK) {
925 if (handles->entries [slot])
926 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
928 handles->entries [slot] = NULL;
930 handles->bitmap [slot / 32] &= ~(1 << (slot % 32));
932 /* print a warning? */
934 #ifndef DISABLE_PERFCOUNTERS
935 mono_perfcounters->gc_num_handles--;
937 /*g_print ("freed entry %d of type %d\n", slot, handles->type);*/
938 unlock_handles (handles);
939 mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_DESTROYED, handles->type, gchandle, NULL);
943 * mono_gchandle_free_domain:
944 * @domain: domain that is unloading
946 * Function used internally to cleanup any GC handle for objects belonging
947 * to the specified domain during appdomain unload.
950 mono_gchandle_free_domain (MonoDomain *domain)
954 for (type = 0; type < 3; ++type) {
956 HandleData *handles = &gc_handles [type];
957 lock_handles (handles);
958 for (slot = 0; slot < handles->size; ++slot) {
959 if (!(handles->bitmap [slot / 32] & (1 << (slot % 32))))
961 if (type <= HANDLE_WEAK_TRACK) {
962 if (domain->domain_id == handles->domain_ids [slot]) {
963 handles->bitmap [slot / 32] &= ~(1 << (slot % 32));
964 if (handles->entries [slot])
965 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
968 if (handles->entries [slot] && mono_object_domain (handles->entries [slot]) == domain) {
969 handles->bitmap [slot / 32] &= ~(1 << (slot % 32));
970 handles->entries [slot] = NULL;
974 unlock_handles (handles);
980 GCHandle_CheckCurrentDomain (guint32 gchandle)
982 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 #ifdef MONO_HAS_SEMAPHORES
1001 MONO_SEM_POST (&finalizer_sem);
1003 SetEvent (finalizer_event);
1007 #ifdef HAVE_BOEHM_GC
1010 collect_objects (gpointer key, gpointer value, gpointer user_data)
1012 GPtrArray *arr = (GPtrArray*)user_data;
1013 g_ptr_array_add (arr, key);
1019 * finalize_domain_objects:
1021 * Run the finalizers of all finalizable objects in req->domain.
1024 finalize_domain_objects (DomainFinalizationReq *req)
1026 MonoDomain *domain = req->domain;
1029 #define NUM_FOBJECTS 64
1030 MonoObject *to_finalize [NUM_FOBJECTS];
1034 /* Process finalizers which are already in the queue */
1035 mono_gc_invoke_finalizers ();
1037 #ifdef HAVE_BOEHM_GC
1038 while (g_hash_table_size (domain->finalizable_objects_hash) > 0) {
1042 * Since the domain is unloading, nobody is allowed to put
1043 * new entries into the hash table. But finalize_object might
1044 * remove entries from the hash table, so we make a copy.
1046 objs = g_ptr_array_new ();
1047 g_hash_table_foreach (domain->finalizable_objects_hash, collect_objects, objs);
1048 /* printf ("FINALIZING %d OBJECTS.\n", objs->len); */
1050 for (i = 0; i < objs->len; ++i) {
1051 MonoObject *o = (MonoObject*)g_ptr_array_index (objs, i);
1052 /* FIXME: Avoid finalizing threads, etc */
1053 mono_gc_run_finalize (o, 0);
1056 g_ptr_array_free (objs, TRUE);
1058 #elif defined(HAVE_SGEN_GC)
1059 while ((count = mono_gc_finalizers_for_domain (domain, to_finalize, NUM_FOBJECTS))) {
1061 for (i = 0; i < count; ++i) {
1062 mono_gc_run_finalize (to_finalize [i], 0);
1067 /* cleanup the reference queue */
1068 reference_queue_clear_for_domain (domain);
1070 /* printf ("DONE.\n"); */
1071 SetEvent (req->done_event);
1073 /* The event is closed in mono_domain_finalize if we get here */
1078 finalizer_thread (gpointer unused)
1080 gboolean wait = TRUE;
1083 /* Wait to be notified that there's at least one
1087 g_assert (mono_domain_get () == mono_get_root_domain ());
1088 mono_gc_set_skip_thread (TRUE);
1091 /* An alertable wait is required so this thread can be suspended on windows */
1092 #ifdef MONO_HAS_SEMAPHORES
1093 MONO_SEM_WAIT_ALERTABLE (&finalizer_sem, TRUE);
1095 WaitForSingleObjectEx (finalizer_event, INFINITE, TRUE);
1099 mono_gc_set_skip_thread (FALSE);
1101 mono_threads_perform_thread_dump ();
1103 mono_console_handle_async_ops ();
1105 #ifndef DISABLE_ATTACH
1106 mono_attach_maybe_start ();
1109 if (domains_to_finalize) {
1110 mono_finalizer_lock ();
1111 if (domains_to_finalize) {
1112 DomainFinalizationReq *req = domains_to_finalize->data;
1113 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
1114 mono_finalizer_unlock ();
1116 finalize_domain_objects (req);
1118 mono_finalizer_unlock ();
1122 /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup),
1123 * before the domain is unloaded.
1125 mono_gc_invoke_finalizers ();
1127 mono_threads_join_threads ();
1129 reference_queue_proccess_all ();
1131 #ifdef MONO_HAS_SEMAPHORES
1132 /* Avoid posting the pending done event until there are pending finalizers */
1133 if (MONO_SEM_TIMEDWAIT (&finalizer_sem, 0) == 0)
1134 /* Don't wait again at the start of the loop */
1137 SetEvent (pending_done_event);
1139 SetEvent (pending_done_event);
1143 SetEvent (shutdown_event);
1147 #ifndef LAZY_GC_THREAD_CREATION
1151 mono_gc_init_finalizer_thread (void)
1153 gc_thread = mono_thread_create_internal (mono_domain_get (), finalizer_thread, NULL, FALSE, 0);
1154 ves_icall_System_Threading_Thread_SetName_internal (gc_thread, mono_string_new (mono_domain_get (), "Finalizer"));
1160 mono_mutex_init_recursive (&handle_section);
1161 mono_mutex_init_recursive (&allocator_section);
1163 mono_mutex_init_recursive (&finalizer_mutex);
1164 mono_mutex_init_recursive (&reference_queue_mutex);
1166 MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_NORMAL].entries);
1167 MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_PINNED].entries);
1169 mono_counters_register ("Minor GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.minor_gc_count);
1170 mono_counters_register ("Major GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.major_gc_count);
1171 mono_counters_register ("Minor GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.minor_gc_time);
1172 mono_counters_register ("Major GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time);
1173 mono_counters_register ("Major GC time concurrent", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time_concurrent);
1175 mono_gc_base_init ();
1177 if (mono_gc_is_disabled ()) {
1182 finalizer_event = CreateEvent (NULL, FALSE, FALSE, NULL);
1183 pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
1184 shutdown_event = CreateEvent (NULL, TRUE, FALSE, NULL);
1185 if (finalizer_event == NULL || pending_done_event == NULL || shutdown_event == NULL) {
1186 g_assert_not_reached ();
1188 #ifdef MONO_HAS_SEMAPHORES
1189 MONO_SEM_INIT (&finalizer_sem, 0);
1192 #ifndef LAZY_GC_THREAD_CREATION
1193 mono_gc_init_finalizer_thread ();
1198 mono_gc_cleanup (void)
1201 g_message ("%s: cleaning up finalizer", __func__);
1205 ResetEvent (shutdown_event);
1207 if (mono_thread_internal_current () != gc_thread) {
1208 gboolean timed_out = FALSE;
1210 mono_gc_finalize_notify ();
1211 /* Finishing the finalizer thread, so wait a little bit... */
1212 /* MS seems to wait for about 2 seconds */
1213 if (WaitForSingleObjectEx (shutdown_event, 2000, FALSE) == WAIT_TIMEOUT) {
1216 /* Set a flag which the finalizer thread can check */
1217 suspend_finalizers = TRUE;
1219 /* Try to abort the thread, in the hope that it is running managed code */
1220 mono_thread_internal_stop (gc_thread);
1222 /* Wait for it to stop */
1223 ret = WaitForSingleObjectEx (gc_thread->handle, 100, TRUE);
1225 if (ret == WAIT_TIMEOUT) {
1227 * The finalizer thread refused to die. There is not much we
1228 * can do here, since the runtime is shutting down so the
1229 * state the finalizer thread depends on will vanish.
1231 g_warning ("Shutting down finalizer thread timed out.");
1239 /* Wait for the thread to actually exit */
1240 ret = WaitForSingleObjectEx (gc_thread->handle, INFINITE, TRUE);
1241 g_assert (ret == WAIT_OBJECT_0);
1243 mono_thread_join (GUINT_TO_POINTER (gc_thread->tid));
1247 #ifdef HAVE_BOEHM_GC
1248 GC_finalizer_notifier = NULL;
1252 mono_reference_queue_cleanup ();
1254 mono_mutex_destroy (&handle_section);
1255 mono_mutex_destroy (&allocator_section);
1256 mono_mutex_destroy (&finalizer_mutex);
1257 mono_mutex_destroy (&reference_queue_mutex);
1262 /* Null GC dummy functions */
1264 mono_gc_finalize_notify (void)
1268 void mono_gc_init (void)
1270 mono_mutex_init_recursive (&handle_section);
1273 void mono_gc_cleanup (void)
1280 mono_gc_is_finalizer_internal_thread (MonoInternalThread *thread)
1282 return thread == gc_thread;
1286 * mono_gc_is_finalizer_thread:
1287 * @thread: the thread to test.
1289 * In Mono objects are finalized asynchronously on a separate thread.
1290 * This routine tests whether the @thread argument represents the
1291 * finalization thread.
1293 * Returns true if @thread is the finalization thread.
1296 mono_gc_is_finalizer_thread (MonoThread *thread)
1298 return mono_gc_is_finalizer_internal_thread (thread->internal_thread);
1301 #if defined(__MACH__)
1302 static pthread_t mach_exception_thread;
1305 mono_gc_register_mach_exception_thread (pthread_t thread)
1307 mach_exception_thread = thread;
1311 mono_gc_get_mach_exception_thread (void)
1313 return mach_exception_thread;
1318 * mono_gc_parse_environment_string_extract_number:
1320 * @str: points to the first digit of the number
1321 * @out: pointer to the variable that will receive the value
1323 * Tries to extract a number from the passed string, taking in to account m, k
1326 * Returns true if passing was successful
1329 mono_gc_parse_environment_string_extract_number (const char *str, size_t *out)
1332 int len = strlen (str), shift = 0;
1334 gboolean is_suffix = FALSE;
1340 suffix = str [len - 1];
1355 if (!isdigit (suffix))
1361 val = strtol (str, &endptr, 10);
1363 if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN))
1364 || (errno != 0 && val == 0) || (endptr == str))
1370 if (val < 0) /* negative numbers cannot be suffixed */
1372 if (*(endptr + 1)) /* Invalid string. */
1375 unshifted = (size_t)val;
1377 if (val < 0) /* overflow */
1379 if (((size_t)val >> shift) != unshifted) /* value too large */
1387 #ifndef HAVE_SGEN_GC
1389 mono_gc_alloc_mature (MonoVTable *vtable)
1391 return mono_object_new_specific (vtable);
1396 static MonoReferenceQueue *ref_queues;
1399 ref_list_remove_element (RefQueueEntry **prev, RefQueueEntry *element)
1402 /* Guard if head is changed concurrently. */
1403 while (*prev != element)
1404 prev = &(*prev)->next;
1405 } while (prev && InterlockedCompareExchangePointer ((void*)prev, element->next, element) != element);
1409 ref_list_push (RefQueueEntry **head, RefQueueEntry *value)
1411 RefQueueEntry *current;
1414 value->next = current;
1415 STORE_STORE_FENCE; /*Must make sure the previous store is visible before the CAS. */
1416 } while (InterlockedCompareExchangePointer ((void*)head, value, current) != current);
1420 reference_queue_proccess (MonoReferenceQueue *queue)
1422 RefQueueEntry **iter = &queue->queue;
1423 RefQueueEntry *entry;
1424 while ((entry = *iter)) {
1426 if (queue->should_be_deleted || !mono_gc_weak_link_get (&entry->dis_link)) {
1427 mono_gc_weak_link_remove (&entry->dis_link, TRUE);
1429 if (queue->should_be_deleted || !mono_gchandle_get_target (entry->gchandle)) {
1430 mono_gchandle_free ((guint32)entry->gchandle);
1432 ref_list_remove_element (iter, entry);
1433 queue->callback (entry->user_data);
1436 iter = &entry->next;
1442 reference_queue_proccess_all (void)
1444 MonoReferenceQueue **iter;
1445 MonoReferenceQueue *queue = ref_queues;
1446 for (; queue; queue = queue->next)
1447 reference_queue_proccess (queue);
1450 mono_mutex_lock (&reference_queue_mutex);
1451 for (iter = &ref_queues; *iter;) {
1453 if (!queue->should_be_deleted) {
1454 iter = &queue->next;
1458 mono_mutex_unlock (&reference_queue_mutex);
1459 reference_queue_proccess (queue);
1462 *iter = queue->next;
1465 mono_mutex_unlock (&reference_queue_mutex);
1469 mono_reference_queue_cleanup (void)
1471 MonoReferenceQueue *queue = ref_queues;
1472 for (; queue; queue = queue->next)
1473 queue->should_be_deleted = TRUE;
1474 reference_queue_proccess_all ();
1478 reference_queue_clear_for_domain (MonoDomain *domain)
1480 MonoReferenceQueue *queue = ref_queues;
1481 for (; queue; queue = queue->next) {
1482 RefQueueEntry **iter = &queue->queue;
1483 RefQueueEntry *entry;
1484 while ((entry = *iter)) {
1485 if (entry->domain == domain) {
1487 mono_gc_weak_link_remove (&entry->dis_link, TRUE);
1489 mono_gchandle_free ((guint32)entry->gchandle);
1491 ref_list_remove_element (iter, entry);
1492 queue->callback (entry->user_data);
1495 iter = &entry->next;
1501 * mono_gc_reference_queue_new:
1502 * @callback callback used when processing collected entries.
1504 * Create a new reference queue used to process collected objects.
1505 * A reference queue let you add a pair of (managed object, user data)
1506 * using the mono_gc_reference_queue_add method.
1508 * Once the managed object is collected @callback will be called
1509 * in the finalizer thread with 'user data' as argument.
1511 * The callback is called from the finalizer thread without any locks held.
1512 * When a AppDomain is unloaded, all callbacks for objects belonging to it
1515 * @returns the new queue.
1518 mono_gc_reference_queue_new (mono_reference_queue_callback callback)
1520 MonoReferenceQueue *res = g_new0 (MonoReferenceQueue, 1);
1521 res->callback = callback;
1523 mono_mutex_lock (&reference_queue_mutex);
1524 res->next = ref_queues;
1526 mono_mutex_unlock (&reference_queue_mutex);
1532 * mono_gc_reference_queue_add:
1533 * @queue the queue to add the reference to.
1534 * @obj the object to be watched for collection
1535 * @user_data parameter to be passed to the queue callback
1537 * Queue an object to be watched for collection, when the @obj is
1538 * collected, the callback that was registered for the @queue will
1539 * be invoked with @user_data as argument.
1541 * @returns false if the queue is scheduled to be freed.
1544 mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *user_data)
1546 RefQueueEntry *entry;
1547 if (queue->should_be_deleted)
1550 entry = g_new0 (RefQueueEntry, 1);
1551 entry->user_data = user_data;
1552 entry->domain = mono_object_domain (obj);
1555 mono_gc_weak_link_add (&entry->dis_link, obj, TRUE);
1557 entry->gchandle = mono_gchandle_new_weakref (obj, TRUE);
1558 mono_object_register_finalizer (obj);
1561 ref_list_push (&queue->queue, entry);
1566 * mono_gc_reference_queue_free:
1567 * @queue the queue that should be freed.
1569 * This operation signals that @queue should be freed. This operation is deferred
1570 * as it happens on the finalizer thread.
1572 * After this call, no further objects can be queued. It's the responsibility of the
1573 * caller to make sure that no further attempt to access queue will be made.
1576 mono_gc_reference_queue_free (MonoReferenceQueue *queue)
1578 queue->should_be_deleted = TRUE;