2004-02-26 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 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         CloseHandle (done_event);
201         //printf ("WAIT RES: %d.\n", res);
202         if (res == WAIT_TIMEOUT)
203                 return FALSE;
204         else
205                 return TRUE;
206 #else
207         /* We don't support domain finalization without a GC */
208         return FALSE;
209 #endif
210 }
211
212 void
213 ves_icall_System_GC_InternalCollect (int generation)
214 {
215         MONO_ARCH_SAVE_REGS;
216
217 #if HAVE_BOEHM_GC
218         GC_gcollect ();
219 #endif
220 }
221
222 gint64
223 ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection)
224 {
225         MONO_ARCH_SAVE_REGS;
226
227 #if HAVE_BOEHM_GC
228         if (forceCollection)
229                 GC_gcollect ();
230         return GC_get_heap_size () - GC_get_free_bytes ();
231 #else
232         return 0;
233 #endif
234 }
235
236 void
237 ves_icall_System_GC_KeepAlive (MonoObject *obj)
238 {
239         MONO_ARCH_SAVE_REGS;
240
241         /*
242          * Does nothing.
243          */
244 }
245
246 void
247 ves_icall_System_GC_ReRegisterForFinalize (MonoObject *obj)
248 {
249         MONO_ARCH_SAVE_REGS;
250
251         object_register_finalizer (obj, run_finalize);
252 }
253
254 void
255 ves_icall_System_GC_SuppressFinalize (MonoObject *obj)
256 {
257         MONO_ARCH_SAVE_REGS;
258
259         object_register_finalizer (obj, NULL);
260 }
261
262 void
263 ves_icall_System_GC_WaitForPendingFinalizers (void)
264 {
265         MONO_ARCH_SAVE_REGS;
266         
267 #if HAVE_BOEHM_GC
268         if (!GC_should_invoke_finalizers ())
269                 return;
270
271         if (mono_thread_current () == gc_thread)
272                 /* Avoid deadlocks */
273                 return;
274
275         ResetEvent (pending_done_event);
276         finalize_notify ();
277         /* g_print ("Waiting for pending finalizers....\n"); */
278         WaitForSingleObject (pending_done_event, INFINITE);
279         /* g_print ("Done pending....\n"); */
280 #else
281 #endif
282 }
283
284 static CRITICAL_SECTION allocator_section;
285 static CRITICAL_SECTION handle_section;
286 static guint32 next_handle = 0;
287 static gpointer *gc_handles = NULL;
288 static guint8 *gc_handle_types = NULL;
289 static guint32 array_size = 0;
290
291 /*
292  * The handle type is encoded in the lower two bits of the handle value:
293  * 0 -> normal
294  * 1 -> pinned
295  * 2 -> weak
296  */
297
298 typedef enum {
299         HANDLE_WEAK,
300         HANDLE_WEAK_TRACK,
301         HANDLE_NORMAL,
302         HANDLE_PINNED
303 } HandleType;
304
305 /*
306  * FIXME: make thread safe and reuse the array entries.
307  */
308 MonoObject *
309 ves_icall_System_GCHandle_GetTarget (guint32 handle)
310 {
311         MonoObject *obj;
312         gint32 type;
313
314         MONO_ARCH_SAVE_REGS;
315
316         if (gc_handles) {
317                 type = handle & 0x3;
318                 EnterCriticalSection (&handle_section);
319                 g_assert (type == gc_handle_types [handle >> 2]);
320                 obj = gc_handles [handle >> 2];
321                 LeaveCriticalSection (&handle_section);
322                 if (!obj)
323                         return NULL;
324
325                 if ((type == HANDLE_WEAK) || (type == HANDLE_WEAK_TRACK))
326                         return REVEAL_POINTER (obj);
327                 else
328                         return obj;
329         }
330         return NULL;
331 }
332
333 guint32
334 ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type)
335 {
336         gpointer val = obj;
337         guint32 h, idx;
338
339         MONO_ARCH_SAVE_REGS;
340
341         EnterCriticalSection (&handle_section);
342         /* Indexes start from 1 since 0 means the handle is not allocated */
343         idx = ++next_handle;
344         if (idx >= array_size) {
345 #if HAVE_BOEHM_GC
346                 gpointer *new_array;
347                 guint8 *new_type_array;
348                 if (!array_size)
349                         array_size = 16;
350                 new_array = GC_MALLOC (sizeof (gpointer) * (array_size * 2));
351                 new_type_array = GC_MALLOC (sizeof (guint8) * (array_size * 2));
352                 if (gc_handles) {
353                         int i;
354                         memcpy (new_array, gc_handles, sizeof (gpointer) * array_size);
355                         memcpy (new_type_array, gc_handle_types, sizeof (guint8) * array_size);
356                         /* need to re-register links for weak refs. test if GC_realloc needs the same */
357                         for (i = 0; i < array_size; ++i) {
358 #if 0 /* This breaks the threaded finalizer, by causing segfaults deep
359        * inside libgc.  I assume it will also break without the
360        * threaded finalizer, just that the stress test (bug 31333)
361        * deadlocks too early without it.  Reverting to the previous
362        * version here stops the segfault.
363        */
364                                 if ((gc_handle_types[i] == HANDLE_WEAK) || (gc_handle_types[i] == HANDLE_WEAK_TRACK)) { /* all and only disguised pointers have it set */
365 #else
366                                 if (((gulong)new_array [i]) & 0x1) {
367 #endif
368                                         if (gc_handles [i] != (gpointer)-1)
369                                                 GC_unregister_disappearing_link (&(gc_handles [i]));
370                                         if (new_array [i] != (gpointer)-1)
371                                                 GC_GENERAL_REGISTER_DISAPPEARING_LINK (&(new_array [i]), REVEAL_POINTER (new_array [i]));
372                                 }
373                         }
374                 }
375                 array_size *= 2;
376                 gc_handles = new_array;
377                 gc_handle_types = new_type_array;
378 #else
379                 mono_raise_exception (mono_get_exception_execution_engine ("No GCHandle support built-in"));
380 #endif
381         }
382
383         /* resuse the type from the old target */
384         if (type == -1)
385                 type =  handle & 0x3;
386         h = (idx << 2) | type;
387         switch (type) {
388         case HANDLE_WEAK:
389         case HANDLE_WEAK_TRACK:
390                 val = (gpointer)HIDE_POINTER (val);
391                 gc_handles [idx] = val;
392                 gc_handle_types [idx] = type;
393 #if HAVE_BOEHM_GC
394                 if (gc_handles [idx] != (gpointer)-1)
395                         GC_GENERAL_REGISTER_DISAPPEARING_LINK (&(gc_handles [idx]), obj);
396 #else
397                 mono_raise_exception (mono_get_exception_execution_engine ("No weakref support"));
398 #endif
399                 break;
400         default:
401                 gc_handles [idx] = val;
402                 gc_handle_types [idx] = type;
403                 break;
404         }
405         LeaveCriticalSection (&handle_section);
406         return h;
407 }
408
409 void
410 ves_icall_System_GCHandle_FreeHandle (guint32 handle)
411 {
412         int idx = handle >> 2;
413         int type = handle & 0x3;
414
415         MONO_ARCH_SAVE_REGS;
416
417         EnterCriticalSection (&handle_section);
418
419 #ifdef HAVE_BOEHM_GC
420         g_assert (type == gc_handle_types [idx]);
421         if ((type == HANDLE_WEAK) || (type == HANDLE_WEAK_TRACK)) {
422                 if (gc_handles [idx] != (gpointer)-1)
423                         GC_unregister_disappearing_link (&(gc_handles [idx]));
424         }
425 #else
426         LeaveCriticalSection (&handle_section);
427         mono_raise_exception (mono_get_exception_execution_engine ("No GCHandle support"));
428 #endif
429
430         gc_handles [idx] = (gpointer)-1;
431         gc_handle_types [idx] = (guint8)-1;
432         LeaveCriticalSection (&handle_section);
433 }
434
435 gpointer
436 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
437 {
438         MonoObject *obj;
439         int type = handle & 0x3;
440
441         MONO_ARCH_SAVE_REGS;
442
443         if (gc_handles) {
444                 EnterCriticalSection (&handle_section);
445                 obj = gc_handles [handle >> 2];
446                 g_assert (gc_handle_types [handle >> 2] == type);
447                 LeaveCriticalSection (&handle_section);
448                 if ((type == HANDLE_WEAK) || (type == HANDLE_WEAK_TRACK)) {
449                         obj = REVEAL_POINTER (obj);
450                         if (obj == (MonoObject *) -1)
451                                 return NULL;
452                 }
453                 return obj;
454         }
455         return NULL;
456 }
457
458 #if HAVE_BOEHM_GC
459
460 static HANDLE finalizer_event;
461 static volatile gboolean finished=FALSE;
462
463 static void finalize_notify (void)
464 {
465 #ifdef DEBUG
466         g_message (G_GNUC_PRETTY_FUNCTION ": prodding finalizer");
467 #endif
468
469         SetEvent (finalizer_event);
470 }
471
472 static void
473 collect_objects (gpointer key, gpointer value, gpointer user_data)
474 {
475         GPtrArray *arr = (GPtrArray*)user_data;
476         g_ptr_array_add (arr, key);
477 }
478
479 /*
480  * finalize_domain_objects:
481  *
482  *  Run the finalizers of all finalizable objects in req->domain.
483  */
484 static void
485 finalize_domain_objects (DomainFinalizationReq *req)
486 {
487         int i;
488         GPtrArray *objs;
489         MonoDomain *domain = req->domain;
490
491         while (g_hash_table_size (domain->finalizable_objects_hash) > 0) {
492                 /* 
493                  * Since the domain is unloading, nobody is allowed to put
494                  * new entries into the hash table. But finalize_object might
495                  * remove entries from the hash table, so we make a copy.
496                  */
497                 objs = g_ptr_array_new ();
498                 g_hash_table_foreach (domain->finalizable_objects_hash, 
499                                                           collect_objects, objs);
500                 //printf ("FINALIZING %d OBJECTS.\n", objs->len);
501
502                 for (i = 0; i < objs->len; ++i) {
503                         MonoObject *o = (MonoObject*)g_ptr_array_index (objs, i);
504                         /* FIXME: Avoid finalizing threads, etc */
505                         run_finalize (o, 0);
506                 }
507
508                 g_ptr_array_free (objs, TRUE);
509         }
510
511         //printf ("DONE.\n");
512         SetEvent (req->done_event);
513
514         /* FIXME: How to delete the event ? */
515         g_free (req);
516 }
517
518 static guint32 finalizer_thread (gpointer unused)
519 {
520         gc_thread = mono_thread_current ();
521
522         SetEvent (thread_started_event);
523
524         while(!finished) {
525                 /* Wait to be notified that there's at least one
526                  * finaliser to run
527                  */
528                 WaitForSingleObject (finalizer_event, INFINITE);
529
530                 if (domains_to_finalize) {
531                         EnterCriticalSection (&finalizer_mutex);
532                         if (domains_to_finalize) {
533                                 DomainFinalizationReq *req = domains_to_finalize->data;
534                                 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
535                                 LeaveCriticalSection (&finalizer_mutex);
536
537                                 finalize_domain_objects (req);
538                         }
539                         else
540                                 LeaveCriticalSection (&finalizer_mutex);
541                 }                               
542
543 #ifdef DEBUG
544                 g_message (G_GNUC_PRETTY_FUNCTION ": invoking finalizers");
545 #endif
546
547                 /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup),
548                  * before the domain is unloaded.
549                  *
550                  * There is a bug in GC_invoke_finalizer () in versions <= 6.2alpha4:
551                  * the 'mem_freed' variable is not initialized when there are no
552                  * objects to finalize, which leads to strange behavior later on.
553                  * The check is necessary to work around that bug.
554                  */
555                 if (GC_should_invoke_finalizers ()) {
556                         GC_invoke_finalizers ();
557                 }
558
559                 SetEvent (pending_done_event);
560         }
561
562         SetEvent (shutdown_event);
563         
564         return(0);
565 }
566
567 /* 
568  * Enable or disable the separate finalizer thread.
569  * It's currently disabled because it still requires some
570  * work in the rest of the runtime.
571  */
572 #define ENABLE_FINALIZER_THREAD
573
574 #ifdef WITH_INCLUDED_LIBGC
575 /* from threads.c */
576 extern void mono_gc_stop_world (void);
577 extern void mono_gc_start_world (void);
578 extern void mono_gc_push_all_stacks (void);
579
580 static void mono_gc_lock (void)
581 {
582         EnterCriticalSection (&allocator_section);
583 }
584
585 static void mono_gc_unlock (void)
586 {
587         LeaveCriticalSection (&allocator_section);
588 }
589
590 static GCThreadFunctions mono_gc_thread_vtable = {
591         NULL,
592
593         mono_gc_lock,
594         mono_gc_unlock,
595
596         mono_gc_stop_world,
597         NULL,
598         mono_gc_push_all_stacks,
599         mono_gc_start_world
600 };
601 #endif /* WITH_INCLUDED_LIBGC */
602
603 void mono_gc_init (void)
604 {
605         InitializeCriticalSection (&handle_section);
606         InitializeCriticalSection (&allocator_section);
607
608         InitializeCriticalSection (&finalizer_mutex);
609
610 #ifdef WITH_INCLUDED_LIBGC
611         gc_thread_vtable = &mono_gc_thread_vtable;
612 #endif
613
614 #ifdef ENABLE_FINALIZER_THREAD
615
616         if (getenv ("GC_DONT_GC")) {
617                 gc_disabled = TRUE;
618                 return;
619         }
620         
621         finalizer_event = CreateEvent (NULL, FALSE, FALSE, NULL);
622         pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
623         shutdown_event = CreateEvent (NULL, TRUE, FALSE, NULL);
624         thread_started_event = CreateEvent (NULL, TRUE, FALSE, NULL);
625         if (finalizer_event == NULL || pending_done_event == NULL || shutdown_event == NULL || thread_started_event == NULL) {
626                 g_assert_not_reached ();
627         }
628
629         GC_finalize_on_demand = 1;
630         GC_finalizer_notifier = finalize_notify;
631
632         mono_thread_create (mono_domain_get (), finalizer_thread, NULL);
633         /*
634          * Wait until the finalizer thread sets gc_thread since its value is needed
635          * by mono_thread_attach ()
636          */
637         WaitForSingleObject (thread_started_event, INFINITE);
638 #endif
639 }
640
641 void mono_gc_cleanup (void)
642 {
643 #ifdef DEBUG
644         g_message (G_GNUC_PRETTY_FUNCTION ": cleaning up finalizer");
645 #endif
646
647 #ifdef ENABLE_FINALIZER_THREAD
648         if (!gc_disabled) {
649                 ResetEvent (shutdown_event);
650                 finished = TRUE;
651                 finalize_notify ();
652                 /* Finishing the finalizer thread, so wait a little bit... */
653                 /* MS seems to wait for about 2 seconds */
654                 /* 
655                  * FIXME: This is not thread safe. If the finalizer thread keeps
656                  * running, and the runtime is shut down, it will lead to a crash.
657                  */
658                 WaitForSingleObject (shutdown_event, 2000);
659         }
660
661 #endif
662 }
663
664 void
665 mono_gc_disable (void)
666 {
667         GC_disable ();
668 }
669
670 void
671 mono_gc_enable (void)
672 {
673         GC_enable ();
674 }
675
676 #else
677
678 /* no Boehm GC support. */
679 void mono_gc_init (void)
680 {
681         InitializeCriticalSection (&handle_section);
682 }
683
684 void mono_gc_cleanup (void)
685 {
686 }
687
688 void
689 mono_gc_disable (void)
690 {
691 }
692
693 void
694 mono_gc_enable (void)
695 {
696 }
697
698 #endif
699
700 gboolean
701 mono_gc_is_finalizer_thread (MonoThread *thread)
702 {
703         return thread == gc_thread;
704 }
705
706