[runtime] Implement the GC domain finalization event using conditional variables...
[mono.git] / mono / metadata / gc.c
1 /*
2  * metadata/gc.c: GC icalls.
3  *
4  * Author: Paolo Molaro <lupus@ximian.com>
5  *
6  * Copyright 2002-2003 Ximian, Inc (http://www.ximian.com)
7  * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
8  * Copyright 2012 Xamarin Inc (http://www.xamarin.com)
9  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
10  */
11
12 #include <config.h>
13 #include <glib.h>
14 #include <string.h>
15
16 #include <mono/metadata/gc-internals.h>
17 #include <mono/metadata/mono-gc.h>
18 #include <mono/metadata/threads.h>
19 #include <mono/metadata/tabledefs.h>
20 #include <mono/metadata/exception.h>
21 #include <mono/metadata/profiler-private.h>
22 #include <mono/metadata/domain-internals.h>
23 #include <mono/metadata/class-internals.h>
24 #include <mono/metadata/metadata-internals.h>
25 #include <mono/metadata/mono-mlist.h>
26 #include <mono/metadata/threads-types.h>
27 #include <mono/metadata/threadpool-ms.h>
28 #include <mono/sgen/sgen-conf.h>
29 #include <mono/sgen/sgen-gc.h>
30 #include <mono/utils/mono-logger-internals.h>
31 #include <mono/metadata/gc-internals.h>
32 #include <mono/metadata/marshal.h> /* for mono_delegate_free_ftnptr () */
33 #include <mono/metadata/attach.h>
34 #include <mono/metadata/console-io.h>
35 #include <mono/utils/mono-os-semaphore.h>
36 #include <mono/utils/mono-memory-model.h>
37 #include <mono/utils/mono-counters.h>
38 #include <mono/utils/mono-time.h>
39 #include <mono/utils/dtrace.h>
40 #include <mono/utils/mono-threads.h>
41 #include <mono/utils/mono-threads-coop.h>
42 #include <mono/utils/atomic.h>
43 #include <mono/utils/mono-coop-semaphore.h>
44 #include <mono/utils/hazard-pointer.h>
45
46 #ifndef HOST_WIN32
47 #include <pthread.h>
48 #endif
49
50 typedef struct DomainFinalizationReq {
51         MonoDomain *domain;
52 #ifdef TARGET_WIN32
53         HANDLE done_event;
54 #else
55         gboolean done;
56         MonoCoopCond cond;
57         MonoCoopMutex mutex;
58 #endif
59 } DomainFinalizationReq;
60
61 static gboolean gc_disabled = FALSE;
62
63 static gboolean finalizing_root_domain = FALSE;
64
65 gboolean log_finalizers = FALSE;
66 gboolean mono_do_not_finalize = FALSE;
67 gchar **mono_do_not_finalize_class_names = NULL;
68
69 #define mono_finalizer_lock() mono_coop_mutex_lock (&finalizer_mutex)
70 #define mono_finalizer_unlock() mono_coop_mutex_unlock (&finalizer_mutex)
71 static MonoCoopMutex finalizer_mutex;
72 static MonoCoopMutex reference_queue_mutex;
73
74 static GSList *domains_to_finalize= NULL;
75 static MonoMList *threads_to_finalize = NULL;
76
77 static gboolean finalizer_thread_exited;
78 /* Uses finalizer_mutex */
79 static MonoCoopCond exited_cond;
80
81 static MonoInternalThread *gc_thread;
82
83 static void object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*));
84
85 static void reference_queue_proccess_all (void);
86 static void mono_reference_queue_cleanup (void);
87 static void reference_queue_clear_for_domain (MonoDomain *domain);
88 static HANDLE pending_done_event;
89
90 static guint32
91 guarded_wait (HANDLE handle, guint32 timeout, gboolean alertable)
92 {
93         guint32 result;
94
95         MONO_ENTER_GC_SAFE;
96         result = WaitForSingleObjectEx (handle, timeout, alertable);
97         MONO_EXIT_GC_SAFE;
98
99         return result;
100 }
101
102 typedef struct {
103         MonoCoopCond *cond;
104         MonoCoopMutex *mutex;
105 } BreakCoopAlertableWaitUD;
106
107 static inline void
108 break_coop_alertable_wait (gpointer user_data)
109 {
110         BreakCoopAlertableWaitUD *ud = (BreakCoopAlertableWaitUD*)user_data;
111
112         mono_coop_mutex_lock (ud->mutex);
113         mono_coop_cond_signal (ud->cond);
114         mono_coop_mutex_unlock (ud->mutex);
115 }
116
117 /*
118  * coop_cond_timedwait_alertable:
119  *
120  *   Wait on COND/MUTEX. If ALERTABLE is non-null, the wait can be interrupted.
121  * In that case, *ALERTABLE will be set to TRUE, and 0 is returned.
122  */
123 static inline gint
124 coop_cond_timedwait_alertable (MonoCoopCond *cond, MonoCoopMutex *mutex, guint32 timeout_ms, gboolean *alertable)
125 {
126         int res;
127
128         if (alertable) {
129                 BreakCoopAlertableWaitUD ud;
130
131                 *alertable = FALSE;
132                 ud.cond = cond;
133                 ud.mutex = mutex;
134                 mono_thread_info_install_interrupt (break_coop_alertable_wait, &ud, alertable);
135                 if (*alertable)
136                         return 0;
137         }
138         res = mono_coop_cond_timedwait (cond, mutex, timeout_ms);
139         if (alertable) {
140                 mono_thread_info_uninstall_interrupt (alertable);
141                 if (*alertable)
142                         return 0;
143         }
144         return res;
145 }
146
147 static gboolean
148 add_thread_to_finalize (MonoInternalThread *thread, MonoError *error)
149 {
150         mono_error_init (error);
151         mono_finalizer_lock ();
152         if (!threads_to_finalize)
153                 MONO_GC_REGISTER_ROOT_SINGLE (threads_to_finalize, MONO_ROOT_SOURCE_FINALIZER_QUEUE, "finalizable threads list");
154         threads_to_finalize = mono_mlist_append_checked (threads_to_finalize, (MonoObject*)thread, error);
155         mono_finalizer_unlock ();
156         return is_ok (error);
157 }
158
159 static volatile gboolean suspend_finalizers = FALSE;
160 /* 
161  * actually, we might want to queue the finalize requests in a separate thread,
162  * but we need to be careful about the execution domain of the thread...
163  */
164 void
165 mono_gc_run_finalize (void *obj, void *data)
166 {
167         MonoError error;
168         MonoObject *exc = NULL;
169         MonoObject *o;
170 #ifndef HAVE_SGEN_GC
171         MonoObject *o2;
172 #endif
173         MonoMethod* finalizer = NULL;
174         MonoDomain *caller_domain = mono_domain_get ();
175         MonoDomain *domain;
176         RuntimeInvokeFunction runtime_invoke;
177
178         // This function is called from the innards of the GC, so our best alternative for now is to do polling here
179         mono_threads_safepoint ();
180
181         o = (MonoObject*)((char*)obj + GPOINTER_TO_UINT (data));
182
183         if (mono_do_not_finalize) {
184                 if (!mono_do_not_finalize_class_names)
185                         return;
186
187                 size_t namespace_len = strlen (o->vtable->klass->name_space);
188                 for (int i = 0; mono_do_not_finalize_class_names [i]; ++i) {
189                         const char *name = mono_do_not_finalize_class_names [i];
190                         if (strncmp (name, o->vtable->klass->name_space, namespace_len))
191                                 break;
192                         if (name [namespace_len] != '.')
193                                 break;
194                         if (strcmp (name + namespace_len + 1, o->vtable->klass->name))
195                                 break;
196                         return;
197                 }
198         }
199
200         if (log_finalizers)
201                 g_log ("mono-gc-finalizers", G_LOG_LEVEL_DEBUG, "<%s at %p> Starting finalizer checks.", o->vtable->klass->name, o);
202
203         if (suspend_finalizers)
204                 return;
205
206         domain = o->vtable->domain;
207
208 #ifndef HAVE_SGEN_GC
209         mono_domain_finalizers_lock (domain);
210
211         o2 = (MonoObject *)g_hash_table_lookup (domain->finalizable_objects_hash, o);
212
213         mono_domain_finalizers_unlock (domain);
214
215         if (!o2)
216                 /* Already finalized somehow */
217                 return;
218 #endif
219
220         /* make sure the finalizer is not called again if the object is resurrected */
221         object_register_finalizer ((MonoObject *)obj, NULL);
222
223         if (log_finalizers)
224                 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Registered finalizer as processed.", o->vtable->klass->name, o);
225
226         if (o->vtable->klass == mono_defaults.internal_thread_class) {
227                 MonoInternalThread *t = (MonoInternalThread*)o;
228
229                 if (mono_gc_is_finalizer_internal_thread (t))
230                         /* Avoid finalizing ourselves */
231                         return;
232
233                 if (t->threadpool_thread && finalizing_root_domain) {
234                         /* Don't finalize threadpool threads when
235                            shutting down - they're finalized when the
236                            threadpool shuts down. */
237                         if (!add_thread_to_finalize (t, &error))
238                                 goto unhandled_error;
239                         return;
240                 }
241         }
242
243         if (o->vtable->klass->image == mono_defaults.corlib && !strcmp (o->vtable->klass->name, "DynamicMethod") && finalizing_root_domain) {
244                 /*
245                  * These can't be finalized during unloading/shutdown, since that would
246                  * free the native code which can still be referenced by other
247                  * finalizers.
248                  * FIXME: This is not perfect, objects dying at the same time as 
249                  * dynamic methods can still reference them even when !shutdown.
250                  */
251                 return;
252         }
253
254         if (mono_runtime_get_no_exec ())
255                 return;
256
257         /* speedup later... and use a timeout */
258         /* g_print ("Finalize run on %p %s.%s\n", o, mono_object_class (o)->name_space, mono_object_class (o)->name); */
259
260         /* Use _internal here, since this thread can enter a doomed appdomain */
261         mono_domain_set_internal (mono_object_domain (o));
262
263         /* delegates that have a native function pointer allocated are
264          * registered for finalization, but they don't have a Finalize
265          * method, because in most cases it's not needed and it's just a waste.
266          */
267         if (o->vtable->klass->delegate) {
268                 MonoDelegate* del = (MonoDelegate*)o;
269                 if (del->delegate_trampoline)
270                         mono_delegate_free_ftnptr ((MonoDelegate*)o);
271                 mono_domain_set_internal (caller_domain);
272                 return;
273         }
274
275         finalizer = mono_class_get_finalizer (o->vtable->klass);
276
277         /* If object has a CCW but has no finalizer, it was only
278          * registered for finalization in order to free the CCW.
279          * Else it needs the regular finalizer run.
280          * FIXME: what to do about ressurection and suppression
281          * of finalizer on object with CCW.
282          */
283         if (mono_marshal_free_ccw (o) && !finalizer) {
284                 mono_domain_set_internal (caller_domain);
285                 return;
286         }
287
288         /* 
289          * To avoid the locking plus the other overhead of mono_runtime_invoke_checked (),
290          * create and precompile a wrapper which calls the finalize method using
291          * a CALLVIRT.
292          */
293         if (log_finalizers)
294                 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Compiling finalizer.", o->vtable->klass->name, o);
295
296         if (!domain->finalize_runtime_invoke) {
297                 MonoMethod *invoke = mono_marshal_get_runtime_invoke (mono_class_get_method_from_name_flags (mono_defaults.object_class, "Finalize", 0, 0), TRUE);
298
299                 domain->finalize_runtime_invoke = mono_compile_method_checked (invoke, &error);
300                 mono_error_assert_ok (&error); /* expect this not to fail */
301         }
302
303         runtime_invoke = (RuntimeInvokeFunction)domain->finalize_runtime_invoke;
304
305         mono_runtime_class_init_full (o->vtable, &error);
306         if (!is_ok (&error))
307                 goto unhandled_error;
308
309         if (G_UNLIKELY (MONO_GC_FINALIZE_INVOKE_ENABLED ())) {
310                 MONO_GC_FINALIZE_INVOKE ((unsigned long)o, mono_object_get_size (o),
311                                 o->vtable->klass->name_space, o->vtable->klass->name);
312         }
313
314         if (log_finalizers)
315                 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Calling finalizer.", o->vtable->klass->name, o);
316
317         runtime_invoke (o, NULL, &exc, NULL);
318
319         if (log_finalizers)
320                 g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Returned from finalizer.", o->vtable->klass->name, o);
321
322 unhandled_error:
323         if (!is_ok (&error))
324                 exc = (MonoObject*)mono_error_convert_to_exception (&error);
325         if (exc)
326                 mono_thread_internal_unhandled_exception (exc);
327
328         mono_domain_set_internal (caller_domain);
329 }
330
331 void
332 mono_gc_finalize_threadpool_threads (void)
333 {
334         while (threads_to_finalize) {
335                 MonoInternalThread *thread = (MonoInternalThread*) mono_mlist_get_data (threads_to_finalize);
336
337                 /* Force finalization of the thread. */
338                 thread->threadpool_thread = FALSE;
339                 mono_object_register_finalizer ((MonoObject*)thread);
340
341                 mono_gc_run_finalize (thread, NULL);
342
343                 threads_to_finalize = mono_mlist_next (threads_to_finalize);
344         }
345 }
346
347 gpointer
348 mono_gc_out_of_memory (size_t size)
349 {
350         /* 
351          * we could allocate at program startup some memory that we could release 
352          * back to the system at this point if we're really low on memory (ie, size is
353          * lower than the memory we set apart)
354          */
355         mono_raise_exception (mono_domain_get ()->out_of_memory_ex);
356
357         return NULL;
358 }
359
360 /*
361  * Some of our objects may point to a different address than the address returned by GC_malloc()
362  * (because of the GetHashCode hack), but we need to pass the real address to register_finalizer.
363  * This also means that in the callback we need to adjust the pointer to get back the real
364  * MonoObject*.
365  * We also need to be consistent in the use of the GC_debug* variants of malloc and register_finalizer, 
366  * since that, too, can cause the underlying pointer to be offset.
367  */
368 static void
369 object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*))
370 {
371         MonoDomain *domain;
372
373         g_assert (obj != NULL);
374
375         domain = obj->vtable->domain;
376
377 #if HAVE_BOEHM_GC
378         if (mono_domain_is_unloading (domain) && (callback != NULL))
379                 /*
380                  * Can't register finalizers in a dying appdomain, since they
381                  * could be invoked after the appdomain has been unloaded.
382                  */
383                 return;
384
385         mono_domain_finalizers_lock (domain);
386
387         if (callback)
388                 g_hash_table_insert (domain->finalizable_objects_hash, obj, obj);
389         else
390                 g_hash_table_remove (domain->finalizable_objects_hash, obj);
391
392         mono_domain_finalizers_unlock (domain);
393
394         mono_gc_register_for_finalization (obj, callback);
395 #elif defined(HAVE_SGEN_GC)
396         /*
397          * If we register finalizers for domains that are unloading we might
398          * end up running them while or after the domain is being cleared, so
399          * the objects will not be valid anymore.
400          */
401         if (!mono_domain_is_unloading (domain))
402                 mono_gc_register_for_finalization (obj, callback);
403 #endif
404 }
405
406 /**
407  * mono_object_register_finalizer:
408  * @obj: object to register
409  *
410  * Records that object @obj has a finalizer, this will call the
411  * Finalize method when the garbage collector disposes the object.
412  * 
413  */
414 void
415 mono_object_register_finalizer (MonoObject *obj)
416 {
417         /* g_print ("Registered finalizer on %p %s.%s\n", obj, mono_object_class (obj)->name_space, mono_object_class (obj)->name); */
418         object_register_finalizer (obj, mono_gc_run_finalize);
419 }
420
421 /**
422  * mono_domain_finalize:
423  * @domain: the domain to finalize
424  * @timeout: msects to wait for the finalization to complete, -1 to wait indefinitely
425  *
426  *  Request finalization of all finalizable objects inside @domain. Wait
427  * @timeout msecs for the finalization to complete.
428  *
429  * Returns: TRUE if succeeded, FALSE if there was a timeout
430  */
431
432 gboolean
433 mono_domain_finalize (MonoDomain *domain, guint32 timeout) 
434 {
435         DomainFinalizationReq *req;
436         MonoInternalThread *thread = mono_thread_internal_current ();
437
438 #if defined(__native_client__)
439         return FALSE;
440 #endif
441
442         if (mono_thread_internal_current () == gc_thread)
443                 /* We are called from inside a finalizer, not much we can do here */
444                 return FALSE;
445
446         /* 
447          * No need to create another thread 'cause the finalizer thread
448          * is still working and will take care of running the finalizers
449          */ 
450         
451         if (gc_disabled)
452                 return TRUE;
453
454         /* We don't support domain finalization without a GC */
455         if (mono_gc_is_null ())
456                 return FALSE;
457
458         mono_gc_collect (mono_gc_max_generation ());
459
460         req = g_new0 (DomainFinalizationReq, 1);
461         req->domain = domain;
462
463 #ifdef TARGET_WIN32
464         req->done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
465         if (req->done_event == NULL) {
466                 g_free (req);
467                 return FALSE;
468         }
469 #else
470         mono_coop_cond_init (&req->cond);
471         mono_coop_mutex_init (&req->mutex);
472 #endif
473
474         if (domain == mono_get_root_domain ())
475                 finalizing_root_domain = TRUE;
476         
477         mono_finalizer_lock ();
478
479         domains_to_finalize = g_slist_append (domains_to_finalize, req);
480
481         mono_finalizer_unlock ();
482
483         /* Tell the finalizer thread to finalize this appdomain */
484         mono_gc_finalize_notify ();
485
486         if (timeout == -1)
487                 timeout = INFINITE;
488
489 #if TARGET_WIN32
490         while (TRUE) {
491                 guint32 res = guarded_wait (done_event, timeout, TRUE);
492                 /* printf ("WAIT RES: %d.\n", res); */
493
494                 if (res == WAIT_IO_COMPLETION) {
495                         if ((thread->state & (ThreadState_StopRequested | ThreadState_SuspendRequested)) != 0)
496                                 return FALSE;
497                 } else if (res == WAIT_TIMEOUT) {
498                         /* We leak the handle here */
499                         return FALSE;
500                 } else {
501                         break;
502                 }
503         }
504
505         CloseHandle (done_event);
506 #else
507         mono_coop_mutex_lock (&req->mutex);
508         while (!req->done) {
509                 gboolean alerted;
510                 int res = coop_cond_timedwait_alertable (&req->cond, &req->mutex, timeout, &alerted);
511                 if (alerted) {
512                         if ((thread->state & (ThreadState_StopRequested | ThreadState_SuspendRequested)) != 0) {
513                                 mono_coop_mutex_unlock (&req->mutex);
514                                 return FALSE;
515                         }
516                 } else if (res == ETIMEDOUT) {
517                         /* We leak the cond/mutex here */
518                         mono_coop_mutex_unlock (&req->mutex);
519                         return FALSE;
520                 } else {
521                         break;
522                 }
523         }
524         mono_coop_mutex_unlock (&req->mutex);
525
526         /* When we reach here, the other thread has already exited the critical section, so this is safe to free */
527         mono_coop_cond_destroy (&req->cond);
528         mono_coop_mutex_destroy (&req->mutex);
529         g_free (req);
530 #endif
531
532         if (domain == mono_get_root_domain ()) {
533                 mono_threadpool_ms_cleanup ();
534                 mono_gc_finalize_threadpool_threads ();
535         }
536
537         mono_profiler_appdomain_event (domain, MONO_PROFILE_END_UNLOAD);
538
539         return TRUE;
540 }
541
542 void
543 ves_icall_System_GC_InternalCollect (int generation)
544 {
545         mono_gc_collect (generation);
546 }
547
548 gint64
549 ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection)
550 {
551         if (forceCollection)
552                 mono_gc_collect (mono_gc_max_generation ());
553         return mono_gc_get_used_size ();
554 }
555
556 void
557 ves_icall_System_GC_KeepAlive (MonoObject *obj)
558 {
559         /*
560          * Does nothing.
561          */
562 }
563
564 void
565 ves_icall_System_GC_ReRegisterForFinalize (MonoObject *obj)
566 {
567         MONO_CHECK_ARG_NULL (obj,);
568
569         object_register_finalizer (obj, mono_gc_run_finalize);
570 }
571
572 void
573 ves_icall_System_GC_SuppressFinalize (MonoObject *obj)
574 {
575         MONO_CHECK_ARG_NULL (obj,);
576
577         /* delegates have no finalizers, but we register them to deal with the
578          * unmanaged->managed trampoline. We don't let the user suppress it
579          * otherwise we'd leak it.
580          */
581         if (obj->vtable->klass->delegate)
582                 return;
583
584         /* FIXME: Need to handle case where obj has COM Callable Wrapper
585          * generated for it that needs cleaned up, but user wants to suppress
586          * their derived object finalizer. */
587
588         object_register_finalizer (obj, NULL);
589 }
590
591 void
592 ves_icall_System_GC_WaitForPendingFinalizers (void)
593 {
594         if (mono_gc_is_null ())
595                 return;
596
597         if (!mono_gc_pending_finalizers ())
598                 return;
599
600         if (mono_thread_internal_current () == gc_thread)
601                 /* Avoid deadlocks */
602                 return;
603
604         /*
605         If the finalizer thread is not live, lets pretend no finalizers are pending since the current thread might
606         be the one responsible for starting it up.
607         */
608         if (gc_thread == NULL)
609                 return;
610
611         ResetEvent (pending_done_event);
612         mono_gc_finalize_notify ();
613         /* g_print ("Waiting for pending finalizers....\n"); */
614         guarded_wait (pending_done_event, INFINITE, TRUE);
615         /* g_print ("Done pending....\n"); */
616 }
617
618 void
619 ves_icall_System_GC_register_ephemeron_array (MonoObject *array)
620 {
621 #ifdef HAVE_SGEN_GC
622         if (!mono_gc_ephemeron_array_add (array)) {
623                 mono_set_pending_exception (mono_object_domain (array)->out_of_memory_ex);
624                 return;
625         }
626 #endif
627 }
628
629 MonoObject*
630 ves_icall_System_GC_get_ephemeron_tombstone (void)
631 {
632         return mono_domain_get ()->ephemeron_tombstone;
633 }
634
635 MonoObject *
636 ves_icall_System_GCHandle_GetTarget (guint32 handle)
637 {
638         return mono_gchandle_get_target (handle);
639 }
640
641 /*
642  * if type == -1, change the target of the handle, otherwise allocate a new handle.
643  */
644 guint32
645 ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type)
646 {
647         if (type == -1) {
648                 mono_gchandle_set_target (handle, obj);
649                 /* the handle doesn't change */
650                 return handle;
651         }
652         switch (type) {
653         case HANDLE_WEAK:
654                 return mono_gchandle_new_weakref (obj, FALSE);
655         case HANDLE_WEAK_TRACK:
656                 return mono_gchandle_new_weakref (obj, TRUE);
657         case HANDLE_NORMAL:
658                 return mono_gchandle_new (obj, FALSE);
659         case HANDLE_PINNED:
660                 return mono_gchandle_new (obj, TRUE);
661         default:
662                 g_assert_not_reached ();
663         }
664         return 0;
665 }
666
667 void
668 ves_icall_System_GCHandle_FreeHandle (guint32 handle)
669 {
670         mono_gchandle_free (handle);
671 }
672
673 gpointer
674 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
675 {
676         MonoObject *obj;
677
678         if (MONO_GC_HANDLE_TYPE (handle) != HANDLE_PINNED)
679                 return (gpointer)-2;
680         obj = mono_gchandle_get_target (handle);
681         if (obj) {
682                 MonoClass *klass = mono_object_class (obj);
683                 if (klass == mono_defaults.string_class) {
684                         return mono_string_chars ((MonoString*)obj);
685                 } else if (klass->rank) {
686                         return mono_array_addr ((MonoArray*)obj, char, 0);
687                 } else {
688                         /* the C# code will check and throw the exception */
689                         /* FIXME: missing !klass->blittable test, see bug #61134 */
690                         if ((klass->flags & TYPE_ATTRIBUTE_LAYOUT_MASK) == TYPE_ATTRIBUTE_AUTO_LAYOUT)
691                                 return (gpointer)-1;
692                         return (char*)obj + sizeof (MonoObject);
693                 }
694         }
695         return NULL;
696 }
697
698 MonoBoolean
699 mono_gc_GCHandle_CheckCurrentDomain (guint32 gchandle)
700 {
701         return mono_gchandle_is_in_domain (gchandle, mono_domain_get ());
702 }
703
704 static MonoCoopSem finalizer_sem;
705 static volatile gboolean finished=FALSE;
706
707 void
708 mono_gc_finalize_notify (void)
709 {
710 #ifdef DEBUG
711         g_message ( "%s: prodding finalizer", __func__);
712 #endif
713
714         if (mono_gc_is_null ())
715                 return;
716
717         mono_coop_sem_post (&finalizer_sem);
718 }
719
720 /*
721 This is the number of entries allowed in the hazard free queue before
722 we explicitly cycle the finalizer thread to trigger pumping the queue.
723
724 It was picked empirically by running the corlib test suite in a stress
725 scenario where all hazard entries are queued.
726
727 In this extreme scenario we double the number of times we cycle the finalizer
728 thread compared to just GC calls.
729
730 Entries are usually in the order of 100's of bytes each, so we're limiting
731 floating garbage to be in the order of a dozen kb.
732 */
733 static gboolean finalizer_thread_pulsed;
734 #define HAZARD_QUEUE_OVERFLOW_SIZE 20
735
736 static void
737 hazard_free_queue_is_too_big (size_t size)
738 {
739         if (size < HAZARD_QUEUE_OVERFLOW_SIZE)
740                 return;
741
742         if (finalizer_thread_pulsed || InterlockedCompareExchange (&finalizer_thread_pulsed, TRUE, FALSE))
743                 return;
744
745         mono_gc_finalize_notify ();
746 }
747
748 static void
749 hazard_free_queue_pump (void)
750 {
751         mono_thread_hazardous_try_free_all ();
752         finalizer_thread_pulsed = FALSE;
753 }
754
755 #ifdef HAVE_BOEHM_GC
756
757 static void
758 collect_objects (gpointer key, gpointer value, gpointer user_data)
759 {
760         GPtrArray *arr = (GPtrArray*)user_data;
761         g_ptr_array_add (arr, key);
762 }
763
764 #endif
765
766 /*
767  * finalize_domain_objects:
768  *
769  *  Run the finalizers of all finalizable objects in req->domain.
770  */
771 static void
772 finalize_domain_objects (DomainFinalizationReq *req)
773 {
774         MonoDomain *domain = req->domain;
775
776         /* Process finalizers which are already in the queue */
777         mono_gc_invoke_finalizers ();
778
779 #ifdef HAVE_BOEHM_GC
780         while (g_hash_table_size (domain->finalizable_objects_hash) > 0) {
781                 int i;
782                 GPtrArray *objs;
783                 /* 
784                  * Since the domain is unloading, nobody is allowed to put
785                  * new entries into the hash table. But finalize_object might
786                  * remove entries from the hash table, so we make a copy.
787                  */
788                 objs = g_ptr_array_new ();
789                 g_hash_table_foreach (domain->finalizable_objects_hash, collect_objects, objs);
790                 /* printf ("FINALIZING %d OBJECTS.\n", objs->len); */
791
792                 for (i = 0; i < objs->len; ++i) {
793                         MonoObject *o = (MonoObject*)g_ptr_array_index (objs, i);
794                         /* FIXME: Avoid finalizing threads, etc */
795                         mono_gc_run_finalize (o, 0);
796                 }
797
798                 g_ptr_array_free (objs, TRUE);
799         }
800 #elif defined(HAVE_SGEN_GC)
801         mono_gc_finalize_domain (domain, &suspend_finalizers);
802         mono_gc_invoke_finalizers ();
803 #endif
804
805         /* cleanup the reference queue */
806         reference_queue_clear_for_domain (domain);
807         
808         /* printf ("DONE.\n"); */
809 #if TARGET_WIN32
810         SetEvent (req->done_event);
811
812         /* The event is closed in mono_domain_finalize if we get here */
813         g_free (req);
814 #else
815         mono_coop_mutex_lock (&req->mutex);
816         req->done = TRUE;
817         mono_coop_cond_signal (&req->cond);
818         mono_coop_mutex_unlock (&req->mutex);
819 #endif
820 }
821
822 static guint32
823 finalizer_thread (gpointer unused)
824 {
825         MonoError error;
826         mono_thread_set_name_internal (mono_thread_internal_current (), mono_string_new (mono_get_root_domain (), "Finalizer"), FALSE, &error);
827         mono_error_assert_ok (&error);
828
829         gboolean wait = TRUE;
830
831         /* Register a hazard free queue pump callback */
832         mono_hazard_pointer_install_free_queue_size_callback (hazard_free_queue_is_too_big);
833
834         while (!finished) {
835                 /* Wait to be notified that there's at least one
836                  * finaliser to run
837                  */
838
839                 g_assert (mono_domain_get () == mono_get_root_domain ());
840                 mono_gc_set_skip_thread (TRUE);
841
842                 if (wait) {
843                         /* An alertable wait is required so this thread can be suspended on windows */
844                         mono_coop_sem_wait (&finalizer_sem, MONO_SEM_FLAGS_ALERTABLE);
845                 }
846                 wait = TRUE;
847
848                 mono_gc_set_skip_thread (FALSE);
849
850                 mono_threads_perform_thread_dump ();
851
852                 mono_console_handle_async_ops ();
853
854                 mono_attach_maybe_start ();
855
856                 if (domains_to_finalize) {
857                         mono_finalizer_lock ();
858                         if (domains_to_finalize) {
859                                 DomainFinalizationReq *req = (DomainFinalizationReq *)domains_to_finalize->data;
860                                 domains_to_finalize = g_slist_remove (domains_to_finalize, req);
861                                 mono_finalizer_unlock ();
862
863                                 finalize_domain_objects (req);
864                         } else {
865                                 mono_finalizer_unlock ();
866                         }
867                 }                               
868
869                 /* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup),
870                  * before the domain is unloaded.
871                  */
872                 mono_gc_invoke_finalizers ();
873
874                 mono_threads_join_threads ();
875
876                 reference_queue_proccess_all ();
877
878                 hazard_free_queue_pump ();
879
880                 /* Avoid posting the pending done event until there are pending finalizers */
881                 if (mono_coop_sem_timedwait (&finalizer_sem, 0, MONO_SEM_FLAGS_NONE) == 0) {
882                         /* Don't wait again at the start of the loop */
883                         wait = FALSE;
884                 } else {
885                         SetEvent (pending_done_event);
886                 }
887         }
888
889         mono_finalizer_lock ();
890         finalizer_thread_exited = TRUE;
891         mono_coop_cond_signal (&exited_cond);
892         mono_finalizer_unlock ();
893
894         return 0;
895 }
896
897 #ifndef LAZY_GC_THREAD_CREATION
898 static
899 #endif
900 void
901 mono_gc_init_finalizer_thread (void)
902 {
903         MonoError error;
904         gc_thread = mono_thread_create_internal (mono_domain_get (), finalizer_thread, NULL, FALSE, 0, &error);
905         mono_error_assert_ok (&error);
906 }
907
908 void
909 mono_gc_init (void)
910 {
911         mono_coop_mutex_init_recursive (&finalizer_mutex);
912         mono_coop_mutex_init_recursive (&reference_queue_mutex);
913
914         mono_counters_register ("Minor GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.minor_gc_count);
915         mono_counters_register ("Major GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.major_gc_count);
916         mono_counters_register ("Minor GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.minor_gc_time);
917         mono_counters_register ("Major GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time);
918         mono_counters_register ("Major GC time concurrent", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time_concurrent);
919
920         mono_gc_base_init ();
921
922         if (mono_gc_is_disabled ()) {
923                 gc_disabled = TRUE;
924                 return;
925         }
926
927         pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
928         g_assert (pending_done_event);
929         mono_coop_cond_init (&exited_cond);
930         mono_coop_sem_init (&finalizer_sem, 0);
931
932 #ifndef LAZY_GC_THREAD_CREATION
933         mono_gc_init_finalizer_thread ();
934 #endif
935 }
936
937 void
938 mono_gc_cleanup (void)
939 {
940 #ifdef DEBUG
941         g_message ("%s: cleaning up finalizer", __func__);
942 #endif
943
944         if (mono_gc_is_null ())
945                 return;
946
947         if (!gc_disabled) {
948                 finished = TRUE;
949                 if (mono_thread_internal_current () != gc_thread) {
950                         gint64 start_ticks = mono_msec_ticks ();
951                         gint64 end_ticks = start_ticks + 2000;
952
953                         mono_gc_finalize_notify ();
954                         /* Finishing the finalizer thread, so wait a little bit... */
955                         /* MS seems to wait for about 2 seconds */
956                         while (!finalizer_thread_exited) {
957                                 gint64 current_ticks = mono_msec_ticks ();
958                                 guint32 timeout;
959
960                                 if (current_ticks >= end_ticks)
961                                         break;
962                                 else
963                                         timeout = end_ticks - current_ticks;
964                                 mono_finalizer_lock ();
965                                 if (!finalizer_thread_exited)
966                                         mono_coop_cond_timedwait (&exited_cond, &finalizer_mutex, timeout);
967                                 mono_finalizer_unlock ();
968                         }
969
970                         if (!finalizer_thread_exited) {
971                                 int ret;
972
973                                 /* Set a flag which the finalizer thread can check */
974                                 suspend_finalizers = TRUE;
975
976                                 /* Try to abort the thread, in the hope that it is running managed code */
977                                 mono_thread_internal_abort (gc_thread);
978
979                                 /* Wait for it to stop */
980                                 ret = guarded_wait (gc_thread->handle, 100, TRUE);
981
982                                 if (ret == WAIT_TIMEOUT) {
983                                         /*
984                                          * The finalizer thread refused to exit. Make it stop.
985                                          */
986                                         mono_thread_internal_stop (gc_thread);
987                                         ret = guarded_wait (gc_thread->handle, 100, TRUE);
988                                         g_assert (ret != WAIT_TIMEOUT);
989                                         /* The thread can't set this flag */
990                                         finalizer_thread_exited = TRUE;
991                                 }
992                         }
993
994                         int ret;
995
996                         /* Wait for the thread to actually exit */
997                         ret = guarded_wait (gc_thread->handle, INFINITE, TRUE);
998                         g_assert (ret == WAIT_OBJECT_0);
999
1000                         mono_thread_join (GUINT_TO_POINTER (gc_thread->tid));
1001                         g_assert (finalizer_thread_exited);
1002                 }
1003                 gc_thread = NULL;
1004                 mono_gc_base_cleanup ();
1005         }
1006
1007         mono_reference_queue_cleanup ();
1008
1009         mono_coop_mutex_destroy (&finalizer_mutex);
1010         mono_coop_mutex_destroy (&reference_queue_mutex);
1011 }
1012
1013 gboolean
1014 mono_gc_is_finalizer_internal_thread (MonoInternalThread *thread)
1015 {
1016         return thread == gc_thread;
1017 }
1018
1019 /**
1020  * mono_gc_is_finalizer_thread:
1021  * @thread: the thread to test.
1022  *
1023  * In Mono objects are finalized asynchronously on a separate thread.
1024  * This routine tests whether the @thread argument represents the
1025  * finalization thread.
1026  * 
1027  * Returns: TRUE if @thread is the finalization thread.
1028  */
1029 gboolean
1030 mono_gc_is_finalizer_thread (MonoThread *thread)
1031 {
1032         return mono_gc_is_finalizer_internal_thread (thread->internal_thread);
1033 }
1034
1035 #if defined(__MACH__)
1036 static pthread_t mach_exception_thread;
1037
1038 void
1039 mono_gc_register_mach_exception_thread (pthread_t thread)
1040 {
1041         mach_exception_thread = thread;
1042 }
1043
1044 pthread_t
1045 mono_gc_get_mach_exception_thread (void)
1046 {
1047         return mach_exception_thread;
1048 }
1049 #endif
1050
1051 static MonoReferenceQueue *ref_queues;
1052
1053 static void
1054 ref_list_remove_element (RefQueueEntry **prev, RefQueueEntry *element)
1055 {
1056         do {
1057                 /* Guard if head is changed concurrently. */
1058                 while (*prev != element)
1059                         prev = &(*prev)->next;
1060         } while (prev && InterlockedCompareExchangePointer ((volatile gpointer *)prev, element->next, element) != element);
1061 }
1062
1063 static void
1064 ref_list_push (RefQueueEntry **head, RefQueueEntry *value)
1065 {
1066         RefQueueEntry *current;
1067         do {
1068                 current = *head;
1069                 value->next = current;
1070                 STORE_STORE_FENCE; /*Must make sure the previous store is visible before the CAS. */
1071         } while (InterlockedCompareExchangePointer ((volatile gpointer *)head, value, current) != current);
1072 }
1073
1074 static void
1075 reference_queue_proccess (MonoReferenceQueue *queue)
1076 {
1077         RefQueueEntry **iter = &queue->queue;
1078         RefQueueEntry *entry;
1079         while ((entry = *iter)) {
1080                 if (queue->should_be_deleted || !mono_gchandle_get_target (entry->gchandle)) {
1081                         mono_gchandle_free ((guint32)entry->gchandle);
1082                         ref_list_remove_element (iter, entry);
1083                         queue->callback (entry->user_data);
1084                         g_free (entry);
1085                 } else {
1086                         iter = &entry->next;
1087                 }
1088         }
1089 }
1090
1091 static void
1092 reference_queue_proccess_all (void)
1093 {
1094         MonoReferenceQueue **iter;
1095         MonoReferenceQueue *queue = ref_queues;
1096         for (; queue; queue = queue->next)
1097                 reference_queue_proccess (queue);
1098
1099 restart:
1100         mono_coop_mutex_lock (&reference_queue_mutex);
1101         for (iter = &ref_queues; *iter;) {
1102                 queue = *iter;
1103                 if (!queue->should_be_deleted) {
1104                         iter = &queue->next;
1105                         continue;
1106                 }
1107                 if (queue->queue) {
1108                         mono_coop_mutex_unlock (&reference_queue_mutex);
1109                         reference_queue_proccess (queue);
1110                         goto restart;
1111                 }
1112                 *iter = queue->next;
1113                 g_free (queue);
1114         }
1115         mono_coop_mutex_unlock (&reference_queue_mutex);
1116 }
1117
1118 static void
1119 mono_reference_queue_cleanup (void)
1120 {
1121         MonoReferenceQueue *queue = ref_queues;
1122         for (; queue; queue = queue->next)
1123                 queue->should_be_deleted = TRUE;
1124         reference_queue_proccess_all ();
1125 }
1126
1127 static void
1128 reference_queue_clear_for_domain (MonoDomain *domain)
1129 {
1130         MonoReferenceQueue *queue = ref_queues;
1131         for (; queue; queue = queue->next) {
1132                 RefQueueEntry **iter = &queue->queue;
1133                 RefQueueEntry *entry;
1134                 while ((entry = *iter)) {
1135                         if (entry->domain == domain) {
1136                                 mono_gchandle_free ((guint32)entry->gchandle);
1137                                 ref_list_remove_element (iter, entry);
1138                                 queue->callback (entry->user_data);
1139                                 g_free (entry);
1140                         } else {
1141                                 iter = &entry->next;
1142                         }
1143                 }
1144         }
1145 }
1146 /**
1147  * mono_gc_reference_queue_new:
1148  * @callback callback used when processing collected entries.
1149  *
1150  * Create a new reference queue used to process collected objects.
1151  * A reference queue let you add a pair of (managed object, user data)
1152  * using the mono_gc_reference_queue_add method.
1153  *
1154  * Once the managed object is collected @callback will be called
1155  * in the finalizer thread with 'user data' as argument.
1156  *
1157  * The callback is called from the finalizer thread without any locks held.
1158  * When a AppDomain is unloaded, all callbacks for objects belonging to it
1159  * will be invoked.
1160  *
1161  * @returns the new queue.
1162  */
1163 MonoReferenceQueue*
1164 mono_gc_reference_queue_new (mono_reference_queue_callback callback)
1165 {
1166         MonoReferenceQueue *res = g_new0 (MonoReferenceQueue, 1);
1167         res->callback = callback;
1168
1169         mono_coop_mutex_lock (&reference_queue_mutex);
1170         res->next = ref_queues;
1171         ref_queues = res;
1172         mono_coop_mutex_unlock (&reference_queue_mutex);
1173
1174         return res;
1175 }
1176
1177 /**
1178  * mono_gc_reference_queue_add:
1179  * @queue the queue to add the reference to.
1180  * @obj the object to be watched for collection
1181  * @user_data parameter to be passed to the queue callback
1182  *
1183  * Queue an object to be watched for collection, when the @obj is
1184  * collected, the callback that was registered for the @queue will
1185  * be invoked with @user_data as argument.
1186  *
1187  * @returns false if the queue is scheduled to be freed.
1188  */
1189 gboolean
1190 mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *user_data)
1191 {
1192         RefQueueEntry *entry;
1193         if (queue->should_be_deleted)
1194                 return FALSE;
1195
1196         g_assert (obj != NULL);
1197
1198         entry = g_new0 (RefQueueEntry, 1);
1199         entry->user_data = user_data;
1200         entry->domain = mono_object_domain (obj);
1201
1202         entry->gchandle = mono_gchandle_new_weakref (obj, TRUE);
1203         mono_object_register_finalizer (obj);
1204
1205         ref_list_push (&queue->queue, entry);
1206         return TRUE;
1207 }
1208
1209 /**
1210  * mono_gc_reference_queue_free:
1211  * @queue the queue that should be freed.
1212  *
1213  * This operation signals that @queue should be freed. This operation is deferred
1214  * as it happens on the finalizer thread.
1215  *
1216  * After this call, no further objects can be queued. It's the responsibility of the
1217  * caller to make sure that no further attempt to access queue will be made.
1218  */
1219 void
1220 mono_gc_reference_queue_free (MonoReferenceQueue *queue)
1221 {
1222         queue->should_be_deleted = TRUE;
1223 }