2004-03-31 Gonzalo Paniagua Javier <gonzalo@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 = WaitForSingleObject (done_event, timeout);
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         WaitForSingleObject (pending_done_event, INFINITE);
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                 mono_raise_exception (mono_get_exception_execution_engine ("No GCHandle support built-in"));
382 #endif
383         }
384
385         /* resuse the type from the old target */
386         if (type == -1)
387                 type =  handle & 0x3;
388         h = (idx << 2) | type;
389         switch (type) {
390         case HANDLE_WEAK:
391         case HANDLE_WEAK_TRACK:
392                 val = (gpointer)HIDE_POINTER (val);
393                 gc_handles [idx] = val;
394                 gc_handle_types [idx] = type;
395 #if HAVE_BOEHM_GC
396                 if (gc_handles [idx] != (gpointer)-1)
397                         GC_GENERAL_REGISTER_DISAPPEARING_LINK (&(gc_handles [idx]), obj);
398 #else
399                 mono_raise_exception (mono_get_exception_execution_engine ("No weakref support"));
400 #endif
401                 break;
402         default:
403                 gc_handles [idx] = val;
404                 gc_handle_types [idx] = type;
405                 break;
406         }
407         LeaveCriticalSection (&handle_section);
408         return h;
409 }
410
411 void
412 ves_icall_System_GCHandle_FreeHandle (guint32 handle)
413 {
414         int idx = handle >> 2;
415         int type = handle & 0x3;
416
417         MONO_ARCH_SAVE_REGS;
418
419         EnterCriticalSection (&handle_section);
420
421 #ifdef HAVE_BOEHM_GC
422         g_assert (type == gc_handle_types [idx]);
423         if ((type == HANDLE_WEAK) || (type == HANDLE_WEAK_TRACK)) {
424                 if (gc_handles [idx] != (gpointer)-1)
425                         GC_unregister_disappearing_link (&(gc_handles [idx]));
426         }
427 #else
428         LeaveCriticalSection (&handle_section);
429         mono_raise_exception (mono_get_exception_execution_engine ("No GCHandle support"));
430 #endif
431
432         gc_handles [idx] = (gpointer)-1;
433         gc_handle_types [idx] = (guint8)-1;
434         LeaveCriticalSection (&handle_section);
435 }
436
437 gpointer
438 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
439 {
440         MonoObject *obj;
441         int type = handle & 0x3;
442
443         MONO_ARCH_SAVE_REGS;
444
445         if (gc_handles) {
446                 EnterCriticalSection (&handle_section);
447                 obj = gc_handles [handle >> 2];
448                 g_assert (gc_handle_types [handle >> 2] == type);
449                 LeaveCriticalSection (&handle_section);
450                 if ((type == HANDLE_WEAK) || (type == HANDLE_WEAK_TRACK)) {
451                         obj = REVEAL_POINTER (obj);
452                         if (obj == (MonoObject *) -1)
453                                 return NULL;
454                 }
455                 return obj;
456         }
457         return NULL;
458 }
459
460 #if HAVE_BOEHM_GC
461
462 static HANDLE finalizer_event;
463 static volatile gboolean finished=FALSE;
464
465 static void finalize_notify (void)
466 {
467 #ifdef DEBUG
468         g_message (G_GNUC_PRETTY_FUNCTION ": prodding finalizer");
469 #endif
470
471         SetEvent (finalizer_event);
472 }
473
474 static void
475 collect_objects (gpointer key, gpointer value, gpointer user_data)
476 {
477         GPtrArray *arr = (GPtrArray*)user_data;
478         g_ptr_array_add (arr, key);
479 }
480
481 /*
482  * finalize_domain_objects:
483  *
484  *  Run the finalizers of all finalizable objects in req->domain.
485  */
486 static void
487 finalize_domain_objects (DomainFinalizationReq *req)
488 {
489         int i;
490         GPtrArray *objs;
491         MonoDomain *domain = req->domain;
492
493         while (g_hash_table_size (domain->finalizable_objects_hash) > 0) {
494                 /* 
495                  * Since the domain is unloading, nobody is allowed to put
496                  * new entries into the hash table. But finalize_object might
497                  * remove entries from the hash table, so we make a copy.
498                  */
499                 objs = g_ptr_array_new ();
500                 g_hash_table_foreach (domain->finalizable_objects_hash, 
501                                                           collect_objects, objs);
502                 //printf ("FINALIZING %d OBJECTS.\n", objs->len);
503
504                 for (i = 0; i < objs->len; ++i) {
505                         MonoObject *o = (MonoObject*)g_ptr_array_index (objs, i);
506                         /* FIXME: Avoid finalizing threads, etc */
507                         run_finalize (o, 0);
508                 }
509
510                 g_ptr_array_free (objs, TRUE);
511         }
512
513         /* printf ("DONE.\n"); */
514         SetEvent (req->done_event);
515
516         /* The event is closed in mono_domain_finalize if we get here */
517         g_free (req);
518 }
519
520 static guint32 finalizer_thread (gpointer unused)
521 {
522         gc_thread = mono_thread_current ();
523
524         SetEvent (thread_started_event);
525
526         while(!finished) {
527                 /* Wait to be notified that there's at least one
528                  * finaliser to run
529                  */
530                 WaitForSingleObject (finalizer_event, INFINITE);
531
532                 if (domains_to_finalize) {
533                         EnterCriticalSection (&finalizer_mutex);
534                         if (domains_to_finalize) {
535                                 DomainFinalizationReq *req = domains_to_finalize->data;
536                                 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
537                                 LeaveCriticalSection (&finalizer_mutex);
538
539                                 finalize_domain_objects (req);
540                         }
541                         else
542                                 LeaveCriticalSection (&finalizer_mutex);
543                 }                               
544
545 #ifdef DEBUG
546                 g_message (G_GNUC_PRETTY_FUNCTION ": invoking finalizers");
547 #endif
548
549                 /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup),
550                  * before the domain is unloaded.
551                  *
552                  * There is a bug in GC_invoke_finalizer () in versions <= 6.2alpha4:
553                  * the 'mem_freed' variable is not initialized when there are no
554                  * objects to finalize, which leads to strange behavior later on.
555                  * The check is necessary to work around that bug.
556                  */
557                 if (GC_should_invoke_finalizers ()) {
558                         GC_invoke_finalizers ();
559                 }
560
561                 SetEvent (pending_done_event);
562         }
563
564         SetEvent (shutdown_event);
565         
566         return(0);
567 }
568
569 /* 
570  * Enable or disable the separate finalizer thread.
571  * It's currently disabled because it still requires some
572  * work in the rest of the runtime.
573  */
574 #define ENABLE_FINALIZER_THREAD
575
576 #ifdef WITH_INCLUDED_LIBGC
577 /* from threads.c */
578 extern void mono_gc_stop_world (void);
579 extern void mono_gc_start_world (void);
580 extern void mono_gc_push_all_stacks (void);
581
582 static void mono_gc_lock (void)
583 {
584         EnterCriticalSection (&allocator_section);
585 }
586
587 static void mono_gc_unlock (void)
588 {
589         LeaveCriticalSection (&allocator_section);
590 }
591
592 static GCThreadFunctions mono_gc_thread_vtable = {
593         NULL,
594
595         mono_gc_lock,
596         mono_gc_unlock,
597
598         mono_gc_stop_world,
599         NULL,
600         mono_gc_push_all_stacks,
601         mono_gc_start_world
602 };
603 #endif /* WITH_INCLUDED_LIBGC */
604
605 void mono_gc_init (void)
606 {
607         InitializeCriticalSection (&handle_section);
608         InitializeCriticalSection (&allocator_section);
609
610         InitializeCriticalSection (&finalizer_mutex);
611
612 #ifdef WITH_INCLUDED_LIBGC
613         gc_thread_vtable = &mono_gc_thread_vtable;
614 #endif
615
616 #ifdef ENABLE_FINALIZER_THREAD
617
618         if (getenv ("GC_DONT_GC")) {
619                 gc_disabled = TRUE;
620                 return;
621         }
622         
623         finalizer_event = CreateEvent (NULL, FALSE, FALSE, NULL);
624         pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
625         shutdown_event = CreateEvent (NULL, TRUE, FALSE, NULL);
626         thread_started_event = CreateEvent (NULL, TRUE, FALSE, NULL);
627         if (finalizer_event == NULL || pending_done_event == NULL || shutdown_event == NULL || thread_started_event == NULL) {
628                 g_assert_not_reached ();
629         }
630
631         GC_finalize_on_demand = 1;
632         GC_finalizer_notifier = finalize_notify;
633
634         mono_thread_create (mono_domain_get (), finalizer_thread, NULL);
635         /*
636          * Wait until the finalizer thread sets gc_thread since its value is needed
637          * by mono_thread_attach ()
638          */
639         WaitForSingleObject (thread_started_event, INFINITE);
640 #endif
641 }
642
643 void mono_gc_cleanup (void)
644 {
645 #ifdef DEBUG
646         g_message (G_GNUC_PRETTY_FUNCTION ": cleaning up finalizer");
647 #endif
648
649 #ifdef ENABLE_FINALIZER_THREAD
650         if (!gc_disabled) {
651                 ResetEvent (shutdown_event);
652                 finished = TRUE;
653                 finalize_notify ();
654                 /* Finishing the finalizer thread, so wait a little bit... */
655                 /* MS seems to wait for about 2 seconds */
656                 /* 
657                  * FIXME: This is not thread safe. If the finalizer thread keeps
658                  * running, and the runtime is shut down, it will lead to a crash.
659                  */
660                 WaitForSingleObject (shutdown_event, 2000);
661         }
662
663 #endif
664 }
665
666 void
667 mono_gc_disable (void)
668 {
669 #ifdef HAVE_GC_ENABLE
670         GC_disable ();
671 #else
672         g_assert_not_reached ();
673 #endif
674 }
675
676 void
677 mono_gc_enable (void)
678 {
679 #ifdef HAVE_GC_ENABLE
680         GC_enable ();
681 #else
682         g_assert_not_reached ();
683 #endif
684 }
685
686 #else
687
688 /* no Boehm GC support. */
689 void mono_gc_init (void)
690 {
691         InitializeCriticalSection (&handle_section);
692 }
693
694 void mono_gc_cleanup (void)
695 {
696 }
697
698 void
699 mono_gc_disable (void)
700 {
701 }
702
703 void
704 mono_gc_enable (void)
705 {
706 }
707
708 #endif
709
710 gboolean
711 mono_gc_is_finalizer_thread (MonoThread *thread)
712 {
713         return thread == gc_thread;
714 }
715
716