2003-10-16 Zoltan Varga <vargaz@freemail.hu>
[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 #ifdef PLATFORM_WINCE /* FIXME: add accessors to gc.dll API */
26 extern void (*__imp_GC_finalizer_notifier)(void);
27 #define GC_finalizer_notifier __imp_GC_finalizer_notifier
28 extern int __imp_GC_finalize_on_demand;
29 #define GC_finalize_on_demand __imp_GC_finalize_on_demand
30 #endif
31
32 static int finalize_slot = -1;
33
34 static gboolean gc_disabled = FALSE;
35
36 static void object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*));
37
38 #if HAVE_BOEHM_GC
39 static void finalize_notify (void);
40 static HANDLE pending_done_event;
41 static HANDLE shutdown_event;
42 #endif
43
44 /* 
45  * actually, we might want to queue the finalize requests in a separate thread,
46  * but we need to be careful about the execution domain of the thread...
47  */
48 static void
49 run_finalize (void *obj, void *data)
50 {
51         MonoObject *exc = NULL;
52         MonoObject *o;
53         o = (MonoObject*)((char*)obj + GPOINTER_TO_UINT (data));
54
55         if (finalize_slot < 0) {
56                 int i;
57                 for (i = 0; i < mono_defaults.object_class->vtable_size; ++i) {
58                         MonoMethod *cm = mono_defaults.object_class->vtable [i];
59                
60                         if (!strcmp (cm->name, "Finalize")) {
61                                 finalize_slot = i;
62                                 break;
63                         }
64                 }
65         }
66
67         /* make sure the finalizer is not called again if the object is resurrected */
68         object_register_finalizer (obj, NULL);
69         /* speedup later... and use a timeout */
70         /*g_print ("Finalize run on %p %s.%s\n", o, mono_object_class (o)->name_space, mono_object_class (o)->name);*/
71         mono_domain_set (mono_object_domain (o));
72
73         mono_runtime_invoke (o->vtable->klass->vtable [finalize_slot], o, NULL, &exc);
74
75         if (exc) {
76                 /* fixme: do something useful */
77         }
78 }
79
80 /*
81  * Some of our objects may point to a different address than the address returned by GC_malloc()
82  * (because of the GetHashCode hack), but we need to pass the real address to register_finalizer.
83  * This also means that in the callback we need to adjust the pointer to get back the real
84  * MonoObject*.
85  * We also need to be consistent in the use of the GC_debug* variants of malloc and register_finalizer, 
86  * since that, too, can cause the underlying pointer to be offset.
87  */
88 static void
89 object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*))
90 {
91 #if HAVE_BOEHM_GC
92         guint offset = 0;
93
94 #ifndef GC_DEBUG
95         /* This assertion is not valid when GC_DEBUG is defined */
96         g_assert (GC_base (obj) == (char*)obj - offset);
97 #endif
98         GC_REGISTER_FINALIZER_NO_ORDER ((char*)obj - offset, callback, GUINT_TO_POINTER (offset), NULL, NULL);
99 #endif
100 }
101
102 void
103 mono_object_register_finalizer (MonoObject *obj)
104 {
105         /*g_print ("Registered finalizer on %p %s.%s\n", obj, mono_object_class (obj)->name_space, mono_object_class (obj)->name);*/
106         object_register_finalizer (obj, run_finalize);
107 }
108
109 /* 
110  * to speedup, at class init time, check if a class or struct
111  * have fields that need to be finalized and set a flag.
112  */
113 static void
114 finalize_fields (MonoClass *class, char *data, gboolean instance, GHashTable *todo) {
115         int i;
116         MonoClassField *field;
117         MonoObject *obj;
118
119         /*if (!instance)
120                 g_print ("Finalize statics on on %s\n", class->name);*/
121         if (instance && class->valuetype)
122                 data -= sizeof (MonoObject);
123         do {
124                 for (i = 0; i < class->field.count; ++i) {
125                         field = &class->fields [i];
126                         if (instance) {
127                                 if (field->type->attrs & FIELD_ATTRIBUTE_STATIC)
128                                         continue;
129                         } else {
130                                 if (!(field->type->attrs & FIELD_ATTRIBUTE_STATIC))
131                                         continue;
132                         }
133                         switch (field->type->type) {
134                         case MONO_TYPE_OBJECT:
135                         case MONO_TYPE_CLASS:
136                                 obj = *((MonoObject**)(data + field->offset));
137                                 if (obj) {
138                                         if (mono_object_class (obj)->has_finalize) {
139                                                 /* disable the registered finalizer */
140                                                 object_register_finalizer (obj, NULL);
141                                                 run_finalize (obj, NULL);
142                                         } else {
143                                                 /* 
144                                                  * if the type doesn't have a finalizer, we finalize 
145                                                  * the fields ourselves just like we do for structs.
146                                                  * Disabled for now: how do we handle loops?
147                                                  */
148                                                 /*finalize_fields (mono_object_class (obj), obj, TRUE, todo);*/
149                                         }
150                                 }
151                                 break;
152                         case MONO_TYPE_VALUETYPE: {
153                                 MonoClass *fclass = mono_class_from_mono_type (field->type);
154                                 if (fclass->enumtype)
155                                         continue;
156                                 /*finalize_fields (fclass, data + field->offset, TRUE, todo);*/
157                                 break;
158                         }
159                         case MONO_TYPE_ARRAY:
160                         case MONO_TYPE_SZARRAY:
161                                 /* FIXME: foreach item... */
162                                 break;
163                         }
164                 }
165                 if (!instance)
166                         return;
167                 class = class->parent;
168         } while (class);
169 }
170
171 static void
172 finalize_static_data (MonoClass *class, MonoVTable *vtable, GHashTable *todo) {
173
174         if (class->enumtype || !vtable->data)
175                 return;
176         finalize_fields (class, vtable->data, FALSE, todo);
177 }
178
179 void
180 mono_domain_finalize (MonoDomain *domain) 
181 {
182         GHashTable *todo = g_hash_table_new (NULL, NULL);
183
184         /* 
185          * No need to create another thread 'cause the finalizer thread
186          * is still working and will take care of running the finalizers
187          */ 
188         
189 #if HAVE_BOEHM_GC
190         GC_gcollect ();
191 #endif
192         mono_g_hash_table_foreach (domain->class_vtable_hash, (GHFunc)finalize_static_data, todo);
193         /* FIXME: finalize objects in todo... */
194         g_hash_table_destroy (todo);
195
196         return;
197 }
198
199 void
200 ves_icall_System_GC_InternalCollect (int generation)
201 {
202         MONO_ARCH_SAVE_REGS;
203
204 #if HAVE_BOEHM_GC
205         GC_gcollect ();
206 #endif
207 }
208
209 gint64
210 ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection)
211 {
212         MONO_ARCH_SAVE_REGS;
213
214 #if HAVE_BOEHM_GC
215         if (forceCollection)
216                 GC_gcollect ();
217         return GC_get_heap_size () - GC_get_free_bytes ();
218 #else
219         return 0;
220 #endif
221 }
222
223 void
224 ves_icall_System_GC_KeepAlive (MonoObject *obj)
225 {
226         MONO_ARCH_SAVE_REGS;
227
228         /*
229          * Does nothing.
230          */
231 }
232
233 void
234 ves_icall_System_GC_ReRegisterForFinalize (MonoObject *obj)
235 {
236         MONO_ARCH_SAVE_REGS;
237
238         object_register_finalizer (obj, run_finalize);
239 }
240
241 void
242 ves_icall_System_GC_SuppressFinalize (MonoObject *obj)
243 {
244         MONO_ARCH_SAVE_REGS;
245
246         object_register_finalizer (obj, NULL);
247 }
248
249 void
250 ves_icall_System_GC_WaitForPendingFinalizers (void)
251 {
252         MONO_ARCH_SAVE_REGS;
253         
254 #if HAVE_BOEHM_GC
255         if (!GC_should_invoke_finalizers ())
256                 return;
257
258         ResetEvent (pending_done_event);
259         finalize_notify ();
260         /* g_print ("Waiting for pending finalizers....\n"); */
261         WaitForSingleObject (pending_done_event, INFINITE);
262         /* g_print ("Done pending....\n"); */
263 #else
264 #endif
265 }
266
267 static CRITICAL_SECTION allocator_section;
268 static CRITICAL_SECTION handle_section;
269 static guint32 next_handle = 0;
270 static gpointer *gc_handles = NULL;
271 static guint8 *gc_handle_types = NULL;
272 static guint32 array_size = 0;
273
274 /*
275  * The handle type is encoded in the lower two bits of the handle value:
276  * 0 -> normal
277  * 1 -> pinned
278  * 2 -> weak
279  */
280
281 typedef enum {
282         HANDLE_WEAK,
283         HANDLE_WEAK_TRACK,
284         HANDLE_NORMAL,
285         HANDLE_PINNED
286 } HandleType;
287
288 /*
289  * FIXME: make thread safe and reuse the array entries.
290  */
291 MonoObject *
292 ves_icall_System_GCHandle_GetTarget (guint32 handle)
293 {
294         MonoObject *obj;
295         gint32 type;
296
297         MONO_ARCH_SAVE_REGS;
298
299         if (gc_handles) {
300                 type = handle & 0x3;
301                 EnterCriticalSection (&handle_section);
302                 g_assert (type == gc_handle_types [handle >> 2]);
303                 obj = gc_handles [handle >> 2];
304                 LeaveCriticalSection (&handle_section);
305                 if (!obj)
306                         return NULL;
307
308                 if ((type == HANDLE_WEAK) || (type == HANDLE_WEAK_TRACK))
309                         return REVEAL_POINTER (obj);
310                 else
311                         return obj;
312         }
313         return NULL;
314 }
315
316 guint32
317 ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type)
318 {
319         gpointer val = obj;
320         guint32 h, idx;
321
322         MONO_ARCH_SAVE_REGS;
323
324         EnterCriticalSection (&handle_section);
325         /* Indexes start from 1 since 0 means the handle is not allocated */
326         idx = ++next_handle;
327         if (idx >= array_size) {
328 #if HAVE_BOEHM_GC
329                 gpointer *new_array;
330                 guint8 *new_type_array;
331                 if (!array_size)
332                         array_size = 16;
333                 new_array = GC_MALLOC (sizeof (gpointer) * (array_size * 2));
334                 new_type_array = GC_MALLOC (sizeof (guint8) * (array_size * 2));
335                 if (gc_handles) {
336                         int i;
337                         memcpy (new_array, gc_handles, sizeof (gpointer) * array_size);
338                         memcpy (new_type_array, gc_handle_types, sizeof (guint8) * array_size);
339                         /* need to re-register links for weak refs. test if GC_realloc needs the same */
340                         for (i = 0; i < array_size; ++i) {
341 #if 0 /* This breaks the threaded finalizer, by causing segfaults deep
342        * inside libgc.  I assume it will also break without the
343        * threaded finalizer, just that the stress test (bug 31333)
344        * deadlocks too early without it.  Reverting to the previous
345        * version here stops the segfault.
346        */
347                                 if ((gc_handle_types[i] == HANDLE_WEAK) || (gc_handle_types[i] == HANDLE_WEAK_TRACK)) { /* all and only disguised pointers have it set */
348 #else
349                                 if (((gulong)new_array [i]) & 0x1) {
350 #endif
351                                         if (gc_handles [i] != (gpointer)-1)
352                                                 GC_unregister_disappearing_link (&(gc_handles [i]));
353                                         if (new_array [i] != (gpointer)-1)
354                                                 GC_GENERAL_REGISTER_DISAPPEARING_LINK (&(new_array [i]), REVEAL_POINTER (new_array [i]));
355                                 }
356                         }
357                 }
358                 array_size *= 2;
359                 gc_handles = new_array;
360                 gc_handle_types = new_type_array;
361 #else
362                 mono_raise_exception (mono_get_exception_execution_engine ("No GCHandle support built-in"));
363 #endif
364         }
365
366         /* resuse the type from the old target */
367         if (type == -1)
368                 type =  handle & 0x3;
369         h = (idx << 2) | type;
370         switch (type) {
371         case HANDLE_WEAK:
372         case HANDLE_WEAK_TRACK:
373                 val = (gpointer)HIDE_POINTER (val);
374                 gc_handles [idx] = val;
375                 gc_handle_types [idx] = type;
376 #if HAVE_BOEHM_GC
377                 if (gc_handles [idx] != (gpointer)-1)
378                         GC_GENERAL_REGISTER_DISAPPEARING_LINK (&(gc_handles [idx]), obj);
379 #else
380                 mono_raise_exception (mono_get_exception_execution_engine ("No weakref support"));
381 #endif
382                 break;
383         default:
384                 gc_handles [idx] = val;
385                 gc_handle_types [idx] = type;
386                 break;
387         }
388         LeaveCriticalSection (&handle_section);
389         return h;
390 }
391
392 void
393 ves_icall_System_GCHandle_FreeHandle (guint32 handle)
394 {
395         int idx = handle >> 2;
396         int type = handle & 0x3;
397
398         MONO_ARCH_SAVE_REGS;
399
400         EnterCriticalSection (&handle_section);
401
402 #ifdef HAVE_BOEHM_GC
403         g_assert (type == gc_handle_types [idx]);
404         if ((type == HANDLE_WEAK) || (type == HANDLE_WEAK_TRACK)) {
405                 if (gc_handles [idx] != (gpointer)-1)
406                         GC_unregister_disappearing_link (&(gc_handles [idx]));
407         }
408 #else
409         mono_raise_exception (mono_get_exception_execution_engine ("No GCHandle support"));
410 #endif
411
412         gc_handles [idx] = (gpointer)-1;
413         gc_handle_types [idx] = (guint8)-1;
414         LeaveCriticalSection (&handle_section);
415 }
416
417 gpointer
418 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
419 {
420         MonoObject *obj;
421         int type = handle & 0x3;
422
423         MONO_ARCH_SAVE_REGS;
424
425         if (gc_handles) {
426                 EnterCriticalSection (&handle_section);
427                 obj = gc_handles [handle >> 2];
428                 g_assert (gc_handle_types [handle >> 2] == type);
429                 LeaveCriticalSection (&handle_section);
430                 if ((type == HANDLE_WEAK) || (type == HANDLE_WEAK_TRACK)) {
431                         obj = REVEAL_POINTER (obj);
432                         if (obj == (MonoObject *) -1)
433                                 return NULL;
434                 }
435                 return obj;
436         }
437         return NULL;
438 }
439
440 #if HAVE_BOEHM_GC
441
442 static HANDLE finalizer_event;
443 static volatile gboolean finished=FALSE;
444
445 static void finalize_notify (void)
446 {
447 #ifdef DEBUG
448         g_message (G_GNUC_PRETTY_FUNCTION ": prodding finalizer");
449 #endif
450
451         SetEvent (finalizer_event);
452 }
453
454 static guint32 finalizer_thread (gpointer unused)
455 {
456         guint32 stack_start;
457         
458         mono_thread_new_init (GetCurrentThreadId (), &stack_start, NULL);
459         
460         while(!finished) {
461                 /* Wait to be notified that there's at least one
462                  * finaliser to run
463                  */
464                 WaitForSingleObject (finalizer_event, INFINITE);
465
466 #ifdef DEBUG
467                 g_message (G_GNUC_PRETTY_FUNCTION ": invoking finalizers");
468 #endif
469
470                 /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup),
471                  * before the domain is unloaded.
472                  *
473                  * There is a bug in GC_invoke_finalizer () in versions <= 6.2alpha4:
474                  * the 'mem_freed' variable is not initialized when there are no
475                  * objects to finalize, which leads to strange behavior later on.
476                  * The check is necessary to work around that bug.
477                  */
478                 if (GC_should_invoke_finalizers ()) {
479                         GC_invoke_finalizers ();
480                 }
481
482                 SetEvent (pending_done_event);
483         }
484
485         SetEvent (shutdown_event);
486         
487         return(0);
488 }
489
490 /* 
491  * Enable or disable the separate finalizer thread.
492  * It's currently disabled because it still requires some
493  * work in the rest of the runtime.
494  */
495 #define ENABLE_FINALIZER_THREAD
496
497 #ifdef WITH_INCLUDED_LIBGC
498 /* from threads.c */
499 extern void mono_gc_stop_world (void);
500 extern void mono_gc_start_world (void);
501 extern void mono_gc_push_all_stacks (void);
502
503 static void mono_gc_lock (void)
504 {
505         EnterCriticalSection (&allocator_section);
506 }
507
508 static void mono_gc_unlock (void)
509 {
510         LeaveCriticalSection (&allocator_section);
511 }
512
513 static GCThreadFunctions mono_gc_thread_vtable = {
514         NULL,
515
516         mono_gc_lock,
517         mono_gc_unlock,
518
519         mono_gc_stop_world,
520         NULL,
521         mono_gc_push_all_stacks,
522         mono_gc_start_world
523 };
524 #endif /* WITH_INCLUDED_LIBGC */
525
526 void mono_gc_init (void)
527 {
528         HANDLE gc_thread;
529
530         InitializeCriticalSection (&handle_section);
531         InitializeCriticalSection (&allocator_section);
532
533 #ifdef WITH_INCLUDED_LIBGC
534         gc_thread_vtable = &mono_gc_thread_vtable;
535 #endif
536
537 #ifdef ENABLE_FINALIZER_THREAD
538
539         if (getenv ("GC_DONT_GC")) {
540                 gc_disabled = TRUE;
541                 return;
542         }
543         
544         finalizer_event = CreateEvent (NULL, FALSE, FALSE, NULL);
545         pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
546         shutdown_event = CreateEvent (NULL, TRUE, FALSE, NULL);
547         if (finalizer_event == NULL || pending_done_event == NULL || shutdown_event == NULL) {
548                 g_assert_not_reached ();
549         }
550
551         GC_finalize_on_demand = 1;
552         GC_finalizer_notifier = finalize_notify;
553         
554         /* Don't use mono_thread_create here, because we don't want
555          * the runtime to wait for this thread to exit when it's
556          * cleaning up.
557          */
558         gc_thread = CreateThread (NULL, mono_threads_get_default_stacksize (), finalizer_thread, NULL, 0, NULL);
559         if (gc_thread == NULL) {
560                 g_assert_not_reached ();
561         }
562 #endif
563 }
564
565 void mono_gc_cleanup (void)
566 {
567 #ifdef DEBUG
568         g_message (G_GNUC_PRETTY_FUNCTION ": cleaning up finalizer");
569 #endif
570
571 #ifdef ENABLE_FINALIZER_THREAD
572         ResetEvent (shutdown_event);
573         finished = TRUE;
574         if (!gc_disabled) {
575                 finalize_notify ();
576                 /* Finishing the finalizer thread, so wait a little bit... */
577                 /* MS seems to wait for about 2 seconds */
578                 /* 
579                  * FIXME: This is not thread safe. If the finalizer thread keeps
580                  * running, and the runtime is shut down, it will lead to a crash.
581                  */
582                 WaitForSingleObject (shutdown_event, 2000);
583         }
584
585 #endif
586 }
587
588 #else
589
590 /* no Boehm GC support. */
591 void mono_gc_init (void)
592 {
593         InitializeCriticalSection (&handle_section);
594 }
595
596 void mono_gc_cleanup (void)
597 {
598 }
599
600 #endif
601