Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / mono / metadata / threads.c
index 86c6b8173ea3d49e8b709c557cc835f613784a68..43c8a61d9ab8159f75591149aa572c470dc52227 100644 (file)
@@ -1,5 +1,6 @@
-/*
- * threads.c: Thread support internal calls
+/**
+ * \file
+ * Thread support internal calls
  *
  * Author:
  *     Dick Porter (dick@ximian.com)
@@ -44,6 +45,7 @@
 #include <mono/utils/mono-error-internals.h>
 #include <mono/utils/os-event.h>
 #include <mono/utils/mono-threads-debug.h>
+#include <mono/utils/unlocked.h>
 #include <mono/metadata/w32handle.h>
 #include <mono/metadata/w32event.h>
 #include <mono/metadata/w32mutex.h>
@@ -52,6 +54,7 @@
 #include <mono/metadata/abi-details.h>
 #include <mono/metadata/w32error.h>
 #include <mono/utils/w32api.h>
+#include <mono/utils/mono-os-wait.h>
 
 #ifdef HAVE_SIGNAL_H
 #include <signal.h>
 
 #if defined(HOST_WIN32)
 #include <objbase.h>
+
+extern gboolean
+mono_native_thread_join_handle (HANDLE thread_handle, gboolean close_handle);
 #endif
 
-#if defined(PLATFORM_ANDROID) && !defined(TARGET_ARM64) && !defined(TARGET_AMD64)
+#if defined(HOST_ANDROID) && !defined(TARGET_ARM64) && !defined(TARGET_AMD64)
 #define USE_TKILL_ON_ANDROID 1
 #endif
 
-#ifdef PLATFORM_ANDROID
+#ifdef HOST_ANDROID
 #include <errno.h>
 
 #ifdef USE_TKILL_ON_ANDROID
@@ -134,7 +140,7 @@ static StaticDataInfo context_static_info;
 static MonoGHashTable *threads=NULL;
 
 /* List of app context GC handles.
- * Added to from ves_icall_System_Runtime_Remoting_Contexts_Context_RegisterContext ().
+ * Added to from mono_threads_register_app_context ().
  */
 static GHashTable *contexts = NULL;
 
@@ -151,7 +157,7 @@ static MonoGHashTable *threads_starting_up = NULL;
 /* Contains tids */
 /* Protected by the threads lock */
 static GHashTable *joinable_threads;
-static int joinable_thread_count;
+static gint32 joinable_thread_count;
 
 #define SET_CURRENT_OBJECT(x) mono_tls_set_thread (x)
 #define GET_CURRENT_OBJECT() (MonoInternalThread*) mono_tls_get_thread ()
@@ -197,9 +203,6 @@ static gboolean shutting_down = FALSE;
 
 static gint32 managed_thread_id_counter = 0;
 
