Avoid duplicates in the SSC xrefs.
[mono.git] / mono / metadata / gc.c
index 3e7c9156e0af060e9a758e7031992842cc30aab0..95fc99d61f6f0e5f414dd3e708ab2cb192bb674d 100644 (file)
@@ -10,6 +10,7 @@
 #include <config.h>
 #include <glib.h>
 #include <string.h>
+#include <errno.h>
 
 #include <mono/metadata/gc-internal.h>
 #include <mono/metadata/mono-gc.h>
 #include <mono/metadata/profiler-private.h>
 #include <mono/metadata/domain-internals.h>
 #include <mono/metadata/class-internals.h>
+#include <mono/metadata/metadata-internals.h>
 #include <mono/metadata/mono-mlist.h>
 #include <mono/metadata/threadpool.h>
-#include <mono/utils/mono-logger.h>
+#include <mono/metadata/threads-types.h>
+#include <mono/utils/mono-logger-internal.h>
 #include <mono/metadata/gc-internal.h>
 #include <mono/metadata/marshal.h> /* for mono_delegate_free_ftnptr () */
 #include <mono/metadata/attach.h>
-#if HAVE_SEMAPHORE_H
-#include <semaphore.h>
-/* we do this only for known working systems (OSX for example
- * has the header and functions, but they don't work at all): in other cases
- * we fall back to the io-layer slightly slower and signal-unsafe Event.
- */
-#ifdef __linux__
-#define USE_POSIX_SEM 1
-#endif
-#endif
+#include <mono/metadata/console-io.h>
+#include <mono/utils/mono-semaphore.h>
 
-#ifndef PLATFORM_WIN32
+#ifndef HOST_WIN32
 #include <pthread.h>
 #endif
 
@@ -59,26 +54,31 @@ static gboolean finalizing_root_domain = FALSE;
 #define mono_finalizer_lock() EnterCriticalSection (&finalizer_mutex)
 #define mono_finalizer_unlock() LeaveCriticalSection (&finalizer_mutex)
 static CRITICAL_SECTION finalizer_mutex;
+static CRITICAL_SECTION reference_queue_mutex;
 
 static GSList *domains_to_finalize= NULL;
 static MonoMList *threads_to_finalize = NULL;
 
-static MonoThread *gc_thread;
+static MonoInternalThread *gc_thread;
 
 static void object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*));
 
+static void mono_gchandle_set_target (guint32 gchandle, MonoObject *obj);
+
+static void reference_queue_proccess_all (void);
+static void mono_reference_queue_cleanup (void);
+static void reference_queue_clear_for_domain (MonoDomain *domain);
 #ifndef HAVE_NULL_GC
 static HANDLE pending_done_event;
 static HANDLE shutdown_event;
-static HANDLE thread_started_event;
 #endif
 
 static void
-add_thread_to_finalize (MonoThread *thread)
+add_thread_to_finalize (MonoInternalThread *thread)
 {
        mono_finalizer_lock ();
        if (!threads_to_finalize)
-               MONO_GC_REGISTER_ROOT (threads_to_finalize);
+               MONO_GC_REGISTER_ROOT_SINGLE (threads_to_finalize);
        threads_to_finalize = mono_mlist_append (threads_to_finalize, (MonoObject*)thread);
        mono_finalizer_unlock ();
 }
@@ -88,8 +88,8 @@ static gboolean suspend_finalizers = FALSE;
  * actually, we might want to queue the finalize requests in a separate thread,
  * but we need to be careful about the execution domain of the thread...
  */
