2005-12-20 Zoltan Varga <vargaz@gmail.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/mono-gc.h>
15 #include <mono/metadata/threads.h>
16 #include <mono/metadata/tabledefs.h>
17 #include <mono/metadata/exception.h>
18 #include <mono/metadata/profiler-private.h>
19 #include <mono/metadata/domain-internals.h>
20 #include <mono/metadata/class-internals.h>
21 #include <mono/utils/mono-logger.h>
22 #include <mono/os/gc_wrapper.h>
23 #include <mono/metadata/marshal.h> /* for mono_delegate_free_ftnptr () */
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 gboolean gc_disabled = FALSE;
38
39 #define mono_finalizer_lock() EnterCriticalSection (&finalizer_mutex)
40 #define mono_finalizer_unlock() LeaveCriticalSection (&finalizer_mutex)
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         mono_domain_lock (o->vtable->domain);
68
69         o2 = g_hash_table_lookup (o->vtable->domain->finalizable_objects_hash, o);
70
71         mono_domain_unlock (o->vtable->domain);
72
73         if (!o2)
74                 /* Already finalized somehow */
75                 return;
76
77         /* make sure the finalizer is not called again if the object is resurrected */
78         object_register_finalizer (obj, NULL);
79
80         if (o->vtable->klass == mono_get_thread_class ())
81                 if (mono_gc_is_finalizer_thread ((MonoThread*)o))
82                         /* Avoid finalizing ourselves */
83                         return;
84
85         /* speedup later... and use a timeout */
86         /* g_print ("Finalize run on %p %s.%s\n", o, mono_object_class (o)->name_space, mono_object_class (o)->name); */
87
88         /* Use _internal here, since this thread can enter a doomed appdomain */
89         mono_domain_set_internal (mono_object_domain (o));              
90
91         /* delegates that have a native function pointer allocated are
92          * registered for finalization, but they don't have a Finalize
93          * method, because in most cases it's not needed and it's just a waste.
94          */
95         if (o->vtable->klass->delegate) {
96                 MonoDelegate* del = (MonoDelegate*)o;
97                 if (del->delegate_trampoline)
98                         mono_delegate_free_ftnptr ((MonoDelegate*)o);
99                 return;
100         }
101
102         mono_runtime_invoke (mono_class_get_finalizer (o->vtable->klass), o, NULL, &exc);
103
104         if (exc) {
105                 /* fixme: do something useful */
106         }
107 }
108
109 gpointer
110 mono_gc_out_of_memory (size_t size)
111 {
112         /* 
113          * we could allocate at program startup some memory that we could release 
114          * back to the system at this point if we're really low on memory (ie, size is
115          * lower than the memory we set apart)
116          */
117         mono_raise_exception (mono_domain_get ()->out_of_memory_ex);
118
119         return NULL;
120 }
121
122 /*
123  * Some of our objects may point to a different address than the address returned by GC_malloc()
124  * (because of the GetHashCode hack), but we need to pass the real address to register_finalizer.
125  * This also means that in the callback we need to adjust the pointer to get back the real
126  * MonoObject*.
127  * We also need to be consistent in the use of the GC_debug* variants of malloc and register_finalizer, 
128  * since that, too, can cause the underlying pointer to be offset.
129  */
130 static void
131 object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*))
132 {
133 #if HAVE_BOEHM_GC
134         guint offset = 0;
135
136 #ifndef GC_DEBUG
137         /* This assertion is not valid when GC_DEBUG is defined */
138         g_assert (GC_base (obj) == (char*)obj - offset);
139 #endif
140
141         if (mono_domain_is_unloading (obj->vtable->domain) && (callback != NULL))
142                 /*
143                  * Can't register finalizers in a dying appdomain, since they
144                  * could be invoked after the appdomain has been unloaded.
145                  */
146                 return;
147
148         mono_domain_lock (obj->vtable->domain);
149
150         if (callback)
151                 g_hash_table_insert (obj->vtable->domain->finalizable_objects_hash, obj,
152                                                          obj);
153         else
154                 g_hash_table_remove (obj->vtable->domain->finalizable_objects_hash, obj);
155
156         mono_domain_unlock (obj->vtable->domain);
157
158         GC_REGISTER_FINALIZER_NO_ORDER ((char*)obj - offset, callback, GUINT_TO_POINTER (offset), NULL, NULL);
159 #endif
160 }
161
162 /**
163  * mono_object_register_finalizer:
164  * @obj: object to register
165  *
166  * Records that object @obj has a finalizer, this will call the
167  * Finalize method when the garbage collector disposes the object.
168  * 
169  */
170 void
171 mono_object_register_finalizer (MonoObject *obj)
172 {
173         /* g_print ("Registered finalizer on %p %s.%s\n", obj, mono_object_class (obj)->name_space, mono_object_class (obj)->name); */
174         object_register_finalizer (obj, run_finalize);
175 }
176
177 /**
178  * mono_domain_finalize:
179  * @domain: the domain to finalize
180  * @timeout: msects to wait for the finalization to complete
181  *
182  *  Request finalization of all finalizable objects inside @domain. Wait
183  * @timeout msecs for the finalization to complete.
184  *
185  * Returns: TRUE if succeeded, FALSE if there was a timeout
186  */
187
188 gboolean
189 mono_domain_finalize (MonoDomain *domain, guint32 timeout) 
190 {
191         DomainFinalizationReq *req;
192         guint32 res;
193         HANDLE done_event;
194
195         if (mono_thread_current () == gc_thread)
196                 /* We are called from inside a finalizer, not much we can do here */
197                 return FALSE;
198
199         mono_profiler_appdomain_event (domain, MONO_PROFILE_START_UNLOAD);
200
201         /* 
202          * No need to create another thread 'cause the finalizer thread
203          * is still working and will take care of running the finalizers
204          */ 
205         
206 #if HAVE_BOEHM_GC
207         if (gc_disabled)
208                 return TRUE;
209
210         GC_gcollect ();
211
212         done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
213
214         req = g_new0 (DomainFinalizationReq, 1);
215         req->domain = domain;
216         req->done_event = done_event;
217         
218         mono_finalizer_lock ();
219
220         domains_to_finalize = g_slist_append (domains_to_finalize, req);
221
222         mono_finalizer_unlock ();
223
224         /* Tell the finalizer thread to finalize this appdomain */
225         finalize_notify ();
226
227         res = WaitForSingleObjectEx (done_event, timeout, TRUE);
228
229         /* printf ("WAIT RES: %d.\n", res); */
230         if (res == WAIT_TIMEOUT) {
231                 /* We leak the handle here */
232                 return FALSE;
233         }
234
235         CloseHandle (done_event);
236         return TRUE;
237 #else
238         /* We don't support domain finalization without a GC */
239         return FALSE;
240 #endif
241 }
242
243 void
244 ves_icall_System_GC_InternalCollect (int generation)
245 {
246         mono_gc_collect (generation);
247 }
248
249 gint64
250 ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection)
251 {
252         MONO_ARCH_SAVE_REGS;
253
254         if (forceCollection)
255                 mono_gc_collect (mono_gc_max_generation ());
256         return mono_gc_get_used_size ();
257 }
258
259 void
260 ves_icall_System_GC_KeepAlive (MonoObject *obj)
261 {
262         MONO_ARCH_SAVE_REGS;
263
264         /*
265          * Does nothing.
266          */
267 }
268
269 void
270 ves_icall_System_GC_ReRegisterForFinalize (MonoObject *obj)
271 {
272         MONO_ARCH_SAVE_REGS;
273
274         object_register_finalizer (obj, run_finalize);
275 }
276
277 void
278 ves_icall_System_GC_SuppressFinalize (MonoObject *obj)
279 {
280         MONO_ARCH_SAVE_REGS;
281
282         object_register_finalizer (obj, NULL);
283 }
284
285 void
286 ves_icall_System_GC_WaitForPendingFinalizers (void)
287 {
288         MONO_ARCH_SAVE_REGS;
289         
290 #if HAVE_BOEHM_GC
291         if (!GC_should_invoke_finalizers ())
292                 return;
293
294         if (mono_thread_current () == gc_thread)
295                 /* Avoid deadlocks */
296                 return;
297
298         ResetEvent (pending_done_event);
299         finalize_notify ();
300         /* g_print ("Waiting for pending finalizers....\n"); */
301         WaitForSingleObjectEx (pending_done_event, INFINITE, TRUE);
302         /* g_print ("Done pending....\n"); */
303 #else
304 #endif
305 }
306 #define mono_allocator_lock() EnterCriticalSection (&allocator_section)
307 #define mono_allocator_unlock() LeaveCriticalSection (&allocator_section)
308 static CRITICAL_SECTION allocator_section;
309 static CRITICAL_SECTION handle_section;
310
311 typedef enum {
312         HANDLE_WEAK,
313         HANDLE_WEAK_TRACK,
314         HANDLE_NORMAL,
315         HANDLE_PINNED
316 } HandleType;
317
318 static void mono_gchandle_set_target (guint32 gchandle, MonoObject *obj);
319
320 MonoObject *
321 ves_icall_System_GCHandle_GetTarget (guint32 handle)
322 {
323         return mono_gchandle_get_target (handle);
324 }
325
326 /*
327  * if type == -1, change the target of the handle, otherwise allocate a new handle.
328  */
329 guint32
330 ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type)
331 {
332         if (type == -1) {
333                 mono_gchandle_set_target (handle, obj);
334                 /* the handle doesn't change */
335                 return handle;
336         }
337         switch (type) {
338         case HANDLE_WEAK:
339                 return mono_gchandle_new_weakref (obj, FALSE);
340         case HANDLE_WEAK_TRACK:
341                 return mono_gchandle_new_weakref (obj, TRUE);
342         case HANDLE_NORMAL:
343                 return mono_gchandle_new (obj, FALSE);
344         case HANDLE_PINNED:
345                 return mono_gchandle_new (obj, TRUE);
346         default:
347                 g_assert_not_reached ();
348         }
349         return 0;
350 }
351
352 void
353 ves_icall_System_GCHandle_FreeHandle (guint32 handle)
354 {
355         mono_gchandle_free (handle);
356 }
357
358 gpointer
359 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
360 {
361         MonoObject *obj;
362
363         obj = mono_gchandle_get_target (handle);
364         if (obj) {
365                 MonoClass *klass = mono_object_class (obj);
366                 if (klass == mono_defaults.string_class) {
367                         return mono_string_chars ((MonoString*)obj);
368                 } else if (klass->rank) {
369                         return mono_array_addr ((MonoArray*)obj, char, 0);
370                 } else {
371                         /* the C# code will check and throw the exception */
372                         /* FIXME: missing !klass->blittable test, see bug #61134 */
373                         if ((klass->flags & TYPE_ATTRIBUTE_LAYOUT_MASK) == TYPE_ATTRIBUTE_AUTO_LAYOUT)
374                                 return (gpointer)-1;
375                         return (char*)obj + sizeof (MonoObject);
376                 }
377         }
378         return NULL;
379 }
380
381 typedef struct {
382         guint32  *bitmap;
383         gpointer *entries;
384         guint32   size;
385         guint8    type;
386         guint     slot_hint : 24; /* starting slot for search */
387         /* 2^16 appdomains should be enough for everyone (though I know I'll regret this in 20 years) */
388         /* we alloc this only for weak refs, since we can get the domain directly in the other cases */
389         guint16  *domain_ids;
390 } HandleData;
391
392 /* weak and weak-track arrays will be allocated in malloc memory 
393  */
394 static HandleData gc_handles [] = {
395         {NULL, NULL, 0, HANDLE_WEAK, 0},
396         {NULL, NULL, 0, HANDLE_WEAK_TRACK, 0},
397         {NULL, NULL, 0, HANDLE_NORMAL, 0},
398         {NULL, NULL, 0, HANDLE_PINNED, 0}
399 };
400
401 #define lock_handles(handles) EnterCriticalSection (&handle_section)
402 #define unlock_handles(handles) LeaveCriticalSection (&handle_section)
403
404 static int
405 find_first_unset (guint32 bitmap)
406 {
407         int i;
408         for (i = 0; i < 32; ++i) {
409                 if (!(bitmap & (1 << i)))
410                         return i;
411         }
412         return -1;
413 }
414
415 static guint32
416 alloc_handle (HandleData *handles, MonoObject *obj)
417 {
418         gint slot, i;
419         lock_handles (handles);
420         if (!handles->size) {
421                 handles->size = 32;
422                 if (handles->type > HANDLE_WEAK_TRACK) {
423                         handles->entries = mono_gc_alloc_fixed (sizeof (gpointer) * handles->size, NULL);
424                 } else {
425                         handles->entries = g_malloc0 (sizeof (gpointer) * handles->size);
426                         handles->domain_ids = g_malloc0 (sizeof (guint16) * handles->size);
427                 }
428                 handles->bitmap = g_malloc0 (handles->size / 8);
429         }
430         i = -1;
431         for (slot = handles->slot_hint; slot < handles->size / 32; ++slot) {
432                 if (handles->bitmap [slot] != 0xffffffff) {
433                         i = find_first_unset (handles->bitmap [slot]);
434                         handles->slot_hint = slot;
435                         break;
436                 }
437         }
438         if (i == -1 && handles->slot_hint != 0) {
439                 for (slot = 0; slot < handles->slot_hint; ++slot) {
440                         if (handles->bitmap [slot] != 0xffffffff) {
441                                 i = find_first_unset (handles->bitmap [slot]);
442                                 handles->slot_hint = slot;
443                                 break;
444                         }
445                 }
446         }
447         if (i == -1) {
448                 guint32 *new_bitmap;
449                 guint32 new_size = handles->size * 2; /* always double: we memset to 0 based on this below */
450
451                 /* resize and copy the bitmap */
452                 new_bitmap = g_malloc0 (new_size / 8);
453                 memcpy (new_bitmap, handles->bitmap, handles->size / 8);
454                 g_free (handles->bitmap);
455                 handles->bitmap = new_bitmap;
456
457                 /* resize and copy the entries */
458                 if (handles->type > HANDLE_WEAK_TRACK) {
459                         gpointer *entries;
460                         entries = mono_gc_alloc_fixed (sizeof (gpointer) * new_size, NULL);
461                         memcpy (entries, handles->entries, sizeof (gpointer) * handles->size);
462                         handles->entries = entries;
463                 } else {
464                         gpointer *entries;
465                         guint16 *domain_ids;
466                         domain_ids = g_malloc0 (sizeof (guint16) * new_size);
467                         entries = g_malloc (sizeof (gpointer) * new_size);
468                         /* we disable GC because we could lose some disappearing link updates */
469                         mono_gc_disable ();
470                         memcpy (entries, handles->entries, sizeof (gpointer) * handles->size);
471                         memset (entries + handles->size, 0, sizeof (gpointer) * handles->size);
472                         memcpy (domain_ids, handles->domain_ids, sizeof (guint16) * handles->size);
473                         for (i = 0; i < handles->size; ++i) {
474                                 MonoObject *obj = mono_gc_weak_link_get (&(handles->entries [i]));
475                                 mono_gc_weak_link_remove (&(handles->entries [i]));
476                                 /*g_print ("reg/unreg entry %d of type %d at %p to object %p (%p), was: %p\n", i, handles->type, &(entries [i]), obj, entries [i], handles->entries [i]);*/
477                                 if (obj) {
478                                         mono_gc_weak_link_add (&(entries [i]), obj);
479                                 }
480                         }
481                         g_free (handles->entries);
482                         g_free (handles->domain_ids);
483                         handles->entries = entries;
484                         handles->domain_ids = domain_ids;
485                         mono_gc_enable ();
486                 }
487
488                 /* set i and slot to the next free position */
489                 i = 0;
490                 slot = (handles->size + 1) / 32;
491                 handles->slot_hint = handles->size + 1;
492                 handles->size = new_size;
493         }
494         handles->bitmap [slot] |= 1 << i;
495         slot = slot * 32 + i;
496         handles->entries [slot] = obj;
497         if (handles->type <= HANDLE_WEAK_TRACK) {
498                 if (obj)
499                         mono_gc_weak_link_add (&(handles->entries [slot]), obj);
500         }
501
502         unlock_handles (handles);
503         /*g_print ("allocated entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
504         return (slot << 3) | (handles->type + 1);
505 }
506
507 /**
508  * mono_gchandle_new:
509  * @obj: managed object to get a handle for
510  * @pinned: whether the object should be pinned
511  *
512  * This returns a handle that wraps the object, this is used to keep a
513  * reference to a managed object from the unmanaged world and preventing the
514  * object from being disposed.
515  * 
516  * If @pinned is false the address of the object can not be obtained, if it is
517  * true the address of the object can be obtained.  This will also pin the
518  * object so it will not be possible by a moving garbage collector to move the
519  * object. 
520  * 
521  * Returns: a handle that can be used to access the object from
522  * unmanaged code.
523  */
524 guint32
525 mono_gchandle_new (MonoObject *obj, gboolean pinned)
526 {
527         return alloc_handle (&gc_handles [pinned? HANDLE_PINNED: HANDLE_NORMAL], obj);
528 }
529
530 /**
531  * mono_gchandle_new_weakref:
532  * @obj: managed object to get a handle for
533  * @pinned: whether the object should be pinned
534  *
535  * This returns a weak handle that wraps the object, this is used to
536  * keep a reference to a managed object from the unmanaged world.
537  * Unlike the mono_gchandle_new the object can be reclaimed by the
538  * garbage collector.  In this case the value of the GCHandle will be
539  * set to zero.
540  * 
541  * If @pinned is false the address of the object can not be obtained, if it is
542  * true the address of the object can be obtained.  This will also pin the
543  * object so it will not be possible by a moving garbage collector to move the
544  * object. 
545  * 
546  * Returns: a handle that can be used to access the object from
547  * unmanaged code.
548  */
549 guint32
550 mono_gchandle_new_weakref (MonoObject *obj, gboolean track_resurrection)
551 {
552         return alloc_handle (&gc_handles [track_resurrection? HANDLE_WEAK_TRACK: HANDLE_WEAK], obj);
553 }
554
555 /**
556  * mono_gchandle_get_target:
557  * @gchandle: a GCHandle's handle.
558  *
559  * The handle was previously created by calling mono_gchandle_new or
560  * mono_gchandle_new_weakref. 
561  *
562  * Returns a pointer to the MonoObject represented by the handle or
563  * NULL for a collected object if using a weakref handle.
564  */
565 MonoObject*
566 mono_gchandle_get_target (guint32 gchandle)
567 {
568         guint slot = gchandle >> 3;
569         guint type = (gchandle & 7) - 1;
570         HandleData *handles = &gc_handles [type];
571         MonoObject *obj = NULL;
572         if (type > 3)
573                 return NULL;
574         lock_handles (handles);
575         if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
576                 if (handles->type <= HANDLE_WEAK_TRACK) {
577                         obj = mono_gc_weak_link_get (&handles->entries [slot]);
578                 } else {
579                         obj = handles->entries [slot];
580                 }
581         } else {
582                 /* print a warning? */
583         }
584         unlock_handles (handles);
585         /*g_print ("get target of entry %d of type %d: %p\n", slot, handles->type, obj);*/
586         return obj;
587 }
588
589 static void
590 mono_gchandle_set_target (guint32 gchandle, MonoObject *obj)
591 {
592         guint slot = gchandle >> 3;
593         guint type = (gchandle & 7) - 1;
594         HandleData *handles = &gc_handles [type];
595         if (type > 3)
596                 return;
597         lock_handles (handles);
598         if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
599                 if (handles->type <= HANDLE_WEAK_TRACK) {
600                         mono_gc_weak_link_remove (&handles->entries [slot]);
601                         if (obj)
602                                 mono_gc_weak_link_add (&handles->entries [slot], obj);
603                 } else {
604                         handles->entries [slot] = obj;
605                 }
606         } else {
607                 /* print a warning? */
608         }
609         /*g_print ("changed entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
610         unlock_handles (handles);
611 }
612
613 /**
614  * mono_gchandle_is_in_domain:
615  * @gchandle: a GCHandle's handle.
616  * @domain: An application domain.
617  *
618  * Returns: true if the object wrapped by the @gchandle belongs to the specific @domain.
619  */
620 gboolean
621 mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain)
622 {
623         guint slot = gchandle >> 3;
624         guint type = (gchandle & 7) - 1;
625         HandleData *handles = &gc_handles [type];
626         gboolean result = FALSE;
627         if (type > 3)
628                 return FALSE;
629         lock_handles (handles);
630         if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
631                 if (handles->type <= HANDLE_WEAK_TRACK) {
632                         result = domain->domain_id == handles->domain_ids [slot];
633                 } else {
634                         MonoObject *obj;
635                         obj = handles->entries [slot];
636                         if (obj == NULL)
637                                 result = TRUE;
638                         else
639                                 result = domain == mono_object_domain (obj);
640                 }
641         } else {
642                 /* print a warning? */
643         }
644         unlock_handles (handles);
645         return result;
646 }
647
648 /**
649  * mono_gchandle_free:
650  * @gchandle: a GCHandle's handle.
651  *
652  * Frees the @gchandle handle.  If there are no outstanding
653  * references, the garbage collector can reclaim the memory of the
654  * object wrapped. 
655  */
656 void
657 mono_gchandle_free (guint32 gchandle)
658 {
659         guint slot = gchandle >> 3;
660         guint type = (gchandle & 7) - 1;
661         HandleData *handles = &gc_handles [type];
662         if (type > 3)
663                 return;
664         lock_handles (handles);
665         if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
666                 if (handles->type <= HANDLE_WEAK_TRACK)
667                         mono_gc_weak_link_remove (&handles->entries [slot]);
668                 handles->entries [slot] = NULL;
669                 handles->bitmap [slot / 32] &= ~(1 << (slot % 32));
670         } else {
671                 /* print a warning? */
672         }
673         /*g_print ("freed entry %d of type %d\n", slot, handles->type);*/
674         unlock_handles (handles);
675 }
676
677 #if HAVE_BOEHM_GC
678
679 static HANDLE finalizer_event;
680 static volatile gboolean finished=FALSE;
681
682 static void finalize_notify (void)
683 {
684 #ifdef DEBUG
685         g_message (G_GNUC_PRETTY_FUNCTION ": prodding finalizer");
686 #endif
687
688         SetEvent (finalizer_event);
689 }
690
691 static void
692 collect_objects (gpointer key, gpointer value, gpointer user_data)
693 {
694         GPtrArray *arr = (GPtrArray*)user_data;
695         g_ptr_array_add (arr, key);
696 }
697
698 /*
699  * finalize_domain_objects:
700  *
701  *  Run the finalizers of all finalizable objects in req->domain.
702  */
703 static void
704 finalize_domain_objects (DomainFinalizationReq *req)
705 {
706         int i;
707         GPtrArray *objs;
708         MonoDomain *domain = req->domain;
709         
710         while (g_hash_table_size (domain->finalizable_objects_hash) > 0) {
711                 /* 
712                  * Since the domain is unloading, nobody is allowed to put
713                  * new entries into the hash table. But finalize_object might
714                  * remove entries from the hash table, so we make a copy.
715                  */
716                 objs = g_ptr_array_new ();
717                 g_hash_table_foreach (domain->finalizable_objects_hash, 
718                                                           collect_objects, objs);
719                 /* printf ("FINALIZING %d OBJECTS.\n", objs->len); */
720
721                 for (i = 0; i < objs->len; ++i) {
722                         MonoObject *o = (MonoObject*)g_ptr_array_index (objs, i);
723                         /* FIXME: Avoid finalizing threads, etc */
724                         run_finalize (o, 0);
725                 }
726
727                 g_ptr_array_free (objs, TRUE);
728         }
729
730         /* Process finalizers which are already in the queue */
731         GC_invoke_finalizers ();
732
733         /* printf ("DONE.\n"); */
734         SetEvent (req->done_event);
735
736         /* The event is closed in mono_domain_finalize if we get here */
737         g_free (req);
738 }
739
740 static guint32 finalizer_thread (gpointer unused)
741 {
742         gc_thread = mono_thread_current ();
743
744         SetEvent (thread_started_event);
745
746         while(!finished) {
747                 /* Wait to be notified that there's at least one
748                  * finaliser to run
749                  */
750                 WaitForSingleObjectEx (finalizer_event, INFINITE, TRUE);
751
752                 if (domains_to_finalize) {
753                         mono_finalizer_lock ();
754                         if (domains_to_finalize) {
755                                 DomainFinalizationReq *req = domains_to_finalize->data;
756                                 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
757                                 mono_finalizer_unlock ();
758
759                                 finalize_domain_objects (req);
760                         }
761                         else
762                                 mono_finalizer_unlock ();
763                 }                               
764
765 #ifdef DEBUG
766                 g_message (G_GNUC_PRETTY_FUNCTION ": invoking finalizers");
767 #endif
768
769                 /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup),
770                  * before the domain is unloaded.
771                  *
772                  * There is a bug in GC_invoke_finalizer () in versions <= 6.2alpha4:
773                  * the 'mem_freed' variable is not initialized when there are no
774                  * objects to finalize, which leads to strange behavior later on.
775                  * The check is necessary to work around that bug.
776                  */
777                 if (GC_should_invoke_finalizers ()) {
778                         GC_invoke_finalizers ();
779                 }
780
781                 SetEvent (pending_done_event);
782         }
783
784         SetEvent (shutdown_event);
785         return(0);
786 }
787
788 /* 
789  * Enable or disable the separate finalizer thread.
790  * It's currently disabled because it still requires some
791  * work in the rest of the runtime.
792  */
793 #define ENABLE_FINALIZER_THREAD
794
795 #ifdef WITH_INCLUDED_LIBGC
796 /* from threads.c */
797 extern void mono_gc_stop_world (void);
798 extern void mono_gc_start_world (void);
799 extern void mono_gc_push_all_stacks (void);
800
801 static void mono_gc_lock (void)
802 {
803         mono_allocator_lock ();
804 }
805
806 static void mono_gc_unlock (void)
807 {
808         mono_allocator_unlock ();
809 }
810
811 static GCThreadFunctions mono_gc_thread_vtable = {
812         NULL,
813
814         mono_gc_lock,
815         mono_gc_unlock,
816
817         mono_gc_stop_world,
818         NULL,
819         mono_gc_push_all_stacks,
820         mono_gc_start_world
821 };
822 #endif /* WITH_INCLUDED_LIBGC */
823
824 static void
825 mono_gc_warning (char *msg, GC_word arg)
826 {
827         mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_GC, msg, (unsigned long)arg);
828 }
829
830 void mono_gc_init (void)
831 {
832         InitializeCriticalSection (&handle_section);
833         InitializeCriticalSection (&allocator_section);
834
835         InitializeCriticalSection (&finalizer_mutex);
836
837 #ifdef WITH_INCLUDED_LIBGC
838         gc_thread_vtable = &mono_gc_thread_vtable;
839 #endif
840         
841         MONO_GC_REGISTER_ROOT (gc_handles [HANDLE_NORMAL].entries);
842         MONO_GC_REGISTER_ROOT (gc_handles [HANDLE_PINNED].entries);
843         GC_no_dls = TRUE;
844
845         GC_oom_fn = mono_gc_out_of_memory;
846
847         GC_set_warn_proc (mono_gc_warning);
848
849 #ifdef ENABLE_FINALIZER_THREAD
850
851         if (g_getenv ("GC_DONT_GC")) {
852                 gc_disabled = TRUE;
853                 return;
854         }
855         
856         finalizer_event = CreateEvent (NULL, FALSE, FALSE, NULL);
857         pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
858         shutdown_event = CreateEvent (NULL, TRUE, FALSE, NULL);
859         thread_started_event = CreateEvent (NULL, TRUE, FALSE, NULL);
860         if (finalizer_event == NULL || pending_done_event == NULL || shutdown_event == NULL || thread_started_event == NULL) {
861                 g_assert_not_reached ();
862         }
863
864         GC_finalize_on_demand = 1;
865         GC_finalizer_notifier = finalize_notify;
866
867         mono_thread_create (mono_domain_get (), finalizer_thread, NULL);
868         /*
869          * Wait until the finalizer thread sets gc_thread since its value is needed
870          * by mono_thread_attach ()
871          */
872         WaitForSingleObjectEx (thread_started_event, INFINITE, FALSE);
873 #endif
874 }
875
876 void mono_gc_cleanup (void)
877 {
878 #ifdef DEBUG
879         g_message (G_GNUC_PRETTY_FUNCTION ": cleaning up finalizer");
880 #endif
881
882 #ifdef ENABLE_FINALIZER_THREAD
883         if (!gc_disabled) {
884                 ResetEvent (shutdown_event);
885                 finished = TRUE;
886                 if (mono_thread_current () != gc_thread) {
887                         finalize_notify ();
888                         /* Finishing the finalizer thread, so wait a little bit... */
889                         /* MS seems to wait for about 2 seconds */
890                         if (WaitForSingleObjectEx (shutdown_event, 2000, FALSE) == WAIT_TIMEOUT) {
891                                 mono_thread_stop (gc_thread);
892                         }
893                 }
894                 gc_thread = NULL;
895                 GC_finalizer_notifier = NULL;
896         }
897
898 #endif
899 }
900
901 #else
902
903 /* no Boehm GC support. */
904 void mono_gc_init (void)
905 {
906         InitializeCriticalSection (&handle_section);
907 }
908
909 void mono_gc_cleanup (void)
910 {
911 }
912
913 #endif
914
915 /**
916  * mono_gc_is_finalizer_thread:
917  * @thread: the thread to test.
918  *
919  * In Mono objects are finalized asynchronously on a separate thread.
920  * This routine tests whether the @thread argument represents the
921  * finalization thread.
922  * 
923  * Returns true if @thread is the finalization thread.
924  */
925 gboolean
926 mono_gc_is_finalizer_thread (MonoThread *thread)
927 {
928         return thread == gc_thread;
929 }
930
931