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