2003-01-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-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_new_thread_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 handle_section;
253 static guint32 next_handle = 0;
254 static gpointer *gc_handles = NULL;
255 static guint8 *gc_handle_types = NULL;
256 static guint32 array_size = 0;
257
258 /*
259  * The handle type is encoded in the lower two bits of the handle value:
260  * 0 -> normal
261  * 1 -> pinned
262  * 2 -> weak
263  */
264
265 typedef enum {
266         HANDLE_WEAK,
267         HANDLE_WEAK_TRACK,
268         HANDLE_NORMAL,
269         HANDLE_PINNED
270 } HandleType;
271
272 /*
273  * FIXME: make thread safe and reuse the array entries.
274  */
275 MonoObject *
276 ves_icall_System_GCHandle_GetTarget (guint32 handle)
277 {
278         MonoObject *obj;
279         gint32 type;
280
281         MONO_ARCH_SAVE_REGS;
282
283         if (gc_handles) {
284                 type = handle & 0x3;
285                 EnterCriticalSection (&handle_section);
286                 g_assert (type == gc_handle_types [handle >> 2]);
287                 obj = gc_handles [handle >> 2];
288                 LeaveCriticalSection (&handle_section);
289                 if (!obj)
290                         return NULL;
291
292                 if ((type == HANDLE_WEAK) || (type == HANDLE_WEAK_TRACK))
293                         return REVEAL_POINTER (obj);
294                 else
295                         return obj;
296         }
297         return NULL;
298 }
299
300 guint32
301 ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type)
302 {
303         gpointer val = obj;
304         guint32 h, idx;
305
306         MONO_ARCH_SAVE_REGS;
307
308         EnterCriticalSection (&handle_section);
309         idx = next_handle++;
310         if (idx >= array_size) {
311 #if HAVE_BOEHM_GC
312                 gpointer *new_array;
313                 guint8 *new_type_array;
314                 if (!array_size)
315                         array_size = 16;
316                 new_array = GC_MALLOC (sizeof (gpointer) * (array_size * 2));
317                 new_type_array = GC_MALLOC (sizeof (guint8) * (array_size * 2));
318                 if (gc_handles) {
319                         int i;
320                         memcpy (new_array, gc_handles, sizeof (gpointer) * array_size);
321                         memcpy (new_type_array, gc_handle_types, sizeof (guint8) * array_size);
322                         /* need to re-register links for weak refs. test if GC_realloc needs the same */
323                         for (i = 0; i < array_size; ++i) {
324 #if 0 /* This breaks the threaded finalizer, by causing segfaults deep
325        * inside libgc.  I assume it will also break without the
326        * threaded finalizer, just that the stress test (bug 31333)
327        * deadlocks too early without it.  Reverting to the previous
328        * version here stops the segfault.
329        */
330                                 if ((gc_handle_types[i] == HANDLE_WEAK) || (gc_handle_types[i] == HANDLE_WEAK_TRACK)) { /* all and only disguised pointers have it set */
331 #else
332                                 if (((gulong)new_array [i]) & 0x1) {
333 #endif
334                                         if (gc_handles [i] != (gpointer)-1)
335                                                 GC_unregister_disappearing_link (&(gc_handles [i]));
336                                         if (new_array [i] != (gpointer)-1)
337                                                 GC_GENERAL_REGISTER_DISAPPEARING_LINK (&(new_array [i]), REVEAL_POINTER (new_array [i]));
338                                 }
339                         }
340                 }
341                 array_size *= 2;
342                 gc_handles = new_array;
343                 gc_handle_types = new_type_array;
344 #else
345                 mono_raise_exception (mono_get_exception_execution_engine ("No GCHandle support built-in"));
346 #endif
347         }
348
349         /* resuse the type from the old target */
350         if (type == -1)
351                 type =  handle & 0x3;
352         h = (idx << 2) | type;
353         switch (type) {
354         case HANDLE_WEAK:
355         case HANDLE_WEAK_TRACK:
356                 val = (gpointer)HIDE_POINTER (val);
357                 gc_handles [idx] = val;
358                 gc_handle_types [idx] = type;
359 #if HAVE_BOEHM_GC
360                 if (gc_handles [idx] != (gpointer)-1)
361                         GC_GENERAL_REGISTER_DISAPPEARING_LINK (&(gc_handles [idx]), obj);
362 #else
363                 mono_raise_exception (mono_get_exception_execution_engine ("No weakref support"));
364 #endif
365                 break;
366         default:
367                 gc_handles [idx] = val;
368                 gc_handle_types [idx] = type;
369                 break;
370         }
371         LeaveCriticalSection (&handle_section);
372         return h;
373 }
374
375 void
376 ves_icall_System_GCHandle_FreeHandle (guint32 handle)
377 {
378         int idx = handle >> 2;
379         int type = handle & 0x3;
380
381         MONO_ARCH_SAVE_REGS;
382
383         EnterCriticalSection (&handle_section);
384
385 #ifdef HAVE_BOEHM_GC
386         g_assert (type == gc_handle_types [idx]);
387         if ((type == HANDLE_WEAK) || (type == HANDLE_WEAK_TRACK)) {
388                 if (gc_handles [idx] != (gpointer)-1)
389                         GC_unregister_disappearing_link (&(gc_handles [idx]));
390         }
391 #else
392         mono_raise_exception (mono_get_exception_execution_engine ("No GCHandle support"));
393 #endif
394
395         gc_handles [idx] = (gpointer)-1;
396         gc_handle_types [idx] = (guint8)-1;
397         LeaveCriticalSection (&handle_section);
398 }
399
400 gpointer
401 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
402 {
403         MonoObject *obj;
404         int type = handle & 0x3;
405
406         MONO_ARCH_SAVE_REGS;
407
408         if (gc_handles) {
409                 EnterCriticalSection (&handle_section);
410                 obj = gc_handles [handle >> 2];
411                 g_assert (gc_handle_types [handle >> 2] == type);
412                 LeaveCriticalSection (&handle_section);
413                 if ((type == HANDLE_WEAK) || (type == HANDLE_WEAK_TRACK)) {
414                         obj = REVEAL_POINTER (obj);
415                         if (obj == (MonoObject *) -1)
416                                 return NULL;
417                 }
418                 return obj;
419         }
420         return NULL;
421 }
422
423 #if HAVE_BOEHM_GC
424
425 static HANDLE finalizer_event;
426 static volatile gboolean finished=FALSE;
427
428 static void finalize_notify (void)
429 {
430 #ifdef DEBUG
431         g_message (G_GNUC_PRETTY_FUNCTION ": prodding finalizer");
432 #endif
433
434         SetEvent (finalizer_event);
435 }
436
437 static guint32 finalizer_thread (gpointer unused)
438 {
439         guint32 stack_start;
440         
441         mono_new_thread_init (GetCurrentThreadId (), &stack_start, NULL);
442         
443         while(!finished) {
444                 /* Wait to be notified that there's at least one
445                  * finaliser to run
446                  */
447                 WaitForSingleObject (finalizer_event, INFINITE);
448
449 #ifdef DEBUG
450                 g_message (G_GNUC_PRETTY_FUNCTION ": invoking finalizers");
451 #endif
452
453                 GC_invoke_finalizers ();
454         }
455         
456         return(0);
457 }
458
459 /* 
460  * Enable or disable the separate finalizer thread.
461  * It's currently disabled because it still requires some
462  * work in the rest of the runtime.
463  */
464 #define ENABLE_FINALIZER_THREAD
465
466 void mono_gc_init (void)
467 {
468         HANDLE gc_thread;
469
470         InitializeCriticalSection (&handle_section);
471
472 #ifdef ENABLE_FINALIZER_THREAD
473
474         if (getenv ("GC_DONT_GC"))
475                 return;
476         
477         finalizer_event = CreateEvent (NULL, FALSE, FALSE, NULL);
478         if (finalizer_event == NULL) {
479                 g_assert_not_reached ();
480         }
481
482         GC_finalize_on_demand = 1;
483         GC_finalizer_notifier = finalize_notify;
484         
485         /* Don't use mono_thread_create here, because we don't want
486          * the runtime to wait for this thread to exit when it's
487          * cleaning up.
488          */
489         gc_thread = CreateThread (NULL, 0, finalizer_thread, NULL, 0, NULL);
490         if (gc_thread == NULL) {
491                 g_assert_not_reached ();
492         }
493 #endif
494 }
495
496 void mono_gc_cleanup (void)
497 {
498 #ifdef DEBUG
499         g_message (G_GNUC_PRETTY_FUNCTION ": cleaning up finalizer");
500 #endif
501
502 #ifdef ENABLE_FINALIZER_THREAD
503         finished = TRUE;
504         finalize_notify ();
505 #endif
506 }
507
508 #else
509
510 /* no Boehm GC support. */
511 void mono_gc_init (void)
512 {
513         InitializeCriticalSection (&handle_section);
514 }
515
516 void mono_gc_cleanup (void)
517 {
518 }
519
520 #endif
521