runtime] Add semicolons after the calls to the coop gc macros to avoid confusing...
[mono.git] / mono / utils / mono-threads.c
index c80a2405aabde5e059f71e8bf7da0c7c77fa25fb..cdf4e3f66e8489ec20461be3d4fe015a3222fe30 100644 (file)
@@ -51,7 +51,6 @@ static __thread guint32 tls_small_id MONO_TLS_FAST;
 static MonoNativeTlsKey small_id_key;
 #endif
 static MonoLinkedListSet thread_list;
-static gboolean disable_new_interrupt = FALSE;
 static gboolean mono_threads_inited = FALSE;
 
 static MonoSemType suspend_semaphore;
@@ -61,70 +60,108 @@ static gboolean unified_suspend_enabled;
 #define mono_thread_info_run_state(info) (((MonoThreadInfo*)info)->thread_state & THREAD_STATE_MASK)
 
 /*warn at 50 ms*/
-#define SLEEP_DURATION_BEFORE_WARNING (50)
+#define SLEEP_DURATION_BEFORE_WARNING (10)
 /*abort at 1 sec*/
-#define SLEEP_DURATION_BEFORE_ABORT 1000
+#define SLEEP_DURATION_BEFORE_ABORT 200
 
-static void
-wait_for_resume (MonoThreadInfo* info)
+static int suspend_posts, resume_posts, abort_posts, waits_done, pending_ops;
+
+void
+mono_threads_notify_initiator_of_abort (MonoThreadInfo* info)
 {
-       MONO_SEM_WAIT_UNITERRUPTIBLE (&info->resume_semaphore);
+       THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-ABORT] %p\n", mono_thread_info_get_tid (info));
+       MONO_SEM_POST (&suspend_semaphore);
+       InterlockedIncrement (&abort_posts);
 }
 
 void
 mono_threads_notify_initiator_of_suspend (MonoThreadInfo* info)
 {
-       THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-SUSPEND] %p\n", info);
+       THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-SUSPEND] %p\n", mono_thread_info_get_tid (info));
        MONO_SEM_POST (&suspend_semaphore);
+       InterlockedIncrement (&suspend_posts);
 }
 
 void
 mono_threads_notify_initiator_of_resume (MonoThreadInfo* info)
 {
-       THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-RESUME] %p\n", info);
+       THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-RESUME] %p\n", mono_thread_info_get_tid (info));
        MONO_SEM_POST (&suspend_semaphore);
+       InterlockedIncrement (&resume_posts);
+}
+
+static void
+resume_async_suspended (MonoThreadInfo *info)
+{
+       g_assert (mono_threads_core_begin_async_resume (info));
 }
 
 static void
 resume_self_suspended (MonoThreadInfo* info)
 {
-       THREADS_SUSPEND_DEBUG ("begin self-resume %p\n", info);
+       THREADS_SUSPEND_DEBUG ("**BEGIN self-resume %p\n", mono_thread_info_get_tid (info));
        MONO_SEM_POST (&info->resume_semaphore);
 }
 
+void
+mono_thread_info_wait_for_resume (MonoThreadInfo* info)
+{
+       THREADS_SUSPEND_DEBUG ("**WAIT self-resume %p\n", mono_thread_info_get_tid (info));
+       MONO_SEM_WAIT_UNITERRUPTIBLE (&info->resume_semaphore);
+}
+
 static void
