[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");
}
[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) {
}
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,
#include <mono/utils/mono-mutex.h>
#include <mono/utils/mono-proclib.h>
+#include <mono/utils/mono-threads.h>
#undef DEBUG_REFS
#if 0
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);
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;
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;
}
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);
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);
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;
}
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);
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
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);
}
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);
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
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);
thr_ret = _wapi_handle_unlock_handle (wait);
g_assert (thr_ret == 0);
- if (apc_pending)
- ret = WAIT_IO_COMPLETION;
-
return(ret);
}
guint32 retval;
gboolean poll;
gpointer sorted_handles [MAXIMUM_WAIT_OBJECTS];
+ gboolean apc_pending = FALSE;
if (current_thread == NULL) {
SetLastError (ERROR_INVALID_HANDLE);
_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
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;
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;
}
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;
}
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)
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) {
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
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;
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) {
typedef struct {
MonoInternalThread *thread;
gboolean install_async_abort;
- gpointer interrupt_handle;
+ MonoThreadInfoInterruptToken *interrupt_token;
} AbortThreadData;
static SuspendThreadResult
* 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;
}
}
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
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 */
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);
}
}
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)
{
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)
{
#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
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. */
#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
MONO_SERVICE_REQUEST_SAMPLE = 1,
} MonoAsyncJob;
+typedef struct _MonoThreadInfoInterruptToken MonoThreadInfoInterruptToken;
+
typedef struct {
MonoLinkedListSetNode node;
guint32 small_id; /*Used by hazard pointers */
volatile gint32 service_requests;
void *jit_data;
+
+ MonoThreadInfoInterruptToken *interrupt_token;
} MonoThreadInfo;
typedef struct {
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);
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);
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);