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