-resume_async_suspended (MonoThreadInfo *info)
+resume_blocking_suspended (MonoThreadInfo* info)
 {
-       g_assert (mono_threads_core_begin_async_resume (info));
+       THREADS_SUSPEND_DEBUG ("**BEGIN blocking-resume %p\n", mono_thread_info_get_tid (info));
+       MONO_SEM_POST (&info->resume_semaphore);
 }
 
 void
 mono_threads_add_to_pending_operation_set (MonoThreadInfo* info)
 {
-       THREADS_SUSPEND_DEBUG ("added %p to pending suspend\n", info);
+       THREADS_SUSPEND_DEBUG ("added %p to pending suspend\n", mono_thread_info_get_tid (info));
        ++pending_suspends;
+       InterlockedIncrement (&pending_ops);
 }
 
 void
 mono_threads_begin_global_suspend (void)
 {
        g_assert (pending_suspends == 0);
-       THREADS_SUSPEND_DEBUG ("------ BEGIN GLOBAL OP\n");
+       THREADS_SUSPEND_DEBUG ("------ BEGIN GLOBAL OP sp %d rp %d ap %d wd %d po %d\n", suspend_posts, resume_posts,
+               abort_posts, waits_done, pending_ops);
+       mono_threads_core_begin_global_suspend ();
 }
 
 void
 mono_threads_end_global_suspend (void) 
 {
        g_assert (pending_suspends == 0);
-       THREADS_SUSPEND_DEBUG ("------ END GLOBAL OP\n");
+       THREADS_SUSPEND_DEBUG ("------ END GLOBAL OP sp %d rp %d ap %d wd %d po %d\n", suspend_posts, resume_posts,
+               abort_posts, waits_done, pending_ops);
+       mono_threads_core_end_global_suspend ();
 }
 
 static void
 dump_threads (void)
 {
        MonoThreadInfo *info;
+       MonoThreadInfo *cur = mono_thread_info_current ();
+
+       MOSTLY_ASYNC_SAFE_PRINTF ("STATE CUE CARD: (? means a positive number, usually 1 or 2)\n");
+       MOSTLY_ASYNC_SAFE_PRINTF ("\t0x0\t- starting (GOOD, unless the thread is running managed code)\n");
+       MOSTLY_ASYNC_SAFE_PRINTF ("\t0x1\t- running (BAD, unless it's the gc thread)\n");
+       MOSTLY_ASYNC_SAFE_PRINTF ("\t0x2\t- detached (GOOD, unless the thread is running managed code)\n");
+       MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?03\t- async suspended (GOOD)\n");
+       MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?04\t- self suspended (GOOD)\n");
+       MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?05\t- async suspend requested (BAD)\n");
+       MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?06\t- self suspend requested (BAD)\n");
+       MOSTLY_ASYNC_SAFE_PRINTF ("\t0x07\t- blocking (GOOD)\n");
+       MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?08\t- blocking with pending suspend (GOOD)\n");
+
        FOREACH_THREAD_SAFE (info) {
-               THREADS_SUSPEND_DEBUG ("--thread %p id %p state %x\n", info, mono_thread_info_get_tid (info), info->thread_state);
+               MOSTLY_ASYNC_SAFE_PRINTF ("--thread %p id %p [%p] state %x  %s\n", info, (void *) mono_thread_info_get_tid (info), (void*)(size_t)info->native_handle, info->thread_state, info == cur ? "GC INITIATOR" : "" );
        } END_FOREACH_THREAD_SAFE
 }
 
@@ -141,14 +178,14 @@ mono_threads_wait_pending_operations (void)
                THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-COUNT] %d\n", c);
                for (i = 0; i < pending_suspends; ++i) {
                        THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-WAITING]\n");
+                       InterlockedIncrement (&waits_done);
                        if (!MONO_SEM_TIMEDWAIT (&suspend_semaphore, SLEEP_DURATION_BEFORE_ABORT))
                                continue;
                        mono_stopwatch_stop (&suspension_time);
 
                        dump_threads ();
 
-                       THREADS_SUSPEND_DEBUG ("WAITING for %d threads, got %d suspended\n", (int)pending_suspends, i);
-                       THREADS_SUSPEND_DEBUG ("cur thread is %p\n", pthread_self ());
+                       MOSTLY_ASYNC_SAFE_PRINTF ("WAITING for %d threads, got %d suspended\n", (int)pending_suspends, i);
                        g_error ("suspend_thread suspend took %d ms, which is more than the allowed %d ms", (int)mono_stopwatch_elapsed_ms (&suspension_time), SLEEP_DURATION_BEFORE_ABORT);
                }
                mono_stopwatch_stop (&suspension_time);
@@ -191,7 +228,7 @@ mono_thread_info_lookup (MonoNativeThreadId id)
        } 
 
        mono_hazard_pointer_clear_all (hp, 1);
-       return mono_hazard_pointer_get_val (hp, 1);
+       return (MonoThreadInfo *) mono_hazard_pointer_get_val (hp, 1);
 }
 
 static gboolean
