Merge pull request #1840 from ludovic-henry/iolayer-thread-interrupt
authorRodrigo Kumpera <kumpera@gmail.com>
Thu, 30 Jul 2015 14:48:44 +0000 (10:48 -0400)
committerRodrigo Kumpera <kumpera@gmail.com>
Thu, 30 Jul 2015 14:48:44 +0000 (10:48 -0400)
[runtime] Implement wait interrupt in thread info, instead of in the io-layer

13 files changed:
mcs/class/corlib/Test/System.Threading/ThreadTest.cs
mono/io-layer/handles-private.h
mono/io-layer/handles.c
mono/io-layer/thread-private.h
mono/io-layer/threads.h
mono/io-layer/wait.c
mono/io-layer/wthreads.c
mono/metadata/threads.c
mono/utils/mono-threads-mach.c
mono/utils/mono-threads-posix.c
mono/utils/mono-threads-windows.c
mono/utils/mono-threads.c
mono/utils/mono-threads.h

index 9bfc1c131eaa41121183797895077b5a4f308e8f..53921d2e13f30f80360c47ddd904bd3b26d20f8b 100644 (file)
@@ -800,15 +800,17 @@ namespace MonoTests.System.Threading
                [Test]
                public void Test_Interrupt ()
                {
+                       ManualResetEvent mre = new ManualResetEvent (false);
                        bool interruptedExceptionThrown = false;
+
                        ThreadPool.QueueUserWorkItem (Test_Interrupt_Worker, Thread.CurrentThread);
 
                        try {
                                try {
-                                       Thread.Sleep (3000);
+                                       mre.WaitOne (3000);
                                } finally {
                                        try {
-                                               Thread.Sleep (0);
+                                               mre.WaitOne (0);
                                        } catch (ThreadInterruptedException) {
                                                Assert.Fail ("ThreadInterruptedException thrown twice");
                                        }
@@ -838,11 +840,12 @@ namespace MonoTests.System.Threading
                [Category ("NotDotNet")] // it crashes nunit.
                public void Test_InterruptCurrentThread ()
                {
+                       ManualResetEvent mre = new ManualResetEvent (false);
                        bool interruptedExceptionThrown = false;
 
                        Thread.CurrentThread.Interrupt ();
                        try {
-                               Thread.Sleep (0);
+                               mre.WaitOne (0);
                                Assert.Fail ();
                        } catch (ThreadInterruptedException) {
                        }
index c938f9e4bbb9de64fcd9db711cf891aa6fdd6e68..c5d4fbeb70e94f400e7824cc1db1373a6f465094 100644 (file)
@@ -79,11 +79,8 @@ extern gboolean _wapi_handle_count_signalled_handles (guint32 numhandles,
                                                      guint32 *lowest);
 extern void _wapi_handle_unlock_handles (guint32 numhandles,
                                         gpointer *handles);
-extern int _wapi_handle_wait_signal (gboolean poll);
-extern int _wapi_handle_timedwait_signal (struct timespec *timeout, gboolean poll);
-extern int _wapi_handle_wait_signal_handle (gpointer handle, gboolean alertable);
-extern int _wapi_handle_timedwait_signal_handle (gpointer handle,
-                                                                                                struct timespec *timeout, gboolean alertable, gboolean poll);
+extern int _wapi_handle_timedwait_signal (struct timespec *timeout, gboolean poll, gboolean *alerted);
+extern int _wapi_handle_timedwait_signal_handle (gpointer handle, struct timespec *timeout, gboolean alertable, gboolean poll, gboolean *alerted);
 extern gboolean _wapi_handle_get_or_set_share (guint64 device, guint64 inode,
                                               guint32 new_sharemode,
                                               guint32 new_access,
index 3ffe49b725d17b4c76c39b7c5bd8f0c765eb1f7a..63d225aab6cfa63cf1e7ad9f665a894e4b54bfb3 100644 (file)
@@ -45,6 +45,7 @@
 
 #include <mono/utils/mono-mutex.h>
 #include <mono/utils/mono-proclib.h>
+#include <mono/utils/mono-threads.h>
 #undef DEBUG_REFS
 
 #if 0
@@ -1537,29 +1538,50 @@ static int timedwait_signal_poll_cond (pthread_cond_t *cond, mono_mutex_t *mutex
        return(ret);
 }
 
-int _wapi_handle_wait_signal (gboolean poll)
+int
+_wapi_handle_timedwait_signal (struct timespec *timeout, gboolean poll, gboolean *alerted)
 {
-       return _wapi_handle_timedwait_signal_handle (_wapi_global_signal_handle, NULL, TRUE, poll);
+       return _wapi_handle_timedwait_signal_handle (_wapi_global_signal_handle, timeout, TRUE, poll, alerted);
 }
 
-int _wapi_handle_timedwait_signal (struct timespec *timeout, gboolean poll)
+static void
+signal_handle_and_unref (gpointer handle)
 {
-       return _wapi_handle_timedwait_signal_handle (_wapi_global_signal_handle, timeout, TRUE, poll);
-}
+       pthread_cond_t *cond;
+       mono_mutex_t *mutex;
+       guint32 idx;
 
-int _wapi_handle_wait_signal_handle (gpointer handle, gboolean alertable)
-{
-       DEBUG ("%s: waiting for %p", __func__, handle);
-       
-       return _wapi_handle_timedwait_signal_handle (handle, NULL, alertable, FALSE);
+       g_assert (handle);
+
+       /* If we reach here, then interrupt token is set to the flag value, which
+        * means that the target thread is either
+        * - before the first CAS in timedwait, which means it won't enter the wait.
+        * - it is after the first CAS, so it is already waiting, or it will enter
+        *    the wait, and it will be interrupted by the broadcast. */
+       idx = GPOINTER_TO_UINT (handle);
+       cond = &_WAPI_PRIVATE_HANDLES (idx).signal_cond;
+       mutex = &_WAPI_PRIVATE_HANDLES (idx).signal_mutex;
+
+       mono_mutex_lock (mutex);
+       mono_cond_broadcast (cond);
+       mono_mutex_unlock (mutex);
+
+       _wapi_handle_unref (handle);
 }
 
-int _wapi_handle_timedwait_signal_handle (gpointer handle,
-                                                                                 struct timespec *timeout, gboolean alertable, gboolean poll)
+int
+_wapi_handle_timedwait_signal_handle (gpointer handle, struct timespec *timeout,
+               gboolean alertable, gboolean poll, gboolean *alerted)
 {
        DEBUG ("%s: waiting for %p (type %s)", __func__, handle,
                   _wapi_handle_typename[_wapi_handle_type (handle)]);
-       
+
+       if (alertable)
+               g_assert (alerted);
+
+       if (alerted)
+               *alerted = FALSE;
+
        if (_WAPI_SHARED_HANDLE (_wapi_handle_type (handle))) {
                if (WAPI_SHARED_HANDLE_DATA(handle).signalled == TRUE) {
                        return (0);
@@ -1593,8 +1615,12 @@ int _wapi_handle_timedwait_signal_handle (gpointer handle,
                pthread_cond_t *cond;
                mono_mutex_t *mutex;
 
-               if (alertable && !wapi_thread_set_wait_handle (handle))
-                       return 0;
+               if (alertable) {
+                       mono_thread_info_install_interrupt (signal_handle_and_unref, handle, alerted);
+                       if (*alerted)
+                               return 0;
+                       _wapi_handle_ref (handle);
+               }
 
                cond = &_WAPI_PRIVATE_HANDLES (idx).signal_cond;
                mutex = &_WAPI_PRIVATE_HANDLES (idx).signal_mutex;
@@ -1609,8 +1635,13 @@ int _wapi_handle_timedwait_signal_handle (gpointer handle,
                                res = mono_cond_wait (cond, mutex);
                }
 
-               if (alertable)
-                       wapi_thread_clear_wait_handle (handle);
+               if (alertable) {
+                       mono_thread_info_uninstall_interrupt (alerted);
+                       if (!*alerted) {
+                               /* if it is alerted, then the handle is unref in the interrupt callback */
+                               _wapi_handle_unref (handle);
+                       }
+               }
 
                return res;
        }
index e9f8f9c8813af499e66b6191b97b1c37e6c400c8..3ea1af1197884f7efaa31af5c52d7e52b970c7d5 100644 (file)
 
 extern struct _WapiHandleOps _wapi_thread_ops;
 
-#define INTERRUPTION_REQUESTED_HANDLE (gpointer)0xFFFFFFFE
-
 struct _WapiHandle_thread
 {
        pthread_t id;
        GPtrArray *owned_mutexes;
-       /* 
-     * Handle this thread waits on. If this is INTERRUPTION_REQUESTED_HANDLE,
-        * it means the thread is interrupted by another thread, and shouldn't enter
-        * a wait.
-        * This also acts as a reference for the handle.
-        */
-       gpointer wait_handle;
 };
 
 typedef struct _WapiHandle_thread WapiHandle_thread;
 
-extern gboolean _wapi_thread_apc_pending (gpointer handle);
 extern gboolean _wapi_thread_cur_apc_pending (void);
 extern void _wapi_thread_own_mutex (gpointer mutex);
 extern void _wapi_thread_disown_mutex (gpointer mutex);
index 13b6bd1401f0b9192d258690f512272d7176e71e..a756a40fb64a7f42dd96e4b70bcda5252afd34eb 100644 (file)
@@ -30,14 +30,6 @@ extern gsize GetCurrentThreadId(void); /* NB return is 32bit in MS API */
 extern void Sleep(guint32 ms);
 extern guint32 SleepEx(guint32 ms, gboolean alertable);
 
-void wapi_clear_interruption (void);
-gboolean wapi_thread_set_wait_handle (gpointer handle);
-void wapi_thread_clear_wait_handle (gpointer handle);
-void wapi_self_interrupt (void);
-
-gpointer wapi_prepare_interrupt_thread (gpointer thread_handle);
-void wapi_finish_interrupt_thread (gpointer wait_handle);
-
 gpointer wapi_create_thread_handle (void);
 void wapi_thread_handle_set_exited (gpointer handle, guint32 exitstatus);
 void wapi_ref_thread_handle (gpointer handle);
index 8bdf39bdbf8ac54d05257c7365b73feb8b902b94..42fc96fe4fb6ad14bdeb451437b48b1bc0b0ac53 100644 (file)
@@ -130,12 +130,10 @@ guint32 WaitForSingleObjectEx(gpointer handle, guint32 timeout,
 
                ret = _wapi_handle_ops_special_wait (handle, timeout, alertable);
        
-               if (alertable && _wapi_thread_apc_pending (current_thread)) {
-                       apc_pending = TRUE;
+               if (alertable && _wapi_thread_cur_apc_pending ())
                        ret = WAIT_IO_COMPLETION;
-               }
 
-               goto check_pending;
+               return ret;
        }
        
        
@@ -153,13 +151,7 @@ guint32 WaitForSingleObjectEx(gpointer handle, guint32 timeout,
                        goto done;
                }
        }
-       
-       if (alertable && _wapi_thread_apc_pending (current_thread)) {
-               apc_pending = TRUE;
-               ret = WAIT_IO_COMPLETION;
-               goto done;
-       }
-       
+
        if (own_if_signalled (handle) == TRUE) {
                DEBUG ("%s: handle %p already signalled", __func__,
                           handle);
@@ -189,15 +181,8 @@ guint32 WaitForSingleObjectEx(gpointer handle, guint32 timeout,
                        ret = WAIT_OBJECT_0;
                        goto done;
                }
-                       
-               if (timeout == INFINITE) {
-                       waited = _wapi_handle_wait_signal_handle (handle, alertable);
-               } else {
-                       waited = _wapi_handle_timedwait_signal_handle (handle, &abstime, alertable, FALSE);
-               }
-       
-               if (alertable)
-                       apc_pending = _wapi_thread_apc_pending (current_thread);
+
+               waited = _wapi_handle_timedwait_signal_handle (handle, timeout == INFINITE ? NULL : &abstime, alertable, FALSE, &apc_pending);
 
                if(waited==0 && !apc_pending) {
                        /* Condition was signalled, so hopefully
@@ -220,19 +205,15 @@ guint32 WaitForSingleObjectEx(gpointer handle, guint32 timeout,
        DEBUG ("%s: wait on handle %p error: %s", __func__, handle,
                   strerror (waited));
 
-       ret = WAIT_TIMEOUT;
-       
+       ret = apc_pending ? WAIT_IO_COMPLETION : WAIT_TIMEOUT;
+
 done:
 
        DEBUG ("%s: unlocking handle %p", __func__, handle);
        
        thr_ret = _wapi_handle_unlock_handle (handle);
        g_assert (thr_ret == 0);
-       
-check_pending:
-       if (apc_pending)
-               ret = WAIT_IO_COMPLETION;
-               
+
        return(ret);
 }
 
@@ -351,13 +332,7 @@ guint32 SignalObjectAndWait(gpointer signal_handle, gpointer wait,
                        goto done;
                }
        }
-       
-       if (alertable && _wapi_thread_apc_pending (current_thread)) {
-               apc_pending = TRUE;
-               ret = WAIT_IO_COMPLETION;
-               goto done;
-       }
-       
+
        if (own_if_signalled (wait)) {
                DEBUG ("%s: handle %p already signalled", __func__, wait);
 
@@ -381,16 +356,8 @@ guint32 SignalObjectAndWait(gpointer signal_handle, gpointer wait,
                        ret = WAIT_OBJECT_0;
                        goto done;
                }
-               
-               if (timeout == INFINITE) {
-                       waited = _wapi_handle_wait_signal_handle (wait, alertable);
-               } else {
-                       waited = _wapi_handle_timedwait_signal_handle (wait, &abstime, alertable, FALSE);
-               }
 
-               if (alertable) {
-                       apc_pending = _wapi_thread_apc_pending (current_thread);
-               }
+               waited = _wapi_handle_timedwait_signal_handle (wait, timeout == INFINITE ? NULL : &abstime, alertable, FALSE, &apc_pending);
 
                if (waited==0 && !apc_pending) {
                        /* Condition was signalled, so hopefully
@@ -413,8 +380,8 @@ guint32 SignalObjectAndWait(gpointer signal_handle, gpointer wait,
        DEBUG ("%s: wait on handle %p error: %s", __func__, wait,
                   strerror (ret));
 
-       ret = WAIT_TIMEOUT;
-       
+       ret = apc_pending ? WAIT_IO_COMPLETION : WAIT_TIMEOUT;
+
 done:
 
        DEBUG ("%s: unlocking handle %p", __func__, wait);
@@ -422,9 +389,6 @@ done:
        thr_ret = _wapi_handle_unlock_handle (wait);
        g_assert (thr_ret == 0);
 
-       if (apc_pending)
-               ret = WAIT_IO_COMPLETION;
-       
        return(ret);
 }
 
@@ -500,6 +464,7 @@ guint32 WaitForMultipleObjectsEx(guint32 numobjects, gpointer *handles,
        guint32 retval;
        gboolean poll;
        gpointer sorted_handles [MAXIMUM_WAIT_OBJECTS];
+       gboolean apc_pending = FALSE;
        
        if (current_thread == NULL) {
                SetLastError (ERROR_INVALID_HANDLE);
@@ -590,9 +555,6 @@ guint32 WaitForMultipleObjectsEx(guint32 numobjects, gpointer *handles,
                _wapi_calc_timeout (&abstime, timeout);
        }
 
-       if (alertable && _wapi_thread_apc_pending (current_thread))
-               return WAIT_IO_COMPLETION;
-       
        for (i = 0; i < numobjects; i++) {
                /* Add a reference, as we need to ensure the handle wont
                 * disappear from under us while we're waiting in the loop
@@ -633,11 +595,7 @@ guint32 WaitForMultipleObjectsEx(guint32 numobjects, gpointer *handles,
                
                if (!done) {
                        /* Enter the wait */
-                       if (timeout == INFINITE) {
-                               ret = _wapi_handle_wait_signal (poll);
-                       } else {
-                               ret = _wapi_handle_timedwait_signal (&abstime, poll);
-                       }
+                       ret = _wapi_handle_timedwait_signal (timeout == INFINITE ? NULL : &abstime, poll, &apc_pending);
                } else {
                        /* No need to wait */
                        ret = 0;
@@ -648,7 +606,7 @@ guint32 WaitForMultipleObjectsEx(guint32 numobjects, gpointer *handles,
                thr_ret = _wapi_handle_unlock_signal_mutex (NULL);
                g_assert (thr_ret == 0);
                
-               if (alertable && _wapi_thread_apc_pending (current_thread)) {
+               if (alertable && apc_pending) {
                        retval = WAIT_IO_COMPLETION;
                        break;
                }
index 90a8cc4ffa855b6690c0895b78f9778b096f8b1b..910cb9af35920e3edf5933eb1939a13ea0bc4a09 100644 (file)
@@ -243,7 +243,7 @@ SleepEx (guint32 ms, gboolean alertable)
        if (alertable) {
                current_thread = get_current_thread_handle ();
                
-               if (_wapi_thread_apc_pending (current_thread))
+               if (_wapi_thread_cur_apc_pending ())
                        return WAIT_IO_COMPLETION;
        }
        
@@ -271,7 +271,7 @@ SleepEx (guint32 ms, gboolean alertable)
        while (TRUE) {
                ret = clock_nanosleep (CLOCK_MONOTONIC, TIMER_ABSTIME, &target, NULL);
 
-               if (alertable && _wapi_thread_apc_pending (current_thread))
+               if (alertable && _wapi_thread_cur_apc_pending ())
                        return WAIT_IO_COMPLETION;
 
                if (ret == 0)
@@ -288,7 +288,7 @@ again:
        memset (&rem, 0, sizeof (rem));
        ret=nanosleep(&req, &rem);
 
-       if (alertable && _wapi_thread_apc_pending (current_thread))
+       if (alertable && _wapi_thread_cur_apc_pending ())
                return WAIT_IO_COMPLETION;
        
        if(ret==-1) {
@@ -316,182 +316,7 @@ Sleep(guint32 ms)
 gboolean
 _wapi_thread_cur_apc_pending (void)
 {
-       return _wapi_thread_apc_pending (get_current_thread_handle ());
-}
-
-gboolean
-_wapi_thread_apc_pending (gpointer handle)
-{
-       WapiHandle_thread *thread;
-
-       thread = lookup_thread (handle);
-       
-       return thread->wait_handle == INTERRUPTION_REQUESTED_HANDLE;
-}
-
-/*
- * wapi_interrupt_thread:
- *
- * The state of the thread handle HANDLE 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. Also, 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.
- */
-gpointer
-wapi_prepare_interrupt_thread (gpointer thread_handle)
-{
-       WapiHandle_thread *thread;
-       gpointer prev_handle, wait_handle;
-
-       thread = lookup_thread (thread_handle); /* FIXME this is wrong, move this whole thing to MonoThreads where it can be done lockfree */
-
-       while (TRUE) {
-               wait_handle = thread->wait_handle;
-
-               /* 
-                * Atomically obtain the handle the thread is waiting on, and
-                * change it to a flag value.
-                */
-               prev_handle = InterlockedCompareExchangePointer (&thread->wait_handle,
-                                                                                                                INTERRUPTION_REQUESTED_HANDLE, wait_handle);
-               if (prev_handle == INTERRUPTION_REQUESTED_HANDLE)
-                       /* Already interrupted */
-                       return 0;
-               if (prev_handle == wait_handle)
-                       break;
-
-               /* Try again */
-       }
-
-       WAIT_DEBUG (printf ("%p: state -> INTERRUPTED.\n", thread->id););
-
-       return wait_handle;
-}
-
-void
-wapi_finish_interrupt_thread (gpointer wait_handle)
-{
-       pthread_cond_t *cond;
-       mono_mutex_t *mutex;
-       guint32 idx;
-
-       if (!wait_handle)
-               /* Not waiting */
-               return;
-
-       /* If we reach here, then wait_handle is set to the flag value, 
-        * which means that the target thread is either
-        * - before the first CAS in timedwait, which means it won't enter the
-        * wait.
-        * - it is after the first CAS, so it is already waiting, or it will 
-        * enter the wait, and it will be interrupted by the broadcast.
-        */
-       idx = GPOINTER_TO_UINT(wait_handle);
-       cond = &_WAPI_PRIVATE_HANDLES(idx).signal_cond;
-       mutex = &_WAPI_PRIVATE_HANDLES(idx).signal_mutex;
-
-       mono_mutex_lock (mutex);
-       mono_cond_broadcast (cond);
-       mono_mutex_unlock (mutex);
-
-       /* ref added by set_wait_handle */
-       _wapi_handle_unref (wait_handle);
-}
-
-/*
- * wapi_self_interrupt:
- *
- *   This is not part of the WIN32 API.
- * Set the 'interrupted' state of the calling thread if it's NULL.
- */
-void
-wapi_self_interrupt (void)
-{
-       gpointer wait_handle;
-
-       wait_handle = wapi_prepare_interrupt_thread (get_current_thread_handle ());
-       if (wait_handle)
-               /* ref added by set_wait_handle */
-               _wapi_handle_unref (wait_handle);
-}
-
-/*
- * wapi_clear_interruption:
- *
- *   This is not part of the WIN32 API. 
- * Clear the 'interrupted' state of the calling thread.
- * This function is signal safe
- */
-void
-wapi_clear_interruption (void)
-{
-       WapiHandle_thread *thread;
-       gpointer prev_handle;
-
-       thread = get_current_thread ();
-
-       prev_handle = InterlockedCompareExchangePointer (&thread->wait_handle,
-                                                                                                        NULL, INTERRUPTION_REQUESTED_HANDLE);
-       if (prev_handle == INTERRUPTION_REQUESTED_HANDLE)
-               WAIT_DEBUG (printf ("%p: state -> NORMAL.\n", GetCurrentThreadId ()););
-}
-
-/**
- * wapi_thread_set_wait_handle:
- *
- *   Set the wait handle for the current thread to HANDLE. Return TRUE on success, FALSE
- * if the thread is in interrupted state, and cannot start waiting.
- */
-gboolean
-wapi_thread_set_wait_handle (gpointer handle)
-{
-       WapiHandle_thread *thread;
-       gpointer prev_handle;
-
-       thread = get_current_thread ();
-
-       prev_handle = InterlockedCompareExchangePointer (&thread->wait_handle,
-                                                                                                        handle, NULL);
-       if (prev_handle == NULL) {
-               /* thread->wait_handle acts as an additional reference to the handle */
-               _wapi_handle_ref (handle);
-
-               WAIT_DEBUG (printf ("%p: state -> WAITING.\n", GetCurrentThreadId ()););
-       } else {
-               g_assert (prev_handle == INTERRUPTION_REQUESTED_HANDLE);
-               WAIT_DEBUG (printf ("%p: unable to set state to WAITING.\n", GetCurrentThreadId ()););
-       }
-
-       return prev_handle == NULL;
-}
-
-/**
- * wapi_thread_clear_wait_handle:
- *
- *   Clear the wait handle of the current thread.
- */
-void
-wapi_thread_clear_wait_handle (gpointer handle)
-{
-       WapiHandle_thread *thread;
-       gpointer prev_handle;
-
-       thread = get_current_thread ();
-
-       prev_handle = InterlockedCompareExchangePointer (&thread->wait_handle,
-                                                                                                        NULL, handle);
-       if (prev_handle == handle) {
-               _wapi_handle_unref (handle);
-               WAIT_DEBUG (printf ("%p: state -> NORMAL.\n", GetCurrentThreadId ()););
-       } else {
-               /*It can be NULL if it was asynchronously cleared*/
-               g_assert (prev_handle == INTERRUPTION_REQUESTED_HANDLE || prev_handle == NULL);
-               WAIT_DEBUG (printf ("%p: finished waiting.\n", GetCurrentThreadId ()););
-       }
+       return mono_thread_info_is_interrupt_state (mono_thread_info_current ());
 }
 
 void
@@ -524,31 +349,20 @@ wapi_current_thread_desc (void)
        WapiHandle_thread *thread;
        gpointer thread_handle;
        int i;
-       gpointer handle;
        GString* text;
        char *res;
 
        thread_handle = get_current_thread_handle ();
        thread = lookup_thread (thread_handle);
 
-       handle = thread->wait_handle;
        text = g_string_new (0);
        g_string_append_printf (text, "thread handle %p state : ", thread_handle);
 
-       if (!handle)
-               g_string_append_printf (text, "not waiting");
-       else if (handle == INTERRUPTION_REQUESTED_HANDLE)
-               g_string_append_printf (text, "interrupted state");
-       else
-               g_string_append_printf (text, "waiting on %p : %s ", handle, _wapi_handle_typename[_wapi_handle_type (handle)]);
+       mono_thread_info_describe_interrupt_token (mono_thread_info_current (), text);
+
        g_string_append_printf (text, " owns (");
-       for (i = 0; i < thread->owned_mutexes->len; i++) {
-               gpointer mutex = g_ptr_array_index (thread->owned_mutexes, i);
-               if (i > 0)
-                       g_string_append_printf (text, ", %p", mutex);
-               else
-                       g_string_append_printf (text, "%p", mutex);
-       }
+       for (i = 0; i < thread->owned_mutexes->len; i++)
+               g_string_append_printf (text, i > 0 ? ", %p" : "%p", g_ptr_array_index (thread->owned_mutexes, i));
        g_string_append_printf (text, ")");
 
        res = text->str;
index 1370c3d0c55ac01e65e2d930e9223a5c5cfce019..17246184a8c573982ac41504c16d66e5919b57f8 100644 (file)
@@ -4028,8 +4028,9 @@ mono_thread_execute_interruption (MonoInternalThread *thread)
                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_interruption ();
+               mono_thread_info_clear_self_interrupt ();
        }
 
        if ((thread->state & ThreadState_AbortRequested) != 0) {
@@ -4399,7 +4400,7 @@ mono_thread_info_get_last_managed (MonoThreadInfo *info)
 typedef struct {
        MonoInternalThread *thread;
        gboolean install_async_abort;
-       gpointer interrupt_handle;
+       MonoThreadInfoInterruptToken *interrupt_token;
 } AbortThreadData;
 
 static SuspendThreadResult
@@ -4442,7 +4443,8 @@ abort_thread_critical (MonoThreadInfo *info, gpointer ud)
                 * functions in the io-layer until the signal handler calls QueueUserAPC which will
                 * make it return.
                 */
-               data->interrupt_handle = mono_thread_info_prepare_interrupt (thread->handle);
+               data->interrupt_token = mono_thread_info_prepare_interrupt (info);
+
                return MonoResumeThread;
        }
 }
@@ -4463,20 +4465,22 @@ abort_thread_internal (MonoInternalThread *thread, gboolean can_raise_exception,
                MonoException *exc = mono_thread_request_interruption (can_raise_exception); 
                if (exc)
                        mono_raise_exception (exc);
-               mono_thread_info_interrupt (thread->handle);
+
+               mono_thread_info_self_interrupt ();
+
                return;
        }
 
        mono_thread_info_safe_suspend_and_run ((MonoNativeThreadId)(gsize)thread->tid, TRUE, abort_thread_critical, &data);
-       if (data.interrupt_handle)
-               mono_thread_info_finish_interrupt (data.interrupt_handle);
+       if (data.interrupt_token)
+               mono_thread_info_finish_interrupt (data.interrupt_token);
        /*FIXME we need to wait for interruption to complete -- figure out how much into interruption we should wait for here*/
 }
 
 typedef struct{
        MonoInternalThread *thread;
        gboolean interrupt;
-       gpointer interrupt_handle;
+       MonoThreadInfoInterruptToken *interrupt_token;
 } SuspendThreadData;
 
 static SuspendThreadResult
@@ -4500,7 +4504,7 @@ suspend_thread_critical (MonoThreadInfo *info, gpointer ud)
                if (InterlockedCompareExchange (&thread->interruption_requested, 1, 0) == 0)
                        InterlockedIncrement (&thread_interruption_requested);
                if (data->interrupt)
-                       data->interrupt_handle = mono_thread_info_prepare_interrupt (thread->handle);
+                       data->interrupt_token = mono_thread_info_prepare_interrupt (thread->thread_info);
                
                if (mono_thread_notify_pending_exc_fn && !running_managed)
                        /* The JIT will notify the thread about the interruption */
@@ -4526,8 +4530,8 @@ suspend_thread_internal (MonoInternalThread *thread, gboolean interrupt)
                data.interrupt = interrupt;
 
                mono_thread_info_safe_suspend_and_run ((MonoNativeThreadId)(gsize)thread->tid, interrupt, suspend_thread_critical, &data);
-               if (data.interrupt_handle)
-                       mono_thread_info_finish_interrupt (data.interrupt_handle);
+               if (data.interrupt_token)
+                       mono_thread_info_finish_interrupt (data.interrupt_token);
                UNLOCK_THREAD (thread);
        }
 }
index fc884e7a05584787d813ff2b241012da9a61eaa1..fbb78b498fa7d252b59e3b236bfa6772676d9a35 100644 (file)
@@ -31,12 +31,6 @@ mono_threads_init_platform (void)
        mono_threads_init_dead_letter ();
 }
 
-void
-mono_threads_core_interrupt (MonoThreadInfo *info)
-{
-       thread_abort (info->native_handle);
-}
-
 void
 mono_threads_core_abort_syscall (MonoThreadInfo *info)
 {
index e9ca1415c04eee3f37dfd855b451b738d72f7ab6..b2e4bd37bcc93a8ec42eb3e9a6f844ecf36e353e 100644 (file)
@@ -236,30 +236,6 @@ mono_threads_core_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
        return handle;
 }
 
-gpointer
-mono_threads_core_prepare_interrupt (HANDLE thread_handle)
-{
-       return wapi_prepare_interrupt_thread (thread_handle);
-}
-
-void
-mono_threads_core_finish_interrupt (gpointer wait_handle)
-{
-       wapi_finish_interrupt_thread (wait_handle);
-}
-
-void
-mono_threads_core_self_interrupt (void)
-{
-       wapi_self_interrupt ();
-}
-
-void
-mono_threads_core_clear_interruption (void)
-{
-       wapi_clear_interruption ();
-}
-
 int
 mono_threads_pthread_kill (MonoThreadInfo *info, int signum)
 {
index 9f3581e9a3907848b62de857f26aaa6546f60570..9e59810fb2b1925e5ffc407c037fb6ecc505c907 100755 (executable)
@@ -395,26 +395,4 @@ mono_threads_core_set_name (MonoNativeThreadId tid, const char *name)
 #endif
 }
 
-
-gpointer
-mono_threads_core_prepare_interrupt (HANDLE thread_handle)
-{
-       return NULL;
-}
-
-void
-mono_threads_core_finish_interrupt (gpointer wait_handle)
-{
-}
-
-void
-mono_threads_core_self_interrupt (void)
-{
-}
-
-void
-mono_threads_core_clear_interruption (void)
-{
-}
-
 #endif
index 366f09468e952c5882802b4dd0edf07d8655bd73..95993f0c9eb6d9d9ecd170507dc2fcd5c83840cb 100644 (file)
@@ -1153,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);
 
-       wait_handle = mono_thread_info_prepare_interrupt (thread_handle);
-       mono_thread_info_finish_interrupt (wait_handle);
+       if (token == NULL)
+               return;
+
+       g_assert (token->callback);
+
+       token->callback (token->data);
+
+       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_self_interrupt ()
+{
+       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_clear_interruption (void)
+mono_thread_info_describe_interrupt_token (MonoThreadInfo *info, GString *text)
 {
-       mono_threads_core_clear_interruption ();
+       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. */
index edbf934ac9bf7321a524a47459231121c00d9f40..73834fe2f868dab76b289e56525ffc0c86da1809 100644 (file)
@@ -117,6 +117,12 @@ and reduce the number of casts drastically.
 #define THREADS_STATE_MACHINE_DEBUG MOSTLY_ASYNC_SAFE_PRINTF
 #endif
 
+#if 1
+#define THREADS_INTERRUPT_DEBUG(...)
+#else
+#define THREADS_INTERRUPT_DEBUG MOSTLY_ASYNC_SAFE_PRINTF
+#endif
+
 /* If this is defined, use the signals backed on Mach. Debug only as signals can't be made usable on OSX. */
 // #define USE_SIGNALS_ON_MACH
 
@@ -170,6 +176,8 @@ typedef enum {
        MONO_SERVICE_REQUEST_SAMPLE = 1,
 } MonoAsyncJob;
 
+typedef struct _MonoThreadInfoInterruptToken MonoThreadInfoInterruptToken;
+
 typedef struct {
        MonoLinkedListSetNode node;
        guint32 small_id; /*Used by hazard pointers */
@@ -243,6 +251,8 @@ typedef struct {
        volatile gint32 service_requests;
 
        void *jit_data;
+
+       MonoThreadInfoInterruptToken *interrupt_token;
 } MonoThreadInfo;
 
 typedef struct {
@@ -401,20 +411,29 @@ mono_thread_info_exit (void);
 HANDLE
 mono_thread_info_open_handle (void);
 
-gpointer
-mono_thread_info_prepare_interrupt (HANDLE thread_handle);
+void
+mono_thread_info_install_interrupt (void (*callback) (gpointer data), gpointer data, gboolean *interrupted);
 
 void
-mono_thread_info_finish_interrupt (gpointer wait_handle);
+mono_thread_info_uninstall_interrupt (gboolean *interrupted);
+
+MonoThreadInfoInterruptToken*
+mono_thread_info_prepare_interrupt (THREAD_INFO_TYPE *info);
 
 void
-mono_thread_info_interrupt (HANDLE thread_handle);
+mono_thread_info_finish_interrupt (MonoThreadInfoInterruptToken *token);
 
 void
 mono_thread_info_self_interrupt (void);
 
 void
-mono_thread_info_clear_interruption (void);
+mono_thread_info_clear_self_interrupt (void);
+
+gboolean
+mono_thread_info_is_interrupt_state (THREAD_INFO_TYPE *info);
+
+void
+mono_thread_info_describe_interrupt_token (THREAD_INFO_TYPE *info, GString *text);
 
 gboolean
 mono_thread_info_is_live (THREAD_INFO_TYPE *info);
@@ -457,7 +476,6 @@ gboolean mono_threads_core_suspend (THREAD_INFO_TYPE *info, gboolean interrupt_k
 gboolean mono_threads_core_resume (THREAD_INFO_TYPE *info);
 void mono_threads_platform_register (THREAD_INFO_TYPE *info); //ok
 void mono_threads_platform_free (THREAD_INFO_TYPE *info);
-void mono_threads_core_interrupt (THREAD_INFO_TYPE *info);
 void mono_threads_core_abort_syscall (THREAD_INFO_TYPE *info);
 gboolean mono_threads_core_needs_abort_syscall (void);
 HANDLE mono_threads_core_create_thread (LPTHREAD_START_ROUTINE start, gpointer arg, guint32 stack_size, guint32 creation_flags, MonoNativeThreadId *out_tid);
@@ -523,10 +541,6 @@ void mono_threads_core_unregister (THREAD_INFO_TYPE *info);
 HANDLE mono_threads_core_open_handle (void);
 HANDLE mono_threads_core_open_thread_handle (HANDLE handle, MonoNativeThreadId tid);
 void mono_threads_core_set_name (MonoNativeThreadId tid, const char *name);
-gpointer mono_threads_core_prepare_interrupt (HANDLE thread_handle);
-void mono_threads_core_finish_interrupt (gpointer wait_handle);
-void mono_threads_core_self_interrupt (void);
-void mono_threads_core_clear_interruption (void);
 
 MonoNativeThreadId mono_native_thread_id_get (void);