[threads] Store MonoInternalThread in MonoThreadInfo for use when detaching (#5058)
[mono.git] / mono / metadata / threads.c
index d874b5664c753ac83209ae3d8b9f7f46ccdfa8a0..69a00d0b6880b51fd2f4a92f6c52973468f77486 100644 (file)
@@ -1,5 +1,6 @@
-/*
- * threads.c: Thread support internal calls
+/**
+ * \file
+ * Thread support internal calls
  *
  * Author:
  *     Dick Porter (dick@ximian.com)
@@ -28,9 +29,8 @@
 #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/metadata/debug-internals.h>
 #include <mono/utils/monobitset.h>
 #include <mono/utils/mono-compiler.h>
 #include <mono/utils/mono-mmap.h>
@@ -42,7 +42,6 @@
 #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/os-event.h>
 #include <mono/utils/mono-threads-debug.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>
@@ -115,14 +115,6 @@ typedef struct {
        StaticDataFreeList *freelist;
 } StaticDataInfo;
 
-/* Number of cached culture objects in the MonoThread->cached_culture_info array
- * (per-type): we use the first NUM entries for CultureInfo and the last for
- * UICultureInfo. So the size of the array is really NUM_CACHED_CULTURES * 2.
- */
-#define NUM_CACHED_CULTURES 4
-#define CULTURES_START_IDX 0
-#define UICULTURES_START_IDX NUM_CACHED_CULTURES
-
 /* Controls access to the 'threads' hash table */
 static void mono_threads_lock (void);
 static void mono_threads_unlock (void);
@@ -143,7 +135,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;
 
@@ -207,7 +199,7 @@ 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)
@@ -228,6 +220,167 @@ get_next_managed_thread_id (void)
        return InterlockedIncrement (&managed_thread_id_counter);
 }
 
+/*
+ * We separate interruptions/exceptions into either sync (they can be processed anytime,
+ * normally as soon as they are set, and are set by the same thread) and async (they can't
+ * be processed inside abort protected blocks and are normally set by other threads). We
+ * can have both a pending sync and async interruption. In this case, the sync exception is
+ * processed first. Since we clean sync flag first, mono_thread_execute_interruption must
+ * also handle all sync type exceptions before the async type exceptions.
+ */
+enum {
+       INTERRUPT_SYNC_REQUESTED_BIT = 0x1,
+       INTERRUPT_ASYNC_REQUESTED_BIT = 0x2,
+       INTERRUPT_REQUESTED_MASK = 0x3,
+       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)
+{
+       gsize state = thread->thread_state;
+       return (state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT;
+}
+
+void
+mono_threads_begin_abort_protected_block (void)
+{
+       MonoInternalThread *thread = mono_thread_internal_current ();
+       gsize old_state, new_state;
+       int new_val;
+       do {
+               old_state = thread->thread_state;
+
+               new_val = ((old_state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT) + 1;
+               //bounds check abort_prot_count
+               g_assert (new_val > 0);
+               g_assert (new_val < (1 << ABORT_PROT_BLOCK_BITS));
+
+               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);
+
+       /* 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)) {
+               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
+mono_thread_state_has_interruption (gsize state)
+{
+       /* pending exception, self abort */
+       if (state & INTERRUPT_SYNC_REQUESTED_BIT)
+               return TRUE;
+
+       /* abort, interruption, suspend */
+       if ((state & INTERRUPT_ASYNC_REQUESTED_BIT) && !(state & ABORT_PROT_BLOCK_MASK))
+               return TRUE;
+
+       return FALSE;
+}
+
+gboolean
+mono_threads_end_abort_protected_block (void)
+{
+       MonoInternalThread *thread = mono_thread_internal_current ();
+       gsize old_state, new_state;
+       int new_val;
+       do {
+               old_state = thread->thread_state;
+
+               //bounds check abort_prot_count
+               new_val = ((old_state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT) - 1;
+               g_assert (new_val >= 0);
+               g_assert (new_val < (1 << ABORT_PROT_BLOCK_BITS));
+
+               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)) {
+               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);
+}
+
+static gboolean
+mono_thread_get_interruption_requested (MonoInternalThread *thread)
+{
+       gsize state = thread->thread_state;
+
+       return mono_thread_state_has_interruption (state);
+}
+
+/*
+ * Returns TRUE is there was a state change
+ * We clear a single interruption request, sync has priority.
+ */
+static gboolean
+mono_thread_clear_interruption_requested (MonoInternalThread *thread)
+{
+       gsize old_state, new_state;
+       do {
+               old_state = thread->thread_state;
+
+               // no interruption to process
+               if (!(old_state & INTERRUPT_SYNC_REQUESTED_BIT) &&
+                               (!(old_state & INTERRUPT_ASYNC_REQUESTED_BIT) || (old_state & ABORT_PROT_BLOCK_MASK)))
+                       return FALSE;
+
+               if (old_state & INTERRUPT_SYNC_REQUESTED_BIT)
+                       new_state = old_state & ~INTERRUPT_SYNC_REQUESTED_BIT;
+               else
+                       new_state = old_state & ~INTERRUPT_ASYNC_REQUESTED_BIT;
+       } 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;
+}
+
+/* Returns TRUE is there was a state change and the interruption can be processed */
+static gboolean
+mono_thread_set_interruption_requested (MonoInternalThread *thread)
+{
+       //always force when the current thread is doing it to itself.
+       gboolean sync = thread == mono_thread_internal_current ();
+       gsize old_state, new_state;
+       do {
+               old_state = thread->thread_state;
+
+               //Already set
+               if ((sync && (old_state & INTERRUPT_SYNC_REQUESTED_BIT)) ||
+                               (!sync && (old_state & INTERRUPT_ASYNC_REQUESTED_BIT)))
+                       return FALSE;
+
+               if (sync)
+                       new_state = old_state | INTERRUPT_SYNC_REQUESTED_BIT;
+               else
+                       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)) {
+               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);
+}
+
 static inline MonoNativeThreadId
 thread_get_tid (MonoInternalThread *thread)
 {
@@ -476,7 +629,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;
                }
        }
 