-static void
-run_finalize (void *obj, void *data)
+void
+mono_gc_run_finalize (void *obj, void *data)
 {
        MonoObject *exc = NULL;
        MonoObject *o;
@@ -97,9 +97,11 @@ run_finalize (void *obj, void *data)
        MonoObject *o2;
 #endif
        MonoMethod* finalizer = NULL;
+       MonoDomain *caller_domain = mono_domain_get ();
        MonoDomain *domain;
-       MonoObject *(*runtime_invoke) (MonoObject *this, void **params, MonoObject **exc, void* compiled_method);
-       
+       RuntimeInvokeFunction runtime_invoke;
+       GSList *l, *refs = NULL;
+
        o = (MonoObject*)((char*)obj + GPOINTER_TO_UINT (data));
 
        if (suspend_finalizers)
@@ -108,24 +110,43 @@ run_finalize (void *obj, void *data)
        domain = o->vtable->domain;
 
 #ifndef HAVE_SGEN_GC
-       EnterCriticalSection (&o->vtable->domain->finalizable_objects_hash_lock);
+       mono_domain_finalizers_lock (domain);
 
-       o2 = g_hash_table_lookup (o->vtable->domain->finalizable_objects_hash, o);
+       o2 = g_hash_table_lookup (domain->finalizable_objects_hash, o);
 
-       LeaveCriticalSection (&o->vtable->domain->finalizable_objects_hash_lock);
+       refs = mono_gc_remove_weak_track_object (domain, o);
+
+       mono_domain_finalizers_unlock (domain);
 
        if (!o2)
                /* Already finalized somehow */
                return;
 #endif
 
+       if (refs) {
+               /*
+                * Support for GCHandles of type WeakTrackResurrection:
+                *
+                *   Its not exactly clear how these are supposed to work, or how their
+                * semantics can be implemented. We only implement one crucial thing:
+                * these handles are only cleared after the finalizer has ran.
+                */
+               for (l = refs; l; l = l->next) {
+                       guint32 gchandle = GPOINTER_TO_UINT (l->data);
+
+                       mono_gchandle_set_target (gchandle, o);
+               }
+
+               g_slist_free (refs);
+       }
+               
        /* make sure the finalizer is not called again if the object is resurrected */
        object_register_finalizer (obj, NULL);
 
-       if (o->vtable->klass == mono_get_thread_class ()) {
-               MonoThread *t = (MonoThread*)o;
+       if (o->vtable->klass == mono_defaults.internal_thread_class) {
+               MonoInternalThread *t = (MonoInternalThread*)o;
 
-               if (mono_gc_is_finalizer_thread (t))
+               if (mono_gc_is_finalizer_internal_thread (t))
                        /* Avoid finalizing ourselves */
                        return;
 
@@ -138,6 +159,17 @@ run_finalize (void *obj, void *data)
                }
        }
 
+       if (o->vtable->klass->image == mono_defaults.corlib && !strcmp (o->vtable->klass->name, "DynamicMethod") && finalizing_root_domain) {
+               /*
+                * These can't be finalized during unloading/shutdown, since that would
+                * free the native code which can still be referenced by other
+                * finalizers.
+                * FIXME: This is not perfect, objects dying at the same time as 
+                * dynamic methods can still reference them even when !shutdown.
+                */
+               return;
+       }
+
        if (mono_runtime_get_no_exec ())
                return;
 
@@ -145,7 +177,7 @@ run_finalize (void *obj, void *data)
        /* g_print ("Finalize run on %p %s.%s\n", o, mono_object_class (o)->name_space, mono_object_class (o)->name); */
 
        /* Use _internal here, since this thread can enter a doomed appdomain */
-       mono_domain_set_internal (mono_object_domain (o));              
+       mono_domain_set_internal (mono_object_domain (o));
 
        /* delegates that have a native function pointer allocated are
         * registered for finalization, but they don't have a Finalize
@@ -155,6 +187,7 @@ run_finalize (void *obj, void *data)
                MonoDelegate* del = (MonoDelegate*)o;
                if (del->delegate_trampoline)
                        mono_delegate_free_ftnptr ((MonoDelegate*)o);
+               mono_domain_set_internal (caller_domain);
                return;
        }
 
@@ -167,8 +200,10 @@ run_finalize (void *obj, void *data)
         * FIXME: what to do about ressurection and suppression
         * of finalizer on object with CCW.
         */
-       if (mono_marshal_free_ccw (o) && !finalizer)
+       if (mono_marshal_free_ccw (o) && !finalizer) {
+               mono_domain_set_internal (caller_domain);
                return;
+       }
 #endif
 
        /* 
@@ -191,19 +226,21 @@ run_finalize (void *obj, void *data)
        if (exc) {
                /* fixme: do something useful */
        }
+
+       mono_domain_set_internal (caller_domain);
 }
 
 void
 mono_gc_finalize_threadpool_threads (void)
 {
        while (threads_to_finalize) {
-               MonoThread *thread = (MonoThread*) mono_mlist_get_data (threads_to_finalize);
+               MonoInternalThread *thread = (MonoInternalThread*) mono_mlist_get_data (threads_to_finalize);
 
                /* Force finalization of the thread. */
                thread->threadpool_thread = FALSE;
                mono_object_register_finalizer ((MonoObject*)thread);
 
-               run_finalize (thread, NULL);
+               mono_gc_run_finalize (thread, NULL);
 
                threads_to_finalize = mono_mlist_next (threads_to_finalize);
        }
@@ -235,7 +272,12 @@ object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*))
 {
 #if HAVE_BOEHM_GC
        guint offset = 0;
-       MonoDomain *domain = obj->vtable->domain;
+       MonoDomain *domain;
+
+       if (obj == NULL)
+               mono_raise_exception (mono_get_exception_argument_null ("obj"));
+       
+       domain = obj->vtable->domain;
 
 #ifndef GC_DEBUG
        /* This assertion is not valid when GC_DEBUG is defined */
@@ -249,18 +291,27 @@ object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*))
                 */
                return;
 
-       EnterCriticalSection (&domain->finalizable_objects_hash_lock);
+       mono_domain_finalizers_lock (domain);
 
        if (callback)
                g_hash_table_insert (domain->finalizable_objects_hash, obj, obj);
        else
                g_hash_table_remove (domain->finalizable_objects_hash, obj);
 
-       LeaveCriticalSection (&domain->finalizable_objects_hash_lock);
+       mono_domain_finalizers_unlock (domain);
 
        GC_REGISTER_FINALIZER_NO_ORDER ((char*)obj - offset, callback, GUINT_TO_POINTER (offset), NULL, NULL);
 #elif defined(HAVE_SGEN_GC)
