2005-05-13 Atsushi Enomoto <atsushi@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                 mono_gc_weak_link_add (&(handles->entries [slot]), obj);
485         }
486
487         unlock_handles (handles);
488         /*g_print ("allocated entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
489         return (slot << 3) | (handles->type + 1);
490 }
491
492 guint32
493 mono_gchandle_new (MonoObject *obj, gboolean pinned)
494 {
495         return alloc_handle (&gc_handles [pinned? HANDLE_PINNED: HANDLE_NORMAL], obj);
496 }
497
498 guint32
499 mono_gchandle_new_weakref (MonoObject *obj, gboolean track_resurrection)
500 {
501         return alloc_handle (&gc_handles [track_resurrection? HANDLE_WEAK_TRACK: HANDLE_WEAK], obj);
502 }
503
504 /* This will return NULL for a collected object if using a weakref handle */
505 MonoObject*
506 mono_gchandle_get_target (guint32 gchandle)
507 {
508         guint slot = gchandle >> 3;
509         guint type = (gchandle & 7) - 1;
510         HandleData *handles = &gc_handles [type];
511         MonoObject *obj = NULL;
512         if (type > 3)
513                 return NULL;
514         lock_handles (handles);
515         if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
516                 if (handles->type <= HANDLE_WEAK_TRACK) {
517                         obj = mono_gc_weak_link_get (&handles->entries [slot]);
518                 } else {
519                         obj = handles->entries [slot];
520                 }
521         } else {
522                 /* print a warning? */
523         }
524         unlock_handles (handles);
525         /*g_print ("get target of entry %d of type %d: %p\n", slot, handles->type, obj);*/
526         return obj;
527 }
528
529 static void
530 mono_gchandle_set_target (guint32 gchandle, MonoObject *obj)
531 {
532         guint slot = gchandle >> 3;
533         guint type = (gchandle & 7) - 1;
534         HandleData *handles = &gc_handles [type];
535         if (type > 3)
536                 return;
537         lock_handles (handles);
538         if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
539                 if (handles->type <= HANDLE_WEAK_TRACK) {
540                         mono_gc_weak_link_remove (&handles->entries [slot]);
541                         mono_gc_weak_link_add (&handles->entries [slot], obj);
542                 } else {
543                         handles->entries [slot] = obj;
544                 }
545         } else {
546                 /* print a warning? */
547         }
548         /*g_print ("changed entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
549         unlock_handles (handles);
550 }
551
552 gboolean
553 mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain)
554 {
555         guint slot = gchandle >> 3;
556         guint type = (gchandle & 7) - 1;
557         HandleData *handles = &gc_handles [type];
558         gboolean result = FALSE;
559         if (type > 3)
560                 return FALSE;
561         lock_handles (handles);
562         if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
563                 if (handles->type <= HANDLE_WEAK_TRACK) {
564                         result = domain->domain_id == handles->domain_ids [slot];
565                 } else {
566                         MonoObject *obj;
567                         obj = handles->entries [slot];
568                         if (obj == NULL)
569                                 result = TRUE;
570                         else
571                                 result = domain == mono_object_domain (obj);
572                 }
573         } else {
574                 /* print a warning? */
575         }
576         unlock_handles (handles);
577         return result;
578 }
579
580 void
581 mono_gchandle_free (guint32 gchandle)
582 {
583         guint slot = gchandle >> 3;
584         guint type = (gchandle & 7) - 1;
585         HandleData *handles = &gc_handles [type];
586         if (type > 3)
587                 return;
588         lock_handles (handles);
589         if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
590                 if (handles->type <= HANDLE_WEAK_TRACK)
591                         mono_gc_weak_link_remove (&handles->entries [slot]);
592                 handles->entries [slot] = NULL;
593                 handles->bitmap [slot / 32] &= ~(1 << (slot % 32));
594         } else {
595                 /* print a warning? */
596         }
597         /*g_print ("freed entry %d of type %d\n", slot, handles->type);*/
598         unlock_handles (handles);
599 }
600
601 #if HAVE_BOEHM_GC
602
603 static HANDLE finalizer_event;
604 static volatile gboolean finished=FALSE;
605
606 static void finalize_notify (void)
607 {
608 #ifdef DEBUG
609         g_message (G_GNUC_PRETTY_FUNCTION ": prodding finalizer");
610 #endif
611
612         SetEvent (finalizer_event);
613 }
614
615 static void
616 collect_objects (gpointer key, gpointer value, gpointer user_data)
617 {
618         GPtrArray *arr = (GPtrArray*)user_data;
619         g_ptr_array_add (arr, key);
620 }
621
622 /*
623  * finalize_domain_objects:
624  *
625  *  Run the finalizers of all finalizable objects in req->domain.
626  */
627 static void
628 finalize_domain_objects (DomainFinalizationReq *req)
629 {
630         int i;
631         GPtrArray *objs;
632         MonoDomain *domain = req->domain;
633         
634         while (g_hash_table_size (domain->finalizable_objects_hash) > 0) {
635                 /* 
636                  * Since the domain is unloading, nobody is allowed to put
637                  * new entries into the hash table. But finalize_object might
638                  * remove entries from the hash table, so we make a copy.
639                  */
640                 objs = g_ptr_array_new ();
641                 g_hash_table_foreach (domain->finalizable_objects_hash, 
642                                                           collect_objects, objs);
643                 /* printf ("FINALIZING %d OBJECTS.\n", objs->len); */
644
645                 for (i = 0; i < objs->len; ++i) {
646                         MonoObject *o = (MonoObject*)g_ptr_array_index (objs, i);
647                         /* FIXME: Avoid finalizing threads, etc */
648                         run_finalize (o, 0);
649                 }
650
651                 g_ptr_array_free (objs, TRUE);
652         }
653
654         /* Process finalizers which are already in the queue */
655         GC_invoke_finalizers ();
656
657         /* printf ("DONE.\n"); */
658         SetEvent (req->done_event);
659
660         /* The event is closed in mono_domain_finalize if we get here */
661         g_free (req);
662 }
663
664 static guint32 finalizer_thread (gpointer unused)
665 {
666         gc_thread = mono_thread_current ();
667
668         SetEvent (thread_started_event);
669
670         while(!finished) {
671                 /* Wait to be notified that there's at least one
672                  * finaliser to run
673                  */
674                 WaitForSingleObjectEx (finalizer_event, INFINITE, TRUE);
675
676                 if (domains_to_finalize) {
677                         EnterCriticalSection (&finalizer_mutex);
678                         if (domains_to_finalize) {
679                                 DomainFinalizationReq *req = domains_to_finalize->data;
680                                 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
681                                 LeaveCriticalSection (&finalizer_mutex);
682
683                                 finalize_domain_objects (req);
684                         }
685                         else
686                                 LeaveCriticalSection (&finalizer_mutex);
687                 }                               
688
689 #ifdef DEBUG
690                 g_message (G_GNUC_PRETTY_FUNCTION ": invoking finalizers");
691 #endif
692
693                 /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup),
694                  * before the domain is unloaded.
695                  *
696                  * There is a bug in GC_invoke_finalizer () in versions <= 6.2alpha4:
697                  * the 'mem_freed' variable is not initialized when there are no
698                  * objects to finalize, which leads to strange behavior later on.
699                  * The check is necessary to work around that bug.
700                  */
701                 if (GC_should_invoke_finalizers ()) {
702                         GC_invoke_finalizers ();
703                 }
704
705                 SetEvent (pending_done_event);
706         }
707
708         SetEvent (shutdown_event);
709         return(0);
710 }
711
712 /* 
713  * Enable or disable the separate finalizer thread.
714  * It's currently disabled because it still requires some
715  * work in the rest of the runtime.
716  */
717 #define ENABLE_FINALIZER_THREAD
718
719 #ifdef WITH_INCLUDED_LIBGC
720 /* from threads.c */
721 extern void mono_gc_stop_world (void);
722 extern void mono_gc_start_world (void);
723 extern void mono_gc_push_all_stacks (void);
724
725 static void mono_gc_lock (void)
726 {
727         EnterCriticalSection (&allocator_section);
728 }
729
730 static void mono_gc_unlock (void)
731 {
732         LeaveCriticalSection (&allocator_section);
733 }
734
735 static GCThreadFunctions mono_gc_thread_vtable = {
736         NULL,
737
738         mono_gc_lock,
739         mono_gc_unlock,
740
741         mono_gc_stop_world,
742         NULL,
743         mono_gc_push_all_stacks,
744         mono_gc_start_world
745 };
746 #endif /* WITH_INCLUDED_LIBGC */
747
748 static void
749 mono_gc_warning (char *msg, GC_word arg)
750 {
751         mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_GC, msg, (unsigned long)arg);
752 }
753
754 void mono_gc_init (void)
755 {
756         InitializeCriticalSection (&handle_section);
757         InitializeCriticalSection (&allocator_section);
758
759         InitializeCriticalSection (&finalizer_mutex);
760
761 #ifdef WITH_INCLUDED_LIBGC
762         gc_thread_vtable = &mono_gc_thread_vtable;
763 #endif
764         
765         MONO_GC_REGISTER_ROOT (gc_handles [HANDLE_NORMAL].entries);
766         MONO_GC_REGISTER_ROOT (gc_handles [HANDLE_PINNED].entries);
767         GC_no_dls = TRUE;
768
769         GC_oom_fn = mono_gc_out_of_memory;
770
771         GC_set_warn_proc (mono_gc_warning);
772
773 #ifdef ENABLE_FINALIZER_THREAD
774
775         if (g_getenv ("GC_DONT_GC")) {
776                 gc_disabled = TRUE;
777                 return;
778         }
779         
780         finalizer_event = CreateEvent (NULL, FALSE, FALSE, NULL);
781         pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
782         shutdown_event = CreateEvent (NULL, TRUE, FALSE, NULL);
783         thread_started_event = CreateEvent (NULL, TRUE, FALSE, NULL);
784         if (finalizer_event == NULL || pending_done_event == NULL || shutdown_event == NULL || thread_started_event == NULL) {
785                 g_assert_not_reached ();
786         }
787
788         GC_finalize_on_demand = 1;
789         GC_finalizer_notifier = finalize_notify;
790
791         mono_thread_create (mono_domain_get (), finalizer_thread, NULL);
792         /*
793          * Wait until the finalizer thread sets gc_thread since its value is needed
794          * by mono_thread_attach ()
795          */
796         WaitForSingleObjectEx (thread_started_event, INFINITE, FALSE);
797 #endif
798 }
799
800 void mono_gc_cleanup (void)
801 {
802 #ifdef DEBUG
803         g_message (G_GNUC_PRETTY_FUNCTION ": cleaning up finalizer");
804 #endif
805
806 #ifdef ENABLE_FINALIZER_THREAD
807         if (!gc_disabled) {
808                 ResetEvent (shutdown_event);
809                 finished = TRUE;
810                 if (mono_thread_current () != gc_thread) {
811                         finalize_notify ();
812                         /* Finishing the finalizer thread, so wait a little bit... */
813                         /* MS seems to wait for about 2 seconds */
814                         if (WaitForSingleObjectEx (shutdown_event, 2000, FALSE) == WAIT_TIMEOUT) {
815                                 mono_thread_stop (gc_thread);
816                         }
817                 }
818                 gc_thread = NULL;
819                 GC_finalizer_notifier = NULL;
820         }
821
822 #endif
823 }
824
825 #else
826
827 /* no Boehm GC support. */
828 void mono_gc_init (void)
829 {
830         InitializeCriticalSection (&handle_section);
831 }
832
833 void mono_gc_cleanup (void)
834 {
835 }
836
837 #endif
838
839 gboolean
840 mono_gc_is_finalizer_thread (MonoThread *thread)
841 {
842         return thread == gc_thread;
843 }
844
845