@@ -495,7 +649,7 @@ static void
 mono_alloc_static_data (gpointer **static_data_ptr, guint32 offset, gboolean threadlocal);
 
 static gboolean
-mono_thread_attach_internal (MonoThread *thread, gboolean force_attach, gboolean force_domain, gsize *stack_ptr)
+mono_thread_attach_internal (MonoThread *thread, gboolean force_attach, gboolean force_domain)
 {
        MonoThreadInfo *info;
        MonoInternalThread *internal;
@@ -504,8 +658,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 ());
@@ -513,7 +677,6 @@ mono_thread_attach_internal (MonoThread *thread, gboolean force_attach, gboolean
        internal->tid = MONO_NATIVE_THREAD_ID_TO_UINT (mono_native_thread_id_get ());
        internal->thread_info = info;
        internal->small_id = info->small_id;
-       internal->stack_ptr = stack_ptr;
 
        THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Setting current_object_key to %p", __func__, mono_native_thread_id_get (), internal));
 
@@ -534,11 +697,11 @@ mono_thread_attach_internal (MonoThread *thread, gboolean force_attach, gboolean
 
        if (shutting_down && !force_attach) {
                mono_threads_unlock ();
+               mono_thread_pop_appdomain_ref ();
                return FALSE;
        }
 
        if (!threads) {
-               MONO_GC_REGISTER_ROOT_FIXED (threads, MONO_ROOT_SOURCE_THREADING, "threads table");
                threads = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_THREADING, "threads table");
        }
 
@@ -581,6 +744,7 @@ typedef struct {
        MonoObject *start_delegate_arg;
        MonoThreadStart start_func;
        gpointer start_func_arg;
+       gboolean force_attach;
        gboolean failed;
        MonoCoopSem registered;
 } StartInfo;
@@ -607,7 +771,7 @@ static guint32 WINAPI start_wrapper_internal(StartInfo *start_info, gsize *stack
 
        THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Start wrapper", __func__, mono_native_thread_id_get ()));
 
-       if (!mono_thread_attach_internal (thread, FALSE, FALSE, stack_ptr)) {
+       if (!mono_thread_attach_internal (thread, start_info->force_attach, FALSE)) {
                start_info->failed = TRUE;
 
                mono_coop_sem_post (&start_info->registered);
@@ -720,11 +884,25 @@ static guint32 WINAPI start_wrapper_internal(StartInfo *start_info, gsize *stack
        return(0);
 }
 
-static gsize WINAPI start_wrapper(void *data)
+static gsize WINAPI
+start_wrapper (gpointer data)
 {
-       volatile gsize dummy;
+       StartInfo *start_info;
+       MonoThreadInfo *info;
+       gsize res;
 
-       return start_wrapper_internal ((StartInfo*) data, (gsize*) &dummy);
+       start_info = (StartInfo*) data;
+       g_assert (start_info);
+
+       info = mono_thread_info_attach ();
+       info->runtime_thread = TRUE;
+
+       /* Run the actual main function of the thread */
+       res = start_wrapper_internal (start_info, info->stack_end);
+
+       mono_thread_info_exit (res);
+
+       g_assert_not_reached ();
 }
 
 /*
@@ -735,10 +913,9 @@ static gsize WINAPI start_wrapper(void *data)
  */
 static gboolean
 create_thread (MonoThread *thread, MonoInternalThread *internal, MonoObject *start_delegate, MonoThreadStart start_func, gpointer start_func_arg,
-       gboolean threadpool_thread, guint32 stack_size, MonoError *error)
+       MonoThreadCreateFlags flags, MonoError *error)
 {
        StartInfo *start_info = NULL;
-       MonoThreadHandle *thread_handle;
        MonoNativeThreadId tid;
        gboolean ret;
        gsize stack_set_size;
@@ -748,30 +925,40 @@ create_thread (MonoThread *thread, MonoInternalThread *internal, MonoObject *sta
        if (start_func)
                g_assert (!start_delegate);
 
+       if (flags & MONO_THREAD_CREATE_FLAGS_THREADPOOL) {
+               g_assert (!(flags & MONO_THREAD_CREATE_FLAGS_DEBUGGER));
+               g_assert (!(flags & MONO_THREAD_CREATE_FLAGS_FORCE_CREATE));
+       }
+       if (flags & MONO_THREAD_CREATE_FLAGS_DEBUGGER) {
+               g_assert (!(flags & MONO_THREAD_CREATE_FLAGS_THREADPOOL));
+               g_assert (!(flags & MONO_THREAD_CREATE_FLAGS_FORCE_CREATE));
+       }
+
        /*
         * Join joinable threads to prevent running out of threads since the finalizer
         * thread might be blocked/backlogged.
         */
        mono_threads_join_threads ();
 
-       mono_error_init (error);
+       error_init (error);
 
        mono_threads_lock ();
-       if (shutting_down) {
+       if (shutting_down && !(flags & MONO_THREAD_CREATE_FLAGS_FORCE_CREATE)) {
                mono_threads_unlock ();
                return FALSE;
        }
        if (threads_starting_up == NULL) {
-               MONO_GC_REGISTER_ROOT_FIXED (threads_starting_up, MONO_ROOT_SOURCE_THREADING, "starting threads table");
                threads_starting_up = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_KEY_VALUE_GC, MONO_ROOT_SOURCE_THREADING, "starting threads table");
        }
        mono_g_hash_table_insert (threads_starting_up, thread, thread);
        mono_threads_unlock ();
 
-       internal->threadpool_thread = threadpool_thread;
-       if (threadpool_thread)
+       internal->threadpool_thread = flags & MONO_THREAD_CREATE_FLAGS_THREADPOOL;
+       if (internal->threadpool_thread)
                mono_thread_set_state (internal, ThreadState_Background);
 
+       internal->debugger_thread = flags & MONO_THREAD_CREATE_FLAGS_DEBUGGER;
+
        start_info = g_new0 (StartInfo, 1);
        start_info->ref = 2;
        start_info->thread = thread;
@@ -779,22 +966,21 @@ create_thread (MonoThread *thread, MonoInternalThread *internal, MonoObject *sta
        start_info->start_delegate_arg = thread->start_obj;
        start_info->start_func = start_func;
        start_info->start_func_arg = start_func_arg;
+       start_info->force_attach = flags & MONO_THREAD_CREATE_FLAGS_FORCE_CREATE;
        start_info->failed = FALSE;
        mono_coop_sem_init (&start_info->registered, 0);
 
-       if (stack_size == 0)
+       if (flags != MONO_THREAD_CREATE_FLAGS_SMALL_STACK)
                stack_set_size = default_stacksize_for_thread (internal);
        else
                stack_set_size = 0;
 
-       thread_handle = mono_threads_create_thread (start_wrapper, start_info, &stack_set_size, &tid);
-
-       if (thread_handle == NULL) {
+       if (!mono_thread_platform_create_thread (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);
                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;
@@ -814,8 +1000,6 @@ create_thread (MonoThread *thread, MonoInternalThread *internal, MonoObject *sta
 
        mono_coop_sem_wait (&start_info->registered, MONO_SEM_FLAGS_NONE);
 
-       mono_threads_close_thread_handle (thread_handle);
-
        THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Done launching thread %p (%"G_GSIZE_FORMAT")", __func__, mono_native_thread_id_get (), internal, (gsize)internal->tid));
 
        ret = !start_info->failed;
@@ -829,19 +1013,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;
 }
@@ -852,13 +1048,13 @@ guint32 mono_threads_get_default_stacksize (void)
  *   ARG should not be a GC reference.
  */
 MonoInternalThread*
-mono_thread_create_internal (MonoDomain *domain, gpointer func, gpointer arg, gboolean threadpool_thread, guint32 stack_size, MonoError *error)
+mono_thread_create_internal (MonoDomain *domain, gpointer func, gpointer arg, MonoThreadCreateFlags flags, MonoError *error)
 {
        MonoThread *thread;
        MonoInternalThread *internal;
        gboolean res;
 
-       mono_error_init (error);
+       error_init (error);
 
        internal = create_internal_thread_object ();
 
@@ -866,14 +1062,17 @@ mono_thread_create_internal (MonoDomain *domain, gpointer func, gpointer arg, gb
 
        LOCK_THREAD (internal);
 
-       res = create_thread (thread, internal, NULL, (MonoThreadStart) func, arg, threadpool_thread, stack_size, error);
-       return_val_if_nok (error, NULL);
+       res = create_thread (thread, internal, NULL, (MonoThreadStart) func, arg, flags, error);
 
        UNLOCK_THREAD (internal);
 
+       return_val_if_nok (error, NULL);
        return internal;
 }
 
+/**
+ * mono_thread_create:
+ */
 void
 mono_thread_create (MonoDomain *domain, gpointer func, gpointer arg)
 {
@@ -885,24 +1084,16 @@ mono_thread_create (MonoDomain *domain, gpointer func, gpointer arg)
 gboolean
 mono_thread_create_checked (MonoDomain *domain, gpointer func, gpointer arg, MonoError *error)
 {
-       return (NULL != mono_thread_create_internal (domain, func, arg, FALSE, 0, error));
-}
-
-MonoThread *
-mono_thread_attach (MonoDomain *domain)
-{
-       MonoThread *thread = mono_thread_attach_full (domain, FALSE);
-
-       return thread;
+       return (NULL != mono_thread_create_internal (domain, func, arg, MONO_THREAD_CREATE_FLAGS_NONE, error));
 }
 
-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 ())
@@ -911,9 +1102,8 @@ 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 ());
-       }
+       info = mono_thread_info_attach ();
+       g_assert (info);
 
        tid=mono_native_thread_id_get ();
 
@@ -921,7 +1111,7 @@ mono_thread_attach_full (MonoDomain *domain, gboolean force_attach)
 
        thread = create_thread_object (domain, internal);
 
-       if (!mono_thread_attach_internal (thread, force_attach, TRUE, &stack_ptr)) {
+       if (!mono_thread_attach_internal (thread, force_attach, TRUE)) {
                /* Mono is shutting down, so just wait for the end */
                for (;;)
                        mono_thread_info_sleep (10000, NULL);
@@ -929,17 +1119,8 @@ 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)
@@ -949,12 +1130,22 @@ mono_thread_attach_full (MonoDomain *domain, gboolean force_attach)
        return thread;
 }
 
+/**
+ * mono_thread_attach:
+ */
+MonoThread *
+mono_thread_attach (MonoDomain *domain)
+{
+       return mono_thread_attach_full (domain, FALSE);
+}
+
 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));
 
@@ -970,17 +1161,6 @@ mono_thread_detach_internal (MonoInternalThread *thread)
        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.
@@ -1006,8 +1186,7 @@ mono_thread_detach_internal (MonoInternalThread *thread)
        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) != 0)
-               InterlockedDecrement (&thread_interruption_requested);
+       mono_thread_clear_interruption_requested (thread);
 
        mono_threads_lock ();
 
@@ -1071,8 +1250,6 @@ mono_thread_detach_internal (MonoInternalThread *thread)
        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);
@@ -1086,6 +1263,8 @@ mono_thread_detach_internal (MonoInternalThread *thread)
        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;
@@ -1095,12 +1274,17 @@ 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.
         */
 }
 
+/**
+ * mono_thread_detach:
+ */
 void
 mono_thread_detach (MonoThread *thread)
 {
@@ -1108,12 +1292,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)
@@ -1143,6 +1327,9 @@ mono_thread_internal_current_is_attached (void)
        return TRUE;
 }
 