-/* Class lazy loading functions */
-static GENERATE_GET_CLASS_WITH_CACHE (appdomain_unloaded_exception, "System", "AppDomainUnloadedException")
-
 static void
 mono_threads_lock (void)
 {
@@ -261,8 +264,14 @@ mono_threads_begin_abort_protected_block (void)
        } while (InterlockedCompareExchangePointer ((volatile gpointer)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
 
        /* Defer async request since we won't be able to process until exiting the block */
-       if (new_val == 1 && (new_state & INTERRUPT_ASYNC_REQUESTED_BIT))
+       if (new_val == 1 && (new_state & INTERRUPT_ASYNC_REQUESTED_BIT)) {
                InterlockedDecrement (&thread_interruption_requested);
+               THREADS_INTERRUPT_DEBUG ("[%d] begin abort protected block old_state %ld new_state %ld, defer tir %d\n", thread->small_id, old_state, new_state, thread_interruption_requested);
+               if (thread_interruption_requested < 0)
+                       g_warning ("bad thread_interruption_requested state");
+       } else {
+               THREADS_INTERRUPT_DEBUG ("[%d] begin abort protected block old_state %ld new_state %ld, tir %d\n", thread->small_id, old_state, new_state, thread_interruption_requested);
+       }
 }
 
 static gboolean
@@ -296,8 +305,12 @@ mono_threads_end_abort_protected_block (void)
                new_state = old_state - (1 << ABORT_PROT_BLOCK_SHIFT);
        } while (InterlockedCompareExchangePointer ((volatile gpointer)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
 
-       if (new_val == 0 && (new_state & INTERRUPT_ASYNC_REQUESTED_BIT))
+       if (new_val == 0 && (new_state & INTERRUPT_ASYNC_REQUESTED_BIT)) {
                InterlockedIncrement (&thread_interruption_requested);
+               THREADS_INTERRUPT_DEBUG ("[%d] end abort protected block old_state %ld new_state %ld, restore tir %d\n", thread->small_id, old_state, new_state, thread_interruption_requested);
+       } else {
+               THREADS_INTERRUPT_DEBUG ("[%d] end abort protected block old_state %ld new_state %ld, tir %d\n", thread->small_id, old_state, new_state, thread_interruption_requested);
+       }
 
        return mono_thread_state_has_interruption (new_state);
 }
@@ -333,7 +346,9 @@ mono_thread_clear_interruption_requested (MonoInternalThread *thread)
        } while (InterlockedCompareExchangePointer ((volatile gpointer)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
 
        InterlockedDecrement (&thread_interruption_requested);
-
+       THREADS_INTERRUPT_DEBUG ("[%d] clear interruption old_state %ld new_state %ld, tir %d\n", thread->small_id, old_state, new_state, thread_interruption_requested);
+       if (thread_interruption_requested < 0)
+               g_warning ("bad thread_interruption_requested state");
        return TRUE;
 }
 
@@ -358,8 +373,12 @@ mono_thread_set_interruption_requested (MonoInternalThread *thread)
                        new_state = old_state | INTERRUPT_ASYNC_REQUESTED_BIT;
        } while (InterlockedCompareExchangePointer ((volatile gpointer)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
 
-       if (sync || !(new_state & ABORT_PROT_BLOCK_MASK))
+       if (sync || !(new_state & ABORT_PROT_BLOCK_MASK)) {
                InterlockedIncrement (&thread_interruption_requested);
+               THREADS_INTERRUPT_DEBUG ("[%d] set interruption on [%d] old_state %ld new_state %ld, tir %d\n", mono_thread_internal_current ()->small_id, thread->small_id, old_state, new_state, thread_interruption_requested);
+       } else {
+               THREADS_INTERRUPT_DEBUG ("[%d] set interruption on [%d] old_state %ld new_state %ld, tir deferred %d\n", mono_thread_internal_current ()->small_id, thread->small_id, old_state, new_state, thread_interruption_requested);
+       }
 
        return sync || !(new_state & ABORT_PROT_BLOCK_MASK);
 }
@@ -445,8 +464,8 @@ typedef union {
 #define SPECIAL_STATIC_OFFSET_TYPE_THREAD 0
 #define SPECIAL_STATIC_OFFSET_TYPE_CONTEXT 1
 
-#define MAKE_SPECIAL_STATIC_OFFSET(index, offset, type) \
-       ((SpecialStaticOffset) { .fields = { (index), (offset), (type) } }.raw)
+#define MAKE_SPECIAL_STATIC_OFFSET(idx, off, ty) \
+       ((SpecialStaticOffset) { .fields = { .index = (idx), .offset = (off), .type = (ty) } }.raw)
 #define ACCESS_SPECIAL_STATIC_OFFSET(x,f) \
        (((SpecialStaticOffset *) &(x))->fields.f)
 
@@ -641,8 +660,18 @@ mono_thread_attach_internal (MonoThread *thread, gboolean force_attach, gboolean
        g_assert (thread);
 
        info = mono_thread_info_current ();
+       g_assert (info);
 
        internal = thread->internal_thread;
+       g_assert (internal);
+
+       /* It is needed to store the MonoInternalThread on the MonoThreadInfo, because of the following case:
+        *  - the MonoInternalThread TLS key is destroyed: set it to NULL
+        *  - the MonoThreadInfo TLS key is destroyed: calls mono_thread_info_detach
+        *    - it calls MonoThreadInfoCallbacks.thread_detach
+        *      - mono_thread_internal_current returns NULL -> fails to detach the MonoInternalThread. */
+       mono_thread_info_set_internal_thread_gchandle (info, mono_gchandle_new ((MonoObject*) internal, FALSE));
+
        internal->handle = mono_threads_open_thread_handle (info->handle);
 #ifdef HOST_WIN32
        internal->native_handle = OpenThread (THREAD_ALL_ACCESS, FALSE, GetCurrentThreadId ());
@@ -710,6 +739,158 @@ mono_thread_attach_internal (MonoThread *thread, gboolean force_attach, gboolean
        return TRUE;
 }
 
+static void
+mono_thread_detach_internal (MonoInternalThread *thread)
+{
+       gboolean removed;
+
+       g_assert (thread != NULL);
+       SET_CURRENT_OBJECT (thread);
+
+       THREAD_DEBUG (g_message ("%s: mono_thread_detach for %p (%"G_GSIZE_FORMAT")", __func__, thread, (gsize)thread->tid));
+
+#ifndef HOST_WIN32
+       mono_w32mutex_abandon ();
+#endif
+
+       if (thread->abort_state_handle) {
+               mono_gchandle_free (thread->abort_state_handle);
+               thread->abort_state_handle = 0;
+       }
+
+       thread->abort_exc = NULL;
+       thread->current_appcontext = NULL;
+
+       /*
+       * Prevent race condition between execution of this method and runtime shutdown.
+       * Adding runtime thread to the joinable threads list will make sure runtime shutdown
+       * won't complete until added runtime thread have exited. Owner of threads attached to the
+       * runtime but not identified as runtime threads needs to make sure thread detach calls won't
+       * race with runtime shutdown.
+       */
+       mono_threads_add_joinable_runtime_thread (thread->thread_info);
+
+       /*
+        * thread->synch_cs can be NULL if this was called after
+        * ves_icall_System_Threading_InternalThread_Thread_free_internal.
+        * This can happen only during shutdown.
+        * The shutting_down flag is not always set, so we can't assert on it.
+        */
+       if (thread->synch_cs)
+               LOCK_THREAD (thread);
+
+       thread->state |= ThreadState_Stopped;
+       thread->state &= ~ThreadState_Background;
+
+       if (thread->synch_cs)
+               UNLOCK_THREAD (thread);
+
+       /*
+       An interruption request has leaked to cleanup. Adjust the global counter.
+
+       This can happen is the abort source thread finds the abortee (this) thread
+       in unmanaged code. If this thread never trips back to managed code or check
+       the local flag it will be left set and positively unbalance the global counter.
+
+       Leaving the counter unbalanced will cause a performance degradation since all threads
+       will now keep checking their local flags all the time.
+       */
+       mono_thread_clear_interruption_requested (thread);
+
+       mono_threads_lock ();
+
+       if (!threads) {
+               removed = FALSE;
+       } else if (mono_g_hash_table_lookup (threads, (gpointer)thread->tid) != thread) {
+               /* We have to check whether the thread object for the
+                * tid is still the same in the table because the
+                * thread might have been destroyed and the tid reused
+                * in the meantime, in which case the tid would be in
+                * the table, but with another thread object.
+                */
+               removed = FALSE;
+       } else {
+               mono_g_hash_table_remove (threads, (gpointer)thread->tid);
+               removed = TRUE;
+       }
+
+       mono_threads_unlock ();
+
+       /* Don't close the handle here, wait for the object finalizer
+        * to do it. Otherwise, the following race condition applies:
+        *
+        * 1) Thread exits (and mono_thread_detach_internal() closes the handle)
+        *
+        * 2) Some other handle is reassigned the same slot
+        *
+        * 3) Another thread tries to join the first thread, and
+        * blocks waiting for the reassigned handle to be signalled
+        * (which might never happen).  This is possible, because the
+        * thread calling Join() still has a reference to the first
+        * thread's object.
+        */
+
+       /* if the thread is not in the hash it has been removed already */
+       if (!removed) {
+               mono_domain_unset ();
+               mono_memory_barrier ();
+
+               if (mono_thread_cleanup_fn)
+                       mono_thread_cleanup_fn (thread_get_tid (thread));
+
+               goto done;
+       }
+
+       mono_release_type_locks (thread);
+
+       /* Can happen when we attach the profiler helper thread in order to heapshot. */
+       if (!mono_thread_info_lookup (MONO_UINT_TO_NATIVE_THREAD_ID (thread->tid))->tools_thread)
+               MONO_PROFILER_RAISE (thread_stopped, (thread->tid));
+
+       mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
+
+       /*
+        * This will signal async signal handlers that the thread has exited.
+        * The profiler callback needs this to be set, so it cannot be done earlier.
+        */
+       mono_domain_unset ();
+       mono_memory_barrier ();
+
+       if (thread == mono_thread_internal_current ())
+               mono_thread_pop_appdomain_ref ();
+
+       mono_free_static_data (thread->static_data);
+       thread->static_data = NULL;
+       ref_stack_destroy (thread->appdomain_refs);
+       thread->appdomain_refs = NULL;
+
+       g_assert (thread->suspended);
+       mono_os_event_destroy (thread->suspended);
+       g_free (thread->suspended);
+       thread->suspended = NULL;
+
+       if (mono_thread_cleanup_fn)
+               mono_thread_cleanup_fn (thread_get_tid (thread));
+
+       mono_memory_barrier ();
+
+       if (mono_gc_is_moving ()) {
+               MONO_GC_UNREGISTER_ROOT (thread->thread_pinning_ref);
+               thread->thread_pinning_ref = NULL;
+       }
+
+done:
+       SET_CURRENT_OBJECT (NULL);
+       mono_domain_unset ();
+
+       mono_thread_info_unset_internal_thread_gchandle ((MonoThreadInfo*) thread->thread_info);
+
+       /* Don't need to close the handle to this thread, even though we took a
+        * reference in mono_thread_attach (), because the GC will do it
+        * when the Thread object is finalised.
+        */
+}
+
 typedef struct {
        gint32 ref;
        MonoThread *thread;
@@ -797,12 +978,12 @@ static guint32 WINAPI start_wrapper_internal(StartInfo *start_info, gsize *stack
         * to lock the thread, and the lock is held by thread_start () which waits for
         * start_notify.
         */
-       mono_profiler_thread_start (tid);
+       MONO_PROFILER_RAISE (thread_started, (tid));
 
        /* if the name was set before starting, we didn't invoke the profiler callback */
        if (internal->name) {
                char *tname = g_utf16_to_utf8 (internal->name, internal->name_len, NULL, NULL, NULL);
-               mono_profiler_thread_name (internal->tid, tname);
+               MONO_PROFILER_RAISE (thread_name, (internal->tid, tname));
                mono_native_thread_set_name (MONO_UINT_TO_NATIVE_THREAD_ID (internal->tid), tname);
                g_free (tname);
        }
@@ -852,9 +1033,7 @@ static guint32 WINAPI start_wrapper_internal(StartInfo *start_info, gsize *stack
 
        mono_thread_detach_internal (internal);
 
-       internal->tid = 0;
-
-       return(0);
+       return 0;
 }
 
 static gsize WINAPI
@@ -867,11 +1046,11 @@ start_wrapper (gpointer data)
        start_info = (StartInfo*) data;
        g_assert (start_info);
 
-       info = mono_thread_info_attach (&res);
+       info = mono_thread_info_attach ();
        info->runtime_thread = TRUE;
 
        /* Run the actual main function of the thread */
-       res = start_wrapper_internal (start_info, &res);
+       res = start_wrapper_internal (start_info, info->stack_end);
 
        mono_thread_info_exit (res);
 
@@ -948,7 +1127,7 @@ create_thread (MonoThread *thread, MonoInternalThread *internal, MonoObject *sta
        else
                stack_set_size = 0;
 
-       if (!mono_thread_platform_create_thread (start_wrapper, start_info, &stack_set_size, &tid)) {
+       if (!mono_thread_platform_create_thread ((MonoThreadStart)start_wrapper, start_info, &stack_set_size, &tid)) {
                /* The thread couldn't be created, so set an exception */
                mono_threads_lock ();
                mono_g_hash_table_remove (threads_starting_up, thread);
@@ -986,19 +1165,31 @@ done:
        return ret;
 }
 
-void mono_thread_new_init (intptr_t tid, gpointer stack_start, gpointer func)
+/**
+ * mono_thread_new_init:
+ */
+void
+mono_thread_new_init (intptr_t tid, gpointer stack_start, gpointer func)
 {
        if (mono_thread_start_cb) {
                mono_thread_start_cb (tid, stack_start, func);
        }
 }
 
-void mono_threads_set_default_stacksize (guint32 stacksize)
+/**
+ * mono_threads_set_default_stacksize:
+ */
+void
+mono_threads_set_default_stacksize (guint32 stacksize)
 {
        default_stacksize = stacksize;
 }
 
-guint32 mono_threads_get_default_stacksize (void)
+/**
+ * mono_threads_get_default_stacksize:
+ */
+guint32
+mono_threads_get_default_stacksize (void)
 {
        return default_stacksize;
 }
@@ -1031,6 +1222,9 @@ mono_thread_create_internal (MonoDomain *domain, gpointer func, gpointer arg, Mo
        return internal;
 }
 
+/**
+ * mono_thread_create:
+ */
 void
 mono_thread_create (MonoDomain *domain, gpointer func, gpointer arg)
 {
@@ -1045,22 +1239,13 @@ mono_thread_create_checked (MonoDomain *domain, gpointer func, gpointer arg, Mon
        return (NULL != mono_thread_create_internal (domain, func, arg, MONO_THREAD_CREATE_FLAGS_NONE, error));
 }
 
-MonoThread *
-mono_thread_attach (MonoDomain *domain)
-{
-       MonoThread *thread = mono_thread_attach_full (domain, FALSE);
-
-       return thread;
-}
-
-MonoThread *
+static MonoThread *
 mono_thread_attach_full (MonoDomain *domain, gboolean force_attach)
 {
        MonoInternalThread *internal;
        MonoThread *thread;
        MonoThreadInfo *info;
        MonoNativeThreadId tid;
-       gsize stack_ptr;
 
        if (mono_thread_internal_current_is_attached ()) {
                if (domain != mono_domain_get ())
@@ -1069,7 +1254,7 @@ mono_thread_attach_full (MonoDomain *domain, gboolean force_attach)
                return mono_thread_current ();
        }
 
-       info = mono_thread_info_attach (&stack_ptr);
+       info = mono_thread_info_attach ();
        g_assert (info);
 
        tid=mono_native_thread_id_get ();
@@ -1086,166 +1271,28 @@ mono_thread_attach_full (MonoDomain *domain, gboolean force_attach)
 
        THREAD_DEBUG (g_message ("%s: Attached thread ID %"G_GSIZE_FORMAT" (handle %p)", __func__, tid, internal->handle));
 
-       if (mono_thread_attach_cb) {
-               guint8 *staddr;
-               size_t stsize;
-
-               mono_thread_info_get_stack_bounds (&staddr, &stsize);
-
-               if (staddr == NULL)
-                       mono_thread_attach_cb (MONO_NATIVE_THREAD_ID_TO_UINT (tid), &stack_ptr);
-               else
-                       mono_thread_attach_cb (MONO_NATIVE_THREAD_ID_TO_UINT (tid), staddr + stsize);
-       }
+       if (mono_thread_attach_cb)
+               mono_thread_attach_cb (MONO_NATIVE_THREAD_ID_TO_UINT (tid), info->stack_end);
 
        /* Can happen when we attach the profiler helper thread in order to heapshot. */
        if (!mono_thread_info_current ()->tools_thread)
-               // FIXME: Need a separate callback
-               mono_profiler_thread_start (MONO_NATIVE_THREAD_ID_TO_UINT (tid));
+               MONO_PROFILER_RAISE (thread_started, (MONO_NATIVE_THREAD_ID_TO_UINT (tid)));
 
        return thread;
 }
 
-void
-mono_thread_detach_internal (MonoInternalThread *thread)
+/**
+ * mono_thread_attach:
+ */
+MonoThread *
+mono_thread_attach (MonoDomain *domain)
 {
-       gboolean removed;
-
-       g_assert (thread != NULL);
-
-       THREAD_DEBUG (g_message ("%s: mono_thread_detach for %p (%"G_GSIZE_FORMAT")", __func__, thread, (gsize)thread->tid));
-
-#ifndef HOST_WIN32
-       mono_w32mutex_abandon ();
-#endif
-
-       if (thread->abort_state_handle) {
-               mono_gchandle_free (thread->abort_state_handle);
-               thread->abort_state_handle = 0;
-       }
-
-       thread->abort_exc = NULL;
-       thread->current_appcontext = NULL;
-
-       /*
-        * thread->synch_cs can be NULL if this was called after
-        * ves_icall_System_Threading_InternalThread_Thread_free_internal.
-        * This can happen only during shutdown.
-        * The shutting_down flag is not always set, so we can't assert on it.
-        */
-       if (thread->synch_cs)
-               LOCK_THREAD (thread);
-
-       thread->state |= ThreadState_Stopped;
-       thread->state &= ~ThreadState_Background;
-
-       if (thread->synch_cs)
-               UNLOCK_THREAD (thread);
-
-       /*
-       An interruption request has leaked to cleanup. Adjust the global counter.
-
-       This can happen is the abort source thread finds the abortee (this) thread
-       in unmanaged code. If this thread never trips back to managed code or check
-       the local flag it will be left set and positively unbalance the global counter.
-       
-       Leaving the counter unbalanced will cause a performance degradation since all threads
-       will now keep checking their local flags all the time.
-       */
-       mono_thread_clear_interruption_requested (thread);
-
-       mono_threads_lock ();
-
-       if (!threads) {
-               removed = FALSE;
-       } else if (mono_g_hash_table_lookup (threads, (gpointer)thread->tid) != thread) {
-               /* We have to check whether the thread object for the
-                * tid is still the same in the table because the
-                * thread might have been destroyed and the tid reused
-                * in the meantime, in which case the tid would be in
-                * the table, but with another thread object.
-                */
-               removed = FALSE;
-       } else {
-               mono_g_hash_table_remove (threads, (gpointer)thread->tid);
-               removed = TRUE;
-       }
-
-       mono_threads_unlock ();
-
-       /* Don't close the handle here, wait for the object finalizer
-        * to do it. Otherwise, the following race condition applies:
-        *
-        * 1) Thread exits (and mono_thread_detach_internal() closes the handle)
-        *
-        * 2) Some other handle is reassigned the same slot
-        *
-        * 3) Another thread tries to join the first thread, and
-        * blocks waiting for the reassigned handle to be signalled
-        * (which might never happen).  This is possible, because the
-        * thread calling Join() still has a reference to the first
-        * thread's object.
-        */
-
-       /* if the thread is not in the hash it has been removed already */
-       if (!removed) {
-               mono_domain_unset ();
-               mono_memory_barrier ();
-
-               if (mono_thread_cleanup_fn)
-                       mono_thread_cleanup_fn (thread_get_tid (thread));
-
-               goto done;
-       }
-
-       mono_release_type_locks (thread);
-
-       /* Can happen when we attach the profiler helper thread in order to heapshot. */
-       if (!mono_thread_info_lookup (MONO_UINT_TO_NATIVE_THREAD_ID (thread->tid))->tools_thread)
-               mono_profiler_thread_end (thread->tid);
-
-       mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
-
-       /*
-        * This will signal async signal handlers that the thread has exited.
-        * The profiler callback needs this to be set, so it cannot be done earlier.
-        */
-       mono_domain_unset ();
-       mono_memory_barrier ();
-
-       if (thread == mono_thread_internal_current ())
-               mono_thread_pop_appdomain_ref ();
-
-       mono_free_static_data (thread->static_data);
-       thread->static_data = NULL;
-       ref_stack_destroy (thread->appdomain_refs);
-       thread->appdomain_refs = NULL;
-
-       g_assert (thread->suspended);
-       mono_os_event_destroy (thread->suspended);
-       g_free (thread->suspended);
-       thread->suspended = NULL;
-
-       if (mono_thread_cleanup_fn)
-               mono_thread_cleanup_fn (thread_get_tid (thread));
-
-       mono_memory_barrier ();
-
-       if (mono_gc_is_moving ()) {
-               MONO_GC_UNREGISTER_ROOT (thread->thread_pinning_ref);
-               thread->thread_pinning_ref = NULL;
-       }
-
-done:
-       SET_CURRENT_OBJECT (NULL);
-       mono_domain_unset ();
-
-       /* Don't need to close the handle to this thread, even though we took a
-        * reference in mono_thread_attach (), because the GC will do it
-        * when the Thread object is finalised.
-        */
+       return mono_thread_attach_full (domain, FALSE);
 }
 
+/**
+ * mono_thread_detach:
+ */
 void
 mono_thread_detach (MonoThread *thread)
 {
@@ -1253,12 +1300,12 @@ mono_thread_detach (MonoThread *thread)
                mono_thread_detach_internal (thread->internal_thread);
 }
 
-/*
+/**
  * mono_thread_detach_if_exiting:
  *
- *   Detach the current thread from the runtime if it is exiting, i.e. it is running pthread dtors.
+ * Detach the current thread from the runtime if it is exiting, i.e. it is running pthread dtors.
  * This should be used at the end of embedding code which calls into managed code, and which
- * can be called from pthread dtors, like dealloc: implementations in objective-c.
+ * can be called from pthread dtors, like <code>dealloc:</code> implementations in Objective-C.
  */
 mono_bool
 mono_thread_detach_if_exiting (void)
@@ -1288,6 +1335,9 @@ mono_thread_internal_current_is_attached (void)
        return TRUE;
 }
 
+/**
+ * mono_thread_exit:
+ */
 void
 mono_thread_exit (void)
 {
@@ -1480,10 +1530,9 @@ mono_thread_get_name (MonoInternalThread *this_obj, guint32 *name_len)
        return res;
 }
 
-/*
+/**
  * mono_thread_get_name_utf8:
- *
- * Return the name of the thread in UTF-8.
+ * \returns the name of the thread in UTF-8.
  * Return NULL if the thread has no name.
  * The returned memory is owned by the caller.
  */
@@ -1506,11 +1555,10 @@ mono_thread_get_name_utf8 (MonoThread *thread)
        return tname;
 }
 
-/*
+/**
  * mono_thread_get_managed_id:
- *
- * Return the Thread.ManagedThreadId value of `thread`.
- * Returns -1 if `thread` is NULL.
+ * \returns the \c Thread.ManagedThreadId value of \p thread.
+ * Returns \c -1 if \p thread is NULL.
  */
 int32_t
 mono_thread_get_managed_id (MonoThread *thread)
@@ -1553,6 +1601,8 @@ ves_icall_System_Threading_Thread_GetName_internal (MonoInternalThread *this_obj
 void 
 mono_thread_set_name_internal (MonoInternalThread *this_obj, MonoString *name, gboolean permanent, gboolean reset, MonoError *error)
 {
+       MonoNativeThreadId tid = 0;
+
        LOCK_THREAD (this_obj);
 
        error_init (error);
@@ -1579,14 +1629,16 @@ mono_thread_set_name_internal (MonoInternalThread *this_obj, MonoString *name, g
        else
                this_obj->name = NULL;
 
-       
+       if (!(this_obj->state & ThreadState_Stopped))
+               tid = thread_get_tid (this_obj);
+
        UNLOCK_THREAD (this_obj);
 
-       if (this_obj->name && this_obj->tid) {
+       if (this_obj->name && tid) {
                char *tname = mono_string_to_utf8_checked (name, error);
                return_if_nok (error);
-               mono_profiler_thread_name (this_obj->tid, tname);
-               mono_native_thread_set_name (thread_get_tid (this_obj), tname);
+               MONO_PROFILER_RAISE (thread_name, ((uintptr_t)tid, tname));
+               mono_native_thread_set_name (tid, tname);
                mono_free (tname);
        }
 }
@@ -1675,6 +1727,9 @@ ves_icall_System_Threading_Thread_ByteArrayToCurrentDomain (MonoArray *arr)
        return result;
 }
 
+/**
+ * mono_thread_current:
+ */
 MonoThread *
 mono_thread_current (void)
 {
@@ -1759,7 +1814,7 @@ mono_join_uninterrupted (MonoThreadHandle* thread_to_join, gint32 ms, MonoError
 }
 
 gboolean
-ves_icall_System_Threading_Thread_Join_internal(MonoThread *this_obj, int ms)
+ves_icall_System_Threading_Thread_Join_internal (MonoThread *this_obj, int ms)
 {
        MonoInternalThread *thread = this_obj->internal_thread;
        MonoThreadHandle *handle = thread->handle;
@@ -1781,207 +1836,120 @@ ves_icall_System_Threading_Thread_Join_internal(MonoThread *this_obj, int ms)
 
        UNLOCK_THREAD (thread);
 
-       if(ms== -1) {
-               ms=MONO_INFINITE_WAIT;
-       }
+       if (ms == -1)
+               ms = MONO_INFINITE_WAIT;
        THREAD_DEBUG (g_message ("%s: joining thread handle %p, %d ms", __func__, handle, ms));
-       
+
        mono_thread_set_state (cur_thread, ThreadState_WaitSleepJoin);
 
-       ret=mono_join_uninterrupted (handle, ms, &error);
+       ret = mono_join_uninterrupted (handle, ms, &error);
 
        mono_thread_clr_state (cur_thread, ThreadState_WaitSleepJoin);
 
        mono_error_set_pending_exception (&error);
 
-       if(ret==MONO_THREAD_INFO_WAIT_RET_SUCCESS_0) {
+       if (ret == MONO_THREAD_INFO_WAIT_RET_SUCCESS_0) {
                THREAD_DEBUG (g_message ("%s: join successful", __func__));
 
-               return(TRUE);
-       }
-       
-       THREAD_DEBUG (g_message ("%s: join failed", __func__));
-
-       return(FALSE);
-}
-
-#define MANAGED_WAIT_FAILED 0x7fffffff
+               /* Wait for the thread to really exit */
+               MonoNativeThreadId tid = thread_get_tid (thread);
+               mono_thread_join (tid);
 
-static gint32
-map_native_wait_result_to_managed (MonoW32HandleWaitRet val, gsize numobjects)
-{
-       if (val >= MONO_W32HANDLE_WAIT_RET_SUCCESS_0 && val < MONO_W32HANDLE_WAIT_RET_SUCCESS_0 + numobjects) {
-               return WAIT_OBJECT_0 + (val - MONO_W32HANDLE_WAIT_RET_SUCCESS_0);
-       } else if (val >= MONO_W32HANDLE_WAIT_RET_ABANDONED_0 && val < MONO_W32HANDLE_WAIT_RET_ABANDONED_0 + numobjects) {
-               return WAIT_ABANDONED_0 + (val - MONO_W32HANDLE_WAIT_RET_ABANDONED_0);
-       } else if (val == MONO_W32HANDLE_WAIT_RET_ALERTED) {
-               return WAIT_IO_COMPLETION;
-       } else if (val == MONO_W32HANDLE_WAIT_RET_TIMEOUT) {
-               return WAIT_TIMEOUT;
-       } else if (val == MONO_W32HANDLE_WAIT_RET_FAILED) {
-               /* WAIT_FAILED in waithandle.cs is different from WAIT_FAILED in Win32 API */
-               return MANAGED_WAIT_FAILED;
-       } else {
-               g_error ("%s: unknown val value %d", __func__, val);
-       }
-}
-
-static MonoW32HandleWaitRet
-mono_wait_uninterrupted (MonoInternalThread *thread, guint32 numhandles, gpointer *handles, gboolean waitall, gint32 ms, MonoError *error)
-{
-       MonoException *exc;
-       MonoW32HandleWaitRet ret;
-       gint64 start;
-       gint32 diff_ms;
-       gint32 wait = ms;
-
-       error_init (error);
-
-       start = (ms == -1) ? 0 : mono_100ns_ticks ();
-       do {
-               MONO_ENTER_GC_SAFE;
-#ifdef HOST_WIN32
-               if (numhandles != 1)
-                       ret = mono_w32handle_convert_wait_ret (WaitForMultipleObjectsEx (numhandles, handles, waitall, wait, TRUE), numhandles);
-               else
-                       ret = mono_w32handle_convert_wait_ret (WaitForSingleObjectEx (handles [0], ms, TRUE), 1);
-#else
-               /* mono_w32handle_wait_multiple optimizes the case for numhandles == 1 */
-               ret = mono_w32handle_wait_multiple (handles, numhandles, waitall, wait, TRUE);
-#endif /* HOST_WIN32 */
-               MONO_EXIT_GC_SAFE;
-
-               if (ret != MONO_W32HANDLE_WAIT_RET_ALERTED)
-                       break;
-
-               exc = mono_thread_execute_interruption ();
-               if (exc) {
-                       mono_error_set_exception_instance (error, exc);
-                       break;
-               }
-
-               if (ms == -1)
-                       continue;
-
-               /* Re-calculate ms according to the time passed */
-               diff_ms = (gint32)((mono_100ns_ticks () - start) / 10000);
-               if (diff_ms >= ms) {
-                       ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
-                       break;
-               }
-               wait = ms - diff_ms;
-       } while (TRUE);
-       
-       return ret;
-}
-
-gint32 ves_icall_System_Threading_WaitHandle_WaitAll_internal(MonoArray *mono_handles, gint32 ms)
-{
-       MonoError error;
-       HANDLE *handles;
-       guint32 numhandles;
-       MonoW32HandleWaitRet ret;
-       guint32 i;
-       MonoObject *waitHandle;
-       MonoInternalThread *thread = mono_thread_internal_current ();
-
-       /* Do this WaitSleepJoin check before creating objects */
-       if (mono_thread_current_check_pending_interrupt ())
-               return map_native_wait_result_to_managed (MONO_W32HANDLE_WAIT_RET_FAILED, 0);
-
-       /* We fail in managed if the array has more than 64 elements */
-       numhandles = (guint32)mono_array_length(mono_handles);
-       handles = g_new0(HANDLE, numhandles);
-
-       for(i = 0; i < numhandles; i++) {       
-               waitHandle = mono_array_get(mono_handles, MonoObject*, i);
-               handles [i] = mono_wait_handle_get_handle ((MonoWaitHandle *) waitHandle);
-       }
-       
-       if(ms== -1) {
-               ms=MONO_INFINITE_WAIT;
+               return TRUE;
        }
+       
+       THREAD_DEBUG (g_message ("%s: join failed", __func__));
 
-       mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
-
-       ret = mono_wait_uninterrupted (thread, numhandles, handles, TRUE, ms, &error);
-
-       mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
-
-       g_free(handles);
+       return FALSE;
+}
 
-       mono_error_set_pending_exception (&error);
+#define MANAGED_WAIT_FAILED 0x7fffffff
 
-       return map_native_wait_result_to_managed (ret, numhandles);
+static gint32
+map_native_wait_result_to_managed (MonoW32HandleWaitRet val, gsize numobjects)
+{
+       if (val >= MONO_W32HANDLE_WAIT_RET_SUCCESS_0 && val < MONO_W32HANDLE_WAIT_RET_SUCCESS_0 + numobjects) {
+               return WAIT_OBJECT_0 + (val - MONO_W32HANDLE_WAIT_RET_SUCCESS_0);
+       } else if (val >= MONO_W32HANDLE_WAIT_RET_ABANDONED_0 && val < MONO_W32HANDLE_WAIT_RET_ABANDONED_0 + numobjects) {
+               return WAIT_ABANDONED_0 + (val - MONO_W32HANDLE_WAIT_RET_ABANDONED_0);
+       } else if (val == MONO_W32HANDLE_WAIT_RET_ALERTED) {
+               return WAIT_IO_COMPLETION;
+       } else if (val == MONO_W32HANDLE_WAIT_RET_TIMEOUT) {
+               return WAIT_TIMEOUT;
+       } else if (val == MONO_W32HANDLE_WAIT_RET_FAILED) {
+               /* WAIT_FAILED in waithandle.cs is different from WAIT_FAILED in Win32 API */
+               return MANAGED_WAIT_FAILED;
+       } else {
+               g_error ("%s: unknown val value %d", __func__, val);
+       }
 }
 
-gint32 ves_icall_System_Threading_WaitHandle_WaitAny_internal(MonoArray *mono_handles, gint32 ms)
+gint32
+ves_icall_System_Threading_WaitHandle_Wait_internal (gpointer *handles, gint32 numhandles, MonoBoolean waitall, gint32 timeout, MonoError *error)
 {
-       MonoError error;
-       HANDLE handles [MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS];
-       uintptr_t numhandles;
        MonoW32HandleWaitRet ret;
-       guint32 i;
-       MonoObject *waitHandle;
-       MonoInternalThread *thread = mono_thread_internal_current ();
+       MonoInternalThread *thread;
+       MonoException *exc;
+       gint64 start;
+       guint32 timeoutLeft;
 
        /* Do this WaitSleepJoin check before creating objects */
        if (mono_thread_current_check_pending_interrupt ())
                return map_native_wait_result_to_managed (MONO_W32HANDLE_WAIT_RET_FAILED, 0);
 
-       numhandles = mono_array_length(mono_handles);
-       if (numhandles > MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS)
-               return map_native_wait_result_to_managed (MONO_W32HANDLE_WAIT_RET_FAILED, 0);
-
-       for(i = 0; i < numhandles; i++) {       
-               waitHandle = mono_array_get(mono_handles, MonoObject*, i);
-               handles [i] = mono_wait_handle_get_handle ((MonoWaitHandle *) waitHandle);
-       }
-       
-       if(ms== -1) {
-               ms=MONO_INFINITE_WAIT;
-       }
+       thread = mono_thread_internal_current ();
 
        mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
 
-       ret = mono_wait_uninterrupted (thread, numhandles, handles, FALSE, ms, &error);
+       if (timeout == -1)
+               timeout = MONO_INFINITE_WAIT;
+       if (timeout != MONO_INFINITE_WAIT)
+               start = mono_msec_ticks ();
 
-       mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
+       timeoutLeft = timeout;
 
-       THREAD_WAIT_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") returning %d", __func__, mono_native_thread_id_get (), ret));
+       for (;;) {
+               MONO_ENTER_GC_SAFE;
+#ifdef HOST_WIN32
+               if (numhandles != 1)
+                       ret = mono_w32handle_convert_wait_ret (mono_win32_wait_for_multiple_objects_ex(numhandles, handles, waitall, timeoutLeft, TRUE), numhandles);
+               else
+                       ret = mono_w32handle_convert_wait_ret (mono_win32_wait_for_single_object_ex (handles [0], timeoutLeft, TRUE), 1);
+#else
+               /* mono_w32handle_wait_multiple optimizes the case for numhandles == 1 */
+               ret = mono_w32handle_wait_multiple (handles, numhandles, waitall, timeoutLeft, TRUE);
+#endif /* HOST_WIN32 */
+               MONO_EXIT_GC_SAFE;
 
-       mono_error_set_pending_exception (&error);
+               if (ret != MONO_W32HANDLE_WAIT_RET_ALERTED)
+                       break;
 
-       return map_native_wait_result_to_managed (ret, numhandles);
-}
+               exc = mono_thread_execute_interruption ();
+               if (exc) {
+                       mono_error_set_exception_instance (error, exc);
+                       break;
+               }
 
-gint32 ves_icall_System_Threading_WaitHandle_WaitOne_internal(HANDLE handle, gint32 ms)
-{
-       MonoError error;
-       MonoW32HandleWaitRet ret;
-       MonoInternalThread *thread = mono_thread_internal_current ();
+               if (timeout != MONO_INFINITE_WAIT) {
+                       gint64 elapsed;
 
-       THREAD_WAIT_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") waiting for %p, %d ms", __func__, mono_native_thread_id_get (), handle, ms));
-       
-       if(ms== -1) {
-               ms=MONO_INFINITE_WAIT;
+                       elapsed = mono_msec_ticks () - start;
+                       if (elapsed >= timeout) {
+                               ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
+                               break;
+                       }
+
+                       timeoutLeft = timeout - elapsed;
+               }
        }
-       
-       if (mono_thread_current_check_pending_interrupt ())
-               return map_native_wait_result_to_managed (MONO_W32HANDLE_WAIT_RET_FAILED, 0);
 
-       mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
-       
-       ret = mono_wait_uninterrupted (thread, 1, &handle, FALSE, ms, &error);
-       
        mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
 
-       mono_error_set_pending_exception (&error);
-       return map_native_wait_result_to_managed (ret, 1);
+       return map_native_wait_result_to_managed (ret, numhandles);
 }
 
 gint32
-ves_icall_System_Threading_WaitHandle_SignalAndWait_Internal (HANDLE toSignal, HANDLE toWait, gint32 ms)
+ves_icall_System_Threading_WaitHandle_SignalAndWait_Internal (gpointer toSignal, gpointer toWait, gint32 ms, MonoError *error)
 {
        MonoW32HandleWaitRet ret;
        MonoInternalThread *thread = mono_thread_internal_current ();
@@ -1996,7 +1964,7 @@ ves_icall_System_Threading_WaitHandle_SignalAndWait_Internal (HANDLE toSignal, H
        
        MONO_ENTER_GC_SAFE;
 #ifdef HOST_WIN32
-       ret = mono_w32handle_convert_wait_ret (SignalObjectAndWait (toSignal, toWait, ms, TRUE), 1);
+       ret = mono_w32handle_convert_wait_ret (mono_win32_signal_object_and_wait (toSignal, toWait, ms, TRUE), 1);
 #else
        ret = mono_w32handle_signal_and_wait (toSignal, toWait, ms, TRUE);
 #endif
@@ -2305,10 +2273,8 @@ void ves_icall_System_Threading_Thread_Interrupt_internal (MonoThread *this_obj)
 
 /**
  * mono_thread_current_check_pending_interrupt:
- *
  * Checks if there's a interruption request and set the pending exception if so.
- *
- * @returns true if a pending exception was set
+ * \returns true if a pending exception was set
  */
 gboolean
 mono_thread_current_check_pending_interrupt (void)
@@ -2331,7 +2297,7 @@ mono_thread_current_check_pending_interrupt (void)
 }
 
 static gboolean
-request_thread_abort (MonoInternalThread *thread, MonoObject *state)
+request_thread_abort (MonoInternalThread *thread, MonoObject *state, gboolean appdomain_unload)
 {
        LOCK_THREAD (thread);
        
@@ -2348,6 +2314,11 @@ request_thread_abort (MonoInternalThread *thread, MonoObject *state)
        }
 
        thread->state |= ThreadState_AbortRequested;
+       if (appdomain_unload)
+               thread->flags |= MONO_THREAD_FLAG_APPDOMAIN_ABORT;
+       else
+               thread->flags &= ~MONO_THREAD_FLAG_APPDOMAIN_ABORT;
+
        if (thread->abort_state_handle)
                mono_gchandle_free (thread->abort_state_handle);
        if (state) {
@@ -2372,7 +2343,7 @@ request_thread_abort (MonoInternalThread *thread, MonoObject *state)
 void
 ves_icall_System_Threading_Thread_Abort (MonoInternalThread *thread, MonoObject *state)
 {
-       if (!request_thread_abort (thread, state))
+       if (!request_thread_abort (thread, state, FALSE))
                return;
 
        if (thread == mono_thread_internal_current ()) {
@@ -2386,17 +2357,15 @@ ves_icall_System_Threading_Thread_Abort (MonoInternalThread *thread, MonoObject
 
 /**
  * mono_thread_internal_abort:
- *
- * Request thread @thread to be aborted.
- *
- * @thread MUST NOT be the current thread.
+ * Request thread \p thread to be aborted.
+ * \p thread MUST NOT be the current thread.
  */
 void
-mono_thread_internal_abort (MonoInternalThread *thread)
+mono_thread_internal_abort (MonoInternalThread *thread, gboolean appdomain_unload)
 {
        g_assert (thread != mono_thread_internal_current ());
 
-       if (!request_thread_abort (thread, NULL))
+       if (!request_thread_abort (thread, NULL, appdomain_unload))
                return;
        async_abort_internal (thread, TRUE);
 }
@@ -2405,17 +2374,23 @@ void
 ves_icall_System_Threading_Thread_ResetAbort (MonoThread *this_obj)
 {
        MonoInternalThread *thread = mono_thread_internal_current ();
-       gboolean was_aborting;
+       gboolean was_aborting, is_domain_abort;
 
        LOCK_THREAD (thread);
        was_aborting = thread->state & ThreadState_AbortRequested;
-       thread->state &= ~ThreadState_AbortRequested;
+       is_domain_abort = thread->flags & MONO_THREAD_FLAG_APPDOMAIN_ABORT; 
+
+       if (was_aborting && !is_domain_abort)
+               thread->state &= ~ThreadState_AbortRequested;
        UNLOCK_THREAD (thread);
 
        if (!was_aborting) {
                const char *msg = "Unable to reset abort because no abort was requested";
                mono_set_pending_exception (mono_get_exception_thread_state (msg));
                return;
+       } else if (is_domain_abort) {
+               /* Silently ignore abort resets in unloading appdomains */
+               return;
        }
 
        mono_get_eh_callbacks ()->mono_clear_abort_threshold ();
@@ -2607,12 +2582,15 @@ is_running_protected_wrapper (void)
        return found;
 }
 
+/**
+ * mono_thread_stop:
+ */
 void
 mono_thread_stop (MonoThread *thread)
 {
        MonoInternalThread *internal = thread->internal_thread;
 
-       if (!request_thread_abort (internal, NULL))
+       if (!request_thread_abort (internal, NULL, FALSE))
                return;
 
        if (internal == mono_thread_internal_current ()) {
@@ -2923,8 +2901,9 @@ free_context (void *user_data)
 }
 
 void
-ves_icall_System_Runtime_Remoting_Contexts_Context_RegisterContext (MonoAppContext *ctx)
+mono_threads_register_app_context (MonoAppContext *ctx, MonoError *error)
 {
+       error_init (error);
        mono_threads_lock ();
 
        //g_print ("Registering context %d in domain %d\n", ctx->context_id, ctx->domain_id);
@@ -2952,11 +2931,18 @@ ves_icall_System_Runtime_Remoting_Contexts_Context_RegisterContext (MonoAppConte
 
        mono_threads_unlock ();
 
-       mono_profiler_context_loaded (ctx);
+       MONO_PROFILER_RAISE (context_loaded, (ctx));
+}
+
+void
+ves_icall_System_Runtime_Remoting_Contexts_Context_RegisterContext (MonoAppContextHandle ctx, MonoError *error)
+{
+       error_init (error);
+       mono_threads_register_app_context (MONO_HANDLE_RAW (ctx), error); /* FIXME use handles in mono_threads_register_app_context */
 }
 
 void
-ves_icall_System_Runtime_Remoting_Contexts_Context_ReleaseContext (MonoAppContext *ctx)
+mono_threads_release_app_context (MonoAppContext* ctx, MonoError *error)
 {
        /*
         * NOTE: Since finalizers are unreliable for the purposes of ensuring
@@ -2966,7 +2952,14 @@ ves_icall_System_Runtime_Remoting_Contexts_Context_ReleaseContext (MonoAppContex
 
        //g_print ("Releasing context %d in domain %d\n", ctx->context_id, ctx->domain_id);
 
-       mono_profiler_context_unloaded (ctx);
+       MONO_PROFILER_RAISE (context_unloaded, (ctx));
+}
+
+void
+ves_icall_System_Runtime_Remoting_Contexts_Context_ReleaseContext (MonoAppContextHandle ctx, MonoError *error)
+{
+       error_init (error);
+       mono_threads_release_app_context (MONO_HANDLE_RAW (ctx), error); /* FIXME use handles in mono_threads_release_app_context */
 }
 
 void mono_thread_init (MonoThreadStartCB start_cb,
@@ -2986,8 +2979,92 @@ void mono_thread_init (MonoThreadStartCB start_cb,
        mono_thread_attach_cb = attach_cb;
 }
 
-void mono_thread_cleanup (void)
+static gpointer
+thread_attach (MonoThreadInfo *info)
+{
+       return mono_gc_thread_attach (info);
+}
+
+static void
+thread_detach (MonoThreadInfo *info)
+{
+       MonoInternalThread *internal;
+       guint32 gchandle;
+
+       /* If a delegate is passed to native code and invoked on a thread we dont
+        * know about, marshal will register it with mono_threads_attach_coop, but
+        * we have no way of knowing when that thread goes away.  SGen has a TSD
+        * so we assume that if the domain is still registered, we can detach
+        * the thread */
+
+       g_assert (info);
+
+       if (!mono_thread_info_try_get_internal_thread_gchandle (info, &gchandle))
+               return;
+
+       internal = (MonoInternalThread*) mono_gchandle_get_target (gchandle);
+       g_assert (internal);
+
+       mono_gchandle_free (gchandle);
+
+       mono_thread_detach_internal (internal);
+}
+
+static void
+thread_detach_with_lock (MonoThreadInfo *info)
+{
+       mono_gc_thread_detach_with_lock (info);
+}
+
+static gboolean
+thread_in_critical_region (MonoThreadInfo *info)
+{
+       return mono_gc_thread_in_critical_region (info);
+}
+
+static gboolean
+ip_in_critical_region (MonoDomain *domain, gpointer ip)
+{
+       MonoJitInfo *ji;
+       MonoMethod *method;
+
+       /*
+        * We pass false for 'try_aot' so this becomes async safe.
+        * It won't find aot methods whose jit info is not yet loaded,
+        * so we preload their jit info in the JIT.
+        */
+       ji = mono_jit_info_table_find_internal (domain, ip, FALSE, FALSE);
+       if (!ji)
+               return FALSE;
+
+       method = mono_jit_info_get_method (ji);
+       g_assert (method);
+
+       return mono_gc_is_critical_method (method);
+}
+
+void
+mono_thread_callbacks_init (void)
+{
+       MonoThreadInfoCallbacks cb;
+
+       memset (&cb, 0, sizeof(cb));
+       cb.thread_attach = thread_attach;
+       cb.thread_detach = thread_detach;
+       cb.thread_detach_with_lock = thread_detach_with_lock;
+       cb.ip_in_critical_region = ip_in_critical_region;
+       cb.thread_in_critical_region = thread_in_critical_region;
+       mono_thread_info_callbacks_init (&cb);
+}
+
+/**
+ * mono_thread_cleanup:
+ */
+void
+mono_thread_cleanup (void)
 {
+       mono_threads_join_threads ();
+
 #if !defined(RUN_IN_SUBTHREAD) && !defined(HOST_WIN32)
        /* The main thread must abandon any held mutexes (particularly
         * important for named mutexes as they are shared across
@@ -3017,6 +3094,9 @@ mono_threads_install_cleanup (MonoThreadCleanupFunc func)
        mono_thread_cleanup_fn = func;
 }
 
+/**
+ * mono_thread_set_manage_callback:
+ */
 void
 mono_thread_set_manage_callback (MonoThread *thread, MonoThreadManageCallback func)
 {
@@ -3158,7 +3238,7 @@ remove_and_abort_threads (gpointer key, gpointer value, gpointer user)
                wait->num++;
 
                THREAD_DEBUG (g_print ("%s: Aborting id: %"G_GSIZE_FORMAT"\n", __func__, (gsize)thread->tid));
-               mono_thread_internal_abort (thread);
+               mono_thread_internal_abort (thread, FALSE);
        }
 
        return TRUE;
@@ -3210,7 +3290,11 @@ mono_threads_set_shutting_down (void)
        }
 }
 
-void mono_thread_manage (void)
+/**
+ * mono_thread_manage:
+ */
+void
+mono_thread_manage (void)
 {
        struct wait_data wait_data;
        struct wait_data *wait = &wait_data;
@@ -3660,7 +3744,10 @@ mono_threads_get_thread_dump (MonoArray **out_threads, MonoArray **out_stack_fra
                                        sf->il_offset = location->il_offset;
 
                                        if (location && location->source_file) {
-                                               MONO_OBJECT_SETREF (sf, filename, mono_string_new (domain, location->source_file));
+                                               MonoString *filename = mono_string_new_checked (domain, location->source_file, error);
+                                               if (!is_ok (error))
+                                                       goto leave;
+                                               MONO_OBJECT_SETREF (sf, filename, filename);
                                                sf->line = location->row;
                                                sf->column = location->column;
                                        }
@@ -3846,10 +3933,6 @@ collect_appdomain_thread (gpointer key, gpointer value, gpointer user_data)
 gboolean
 mono_threads_abort_appdomain_threads (MonoDomain *domain, int timeout)
 {
-#ifdef __native_client__
-       return FALSE;
-#endif
-
        abort_appdomain_data user_data;
        gint64 start_time;
        int orig_timeout = timeout;
@@ -3870,7 +3953,7 @@ mono_threads_abort_appdomain_threads (MonoDomain *domain, int timeout)
                if (user_data.wait.num > 0) {
                        /* Abort the threads outside the threads lock */
                        for (i = 0; i < user_data.wait.num; ++i)
-                               mono_thread_internal_abort (user_data.wait.threads [i]);
+                               mono_thread_internal_abort (user_data.wait.threads [i], TRUE);
 
                        /*
                         * We should wait for the threads either to abort, or to leave the
@@ -3893,6 +3976,14 @@ mono_threads_abort_appdomain_threads (MonoDomain *domain, int timeout)
        return TRUE;
 }
 
+void
+mono_thread_self_abort (void)
+{
+       MonoError error;
+       self_abort_internal (&error);
+       mono_error_set_pending_exception (&error);
+}
+
 /*
  * mono_thread_get_undeniable_exception:
  *
@@ -4365,14 +4456,18 @@ mono_thread_execute_interruption (void)
        LOCK_THREAD (thread);
 
        /* MonoThread::interruption_requested can only be changed with atomics */
-       if (mono_thread_clear_interruption_requested (thread)) {
-               /* this will consume pending APC calls */
+       if (!mono_thread_clear_interruption_requested (thread)) {
+               UNLOCK_THREAD (thread);
+               return NULL;
+       }
+
+       /* this will consume pending APC calls */
 #ifdef HOST_WIN32
-               WaitForSingleObjectEx (GetCurrentThread(), 0, TRUE);
+       mono_win32_wait_for_single_object_ex (GetCurrentThread (), 0, TRUE);
 #endif
-               /* Clear the interrupted flag of the thread so it can wait again */
-               mono_thread_info_clear_self_interrupt ();
-       }
+
+       /* Clear the interrupted flag of the thread so it can wait again */
+       mono_thread_info_clear_self_interrupt ();
 
        /* If there's a pending exception and an AbortRequested, the pending exception takes precedence */
        if (sys_thread->pending_exception) {
@@ -4438,9 +4533,8 @@ mono_thread_request_interruption (gboolean running_managed)
 
                /* this will awake the thread if it is in WaitForSingleObject 
                   or similar */
-               /* Our implementation of this function ignores the func argument */
 #ifdef HOST_WIN32
-               QueueUserAPC ((PAPCFUNC)dummy_apc, thread->native_handle, (ULONG_PTR)NULL);
+               mono_win32_interrupt_wait (thread->thread_info, thread->native_handle, (DWORD)thread->tid);
 #else
                mono_thread_info_self_interrupt ();
 #endif
@@ -4606,10 +4700,8 @@ mono_thread_set_state (MonoInternalThread *thread, MonoThreadState state)
 
 /**
  * mono_thread_test_and_set_state:
- *
- * Test if current state of @thread include @test. If it does not, OR @set into the state.
- *
- * Returns TRUE is @set was OR'd in.
+ * Test if current state of \p thread include \p test. If it does not, OR \p set into the state.
+ * \returns TRUE if \p set was OR'd in.
  */
 gboolean
 mono_thread_test_and_set_state (MonoInternalThread *thread, MonoThreadState test, MonoThreadState set)
@@ -4902,14 +4994,14 @@ mono_thread_internal_suspend_for_shutdown (MonoInternalThread *thread)
        mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), FALSE, suspend_for_shutdown_critical, NULL);
 }
 
-/*
+/**
  * mono_thread_is_foreign:
- * @thread: the thread to query
+ * \param thread the thread to query
  *
  * This function allows one to determine if a thread was created by the mono runtime and has
- * a well defined lifecycle or it's a foreigh one, created by the native environment.
+ * a well defined lifecycle or it's a foreign one, created by the native environment.
  *
- * Returns: TRUE if @thread was not created by the runtime.
+ * \returns TRUE if \p thread was not created by the runtime.
  */
 mono_bool
 mono_thread_is_foreign (MonoThread *thread)
@@ -4918,6 +5010,78 @@ mono_thread_is_foreign (MonoThread *thread)
        return info->runtime_thread == FALSE;
 }
 
+#ifndef HOST_WIN32
+static void
+threads_native_thread_join_lock (gpointer tid, gpointer value)
+{
+       pthread_t thread = (pthread_t)tid;
+       if (thread != pthread_self ()) {
+               MONO_ENTER_GC_SAFE;
+               /* This shouldn't block */
+               mono_threads_join_lock ();
+               mono_native_thread_join (thread);
+               mono_threads_join_unlock ();
+               MONO_EXIT_GC_SAFE;
+       }
+}
+static void
+threads_native_thread_join_nolock (gpointer tid, gpointer value)
+{
+       pthread_t thread = (pthread_t)tid;
+       MONO_ENTER_GC_SAFE;
+       mono_native_thread_join (thread);
+       MONO_EXIT_GC_SAFE;
+}
+
+static void
+threads_add_joinable_thread_nolock (gpointer tid)
+{
+       g_hash_table_insert (joinable_threads, tid, tid);
+}
+#else
+static void
+threads_native_thread_join_lock (gpointer tid, gpointer value)
+{
+       MonoNativeThreadId thread_id = (MonoNativeThreadId)(guint64)tid;
+       HANDLE thread_handle = (HANDLE)value;
+       if (thread_id != GetCurrentThreadId () && thread_handle != NULL && thread_handle != INVALID_HANDLE_VALUE) {
+               MONO_ENTER_GC_SAFE;
+               /* This shouldn't block */
+               mono_threads_join_lock ();
+               mono_native_thread_join_handle (thread_handle, TRUE);
+               mono_threads_join_unlock ();
+               MONO_EXIT_GC_SAFE;
+       }
+}
+
+static void
+threads_native_thread_join_nolock (gpointer tid, gpointer value)
+{
+       HANDLE thread_handle = (HANDLE)value;
+       MONO_ENTER_GC_SAFE;
+       mono_native_thread_join_handle (thread_handle, TRUE);
+       MONO_EXIT_GC_SAFE;
+}
+
+static void
+threads_add_joinable_thread_nolock (gpointer tid)
+{
+       g_hash_table_insert (joinable_threads, tid, (gpointer)OpenThread (SYNCHRONIZE, TRUE, (MonoNativeThreadId)(guint64)tid));
+}
+#endif
+
+void
+mono_threads_add_joinable_runtime_thread (gpointer thread_info)
+{
+       g_assert (thread_info);
+       MonoThreadInfo *mono_thread_info = (MonoThreadInfo*)thread_info;
+
+       if (mono_thread_info->runtime_thread) {
+               if (InterlockedCompareExchange (&mono_thread_info->thread_pending_native_join, TRUE, FALSE) == FALSE)
+                       mono_threads_add_joinable_thread ((gpointer)(MONO_UINT_TO_NATIVE_THREAD_ID (mono_thread_info_get_tid (mono_thread_info))));
+       }
+}
+
 /*
  * mono_add_joinable_thread:
  *
@@ -4927,21 +5091,24 @@ mono_thread_is_foreign (MonoThread *thread)
 void
 mono_threads_add_joinable_thread (gpointer tid)
 {
-#ifndef HOST_WIN32
        /*
         * We cannot detach from threads because it causes problems like
         * 2fd16f60/r114307. So we collect them and join them when
-        * we have time (in he finalizer thread).
+        * we have time (in the finalizer thread).
         */
        joinable_threads_lock ();
        if (!joinable_threads)
                joinable_threads = g_hash_table_new (NULL, NULL);
-       g_hash_table_insert (joinable_threads, tid, tid);
-       joinable_thread_count ++;
+
+       gpointer orig_key;
+       gpointer value;
+       if (!g_hash_table_lookup_extended (joinable_threads, tid, &orig_key, &value)) {
+               threads_add_joinable_thread_nolock (tid);
+               UnlockedIncrement (&joinable_thread_count);
+       }
        joinable_threads_unlock ();
 
        mono_gc_finalize_notify ();
-#endif
 }
 
 /*
@@ -4953,15 +5120,13 @@ mono_threads_add_joinable_thread (gpointer tid)
 void
 mono_threads_join_threads (void)
 {
-#ifndef HOST_WIN32
        GHashTableIter iter;
        gpointer key;
-       gpointer tid;
-       pthread_t thread;
+       gpointer value;
        gboolean found;
 
        /* Fastpath */
-       if (!joinable_thread_count)
+       if (!UnlockedRead (&joinable_thread_count))
                return;
 
        while (TRUE) {
@@ -4969,27 +5134,17 @@ mono_threads_join_threads (void)
                found = FALSE;
                if (g_hash_table_size (joinable_threads)) {
                        g_hash_table_iter_init (&iter, joinable_threads);
-                       g_hash_table_iter_next (&iter, &key, (void**)&tid);
-                       thread = (pthread_t)tid;
+                       g_hash_table_iter_next (&iter, &key, (void**)&value);
                        g_hash_table_remove (joinable_threads, key);
-                       joinable_thread_count --;
+                       UnlockedDecrement (&joinable_thread_count);
                        found = TRUE;
                }
                joinable_threads_unlock ();
-               if (found) {
-                       if (thread != pthread_self ()) {
-                               MONO_ENTER_GC_SAFE;
-                               /* This shouldn't block */
-                               mono_threads_join_lock ();
-                               mono_native_thread_join (thread);
-                               mono_threads_join_unlock ();
-                               MONO_EXIT_GC_SAFE;
-                       }
-               } else {
+               if (found)
+                       threads_native_thread_join_lock (key, value);
+               else
                        break;
-               }
        }
-#endif
 }
 
 /*
@@ -5001,26 +5156,25 @@ mono_threads_join_threads (void)
 void
 mono_thread_join (gpointer tid)
 {
-#ifndef HOST_WIN32
-       pthread_t thread;
        gboolean found = FALSE;
+       gpointer orig_key;
+       gpointer value;
 
        joinable_threads_lock ();
        if (!joinable_threads)
                joinable_threads = g_hash_table_new (NULL, NULL);
-       if (g_hash_table_lookup (joinable_threads, tid)) {
+
+       if (g_hash_table_lookup_extended (joinable_threads, tid, &orig_key, &value)) {
                g_hash_table_remove (joinable_threads, tid);
-               joinable_thread_count --;
+               UnlockedDecrement (&joinable_thread_count);
                found = TRUE;
        }
        joinable_threads_unlock ();
+
        if (!found)
                return;
-       thread = (pthread_t)tid;
-       MONO_ENTER_GC_SAFE;
-       mono_native_thread_join (thread);
-       MONO_EXIT_GC_SAFE;
-#endif
+
+       threads_native_thread_join_nolock (tid, value);
 }
 
 void
@@ -5051,35 +5205,32 @@ ves_icall_System_Threading_Thread_GetStackTraces (MonoArray **out_threads, MonoA
 /*
  * mono_threads_attach_coop: called by native->managed wrappers
  *
- * In non-coop mode:
- *  - @dummy: is NULL
+ *  - @dummy:
+ *    - blocking mode: contains gc unsafe transition cookie
+ *    - non-blocking mode: contains random data
  *  - @return: the original domain which needs to be restored, or NULL.
- *
- * In coop mode:
- *  - @dummy: contains the original domain
- *  - @return: a cookie containing current MonoThreadInfo*.
  */
 gpointer
 mono_threads_attach_coop (MonoDomain *domain, gpointer *dummy)
 {
        MonoDomain *orig;
-       gboolean fresh_thread = FALSE;
+       MonoThreadInfo *info;
+       gboolean external;
+
+       orig = mono_domain_get ();
 
        if (!domain) {
                /* Happens when called from AOTed code which is only used in the root domain. */
                domain = mono_get_root_domain ();
+               g_assert (domain);
        }
 
-       g_assert (domain);
-
        /* On coop, when we detached, we moved the thread from  RUNNING->BLOCKING.
         * If we try to reattach we do a BLOCKING->RUNNING transition.  If the thread
         * is fresh, mono_thread_attach() will do a STARTING->RUNNING transition so
         * we're only responsible for making the cookie. */
-       if (mono_threads_is_coop_enabled ()) {
-               MonoThreadInfo *info = mono_thread_info_current_unchecked ();
-               fresh_thread = !info || !mono_thread_info_is_live (info);
-       }
+       if (mono_threads_is_blocking_transition_enabled ())
+               external = !(info = mono_thread_info_current_unchecked ()) || !mono_thread_info_is_live (info);
 
        if (!mono_thread_internal_current ()) {
                mono_thread_attach_full (domain, FALSE);
@@ -5088,61 +5239,52 @@ mono_threads_attach_coop (MonoDomain *domain, gpointer *dummy)
                mono_thread_set_state (mono_thread_internal_current (), ThreadState_Background);
        }
 
-       orig = mono_domain_get ();
        if (orig != domain)
                mono_domain_set (domain, TRUE);
 
-       if (!mono_threads_is_coop_enabled ())
-               return orig != domain ? orig : NULL;
-
-       if (fresh_thread) {
-               *dummy = NULL;
-               /* mono_thread_attach put the thread in RUNNING mode from STARTING, but we need to
-                * return the right cookie. */
-               return mono_threads_enter_gc_unsafe_region_cookie ();
-       } else {
-               *dummy = orig;
-               /* thread state (BLOCKING|RUNNING) -> RUNNING */
-               return mono_threads_enter_gc_unsafe_region (dummy);
+       if (mono_threads_is_blocking_transition_enabled ()) {
+               if (external) {
+                       /* mono_thread_attach put the thread in RUNNING mode from STARTING, but we need to
+                        * return the right cookie. */
+                       *dummy = mono_threads_enter_gc_unsafe_region_cookie ();
+               } else {
+                       /* thread state (BLOCKING|RUNNING) -> RUNNING */
+                       *dummy = mono_threads_enter_gc_unsafe_region (dummy);
+               }
        }
+
+       return orig;
 }
 
 /*
  * mono_threads_detach_coop: called by native->managed wrappers
  *
- * In non-coop mode:
  *  - @cookie: the original domain which needs to be restored, or NULL.
- *  - @dummy: is NULL
- *
- * In coop mode:
- *  - @cookie: contains current MonoThreadInfo* if it was in BLOCKING mode, NULL otherwise
- *  - @dummy: contains the original domain
+ *  - @dummy:
+ *    - blocking mode: contains gc unsafe transition cookie
+ *    - non-blocking mode: contains random data
  */
 void
 mono_threads_detach_coop (gpointer cookie, gpointer *dummy)
 {
        MonoDomain *domain, *orig;
 
-       if (!mono_threads_is_coop_enabled ()) {
-               orig = (MonoDomain*) cookie;
-               if (orig)
-                       mono_domain_set (orig, TRUE);
-       } else {
-               orig = (MonoDomain*) *dummy;
+       orig = (MonoDomain*) cookie;
 
-               domain = mono_domain_get ();
-               g_assert (domain);
+       domain = mono_domain_get ();
+       g_assert (domain);
 
+       if (mono_threads_is_blocking_transition_enabled ()) {
                /* it won't do anything if cookie is NULL
                 * thread state RUNNING -> (RUNNING|BLOCKING) */
-               mono_threads_exit_gc_unsafe_region (cookie, dummy);
+               mono_threads_exit_gc_unsafe_region (*dummy, dummy);
+       }
 
-               if (orig != domain) {
-                       if (!orig)
-                               mono_domain_unset ();
-                       else
-                               mono_domain_set (orig, TRUE);
-               }
+       if (orig != domain) {
+               if (!orig)
+                       mono_domain_unset ();
+               else
+                       mono_domain_set (orig, TRUE);
        }
 }