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