@@ -223,7 +260,7 @@ mono_thread_info_remove (MonoThreadInfo *info)
 static void
 free_thread_info (gpointer mem)
 {
-       MonoThreadInfo *info = mem;
+       MonoThreadInfo *info = (MonoThreadInfo *) mem;
 
        MONO_SEM_DESTROY (&info->resume_semaphore);
        mono_threads_platform_free (info);
@@ -294,7 +331,7 @@ register_thread (MonoThreadInfo *info, gpointer baseptr)
 static void
 unregister_thread (void *arg)
 {
-       MonoThreadInfo *info = arg;
+       MonoThreadInfo *info = (MonoThreadInfo *) arg;
        int small_id = info->small_id;
        g_assert (info);
 
@@ -320,7 +357,14 @@ unregister_thread (void *arg)
        if (threads_callbacks.thread_detach)
                threads_callbacks.thread_detach (info);
 
+       /*
+       Since the thread info lock is taken from within blocking sections, we can't check from there, so it must be done here.
+       This ensures that we won't lose any suspend requests as a suspend initiator must hold the lock.
+       Once we're holding the suspend lock, no threads can suspend us and once we unregister, no thread can find us. 
+       */
+       MONO_PREPARE_BLOCKING;
        mono_thread_info_suspend_lock ();
+       MONO_FINISH_BLOCKING;
 
        /*
        Now perform the callback that must be done under locks.
@@ -375,7 +419,7 @@ mono_threads_unregister_current_thread (MonoThreadInfo *info)
        g_assert (result);
 }
 
-static inline MonoThreadInfo*
+MonoThreadInfo*
 mono_thread_info_current_unchecked (void)
 {
        return (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
@@ -448,8 +492,14 @@ mono_threads_attach_tools_thread (void)
 
        /* Must only be called once */
        g_assert (!mono_native_tls_get_value (thread_info_key));
+       
+       while (!mono_threads_inited) { 
+               g_usleep (10);
+       }
 
        info = mono_thread_info_attach (&dummy);
+       g_assert (info);
+
        info->tools_thread = TRUE;
 }
 
@@ -459,14 +509,18 @@ mono_thread_info_attach (void *baseptr)
        MonoThreadInfo *info;
        if (!mono_threads_inited)
        {
+#ifdef HOST_WIN32
                /* This can happen from DllMain(DLL_THREAD_ATTACH) on Windows, if a
                 * thread is created before an embedding API user initialized Mono. */
                THREADS_DEBUG ("mono_thread_info_attach called before mono_threads_init\n");
                return NULL;
+#else
+               g_assert (mono_threads_inited);
+#endif
        }
-       info = mono_native_tls_get_value (thread_info_key);
+       info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
        if (!info) {
-               info = g_malloc0 (thread_info_size);
+               info = (MonoThreadInfo *) g_malloc0 (thread_info_size);
                THREADS_DEBUG ("attaching %p\n", info);
                if (!register_thread (info, baseptr))
                        return NULL;
@@ -487,7 +541,7 @@ mono_thread_info_detach (void)
                THREADS_DEBUG ("mono_thread_info_detach called before mono_threads_init\n");
                return;
        }
-       info = mono_native_tls_get_value (thread_info_key);
+       info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
        if (info) {
                THREADS_DEBUG ("detaching %p\n", info);
                unregister_thread (info);
@@ -521,8 +575,8 @@ mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size)
        res = mono_native_tls_alloc (&thread_info_key, NULL);
        res = mono_native_tls_alloc (&thread_exited_key, NULL);
 #else
-       res = mono_native_tls_alloc (&thread_info_key, unregister_thread);
-       res = mono_native_tls_alloc (&thread_exited_key, thread_exited_dtor);
+       res = mono_native_tls_alloc (&thread_info_key, (void *) unregister_thread);
+       res = mono_native_tls_alloc (&thread_exited_key, (void *) thread_exited_dtor);
 #endif
        g_assert (res);
 
@@ -531,7 +585,7 @@ mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size)
 #endif
        g_assert (res);
 
-       unified_suspend_enabled = g_getenv ("MONO_ENABLE_UNIFIED_SUSPEND") != NULL;
+       unified_suspend_enabled = g_getenv ("MONO_ENABLE_UNIFIED_SUSPEND") != NULL || MONO_THREADS_PLATFORM_REQUIRES_UNIFIED_SUSPEND;
 
        MONO_SEM_INIT (&global_suspend_semaphore, 1);
        MONO_SEM_INIT (&suspend_semaphore, 0);
@@ -555,12 +609,6 @@ mono_threads_runtime_init (MonoThreadInfoRuntimeCallbacks *callbacks)
        runtime_callbacks = *callbacks;
 }
 