-       mono_gc_register_for_finalization (obj, callback);
+       if (obj == NULL)
+               mono_raise_exception (mono_get_exception_argument_null ("obj"));
+
+       /*
+        * If we register finalizers for domains that are unloading we might
+        * end up running them while or after the domain is being cleared, so
+        * the objects will not be valid anymore.
+        */
+       if (!mono_domain_is_unloading (obj->vtable->domain))
+               mono_gc_register_for_finalization (obj, callback);
 #endif
 }
 
@@ -276,7 +327,7 @@ void
 mono_object_register_finalizer (MonoObject *obj)
 {
        /* g_print ("Registered finalizer on %p %s.%s\n", obj, mono_object_class (obj)->name_space, mono_object_class (obj)->name); */
-       object_register_finalizer (obj, run_finalize);
+       object_register_finalizer (obj, mono_gc_run_finalize);
 }
 
 /**
@@ -296,8 +347,9 @@ mono_domain_finalize (MonoDomain *domain, guint32 timeout)
        DomainFinalizationReq *req;
        guint32 res;
        HANDLE done_event;
+       MonoInternalThread *thread = mono_thread_internal_current ();
 
-       if (mono_thread_current () == gc_thread)
+       if (mono_thread_internal_current () == gc_thread)
                /* We are called from inside a finalizer, not much we can do here */
                return FALSE;
 
@@ -336,12 +388,19 @@ mono_domain_finalize (MonoDomain *domain, guint32 timeout)
        if (timeout == -1)
                timeout = INFINITE;
 
-       res = WaitForSingleObjectEx (done_event, timeout, TRUE);
+       while (TRUE) {
+               res = WaitForSingleObjectEx (done_event, timeout, TRUE);
+               /* printf ("WAIT RES: %d.\n", res); */
 
-       /* printf ("WAIT RES: %d.\n", res); */
-       if (res == WAIT_TIMEOUT) {
-               /* We leak the handle here */
-               return FALSE;
+               if (res == WAIT_IO_COMPLETION) {
+                       if ((thread->state & (ThreadState_StopRequested | ThreadState_SuspendRequested)) != 0)
+                               return FALSE;
+               } else if (res == WAIT_TIMEOUT) {
+                       /* We leak the handle here */
+                       return FALSE;
+               } else {
+                       break;
+               }
        }
 
        CloseHandle (done_event);
@@ -387,15 +446,17 @@ ves_icall_System_GC_KeepAlive (MonoObject *obj)
 void
 ves_icall_System_GC_ReRegisterForFinalize (MonoObject *obj)
 {
-       MONO_ARCH_SAVE_REGS;
+       if (!obj)
+               mono_raise_exception (mono_get_exception_argument_null ("obj"));
 
-       object_register_finalizer (obj, run_finalize);
+       object_register_finalizer (obj, mono_gc_run_finalize);
 }
 
 void
 ves_icall_System_GC_SuppressFinalize (MonoObject *obj)
 {
-       MONO_ARCH_SAVE_REGS;
+       if (!obj)
+               mono_raise_exception (mono_get_exception_argument_null ("obj"));
 
        /* delegates have no finalizers, but we register them to deal with the
         * unmanaged->managed trampoline. We don't let the user suppress it
@@ -418,7 +479,7 @@ ves_icall_System_GC_WaitForPendingFinalizers (void)
        if (!mono_gc_pending_finalizers ())
                return;
 
-       if (mono_thread_current () == gc_thread)
+       if (mono_thread_internal_current () == gc_thread)
                /* Avoid deadlocks */
                return;
 
@@ -430,6 +491,21 @@ ves_icall_System_GC_WaitForPendingFinalizers (void)
 #endif
 }
 
+void
+ves_icall_System_GC_register_ephemeron_array (MonoObject *array)
+{
+#ifdef HAVE_SGEN_GC
+       if (!mono_gc_ephemeron_array_add (array))
+               mono_raise_exception (mono_object_domain (array)->out_of_memory_ex);
+#endif
+}
+
+MonoObject*
+ves_icall_System_GC_get_ephemeron_tombstone (void)
+{
+       return mono_domain_get ()->ephemeron_tombstone;
+}
+
 #define mono_allocator_lock() EnterCriticalSection (&allocator_section)
 #define mono_allocator_unlock() LeaveCriticalSection (&allocator_section)
 static CRITICAL_SECTION allocator_section;
@@ -442,8 +518,6 @@ typedef enum {
        HANDLE_PINNED
 } HandleType;
 
-static void mono_gchandle_set_target (guint32 gchandle, MonoObject *obj);
-
 static HandleType mono_gchandle_get_type (guint32 gchandle);
 
 MonoObject *
@@ -544,14 +618,15 @@ find_first_unset (guint32 bitmap)
 }
 
 static guint32
