Revert "[threads] Make OSEvent alertable to fix bug #51653" (#4346)
[mono.git] / mono / metadata / threads.c
index 2ae43aab0ae3be675b979e4cb5756fc1d6ed169a..b94a4ff7ff3a96bc0425f137514931e03b118acc 100644 (file)
@@ -28,7 +28,6 @@
 #include <mono/metadata/gc-internals.h>
 #include <mono/metadata/marshal.h>
 #include <mono/metadata/runtime.h>
-#include <mono/io-layer/io-layer.h>
 #include <mono/metadata/object-internals.h>
 #include <mono/metadata/mono-debug-debugger.h>
 #include <mono/utils/monobitset.h>
 #include <mono/utils/mono-tls.h>
 #include <mono/utils/atomic.h>
 #include <mono/utils/mono-memory-model.h>
-#include <mono/utils/mono-threads-coop.h>
 #include <mono/utils/mono-error-internals.h>
-#include <mono/utils/w32handle.h>
+#include <mono/utils/os-event.h>
+#include <mono/utils/mono-threads-debug.h>
+#include <mono/metadata/w32handle.h>
 #include <mono/metadata/w32event.h>
 #include <mono/metadata/w32mutex.h>
 
-#include <mono/metadata/gc-internals.h>
 #include <mono/metadata/reflection-internals.h>
 #include <mono/metadata/abi-details.h>
+#include <mono/metadata/w32error.h>
+#include <mono/utils/w32api.h>
 
 #ifdef HAVE_SIGNAL_H
 #include <signal.h>
@@ -155,28 +156,13 @@ static MonoReferenceQueue *context_queue;
  */
 static MonoGHashTable *threads_starting_up = NULL;
 
-/* The TLS key that holds the MonoObject assigned to each thread */
-static MonoNativeTlsKey current_object_key;
-
 /* Contains tids */
 /* Protected by the threads lock */
 static GHashTable *joinable_threads;
 static int joinable_thread_count;
 
-#ifdef MONO_HAVE_FAST_TLS
-/* we need to use both the Tls* functions and __thread because
- * the gc needs to see all the threads 
- */
-MONO_FAST_TLS_DECLARE(tls_current_object);
-#define SET_CURRENT_OBJECT(x) do { \
-       MONO_FAST_TLS_SET (tls_current_object, x); \
-       mono_native_tls_set_value (current_object_key, x); \
-} while (FALSE)
-#define GET_CURRENT_OBJECT() ((MonoInternalThread*) MONO_FAST_TLS_GET (tls_current_object))
-#else
-#define SET_CURRENT_OBJECT(x) mono_native_tls_set_value (current_object_key, x)
-#define GET_CURRENT_OBJECT() (MonoInternalThread*) mono_native_tls_get_value (current_object_key)
-#endif
+#define SET_CURRENT_OBJECT(x) mono_tls_set_thread (x)
+#define GET_CURRENT_OBJECT() (MonoInternalThread*) mono_tls_get_thread ()
 
 /* function called at thread start */
 static MonoThreadStartCB mono_thread_start_cb = NULL;
@@ -213,14 +199,14 @@ static mono_mutex_t interlocked_mutex;
 static gint32 thread_interruption_requested = 0;
 
 /* Event signaled when a thread changes its background mode */
-static HANDLE background_change_event;
+static MonoOSEvent background_change_event;
 
 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 GENERATE_GET_CLASS_WITH_CACHE (appdomain_unloaded_exception, "System", "AppDomainUnloadedException")
 
 static void
 mono_threads_lock (void)
@@ -241,24 +227,165 @@ get_next_managed_thread_id (void)
        return InterlockedIncrement (&managed_thread_id_counter);
 }
 