-MonoThreadInfoCallbacks *
-mono_threads_get_callbacks (void)
-{
-       return &threads_callbacks;
-}
-
 MonoThreadInfoRuntimeCallbacks *
 mono_threads_get_runtime_callbacks (void)
 {
@@ -640,11 +688,11 @@ mono_thread_info_end_self_suspend (void)
        case SelfSuspendResumed:
                return;
        case SelfSuspendWait:
-               wait_for_resume (info);
+               mono_thread_info_wait_for_resume (info);
                break;
        case SelfSuspendNotifyAndWait:
                mono_threads_notify_initiator_of_suspend (info);
-               wait_for_resume (info);
+               mono_thread_info_wait_for_resume (info);
                mono_threads_notify_initiator_of_resume (info);
                break;
        }
@@ -677,6 +725,10 @@ mono_thread_info_core_resume (MonoThreadInfo *info)
                resume_async_suspended (info);
                res = TRUE;
                break;
+       case ResumeInitBlockingResume:
+               resume_blocking_suspended (info);
+               res = TRUE;
+               break;
        }
 
        return res;
@@ -748,10 +800,6 @@ is_thread_in_critical_region (MonoThreadInfo *info)
        if (info->inside_critical_region)
                return TRUE;
 
-       if (threads_callbacks.mono_thread_in_critical_region && threads_callbacks.mono_thread_in_critical_region (info)) {
-               return TRUE;
-       }
-
        /* Are we inside a GC critical region? */
        if (threads_callbacks.mono_thread_in_critical_region && threads_callbacks.mono_thread_in_critical_region (info)) {
                return TRUE;
@@ -768,8 +816,8 @@ is_thread_in_critical_region (MonoThreadInfo *info)
                return TRUE;
 
        ji = mono_jit_info_table_find (
-               state->unwind_data [MONO_UNWIND_DATA_DOMAIN],
-               MONO_CONTEXT_GET_IP (&state->ctx));
+               (MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN],
+               (char *) MONO_CONTEXT_GET_IP (&state->ctx));
 
        if (!ji)
                return FALSE;