-alloc_handle (HandleData *handles, MonoObject *obj)
+alloc_handle (HandleData *handles, MonoObject *obj, gboolean track)
 {
        gint slot, i;
+       guint32 res;
        lock_handles (handles);
        if (!handles->size) {
                handles->size = 32;
                if (handles->type > HANDLE_WEAK_TRACK) {
-                       handles->entries = mono_gc_alloc_fixed (sizeof (gpointer) * handles->size, NULL);
+                       handles->entries = mono_gc_alloc_fixed (sizeof (gpointer) * handles->size, mono_gc_make_root_descr_all_refs (handles->size));
                } else {
                        handles->entries = g_malloc0 (sizeof (gpointer) * handles->size);
                        handles->domain_ids = g_malloc0 (sizeof (guint16) * handles->size);
@@ -588,8 +663,11 @@ alloc_handle (HandleData *handles, MonoObject *obj)
                /* resize and copy the entries */
                if (handles->type > HANDLE_WEAK_TRACK) {
                        gpointer *entries;
-                       entries = mono_gc_alloc_fixed (sizeof (gpointer) * new_size, NULL);
+
+                       entries = mono_gc_alloc_fixed (sizeof (gpointer) * new_size, mono_gc_make_root_descr_all_refs (new_size));
                        memcpy (entries, handles->entries, sizeof (gpointer) * handles->size);
+
+                       mono_gc_free_fixed (handles->entries);
                        handles->entries = entries;
                } else {
                        gpointer *entries;
@@ -607,7 +685,7 @@ alloc_handle (HandleData *handles, MonoObject *obj)
                                        mono_gc_weak_link_remove (&(handles->entries [i]));
                                /*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]);*/
                                if (obj) {
-                                       mono_gc_weak_link_add (&(entries [i]), obj);
+                                       mono_gc_weak_link_add (&(entries [i]), obj, track);
                                }
                        }
                        g_free (handles->entries);
@@ -627,14 +705,18 @@ alloc_handle (HandleData *handles, MonoObject *obj)
        slot = slot * 32 + i;
        handles->entries [slot] = obj;
        if (handles->type <= HANDLE_WEAK_TRACK) {
+               /*FIXME, what to use when obj == null?*/
+               handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
                if (obj)
-                       mono_gc_weak_link_add (&(handles->entries [slot]), obj);
+                       mono_gc_weak_link_add (&(handles->entries [slot]), obj, track);
        }
 
        mono_perfcounters->gc_num_handles++;
        unlock_handles (handles);
        /*g_print ("allocated entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
-       return (slot << 3) | (handles->type + 1);
+       res = (slot << 3) | (handles->type + 1);
+       mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_CREATED, handles->type, res, obj);
+       return res;
 }
 
 /**
@@ -657,7 +739,7 @@ alloc_handle (HandleData *handles, MonoObject *obj)
 guint32
 mono_gchandle_new (MonoObject *obj, gboolean pinned)
 {
-       return alloc_handle (&gc_handles [pinned? HANDLE_PINNED: HANDLE_NORMAL], obj);
+       return alloc_handle (&gc_handles [pinned? HANDLE_PINNED: HANDLE_NORMAL], obj, FALSE);
 }
 
 /**
@@ -682,7 +764,14 @@ mono_gchandle_new (MonoObject *obj, gboolean pinned)
 guint32
 mono_gchandle_new_weakref (MonoObject *obj, gboolean track_resurrection)
 {
-       return alloc_handle (&gc_handles [track_resurrection? HANDLE_WEAK_TRACK: HANDLE_WEAK], obj);
+       guint32 handle = alloc_handle (&gc_handles [track_resurrection? HANDLE_WEAK_TRACK: HANDLE_WEAK], obj, track_resurrection);
+
+#ifndef HAVE_SGEN_GC
+       if (track_resurrection)
+               mono_gc_add_weak_track_handle (obj, handle);
+#endif
+
+       return handle;
 }
 
 static HandleType
@@ -733,15 +822,20 @@ mono_gchandle_set_target (guint32 gchandle, MonoObject *obj)
        guint slot = gchandle >> 3;
        guint type = (gchandle & 7) - 1;
        HandleData *handles = &gc_handles [type];
+       MonoObject *old_obj = NULL;
+
        if (type > 3)
                return;
        lock_handles (handles);
        if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
                if (handles->type <= HANDLE_WEAK_TRACK) {
+                       old_obj = handles->entries [slot];
                        if (handles->entries [slot])
                                mono_gc_weak_link_remove (&handles->entries [slot]);
                        if (obj)
-                               mono_gc_weak_link_add (&handles->entries [slot], obj);
+                               mono_gc_weak_link_add (&handles->entries [slot], obj, handles->type == HANDLE_WEAK_TRACK);
+                       /*FIXME, what to use when obj == null?*/
+                       handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
                } else {
                        handles->entries [slot] = obj;
                }
@@ -750,6 +844,11 @@ mono_gchandle_set_target (guint32 gchandle, MonoObject *obj)
        }
        /*g_print ("changed entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
        unlock_handles (handles);
+
+#ifndef HAVE_SGEN_GC
+       if (type == HANDLE_WEAK_TRACK)
+               mono_gc_change_weak_track_handle (old_obj, obj, gchandle);
+#endif
 }
 
 /**
@@ -803,6 +902,11 @@ mono_gchandle_free (guint32 gchandle)
        HandleData *handles = &gc_handles [type];
        if (type > 3)
                return;
+#ifndef HAVE_SGEN_GC
+       if (type == HANDLE_WEAK_TRACK)
+               mono_gc_remove_weak_track_handle (gchandle);
+#endif
+
        lock_handles (handles);
        if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
                if (handles->type <= HANDLE_WEAK_TRACK) {
@@ -818,6 +922,7 @@ mono_gchandle_free (guint32 gchandle)
        mono_perfcounters->gc_num_handles--;
        /*g_print ("freed entry %d of type %d\n", slot, handles->type);*/
        unlock_handles (handles);
+       mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_DESTROYED, handles->type, gchandle, NULL);
 }
 
 /**
@@ -857,10 +962,16 @@ mono_gchandle_free_domain (MonoDomain *domain)
 
 }
 
+MonoBoolean
+GCHandle_CheckCurrentDomain (guint32 gchandle)
+{
+       return mono_gchandle_is_in_domain (gchandle, mono_domain_get ());
+}
+
 #ifndef HAVE_NULL_GC
 
-#if USE_POSIX_SEM
-static sem_t finalizer_sem;
+#ifdef MONO_HAS_SEMAPHORES
+static MonoSemType finalizer_sem;
 #endif
 static HANDLE finalizer_event;
 static volatile gboolean finished=FALSE;
@@ -869,11 +980,11 @@ void
 mono_gc_finalize_notify (void)
 {
 #ifdef DEBUG
-       g_message (G_GNUC_PRETTY_FUNCTION ": prodding finalizer");
+       g_message ( "%s: prodding finalizer", __func__);
 #endif
 
-#if USE_POSIX_SEM
-       sem_post (&finalizer_sem);
+#ifdef MONO_HAS_SEMAPHORES
+       MONO_SEM_POST (&finalizer_sem);
 #else
        SetEvent (finalizer_event);
 #endif
@@ -916,7 +1027,7 @@ finalize_domain_objects (DomainFinalizationReq *req)
                for (i = 0; i < objs->len; ++i) {
                        MonoObject *o = (MonoObject*)g_ptr_array_index (objs, i);
                        /* FIXME: Avoid finalizing threads, etc */
