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