2 * metadata/gc.c: GC icalls.
4 * Author: Paolo Molaro <lupus@ximian.com>
6 * (C) 2002 Ximian, Inc.
13 #include <mono/metadata/gc-internal.h>
14 #include <mono/metadata/mono-gc.h>
15 #include <mono/metadata/threads.h>
16 #include <mono/metadata/tabledefs.h>
17 #include <mono/metadata/exception.h>
18 #include <mono/metadata/profiler-private.h>
19 #include <mono/metadata/domain-internals.h>
20 #include <mono/metadata/class-internals.h>
21 #include <mono/utils/mono-logger.h>
22 #define GC_I_HIDE_POINTERS
23 #include <mono/os/gc_wrapper.h>
26 #define HIDE_POINTER(v) (v)
27 #define REVEAL_POINTER(v) (v)
30 typedef struct DomainFinalizationReq {
33 } DomainFinalizationReq;
35 #ifdef PLATFORM_WINCE /* FIXME: add accessors to gc.dll API */
36 extern void (*__imp_GC_finalizer_notifier)(void);
37 #define GC_finalizer_notifier __imp_GC_finalizer_notifier
38 extern int __imp_GC_finalize_on_demand;
39 #define GC_finalize_on_demand __imp_GC_finalize_on_demand
42 static int finalize_slot = -1;
44 static gboolean gc_disabled = FALSE;
46 static CRITICAL_SECTION finalizer_mutex;
48 static GSList *domains_to_finalize= NULL;
50 static MonoThread *gc_thread;
52 static void object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*));
55 static void finalize_notify (void);
56 static HANDLE pending_done_event;
57 static HANDLE shutdown_event;
58 static HANDLE thread_started_event;
62 * actually, we might want to queue the finalize requests in a separate thread,
63 * but we need to be careful about the execution domain of the thread...
66 run_finalize (void *obj, void *data)
68 MonoObject *exc = NULL;
70 o = (MonoObject*)((char*)obj + GPOINTER_TO_UINT (data));
72 if (finalize_slot < 0) {
74 MonoClass* obj_class = mono_get_object_class ();
75 for (i = 0; i < obj_class->vtable_size; ++i) {
76 MonoMethod *cm = obj_class->vtable [i];
78 if (!strcmp (mono_method_get_name (cm), "Finalize")) {
85 mono_domain_lock (o->vtable->domain);
87 o2 = g_hash_table_lookup (o->vtable->domain->finalizable_objects_hash, o);
89 mono_domain_unlock (o->vtable->domain);
92 /* Already finalized somehow */
95 /* make sure the finalizer is not called again if the object is resurrected */
96 object_register_finalizer (obj, NULL);
98 if (o->vtable->klass == mono_get_thread_class ())
99 if (mono_gc_is_finalizer_thread ((MonoThread*)o))
100 /* Avoid finalizing ourselves */
103 /* speedup later... and use a timeout */
104 /* g_print ("Finalize run on %p %s.%s\n", o, mono_object_class (o)->name_space, mono_object_class (o)->name); */
106 /* Use _internal here, since this thread can enter a doomed appdomain */
107 mono_domain_set_internal (mono_object_domain (o));
109 mono_runtime_invoke (o->vtable->klass->vtable [finalize_slot], o, NULL, &exc);
112 /* fixme: do something useful */
117 mono_gc_out_of_memory (size_t size)
120 * we could allocate at program startup some memory that we could release
121 * back to the system at this point if we're really low on memory (ie, size is
122 * lower than the memory we set apart)
124 mono_raise_exception (mono_domain_get ()->out_of_memory_ex);
130 * Some of our objects may point to a different address than the address returned by GC_malloc()
131 * (because of the GetHashCode hack), but we need to pass the real address to register_finalizer.
132 * This also means that in the callback we need to adjust the pointer to get back the real
134 * We also need to be consistent in the use of the GC_debug* variants of malloc and register_finalizer,
135 * since that, too, can cause the underlying pointer to be offset.
138 object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*))
144 /* This assertion is not valid when GC_DEBUG is defined */
145 g_assert (GC_base (obj) == (char*)obj - offset);
148 if (mono_domain_is_unloading (obj->vtable->domain) && (callback != NULL))
150 * Can't register finalizers in a dying appdomain, since they
151 * could be invoked after the appdomain has been unloaded.
155 mono_domain_lock (obj->vtable->domain);
158 g_hash_table_insert (obj->vtable->domain->finalizable_objects_hash, obj,
161 g_hash_table_remove (obj->vtable->domain->finalizable_objects_hash, obj);
163 mono_domain_unlock (obj->vtable->domain);
165 GC_REGISTER_FINALIZER_NO_ORDER ((char*)obj - offset, callback, GUINT_TO_POINTER (offset), NULL, NULL);
170 mono_object_register_finalizer (MonoObject *obj)
172 /* g_print ("Registered finalizer on %p %s.%s\n", obj, mono_object_class (obj)->name_space, mono_object_class (obj)->name); */
173 object_register_finalizer (obj, run_finalize);
177 * mono_domain_finalize:
179 * Request finalization of all finalizable objects inside @domain. Wait
180 * @timeout msecs for the finalization to complete.
181 * Returns: TRUE if succeeded, FALSE if there was a timeout
185 mono_domain_finalize (MonoDomain *domain, guint32 timeout)
187 DomainFinalizationReq *req;
191 if (mono_thread_current () == gc_thread)
192 /* We are called from inside a finalizer, not much we can do here */
195 mono_profiler_appdomain_event (domain, MONO_PROFILE_START_UNLOAD);
198 * No need to create another thread 'cause the finalizer thread
199 * is still working and will take care of running the finalizers
208 done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
210 req = g_new0 (DomainFinalizationReq, 1);
211 req->domain = domain;
212 req->done_event = done_event;
214 EnterCriticalSection (&finalizer_mutex);
216 domains_to_finalize = g_slist_append (domains_to_finalize, req);
218 LeaveCriticalSection (&finalizer_mutex);
220 /* Tell the finalizer thread to finalize this appdomain */
223 res = WaitForSingleObjectEx (done_event, timeout, TRUE);
225 /* printf ("WAIT RES: %d.\n", res); */
226 if (res == WAIT_TIMEOUT) {
227 /* We leak the handle here */
231 CloseHandle (done_event);
234 /* We don't support domain finalization without a GC */
240 ves_icall_System_GC_InternalCollect (int generation)
250 ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection)
255 mono_gc_collect (mono_gc_max_generation ());
256 return mono_gc_get_used_size ();
260 ves_icall_System_GC_KeepAlive (MonoObject *obj)
270 ves_icall_System_GC_ReRegisterForFinalize (MonoObject *obj)
274 object_register_finalizer (obj, run_finalize);
278 ves_icall_System_GC_SuppressFinalize (MonoObject *obj)
282 object_register_finalizer (obj, NULL);
286 ves_icall_System_GC_WaitForPendingFinalizers (void)
291 if (!GC_should_invoke_finalizers ())
294 if (mono_thread_current () == gc_thread)
295 /* Avoid deadlocks */
298 ResetEvent (pending_done_event);
300 /* g_print ("Waiting for pending finalizers....\n"); */
301 WaitForSingleObjectEx (pending_done_event, INFINITE, TRUE);
302 /* g_print ("Done pending....\n"); */
307 static CRITICAL_SECTION allocator_section;
308 static CRITICAL_SECTION handle_section;
317 static void mono_gchandle_set_target (guint32 gchandle, MonoObject *obj);
320 ves_icall_System_GCHandle_GetTarget (guint32 handle)
322 return mono_gchandle_get_target (handle);
326 * if type == -1, change the target of the handle, otherwise allocate a new handle.
329 ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type)
332 mono_gchandle_set_target (handle, obj);
333 /* the handle doesn't change */
338 return mono_gchandle_new_weakref (obj, FALSE);
339 case HANDLE_WEAK_TRACK:
340 return mono_gchandle_new_weakref (obj, TRUE);
342 return mono_gchandle_new (obj, FALSE);
344 return mono_gchandle_new (obj, TRUE);
346 g_assert_not_reached ();
352 ves_icall_System_GCHandle_FreeHandle (guint32 handle)
354 mono_gchandle_free (handle);
358 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
362 obj = mono_gchandle_get_target (handle);
364 MonoClass *klass = mono_object_class (obj);
365 if (klass == mono_defaults.string_class) {
366 return mono_string_chars ((MonoString*)obj);
367 } else if (klass->rank) {
368 return mono_array_addr ((MonoArray*)obj, char, 0);
370 /* the C# code will check and throw the exception */
371 /* FIXME: missing !klass->blittable test, see bug #61134 */
372 if ((klass->flags & TYPE_ATTRIBUTE_LAYOUT_MASK) == TYPE_ATTRIBUTE_AUTO_LAYOUT)
374 return (char*)obj + sizeof (MonoObject);
385 guint slot_hint : 24; /* starting slot for search */
388 /* weak and weak-track arrays will be allocated in malloc memory
389 * (sadly libgc still requires REVEAL/HIDE_POINTER)
391 static HandleData gc_handles [] = {
392 {NULL, NULL, 0, HANDLE_WEAK, 0},
393 {NULL, NULL, 0, HANDLE_WEAK_TRACK, 0},
394 {NULL, NULL, 0, HANDLE_NORMAL, 0},
395 {NULL, NULL, 0, HANDLE_PINNED, 0}
398 #define lock_handles(handles) EnterCriticalSection (&handle_section)
399 #define unlock_handles(handles) LeaveCriticalSection (&handle_section)
402 find_first_unset (guint32 bitmap)
405 for (i = 0; i < 32; ++i) {
406 if (!(bitmap & (1 << i)))
413 alloc_handle (HandleData *handles, MonoObject *obj)
416 lock_handles (handles);
417 if (!handles->size) {
419 if (handles->type > HANDLE_WEAK_TRACK) {
421 handles->entries = GC_MALLOC (sizeof (gpointer) * handles->size);
423 handles->entries = g_malloc0 (sizeof (gpointer) * handles->size);
426 handles->entries = g_malloc0 (sizeof (gpointer) * handles->size);
428 handles->bitmap = g_malloc0 (handles->size / 8);
431 for (slot = handles->slot_hint; slot < handles->size / 32; ++slot) {
432 if (handles->bitmap [slot] != 0xffffffff) {
433 i = find_first_unset (handles->bitmap [slot]);
434 handles->slot_hint = slot;
438 if (i == -1 && handles->slot_hint != 0) {
439 for (slot = 0; slot < handles->slot_hint; ++slot) {
440 if (handles->bitmap [slot] != 0xffffffff) {
441 i = find_first_unset (handles->bitmap [slot]);
442 handles->slot_hint = slot;
449 guint32 new_size = handles->size * 2; /* always double: we memset to 0 based on this below */
451 /* resize and copy the bitmap */
452 new_bitmap = g_malloc0 (new_size / 8);
453 memcpy (new_bitmap, handles->bitmap, handles->size / 8);
454 g_free (handles->bitmap);
455 handles->bitmap = new_bitmap;
457 /* resize and copy the entries */
458 if (handles->type > HANDLE_WEAK_TRACK) {
461 entries = GC_MALLOC (sizeof (gpointer) * new_size);
462 memcpy (entries, handles->entries, sizeof (gpointer) * handles->size);
463 handles->entries = entries;
465 handles->entries = g_realloc (handles->entries, sizeof (gpointer) * new_size);
466 memset (handles->entries + handles->size, 0, sizeof (gpointer) * handles->size);
471 entries = g_malloc (sizeof (gpointer) * new_size);
472 /* we disable GC because we could lose some disappearing link updates */
474 memcpy (entries, handles->entries, sizeof (gpointer) * handles->size);
475 memset (entries + handles->size, 0, sizeof (gpointer) * handles->size);
476 for (i = 0; i < handles->size; ++i) {
477 GC_unregister_disappearing_link (&(handles->entries [i]));
478 /*g_print ("reg/unreg entry %d of type %d at %p to object %p (%p), was: %p\n", i, handles->type, &(entries [i]), REVEAL_POINTER (entries [i]), entries [i], handles->entries [i]);*/
479 if (entries [i] && entries [i] != (gpointer)-1) {
480 GC_GENERAL_REGISTER_DISAPPEARING_LINK (&(entries [i]), REVEAL_POINTER (entries [i]));
483 g_free (handles->entries);
484 handles->entries = entries;
487 handles->entries = g_realloc (handles->entries, sizeof (gpointer) * new_size);
488 memset (handles->entries + handles->size, 0, sizeof (gpointer) * handles->size);
492 /* set i and slot to the next free position */
494 slot = (handles->size + 1) / 32;
495 handles->slot_hint = handles->size + 1;
496 handles->size = new_size;
498 handles->bitmap [slot] |= 1 << i;
499 slot = slot * 32 + i;
500 handles->entries [slot] = obj;
502 if (handles->type <= HANDLE_WEAK_TRACK) {
503 handles->entries [slot] = (void*)HIDE_POINTER (obj);
504 GC_GENERAL_REGISTER_DISAPPEARING_LINK (&(handles->entries [slot]), obj);
508 unlock_handles (handles);
509 /*g_print ("allocated entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
510 return (slot << 2) | handles->type;
514 mono_gchandle_new (MonoObject *obj, gboolean pinned)
516 return alloc_handle (&gc_handles [pinned? HANDLE_PINNED: HANDLE_NORMAL], obj);
520 mono_gchandle_new_weakref (MonoObject *obj, gboolean track_resurrection)
522 return alloc_handle (&gc_handles [track_resurrection? HANDLE_WEAK_TRACK: HANDLE_WEAK], obj);
525 /* This will return NULL for a collected object if using a weakref handle */
527 mono_gchandle_get_target (guint32 gchandle)
529 guint slot = gchandle >> 2;
530 HandleData *handles = &gc_handles [gchandle & 3];
531 MonoObject *obj = NULL;
532 lock_handles (handles);
533 if (slot < handles->size) {
534 obj = handles->entries [slot];
535 if (handles->type <= HANDLE_WEAK_TRACK) {
536 obj = REVEAL_POINTER (obj);
537 if (obj == (MonoObject *) -1)
541 /* print a warning? */
543 unlock_handles (handles);
544 /*g_print ("get target of entry %d of type %d: %p\n", slot, handles->type, obj);*/
549 mono_gchandle_set_target (guint32 gchandle, MonoObject *obj)
551 guint slot = gchandle >> 2;
552 HandleData *handles = &gc_handles [gchandle & 3];
553 lock_handles (handles);
554 if (slot < handles->size) {
556 if (handles->type <= HANDLE_WEAK_TRACK) {
557 GC_unregister_disappearing_link (&handles->entries [slot]);
558 GC_GENERAL_REGISTER_DISAPPEARING_LINK (&(handles->entries [slot]), obj);
559 handles->entries [slot] = (void*)HIDE_POINTER (obj);
561 handles->entries [slot] = obj;
564 handles->entries [slot] = obj;
567 /* print a warning? */
569 /*g_print ("changed entry %d of type %d to object %p (in slot: %p)\n", slot, gchandle & 3, obj, handles->entries [slot]);*/
570 unlock_handles (handles);
574 mono_gchandle_free (guint32 gchandle)
576 guint slot = gchandle >> 2;
577 HandleData *handles = &gc_handles [gchandle & 3];
578 lock_handles (handles);
579 if (slot < handles->size) {
581 if (handles->type <= HANDLE_WEAK_TRACK)
582 GC_unregister_disappearing_link (&handles->entries [slot]);
584 handles->entries [slot] = NULL;
585 handles->bitmap [slot / 32] &= ~(1 << (slot % 32));
587 /* print a warning? */
589 /*g_print ("freed entry %d of type %d\n", slot, gchandle & 3);*/
590 unlock_handles (handles);
595 static HANDLE finalizer_event;
596 static volatile gboolean finished=FALSE;
598 static void finalize_notify (void)
601 g_message (G_GNUC_PRETTY_FUNCTION ": prodding finalizer");
604 SetEvent (finalizer_event);
608 collect_objects (gpointer key, gpointer value, gpointer user_data)
610 GPtrArray *arr = (GPtrArray*)user_data;
611 g_ptr_array_add (arr, key);
615 * finalize_domain_objects:
617 * Run the finalizers of all finalizable objects in req->domain.
620 finalize_domain_objects (DomainFinalizationReq *req)
624 MonoDomain *domain = req->domain;
626 while (g_hash_table_size (domain->finalizable_objects_hash) > 0) {
628 * Since the domain is unloading, nobody is allowed to put
629 * new entries into the hash table. But finalize_object might
630 * remove entries from the hash table, so we make a copy.
632 objs = g_ptr_array_new ();
633 g_hash_table_foreach (domain->finalizable_objects_hash,
634 collect_objects, objs);
635 /* printf ("FINALIZING %d OBJECTS.\n", objs->len); */
637 for (i = 0; i < objs->len; ++i) {
638 MonoObject *o = (MonoObject*)g_ptr_array_index (objs, i);
639 /* FIXME: Avoid finalizing threads, etc */
643 g_ptr_array_free (objs, TRUE);
646 /* Process finalizers which are already in the queue */
647 GC_invoke_finalizers ();
649 /* printf ("DONE.\n"); */
650 SetEvent (req->done_event);
652 /* The event is closed in mono_domain_finalize if we get here */
656 static guint32 finalizer_thread (gpointer unused)
658 gc_thread = mono_thread_current ();
660 SetEvent (thread_started_event);
663 /* Wait to be notified that there's at least one
666 WaitForSingleObjectEx (finalizer_event, INFINITE, TRUE);
668 if (domains_to_finalize) {
669 EnterCriticalSection (&finalizer_mutex);
670 if (domains_to_finalize) {
671 DomainFinalizationReq *req = domains_to_finalize->data;
672 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
673 LeaveCriticalSection (&finalizer_mutex);
675 finalize_domain_objects (req);
678 LeaveCriticalSection (&finalizer_mutex);
682 g_message (G_GNUC_PRETTY_FUNCTION ": invoking finalizers");
685 /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup),
686 * before the domain is unloaded.
688 * There is a bug in GC_invoke_finalizer () in versions <= 6.2alpha4:
689 * the 'mem_freed' variable is not initialized when there are no
690 * objects to finalize, which leads to strange behavior later on.
691 * The check is necessary to work around that bug.
693 if (GC_should_invoke_finalizers ()) {
694 GC_invoke_finalizers ();
697 SetEvent (pending_done_event);
700 SetEvent (shutdown_event);
705 * Enable or disable the separate finalizer thread.
706 * It's currently disabled because it still requires some
707 * work in the rest of the runtime.
709 #define ENABLE_FINALIZER_THREAD
711 #ifdef WITH_INCLUDED_LIBGC
713 extern void mono_gc_stop_world (void);
714 extern void mono_gc_start_world (void);
715 extern void mono_gc_push_all_stacks (void);
717 static void mono_gc_lock (void)
719 EnterCriticalSection (&allocator_section);
722 static void mono_gc_unlock (void)
724 LeaveCriticalSection (&allocator_section);
727 static GCThreadFunctions mono_gc_thread_vtable = {
735 mono_gc_push_all_stacks,
738 #endif /* WITH_INCLUDED_LIBGC */
741 mono_gc_warning (char *msg, GC_word arg)
743 mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_GC, msg, (unsigned long)arg);
746 void mono_gc_init (void)
748 InitializeCriticalSection (&handle_section);
749 InitializeCriticalSection (&allocator_section);
751 InitializeCriticalSection (&finalizer_mutex);
753 #ifdef WITH_INCLUDED_LIBGC
754 gc_thread_vtable = &mono_gc_thread_vtable;
757 MONO_GC_REGISTER_ROOT (gc_handles [HANDLE_NORMAL].entries);
758 MONO_GC_REGISTER_ROOT (gc_handles [HANDLE_PINNED].entries);
761 GC_oom_fn = mono_gc_out_of_memory;
763 GC_set_warn_proc (mono_gc_warning);
765 #ifdef ENABLE_FINALIZER_THREAD
767 if (g_getenv ("GC_DONT_GC")) {
772 finalizer_event = CreateEvent (NULL, FALSE, FALSE, NULL);
773 pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
774 shutdown_event = CreateEvent (NULL, TRUE, FALSE, NULL);
775 thread_started_event = CreateEvent (NULL, TRUE, FALSE, NULL);
776 if (finalizer_event == NULL || pending_done_event == NULL || shutdown_event == NULL || thread_started_event == NULL) {
777 g_assert_not_reached ();
780 GC_finalize_on_demand = 1;
781 GC_finalizer_notifier = finalize_notify;
783 mono_thread_create (mono_domain_get (), finalizer_thread, NULL);
785 * Wait until the finalizer thread sets gc_thread since its value is needed
786 * by mono_thread_attach ()
788 WaitForSingleObjectEx (thread_started_event, INFINITE, FALSE);
792 void mono_gc_cleanup (void)
795 g_message (G_GNUC_PRETTY_FUNCTION ": cleaning up finalizer");
798 #ifdef ENABLE_FINALIZER_THREAD
800 ResetEvent (shutdown_event);
802 if (mono_thread_current () != gc_thread) {
804 /* Finishing the finalizer thread, so wait a little bit... */
805 /* MS seems to wait for about 2 seconds */
806 if (WaitForSingleObjectEx (shutdown_event, 2000000, FALSE) == WAIT_TIMEOUT) {
807 mono_thread_stop (gc_thread);
811 GC_finalizer_notifier = NULL;
819 /* no Boehm GC support. */
820 void mono_gc_init (void)
822 InitializeCriticalSection (&handle_section);
825 void mono_gc_cleanup (void)
832 mono_gc_is_finalizer_thread (MonoThread *thread)
834 return thread == gc_thread;