-                       run_finalize (o, 0);
+                       mono_gc_run_finalize (o, 0);
                }
 
                g_ptr_array_free (objs, TRUE);
@@ -928,7 +1039,7 @@ finalize_domain_objects (DomainFinalizationReq *req)
        while ((count = mono_gc_finalizers_for_domain (domain, to_finalize, NUM_FOBJECTS))) {
                int i;
                for (i = 0; i < count; ++i) {
-                       run_finalize (to_finalize [i], 0);
+                       mono_gc_run_finalize (to_finalize [i], 0);
                }
        }
 #endif
@@ -936,6 +1047,9 @@ finalize_domain_objects (DomainFinalizationReq *req)
        /* Process finalizers which are already in the queue */
        mono_gc_invoke_finalizers ();
 
+       /* cleanup the reference queue */
+       reference_queue_clear_for_domain (domain);
+       
        /* printf ("DONE.\n"); */
        SetEvent (req->done_event);
 
@@ -946,21 +1060,24 @@ finalize_domain_objects (DomainFinalizationReq *req)
 static guint32
 finalizer_thread (gpointer unused)
 {
-       gc_thread = mono_thread_current ();
-
-       SetEvent (thread_started_event);
-
        while (!finished) {
                /* Wait to be notified that there's at least one
                 * finaliser to run
                 */
-#if USE_POSIX_SEM
-               sem_wait (&finalizer_sem);
+
+               g_assert (mono_domain_get () == mono_get_root_domain ());
+
+               /* An alertable wait is required so this thread can be suspended on windows */
+#ifdef MONO_HAS_SEMAPHORES
+               MONO_SEM_WAIT_ALERTABLE (&finalizer_sem, TRUE);
 #else
-               /* Use alertable=FALSE since we will be asked to exit using the event too */
-               WaitForSingleObjectEx (finalizer_event, INFINITE, FALSE);
+               WaitForSingleObjectEx (finalizer_event, INFINITE, TRUE);
 #endif
 
+               mono_threads_perform_thread_dump ();
+
+               mono_console_handle_async_ops ();
+
 #ifndef DISABLE_ATTACH
                mono_attach_maybe_start ();
 #endif
@@ -983,6 +1100,8 @@ finalizer_thread (gpointer unused)
                 */
                mono_gc_invoke_finalizers ();
 
+               reference_queue_proccess_all ();
+
                SetEvent (pending_done_event);
        }
 
@@ -997,13 +1116,14 @@ mono_gc_init (void)
        InitializeCriticalSection (&allocator_section);
 
        InitializeCriticalSection (&finalizer_mutex);
+       InitializeCriticalSection (&reference_queue_mutex);
 
-       MONO_GC_REGISTER_ROOT (gc_handles [HANDLE_NORMAL].entries);
-       MONO_GC_REGISTER_ROOT (gc_handles [HANDLE_PINNED].entries);
+       MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_NORMAL].entries);
+       MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_PINNED].entries);
 
        mono_gc_base_init ();
 