@@ -844,7 +892,7 @@ mono_thread_info_safe_suspend_and_run (MonoNativeThreadId id, gboolean interrupt
                goto done;
 
        switch (result = callback (info, user_data)) {
-       case ResumeThread:
+       case MonoResumeThread:
                mono_hazard_pointer_set (hp, 1, info);
                mono_thread_info_core_resume (info);
                mono_threads_wait_pending_operations ();
@@ -929,12 +977,6 @@ mono_thread_info_suspend_unlock (void)
        MONO_SEM_POST (&global_suspend_semaphore);
 }
 
-void
-mono_thread_info_disable_new_interrupt (gboolean disable)
-{
-       disable_new_interrupt = disable;
-}
-
 /*
  * This is a very specific function whose only purpose is to
  * break a given thread from socket syscalls.
@@ -965,10 +1007,14 @@ mono_thread_info_abort_socket_syscall_for_close (MonoNativeThreadId tid)
        }
 
        mono_thread_info_suspend_lock ();
+       mono_threads_begin_global_suspend ();
 
        mono_threads_core_abort_syscall (info);
+       mono_threads_wait_pending_operations ();
 
        mono_hazard_pointer_clear (hp, 1);
+
+       mono_threads_end_global_suspend ();
        mono_thread_info_suspend_unlock ();
 }
 
@@ -978,30 +1024,6 @@ mono_thread_info_unified_management_enabled (void)
        return unified_suspend_enabled;
 }
 
-/*
-Disabled by default for now.
-To enable this we need mini to implement the callbacks by MonoThreadInfoRuntimeCallbacks
-which means mono-context and setup_async_callback, and we need a mono-threads backend.
-*/
-gboolean
-mono_thread_info_new_interrupt_enabled (void)
-{
-       /*We need STW gc events to work correctly*/
-#if defined (HAVE_BOEHM_GC) && !defined (USE_INCLUDED_LIBGC)
-       return FALSE;
-#endif
-#if defined(HOST_WIN32)
-       return !disable_new_interrupt;
-#endif
-#if defined (__i386__) || defined(__x86_64__)
-       return !disable_new_interrupt;
-#endif
-#if defined(__arm__) || defined(__aarch64__)
-       return !disable_new_interrupt;
-#endif
-       return FALSE;
-}
-
 /*
  * mono_thread_info_set_is_async_context:
  *
@@ -1131,42 +1153,204 @@ mono_thread_info_set_name (MonoNativeThreadId tid, const char *name)
        mono_threads_core_set_name (tid, name);
 }
 
+#define INTERRUPT_STATE ((MonoThreadInfoInterruptToken*) (size_t) -1)
+
+struct _MonoThreadInfoInterruptToken {
+       void (*callback) (gpointer data);
+       gpointer data;
+};
+
 /*
- * mono_thread_info_prepare_interrupt:
+ * mono_thread_info_install_interrupt: install an interruption token for the current thread.
  *
- *   See wapi_prepare_interrupt ().
+ *  - @callback: must be able to be called from another thread and always cancel the wait
+ *  - @data: passed to the callback
+ *  - @interrupted: will be set to TRUE if a token is already installed, FALSE otherwise
+ *     if set to TRUE, it must mean that the thread is in interrupted state
  */
-gpointer
-mono_thread_info_prepare_interrupt (HANDLE thread_handle)
+void
+mono_thread_info_install_interrupt (void (*callback) (gpointer data), gpointer data, gboolean *interrupted)
 {
-       return mono_threads_core_prepare_interrupt (thread_handle);
+       MonoThreadInfo *info;
+       MonoThreadInfoInterruptToken *previous_token, *token;
+
+       g_assert (callback);
+
+       g_assert (interrupted);
+       *interrupted = FALSE;
+
+       info = mono_thread_info_current ();
+       g_assert (info);
+
+       /* The memory of this token can be freed at 2 places:
+        *  - if the token is not interrupted: it will be freed in uninstall, as info->interrupt_token has not been replaced
+        *     by the INTERRUPT_STATE flag value, and it still contains the pointer to the memory location
+        *  - if the token is interrupted: it will be freed in finish, as the token is now owned by the prepare/finish
+        *     functions, and info->interrupt_token does not contains a pointer to the memory anymore */
+       token = g_new0 (MonoThreadInfoInterruptToken, 1);
+       token->callback = callback;
+       token->data = data;
+
+       previous_token = InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, token, NULL);
+
+       if (previous_token) {
+               if (previous_token != INTERRUPT_STATE)
+                       g_error ("mono_thread_info_install_interrupt: previous_token should be INTERRUPT_STATE (%p), but it was %p", INTERRUPT_STATE, previous_token);
+
+               g_free (token);
+
+               *interrupted = TRUE;
+       }
+
+       THREADS_INTERRUPT_DEBUG ("interrupt install    tid %p token %p previous_token %p interrupted %s\n",
+               mono_thread_info_get_tid (info), token, previous_token, *interrupted ? "TRUE" : "FALSE");
 }
 
 void
-mono_thread_info_finish_interrupt (gpointer wait_handle)
+mono_thread_info_uninstall_interrupt (gboolean *interrupted)
+{
+       MonoThreadInfo *info;
+       MonoThreadInfoInterruptToken *previous_token;
+
+       g_assert (interrupted);
+       *interrupted = FALSE;
+
+       info = mono_thread_info_current ();
+       g_assert (info);
+
+       previous_token = InterlockedExchangePointer ((gpointer*) &info->interrupt_token, NULL);
+
+       /* only the installer can uninstall the token */
+       g_assert (previous_token);
+
+       if (previous_token == INTERRUPT_STATE) {
+               /* if it is interrupted, then it is going to be freed in finish interrupt */
+               *interrupted = TRUE;
+       } else {
+               g_free (previous_token);
+       }
+
+       THREADS_INTERRUPT_DEBUG ("interrupt uninstall  tid %p previous_token %p interrupted %s\n",
+               mono_thread_info_get_tid (info), previous_token, *interrupted ? "TRUE" : "FALSE");
+}
+
+static MonoThreadInfoInterruptToken*
+set_interrupt_state (MonoThreadInfo *info)
+{
+       MonoThreadInfoInterruptToken *token, *previous_token;
+
+       g_assert (info);
+
+       /* Atomically obtain the token the thread is
+       * waiting on, and change it to a flag value. */
+
+       do {
+               previous_token = info->interrupt_token;
+
+               /* Already interrupted */
+               if (previous_token == INTERRUPT_STATE) {
+                       token = NULL;
+                       break;
+               }
+
+               token = previous_token;
+       } while (InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, INTERRUPT_STATE, previous_token) != previous_token);
+
+       return token;
+}
+
+/*
+ * mono_thread_info_prepare_interrupt:
+ *
+ * The state of the thread info interrupt token is set to 'interrupted' which means that :
+ *  - if the thread calls one of the WaitFor functions, the function will return with
+ *     WAIT_IO_COMPLETION instead of waiting
+ *  - if the thread was waiting when this function was called, the wait will be broken
+ *
+ * It is possible that the wait functions return WAIT_IO_COMPLETION, but the target thread
+ * didn't receive the interrupt signal yet, in this case it should call the wait function
+ * again. This essentially means that the target thread will busy wait until it is ready to
+ * process the interruption.
+ */
+MonoThreadInfoInterruptToken*
+mono_thread_info_prepare_interrupt (MonoThreadInfo *info)
 {
-       mono_threads_core_finish_interrupt (wait_handle);
+       MonoThreadInfoInterruptToken *token;
+
+       token = set_interrupt_state (info);
+
+       THREADS_INTERRUPT_DEBUG ("interrupt prepare    tid %p token %p\n",
+               mono_thread_info_get_tid (info), token);
+
+       return token;
 }
 
 void
