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
;
309 static guint32 next_handle
= 0;
310 static gpointer
*gc_handles
= NULL
;
311 static guint8
*gc_handle_types
= NULL
;
312 static guint32 array_size
= 0;
315 * The handle type is encoded in the lower two bits of the handle value:
329 * FIXME: make thread safe and reuse the array entries.
332 ves_icall_System_GCHandle_GetTarget (guint32 handle
)
341 EnterCriticalSection (&handle_section
);
342 g_assert (type
== gc_handle_types
[handle
>> 2]);
343 obj
= gc_handles
[handle
>> 2];
344 LeaveCriticalSection (&handle_section
);
348 if ((type
== HANDLE_WEAK
) || (type
== HANDLE_WEAK_TRACK
))
349 return REVEAL_POINTER (obj
);
357 ves_icall_System_GCHandle_GetTargetHandle (MonoObject
*obj
, guint32 handle
, gint32 type
)
364 EnterCriticalSection (&handle_section
);
365 /* Indexes start from 1 since 0 means the handle is not allocated */
367 if (idx
>= array_size
) {
369 guint8
*new_type_array
;
373 new_array
= GC_MALLOC (sizeof (gpointer
) * (array_size
* 2));
374 new_type_array
= GC_MALLOC (sizeof (guint8
) * (array_size
* 2));
376 new_array
= g_malloc0 (sizeof (gpointer
) * (array_size
* 2));
377 new_type_array
= g_malloc0 (sizeof (guint8
) * (array_size
* 2));
381 memcpy (new_array
, gc_handles
, sizeof (gpointer
) * array_size
);
382 memcpy (new_type_array
, gc_handle_types
, sizeof (guint8
) * array_size
);
383 /* need to re-register links for weak refs. test if GC_realloc needs the same */
384 for (i
= 0; i
< array_size
; ++i
) {
385 #if 0 /* This breaks the threaded finalizer, by causing segfaults deep
386 * inside libgc. I assume it will also break without the
387 * threaded finalizer, just that the stress test (bug 31333)
388 * deadlocks too early without it. Reverting to the previous
389 * version here stops the segfault.
391 if ((gc_handle_types
[i
] == HANDLE_WEAK
) || (gc_handle_types
[i
] == HANDLE_WEAK_TRACK
)) { /* all and only disguised pointers have it set */
393 if (((gulong
)new_array
[i
]) & 0x1) {
396 if (gc_handles
[i
] != (gpointer
)-1)
397 GC_unregister_disappearing_link (&(gc_handles
[i
]));
398 if (new_array
[i
] != (gpointer
)-1)
399 GC_GENERAL_REGISTER_DISAPPEARING_LINK (&(new_array
[i
]), REVEAL_POINTER (new_array
[i
]));
405 #ifndef HAVE_BOEHM_GC
407 g_free (gc_handle_types
);
409 gc_handles
= new_array
;
410 gc_handle_types
= new_type_array
;
413 /* resuse the type from the old target */
416 h
= (idx
<< 2) | type
;
419 case HANDLE_WEAK_TRACK
:
420 val
= (gpointer
)HIDE_POINTER (val
);
421 gc_handles
[idx
] = val
;
422 gc_handle_types
[idx
] = type
;
424 if (gc_handles
[idx
] != (gpointer
)-1)
425 GC_GENERAL_REGISTER_DISAPPEARING_LINK (&(gc_handles
[idx
]), obj
);
429 gc_handles
[idx
] = val
;
430 gc_handle_types
[idx
] = type
;
433 LeaveCriticalSection (&handle_section
);
438 ves_icall_System_GCHandle_FreeHandle (guint32 handle
)
440 int idx
= handle
>> 2;
441 int type
= handle
& 0x3;
445 EnterCriticalSection (&handle_section
);
448 g_assert (type
== gc_handle_types
[idx
]);
449 if ((type
== HANDLE_WEAK
) || (type
== HANDLE_WEAK_TRACK
)) {
450 if (gc_handles
[idx
] != (gpointer
)-1)
451 GC_unregister_disappearing_link (&(gc_handles
[idx
]));
455 gc_handles
[idx
] = (gpointer
)-1;
456 gc_handle_types
[idx
] = (guint8
)-1;
457 LeaveCriticalSection (&handle_section
);
461 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle
)
464 int type
= handle
& 0x3;
469 EnterCriticalSection (&handle_section
);
470 obj
= gc_handles
[handle
>> 2];
471 g_assert (gc_handle_types
[handle
>> 2] == type
);
472 LeaveCriticalSection (&handle_section
);
473 if ((type
== HANDLE_WEAK
) || (type
== HANDLE_WEAK_TRACK
)) {
474 obj
= REVEAL_POINTER (obj
);
475 if (obj
== (MonoObject
*) -1)
479 MonoClass
*klass
= mono_object_class (obj
);
480 if (klass
== mono_defaults
.string_class
) {
481 return mono_string_chars ((MonoString
*)obj
);
482 } else if (klass
->rank
) {
483 return mono_array_addr ((MonoArray
*)obj
, char, 0);
485 /* the C# code will check and throw the exception */
486 /* FIXME: missing !klass->blittable test, see bug #61134 */
487 if ((klass
->flags
& TYPE_ATTRIBUTE_LAYOUT_MASK
) == TYPE_ATTRIBUTE_AUTO_LAYOUT
)
489 return (char*)obj
+ sizeof (MonoObject
);
497 mono_gchandle_new (MonoObject
*obj
, gboolean pinned
)
499 return ves_icall_System_GCHandle_GetTargetHandle (obj
, 0, pinned
? HANDLE_PINNED
: HANDLE_NORMAL
);
503 mono_gchandle_new_weakref (MonoObject
*obj
, gboolean track_resurrection
)
505 return ves_icall_System_GCHandle_GetTargetHandle (obj
, 0, track_resurrection
? HANDLE_WEAK_TRACK
: HANDLE_WEAK
);
508 /* This will return NULL for a collected object if using a weakref handle */
510 mono_gchandle_get_target (guint32 gchandle
)
512 return ves_icall_System_GCHandle_GetTarget (gchandle
);
516 mono_gchandle_free (guint32 gchandle
)
518 ves_icall_System_GCHandle_FreeHandle (gchandle
);
523 static HANDLE finalizer_event
;
524 static volatile gboolean finished
=FALSE
;
526 static void finalize_notify (void)
529 g_message (G_GNUC_PRETTY_FUNCTION
": prodding finalizer");
532 SetEvent (finalizer_event
);
536 collect_objects (gpointer key
, gpointer value
, gpointer user_data
)
538 GPtrArray
*arr
= (GPtrArray
*)user_data
;
539 g_ptr_array_add (arr
, key
);
543 * finalize_domain_objects:
545 * Run the finalizers of all finalizable objects in req->domain.
548 finalize_domain_objects (DomainFinalizationReq
*req
)
552 MonoDomain
*domain
= req
->domain
;
554 while (g_hash_table_size (domain
->finalizable_objects_hash
) > 0) {
556 * Since the domain is unloading, nobody is allowed to put
557 * new entries into the hash table. But finalize_object might
558 * remove entries from the hash table, so we make a copy.
560 objs
= g_ptr_array_new ();
561 g_hash_table_foreach (domain
->finalizable_objects_hash
,
562 collect_objects
, objs
);
563 /* printf ("FINALIZING %d OBJECTS.\n", objs->len); */
565 for (i
= 0; i
< objs
->len
; ++i
) {
566 MonoObject
*o
= (MonoObject
*)g_ptr_array_index (objs
, i
);
567 /* FIXME: Avoid finalizing threads, etc */
571 g_ptr_array_free (objs
, TRUE
);
574 /* Process finalizers which are already in the queue */
575 GC_invoke_finalizers ();
577 /* printf ("DONE.\n"); */
578 SetEvent (req
->done_event
);
580 /* The event is closed in mono_domain_finalize if we get here */
584 static guint32
finalizer_thread (gpointer unused
)
586 gc_thread
= mono_thread_current ();
588 SetEvent (thread_started_event
);
591 /* Wait to be notified that there's at least one
594 WaitForSingleObjectEx (finalizer_event
, INFINITE
, TRUE
);
596 if (domains_to_finalize
) {
597 EnterCriticalSection (&finalizer_mutex
);
598 if (domains_to_finalize
) {
599 DomainFinalizationReq
*req
= domains_to_finalize
->data
;
600 domains_to_finalize
= g_slist_remove (domains_to_finalize
, req
);
601 LeaveCriticalSection (&finalizer_mutex
);
603 finalize_domain_objects (req
);
606 LeaveCriticalSection (&finalizer_mutex
);
610 g_message (G_GNUC_PRETTY_FUNCTION
": invoking finalizers");
613 /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup),
614 * before the domain is unloaded.
616 * There is a bug in GC_invoke_finalizer () in versions <= 6.2alpha4:
617 * the 'mem_freed' variable is not initialized when there are no
618 * objects to finalize, which leads to strange behavior later on.
619 * The check is necessary to work around that bug.
621 if (GC_should_invoke_finalizers ()) {
622 GC_invoke_finalizers ();
625 SetEvent (pending_done_event
);
628 SetEvent (shutdown_event
);
633 * Enable or disable the separate finalizer thread.
634 * It's currently disabled because it still requires some
635 * work in the rest of the runtime.
637 #define ENABLE_FINALIZER_THREAD
639 #ifdef WITH_INCLUDED_LIBGC
641 extern void mono_gc_stop_world (void);
642 extern void mono_gc_start_world (void);
643 extern void mono_gc_push_all_stacks (void);
645 static void mono_gc_lock (void)
647 EnterCriticalSection (&allocator_section
);
650 static void mono_gc_unlock (void)
652 LeaveCriticalSection (&allocator_section
);
655 static GCThreadFunctions mono_gc_thread_vtable
= {
663 mono_gc_push_all_stacks
,
666 #endif /* WITH_INCLUDED_LIBGC */
669 mono_gc_warning (char *msg
, GC_word arg
)
671 mono_trace (G_LOG_LEVEL_WARNING
, MONO_TRACE_GC
, msg
, (unsigned long)arg
);
674 void mono_gc_init (void)
676 InitializeCriticalSection (&handle_section
);
677 InitializeCriticalSection (&allocator_section
);
679 InitializeCriticalSection (&finalizer_mutex
);
681 #ifdef WITH_INCLUDED_LIBGC
682 gc_thread_vtable
= &mono_gc_thread_vtable
;
685 MONO_GC_REGISTER_ROOT (gc_handles
);
686 MONO_GC_REGISTER_ROOT (gc_handle_types
);
689 GC_oom_fn
= mono_gc_out_of_memory
;
691 GC_set_warn_proc (mono_gc_warning
);
693 #ifdef ENABLE_FINALIZER_THREAD
695 if (g_getenv ("GC_DONT_GC")) {
700 finalizer_event
= CreateEvent (NULL
, FALSE
, FALSE
, NULL
);
701 pending_done_event
= CreateEvent (NULL
, TRUE
, FALSE
, NULL
);
702 shutdown_event
= CreateEvent (NULL
, TRUE
, FALSE
, NULL
);
703 thread_started_event
= CreateEvent (NULL
, TRUE
, FALSE
, NULL
);
704 if (finalizer_event
== NULL
|| pending_done_event
== NULL
|| shutdown_event
== NULL
|| thread_started_event
== NULL
) {
705 g_assert_not_reached ();
708 GC_finalize_on_demand
= 1;
709 GC_finalizer_notifier
= finalize_notify
;
711 mono_thread_create (mono_domain_get (), finalizer_thread
, NULL
);
713 * Wait until the finalizer thread sets gc_thread since its value is needed
714 * by mono_thread_attach ()
716 WaitForSingleObjectEx (thread_started_event
, INFINITE
, FALSE
);
720 void mono_gc_cleanup (void)
723 g_message (G_GNUC_PRETTY_FUNCTION
": cleaning up finalizer");
726 #ifdef ENABLE_FINALIZER_THREAD
728 ResetEvent (shutdown_event
);
730 if (mono_thread_current () != gc_thread
) {
732 /* Finishing the finalizer thread, so wait a little bit... */
733 /* MS seems to wait for about 2 seconds */
734 if (WaitForSingleObjectEx (shutdown_event
, 2000000, FALSE
) == WAIT_TIMEOUT
) {
735 mono_thread_stop (gc_thread
);
739 GC_finalizer_notifier
= NULL
;
747 /* no Boehm GC support. */
748 void mono_gc_init (void)
750 InitializeCriticalSection (&handle_section
);
753 void mono_gc_cleanup (void)
760 mono_gc_is_finalizer_thread (MonoThread
*thread
)
762 return thread
== gc_thread
;