2 * metadata/gc.c: GC icalls.
4 * Author: Paolo Molaro <lupus@ximian.com>
6 * (C) 2002 Ximian, Inc.
13 #include <mono/metadata/gc.h>
14 #include <mono/metadata/threads.h>
15 #include <mono/metadata/tabledefs.h>
16 #include <mono/metadata/exception.h>
17 #define GC_I_HIDE_POINTERS
18 #include <mono/os/gc_wrapper.h>
21 #define HIDE_POINTER(v) (v)
22 #define REVEAL_POINTER(v) (v)
25 static int finalize_slot = -1;
27 static void object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*));
30 * actually, we might want to queue the finalize requests in a separate thread,
31 * but we need to be careful about the execution domain of the thread...
34 run_finalize (void *obj, void *data)
36 MonoObject *exc = NULL;
38 o = (MonoObject*)((char*)obj + GPOINTER_TO_UINT (data));
40 if (finalize_slot < 0) {
42 for (i = 0; i < mono_defaults.object_class->vtable_size; ++i) {
43 MonoMethod *cm = mono_defaults.object_class->vtable [i];
45 if (!strcmp (cm->name, "Finalize")) {
51 /* make sure the finalizer is not called again if the object is resurrected */
52 object_register_finalizer (obj, NULL);
53 /* speedup later... and use a timeout */
54 /*g_print ("Finalize run on %p %s.%s\n", o, mono_object_class (o)->name_space, mono_object_class (o)->name);*/
55 mono_domain_set (mono_object_domain (o));
56 mono_runtime_invoke (o->vtable->klass->vtable [finalize_slot], o, NULL, &exc);
59 /* fixme: do something useful */
64 * Some of our objects may point to a different address than the address returned by GC_malloc()
65 * (because of the GetHashCode hack), but we need to pass the real address to register_finalizer.
66 * This also means that in the callback we need to adjust the pointer to get back the real
68 * We also need to be consistent in the use of the GC_debug* variants of malloc and register_finalizer,
69 * since that, too, can cause the underlying pointer to be offset.
72 object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*))
77 g_assert (GC_base (obj) == (char*)obj - offset);
78 GC_REGISTER_FINALIZER_NO_ORDER ((char*)obj - offset, callback, GUINT_TO_POINTER (offset), NULL, NULL);
83 mono_object_register_finalizer (MonoObject *obj)
85 /*g_print ("Registered finalizer on %p %s.%s\n", obj, mono_object_class (obj)->name_space, mono_object_class (obj)->name);*/
86 object_register_finalizer (obj, run_finalize);
90 * to speedup, at class init time, check if a class or struct
91 * have fields that need to be finalized and set a flag.
94 finalize_fields (MonoClass *class, char *data, gboolean instance, GHashTable *todo) {
96 MonoClassField *field;
100 g_print ("Finalize statics on on %s\n", class->name);*/
101 if (instance && class->valuetype)
102 data -= sizeof (MonoObject);
104 for (i = 0; i < class->field.count; ++i) {
105 field = &class->fields [i];
107 if (field->type->attrs & FIELD_ATTRIBUTE_STATIC)
110 if (!(field->type->attrs & FIELD_ATTRIBUTE_STATIC))
113 switch (field->type->type) {
114 case MONO_TYPE_OBJECT:
115 case MONO_TYPE_CLASS:
116 obj = *((MonoObject**)(data + field->offset));
118 if (mono_object_class (obj)->has_finalize) {
119 /* disable the registered finalizer */
120 object_register_finalizer (obj, NULL);
121 run_finalize (obj, NULL);
124 * if the type doesn't have a finalizer, we finalize
125 * the fields ourselves just like we do for structs.
126 * Disabled for now: how do we handle loops?
128 /*finalize_fields (mono_object_class (obj), obj, TRUE, todo);*/
132 case MONO_TYPE_VALUETYPE: {
133 MonoClass *fclass = mono_class_from_mono_type (field->type);
134 if (fclass->enumtype)
136 /*finalize_fields (fclass, data + field->offset, TRUE, todo);*/
139 case MONO_TYPE_ARRAY:
140 case MONO_TYPE_SZARRAY:
141 /* FIXME: foreach item... */
147 class = class->parent;
152 finalize_static_data (MonoClass *class, MonoVTable *vtable, GHashTable *todo) {
154 if (class->enumtype || !vtable->data)
156 finalize_fields (class, vtable->data, FALSE, todo);
160 mono_domain_finalize (MonoDomain *domain) {
162 GHashTable *todo = g_hash_table_new (NULL, NULL);
166 mono_g_hash_table_foreach (domain->class_vtable_hash, (GHFunc)finalize_static_data, todo);
167 /* FIXME: finalize objects in todo... */
168 g_hash_table_destroy (todo);
172 ves_icall_System_GC_InternalCollect (int generation)
182 ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection)
189 return GC_get_heap_size ();
196 ves_icall_System_GC_KeepAlive (MonoObject *obj)
206 ves_icall_System_GC_ReRegisterForFinalize (MonoObject *obj)
210 object_register_finalizer (obj, run_finalize);
214 ves_icall_System_GC_SuppressFinalize (MonoObject *obj)
218 object_register_finalizer (obj, NULL);
222 ves_icall_System_GC_WaitForPendingFinalizers (void)
227 /*static CRITICAL_SECTION handle_section;*/
228 static guint32 next_handle = 0;
229 static gpointer *gc_handles = NULL;
230 static guint8 *gc_handle_types = NULL;
231 static guint32 array_size = 0;
234 * The handle type is encoded in the lower two bits of the handle value:
248 * FIXME: make thread safe and reuse the array entries.
251 ves_icall_System_GCHandle_GetTarget (guint32 handle)
260 g_assert (type == gc_handle_types [handle >> 2]);
261 obj = gc_handles [handle >> 2];
265 if ((type == HANDLE_WEAK) || (type == HANDLE_WEAK_TRACK))
266 return REVEAL_POINTER (obj);
274 ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type)
279 guint32 h, idx = next_handle++;
281 if (idx >= array_size) {
284 guint8 *new_type_array;
287 new_array = GC_MALLOC (sizeof (gpointer) * (array_size * 2));
288 new_type_array = GC_MALLOC (sizeof (guint8) * (array_size * 2));
291 memcpy (new_array, gc_handles, sizeof (gpointer) * array_size);
292 memcpy (new_type_array, gc_handle_types, sizeof (guint8) * array_size);
293 /* need to re-register links for weak refs. test if GC_realloc needs the same */
294 for (i = 0; i < array_size; ++i) {
295 #if 0 /* This breaks the threaded finalizer, by causing segfaults deep
296 * inside libgc. I assume it will also break without the
297 * threaded finalizer, just that the stress test (bug 31333)
298 * deadlocks too early without it. Reverting to the previous
299 * version here stops the segfault.
301 if ((gc_handle_types[i] == HANDLE_WEAK) || (gc_handle_types[i] == HANDLE_WEAK_TRACK)) { /* all and only disguised pointers have it set */
303 if (((gulong)new_array [i]) & 0x1) {
305 if (gc_handles [i] != (gpointer)-1)
306 GC_unregister_disappearing_link (&(gc_handles [i]));
307 if (new_array [i] != (gpointer)-1)
308 GC_GENERAL_REGISTER_DISAPPEARING_LINK (&(new_array [i]), REVEAL_POINTER (new_array [i]));
313 gc_handles = new_array;
314 gc_handle_types = new_type_array;
316 g_error ("No GCHandle support built-in");
320 /* resuse the type from the old target */
323 h = (idx << 2) | type;
326 case HANDLE_WEAK_TRACK:
327 val = (gpointer)HIDE_POINTER (val);
328 gc_handles [idx] = val;
329 gc_handle_types [idx] = type;
331 if (gc_handles [idx] != (gpointer)-1)
332 GC_GENERAL_REGISTER_DISAPPEARING_LINK (&(gc_handles [idx]), obj);
334 g_error ("No weakref support");
338 gc_handles [idx] = val;
339 gc_handle_types [idx] = type;
346 ves_icall_System_GCHandle_FreeHandle (guint32 handle)
350 int idx = handle >> 2;
351 int type = handle & 0x3;
354 g_assert (type == gc_handle_types [idx]);
355 if ((type == HANDLE_WEAK) || (type == HANDLE_WEAK_TRACK)) {
356 if (gc_handles [idx] != (gpointer)-1)
357 GC_unregister_disappearing_link (&(gc_handles [idx]));
360 g_error ("No GCHandle support");
363 gc_handles [idx] = (gpointer)-1;
364 gc_handle_types [idx] = (guint8)-1;
368 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
373 int type = handle & 0x3;
376 obj = gc_handles [handle >> 2];
377 g_assert (gc_handle_types [handle >> 2] == type);
378 if ((type == HANDLE_WEAK) || (type == HANDLE_WEAK_TRACK)) {
379 obj = REVEAL_POINTER (obj);
380 if (obj == (MonoObject *) -1)
390 static HANDLE finalizer_event;
391 static volatile gboolean finished=FALSE;
393 static void finalize_notify (void)
396 g_message (G_GNUC_PRETTY_FUNCTION ": prodding finalizer");
399 SetEvent (finalizer_event);
402 static guint32 finalizer_thread (gpointer unused)
406 mono_new_thread_init (NULL, &stack_start);
409 /* Wait to be notified that there's at least one
412 WaitForSingleObject (finalizer_event, INFINITE);
415 g_message (G_GNUC_PRETTY_FUNCTION ": invoking finalizers");
418 GC_invoke_finalizers ();
424 void mono_gc_init (void)
428 if (getenv ("GC_DONT_GC"))
431 finalizer_event = CreateEvent (NULL, FALSE, FALSE, NULL);
432 if (finalizer_event == NULL) {
433 g_assert_not_reached ();
436 GC_finalize_on_demand = 1;
437 GC_finalizer_notifier = finalize_notify;
439 /* Don't use mono_thread_create here, because we don't want
440 * the runtime to wait for this thread to exit when it's
443 gc_thread = CreateThread (NULL, 0, finalizer_thread, NULL, 0, NULL);
444 if (gc_thread == NULL) {
445 g_assert_not_reached ();
449 void mono_gc_cleanup (void)
452 g_message (G_GNUC_PRETTY_FUNCTION ": cleaning up finalizer");
461 /* no Boehm GC support. */
462 void mono_gc_init (void)
466 void mono_gc_cleanup (void)