-       if (g_getenv ("GC_DONT_GC")) {
+       if (mono_gc_is_disabled ()) {
                gc_disabled = TRUE;
                return;
        }
@@ -1011,39 +1131,28 @@ mono_gc_init (void)
        finalizer_event = CreateEvent (NULL, FALSE, FALSE, NULL);
        pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
        shutdown_event = CreateEvent (NULL, TRUE, FALSE, NULL);
-       thread_started_event = CreateEvent (NULL, TRUE, FALSE, NULL);
-       if (finalizer_event == NULL || pending_done_event == NULL || shutdown_event == NULL || thread_started_event == NULL) {
+       if (finalizer_event == NULL || pending_done_event == NULL || shutdown_event == NULL) {
                g_assert_not_reached ();
        }
-#if USE_POSIX_SEM
-       sem_init (&finalizer_sem, 0, 0);
+#ifdef MONO_HAS_SEMAPHORES
+       MONO_SEM_INIT (&finalizer_sem, 0);
 #endif
 
-       mono_thread_create (mono_domain_get (), finalizer_thread, NULL);
-
-       /*
-        * Wait until the finalizer thread sets gc_thread since its value is needed
-        * by mono_thread_attach ()
-        *
-        * FIXME: Eliminate this as to avoid some deadlocks on windows. 
-        * Waiting for a new thread should result in a deadlock when the runtime is
-        * initialized from _CorDllMain that is called while the OS loader lock is
-        * held by LoadLibrary.
-        */
-       WaitForSingleObjectEx (thread_started_event, INFINITE, FALSE);
+       gc_thread = mono_thread_create_internal (mono_domain_get (), finalizer_thread, NULL, FALSE, 0);
+       ves_icall_System_Threading_Thread_SetName_internal (gc_thread, mono_string_new (mono_domain_get (), "Finalizer"));
 }
 
 void
 mono_gc_cleanup (void)
 {
 #ifdef DEBUG
-       g_message (G_GNUC_PRETTY_FUNCTION ": cleaning up finalizer");
+       g_message ("%s: cleaning up finalizer", __func__);
 #endif
 
        if (!gc_disabled) {
                ResetEvent (shutdown_event);
                finished = TRUE;
-               if (mono_thread_current () != gc_thread) {
+               if (mono_thread_internal_current () != gc_thread) {
                        mono_gc_finalize_notify ();
                        /* Finishing the finalizer thread, so wait a little bit... */
                        /* MS seems to wait for about 2 seconds */
@@ -1054,7 +1163,7 @@ mono_gc_cleanup (void)
                                suspend_finalizers = TRUE;
 
                                /* Try to abort the thread, in the hope that it is running managed code */
-                               mono_thread_stop (gc_thread);
+                               mono_thread_internal_stop (gc_thread);
 
                                /* Wait for it to stop */
                                ret = WaitForSingleObjectEx (gc_thread->handle, 100, TRUE);
@@ -1082,9 +1191,12 @@ mono_gc_cleanup (void)
 #endif
        }
 
+       mono_reference_queue_cleanup ();
+
        DeleteCriticalSection (&handle_section);
        DeleteCriticalSection (&allocator_section);
        DeleteCriticalSection (&finalizer_mutex);
+       DeleteCriticalSection (&reference_queue_mutex);
 }
 
 #else
@@ -1106,6 +1218,12 @@ void mono_gc_cleanup (void)
 
 #endif
 
+gboolean
+mono_gc_is_finalizer_internal_thread (MonoInternalThread *thread)
+{
+       return thread == gc_thread;
+}
+
 /**
  * mono_gc_is_finalizer_thread:
  * @thread: the thread to test.
@@ -1119,7 +1237,372 @@ void mono_gc_cleanup (void)
 gboolean
 mono_gc_is_finalizer_thread (MonoThread *thread)
 {
-       return thread == gc_thread;
+       return mono_gc_is_finalizer_internal_thread (thread->internal_thread);
+}
+
+#if defined(__MACH__)
+static pthread_t mach_exception_thread;
+
+void
+mono_gc_register_mach_exception_thread (pthread_t thread)
+{
+       mach_exception_thread = thread;
+}
+
+pthread_t
+mono_gc_get_mach_exception_thread (void)
+{
+       return mach_exception_thread;
+}
+#endif
+
+/**
+ * mono_gc_parse_environment_string_extract_number:
+ *
+ * @str: points to the first digit of the number
+ * @out: pointer to the variable that will receive the value
+ *
+ * Tries to extract a number from the passed string, taking in to account m, k
+ * and g suffixes
+ *
+ * Returns true if passing was successful
+ */
+gboolean
+mono_gc_parse_environment_string_extract_number (const char *str, glong *out)
+{
+       char *endptr;
+       int len = strlen (str), shift = 0;
+       glong val;
+       gboolean is_suffix = FALSE;
+       char suffix;
+
+       if (!len)
+               return FALSE;
+
+       suffix = str [len - 1];
+
+       switch (suffix) {
+               case 'g':
+               case 'G':
+                       shift += 10;
+               case 'm':
+               case 'M':
+                       shift += 10;
+               case 'k':
+               case 'K':
+                       shift += 10;
+                       is_suffix = TRUE;
+                       break;
+               default:
+                       if (!isdigit (suffix))
+                               return FALSE;
+                       break;
+       }
+
+       errno = 0;
+       val = strtol (str, &endptr, 10);
+
+       if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN))
+                       || (errno != 0 && val == 0) || (endptr == str))
+               return FALSE;
+
+       if (is_suffix) {
+               if (*(endptr + 1)) /* Invalid string. */
+                       return FALSE;
+               val <<= shift;
+       }
+
+       *out = val;
+       return TRUE;
+}
+
+#ifndef HAVE_SGEN_GC
+void*
+mono_gc_alloc_mature (MonoVTable *vtable)
+{
+       return mono_object_new_specific (vtable);
+}
+#endif
+
+
+static MonoReferenceQueue *ref_queues;
+
+static void
+ref_list_remove_element (RefQueueEntry **prev, RefQueueEntry *element)
+{
+       do {
+               /* Guard if head is changed concurrently. */
+               while (*prev != element)
+                       prev = &(*prev)->next;
+       } while (prev && InterlockedCompareExchangePointer ((void*)prev, element->next, element) != element);
+}
+
+static void
+ref_list_push (RefQueueEntry **head, RefQueueEntry *value)
+{
+       RefQueueEntry *current;
+       do {
+               current = *head;
+               value->next = current;
+       } while (InterlockedCompareExchangePointer ((void*)head, value, current) != current);
+}
+
+static void
+reference_queue_proccess (MonoReferenceQueue *queue)
+{
+       RefQueueEntry **iter = &queue->queue;
+       RefQueueEntry *entry;
+       while ((entry = *iter)) {
+#ifdef HAVE_SGEN_GC
+               if (queue->should_be_deleted || !mono_gc_weak_link_get (&entry->dis_link)) {
+                       mono_gc_weak_link_remove (&entry->dis_link);
+#else
+               if (queue->should_be_deleted || !mono_gchandle_get_target (entry->gchandle)) {
+                       mono_gchandle_free ((guint32)entry->gchandle);
+#endif
+                       ref_list_remove_element (iter, entry);
+                       queue->callback (entry->user_data);
+                       g_free (entry);
+               } else {
+                       iter = &entry->next;
+               }
+       }
+}
+
+static void
+reference_queue_proccess_all (void)
+{
+       MonoReferenceQueue **iter;
+       MonoReferenceQueue *queue = ref_queues;
+       for (; queue; queue = queue->next)
+               reference_queue_proccess (queue);
+
+restart:
+       EnterCriticalSection (&reference_queue_mutex);
+       for (iter = &ref_queues; *iter;) {
+               queue = *iter;
+               if (!queue->should_be_deleted) {
+                       iter = &queue->next;
+                       continue;
+               }
+               if (queue->queue) {
+                       LeaveCriticalSection (&reference_queue_mutex);
+                       reference_queue_proccess (queue);
+                       goto restart;
+               }
+               *iter = queue->next;
+               g_free (queue);
+       }
+       LeaveCriticalSection (&reference_queue_mutex);
+}
+
+static void
+mono_reference_queue_cleanup (void)
+{
+       MonoReferenceQueue *queue = ref_queues;
+       for (; queue; queue = queue->next)
+               queue->should_be_deleted = TRUE;
+       reference_queue_proccess_all ();
+}
+
+static void
+reference_queue_clear_for_domain (MonoDomain *domain)
+{
+       MonoReferenceQueue *queue = ref_queues;
+       for (; queue; queue = queue->next) {
+               RefQueueEntry **iter = &queue->queue;
+               RefQueueEntry *entry;
+               while ((entry = *iter)) {
+                       MonoObject *obj;
+#ifdef HAVE_SGEN_GC
+                       obj = mono_gc_weak_link_get (&entry->dis_link);
+                       if (obj && mono_object_domain (obj) == domain) {
+                               mono_gc_weak_link_remove (&entry->dis_link);
+#else
+                       obj = mono_gchandle_get_target (entry->gchandle);
+                       if (obj && mono_object_domain (obj) == domain) {
+                               mono_gchandle_free ((guint32)entry->gchandle);
+#endif
+                               ref_list_remove_element (iter, entry);
+                               queue->callback (entry->user_data);
+                               g_free (entry);
+                       } else {
+                               iter = &entry->next;
+                       }
+               }
+       }
+}
+/**
+ * mono_gc_reference_queue_new:
+ * @callback callback used when processing dead entries.
+ *
+ * Create a new reference queue used to process collected objects.
+ * A reference queue let you queue the pair (managed object, user data).
+ * Once the managed object is collected @callback will be called
+ * in the finalizer thread with 'user data' as argument.
+ *
+ * The callback is called without any locks held.
+ */
+MonoReferenceQueue*
+mono_gc_reference_queue_new (mono_reference_queue_callback callback)
+{
+       MonoReferenceQueue *res = g_new0 (MonoReferenceQueue, 1);
+       res->callback = callback;
+
+       EnterCriticalSection (&reference_queue_mutex);
+       res->next = ref_queues;
+       ref_queues = res;
+       LeaveCriticalSection (&reference_queue_mutex);
+
+       return res;
+}
+
+/**
+ * mono_gc_reference_queue_add:
+ * @queue the queue to add the reference to.
+ * @obj the object to be watched for collection
+ * @user_data parameter to be passed to the queue callback
+ *
+ * Queue an object to be watched for collection.
+ *
+ * @returns false if the queue is scheduled to be freed.
+ */
+gboolean
+mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *user_data)
+{
+       RefQueueEntry *entry;
+       if (queue->should_be_deleted)
+               return FALSE;
+
+       entry = g_new0 (RefQueueEntry, 1);
+       entry->user_data = user_data;
+
+#ifdef HAVE_SGEN_GC
+       mono_gc_weak_link_add (&entry->dis_link, obj, TRUE);
+#else
+       entry->gchandle = mono_gchandle_new_weakref (obj, TRUE);
+       mono_object_register_finalizer (obj);
+#endif
+
+       ref_list_push (&queue->queue, entry);
+       return TRUE;
+}
+
+/**
+ * mono_gc_reference_queue_free:
+ * @queue the queue that should be deleted.
+ *
+ * This operation signals that @queue should be deleted. This operation is deferred
+ * as it happens on the finalizer thread.
+ *
+ * After this call, no further objects can be queued. It's the responsibility of the
+ * caller to make sure that no further attempt to access queue will be made.
+ */
+void
+mono_gc_reference_queue_free (MonoReferenceQueue *queue)
+{
+       queue->should_be_deleted = TRUE;
+}
+
+#define ptr_mask ((sizeof (void*) - 1))
+#define _toi(ptr) ((size_t)ptr)
+#define unaligned_bytes(ptr) (_toi(ptr) & ptr_mask)
+#define align_down(ptr) ((void*)(_toi(ptr) & ~ptr_mask))
+#define align_up(ptr) ((void*) ((_toi(ptr) + ptr_mask) & ~ptr_mask))
+
+/**
+ * Zero @size bytes starting at @dest.
+ *
+ * Use this to zero memory that can hold managed pointers.
+ *
+ * FIXME borrow faster code from some BSD libc or bionic
+ */
+void
+mono_gc_bzero (void *dest, size_t size)
+{
+       char *p = (char*)dest;
+       char *end = p + size;
+       char *align_end = p + unaligned_bytes (p);
+       char *word_end;
+
+       while (p < align_end)
+               *p++ = 0;
+
+       word_end = align_down (end);
+       while (p < word_end) {
+               *((void**)p) = NULL;
+               p += sizeof (void*);
+       }
+
+       while (p < end)
+               *p++ = 0;
+}
+
+
+/**
+ * Move @size bytes from @src to @dest.
+ * size MUST be a multiple of sizeof (gpointer)
+ *
+ * FIXME borrow faster code from some BSD libc or bionic
+ */
+void
+mono_gc_memmove (void *dest, const void *src, size_t size)
+{
+       /*
+        * If dest and src are differently aligned with respect to
+        * pointer size then it makes no sense to do aligned copying.
+        * In fact, we would end up with unaligned loads which is
+        * incorrect on some architectures.
+        */
+       if ((char*)dest - (char*)align_down (dest) != (char*)src - (char*)align_down (src)) {
+               memmove (dest, src, size);
+               return;
+       }
+
+       /*
+        * A bit of explanation on why we align only dest before doing word copies.
+        * Pointers to managed objects must always be stored in word aligned addresses, so
+        * even if dest is misaligned, src will be by the same amount - this ensure proper atomicity of reads.
+        */
+       if (dest > src && ((size_t)((char*)dest - (char*)src) < size)) {
+               char *p = (char*)dest + size;
+               char *s = (char*)src + size;
+               char *start = (char*)dest;
+               char *align_end = MAX((char*)dest, (char*)align_down (p));
+               char *word_start;
+
+               while (p > align_end)
+                       *--p = *--s;
+
+               word_start = align_up (start);
+               while (p > word_start) {
+                       p -= sizeof (void*);
+                       s -= sizeof (void*);
+                       *((void**)p) = *((void**)s);
+               }
+
+               while (p > start)
+                       *--p = *--s;
+       } else {
+               char *p = (char*)dest;
+               char *s = (char*)src;
+               char *end = p + size;
+               char *align_end = MIN ((char*)end, (char*)align_up (p));
+               char *word_end;
+
+               while (p < align_end)
+                       *p++ = *s++;
+
+               word_end = align_down (end);
+               while (p < word_end) {
+                       *((void**)p) = *((void**)s);
+                       p += sizeof (void*);
+                       s += sizeof (void*);
+               }
+
+               while (p < end)
+                       *p++ = *s++;
+       }
 }