2003-01-22 Martin Baulig <martin@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-internal.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
52         /* make sure the finalizer is not called again if the object is resurrected */
53         object_register_finalizer (obj, NULL);
54         /* speedup later... and use a timeout */
55         /*g_print ("Finalize run on %p %s.%s\n", o, mono_object_class (o)->name_space, mono_object_class (o)->name);*/
56         mono_domain_set (mono_object_domain (o));
57
58         mono_runtime_invoke (o->vtable->klass->vtable [finalize_slot], o, NULL, &exc);
59
60         if (exc) {
61                 /* fixme: do something useful */
62         }
63 }
64
65 /*
66  * Some of our objects may point to a different address than the address returned by GC_malloc()
67  * (because of the GetHashCode hack), but we need to pass the real address to register_finalizer.
68  * This also means that in the callback we need to adjust the pointer to get back the real
69  * MonoObject*.
70  * We also need to be consistent in the use of the GC_debug* variants of malloc and register_finalizer, 
71  * since that, too, can cause the underlying pointer to be offset.
72  */
73 static void
74 object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*))
75 {
76 #if HAVE_BOEHM_GC
77         guint offset = 0;
78
79         g_assert (GC_base (obj) == (char*)obj - offset);
80         GC_REGISTER_FINALIZER_NO_ORDER ((char*)obj - offset, callback, GUINT_TO_POINTER (offset), NULL, NULL);
81 #endif
82 }
83
84 void
85 mono_object_register_finalizer (MonoObject *obj)
86 {
87         /*g_print ("Registered finalizer on %p %s.%s\n", obj, mono_object_class (obj)->name_space, mono_object_class (obj)->name);*/
88         object_register_finalizer (obj, run_finalize);
89 }
90
91 /* 
92  * to speedup, at class init time, check if a class or struct
93  * have fields that need to be finalized and set a flag.
94  */
95 static void
96 finalize_fields (MonoClass *class, char *data, gboolean instance, GHashTable *todo) {
97         int i;
98         MonoClassField *field;
99         MonoObject *obj;
100
101         /*if (!instance)
102                 g_print ("Finalize statics on on %s\n", class->name);*/
103         if (instance && class->valuetype)
104                 data -= sizeof (MonoObject);
105         do {
106                 for (i = 0; i < class->field.count; ++i) {
107                         field = &class->fields [i];
108                         if (instance) {
109                                 if (field->type->attrs & FIELD_ATTRIBUTE_STATIC)
110                                         continue;
111                         } else {
112                                 if (!(field->type->attrs & FIELD_ATTRIBUTE_STATIC))
113                                         continue;
114                         }
115                         switch (field->type->type) {
116                         case MONO_TYPE_OBJECT:
117                         case MONO_TYPE_CLASS:
118                                 obj = *((MonoObject**)(data + field->offset));
119                                 if (obj) {
120                                         if (mono_object_class (obj)->has_finalize) {
121                                                 /* disable the registered finalizer */
122                                                 object_register_finalizer (obj, NULL);
123                                                 run_finalize (obj, NULL);
124                                         } else {
125                                                 /* 
126                                                  * if the type doesn't have a finalizer, we finalize 
127                                                  * the fields ourselves just like we do for structs.
128                                                  * Disabled for now: how do we handle loops?
129                                                  */
130                                                 /*finalize_fields (mono_object_class (obj), obj, TRUE, todo);*/
131                                         }
132                                 }
133                                 break;
134                         case MONO_TYPE_VALUETYPE: {
135                                 MonoClass *fclass = mono_class_from_mono_type (field->type);
136                                 if (fclass->enumtype)
137                                         continue;
138                                 /*finalize_fields (fclass, data + field->offset, TRUE, todo);*/
139                                 break;
140                         }
141                         case MONO_TYPE_ARRAY:
142                         case MONO_TYPE_SZARRAY:
143                                 /* FIXME: foreach item... */
144                                 break;
145                         }
146                 }
147                 if (!instance)
148                         return;
149                 class = class->parent;
150         } while (class);
151 }
152
153 static void
154 finalize_static_data (MonoClass *class, MonoVTable *vtable, GHashTable *todo) {
155
156         if (class->enumtype || !vtable->data)
157                 return;
158         finalize_fields (class, vtable->data, FALSE, todo);
159 }
160
161 void
162 mono_domain_finalize (MonoDomain *domain) {
163
164         GHashTable *todo = g_hash_table_new (NULL, NULL);
165 #if HAVE_BOEHM_GC
166         GC_gcollect ();
167 #endif
168         mono_g_hash_table_foreach (domain->class_vtable_hash, (GHFunc)finalize_static_data, todo);
169         /* FIXME: finalize objects in todo... */
170         g_hash_table_destroy (todo);
171 }
172
173 void
174 ves_icall_System_GC_InternalCollect (int generation)
175 {
176         MONO_ARCH_SAVE_REGS;
177
178 #if HAVE_BOEHM_GC
179         GC_gcollect ();
180 #endif
181 }
182
183 gint64
184 ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection)
185 {
186         MONO_ARCH_SAVE_REGS;
187
188 #if HAVE_BOEHM_GC
189         if (forceCollection)
190                 GC_gcollect ();
191         return GC_get_heap_size ();
192 #else
193         return 0;
194 #endif
195 }
196
197 void
198 ves_icall_System_GC_KeepAlive (MonoObject *obj)
199 {
200         MONO_ARCH_SAVE_REGS;
201
202         /*
203          * Does nothing.
204          */
205 }
206
207 void
208 ves_icall_System_GC_ReRegisterForFinalize (MonoObject *obj)
209 {
210         MONO_ARCH_SAVE_REGS;
211
212         object_register_finalizer (obj, run_finalize);
213 }
214
215 void
216 ves_icall_System_GC_SuppressFinalize (MonoObject *obj)
217 {
218         MONO_ARCH_SAVE_REGS;
219
220         object_register_finalizer (obj, NULL);
221 }
222
223 void
224 ves_icall_System_GC_WaitForPendingFinalizers (void)
225 {
226         MONO_ARCH_SAVE_REGS;
227 }
228
229 static CRITICAL_SECTION handle_section;
230 static guint32 next_handle = 0;
231 static gpointer *gc_handles = NULL;
232 static guint8 *gc_handle_types = NULL;
233 static guint32 array_size = 0;
234
235 /*
236  * The handle type is encoded in the lower two bits of the handle value:
237  * 0 -> normal
238  * 1 -> pinned
239  * 2 -> weak
240  */
241
242 typedef enum {
243         HANDLE_WEAK,
244         HANDLE_WEAK_TRACK,
245         HANDLE_NORMAL,
246         HANDLE_PINNED
247 } HandleType;
248
249 /*
250  * FIXME: make thread safe and reuse the array entries.
251  */
252 MonoObject *
253 ves_icall_System_GCHandle_GetTarget (guint32 handle)
254 {
255         MonoObject *obj;
256         gint32 type;
257
258         MONO_ARCH_SAVE_REGS;
259
260         if (gc_handles) {
261                 type = handle & 0x3;
262                 EnterCriticalSection (&handle_section);
263                 g_assert (type == gc_handle_types [handle >> 2]);
264                 obj = gc_handles [handle >> 2];
265                 LeaveCriticalSection (&handle_section);
266                 if (!obj)
267                         return NULL;
268
269                 if ((type == HANDLE_WEAK) || (type == HANDLE_WEAK_TRACK))
270                         return REVEAL_POINTER (obj);
271                 else
272                         return obj;
273         }
274         return NULL;
275 }
276
277 guint32
278 ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type)
279 {
280         gpointer val = obj;
281         guint32 h, idx;
282
283         MONO_ARCH_SAVE_REGS;
284
285         EnterCriticalSection (&handle_section);
286         idx = next_handle++;
287         if (idx >= array_size) {
288 #if HAVE_BOEHM_GC
289                 gpointer *new_array;
290                 guint8 *new_type_array;
291                 if (!array_size)
292                         array_size = 16;
293                 new_array = GC_MALLOC (sizeof (gpointer) * (array_size * 2));
294                 new_type_array = GC_MALLOC (sizeof (guint8) * (array_size * 2));
295                 if (gc_handles) {
296                         int i;
297                         memcpy (new_array, gc_handles, sizeof (gpointer) * array_size);
298                         memcpy (new_type_array, gc_handle_types, sizeof (guint8) * array_size);
299                         /* need to re-register links for weak refs. test if GC_realloc needs the same */
300                         for (i = 0; i < array_size; ++i) {
301 #if 0 /* This breaks the threaded finalizer, by causing segfaults deep
302        * inside libgc.  I assume it will also break without the
303        * threaded finalizer, just that the stress test (bug 31333)
304        * deadlocks too early without it.  Reverting to the previous
305        * version here stops the segfault.
306        */
307                                 if ((gc_handle_types[i] == HANDLE_WEAK) || (gc_handle_types[i] == HANDLE_WEAK_TRACK)) { /* all and only disguised pointers have it set */
308 #else
309                                 if (((gulong)new_array [i]) & 0x1) {
310 #endif
311                                         if (gc_handles [i] != (gpointer)-1)
312                                                 GC_unregister_disappearing_link (&(gc_handles [i]));
313                                         if (new_array [i] != (gpointer)-1)
314                                                 GC_GENERAL_REGISTER_DISAPPEARING_LINK (&(new_array [i]), REVEAL_POINTER (new_array [i]));
315                                 }
316                         }
317                 }
318                 array_size *= 2;
319                 gc_handles = new_array;
320                 gc_handle_types = new_type_array;
321 #else
322                 mono_raise_exception (mono_get_exception_execution_engine ("No GCHandle support built-in"));
323 #endif
324         }
325
326         /* resuse the type from the old target */
327         if (type == -1)
328                 type =  handle & 0x3;
329         h = (idx << 2) | type;
330         switch (type) {
331         case HANDLE_WEAK:
332         case HANDLE_WEAK_TRACK:
333                 val = (gpointer)HIDE_POINTER (val);
334                 gc_handles [idx] = val;
335                 gc_handle_types [idx] = type;
336 #if HAVE_BOEHM_GC
337                 if (gc_handles [idx] != (gpointer)-1)
338                         GC_GENERAL_REGISTER_DISAPPEARING_LINK (&(gc_handles [idx]), obj);
339 #else
340                 mono_raise_exception (mono_get_exception_execution_engine ("No weakref support"));
341 #endif
342                 break;
343         default:
344                 gc_handles [idx] = val;
345                 gc_handle_types [idx] = type;
346                 break;
347         }
348         LeaveCriticalSection (&handle_section);
349         return h;
350 }
351
352 void
353 ves_icall_System_GCHandle_FreeHandle (guint32 handle)
354 {
355         int idx = handle >> 2;
356         int type = handle & 0x3;
357
358         MONO_ARCH_SAVE_REGS;
359
360         EnterCriticalSection (&handle_section);
361
362 #ifdef HAVE_BOEHM_GC
363         g_assert (type == gc_handle_types [idx]);
364         if ((type == HANDLE_WEAK) || (type == HANDLE_WEAK_TRACK)) {
365                 if (gc_handles [idx] != (gpointer)-1)
366                         GC_unregister_disappearing_link (&(gc_handles [idx]));
367         }
368 #else
369         mono_raise_exception (mono_get_exception_execution_engine ("No GCHandle support"));
370 #endif
371
372         gc_handles [idx] = (gpointer)-1;
373         gc_handle_types [idx] = (guint8)-1;
374         LeaveCriticalSection (&handle_section);
375 }
376
377 gpointer
378 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
379 {
380         MonoObject *obj;
381         int type = handle & 0x3;
382
383         MONO_ARCH_SAVE_REGS;
384
385         if (gc_handles) {
386                 EnterCriticalSection (&handle_section);
387                 obj = gc_handles [handle >> 2];
388                 g_assert (gc_handle_types [handle >> 2] == type);
389                 LeaveCriticalSection (&handle_section);
390                 if ((type == HANDLE_WEAK) || (type == HANDLE_WEAK_TRACK)) {
391                         obj = REVEAL_POINTER (obj);
392                         if (obj == (MonoObject *) -1)
393                                 return NULL;
394                 }
395                 return obj;
396         }
397         return NULL;
398 }
399
400 #if HAVE_BOEHM_GC
401
402 static HANDLE finalizer_event;
403 static volatile gboolean finished=FALSE;
404
405 static void finalize_notify (void)
406 {
407 #ifdef DEBUG
408         g_message (G_GNUC_PRETTY_FUNCTION ": prodding finalizer");
409 #endif
410
411         SetEvent (finalizer_event);
412 }
413
414 static guint32 finalizer_thread (gpointer unused)
415 {
416         guint32 stack_start;
417         
418         mono_new_thread_init (NULL, &stack_start, NULL);
419         
420         while(!finished) {
421                 /* Wait to be notified that there's at least one
422                  * finaliser to run
423                  */
424                 WaitForSingleObject (finalizer_event, INFINITE);
425
426 #ifdef DEBUG
427                 g_message (G_GNUC_PRETTY_FUNCTION ": invoking finalizers");
428 #endif
429
430                 GC_invoke_finalizers ();
431         }
432         
433         return(0);
434 }
435
436 /* 
437  * Enable or disable the separate finalizer thread.
438  * It's currently disabled because it still requires some
439  * work in the rest of the runtime.
440  */
441 #undef ENABLE_FINALIZER_THREAD
442
443 void mono_gc_init (void)
444 {
445         HANDLE gc_thread;
446
447         InitializeCriticalSection (&handle_section);
448
449 #ifdef ENABLE_FINALIZER_THREAD
450
451         if (getenv ("GC_DONT_GC"))
452                 return;
453         
454         finalizer_event = CreateEvent (NULL, FALSE, FALSE, NULL);
455         if (finalizer_event == NULL) {
456                 g_assert_not_reached ();
457         }
458
459         GC_finalize_on_demand = 1;
460         GC_finalizer_notifier = finalize_notify;
461         
462         /* Don't use mono_thread_create here, because we don't want
463          * the runtime to wait for this thread to exit when it's
464          * cleaning up.
465          */
466         gc_thread = CreateThread (NULL, 0, finalizer_thread, NULL, 0, NULL);
467         if (gc_thread == NULL) {
468                 g_assert_not_reached ();
469         }
470 #endif
471 }
472
473 void mono_gc_cleanup (void)
474 {
475 #ifdef DEBUG
476         g_message (G_GNUC_PRETTY_FUNCTION ": cleaning up finalizer");
477 #endif
478
479 #ifdef ENABLE_FINALIZER_THREAD
480         finished = TRUE;
481         finalize_notify ();
482 #endif
483 }
484
485 #else
486
487 /* no Boehm GC support. */
488 void mono_gc_init (void)
489 {
490         InitializeCriticalSection (&handle_section);
491 }
492
493 void mono_gc_cleanup (void)
494 {
495 }
496
497 #endif
498