- Trying to get our CHangeLog back
[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                 else
669                         handles->entries [slot] = NULL;
670                 handles->bitmap [slot / 32] &= ~(1 << (slot % 32));
671         } else {
672                 /* print a warning? */
673         }
674         /*g_print ("freed entry %d of type %d\n", slot, handles->type);*/
675         unlock_handles (handles);
676 }
677
678 #if HAVE_BOEHM_GC
679
680 static HANDLE finalizer_event;
681 static volatile gboolean finished=FALSE;
682
683 static void finalize_notify (void)
684 {
685 #ifdef DEBUG
686         g_message (G_GNUC_PRETTY_FUNCTION ": prodding finalizer");
687 #endif
688
689         SetEvent (finalizer_event);
690 }
691
692 static void
693 collect_objects (gpointer key, gpointer value, gpointer user_data)
694 {
695         GPtrArray *arr = (GPtrArray*)user_data;
696         g_ptr_array_add (arr, key);
697 }
698
699 /*
700  * finalize_domain_objects:
701  *
702  *  Run the finalizers of all finalizable objects in req->domain.
703  */
704 static void
705 finalize_domain_objects (DomainFinalizationReq *req)
706 {
707         int i;
708         GPtrArray *objs;
709         MonoDomain *domain = req->domain;
710         
711         while (g_hash_table_size (domain->finalizable_objects_hash) > 0) {
712                 /* 
713                  * Since the domain is unloading, nobody is allowed to put
714                  * new entries into the hash table. But finalize_object might
715                  * remove entries from the hash table, so we make a copy.
716                  */
717                 objs = g_ptr_array_new ();
718                 g_hash_table_foreach (domain->finalizable_objects_hash, 
719                                                           collect_objects, objs);
720                 /* printf ("FINALIZING %d OBJECTS.\n", objs->len); */
721
722                 for (i = 0; i < objs->len; ++i) {
723                         MonoObject *o = (MonoObject*)g_ptr_array_index (objs, i);
724                         /* FIXME: Avoid finalizing threads, etc */
725                         run_finalize (o, 0);
726                 }
727
728                 g_ptr_array_free (objs, TRUE);
729         }
730
731         /* Process finalizers which are already in the queue */
732         GC_invoke_finalizers ();
733
734         /* printf ("DONE.\n"); */
735         SetEvent (req->done_event);
736
737         /* The event is closed in mono_domain_finalize if we get here */
738         g_free (req);
739 }
740
741 static guint32 finalizer_thread (gpointer unused)
742 {
743         gc_thread = mono_thread_current ();
744
745         SetEvent (thread_started_event);
746
747         while(!finished) {
748                 /* Wait to be notified that there's at least one
749                  * finaliser to run
750                  */
751                 WaitForSingleObjectEx (finalizer_event, INFINITE, TRUE);
752
753                 if (domains_to_finalize) {
754                         mono_finalizer_lock ();
755                         if (domains_to_finalize) {
756                                 DomainFinalizationReq *req = domains_to_finalize->data;
757                                 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
758                                 mono_finalizer_unlock ();
759
760                                 finalize_domain_objects (req);
761                         }
762                         else
763                                 mono_finalizer_unlock ();
764                 }                               
765
766 #ifdef DEBUG
767                 g_message (G_GNUC_PRETTY_FUNCTION ": invoking finalizers");
768 #endif
769
770                 /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup),
771                  * before the domain is unloaded.
772                  *
773                  * There is a bug in GC_invoke_finalizer () in versions <= 6.2alpha4:
774                  * the 'mem_freed' variable is not initialized when there are no
775                  * objects to finalize, which leads to strange behavior later on.
776                  * The check is necessary to work around that bug.
777                  */
778                 if (GC_should_invoke_finalizers ()) {
779                         GC_invoke_finalizers ();
780                 }
781
782                 SetEvent (pending_done_event);
783         }
784
785         SetEvent (shutdown_event);
786         return(0);
787 }
788
789 /* 
790  * Enable or disable the separate finalizer thread.
791  * It's currently disabled because it still requires some
792  * work in the rest of the runtime.
793  */
794 #define ENABLE_FINALIZER_THREAD
795
796 static void
797 mono_gc_warning (char *msg, GC_word arg)
798 {
799         mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_GC, msg, (unsigned long)arg);
800 }
801
802 void mono_gc_init (void)
803 {
804         InitializeCriticalSection (&handle_section);
805         InitializeCriticalSection (&allocator_section);
806
807         InitializeCriticalSection (&finalizer_mutex);
808
809         MONO_GC_REGISTER_ROOT (gc_handles [HANDLE_NORMAL].entries);
810         MONO_GC_REGISTER_ROOT (gc_handles [HANDLE_PINNED].entries);
811         GC_no_dls = TRUE;
812
813         GC_oom_fn = mono_gc_out_of_memory;
814
815         GC_set_warn_proc (mono_gc_warning);
816
817 #ifdef ENABLE_FINALIZER_THREAD
818
819         if (g_getenv ("GC_DONT_GC")) {
820                 gc_disabled = TRUE;
821                 return;
822         }
823         
824         finalizer_event = CreateEvent (NULL, FALSE, FALSE, NULL);
825         pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
826         shutdown_event = CreateEvent (NULL, TRUE, FALSE, NULL);
827         thread_started_event = CreateEvent (NULL, TRUE, FALSE, NULL);
828         if (finalizer_event == NULL || pending_done_event == NULL || shutdown_event == NULL || thread_started_event == NULL) {
829                 g_assert_not_reached ();
830         }
831
832         GC_finalize_on_demand = 1;
833         GC_finalizer_notifier = finalize_notify;
834
835         mono_thread_create (mono_domain_get (), finalizer_thread, NULL);
836         /*
837          * Wait until the finalizer thread sets gc_thread since its value is needed
838          * by mono_thread_attach ()
839          */
840         WaitForSingleObjectEx (thread_started_event, INFINITE, FALSE);
841 #endif
842 }
843
844 void mono_gc_cleanup (void)
845 {
846 #ifdef DEBUG
847         g_message (G_GNUC_PRETTY_FUNCTION ": cleaning up finalizer");
848 #endif
849
850 #ifdef ENABLE_FINALIZER_THREAD
851         if (!gc_disabled) {
852                 ResetEvent (shutdown_event);
853                 finished = TRUE;
854                 if (mono_thread_current () != gc_thread) {
855                         finalize_notify ();
856                         /* Finishing the finalizer thread, so wait a little bit... */
857                         /* MS seems to wait for about 2 seconds */
858                         if (WaitForSingleObjectEx (shutdown_event, 2000, FALSE) == WAIT_TIMEOUT) {
859                                 mono_thread_stop (gc_thread);
860                         }
861                 }
862                 gc_thread = NULL;
863                 GC_finalizer_notifier = NULL;
864         }
865
866 #endif
867 }
868
869 #else
870
871 /* no Boehm GC support. */
872 void mono_gc_init (void)
873 {
874         InitializeCriticalSection (&handle_section);
875 }
876
877 void mono_gc_cleanup (void)
878 {
879 }
880
881 #endif
882
883 /**
884  * mono_gc_is_finalizer_thread:
885  * @thread: the thread to test.
886  *
887  * In Mono objects are finalized asynchronously on a separate thread.
888  * This routine tests whether the @thread argument represents the
889  * finalization thread.
890  * 
891  * Returns true if @thread is the finalization thread.
892  */
893 gboolean
894 mono_gc_is_finalizer_thread (MonoThread *thread)
895 {
896         return thread == gc_thread;
897 }
898
899