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