-MonoNativeTlsKey
-mono_thread_get_tls_key (void)
+enum {
+       INTERRUPT_REQUESTED_BIT = 0x1,
+       INTERRUPT_REQUEST_DEFERRED_BIT = 0x2,
+       ABORT_PROT_BLOCK_SHIFT = 2,
+       ABORT_PROT_BLOCK_BITS = 8,
+       ABORT_PROT_BLOCK_MASK = (((1 << ABORT_PROT_BLOCK_BITS) - 1) << ABORT_PROT_BLOCK_SHIFT)
+};
+
+static int
+mono_thread_get_abort_prot_block_count (MonoInternalThread *thread)
 {
-       return current_object_key;
+       gsize state = thread->thread_state;
+       return (state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT;
 }
 
-gint32
-mono_thread_get_tls_offset (void)
+static void
+verify_thread_state (gsize state)
 {
-       int offset = -1;
+       //can't have both INTERRUPT_REQUESTED_BIT and INTERRUPT_REQUEST_DEFERRED_BIT set at the same time
+       g_assert ((state & (INTERRUPT_REQUESTED_BIT | INTERRUPT_REQUEST_DEFERRED_BIT)) != (INTERRUPT_REQUESTED_BIT | INTERRUPT_REQUEST_DEFERRED_BIT));
 
-#ifdef HOST_WIN32
-       if (current_object_key)
-               offset = current_object_key;
-#else
-       MONO_THREAD_VAR_OFFSET (tls_current_object,offset);
-#endif
-       return offset;
+       //XXX This would be nice to be true, but can happen due to self-aborts (and possibly set-pending-exception)
+       //if prot_count > 0, INTERRUPT_REQUESTED_BIT must never be set
+       // int prot_count = (state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT;
+       // g_assert (!(prot_count > 0 && (state & INTERRUPT_REQUESTED_BIT)));
+}
+
+void
+mono_threads_begin_abort_protected_block (void)
+{
+       MonoInternalThread *thread = mono_thread_internal_current ();
+       gsize old_state, new_state;
+       do {
+               old_state = thread->thread_state;
+               verify_thread_state (old_state);
+
+               int new_val = ((old_state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT) + 1;
+
+               new_state = 0;
+               if (old_state & (INTERRUPT_REQUESTED_BIT | INTERRUPT_REQUEST_DEFERRED_BIT)) {
+                       if (old_state & INTERRUPT_REQUESTED_BIT)
+                               printf ("begin prot happy as it demoted interrupt to deferred interrupt\n");
+                       new_state |= INTERRUPT_REQUEST_DEFERRED_BIT;
+               }
+
+               //bounds check abort_prot_count
+               g_assert (new_val > 0);
+               g_assert (new_val < (1 << ABORT_PROT_BLOCK_BITS));
+               new_state |= new_val << ABORT_PROT_BLOCK_SHIFT;
+
+       } while (InterlockedCompareExchangePointer ((volatile gpointer)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
+}
+
+gboolean
+mono_threads_end_abort_protected_block (void)
+{
+       MonoInternalThread *thread = mono_thread_internal_current ();
+       gsize old_state, new_state;
+       do {
+               old_state = thread->thread_state;
+               verify_thread_state (old_state);
+
+               int new_val = ((old_state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT) - 1;
+               new_state = 0;
+
+               if ((old_state & INTERRUPT_REQUEST_DEFERRED_BIT) && new_val == 0) {
+                       printf ("end abort on alert, promoted deferred to pront interrupt\n");
+                       new_state |= INTERRUPT_REQUESTED_BIT;
+               }
+
+               //bounds check abort_prot_count
+               g_assert (new_val >= 0);
+               g_assert (new_val < (1 << ABORT_PROT_BLOCK_BITS));
+               new_state |= new_val << ABORT_PROT_BLOCK_SHIFT;
+
+       } while (InterlockedCompareExchangePointer ((volatile gpointer)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
+       return (new_state & INTERRUPT_REQUESTED_BIT) == INTERRUPT_REQUESTED_BIT;
+}
+
+
+//Don't use this function, use inc/dec below
+static void
+mono_thread_abort_prot_block_count_add (MonoInternalThread *thread, int val)
+{
+       gsize old_state, new_state;
+       do {
+               old_state = thread->thread_state;
+               verify_thread_state (old_state);
+
+               int new_val = val + ((old_state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT);
+               //bounds check abort_prot_count
+               g_assert (new_val >= 0);
+               g_assert (new_val < (1 << ABORT_PROT_BLOCK_BITS));
+               new_state = (old_state & ~ABORT_PROT_BLOCK_MASK) | (new_val << ABORT_PROT_BLOCK_SHIFT);
+
+       } while (InterlockedCompareExchangePointer ((volatile gpointer)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
+}
+
+static void
+mono_thread_inc_abort_prot_block_count (MonoInternalThread *thread)
+{
+       mono_thread_abort_prot_block_count_add (thread, 1);
+}
+
+static void
+mono_thread_dec_abort_prot_block_count (MonoInternalThread *thread)
+{
+       mono_thread_abort_prot_block_count_add (thread, -1);
+}
+
+static gboolean
+mono_thread_get_interruption_requested (MonoInternalThread *thread)
+{
+       gsize state = thread->thread_state;
+       return (state & INTERRUPT_REQUESTED_BIT) == INTERRUPT_REQUESTED_BIT;
+}
+
+/* Returns TRUE is there was a state change */
+static gboolean
+mono_thread_clear_interruption_requested (MonoInternalThread *thread)
+{
+       gsize old_state, new_state;
+       do {
+               old_state = thread->thread_state;
+               verify_thread_state (old_state);
+
+               //Already cleared
+               if (!(old_state & (INTERRUPT_REQUESTED_BIT | INTERRUPT_REQUEST_DEFERRED_BIT)))
+                       return FALSE;
+               new_state = old_state & ~(INTERRUPT_REQUESTED_BIT | INTERRUPT_REQUEST_DEFERRED_BIT);
+       } while (InterlockedCompareExchangePointer ((volatile gpointer)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
+       return TRUE;
+}
+
+/* Returns TRUE is there was a state change */
+static gboolean
+mono_thread_set_interruption_requested (MonoInternalThread *thread)
+{
+       //always force when the current thread is doing it to itself.
+       gboolean force_interrupt = thread == mono_thread_internal_current ();
+       gsize old_state, new_state;
+       do {
+               old_state = thread->thread_state;
+               verify_thread_state (old_state);
+
+               int prot_count = ((old_state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT);
+               //Already set
+               if (old_state & (INTERRUPT_REQUESTED_BIT | INTERRUPT_REQUEST_DEFERRED_BIT))
+                       return FALSE;
+
+               //If there's an outstanding prot block, we queue it
+               if (prot_count && !force_interrupt) {
+                       printf ("set interrupt unhappy, as it's only putting a deferred req %d\n", force_interrupt);
+                       new_state = old_state | INTERRUPT_REQUEST_DEFERRED_BIT;
+               } else
+                       new_state = old_state | INTERRUPT_REQUESTED_BIT;
+       } while (InterlockedCompareExchangePointer ((volatile gpointer)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
+
+       return (new_state & INTERRUPT_REQUESTED_BIT) == INTERRUPT_REQUESTED_BIT;
 }
 
 static inline MonoNativeThreadId
@@ -316,143 +443,6 @@ is_threadabort_exception (MonoClass *klass)
        return klass == mono_defaults.threadabortexception_class;
 }
 
-/*
- * NOTE: this function can be called also for threads different from the current one:
- * make sure no code called from it will ever assume it is run on the thread that is
- * getting cleaned up.
- */
-static void thread_cleanup (MonoInternalThread *thread)
-{
-       gboolean ret;
-
-       g_assert (thread != NULL);
-
-       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;
-
-       /*
-        * This is necessary because otherwise we might have
-        * cross-domain references which will not get cleaned up when
-        * the target domain is unloaded.
-        */
-       if (thread->cached_culture_info) {
-               int i;
-               for (i = 0; i < NUM_CACHED_CULTURES * 2; ++i)
-                       mono_array_set (thread->cached_culture_info, MonoObject*, i, 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.
-       */
-       if (InterlockedExchange (&thread->interruption_requested, 0))
-               InterlockedDecrement (&thread_interruption_requested);
-
-       mono_threads_lock ();
-
-       if (!threads) {
-               ret = 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.
-                */
-               ret = FALSE;
-       } else {
-               mono_g_hash_table_remove (threads, (gpointer)thread->tid);
-               ret = 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 thread_cleanup() 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 (!ret) {
-               if (thread == mono_thread_internal_current ()) {
-                       mono_domain_unset ();
-                       mono_memory_barrier ();
-               }
-               if (mono_thread_cleanup_fn)
-                       mono_thread_cleanup_fn (thread_get_tid (thread));
-               return;
-       }
-       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);
-
-       if (thread == mono_thread_internal_current ()) {
-               /*
-                * 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 ();
-
-       thread->cached_culture_info = NULL;
-
-       mono_free_static_data (thread->static_data);
-       thread->static_data = NULL;
-       ref_stack_destroy (thread->appdomain_refs);
-       thread->appdomain_refs = NULL;
-
-       if (mono_thread_cleanup_fn)
-               mono_thread_cleanup_fn (thread_get_tid (thread));
-
-       if (mono_gc_is_moving ()) {
-               MONO_GC_UNREGISTER_ROOT (thread->thread_pinning_ref);
-               thread->thread_pinning_ref = NULL;
-       }
-
-}
-
 /*
  * A special static data offset (guint32) consists of 3 parts:
  *
@@ -539,22 +529,18 @@ set_current_thread_for_domain (MonoDomain *domain, MonoInternalThread *thread, M
 }
 
 static MonoThread*
-create_thread_object (MonoDomain *domain)
+create_thread_object (MonoDomain *domain, MonoInternalThread *internal)
 {
+       MonoThread *thread;
+       MonoVTable *vtable;
        MonoError error;
-       MonoVTable *vt = mono_class_vtable (domain, mono_defaults.thread_class);
-       MonoThread *t = (MonoThread*)mono_object_new_mature (vt, &error);
-       /* only possible failure mode is OOM, from which we don't expect to recover. */
-       mono_error_assert_ok (&error);
-       return t;
-}
 
-static MonoThread*
-new_thread_with_internal (MonoDomain *domain, MonoInternalThread *internal)
-{
-       MonoThread *thread;
+       vtable = mono_class_vtable (domain, mono_defaults.thread_class);
+       g_assert (vtable);
 
-       thread = create_thread_object (domain);
+       thread = (MonoThread*)mono_object_new_mature (vtable, &error);
+       /* only possible failure mode is OOM, from which we don't expect to recover. */
+       mono_error_assert_ok (&error);
 
        MONO_OBJECT_SETREF (thread, internal_thread, internal);
 
@@ -562,7 +548,7 @@ new_thread_with_internal (MonoDomain *domain, MonoInternalThread *internal)
 }
 
 static MonoInternalThread*
-create_internal_thread (void)
+create_internal_thread_object (void)
 {
        MonoError error;
        MonoInternalThread *thread;
@@ -585,6 +571,9 @@ create_internal_thread (void)
 
        thread->priority = MONO_THREAD_PRIORITY_NORMAL;
 
+       thread->suspended = g_new0 (MonoOSEvent, 1);
+       mono_os_event_init (thread->suspended, TRUE);
+
        return thread;
 }
 
@@ -592,7 +581,6 @@ static void
 mono_thread_internal_set_priority (MonoInternalThread *internal, MonoThreadPriority priority)
 {
        g_assert (internal);
-       g_assert (internal->handle);
 
        g_assert (priority >= MONO_THREAD_PRIORITY_LOWEST);
        g_assert (priority <= MONO_THREAD_PRIORITY_HIGHEST);
@@ -601,7 +589,9 @@ mono_thread_internal_set_priority (MonoInternalThread *internal, MonoThreadPrior
 #ifdef HOST_WIN32
        BOOL res;
 
-       res = SetThreadPriority (internal->handle, priority - 2);
+       g_assert (internal->native_handle);
+
+       res = SetThreadPriority (internal->native_handle, priority - 2);
        if (!res)
                g_error ("%s: SetThreadPriority failed, error %d", __func__, GetLastError ());
 #else /* HOST_WIN32 */
@@ -646,7 +636,8 @@ mono_thread_internal_set_priority (MonoInternalThread *internal, MonoThreadPrior
                        param.sched_priority = 0;
                        break;
                default:
-                       g_error ("%s: unknown policy %d", __func__, policy);
+                       g_warning ("%s: unknown policy %d", __func__, policy);
+                       return;
                }
        }
 
@@ -676,7 +667,10 @@ mono_thread_attach_internal (MonoThread *thread, gboolean force_attach, gboolean
        info = mono_thread_info_current ();
 
        internal = thread->internal_thread;
-       internal->handle = mono_thread_info_duplicate_handle (info);
+       internal->handle = mono_threads_open_thread_handle (info->handle);
+#ifdef HOST_WIN32
+       internal->native_handle = OpenThread (THREAD_ALL_ACCESS, FALSE, GetCurrentThreadId ());
+#endif
        internal->tid = MONO_NATIVE_THREAD_ID_TO_UINT (mono_native_thread_id_get ());
        internal->thread_info = info;
        internal->small_id = info->small_id;
@@ -727,7 +721,7 @@ mono_thread_attach_internal (MonoThread *thread, gboolean force_attach, gboolean
 
        g_assert (!internal->root_domain_thread);
        if (domain != root_domain)
-               MONO_OBJECT_SETREF (internal, root_domain_thread, new_thread_with_internal (root_domain, internal));
+               MONO_OBJECT_SETREF (internal, root_domain_thread, create_thread_object (root_domain, internal));
        else
                MONO_OBJECT_SETREF (internal, root_domain_thread, thread);
 
@@ -867,14 +861,15 @@ static guint32 WINAPI start_wrapper_internal(StartInfo *start_info, gsize *stack
 
        /* If the thread calls ExitThread at all, this remaining code
         * will not be executed, but the main thread will eventually
-        * call thread_cleanup() on this thread's behalf.
+        * call mono_thread_detach_internal() on this thread's behalf.
         */
 
        THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Start wrapper terminating", __func__, mono_native_thread_id_get ()));
 
        /* Do any cleanup needed for apartment state. This
-        * cannot be done in thread_cleanup since thread_cleanup could be 
-        * called for a thread other than the current thread.
+        * cannot be done in mono_thread_detach_internal since
+        * mono_thread_detach_internal could be  called for a
+        * thread other than the current thread.
         * mono_thread_cleanup_apartment_state cleans up apartment
         * for the current thead */
        mono_thread_cleanup_apartment_state ();
@@ -890,9 +885,6 @@ static gsize WINAPI start_wrapper(void *data)
 {
        volatile gsize dummy;
 
-       /* Avoid scanning the frames above this frame during a GC */
-       mono_gc_set_stack_end ((void*)&dummy);
-
        return start_wrapper_internal ((StartInfo*) data, (gsize*) &dummy);
 }
 
@@ -907,7 +899,7 @@ create_thread (MonoThread *thread, MonoInternalThread *internal, MonoObject *sta
        gboolean threadpool_thread, guint32 stack_size, MonoError *error)
 {
        StartInfo *start_info = NULL;
-       HANDLE thread_handle;
+       MonoThreadHandle *thread_handle;
        MonoNativeThreadId tid;
        gboolean ret;
        gsize stack_set_size;
@@ -963,7 +955,7 @@ create_thread (MonoThread *thread, MonoInternalThread *internal, MonoObject *sta
                mono_threads_lock ();
                mono_g_hash_table_remove (threads_starting_up, thread);
                mono_threads_unlock ();
-               mono_error_set_execution_engine (error, "Couldn't create thread. Error 0x%x", GetLastError());
+               mono_error_set_execution_engine (error, "Couldn't create thread. Error 0x%x", mono_w32error_get_last());
                /* ref is not going to be decremented in start_wrapper_internal */
                InterlockedDecrement (&start_info->ref);
                ret = FALSE;
@@ -1029,11 +1021,9 @@ mono_thread_create_internal (MonoDomain *domain, gpointer func, gpointer arg, gb
 
        mono_error_init (error);
 
-       thread = create_thread_object (domain);
+       internal = create_internal_thread_object ();
 
-       internal = create_internal_thread ();
-
-       MONO_OBJECT_SETREF (thread, internal_thread, internal);
+       thread = create_thread_object (domain, internal);
 
        LOCK_THREAD (internal);
 
@@ -1082,57 +1072,189 @@ mono_thread_attach_full (MonoDomain *domain, gboolean force_attach)
                return mono_thread_current ();
        }
 
-       if (!mono_gc_register_thread (&domain)) {
-               g_error ("Thread %"G_GSIZE_FORMAT" calling into managed code is not registered with the GC. On UNIX, this can be fixed by #include-ing <gc.h> before <pthread.h> in the file containing the thread creation code.", mono_native_thread_id_get ());
-       }
+       if (!mono_gc_register_thread (&domain)) {
+               g_error ("Thread %"G_GSIZE_FORMAT" calling into managed code is not registered with the GC. On UNIX, this can be fixed by #include-ing <gc.h> before <pthread.h> in the file containing the thread creation code.", mono_native_thread_id_get ());
+       }
+
+       tid=mono_native_thread_id_get ();
+
+       internal = create_internal_thread_object ();
+
+       thread = create_thread_object (domain, internal);
+
+       if (!mono_thread_attach_internal (thread, force_attach, TRUE, &stack_ptr)) {
+               /* Mono is shutting down, so just wait for the end */
+               for (;;)
+                       mono_thread_info_sleep (10000, NULL);
+       }
+
+       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);
+       }
+
+       /* 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));
+
+       return thread;
+}
+
+void
+mono_thread_detach_internal (MonoInternalThread *thread)
+{
+       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;
+
+       /*
+        * This is necessary because otherwise we might have
+        * cross-domain references which will not get cleaned up when
+        * the target domain is unloaded.
+        */
+       if (thread->cached_culture_info) {
+               int i;
+               for (i = 0; i < NUM_CACHED_CULTURES * 2; ++i)
+                       mono_array_set (thread->cached_culture_info, MonoObject*, i, 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.
+       */
+       if (mono_thread_clear_interruption_requested (thread))
+               InterlockedDecrement (&thread_interruption_requested);
+
+       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 ();
 
-       tid=mono_native_thread_id_get ();
+       /* 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.
+        */
 
-       internal = create_internal_thread ();
+       /* if the thread is not in the hash it has been removed already */
+       if (!removed) {
+               mono_domain_unset ();
+               mono_memory_barrier ();
 
-       thread = new_thread_with_internal (domain, internal);
+               if (mono_thread_cleanup_fn)
+                       mono_thread_cleanup_fn (thread_get_tid (thread));
 
-       if (!mono_thread_attach_internal (thread, force_attach, TRUE, &stack_ptr)) {
-               /* Mono is shutting down, so just wait for the end */
-               for (;;)
-                       mono_thread_info_sleep (10000, NULL);
+               goto done;
        }
 
-       THREAD_DEBUG (g_message ("%s: Attached thread ID %"G_GSIZE_FORMAT" (handle %p)", __func__, tid, internal->handle));
+       mono_release_type_locks (thread);
 
-       if (mono_thread_attach_cb) {
-               guint8 *staddr;
-               size_t stsize;
+       /* 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_thread_info_get_stack_bounds (&staddr, &stsize);
+       mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
 
-               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);
-       }
+       /*
+        * 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 ();
 
-       /* 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));
+       if (thread == mono_thread_internal_current ())
+               mono_thread_pop_appdomain_ref ();
 
-       return thread;
-}
+       thread->cached_culture_info = NULL;
 
-void
-mono_thread_detach_internal (MonoInternalThread *thread)
-{
-       g_return_if_fail (thread != NULL);
+       mono_free_static_data (thread->static_data);
+       thread->static_data = NULL;
+       ref_stack_destroy (thread->appdomain_refs);
+       thread->appdomain_refs = NULL;
 
-       THREAD_DEBUG (g_message ("%s: mono_thread_detach for %p (%"G_GSIZE_FORMAT")", __func__, thread, (gsize)thread->tid));
+       g_assert (thread->suspended);
+       mono_os_event_destroy (thread->suspended);
+       g_free (thread->suspended);
+       thread->suspended = NULL;
 
-#ifndef HOST_WIN32
-       mono_w32mutex_abandon ();
-#endif
+       if (mono_thread_cleanup_fn)
+               mono_thread_cleanup_fn (thread_get_tid (thread));
+
+       mono_memory_barrier ();
 
-       thread_cleanup (thread);
+       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 ();
 
@@ -1197,7 +1319,7 @@ mono_thread_exit (void)
        if (mono_thread_get_main () && (thread == mono_thread_get_main ()->internal_thread))
                exit (mono_environment_exitcode_get ());
 
-       mono_thread_info_exit ();
+       mono_thread_info_exit (0);
 }
 
 void
@@ -1205,13 +1327,19 @@ ves_icall_System_Threading_Thread_ConstructInternalThread (MonoThread *this_obj)
 {
        MonoInternalThread *internal;
 
-       internal = create_internal_thread ();
+       internal = create_internal_thread_object ();
 
        internal->state = ThreadState_Unstarted;
 
        InterlockedCompareExchangePointer ((volatile gpointer *)&this_obj->internal_thread, internal, NULL);
 }
 
+MonoThread *
+ves_icall_System_Threading_Thread_GetCurrentThread (void)
+{
+       return mono_thread_current ();
+}
+
 HANDLE
 ves_icall_System_Threading_Thread_Thread_internal (MonoThread *this_obj,
                                                                                                   MonoObject *start)
@@ -1258,17 +1386,24 @@ ves_icall_System_Threading_Thread_Thread_internal (MonoThread *this_obj,
  * This is called from the finalizer of the internal thread object.
  */
 void
-ves_icall_System_Threading_InternalThread_Thread_free_internal (MonoInternalThread *this_obj, HANDLE thread)
+ves_icall_System_Threading_InternalThread_Thread_free_internal (MonoInternalThread *this_obj)
 {
-       THREAD_DEBUG (g_message ("%s: Closing thread %p, handle %p", __func__, this, thread));
+       THREAD_DEBUG (g_message ("%s: Closing thread %p, handle %p", __func__, this, this_obj->handle));
 
        /*
-        * Since threads keep a reference to their thread object while running, by the time this function is called,
-        * the thread has already exited/detached, i.e. thread_cleanup () has ran. The exception is during shutdown,
-        * when thread_cleanup () can be called after this.
+        * Since threads keep a reference to their thread object while running, by
+        * the time this function is called, the thread has already exited/detached,
+        * i.e. mono_thread_detach_internal () has ran. The exception is during
+        * shutdown, when mono_thread_detach_internal () can be called after this.
         */
-       if (thread)
-               mono_threads_close_thread_handle (thread);
+       if (this_obj->handle) {
+               mono_threads_close_thread_handle (this_obj->handle);
+               this_obj->handle = NULL;
+       }
+
+#if HOST_WIN32
+       CloseHandle (this_obj->native_handle);
+#endif
 
        if (this_obj->synch_cs) {
                MonoCoopMutex *synch_cs = this_obj->synch_cs;
@@ -1309,8 +1444,8 @@ ves_icall_System_Threading_Thread_Sleep_internal(gint32 ms)
                        if (exc) {
                                mono_raise_exception (exc);
                        } else {
-                               // FIXME: !INFINITE
-                               if (ms != INFINITE)
+                               // FIXME: !MONO_INFINITE_WAIT
+                               if (ms != MONO_INFINITE_WAIT)
                                        break;
                        }
                } else {
@@ -1515,7 +1650,7 @@ ves_icall_System_Threading_Thread_SetPriority (MonoThread *this_obj, int priorit
 
        LOCK_THREAD (internal);
        internal->priority = priority;
-       if (internal->handle != NULL)
+       if (internal->thread_info != NULL)
                mono_thread_internal_set_priority (internal, priority);
        UNLOCK_THREAD (internal);
 }
@@ -1569,7 +1704,7 @@ mono_thread_current (void)
 
        if (!*current_thread_ptr) {
                g_assert (domain != mono_get_root_domain ());
-               *current_thread_ptr = new_thread_with_internal (domain, internal);
+               *current_thread_ptr = create_thread_object (domain, internal);
        }
        return *current_thread_ptr;
 }
@@ -1586,7 +1721,7 @@ mono_thread_current_for_thread (MonoInternalThread *internal)
 
        if (!*current_thread_ptr) {
                g_assert (domain != mono_get_root_domain ());
-               *current_thread_ptr = new_thread_with_internal (domain, internal);
+               *current_thread_ptr = create_thread_object (domain, internal);
        }
        return *current_thread_ptr;
 }
@@ -1599,13 +1734,55 @@ mono_thread_internal_current (void)
        return res;
 }
 
+static MonoThreadInfoWaitRet
+mono_join_uninterrupted (MonoThreadHandle* thread_to_join, gint32 ms, MonoError *error)
+{
+       MonoException *exc;
+       MonoThreadInfoWaitRet ret;
+       gint64 start;
+       gint32 diff_ms;
+       gint32 wait = ms;
+
+       mono_error_init (error);
+
+       start = (ms == -1) ? 0 : mono_msec_ticks ();
+       for (;;) {
+               MONO_ENTER_GC_SAFE;
+               ret = mono_thread_info_wait_one_handle (thread_to_join, ms, TRUE);
+               MONO_EXIT_GC_SAFE;
+
+               if (ret != MONO_THREAD_INFO_WAIT_RET_ALERTED)
+                       return ret;
+
+               exc = mono_thread_execute_interruption ();
+               if (exc) {
+                       mono_error_set_exception_instance (error, exc);
+                       return ret;
+               }
+
+               if (ms == -1)
+                       continue;
+
+               /* Re-calculate ms according to the time passed */
+               diff_ms = (gint32)(mono_msec_ticks () - start);
+               if (diff_ms >= ms) {
+                       ret = MONO_THREAD_INFO_WAIT_RET_TIMEOUT;
+                       return ret;
+               }
+               wait = ms - diff_ms;
+       }
+
+       return ret;
+}
+
 gboolean
 ves_icall_System_Threading_Thread_Join_internal(MonoThread *this_obj, int ms)
 {
        MonoInternalThread *thread = this_obj->internal_thread;
-       HANDLE handle = thread->handle;
+       MonoThreadHandle *handle = thread->handle;
        MonoInternalThread *cur_thread = mono_thread_internal_current ();
        gboolean ret;
+       MonoError error;
 
        if (mono_thread_current_check_pending_interrupt ())
                return FALSE;
@@ -1622,19 +1799,19 @@ ves_icall_System_Threading_Thread_Join_internal(MonoThread *this_obj, int ms)
        UNLOCK_THREAD (thread);
 
        if(ms== -1) {
-               ms=INFINITE;
+               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);
 
-       MONO_ENTER_GC_SAFE;
-       ret=WaitForSingleObjectEx (handle, ms, TRUE);
-       MONO_EXIT_GC_SAFE;
+       ret=mono_join_uninterrupted (handle, ms, &error);
 
        mono_thread_clr_state (cur_thread, ThreadState_WaitSleepJoin);
-       
-       if(ret==WAIT_OBJECT_0) {
+
+       mono_error_set_pending_exception (&error);
+
+       if(ret==MONO_THREAD_INFO_WAIT_RET_SUCCESS_0) {
                THREAD_DEBUG (g_message ("%s: join successful", __func__));
 
                return(TRUE);
@@ -1648,17 +1825,29 @@ ves_icall_System_Threading_Thread_Join_internal(MonoThread *this_obj, int ms)
 #define MANAGED_WAIT_FAILED 0x7fffffff
 
 static gint32
-map_native_wait_result_to_managed (gint32 val)
-{
-       /* WAIT_FAILED in waithandle.cs is different from WAIT_FAILED in Win32 API */
-       return val == WAIT_FAILED ? MANAGED_WAIT_FAILED : val;
+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 gint32
+static MonoW32HandleWaitRet
 mono_wait_uninterrupted (MonoInternalThread *thread, guint32 numhandles, gpointer *handles, gboolean waitall, gint32 ms, MonoError *error)
 {
        MonoException *exc;
-       guint32 ret;
+       MonoW32HandleWaitRet ret;
        gint64 start;
        gint32 diff_ms;
        gint32 wait = ms;
@@ -1668,13 +1857,18 @@ mono_wait_uninterrupted (MonoInternalThread *thread, guint32 numhandles, gpointe
        start = (ms == -1) ? 0 : mono_100ns_ticks ();
        do {
                MONO_ENTER_GC_SAFE;
+#ifdef HOST_WIN32
                if (numhandles != 1)
-                       ret = WaitForMultipleObjectsEx (numhandles, handles, waitall, wait, TRUE);
+                       ret = mono_w32handle_convert_wait_ret (WaitForMultipleObjectsEx (numhandles, handles, waitall, wait, TRUE), numhandles);
                else
-                       ret = WaitForSingleObjectEx (handles [0], ms, TRUE);
+                       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 != WAIT_IO_COMPLETION)
+               if (ret != MONO_W32HANDLE_WAIT_RET_ALERTED)
                        break;
 
                exc = mono_thread_execute_interruption ();
@@ -1689,7 +1883,7 @@ mono_wait_uninterrupted (MonoInternalThread *thread, guint32 numhandles, gpointe
                /* Re-calculate ms according to the time passed */
                diff_ms = (gint32)((mono_100ns_ticks () - start) / 10000);
                if (diff_ms >= ms) {
-                       ret = WAIT_TIMEOUT;
+                       ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
                        break;
                }
                wait = ms - diff_ms;
@@ -1703,14 +1897,14 @@ gint32 ves_icall_System_Threading_WaitHandle_WaitAll_internal(MonoArray *mono_ha
        MonoError error;
        HANDLE *handles;
        guint32 numhandles;
-       guint32 ret;
+       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 (WAIT_FAILED);
+               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);
@@ -1722,7 +1916,7 @@ gint32 ves_icall_System_Threading_WaitHandle_WaitAll_internal(MonoArray *mono_ha
        }
        
        if(ms== -1) {
-               ms=INFINITE;
+               ms=MONO_INFINITE_WAIT;
        }
 
        mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
@@ -1735,8 +1929,7 @@ gint32 ves_icall_System_Threading_WaitHandle_WaitAll_internal(MonoArray *mono_ha
 
        mono_error_set_pending_exception (&error);
 
-       /* WAIT_FAILED in waithandle.cs is different from WAIT_FAILED in Win32 API */
-       return map_native_wait_result_to_managed (ret);
+       return map_native_wait_result_to_managed (ret, numhandles);
 }
 
 gint32 ves_icall_System_Threading_WaitHandle_WaitAny_internal(MonoArray *mono_handles, gint32 ms)
@@ -1744,18 +1937,18 @@ gint32 ves_icall_System_Threading_WaitHandle_WaitAny_internal(MonoArray *mono_ha
        MonoError error;
        HANDLE handles [MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS];
        uintptr_t numhandles;
-       guint32 ret;
+       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 (WAIT_FAILED);
+               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 (WAIT_FAILED);
+               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);
@@ -1763,7 +1956,7 @@ gint32 ves_icall_System_Threading_WaitHandle_WaitAny_internal(MonoArray *mono_ha
        }
        
        if(ms== -1) {
-               ms=INFINITE;
+               ms=MONO_INFINITE_WAIT;
        }
 
        mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
@@ -1776,24 +1969,23 @@ gint32 ves_icall_System_Threading_WaitHandle_WaitAny_internal(MonoArray *mono_ha
 
        mono_error_set_pending_exception (&error);
 
-       /* WAIT_FAILED in waithandle.cs is different from WAIT_FAILED in Win32 API */
-       return map_native_wait_result_to_managed (ret);
+       return map_native_wait_result_to_managed (ret, numhandles);
 }
 
 gint32 ves_icall_System_Threading_WaitHandle_WaitOne_internal(HANDLE handle, gint32 ms)
 {
        MonoError error;
-       guint32 ret;
+       MonoW32HandleWaitRet ret;
        MonoInternalThread *thread = mono_thread_internal_current ();
 
        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=INFINITE;
+               ms=MONO_INFINITE_WAIT;
        }
        
        if (mono_thread_current_check_pending_interrupt ())
-               return map_native_wait_result_to_managed (WAIT_FAILED);
+               return map_native_wait_result_to_managed (MONO_W32HANDLE_WAIT_RET_FAILED, 0);
 
        mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
        
@@ -1802,30 +1994,34 @@ gint32 ves_icall_System_Threading_WaitHandle_WaitOne_internal(HANDLE handle, gin
        mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
 
        mono_error_set_pending_exception (&error);
-       return map_native_wait_result_to_managed (ret);
+       return map_native_wait_result_to_managed (ret, 1);
 }
 
 gint32
 ves_icall_System_Threading_WaitHandle_SignalAndWait_Internal (HANDLE toSignal, HANDLE toWait, gint32 ms)
 {
-       guint32 ret;
+       MonoW32HandleWaitRet ret;
        MonoInternalThread *thread = mono_thread_internal_current ();
 
        if (ms == -1)
-               ms = INFINITE;
+               ms = MONO_INFINITE_WAIT;
 
        if (mono_thread_current_check_pending_interrupt ())
-               return map_native_wait_result_to_managed (WAIT_FAILED);
+               return map_native_wait_result_to_managed (MONO_W32HANDLE_WAIT_RET_FAILED, 0);
 
        mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
        
        MONO_ENTER_GC_SAFE;
-       ret = SignalObjectAndWait (toSignal, toWait, ms, TRUE);
+#ifdef HOST_WIN32
+       ret = mono_w32handle_convert_wait_ret (SignalObjectAndWait (toSignal, toWait, ms, TRUE), 1);
+#else
+       ret = mono_w32handle_signal_and_wait (toSignal, toWait, ms, TRUE);
+#endif
        MONO_EXIT_GC_SAFE;
        
        mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
 
-       return map_native_wait_result_to_managed (ret);
+       return map_native_wait_result_to_managed (ret, 1);
 }
 
 gint32 ves_icall_System_Threading_Interlocked_Increment_Int (gint32 *location)
@@ -2072,7 +2268,7 @@ ves_icall_System_Threading_Thread_ClrState (MonoInternalThread* this_obj, guint3
                 * be notified, since it has to rebuild the list of threads to
                 * wait for.
                 */
-               mono_w32event_set (background_change_event);
+               mono_os_event_set (&background_change_event);
        }
 }
 
@@ -2086,7 +2282,7 @@ ves_icall_System_Threading_Thread_SetState (MonoInternalThread* this_obj, guint3
                 * be notified, since it has to rebuild the list of threads to
                 * wait for.
                 */
-               mono_w32event_set (background_change_event);
+               mono_os_event_set (&background_change_event);
        }
 }
 
@@ -2240,6 +2436,8 @@ ves_icall_System_Threading_Thread_ResetAbort (MonoThread *this_obj)
                mono_set_pending_exception (mono_get_exception_thread_state (msg));
                return;
        }
+
+       mono_get_eh_callbacks ()->mono_clear_abort_threshold ();
        thread->abort_exc = NULL;
        if (thread->abort_state_handle) {
                mono_gchandle_free (thread->abort_state_handle);
@@ -2257,6 +2455,7 @@ mono_thread_internal_reset_abort (MonoInternalThread *thread)
        thread->state &= ~ThreadState_AbortRequested;
 
        if (thread->abort_exc) {
+               mono_get_eh_callbacks ()->mono_clear_abort_threshold ();
                thread->abort_exc = NULL;
                if (thread->abort_state_handle) {
                        mono_gchandle_free (thread->abort_state_handle);
@@ -2324,6 +2523,7 @@ mono_thread_suspend (MonoInternalThread *thread)
        }
        
        thread->state |= ThreadState_SuspendRequested;
+       mono_os_event_reset (thread->suspended);
 
        if (thread == mono_thread_internal_current ()) {
                /* calls UNLOCK_THREAD (thread) */
@@ -2350,7 +2550,9 @@ static gboolean
 mono_thread_resume (MonoInternalThread *thread)
 {
        if ((thread->state & ThreadState_SuspendRequested) != 0) {
+               // MOSTLY_ASYNC_SAFE_PRINTF ("RESUME (1) thread %p\n", thread_get_tid (thread));
                thread->state &= ~ThreadState_SuspendRequested;
+               mono_os_event_set (thread->suspended);
                return TRUE;
        }
 
@@ -2359,16 +2561,23 @@ mono_thread_resume (MonoInternalThread *thread)
                (thread->state & ThreadState_Aborted) != 0 || 
                (thread->state & ThreadState_Stopped) != 0)
        {
+               // MOSTLY_ASYNC_SAFE_PRINTF ("RESUME (2) thread %p\n", thread_get_tid (thread));
                return FALSE;
        }
 
-       UNLOCK_THREAD (thread);
+       // MOSTLY_ASYNC_SAFE_PRINTF ("RESUME (3) thread %p\n", thread_get_tid (thread));
 
-       /* Awake the thread */
-       if (!mono_thread_info_resume (thread_get_tid (thread)))
-               return FALSE;
+       mono_os_event_set (thread->suspended);
 
-       LOCK_THREAD (thread);
+       if (!thread->self_suspended) {
+               UNLOCK_THREAD (thread);
+
+               /* Awake the thread */
+               if (!mono_thread_info_resume (thread_get_tid (thread)))
+                       return FALSE;
+
+               LOCK_THREAD (thread);
+       }
 
        thread->state &= ~ThreadState_Suspended;
 
@@ -2822,13 +3031,6 @@ ves_icall_System_Runtime_Remoting_Contexts_Context_ReleaseContext (MonoAppContex
        mono_profiler_context_unloaded (ctx);
 }
 
-void
-mono_thread_init_tls (void)
-{
-       MONO_FAST_TLS_INIT (tls_current_object);
-       mono_native_tls_alloc (&current_object_key, NULL);
-}
-
 void mono_thread_init (MonoThreadStartCB start_cb,
                       MonoThreadAttachCB attach_cb)
 {
@@ -2837,35 +3039,25 @@ void mono_thread_init (MonoThreadStartCB start_cb,
        mono_os_mutex_init_recursive(&interlocked_mutex);
        mono_os_mutex_init_recursive(&joinable_threads_mutex);
        
-       background_change_event = mono_w32event_create (TRUE, FALSE);
-       g_assert(background_change_event != NULL);
+       mono_os_event_init (&background_change_event, FALSE);
        
        mono_init_static_data_info (&thread_static_info);
        mono_init_static_data_info (&context_static_info);
 
-       THREAD_DEBUG (g_message ("%s: Allocated current_object_key %d", __func__, current_object_key));
-
        mono_thread_start_cb = start_cb;
        mono_thread_attach_cb = attach_cb;
-
-       /* Get a pseudo handle to the current process.  This is just a
-        * kludge so that wapi can build a process handle if needed.
-        * As a pseudo handle is returned, we don't need to clean
-        * anything up.
-        */
-       GetCurrentProcess ();
 }
 
 void mono_thread_cleanup (void)
 {
-#if !defined(RUN_IN_SUBTHREAD)
+#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
         * processes, see bug 74680.)  This will happen when the
         * thread exits, but if it's not running in a subthread it
         * won't exit in time.
         */
-       mono_thread_info_set_exited (mono_thread_info_current ());
+       mono_w32mutex_abandon ();
 #endif
 
 #if 0
@@ -2877,10 +3069,8 @@ void mono_thread_cleanup (void)
        mono_os_mutex_destroy (&interlocked_mutex);
        mono_os_mutex_destroy (&delayed_free_table_mutex);
        mono_os_mutex_destroy (&small_id_mutex);
-       CloseHandle (background_change_event);
+       mono_os_event_destroy (&background_change_event);
 #endif
-
-       mono_native_tls_free (current_object_key);
 }
 
 void
@@ -2909,75 +3099,39 @@ static void print_tids (gpointer key, gpointer value, gpointer user)
 
 struct wait_data 
 {
-       HANDLE handles[MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS];
+       MonoThreadHandle *handles[MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS];
        MonoInternalThread *threads[MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS];
        guint32 num;
 };
 
 static void
-wait_for_tids (struct wait_data *wait, guint32 timeout)
-{
-       guint32 i, ret;
-       
-       THREAD_DEBUG (g_message("%s: %d threads to wait for in this batch", __func__, wait->num));
-
-       MONO_ENTER_GC_SAFE;
-       ret=WaitForMultipleObjectsEx(wait->num, wait->handles, TRUE, timeout, TRUE);
-       MONO_EXIT_GC_SAFE;
-
-       if(ret==WAIT_FAILED) {
-               /* See the comment in build_wait_tids() */
-               THREAD_DEBUG (g_message ("%s: Wait failed", __func__));
-               return;
-       }
-       
-       for(i=0; i<wait->num; i++)
-               mono_threads_close_thread_handle (wait->handles [i]);
-
-       if (ret == WAIT_TIMEOUT)
-               return;
-
-       for(i=0; i<wait->num; i++) {
-               MonoInternalThread *internal;
-
-               internal = wait->threads [i];
-
-               mono_threads_lock ();
-               if (mono_g_hash_table_lookup (threads, (gpointer) internal->tid) == internal)
-                       g_error ("%s: failed to call mono_thread_detach_internal on thread %p, InternalThread: %p", __func__, internal->tid, internal);
-               mono_threads_unlock ();
-       }
-}
-
-static void wait_for_tids_or_state_change (struct wait_data *wait, guint32 timeout)
+wait_for_tids (struct wait_data *wait, guint32 timeout, gboolean check_state_change)
 {
-       guint32 i, ret, count;
+       guint32 i;
+       MonoThreadInfoWaitRet ret;
        
        THREAD_DEBUG (g_message("%s: %d threads to wait for in this batch", __func__, wait->num));
 
-       /* Add the thread state change event, so it wakes up if a thread changes
-        * to background mode.
-        */
-       count = wait->num;
-       if (count < MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS) {
-               wait->handles [count] = background_change_event;
-               count++;
-       }
+       /* Add the thread state change event, so it wakes
+        * up if a thread changes to background mode. */
 
        MONO_ENTER_GC_SAFE;
-       ret=WaitForMultipleObjectsEx (count, wait->handles, FALSE, timeout, TRUE);
+       if (check_state_change)
+               ret = mono_thread_info_wait_multiple_handle (wait->handles, wait->num, &background_change_event, FALSE, timeout, TRUE);
+       else
+               ret = mono_thread_info_wait_multiple_handle (wait->handles, wait->num, NULL, TRUE, timeout, TRUE);
        MONO_EXIT_GC_SAFE;
 
-       if(ret==WAIT_FAILED) {
+       if (ret == MONO_THREAD_INFO_WAIT_RET_FAILED) {
                /* See the comment in build_wait_tids() */
                THREAD_DEBUG (g_message ("%s: Wait failed", __func__));
                return;
        }
        
-       for(i=0; i<wait->num; i++)
+       for( i = 0; i < wait->num; i++)
                mono_threads_close_thread_handle (wait->handles [i]);
 
-       if (ret == WAIT_TIMEOUT)
+       if (ret == MONO_THREAD_INFO_WAIT_RET_TIMEOUT)
                return;
        
        if (ret < wait->num) {
@@ -2996,8 +3150,7 @@ static void build_wait_tids (gpointer key, gpointer value, gpointer user)
 {
        struct wait_data *wait=(struct wait_data *)user;
 
-       if(wait->num<MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS) {
-               HANDLE handle;
+       if(wait->num<MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS - 1) {
                MonoInternalThread *thread=(MonoInternalThread *)value;
 
                /* Ignore background threads, we abort them later */
@@ -3027,15 +3180,9 @@ static void build_wait_tids (gpointer key, gpointer value, gpointer user)
                        return;
                }
 
-               handle = mono_threads_open_thread_handle (thread->handle, thread_get_tid (thread));
-               if (handle == NULL) {
-                       THREAD_DEBUG (g_message ("%s: ignoring unopenable thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
-                       return;
-               }
-               
                THREAD_DEBUG (g_message ("%s: Invoking mono_thread_manage callback on thread %p", __func__, thread));
                if ((thread->manage_callback == NULL) || (thread->manage_callback (thread->root_domain_thread) == TRUE)) {
-                       wait->handles[wait->num]=handle;
+                       wait->handles[wait->num]=mono_threads_open_thread_handle (thread->handle);
                        wait->threads[wait->num]=thread;
                        wait->num++;
 
@@ -3058,7 +3205,6 @@ remove_and_abort_threads (gpointer key, gpointer value, gpointer user)
        struct wait_data *wait=(struct wait_data *)user;
        MonoNativeThreadId self = mono_native_thread_id_get ();
        MonoInternalThread *thread = (MonoInternalThread *)value;
-       HANDLE handle;
 
        if (wait->num >= MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS)
                return FALSE;
@@ -3068,11 +3214,7 @@ remove_and_abort_threads (gpointer key, gpointer value, gpointer user)
             && (thread->state & ThreadState_Background) != 0
             && (thread->flags & MONO_THREAD_FLAG_DONT_MANAGE) == 0
        ) {
-               handle = mono_threads_open_thread_handle (thread->handle, thread_get_tid (thread));
-               if (handle == NULL)
-                       return FALSE;
-
-               wait->handles[wait->num] = handle;
+               wait->handles[wait->num] = mono_threads_open_thread_handle (thread->handle);
                wait->threads[wait->num] = thread;
                wait->num++;
 
@@ -3120,7 +3262,7 @@ mono_threads_set_shutting_down (void)
                mono_thread_detach_internal (current_thread);
 
                /* Wake up other threads potentially waiting for us */
-               mono_thread_info_exit ();
+               mono_thread_info_exit (0);
        } else {
                shutting_down = TRUE;
 
@@ -3128,7 +3270,7 @@ mono_threads_set_shutting_down (void)
                 * interrupt the main thread if it is waiting for all
                 * the other threads.
                 */
-               mono_w32event_set (background_change_event);
+               mono_os_event_set (&background_change_event);
                
                mono_threads_unlock ();
        }
@@ -3161,16 +3303,15 @@ void mono_thread_manage (void)
                THREAD_DEBUG (g_message ("%s: There are %d threads to join", __func__, mono_g_hash_table_size (threads));
                        mono_g_hash_table_foreach (threads, print_tids, NULL));
        
-               mono_w32event_reset (background_change_event);
+               mono_os_event_reset (&background_change_event);
                wait->num=0;
-               /*We must zero all InternalThread pointers to avoid making the GC unhappy.*/
+               /* We must zero all InternalThread pointers to avoid making the GC unhappy. */
                memset (wait->threads, 0, MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS * SIZEOF_VOID_P);
                mono_g_hash_table_foreach (threads, build_wait_tids, wait);
                mono_threads_unlock ();
-               if(wait->num>0) {
+               if (wait->num > 0)
                        /* Something to wait for */
-                       wait_for_tids_or_state_change (wait, INFINITE);
-               }
+                       wait_for_tids (wait, MONO_INFINITE_WAIT, TRUE);
                THREAD_DEBUG (g_message ("%s: I have %d threads after waiting.", __func__, wait->num));
        } while(wait->num>0);
 
@@ -3196,9 +3337,9 @@ void mono_thread_manage (void)
                mono_threads_unlock ();
 
                THREAD_DEBUG (g_message ("%s: wait->num is now %d", __func__, wait->num));
-               if(wait->num>0) {
+               if (wait->num > 0) {
                        /* Something to wait for */
-                       wait_for_tids (wait, INFINITE);
+                       wait_for_tids (wait, MONO_INFINITE_WAIT, FALSE);
                }
        } while (wait->num > 0);
        
@@ -3215,7 +3356,6 @@ collect_threads_for_suspend (gpointer key, gpointer value, gpointer user_data)
 {
        MonoInternalThread *thread = (MonoInternalThread*)value;
        struct wait_data *wait = (struct wait_data*)user_data;
-       HANDLE handle;
 
        /* 
         * We try to exclude threads early, to avoid running into the MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS
@@ -3227,11 +3367,7 @@ collect_threads_for_suspend (gpointer key, gpointer value, gpointer user_data)
                return;
 
        if (wait->num<MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS) {
-               handle = mono_threads_open_thread_handle (thread->handle, thread_get_tid (thread));
-               if (handle == NULL)
-                       return;
-
-               wait->handles [wait->num] = handle;
+               wait->handles [wait->num] = mono_threads_open_thread_handle (thread->handle);
                wait->threads [wait->num] = thread;
                wait->num++;
        }
@@ -3294,8 +3430,8 @@ void mono_thread_suspend_all_other_threads (void)
                             || mono_gc_is_finalizer_internal_thread (thread)
                             || (thread->flags & MONO_THREAD_FLAG_DONT_MANAGE)
                        ) {
-                               //mono_threads_close_thread_handle (wait->handles [i]);
-                               wait->threads [i] = NULL; /* ignore this thread in next loop */
+                               mono_threads_close_thread_handle (wait->handles [i]);
+                               wait->threads [i] = NULL;
                                continue;
                        }
 
@@ -3306,7 +3442,7 @@ void mono_thread_suspend_all_other_threads (void)
                                (thread->state & ThreadState_Stopped) != 0) {
                                UNLOCK_THREAD (thread);
                                mono_threads_close_thread_handle (wait->handles [i]);
-                               wait->threads [i] = NULL; /* ignore this thread in next loop */
+                               wait->threads [i] = NULL;
                                continue;
                        }
 
@@ -3317,9 +3453,13 @@ void mono_thread_suspend_all_other_threads (void)
                                thread->state &= ~ThreadState_AbortRequested;
                        
                        thread->state |= ThreadState_SuspendRequested;
+                       mono_os_event_reset (thread->suspended);
 
                        /* Signal the thread to suspend + calls UNLOCK_THREAD (thread) */
                        async_suspend_internal (thread, TRUE);
+
+                       mono_threads_close_thread_handle (wait->handles [i]);
+                       wait->threads [i] = NULL;
                }
                if (eventidx <= 0) {
                        /* 
@@ -3755,10 +3895,7 @@ collect_appdomain_thread (gpointer key, gpointer value, gpointer user_data)
                /* printf ("ABORTING THREAD %p BECAUSE IT REFERENCES DOMAIN %s.\n", thread->tid, domain->friendly_name); */
 
                if(data->wait.num<MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS) {
-                       HANDLE handle = mono_threads_open_thread_handle (thread->handle, thread_get_tid (thread));
-                       if (handle == NULL)
-                               return;
-                       data->wait.handles [data->wait.num] = handle;
+                       data->wait.handles [data->wait.num] = mono_threads_open_thread_handle (thread->handle);
                        data->wait.threads [data->wait.num] = thread;
                        data->wait.num++;
                } else {
@@ -3807,7 +3944,7 @@ mono_threads_abort_appdomain_threads (MonoDomain *domain, int timeout)
                         * We should wait for the threads either to abort, or to leave the
                         * domain. We can't do the latter, so we wait with a timeout.
                         */
-                       wait_for_tids (&user_data.wait, 100);
+                       wait_for_tids (&user_data.wait, 100, FALSE);
                }
 
                /* Update remaining time */
@@ -3868,17 +4005,22 @@ mono_thread_get_undeniable_exception (void)
 {
        MonoInternalThread *thread = mono_thread_internal_current ();
 
-       if (thread && thread->abort_exc && !is_running_protected_wrapper ()) {
-               /*
-                * FIXME: Clear the abort exception and return an AppDomainUnloaded 
-                * exception if the thread no longer references a dying appdomain.
-                */
-               thread->abort_exc->trace_ips = NULL;
-               thread->abort_exc->stack_trace = NULL;
-               return thread->abort_exc;
-       }
+       if (!(thread && thread->abort_exc && !is_running_protected_wrapper ()))
+               return NULL;
 
-       return NULL;
+       // We don't want to have our exception effect calls made by
+       // the catching block
+
+       if (!mono_get_eh_callbacks ()->mono_above_abort_threshold ())
+               return NULL;
+
+       /*
+        * FIXME: Clear the abort exception and return an AppDomainUnloaded 
+        * exception if the thread no longer references a dying appdomain.
+        */ 
+       thread->abort_exc->trace_ips = NULL;
+       thread->abort_exc->stack_trace = NULL;
+       return thread->abort_exc;
 }
 
 #if MONO_SMALL_CONFIG
@@ -4324,7 +4466,7 @@ mono_thread_execute_interruption (void)
        LOCK_THREAD (thread);
 
        /* MonoThread::interruption_requested can only be changed with atomics */
-       if (InterlockedCompareExchange (&thread->interruption_requested, FALSE, TRUE)) {
+       if (mono_thread_clear_interruption_requested (thread)) {
                /* this will consume pending APC calls */
 #ifdef HOST_WIN32
                WaitForSingleObjectEx (GetCurrentThread(), 0, TRUE);
@@ -4404,7 +4546,7 @@ mono_thread_request_interruption (gboolean running_managed)
                thread->state & ThreadState_Background)
                ExitThread (1);
 #endif
-       if (InterlockedCompareExchange (&thread->interruption_requested, 1, 0) == 1)
+       if (!mono_thread_set_interruption_requested (thread))
                return NULL;
        InterlockedIncrement (&thread_interruption_requested);
 
@@ -4417,7 +4559,7 @@ mono_thread_request_interruption (gboolean running_managed)
                   or similar */
                /* Our implementation of this function ignores the func argument */
 #ifdef HOST_WIN32
-               QueueUserAPC ((PAPCFUNC)dummy_apc, thread->handle, (ULONG_PTR)NULL);
+               QueueUserAPC ((PAPCFUNC)dummy_apc, thread->native_handle, (ULONG_PTR)NULL);
 #else
                mono_thread_info_self_interrupt ();
 #endif
@@ -4448,7 +4590,7 @@ mono_thread_resume_interruption (void)
        if (!still_aborting)
                return FALSE;
 
-       if (InterlockedCompareExchange (&thread->interruption_requested, 1, 0) == 1)
+       if (!mono_thread_set_interruption_requested (thread))
                return NULL;
        InterlockedIncrement (&thread_interruption_requested);
 
@@ -4463,7 +4605,7 @@ gboolean mono_thread_interruption_requested ()
                MonoInternalThread *thread = mono_thread_internal_current ();
                /* The thread may already be stopping */
                if (thread != NULL) 
-                       return (thread->interruption_requested);
+                       return mono_thread_get_interruption_requested (thread);
        }
        return FALSE;
 }
@@ -4474,15 +4616,14 @@ mono_thread_interruption_checkpoint_request (gboolean bypass_abort_protection)
        MonoInternalThread *thread = mono_thread_internal_current ();
 
        /* The thread may already be stopping */
-       if (thread == NULL)
+       if (!thread)
+               return NULL;
+       if (!mono_thread_get_interruption_requested (thread))
+               return NULL;
+       if (!bypass_abort_protection && is_running_protected_wrapper ())
                return NULL;
 
-       if (thread->interruption_requested && (bypass_abort_protection || !is_running_protected_wrapper ())) {
-               MonoException* exc = mono_thread_execute_interruption ();
-               if (exc)
-                       return exc;
-       }
-       return NULL;
+       return mono_thread_execute_interruption ();
 }
 
 /*
@@ -4627,28 +4768,27 @@ mono_thread_test_state (MonoInternalThread *thread, MonoThreadState test)
        return ret;
 }
 
-static gboolean has_tls_get = FALSE;
-
-void
-mono_runtime_set_has_tls_get (gboolean val)
-{
-       has_tls_get = val;
-}
-
-gboolean
-mono_runtime_has_tls_get (void)
-{
-       return has_tls_get;
-}
-
 static void
 self_interrupt_thread (void *_unused)
 {
-       MonoThreadInfo *info = mono_thread_info_current ();
-       MonoException *exc = mono_thread_execute_interruption ();
-       if (exc) /*We must use _with_context since we didn't trampoline into the runtime*/
-               mono_raise_exception_with_context (exc, &info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX].ctx); /* FIXME using thread_saved_state [ASYNC_SUSPEND_STATE_INDEX] can race with another suspend coming in. */
-       g_assert_not_reached (); /*this MUST not happen since we can't resume from an async call*/
+       MonoException *exc;
+       MonoThreadInfo *info;
+
+       exc = mono_thread_execute_interruption ();
+       if (!exc) {
+               if (mono_threads_is_coop_enabled ()) {
+                       /* We can return from an async call in coop, as
+                        * it's simply called when exiting the safepoint */
+                       return;
+               }
+
+               g_error ("%s: we can't resume from an async call", __func__);
+       }
+
+       info = mono_thread_info_current ();
+
+       /* We must use _with_context since we didn't trampoline into the runtime */
+       mono_raise_exception_with_context (exc, &info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX].ctx); /* FIXME using thread_saved_state [ASYNC_SUSPEND_STATE_INDEX] can race with another suspend coming in. */
 }
 
 static gboolean
@@ -4709,11 +4849,11 @@ async_abort_critical (MonoThreadInfo *info, gpointer ud)
        The target thread is running at least one protected block, which must not be interrupted, so we give up.
        The protected block code will give them a chance when appropriate.
        */
-       if (thread->abort_protected_block_count)
+       if (mono_thread_get_abort_prot_block_count (thread) > 0)
                return MonoResumeThread;
 
        /*someone is already interrupting it*/
-       if (InterlockedCompareExchange (&thread->interruption_requested, 1, 0) == 1)
+       if (!mono_thread_set_interruption_requested (thread))
                return MonoResumeThread;
 
        InterlockedIncrement (&thread_interruption_requested);
@@ -4799,11 +4939,16 @@ async_suspend_critical (MonoThreadInfo *info, gpointer ud)
        running_managed = mono_jit_info_match (ji, MONO_CONTEXT_GET_IP (&mono_thread_info_get_suspend_state (info)->ctx));
 
        if (running_managed && !protected_wrapper) {
-               thread->state &= ~ThreadState_SuspendRequested;
-               thread->state |= ThreadState_Suspended;
-               return KeepSuspended;
+               if (mono_threads_is_coop_enabled ()) {
+                       mono_thread_info_setup_async_call (info, self_interrupt_thread, NULL);
+                       return MonoResumeThread;
+               } else {
+                       thread->state &= ~ThreadState_SuspendRequested;
+                       thread->state |= ThreadState_Suspended;
+                       return KeepSuspended;
+               }
        } else {
-               if (InterlockedCompareExchange (&thread->interruption_requested, 1, 0) == 0)
+               if (mono_thread_set_interruption_requested (thread))
                        InterlockedIncrement (&thread_interruption_requested);
                if (data->interrupt)
                        data->interrupt_token = mono_thread_info_prepare_interrupt ((MonoThreadInfo *)thread->thread_info);
@@ -4820,6 +4965,10 @@ async_suspend_internal (MonoInternalThread *thread, gboolean interrupt)
 
        g_assert (thread != mono_thread_internal_current ());
 
+       // MOSTLY_ASYNC_SAFE_PRINTF ("ASYNC SUSPEND thread %p\n", thread_get_tid (thread));
+
+       thread->self_suspended = FALSE;
+
        data.thread = thread;
        data.interrupt = interrupt;
        data.interrupt_token = NULL;
@@ -4836,16 +4985,26 @@ static void
 self_suspend_internal (void)
 {
        MonoInternalThread *thread;
+       MonoOSEvent *event;
+       MonoOSEventWaitRet res;
 
        thread = mono_thread_internal_current ();
 
-       mono_thread_info_begin_self_suspend ();
+       // MOSTLY_ASYNC_SAFE_PRINTF ("SELF SUSPEND thread %p\n", thread_get_tid (thread));
+
+       thread->self_suspended = TRUE;
+
        thread->state &= ~ThreadState_SuspendRequested;
        thread->state |= ThreadState_Suspended;
 
        UNLOCK_THREAD (thread);
 
-       mono_thread_info_end_self_suspend ();
+       event = thread->suspended;
+
+       MONO_ENTER_GC_SAFE;
+       res = mono_os_event_wait_one (event, MONO_INFINITE_WAIT);
+       g_assert (res == MONO_OS_EVENT_WAIT_RET_SUCCESS_0 || res == MONO_OS_EVENT_WAIT_RET_ALERTED);
+       MONO_EXIT_GC_SAFE;
 }
 
 /*
@@ -4926,7 +5085,9 @@ mono_threads_join_threads (void)
                        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 {
@@ -5097,34 +5258,15 @@ mono_threads_detach_coop (gpointer cookie, gpointer *dummy)
        }
 }
 
-void
-mono_threads_begin_abort_protected_block (void)
-{
-       MonoInternalThread *thread;
-
-       thread = mono_thread_internal_current ();
-       ++thread->abort_protected_block_count;
-       mono_memory_barrier ();
-}
-
-void
-mono_threads_end_abort_protected_block (void)
-{
-       MonoInternalThread *thread;
-
-       thread = mono_thread_internal_current ();
-
-       mono_memory_barrier ();
-       --thread->abort_protected_block_count;
-}
-
 MonoException*
 mono_thread_try_resume_interruption (void)
 {
        MonoInternalThread *thread;
 
        thread = mono_thread_internal_current ();
-       if (thread->abort_protected_block_count || mono_get_eh_callbacks ()->mono_current_thread_has_handle_block_guard ())
+       if (!mono_get_eh_callbacks ()->mono_above_abort_threshold ())
+               return NULL;
+       if (mono_thread_get_abort_prot_block_count (thread) > 0 || mono_get_eh_callbacks ()->mono_current_thread_has_handle_block_guard ())
                return NULL;
 
        return mono_thread_resume_interruption ();
@@ -5144,7 +5286,7 @@ mono_threads_is_ready_to_be_interrupted (void)
                return FALSE;
        }
 
-       if (thread->abort_protected_block_count || mono_get_eh_callbacks ()->mono_current_thread_has_handle_block_guard ()) {
+       if (mono_thread_get_abort_prot_block_count (thread) || mono_get_eh_callbacks ()->mono_current_thread_has_handle_block_guard ()) {
                UNLOCK_THREAD (thread);
                return FALSE;
        }