-mono_thread_info_interrupt (HANDLE thread_handle)
+mono_thread_info_finish_interrupt (MonoThreadInfoInterruptToken *token)
 {
-       gpointer wait_handle;
+       THREADS_INTERRUPT_DEBUG ("interrupt finish     token %p\n", token);
+
+       if (token == NULL)
+               return;
+
+       g_assert (token->callback);
+
+       token->callback (token->data);
 
-       wait_handle = mono_thread_info_prepare_interrupt (thread_handle);
-       mono_thread_info_finish_interrupt (wait_handle);
+       g_free (token);
 }
-       
+
 void
 mono_thread_info_self_interrupt (void)
 {
-       mono_threads_core_self_interrupt ();
+       MonoThreadInfo *info;
+       MonoThreadInfoInterruptToken *token;
+
+       info = mono_thread_info_current ();
+       g_assert (info);
+
+       token = set_interrupt_state (info);
+       g_assert (!token);
+
+       THREADS_INTERRUPT_DEBUG ("interrupt self       tid %p\n",
+               mono_thread_info_get_tid (info));
 }
 
+/* Clear the interrupted flag of the current thread, set with
+ * mono_thread_info_self_interrupt, so it can wait again */
 void
-mono_thread_info_clear_interruption (void)
+mono_thread_info_clear_self_interrupt ()
 {
-       mono_threads_core_clear_interruption ();
+       MonoThreadInfo *info;
+       MonoThreadInfoInterruptToken *previous_token;
+
+       info = mono_thread_info_current ();
+       g_assert (info);
+
+       previous_token = InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, NULL, INTERRUPT_STATE);
+       g_assert (previous_token == NULL || previous_token == INTERRUPT_STATE);
+
+       THREADS_INTERRUPT_DEBUG ("interrupt clear self tid %p previous_token %p\n", mono_thread_info_get_tid (info), previous_token);
+}
+
+gboolean
+mono_thread_info_is_interrupt_state (MonoThreadInfo *info)
+{
+       g_assert (info);
+       return InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE;
+}
+
+void
+mono_thread_info_describe_interrupt_token (MonoThreadInfo *info, GString *text)
+{
+       g_assert (info);
+
+       if (!InterlockedReadPointer ((gpointer*) &info->interrupt_token))
+               g_string_append_printf (text, "not waiting");
+       else if (InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE)
+               g_string_append_printf (text, "interrupted state");
+       else
+               g_string_append_printf (text, "waiting");
 }
 
 /* info must be self or be held in a hazard pointer. */
@@ -1175,7 +1359,7 @@ mono_threads_add_async_job (MonoThreadInfo *info, MonoAsyncJob job)
 {
        MonoAsyncJob old_job;
        do {
-               old_job = info->service_requests;
+               old_job = (MonoAsyncJob) info->service_requests;
                if (old_job & job)
                        return FALSE;
        } while (InterlockedCompareExchange (&info->service_requests, old_job | job, old_job) != old_job);
@@ -1188,7 +1372,7 @@ mono_threads_consume_async_jobs (void)
        MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
 
        if (!info)
-               return 0;
+               return (MonoAsyncJob) 0;
 
-       return InterlockedExchange (&info->service_requests, 0);
+       return (MonoAsyncJob) InterlockedExchange (&info->service_requests, 0);
 }