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