2002-11-28 Dietmar Maurer <dietmar@ximian.com>
[mono.git] / mono / metadata / gc.c
1 /*
2  * metadata/gc.c: GC icalls.
3  *
4  * Author: Paolo Molaro <lupus@ximian.com>
5  *
6  * (C) 2002 Ximian, Inc.
7  */
8
9 #include <config.h>
10 #include <glib.h>
11 #include <string.h>
12
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>
19
20 #ifndef HIDE_POINTER
21 #define HIDE_POINTER(v)         (v)
22 #define REVEAL_POINTER(v)       (v)
23 #endif
24
25 static int finalize_slot = -1;
26
27 static void object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*));
28
29 /* 
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...
32  */
33 static void
34 run_finalize (void *obj, void *data)
35 {
36         MonoObject *exc = NULL;
37         MonoObject *o;
38         o = (MonoObject*)((char*)obj + GPOINTER_TO_UINT (data));
39
40         if (finalize_slot < 0) {
41                 int i;
42                 for (i = 0; i < mono_defaults.object_class->vtable_size; ++i) {
43                         MonoMethod *cm = mono_defaults.object_class->vtable [i];
44                
45                         if (!strcmp (cm->name, "Finalize")) {
46                                 finalize_slot = i;
47                                 break;
48                         }
49                 }
50         }
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);
57
58         if (exc) {
59                 /* fixme: do something useful */
60         }
61 }
62
63 /*
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
67  * MonoObject*.
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.
70  */
71 static void
72 object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*))
73 {
74 #if HAVE_BOEHM_GC
75         guint offset = 0;
76
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);
79 #endif
80 }
81
82 void
83 mono_object_register_finalizer (MonoObject *obj)
84 {
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);
87 }
88
89 /* 
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.
92  */
93 static void
94 finalize_fields (MonoClass *class, char *data, gboolean instance, GHashTable *todo) {
95         int i;
96         MonoClassField *field;
97         MonoObject *obj;
98
99         /*if (!instance)
100                 g_print ("Finalize statics on on %s\n", class->name);*/
101         if (instance && class->valuetype)
102                 data -= sizeof (MonoObject);
103         do {
104                 for (i = 0; i < class->field.count; ++i) {
105                         field = &class->fields [i];
106                         if (instance) {
107                                 if (field->type->attrs & FIELD_ATTRIBUTE_STATIC)
108                                         continue;
109                         } else {
110                                 if (!(field->type->attrs & FIELD_ATTRIBUTE_STATIC))
111                                         continue;
112                         }
113                         switch (field->type->type) {
114                         case MONO_TYPE_OBJECT:
115                         case MONO_TYPE_CLASS:
116                                 obj = *((MonoObject**)(data + field->offset));
117                                 if (obj) {
118                                         if (mono_object_class (obj)->has_finalize) {
119                                                 /* disable the registered finalizer */
120                                                 object_register_finalizer (obj, NULL);
121                                                 run_finalize (obj, NULL);
122                                         } else {
123                                                 /* 
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?
127                                                  */
128                                                 /*finalize_fields (mono_object_class (obj), obj, TRUE, todo);*/
129                                         }
130                                 }
131                                 break;
132                         case MONO_TYPE_VALUETYPE: {
133                                 MonoClass *fclass = mono_class_from_mono_type (field->type);
134                                 if (fclass->enumtype)
135                                         continue;
136                                 /*finalize_fields (fclass, data + field->offset, TRUE, todo);*/
137                                 break;
138                         }
139                         case MONO_TYPE_ARRAY:
140                         case MONO_TYPE_SZARRAY:
141                                 /* FIXME: foreach item... */
142                                 break;
143                         }
144                 }
145                 if (!instance)
146                         return;
147                 class = class->parent;
148         } while (class);
149 }
150
151 static void
152 finalize_static_data (MonoClass *class, MonoVTable *vtable, GHashTable *todo) {
153
154         if (class->enumtype || !vtable->data)
155                 return;
156         finalize_fields (class, vtable->data, FALSE, todo);
157 }
158
159 void
160 mono_domain_finalize (MonoDomain *domain) {
161
162         GHashTable *todo = g_hash_table_new (NULL, NULL);
163 #if HAVE_BOEHM_GC
164         GC_gcollect ();
165 #endif
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);
169 }
170
171 void
172 ves_icall_System_GC_InternalCollect (int generation)
173 {
174         MONO_ARCH_SAVE_REGS;
175
176 #if HAVE_BOEHM_GC
177         GC_gcollect ();
178 #endif
179 }
180
181 gint64
182 ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection)
183 {
184         MONO_ARCH_SAVE_REGS;
185
186 #if HAVE_BOEHM_GC
187         if (forceCollection)
188                 GC_gcollect ();
189         return GC_get_heap_size ();
190 #else
191         return 0;
192 #endif
193 }
194
195 void
196 ves_icall_System_GC_KeepAlive (MonoObject *obj)
197 {
198         MONO_ARCH_SAVE_REGS;
199
200         /*
201          * Does nothing.
202          */
203 }
204
205 void
206 ves_icall_System_GC_ReRegisterForFinalize (MonoObject *obj)
207 {
208         MONO_ARCH_SAVE_REGS;
209
210         object_register_finalizer (obj, run_finalize);
211 }
212
213 void
214 ves_icall_System_GC_SuppressFinalize (MonoObject *obj)
215 {
216         MONO_ARCH_SAVE_REGS;
217
218         object_register_finalizer (obj, NULL);
219 }
220
221 void
222 ves_icall_System_GC_WaitForPendingFinalizers (void)
223 {
224         MONO_ARCH_SAVE_REGS;
225 }
226
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;
232
233 /*
234  * The handle type is encoded in the lower two bits of the handle value:
235  * 0 -> normal
236  * 1 -> pinned
237  * 2 -> weak
238  */
239
240 typedef enum {
241         HANDLE_WEAK,
242         HANDLE_WEAK_TRACK,
243         HANDLE_NORMAL,
244         HANDLE_PINNED
245 } HandleType;
246
247 /*
248  * FIXME: make thread safe and reuse the array entries.
249  */
250 MonoObject *
251 ves_icall_System_GCHandle_GetTarget (guint32 handle)
252 {
253         MONO_ARCH_SAVE_REGS;
254
255         MonoObject *obj;
256         gint32 type;
257
258         if (gc_handles) {
259                 type = handle & 0x3;
260                 g_assert (type == gc_handle_types [handle >> 2]);
261                 obj = gc_handles [handle >> 2];
262                 if (!obj)
263                         return NULL;
264
265                 if ((type == HANDLE_WEAK) || (type == HANDLE_WEAK_TRACK))
266                         return REVEAL_POINTER (obj);
267                 else
268                 return obj;
269         }
270         return NULL;
271 }
272
273 guint32
274 ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type)
275 {
276         MONO_ARCH_SAVE_REGS;
277
278         gpointer val = obj;
279         guint32 h, idx = next_handle++;
280
281         if (idx >= array_size) {
282 #if HAVE_BOEHM_GC
283                 gpointer *new_array;
284                 guint8 *new_type_array;
285                 if (!array_size)
286                         array_size = 16;
287                 new_array = GC_MALLOC (sizeof (gpointer) * (array_size * 2));
288                 new_type_array = GC_MALLOC (sizeof (guint8) * (array_size * 2));
289                 if (gc_handles) {
290                         int i;
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.
300        */
301                                 if ((gc_handle_types[i] == HANDLE_WEAK) || (gc_handle_types[i] == HANDLE_WEAK_TRACK)) { /* all and only disguised pointers have it set */
302 #else
303                                 if (((gulong)new_array [i]) & 0x1) {
304 #endif
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]));
309                                 }
310                         }
311                 }
312                 array_size *= 2;
313                 gc_handles = new_array;
314                 gc_handle_types = new_type_array;
315 #else
316                 g_error ("No GCHandle support built-in");
317 #endif
318         }
319
320         /* resuse the type from the old target */
321         if (type == -1)
322                 type =  handle & 0x3;
323         h = (idx << 2) | type;
324         switch (type) {
325         case HANDLE_WEAK:
326         case HANDLE_WEAK_TRACK:
327                 val = (gpointer)HIDE_POINTER (val);
328                 gc_handles [idx] = val;
329                 gc_handle_types [idx] = type;
330 #if HAVE_BOEHM_GC
331                 if (gc_handles [idx] != (gpointer)-1)
332                         GC_GENERAL_REGISTER_DISAPPEARING_LINK (&(gc_handles [idx]), obj);
333 #else
334                 g_error ("No weakref support");
335 #endif
336                 break;
337         default:
338                 gc_handles [idx] = val;
339                 gc_handle_types [idx] = type;
340                 break;
341         }
342         return h;
343 }
344
345 void
346 ves_icall_System_GCHandle_FreeHandle (guint32 handle)
347 {
348         MONO_ARCH_SAVE_REGS;
349
350         int idx = handle >> 2;
351         int type = handle & 0x3;
352
353 #ifdef HAVE_BOEHM_GC
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]));
358         }
359 #else
360         g_error ("No GCHandle support");
361 #endif
362
363         gc_handles [idx] = (gpointer)-1;
364         gc_handle_types [idx] = (guint8)-1;
365 }
366
367 gpointer
368 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
369 {
370         MONO_ARCH_SAVE_REGS;
371
372         MonoObject *obj;
373         int type = handle & 0x3;
374
375         if (gc_handles) {
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)
381                                 return NULL;
382                 }
383                 return obj;
384         }
385         return NULL;
386 }
387
388 #if HAVE_BOEHM_GC
389
390 static HANDLE finalizer_event;
391 static volatile gboolean finished=FALSE;
392
393 static void finalize_notify (void)
394 {
395 #ifdef DEBUG
396         g_message (G_GNUC_PRETTY_FUNCTION ": prodding finalizer");
397 #endif
398
399         SetEvent (finalizer_event);
400 }
401
402 static guint32 finalizer_thread (gpointer unused)
403 {
404         guint32 stack_start;
405         
406         mono_new_thread_init (NULL, &stack_start);
407         
408         while(!finished) {
409                 /* Wait to be notified that there's at least one
410                  * finaliser to run
411                  */
412                 WaitForSingleObject (finalizer_event, INFINITE);
413
414 #ifdef DEBUG
415                 g_message (G_GNUC_PRETTY_FUNCTION ": invoking finalizers");
416 #endif
417
418                 GC_invoke_finalizers ();
419         }
420         
421         return(0);
422 }
423
424 void mono_gc_init (void)
425 {
426         HANDLE gc_thread;
427
428         if (getenv ("GC_DONT_GC"))
429                 return;
430         
431         finalizer_event = CreateEvent (NULL, FALSE, FALSE, NULL);
432         if (finalizer_event == NULL) {
433                 g_assert_not_reached ();
434         }
435         
436         GC_finalize_on_demand = 1;
437         GC_finalizer_notifier = finalize_notify;
438         
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
441          * cleaning up.
442          */
443         gc_thread = CreateThread (NULL, 0, finalizer_thread, NULL, 0, NULL);
444         if (gc_thread == NULL) {
445                 g_assert_not_reached ();
446         }
447 }
448
449 void mono_gc_cleanup (void)
450 {
451 #ifdef DEBUG
452         g_message (G_GNUC_PRETTY_FUNCTION ": cleaning up finalizer");
453 #endif
454
455         finished = TRUE;
456         finalize_notify ();
457 }
458
459 #else
460
461 /* no Boehm GC support. */
462 void mono_gc_init (void)
463 {
464 }
465
466 void mono_gc_cleanup (void)
467 {
468 }
469
470 #endif
471