+/**
+ * mono_thread_exit:
+ */
 void
 mono_thread_exit (void)
 {
@@ -1204,7 +1391,7 @@ ves_icall_System_Threading_Thread_Thread_internal (MonoThread *this_obj,
                return this_obj;
        }
 
-       res = create_thread (this_obj, internal, start, NULL, NULL, FALSE, 0, &error);
+       res = create_thread (this_obj, internal, start, NULL, NULL, MONO_THREAD_CREATE_FLAGS_NONE, &error);
        if (!res) {
                mono_error_cleanup (&error);
                UNLOCK_THREAD (internal);
@@ -1335,10 +1522,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.
  */
@@ -1361,11 +1547,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)
@@ -1388,7 +1573,7 @@ ves_icall_System_Threading_Thread_GetName_internal (MonoInternalThread *this_obj
        MonoError error;
        MonoString* str;
 
-       mono_error_init (&error);
+       error_init (&error);
 
        LOCK_THREAD (this_obj);
        
@@ -1406,13 +1591,15 @@ ves_icall_System_Threading_Thread_GetName_internal (MonoInternalThread *this_obj
 }
 
 void 
-mono_thread_set_name_internal (MonoInternalThread *this_obj, MonoString *name, gboolean permanent, MonoError *error)
+mono_thread_set_name_internal (MonoInternalThread *this_obj, MonoString *name, gboolean permanent, gboolean reset, MonoError *error)
 {
        LOCK_THREAD (this_obj);
 
-       mono_error_init (error);
+       error_init (error);
 
-       if ((this_obj->flags & MONO_THREAD_FLAG_NAME_SET)) {
+       if (reset) {
+               this_obj->flags &= ~MONO_THREAD_FLAG_NAME_SET;
+       } else if (this_obj->flags & MONO_THREAD_FLAG_NAME_SET) {
                UNLOCK_THREAD (this_obj);
                
                mono_error_set_invalid_operation (error, "Thread.Name can only be set once.");
@@ -1423,8 +1610,7 @@ mono_thread_set_name_internal (MonoInternalThread *this_obj, MonoString *name, g
                this_obj->name_len = 0;
        }
        if (name) {
-               this_obj->name = g_new (gunichar2, mono_string_length (name));
-               memcpy (this_obj->name, mono_string_chars (name), mono_string_length (name) * 2);
+               this_obj->name = g_memdup (mono_string_chars (name), mono_string_length (name) * sizeof (gunichar2));
                this_obj->name_len = mono_string_length (name);
 
                if (permanent)
@@ -1449,7 +1635,7 @@ void
 ves_icall_System_Threading_Thread_SetName_internal (MonoInternalThread *this_obj, MonoString *name)
 {
        MonoError error;
-       mono_thread_set_name_internal (this_obj, name, TRUE, &error);
+       mono_thread_set_name_internal (this_obj, name, TRUE, FALSE, &error);
        mono_error_set_pending_exception (&error);
 }
 
@@ -1499,7 +1685,7 @@ byte_array_to_domain (MonoArray *arr, MonoDomain *domain, MonoError *error)
 {
        MonoArray *copy;
 
-       mono_error_init (error);
+       error_init (error);
        if (!arr)
                return NULL;
 
@@ -1529,6 +1715,9 @@ ves_icall_System_Threading_Thread_ByteArrayToCurrentDomain (MonoArray *arr)
        return result;
 }
 
+/**
+ * mono_thread_current:
+ */
 MonoThread *
 mono_thread_current (void)
 {
@@ -1580,7 +1769,7 @@ mono_join_uninterrupted (MonoThreadHandle* thread_to_join, gint32 ms, MonoError
        gint32 diff_ms;
        gint32 wait = ms;
 
-       mono_error_init (error);
+       error_init (error);
 
        start = (ms == -1) ? 0 : mono_msec_ticks ();
        for (;;) {
@@ -1680,28 +1869,40 @@ map_native_wait_result_to_managed (MonoW32HandleWaitRet val, gsize numobjects)
        }
 }
 
-static MonoW32HandleWaitRet
-mono_wait_uninterrupted (MonoInternalThread *thread, guint32 numhandles, gpointer *handles, gboolean waitall, gint32 ms, MonoError *error)
+gint32
+ves_icall_System_Threading_WaitHandle_Wait_internal (gpointer *handles, gint32 numhandles, MonoBoolean waitall, gint32 timeout, MonoError *error)
 {
-       MonoException *exc;
        MonoW32HandleWaitRet ret;
+       MonoInternalThread *thread;
+       MonoException *exc;
        gint64 start;
-       gint32 diff_ms;
-       gint32 wait = ms;
+       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);
 
-       mono_error_init (error);
+       thread = mono_thread_internal_current ();
 
-       start = (ms == -1) ? 0 : mono_100ns_ticks ();
-       do {
+       mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
+
+       if (timeout == -1)
+               timeout = MONO_INFINITE_WAIT;
+       if (timeout != MONO_INFINITE_WAIT)
+               start = mono_msec_ticks ();
+
+       timeoutLeft = timeout;
+
+       for (;;) {
                MONO_ENTER_GC_SAFE;
 #ifdef HOST_WIN32
                if (numhandles != 1)
-                       ret = mono_w32handle_convert_wait_ret (WaitForMultipleObjectsEx (numhandles, handles, waitall, wait, TRUE), numhandles);
+                       ret = mono_w32handle_convert_wait_ret (WaitForMultipleObjectsEx (numhandles, handles, waitall, timeoutLeft, TRUE), numhandles);
                else
-                       ret = mono_w32handle_convert_wait_ret (WaitForSingleObjectEx (handles [0], ms, TRUE), 1);
+                       ret = mono_w32handle_convert_wait_ret (WaitForSingleObjectEx (handles [0], timeoutLeft, TRUE), 1);
 #else
                /* mono_w32handle_wait_multiple optimizes the case for numhandles == 1 */
-               ret = mono_w32handle_wait_multiple (handles, numhandles, waitall, wait, TRUE);
+               ret = mono_w32handle_wait_multiple (handles, numhandles, waitall, timeoutLeft, TRUE);
 #endif /* HOST_WIN32 */
                MONO_EXIT_GC_SAFE;
 
@@ -1714,128 +1915,26 @@ mono_wait_uninterrupted (MonoInternalThread *thread, guint32 numhandles, gpointe
                        break;
                }
 
-               if (ms == -1)
-                       continue;
+               if (timeout != MONO_INFINITE_WAIT) {
+                       gint64 elapsed;
 
-               /* 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;
-       }
-
-       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);
-
-       mono_error_set_pending_exception (&error);
-
-       return map_native_wait_result_to_managed (ret, numhandles);
-}
-
-gint32 ves_icall_System_Threading_WaitHandle_WaitAny_internal(MonoArray *mono_handles, gint32 ms)
-{
-       MonoError error;
-       HANDLE handles [MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS];
-       uintptr_t 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);
-
-       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);
+                       elapsed = mono_msec_ticks () - start;
+                       if (elapsed >= timeout) {
+                               ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
+                               break;
+                       }
 
-       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;
+                       timeoutLeft = timeout - elapsed;
+               }
        }
 
-       mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
-
-       ret = mono_wait_uninterrupted (thread, numhandles, handles, FALSE, ms, &error);
-
        mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
 
-       THREAD_WAIT_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") returning %d", __func__, mono_native_thread_id_get (), ret));
-
-       mono_error_set_pending_exception (&error);
-
        return map_native_wait_result_to_managed (ret, numhandles);
 }
 
-gint32 ves_icall_System_Threading_WaitHandle_WaitOne_internal(HANDLE handle, gint32 ms)
-{
-       MonoError error;
-       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=MONO_INFINITE_WAIT;
-       }
-       
-       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);
-}
-
 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 ();
@@ -2159,10 +2258,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)
@@ -2189,9 +2286,7 @@ request_thread_abort (MonoInternalThread *thread, MonoObject *state)
 {
        LOCK_THREAD (thread);
        
-       if ((thread->state & ThreadState_AbortRequested) != 0 || 
-               (thread->state & ThreadState_StopRequested) != 0 ||
-               (thread->state & ThreadState_Stopped) != 0)
+       if (thread->state & (ThreadState_AbortRequested | ThreadState_Stopped))
        {
                UNLOCK_THREAD (thread);
                return FALSE;
@@ -2242,10 +2337,8 @@ 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)
@@ -2343,17 +2436,13 @@ mono_thread_suspend (MonoInternalThread *thread)
 {
        LOCK_THREAD (thread);
 
-       if ((thread->state & ThreadState_Unstarted) != 0 || 
-               (thread->state & ThreadState_Aborted) != 0 || 
-               (thread->state & ThreadState_Stopped) != 0)
+       if (thread->state & (ThreadState_Unstarted | ThreadState_Aborted | ThreadState_Stopped))
        {
                UNLOCK_THREAD (thread);
                return FALSE;
        }
 
-       if ((thread->state & ThreadState_Suspended) != 0 || 
-               (thread->state & ThreadState_SuspendRequested) != 0 ||
-               (thread->state & ThreadState_StopRequested) != 0) 
+       if (thread->state & (ThreadState_Suspended | ThreadState_SuspendRequested | ThreadState_AbortRequested))
        {
                UNLOCK_THREAD (thread);
                return TRUE;
@@ -2467,53 +2556,17 @@ is_running_protected_wrapper (void)
        return found;
 }
 
-static gboolean
-request_thread_stop (MonoInternalThread *thread)
-{
-       LOCK_THREAD (thread);
-
-       if ((thread->state & ThreadState_StopRequested) != 0 ||
-               (thread->state & ThreadState_Stopped) != 0)
-       {
-               UNLOCK_THREAD (thread);
-               return FALSE;
-       }
-       
-       /* Make sure the thread is awake */
-       mono_thread_resume (thread);
-
-       thread->state |= ThreadState_StopRequested;
-       thread->state &= ~ThreadState_AbortRequested;
-       
-       UNLOCK_THREAD (thread);
-       return TRUE;
-}
-
 /**
- * mono_thread_internal_stop:
- *
- * Request thread @thread to stop.
- *
- * @thread MUST NOT be the current thread.
+ * mono_thread_stop:
  */
 void
-mono_thread_internal_stop (MonoInternalThread *thread)
-{
-       g_assert (thread != mono_thread_internal_current ());
-
-       if (!request_thread_stop (thread))
-               return;
-       
-       async_abort_internal (thread, TRUE);
-}
-
-void mono_thread_stop (MonoThread *thread)
+mono_thread_stop (MonoThread *thread)
 {
        MonoInternalThread *internal = thread->internal_thread;
 
-       if (!request_thread_stop (internal))
+       if (!request_thread_abort (internal, NULL))
                return;
-       
+
        if (internal == mono_thread_internal_current ()) {
                MonoError error;
                self_abort_internal (&error);
@@ -2822,8 +2875,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);
@@ -2855,7 +2909,14 @@ ves_icall_System_Runtime_Remoting_Contexts_Context_RegisterContext (MonoAppConte
 }
 
 void
-ves_icall_System_Runtime_Remoting_Contexts_Context_ReleaseContext (MonoAppContext *ctx)
+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
+mono_threads_release_app_context (MonoAppContext* ctx, MonoError *error)
 {
        /*
         * NOTE: Since finalizers are unreliable for the purposes of ensuring
@@ -2868,6 +2929,13 @@ ves_icall_System_Runtime_Remoting_Contexts_Context_ReleaseContext (MonoAppContex
        mono_profiler_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,
                       MonoThreadAttachCB attach_cb)
 {
@@ -2885,7 +2953,89 @@ 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)
+{
+       return 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)
 {
 #if !defined(RUN_IN_SUBTHREAD) && !defined(HOST_WIN32)
        /* The main thread must abandon any held mutexes (particularly
@@ -2916,6 +3066,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)
 {
@@ -3046,22 +3199,21 @@ remove_and_abort_threads (gpointer key, gpointer value, gpointer user)
        if (wait->num >= MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS)
                return FALSE;
 
-       /* The finalizer thread is not a background thread */
-       if (!mono_native_thread_id_equals (thread_get_tid (thread), self)
-            && (thread->state & ThreadState_Background) != 0
-            && (thread->flags & MONO_THREAD_FLAG_DONT_MANAGE) == 0
-       ) {
+       if (mono_native_thread_id_equals (thread_get_tid (thread), self))
+               return FALSE;
+       if (mono_gc_is_finalizer_internal_thread (thread))
+               return FALSE;
+
+       if ((thread->state & ThreadState_Background) && !(thread->flags & MONO_THREAD_FLAG_DONT_MANAGE)) {
                wait->handles[wait->num] = mono_threads_open_thread_handle (thread->handle);
                wait->threads[wait->num] = thread;
                wait->num++;
 
                THREAD_DEBUG (g_print ("%s: Aborting id: %"G_GSIZE_FORMAT"\n", __func__, (gsize)thread->tid));
                mono_thread_internal_abort (thread);
-               return TRUE;
        }
 
-       return !mono_native_thread_id_equals (thread_get_tid (thread), self)
-               && !mono_gc_is_finalizer_internal_thread (thread);
+       return TRUE;
 }
 
 /** 
@@ -3085,13 +3237,10 @@ mono_threads_set_shutting_down (void)
 
                LOCK_THREAD (current_thread);
 
-               if ((current_thread->state & ThreadState_SuspendRequested) ||
-                   (current_thread->state & ThreadState_AbortRequested) ||
-                   (current_thread->state & ThreadState_StopRequested)) {
+               if (current_thread->state & (ThreadState_SuspendRequested | ThreadState_AbortRequested)) {
                        UNLOCK_THREAD (current_thread);
                        mono_thread_execute_interruption ();
                } else {
-                       current_thread->state |= ThreadState_Stopped;
                        UNLOCK_THREAD (current_thread);
                }
 
@@ -3113,7 +3262,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;
@@ -3274,9 +3427,7 @@ void mono_thread_suspend_all_other_threads (void)
 
                        LOCK_THREAD (thread);
 
-                       if ((thread->state & ThreadState_Suspended) != 0 || 
-                               (thread->state & ThreadState_StopRequested) != 0 ||
-                               (thread->state & ThreadState_Stopped) != 0) {
+                       if (thread->state & (ThreadState_Suspended | ThreadState_Stopped)) {
                                UNLOCK_THREAD (thread);
                                mono_threads_close_thread_handle (wait->handles [i]);
                                wait->threads [i] = NULL;
@@ -3499,7 +3650,7 @@ mono_threads_get_thread_dump (MonoArray **out_threads, MonoArray **out_stack_fra
        MonoDebugSourceLocation *location;
        int tindex, nthreads;
 
-       mono_error_init (error);
+       error_init (error);
        
        *out_threads = NULL;
        *out_stack_frames = NULL;
@@ -3565,7 +3716,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;
                                        }
@@ -3751,10 +3905,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;
@@ -3798,39 +3948,6 @@ mono_threads_abort_appdomain_threads (MonoDomain *domain, int timeout)
        return TRUE;
 }
 
-static void
-clear_cached_culture (gpointer key, gpointer value, gpointer user_data)
-{
-       MonoInternalThread *thread = (MonoInternalThread*)value;
-       MonoDomain *domain = (MonoDomain*)user_data;
-       int i;
-
-       /* No locking needed here */
-       /* FIXME: why no locking? writes to the cache are protected with synch_cs above */
-
-       if (thread->cached_culture_info) {
-               for (i = 0; i < NUM_CACHED_CULTURES * 2; ++i) {
-                       MonoObject *obj = mono_array_get (thread->cached_culture_info, MonoObject*, i);
-                       if (obj && obj->vtable->domain == domain)
-                               mono_array_set (thread->cached_culture_info, MonoObject*, i, NULL);
-               }
-       }
-}
-       
-/*
- * mono_threads_clear_cached_culture:
- *
- *   Clear the cached_current_culture from all threads if it is in the
- * given appdomain.
- */
-void
-mono_threads_clear_cached_culture (MonoDomain *domain)
-{
-       mono_threads_lock ();
-       mono_g_hash_table_foreach (threads, clear_cached_culture, domain);
-       mono_threads_unlock ();
-}
-
 /*
  * mono_thread_get_undeniable_exception:
  *
@@ -4303,16 +4420,17 @@ mono_thread_execute_interruption (void)
        LOCK_THREAD (thread);
 
        /* MonoThread::interruption_requested can only be changed with atomics */
-       if (InterlockedCompareExchange (&thread->interruption_requested, FALSE, TRUE)) {
-               /* 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);
+       WaitForSingleObjectEx (GetCurrentThread(), 0, TRUE);
 #endif
-               InterlockedDecrement (&thread_interruption_requested);
-
-               /* 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) {
@@ -4323,7 +4441,7 @@ mono_thread_execute_interruption (void)
 
                UNLOCK_THREAD (thread);
                return exc;
-       } else if ((thread->state & ThreadState_AbortRequested) != 0) {
+       } else if (thread->state & (ThreadState_AbortRequested)) {
                UNLOCK_THREAD (thread);
                g_assert (sys_thread->pending_exception == NULL);
                if (thread->abort_exc == NULL) {
@@ -4334,19 +4452,10 @@ mono_thread_execute_interruption (void)
                        MONO_OBJECT_SETREF (thread, abort_exc, mono_get_exception_thread_abort ());
                }
                return thread->abort_exc;
-       }
-       else if ((thread->state & ThreadState_SuspendRequested) != 0) {
+       } else if (thread->state & (ThreadState_SuspendRequested)) {
                /* calls UNLOCK_THREAD (thread) */
                self_suspend_internal ();
                return NULL;
-       }
-       else if ((thread->state & ThreadState_StopRequested) != 0) {
-               /* FIXME: do this through the JIT? */
-
-               UNLOCK_THREAD (thread);
-               
-               mono_thread_exit ();
-               return NULL;
        } else if (thread->thread_interrupt_requested) {
 
                thread->thread_interrupt_requested = FALSE;
@@ -4377,15 +4486,8 @@ mono_thread_request_interruption (gboolean running_managed)
        if (thread == NULL) 
                return NULL;
 
-#ifdef HOST_WIN32
-       if (thread->interrupt_on_stop && 
-               thread->state & ThreadState_StopRequested && 
-               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);
 
        if (!running_managed || is_running_protected_wrapper ()) {
                /* Can't stop while in unmanaged code. Increase the global interruption
@@ -4410,7 +4512,7 @@ mono_thread_request_interruption (gboolean running_managed)
 /*This function should be called by a thread after it has exited all of
  * its handle blocks at interruption time.*/
 MonoException*
-mono_thread_resume_interruption (void)
+mono_thread_resume_interruption (gboolean exec)
 {
        MonoInternalThread *thread = mono_thread_internal_current ();
        gboolean still_aborting;
@@ -4420,20 +4522,22 @@ mono_thread_resume_interruption (void)
                return NULL;
 
        LOCK_THREAD (thread);
-       still_aborting = (thread->state & (ThreadState_AbortRequested|ThreadState_StopRequested)) != 0;
+       still_aborting = (thread->state & (ThreadState_AbortRequested)) != 0;
        UNLOCK_THREAD (thread);
 
        /*This can happen if the protected block called Thread::ResetAbort*/
        if (!still_aborting)
-               return FALSE;
+               return NULL;
 
-       if (InterlockedCompareExchange (&thread->interruption_requested, 1, 0) == 1)
+       if (!mono_thread_set_interruption_requested (thread))
                return NULL;
-       InterlockedIncrement (&thread_interruption_requested);
 
        mono_thread_info_self_interrupt ();
 
-       return mono_thread_execute_interruption ();
+       if (exec)
+               return mono_thread_execute_interruption ();
+       else
+               return NULL;
 }
 
 gboolean mono_thread_interruption_requested ()
@@ -4442,7 +4546,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;
 }
@@ -4455,7 +4559,7 @@ mono_thread_interruption_checkpoint_request (gboolean bypass_abort_protection)
        /* The thread may already be stopping */
        if (!thread)
                return NULL;
-       if (!thread->interruption_requested)
+       if (!mono_thread_get_interruption_requested (thread))
                return NULL;
        if (!bypass_abort_protection && is_running_protected_wrapper ())
                return NULL;
@@ -4560,10 +4664,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)
@@ -4682,19 +4784,10 @@ async_abort_critical (MonoThreadInfo *info, gpointer ud)
        if (mono_get_eh_callbacks ()->mono_install_handler_block_guard (mono_thread_info_get_suspend_state (info)))
                return MonoResumeThread;
 
-       /*
-       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)
-               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);
-
        ji = mono_thread_info_get_last_managed (info);
        protected_wrapper = ji && !ji->is_trampoline && !ji->async && mono_threads_is_critical_method (mono_jit_info_get_method (ji));
        running_managed = mono_jit_info_match (ji, MONO_CONTEXT_GET_IP (&mono_thread_info_get_suspend_state (info)->ctx));
@@ -4741,7 +4834,7 @@ self_abort_internal (MonoError *error)
 {
        MonoException *exc;
 
-       mono_error_init (error);
+       error_init (error);
 
        /* FIXME this is insanely broken, it doesn't cause interruption to happen synchronously
         * since passing FALSE to mono_thread_request_interruption makes sure it returns NULL */
@@ -4785,8 +4878,7 @@ async_suspend_critical (MonoThreadInfo *info, gpointer ud)
                        return KeepSuspended;
                }
        } else {
-               if (InterlockedCompareExchange (&thread->interruption_requested, 1, 0) == 0)
-                       InterlockedIncrement (&thread_interruption_requested);
+               mono_thread_set_interruption_requested (thread);
                if (data->interrupt)
                        data->interrupt_token = mono_thread_info_prepare_interrupt ((MonoThreadInfo *)thread->thread_info);
 
@@ -4839,19 +4931,41 @@ self_suspend_internal (void)
        event = thread->suspended;
 
        MONO_ENTER_GC_SAFE;
-       res = mono_os_event_wait_one (event, MONO_INFINITE_WAIT);
+       res = mono_os_event_wait_one (event, MONO_INFINITE_WAIT, TRUE);
        g_assert (res == MONO_OS_EVENT_WAIT_RET_SUCCESS_0 || res == MONO_OS_EVENT_WAIT_RET_ALERTED);
        MONO_EXIT_GC_SAFE;
 }
 
-/*
+static void
+suspend_for_shutdown_async_call (gpointer unused)
+{
+       for (;;)
+               mono_thread_info_yield ();
+}
+
+static SuspendThreadResult
+suspend_for_shutdown_critical (MonoThreadInfo *info, gpointer unused)
+{
+       mono_thread_info_setup_async_call (info, suspend_for_shutdown_async_call, NULL);
+       return MonoResumeThread;
+}
+
+void
+mono_thread_internal_suspend_for_shutdown (MonoInternalThread *thread)
+{
+       g_assert (thread != mono_thread_internal_current ());
+
+       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)
@@ -4922,7 +5036,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 {
@@ -4963,13 +5079,6 @@ mono_thread_join (gpointer tid)
 #endif
 }
 
-void
-mono_thread_internal_check_for_interruption_critical (MonoInternalThread *thread)
-{
-       if ((thread->state & (ThreadState_StopRequested | ThreadState_SuspendRequested)) != 0)
-               mono_thread_interruption_checkpoint ();
-}
-
 void
 mono_thread_internal_unhandled_exception (MonoObject* exc)
 {
@@ -5093,39 +5202,6 @@ 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 ())
-               return NULL;
-
-       return mono_thread_resume_interruption ();
-}
-
 #if 0
 /* Returns TRUE if the current thread is ready to be interrupted. */
 gboolean
@@ -5135,12 +5211,12 @@ mono_threads_is_ready_to_be_interrupted (void)
 
        thread = mono_thread_internal_current ();
        LOCK_THREAD (thread);
-       if (thread->state & (MonoThreadState)(ThreadState_StopRequested | ThreadState_SuspendRequested | ThreadState_AbortRequested)) {
+       if (thread->state & (ThreadState_SuspendRequested | ThreadState_AbortRequested)) {
                UNLOCK_THREAD (thread);
                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;
        }