2003-04-03 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 static guint32
162 internal_domain_finalize (gpointer data) {
163         MonoDomain *domain=(MonoDomain *)data;
164         GHashTable *todo = g_hash_table_new (NULL, NULL);
165
166         mono_thread_new_init (GetCurrentThreadId (), todo, NULL);
167         
168 #if HAVE_BOEHM_GC
169         GC_gcollect ();
170 #endif
171         mono_g_hash_table_foreach (domain->class_vtable_hash, (GHFunc)finalize_static_data, todo);
172         /* FIXME: finalize objects in todo... */
173         g_hash_table_destroy (todo);
174
175         return(0);
176 }
177
178 void
179 mono_domain_finalize (MonoDomain *domain) 
180 {
181         HANDLE finalize_thread;
182         
183         /* Need to run managed code in a subthread.
184          * Mono_domain_finalize() is called from the main thread in
185          * the jit and the embedded example, hence the thread creation
186          * here.
187          */
188         finalize_thread=CreateThread (NULL, 0, internal_domain_finalize, domain, 0, NULL);
189         if(finalize_thread==NULL) {
190                 g_assert_not_reached ();
191         }
192         WaitForSingleObject (finalize_thread, INFINITE);
193         CloseHandle (finalize_thread);
194 }
195
196 void
197 ves_icall_System_GC_InternalCollect (int generation)
198 {
199         MONO_ARCH_SAVE_REGS;
200
201 #if HAVE_BOEHM_GC
202         GC_gcollect ();
203 #endif
204 }
205
206 gint64
207 ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection)
208 {
209         MONO_ARCH_SAVE_REGS;
210
211 #if HAVE_BOEHM_GC
212         if (forceCollection)
213                 GC_gcollect ();
214         return GC_get_heap_size ();
215 #else
216         return 0;
217 #endif
218 }
219
220 void
221 ves_icall_System_GC_KeepAlive (MonoObject *obj)
222 {
223         MONO_ARCH_SAVE_REGS;
224
225         /*
226          * Does nothing.
227          */
228 }
229
230 void
231 ves_icall_System_GC_ReRegisterForFinalize (MonoObject *obj)
232 {
233         MONO_ARCH_SAVE_REGS;
234
235         object_register_finalizer (obj, run_finalize);
236 }
237
238 void
239 ves_icall_System_GC_SuppressFinalize (MonoObject *obj)
240 {
241         MONO_ARCH_SAVE_REGS;
242
243         object_register_finalizer (obj, NULL);
244 }
245
246 void
247 ves_icall_System_GC_WaitForPendingFinalizers (void)
248 {
249         MONO_ARCH_SAVE_REGS;
250 }
251
252 static CRITICAL_SECTION allocator_section;
253 static CRITICAL_SECTION handle_section;
254 static guint32 next_handle = 0;
255 static gpointer *gc_handles = NULL;
256 static guint8 *gc_handle_types = NULL;
257 static guint32 array_size = 0;
258
259 /*
260  * The handle type is encoded in the lower two bits of the handle value:
261  * 0 -> normal
262  * 1 -> pinned
263  * 2 -> weak
264  */
265
266 typedef enum {
267         HANDLE_WEAK,
268         HANDLE_WEAK_TRACK,
269         HANDLE_NORMAL,
270         HANDLE_PINNED
271 } HandleType;
272
273 /*
274  * FIXME: make thread safe and reuse the array entries.
275  */
276 MonoObject *
277 ves_icall_System_GCHandle_GetTarget (guint32 handle)
278 {
279         MonoObject *obj;
280         gint32 type;
281
282         MONO_ARCH_SAVE_REGS;
283
284         if (gc_handles) {
285                 type = handle & 0x3;
286                 EnterCriticalSection (&handle_section);
287                 g_assert (type == gc_handle_types [handle >> 2]);
288                 obj = gc_handles [handle >> 2];
289                 LeaveCriticalSection (&handle_section);
290                 if (!obj)
291                         return NULL;
292
293                 if ((type == HANDLE_WEAK) || (type == HANDLE_WEAK_TRACK))
294                         return REVEAL_POINTER (obj);
295                 else
296                         return obj;
297         }
298         return NULL;
299 }
300
301 guint32
302 ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type)
303 {
304         gpointer val = obj;
305         guint32 h, idx;
306
307         MONO_ARCH_SAVE_REGS;
308
309         EnterCriticalSection (&handle_section);
310         idx = next_handle++;
311         if (idx >= array_size) {
312 #if HAVE_BOEHM_GC
313                 gpointer *new_array;
314                 guint8 *new_type_array;
315                 if (!array_size)
316                         array_size = 16;
317                 new_array = GC_MALLOC (sizeof (gpointer) * (array_size * 2));
318                 new_type_array = GC_MALLOC (sizeof (guint8) * (array_size * 2));
319                 if (gc_handles) {
320                         int i;
321                         memcpy (new_array, gc_handles, sizeof (gpointer) * array_size);
322                         memcpy (new_type_array, gc_handle_types, sizeof (guint8) * array_size);
323                         /* need to re-register links for weak refs. test if GC_realloc needs the same */
324                         for (i = 0; i < array_size; ++i) {
325 #if 0 /* This breaks the threaded finalizer, by causing segfaults deep
326        * inside libgc.  I assume it will also break without the
327        * threaded finalizer, just that the stress test (bug 31333)
328        * deadlocks too early without it.  Reverting to the previous
329        * version here stops the segfault.
330        */
331                                 if ((gc_handle_types[i] == HANDLE_WEAK) || (gc_handle_types[i] == HANDLE_WEAK_TRACK)) { /* all and only disguised pointers have it set */
332 #else
333                                 if (((gulong)new_array [i]) & 0x1) {
334 #endif
335                                         if (gc_handles [i] != (gpointer)-1)
336                                                 GC_unregister_disappearing_link (&(gc_handles [i]));
337                                         if (new_array [i] != (gpointer)-1)
338                                                 GC_GENERAL_REGISTER_DISAPPEARING_LINK (&(new_array [i]), REVEAL_POINTER (new_array [i]));
339                                 }
340                         }
341                 }
342                 array_size *= 2;
343                 gc_handles = new_array;
344                 gc_handle_types = new_type_array;
345 #else
346                 mono_raise_exception (mono_get_exception_execution_engine ("No GCHandle support built-in"));
347 #endif
348         }
349
350         /* resuse the type from the old target */
351         if (type == -1)
352                 type =  handle & 0x3;
353         h = (idx << 2) | type;
354         switch (type) {
355         case HANDLE_WEAK:
356         case HANDLE_WEAK_TRACK:
357                 val = (gpointer)HIDE_POINTER (val);
358                 gc_handles [idx] = val;
359                 gc_handle_types [idx] = type;
360 #if HAVE_BOEHM_GC
361                 if (gc_handles [idx] != (gpointer)-1)
362                         GC_GENERAL_REGISTER_DISAPPEARING_LINK (&(gc_handles [idx]), obj);
363 #else
364                 mono_raise_exception (mono_get_exception_execution_engine ("No weakref support"));
365 #endif
366                 break;
367         default:
368                 gc_handles [idx] = val;
369                 gc_handle_types [idx] = type;
370                 break;
371         }
372         LeaveCriticalSection (&handle_section);
373         return h;
374 }
375
376 void
377 ves_icall_System_GCHandle_FreeHandle (guint32 handle)
378 {
379         int idx = handle >> 2;
380         int type = handle & 0x3;
381
382         MONO_ARCH_SAVE_REGS;
383
384         EnterCriticalSection (&handle_section);
385
386 #ifdef HAVE_BOEHM_GC
387         g_assert (type == gc_handle_types [idx]);
388         if ((type == HANDLE_WEAK) || (type == HANDLE_WEAK_TRACK)) {
389                 if (gc_handles [idx] != (gpointer)-1)
390                         GC_unregister_disappearing_link (&(gc_handles [idx]));
391         }
392 #else
393         mono_raise_exception (mono_get_exception_execution_engine ("No GCHandle support"));
394 #endif
395
396         gc_handles [idx] = (gpointer)-1;
397         gc_handle_types [idx] = (guint8)-1;
398         LeaveCriticalSection (&handle_section);
399 }
400
401 gpointer
402 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
403 {
404         MonoObject *obj;
405         int type = handle & 0x3;
406
407         MONO_ARCH_SAVE_REGS;
408
409         if (gc_handles) {
410                 EnterCriticalSection (&handle_section);
411                 obj = gc_handles [handle >> 2];
412                 g_assert (gc_handle_types [handle >> 2] == type);
413                 LeaveCriticalSection (&handle_section);
414                 if ((type == HANDLE_WEAK) || (type == HANDLE_WEAK_TRACK)) {
415                         obj = REVEAL_POINTER (obj);
416                         if (obj == (MonoObject *) -1)
417                                 return NULL;
418                 }
419                 return obj;
420         }
421         return NULL;
422 }
423
424 #if HAVE_BOEHM_GC
425
426 static HANDLE finalizer_event;
427 static volatile gboolean finished=FALSE;
428
429 static void finalize_notify (void)
430 {
431 #ifdef DEBUG
432         g_message (G_GNUC_PRETTY_FUNCTION ": prodding finalizer");
433 #endif
434
435         SetEvent (finalizer_event);
436 }
437
438 static guint32 finalizer_thread (gpointer unused)
439 {
440         guint32 stack_start;
441         
442         mono_thread_new_init (GetCurrentThreadId (), &stack_start, NULL);
443         
444         while(!finished) {
445                 /* Wait to be notified that there's at least one
446                  * finaliser to run
447                  */
448                 WaitForSingleObject (finalizer_event, INFINITE);
449
450 #ifdef DEBUG
451                 g_message (G_GNUC_PRETTY_FUNCTION ": invoking finalizers");
452 #endif
453
454                 GC_invoke_finalizers ();
455         }
456         
457         return(0);
458 }
459
460 /* 
461  * Enable or disable the separate finalizer thread.
462  * It's currently disabled because it still requires some
463  * work in the rest of the runtime.
464  */
465 #define ENABLE_FINALIZER_THREAD
466
467 #ifdef WITH_INCLUDED_LIBGC
468 /* from threads.c */
469 extern void mono_gc_stop_world (void);
470 extern void mono_gc_start_world (void);
471 extern void mono_gc_push_all_stacks (void);
472
473 static void mono_gc_lock (void)
474 {
475         EnterCriticalSection (&allocator_section);
476 }
477
478 static void mono_gc_unlock (void)
479 {
480         LeaveCriticalSection (&allocator_section);
481 }
482
483 static GCThreadFunctions mono_gc_thread_vtable = {
484         NULL,
485
486         mono_gc_lock,
487         mono_gc_unlock,
488
489         mono_gc_stop_world,
490         NULL,
491         mono_gc_push_all_stacks,
492         mono_gc_start_world
493 };
494 #endif /* WITH_INCLUDED_LIBGC */
495
496 void mono_gc_init (void)
497 {
498         HANDLE gc_thread;
499
500         InitializeCriticalSection (&handle_section);
501         InitializeCriticalSection (&allocator_section);
502
503 #ifdef WITH_INCLUDED_LIBGC
504         gc_thread_vtable = &mono_gc_thread_vtable;
505 #endif
506
507 #ifdef ENABLE_FINALIZER_THREAD
508
509         if (getenv ("GC_DONT_GC"))
510                 return;
511         
512         finalizer_event = CreateEvent (NULL, FALSE, FALSE, NULL);
513         if (finalizer_event == NULL) {
514                 g_assert_not_reached ();
515         }
516
517         GC_finalize_on_demand = 1;
518         GC_finalizer_notifier = finalize_notify;
519         
520         /* Don't use mono_thread_create here, because we don't want
521          * the runtime to wait for this thread to exit when it's
522          * cleaning up.
523          */
524         gc_thread = CreateThread (NULL, 0, finalizer_thread, NULL, 0, NULL);
525         if (gc_thread == NULL) {
526                 g_assert_not_reached ();
527         }
528 #endif
529 }
530
531 void mono_gc_cleanup (void)
532 {
533 #ifdef DEBUG
534         g_message (G_GNUC_PRETTY_FUNCTION ": cleaning up finalizer");
535 #endif
536
537 #ifdef ENABLE_FINALIZER_THREAD
538         finished = TRUE;
539         finalize_notify ();
540 #endif
541 }
542
543 #else
544
545 /* no Boehm GC support. */
546 void mono_gc_init (void)
547 {
548         InitializeCriticalSection (&handle_section);
549 }
550
551 void mono_gc_cleanup (void)
552 {
553 }
554
555 #endif
556