-/*
- * threads.c: Thread support internal calls
+/**
+ * \file
+ * Thread support internal calls
*
* Author:
* Dick Porter (dick@ximian.com)
static MonoGHashTable *threads=NULL;
/* List of app context GC handles.
- * Added to from ves_icall_System_Runtime_Remoting_Contexts_Context_RegisterContext ().
+ * Added to from mono_threads_register_app_context ().
*/
static GHashTable *contexts = NULL;
return InterlockedIncrement (&managed_thread_id_counter);
}
+/*
+ * We separate interruptions/exceptions into either sync (they can be processed anytime,
+ * normally as soon as they are set, and are set by the same thread) and async (they can't
+ * be processed inside abort protected blocks and are normally set by other threads). We
+ * can have both a pending sync and async interruption. In this case, the sync exception is
+ * processed first. Since we clean sync flag first, mono_thread_execute_interruption must
+ * also handle all sync type exceptions before the async type exceptions.
+ */
enum {
- INTERRUPT_REQUESTED_BIT = 0x1,
- INTERRUPT_REQUEST_DEFERRED_BIT = 0x2,
+ INTERRUPT_SYNC_REQUESTED_BIT = 0x1,
+ INTERRUPT_ASYNC_REQUESTED_BIT = 0x2,
+ INTERRUPT_REQUESTED_MASK = 0x3,
ABORT_PROT_BLOCK_SHIFT = 2,
ABORT_PROT_BLOCK_BITS = 8,
ABORT_PROT_BLOCK_MASK = (((1 << ABORT_PROT_BLOCK_BITS) - 1) << ABORT_PROT_BLOCK_SHIFT)
return (state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT;
}
-static void
-verify_thread_state (gsize state)
-{
- //can't have both INTERRUPT_REQUESTED_BIT and INTERRUPT_REQUEST_DEFERRED_BIT set at the same time
- g_assert ((state & (INTERRUPT_REQUESTED_BIT | INTERRUPT_REQUEST_DEFERRED_BIT)) != (INTERRUPT_REQUESTED_BIT | INTERRUPT_REQUEST_DEFERRED_BIT));
-
- //XXX This would be nice to be true, but can happen due to self-aborts (and possibly set-pending-exception)
- //if prot_count > 0, INTERRUPT_REQUESTED_BIT must never be set
- // int prot_count = (state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT;
- // g_assert (!(prot_count > 0 && (state & INTERRUPT_REQUESTED_BIT)));
-}
-
void
mono_threads_begin_abort_protected_block (void)
{
MonoInternalThread *thread = mono_thread_internal_current ();
gsize old_state, new_state;
+ int new_val;
do {
old_state = thread->thread_state;
- verify_thread_state (old_state);
-
- int new_val = ((old_state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT) + 1;
-
- new_state = 0;
- if (old_state & (INTERRUPT_REQUESTED_BIT | INTERRUPT_REQUEST_DEFERRED_BIT)) {
- new_state |= INTERRUPT_REQUEST_DEFERRED_BIT;
- }
+ new_val = ((old_state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT) + 1;
//bounds check abort_prot_count
g_assert (new_val > 0);
g_assert (new_val < (1 << ABORT_PROT_BLOCK_BITS));
- new_state |= new_val << ABORT_PROT_BLOCK_SHIFT;
+ new_state = old_state + (1 << ABORT_PROT_BLOCK_SHIFT);
} while (InterlockedCompareExchangePointer ((volatile gpointer)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
+
+ /* Defer async request since we won't be able to process until exiting the block */
+ if (new_val == 1 && (new_state & INTERRUPT_ASYNC_REQUESTED_BIT)) {
+ InterlockedDecrement (&thread_interruption_requested);
+ THREADS_INTERRUPT_DEBUG ("[%d] begin abort protected block old_state %ld new_state %ld, defer tir %d\n", thread->small_id, old_state, new_state, thread_interruption_requested);
+ if (thread_interruption_requested < 0)
+ g_warning ("bad thread_interruption_requested state");
+ } else {
+ THREADS_INTERRUPT_DEBUG ("[%d] begin abort protected block old_state %ld new_state %ld, tir %d\n", thread->small_id, old_state, new_state, thread_interruption_requested);
+ }
}
-gboolean
-mono_threads_end_abort_protected_block (void)
+static gboolean
+mono_thread_state_has_interruption (gsize state)
{
- MonoInternalThread *thread = mono_thread_internal_current ();
- gsize old_state, new_state;
- do {
- old_state = thread->thread_state;
- verify_thread_state (old_state);
-
- int new_val = ((old_state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT) - 1;
- new_state = 0;
-
- if ((old_state & INTERRUPT_REQUEST_DEFERRED_BIT) && new_val == 0) {
- new_state |= INTERRUPT_REQUESTED_BIT;
- }
+ /* pending exception, self abort */
+ if (state & INTERRUPT_SYNC_REQUESTED_BIT)
+ return TRUE;
- //bounds check abort_prot_count
- g_assert (new_val >= 0);
- g_assert (new_val < (1 << ABORT_PROT_BLOCK_BITS));
- new_state |= new_val << ABORT_PROT_BLOCK_SHIFT;
+ /* abort, interruption, suspend */
+ if ((state & INTERRUPT_ASYNC_REQUESTED_BIT) && !(state & ABORT_PROT_BLOCK_MASK))
+ return TRUE;
- } while (InterlockedCompareExchangePointer ((volatile gpointer)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
- return (new_state & INTERRUPT_REQUESTED_BIT) == INTERRUPT_REQUESTED_BIT;
+ return FALSE;
}
-
-//Don't use this function, use inc/dec below
-static void
-mono_thread_abort_prot_block_count_add (MonoInternalThread *thread, int val)
+gboolean
+mono_threads_end_abort_protected_block (void)
{
+ MonoInternalThread *thread = mono_thread_internal_current ();
gsize old_state, new_state;
+ int new_val;
do {
old_state = thread->thread_state;
- verify_thread_state (old_state);
- int new_val = val + ((old_state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT);
//bounds check abort_prot_count
+ new_val = ((old_state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT) - 1;
g_assert (new_val >= 0);
g_assert (new_val < (1 << ABORT_PROT_BLOCK_BITS));
- new_state = (old_state & ~ABORT_PROT_BLOCK_MASK) | (new_val << ABORT_PROT_BLOCK_SHIFT);
+ new_state = old_state - (1 << ABORT_PROT_BLOCK_SHIFT);
} while (InterlockedCompareExchangePointer ((volatile gpointer)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
-}
-static void
-mono_thread_inc_abort_prot_block_count (MonoInternalThread *thread)
-{
- mono_thread_abort_prot_block_count_add (thread, 1);
-}
+ if (new_val == 0 && (new_state & INTERRUPT_ASYNC_REQUESTED_BIT)) {
+ InterlockedIncrement (&thread_interruption_requested);
+ THREADS_INTERRUPT_DEBUG ("[%d] end abort protected block old_state %ld new_state %ld, restore tir %d\n", thread->small_id, old_state, new_state, thread_interruption_requested);
+ } else {
+ THREADS_INTERRUPT_DEBUG ("[%d] end abort protected block old_state %ld new_state %ld, tir %d\n", thread->small_id, old_state, new_state, thread_interruption_requested);
+ }
-static void
-mono_thread_dec_abort_prot_block_count (MonoInternalThread *thread)
-{
- mono_thread_abort_prot_block_count_add (thread, -1);
+ return mono_thread_state_has_interruption (new_state);
}
static gboolean
mono_thread_get_interruption_requested (MonoInternalThread *thread)
{
gsize state = thread->thread_state;
- return (state & INTERRUPT_REQUESTED_BIT) == INTERRUPT_REQUESTED_BIT;
+
+ return mono_thread_state_has_interruption (state);
}
-/* Returns TRUE is there was a state change */
+/*
+ * Returns TRUE is there was a state change
+ * We clear a single interruption request, sync has priority.
+ */
static gboolean
mono_thread_clear_interruption_requested (MonoInternalThread *thread)
{
gsize old_state, new_state;
do {
old_state = thread->thread_state;
- verify_thread_state (old_state);
- //Already cleared
- if (!(old_state & (INTERRUPT_REQUESTED_BIT | INTERRUPT_REQUEST_DEFERRED_BIT)))
+ // no interruption to process
+ if (!(old_state & INTERRUPT_SYNC_REQUESTED_BIT) &&
+ (!(old_state & INTERRUPT_ASYNC_REQUESTED_BIT) || (old_state & ABORT_PROT_BLOCK_MASK)))
return FALSE;
- new_state = old_state & ~(INTERRUPT_REQUESTED_BIT | INTERRUPT_REQUEST_DEFERRED_BIT);
+
+ if (old_state & INTERRUPT_SYNC_REQUESTED_BIT)
+ new_state = old_state & ~INTERRUPT_SYNC_REQUESTED_BIT;
+ else
+ new_state = old_state & ~INTERRUPT_ASYNC_REQUESTED_BIT;
} while (InterlockedCompareExchangePointer ((volatile gpointer)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
+
+ InterlockedDecrement (&thread_interruption_requested);
+ THREADS_INTERRUPT_DEBUG ("[%d] clear interruption old_state %ld new_state %ld, tir %d\n", thread->small_id, old_state, new_state, thread_interruption_requested);
+ if (thread_interruption_requested < 0)
+ g_warning ("bad thread_interruption_requested state");
return TRUE;
}
-/* Returns TRUE is there was a state change */
+/* Returns TRUE is there was a state change and the interruption can be processed */
static gboolean
mono_thread_set_interruption_requested (MonoInternalThread *thread)
{
//always force when the current thread is doing it to itself.
- gboolean force_interrupt = thread == mono_thread_internal_current ();
+ gboolean sync = thread == mono_thread_internal_current ();
gsize old_state, new_state;
do {
old_state = thread->thread_state;
- verify_thread_state (old_state);
- int prot_count = ((old_state & ABORT_PROT_BLOCK_MASK) >> ABORT_PROT_BLOCK_SHIFT);
//Already set
- if (old_state & (INTERRUPT_REQUESTED_BIT | INTERRUPT_REQUEST_DEFERRED_BIT))
+ if ((sync && (old_state & INTERRUPT_SYNC_REQUESTED_BIT)) ||
+ (!sync && (old_state & INTERRUPT_ASYNC_REQUESTED_BIT)))
return FALSE;
- //If there's an outstanding prot block, we queue it
- if (prot_count && !force_interrupt) {
- new_state = old_state | INTERRUPT_REQUEST_DEFERRED_BIT;
- } else
- new_state = old_state | INTERRUPT_REQUESTED_BIT;
+ if (sync)
+ new_state = old_state | INTERRUPT_SYNC_REQUESTED_BIT;
+ else
+ new_state = old_state | INTERRUPT_ASYNC_REQUESTED_BIT;
} while (InterlockedCompareExchangePointer ((volatile gpointer)&thread->thread_state, (gpointer)new_state, (gpointer)old_state) != (gpointer)old_state);
- return (new_state & INTERRUPT_REQUESTED_BIT) == INTERRUPT_REQUESTED_BIT;
+ if (sync || !(new_state & ABORT_PROT_BLOCK_MASK)) {
+ InterlockedIncrement (&thread_interruption_requested);
+ THREADS_INTERRUPT_DEBUG ("[%d] set interruption on [%d] old_state %ld new_state %ld, tir %d\n", mono_thread_internal_current ()->small_id, thread->small_id, old_state, new_state, thread_interruption_requested);
+ } else {
+ THREADS_INTERRUPT_DEBUG ("[%d] set interruption on [%d] old_state %ld new_state %ld, tir deferred %d\n", mono_thread_internal_current ()->small_id, thread->small_id, old_state, new_state, thread_interruption_requested);
+ }
+
+ return sync || !(new_state & ABORT_PROT_BLOCK_MASK);
}
static inline MonoNativeThreadId
g_assert (thread);
info = mono_thread_info_current ();
+ g_assert (info);
internal = thread->internal_thread;
+ g_assert (internal);
+
+ /* It is needed to store the MonoInternalThread on the MonoThreadInfo, because of the following case:
+ * - the MonoInternalThread TLS key is destroyed: set it to NULL
+ * - the MonoThreadInfo TLS key is destroyed: calls mono_thread_info_detach
+ * - it calls MonoThreadInfoCallbacks.thread_detach
+ * - mono_thread_internal_current returns NULL -> fails to detach the MonoInternalThread. */
+ mono_thread_info_set_internal_thread_gchandle (info, mono_gchandle_new ((MonoObject*) internal, FALSE));
+
internal->handle = mono_threads_open_thread_handle (info->handle);
#ifdef HOST_WIN32
internal->native_handle = OpenThread (THREAD_ALL_ACCESS, FALSE, GetCurrentThreadId ());
start_info = (StartInfo*) data;
g_assert (start_info);
- info = mono_thread_info_attach (&res);
+ info = mono_thread_info_attach ();
info->runtime_thread = TRUE;
/* Run the actual main function of the thread */
- res = start_wrapper_internal (start_info, &res);
+ res = start_wrapper_internal (start_info, info->stack_end);
mono_thread_info_exit (res);
return ret;
}
-void mono_thread_new_init (intptr_t tid, gpointer stack_start, gpointer func)
+/**
+ * mono_thread_new_init:
+ */
+void
+mono_thread_new_init (intptr_t tid, gpointer stack_start, gpointer func)
{
if (mono_thread_start_cb) {
mono_thread_start_cb (tid, stack_start, func);
}
}
-void mono_threads_set_default_stacksize (guint32 stacksize)
+/**
+ * mono_threads_set_default_stacksize:
+ */
+void
+mono_threads_set_default_stacksize (guint32 stacksize)
{
default_stacksize = stacksize;
}
-guint32 mono_threads_get_default_stacksize (void)
+/**
+ * mono_threads_get_default_stacksize:
+ */
+guint32
+mono_threads_get_default_stacksize (void)
{
return default_stacksize;
}
return internal;
}
+/**
+ * mono_thread_create:
+ */
void
mono_thread_create (MonoDomain *domain, gpointer func, gpointer arg)
{
return (NULL != mono_thread_create_internal (domain, func, arg, MONO_THREAD_CREATE_FLAGS_NONE, error));
}
-MonoThread *
-mono_thread_attach (MonoDomain *domain)
-{
- MonoThread *thread = mono_thread_attach_full (domain, FALSE);
-
- return thread;
-}
-
-MonoThread *
+static MonoThread *
mono_thread_attach_full (MonoDomain *domain, gboolean force_attach)
{
MonoInternalThread *internal;
MonoThread *thread;
MonoThreadInfo *info;
MonoNativeThreadId tid;
- gsize stack_ptr;
if (mono_thread_internal_current_is_attached ()) {
if (domain != mono_domain_get ())
return mono_thread_current ();
}
- info = mono_thread_info_attach (&stack_ptr);
+ info = mono_thread_info_attach ();
g_assert (info);
tid=mono_native_thread_id_get ();
THREAD_DEBUG (g_message ("%s: Attached thread ID %"G_GSIZE_FORMAT" (handle %p)", __func__, tid, internal->handle));
- if (mono_thread_attach_cb) {
- guint8 *staddr;
- size_t stsize;
-
- mono_thread_info_get_stack_bounds (&staddr, &stsize);
-
- if (staddr == NULL)
- mono_thread_attach_cb (MONO_NATIVE_THREAD_ID_TO_UINT (tid), &stack_ptr);
- else
- mono_thread_attach_cb (MONO_NATIVE_THREAD_ID_TO_UINT (tid), staddr + stsize);
- }
+ if (mono_thread_attach_cb)
+ mono_thread_attach_cb (MONO_NATIVE_THREAD_ID_TO_UINT (tid), info->stack_end);
/* Can happen when we attach the profiler helper thread in order to heapshot. */
if (!mono_thread_info_current ()->tools_thread)
return thread;
}
+/**
+ * mono_thread_attach:
+ */
+MonoThread *
+mono_thread_attach (MonoDomain *domain)
+{
+ return mono_thread_attach_full (domain, FALSE);
+}
+
void
mono_thread_detach_internal (MonoInternalThread *thread)
{
gboolean removed;
g_assert (thread != NULL);
+ SET_CURRENT_OBJECT (thread);
THREAD_DEBUG (g_message ("%s: mono_thread_detach for %p (%"G_GSIZE_FORMAT")", __func__, thread, (gsize)thread->tid));
Leaving the counter unbalanced will cause a performance degradation since all threads
will now keep checking their local flags all the time.
*/
- if (mono_thread_clear_interruption_requested (thread))
- InterlockedDecrement (&thread_interruption_requested);
+ mono_thread_clear_interruption_requested (thread);
mono_threads_lock ();
SET_CURRENT_OBJECT (NULL);
mono_domain_unset ();
+ mono_thread_info_unset_internal_thread_gchandle ((MonoThreadInfo*) thread->thread_info);
+
/* Don't need to close the handle to this thread, even though we took a
* reference in mono_thread_attach (), because the GC will do it
* when the Thread object is finalised.
*/
}
+/**
+ * mono_thread_detach:
+ */
void
mono_thread_detach (MonoThread *thread)
{
mono_thread_detach_internal (thread->internal_thread);
}
-/*
+/**
* mono_thread_detach_if_exiting:
*
- * Detach the current thread from the runtime if it is exiting, i.e. it is running pthread dtors.
+ * Detach the current thread from the runtime if it is exiting, i.e. it is running pthread dtors.
* This should be used at the end of embedding code which calls into managed code, and which
- * can be called from pthread dtors, like dealloc: implementations in objective-c.
+ * can be called from pthread dtors, like <code>dealloc:</code> implementations in Objective-C.
*/
mono_bool
mono_thread_detach_if_exiting (void)
return TRUE;
}
+/**
+ * mono_thread_exit:
+ */
void
mono_thread_exit (void)
{
return res;
}
-/*
+/**
* mono_thread_get_name_utf8:
- *
- * Return the name of the thread in UTF-8.
+ * \returns the name of the thread in UTF-8.
* Return NULL if the thread has no name.
* The returned memory is owned by the caller.
*/
return tname;
}
-/*
+/**
* mono_thread_get_managed_id:
- *
- * Return the Thread.ManagedThreadId value of `thread`.
- * Returns -1 if `thread` is NULL.
+ * \returns the \c Thread.ManagedThreadId value of \p thread.
+ * Returns \c -1 if \p thread is NULL.
*/
int32_t
mono_thread_get_managed_id (MonoThread *thread)
return result;
}
+/**
+ * mono_thread_current:
+ */
MonoThread *
mono_thread_current (void)
{
}
}
-static MonoW32HandleWaitRet
-mono_wait_uninterrupted (MonoInternalThread *thread, guint32 numhandles, gpointer *handles, gboolean waitall, gint32 ms, MonoError *error)
+gint32
+ves_icall_System_Threading_WaitHandle_Wait_internal (gpointer *handles, gint32 numhandles, MonoBoolean waitall, gint32 timeout, MonoError *error)
{
- MonoException *exc;
MonoW32HandleWaitRet ret;
+ MonoInternalThread *thread;
+ MonoException *exc;
gint64 start;
- gint32 diff_ms;
- gint32 wait = ms;
+ guint32 timeoutLeft;
- error_init (error);
+ /* Do this WaitSleepJoin check before creating objects */
+ if (mono_thread_current_check_pending_interrupt ())
+ return map_native_wait_result_to_managed (MONO_W32HANDLE_WAIT_RET_FAILED, 0);
- start = (ms == -1) ? 0 : mono_100ns_ticks ();
- do {
+ thread = mono_thread_internal_current ();
+
+ mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
+
+ if (timeout == -1)
+ timeout = MONO_INFINITE_WAIT;
+ if (timeout != MONO_INFINITE_WAIT)
+ start = mono_msec_ticks ();
+
+ timeoutLeft = timeout;
+
+ for (;;) {
MONO_ENTER_GC_SAFE;
#ifdef HOST_WIN32
if (numhandles != 1)
- ret = mono_w32handle_convert_wait_ret (WaitForMultipleObjectsEx (numhandles, handles, waitall, wait, TRUE), numhandles);
+ ret = mono_w32handle_convert_wait_ret (WaitForMultipleObjectsEx (numhandles, handles, waitall, timeoutLeft, TRUE), numhandles);
else
- ret = mono_w32handle_convert_wait_ret (WaitForSingleObjectEx (handles [0], ms, TRUE), 1);
+ ret = mono_w32handle_convert_wait_ret (WaitForSingleObjectEx (handles [0], timeoutLeft, TRUE), 1);
#else
/* mono_w32handle_wait_multiple optimizes the case for numhandles == 1 */
- ret = mono_w32handle_wait_multiple (handles, numhandles, waitall, wait, TRUE);
+ ret = mono_w32handle_wait_multiple (handles, numhandles, waitall, timeoutLeft, TRUE);
#endif /* HOST_WIN32 */
MONO_EXIT_GC_SAFE;
break;
}
- if (ms == -1)
- continue;
-
- /* Re-calculate ms according to the time passed */
- diff_ms = (gint32)((mono_100ns_ticks () - start) / 10000);
- if (diff_ms >= ms) {
- ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
- break;
- }
- wait = ms - diff_ms;
- } while (TRUE);
-
- return ret;
-}
-
-gint32 ves_icall_System_Threading_WaitHandle_WaitAll_internal(MonoArray *mono_handles, gint32 ms)
-{
- MonoError error;
- HANDLE *handles;
- guint32 numhandles;
- MonoW32HandleWaitRet ret;
- guint32 i;
- MonoObject *waitHandle;
- MonoInternalThread *thread = mono_thread_internal_current ();
-
- /* Do this WaitSleepJoin check before creating objects */
- if (mono_thread_current_check_pending_interrupt ())
- return map_native_wait_result_to_managed (MONO_W32HANDLE_WAIT_RET_FAILED, 0);
-
- /* We fail in managed if the array has more than 64 elements */
- numhandles = (guint32)mono_array_length(mono_handles);
- handles = g_new0(HANDLE, numhandles);
+ if (timeout != MONO_INFINITE_WAIT) {
+ gint64 elapsed;
- for(i = 0; i < numhandles; i++) {
- waitHandle = mono_array_get(mono_handles, MonoObject*, i);
- handles [i] = mono_wait_handle_get_handle ((MonoWaitHandle *) waitHandle);
- }
-
- if(ms== -1) {
- ms=MONO_INFINITE_WAIT;
- }
-
- mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
-
- ret = mono_wait_uninterrupted (thread, numhandles, handles, TRUE, ms, &error);
-
- mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
-
- g_free(handles);
-
- mono_error_set_pending_exception (&error);
-
- return map_native_wait_result_to_managed (ret, numhandles);
-}
-
-gint32 ves_icall_System_Threading_WaitHandle_WaitAny_internal(MonoArray *mono_handles, gint32 ms)
-{
- MonoError error;
- HANDLE handles [MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS];
- uintptr_t numhandles;
- MonoW32HandleWaitRet ret;
- guint32 i;
- MonoObject *waitHandle;
- MonoInternalThread *thread = mono_thread_internal_current ();
-
- /* Do this WaitSleepJoin check before creating objects */
- if (mono_thread_current_check_pending_interrupt ())
- return map_native_wait_result_to_managed (MONO_W32HANDLE_WAIT_RET_FAILED, 0);
-
- numhandles = mono_array_length(mono_handles);
- if (numhandles > MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS)
- return map_native_wait_result_to_managed (MONO_W32HANDLE_WAIT_RET_FAILED, 0);
+ elapsed = mono_msec_ticks () - start;
+ if (elapsed >= timeout) {
+ ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
+ break;
+ }
- for(i = 0; i < numhandles; i++) {
- waitHandle = mono_array_get(mono_handles, MonoObject*, i);
- handles [i] = mono_wait_handle_get_handle ((MonoWaitHandle *) waitHandle);
- }
-
- if(ms== -1) {
- ms=MONO_INFINITE_WAIT;
+ timeoutLeft = timeout - elapsed;
+ }
}
- mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
-
- ret = mono_wait_uninterrupted (thread, numhandles, handles, FALSE, ms, &error);
-
mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
- THREAD_WAIT_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") returning %d", __func__, mono_native_thread_id_get (), ret));
-
- mono_error_set_pending_exception (&error);
-
return map_native_wait_result_to_managed (ret, numhandles);
}
-gint32 ves_icall_System_Threading_WaitHandle_WaitOne_internal(HANDLE handle, gint32 ms)
-{
- MonoError error;
- MonoW32HandleWaitRet ret;
- MonoInternalThread *thread = mono_thread_internal_current ();
-
- THREAD_WAIT_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") waiting for %p, %d ms", __func__, mono_native_thread_id_get (), handle, ms));
-
- if(ms== -1) {
- ms=MONO_INFINITE_WAIT;
- }
-
- if (mono_thread_current_check_pending_interrupt ())
- return map_native_wait_result_to_managed (MONO_W32HANDLE_WAIT_RET_FAILED, 0);
-
- mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
-
- ret = mono_wait_uninterrupted (thread, 1, &handle, FALSE, ms, &error);
-
- mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
-
- mono_error_set_pending_exception (&error);
- return map_native_wait_result_to_managed (ret, 1);
-}
-
gint32
-ves_icall_System_Threading_WaitHandle_SignalAndWait_Internal (HANDLE toSignal, HANDLE toWait, gint32 ms)
+ves_icall_System_Threading_WaitHandle_SignalAndWait_Internal (gpointer toSignal, gpointer toWait, gint32 ms, MonoError *error)
{
MonoW32HandleWaitRet ret;
MonoInternalThread *thread = mono_thread_internal_current ();
/**
* mono_thread_current_check_pending_interrupt:
- *
* Checks if there's a interruption request and set the pending exception if so.
- *
- * @returns true if a pending exception was set
+ * \returns true if a pending exception was set
*/
gboolean
mono_thread_current_check_pending_interrupt (void)
/**
* mono_thread_internal_abort:
- *
- * Request thread @thread to be aborted.
- *
- * @thread MUST NOT be the current thread.
+ * Request thread \p thread to be aborted.
+ * \p thread MUST NOT be the current thread.
*/
void
mono_thread_internal_abort (MonoInternalThread *thread)
return found;
}
+/**
+ * mono_thread_stop:
+ */
void
mono_thread_stop (MonoThread *thread)
{
}
void
-ves_icall_System_Runtime_Remoting_Contexts_Context_RegisterContext (MonoAppContext *ctx)
+mono_threads_register_app_context (MonoAppContext *ctx, MonoError *error)
{
+ error_init (error);
mono_threads_lock ();
//g_print ("Registering context %d in domain %d\n", ctx->context_id, ctx->domain_id);
}
void
-ves_icall_System_Runtime_Remoting_Contexts_Context_ReleaseContext (MonoAppContext *ctx)
+ves_icall_System_Runtime_Remoting_Contexts_Context_RegisterContext (MonoAppContextHandle ctx, MonoError *error)
+{
+ error_init (error);
+ mono_threads_register_app_context (MONO_HANDLE_RAW (ctx), error); /* FIXME use handles in mono_threads_register_app_context */
+}
+
+void
+mono_threads_release_app_context (MonoAppContext* ctx, MonoError *error)
{
/*
* NOTE: Since finalizers are unreliable for the purposes of ensuring
mono_profiler_context_unloaded (ctx);
}
+void
+ves_icall_System_Runtime_Remoting_Contexts_Context_ReleaseContext (MonoAppContextHandle ctx, MonoError *error)
+{
+ error_init (error);
+ mono_threads_release_app_context (MONO_HANDLE_RAW (ctx), error); /* FIXME use handles in mono_threads_release_app_context */
+}
+
void mono_thread_init (MonoThreadStartCB start_cb,
MonoThreadAttachCB attach_cb)
{
mono_thread_attach_cb = attach_cb;
}
-void mono_thread_cleanup (void)
+static gpointer
+thread_attach (MonoThreadInfo *info)
+{
+ return mono_gc_thread_attach (info);
+}
+
+static void
+thread_detach (MonoThreadInfo *info)
+{
+ MonoInternalThread *internal;
+ guint32 gchandle;
+
+ /* If a delegate is passed to native code and invoked on a thread we dont
+ * know about, marshal will register it with mono_threads_attach_coop, but
+ * we have no way of knowing when that thread goes away. SGen has a TSD
+ * so we assume that if the domain is still registered, we can detach
+ * the thread */
+
+ g_assert (info);
+
+ if (!mono_thread_info_try_get_internal_thread_gchandle (info, &gchandle))
+ return;
+
+ internal = (MonoInternalThread*) mono_gchandle_get_target (gchandle);
+ g_assert (internal);
+
+ mono_gchandle_free (gchandle);
+
+ mono_thread_detach_internal (internal);
+}
+
+static void
+thread_detach_with_lock (MonoThreadInfo *info)
+{
+ return mono_gc_thread_detach_with_lock (info);
+}
+
+static gboolean
+thread_in_critical_region (MonoThreadInfo *info)
+{
+ return mono_gc_thread_in_critical_region (info);
+}
+
+static gboolean
+ip_in_critical_region (MonoDomain *domain, gpointer ip)
+{
+ MonoJitInfo *ji;
+ MonoMethod *method;
+
+ /*
+ * We pass false for 'try_aot' so this becomes async safe.
+ * It won't find aot methods whose jit info is not yet loaded,
+ * so we preload their jit info in the JIT.
+ */
+ ji = mono_jit_info_table_find_internal (domain, ip, FALSE, FALSE);
+ if (!ji)
+ return FALSE;
+
+ method = mono_jit_info_get_method (ji);
+ g_assert (method);
+
+ return mono_gc_is_critical_method (method);
+}
+
+void
+mono_thread_callbacks_init (void)
+{
+ MonoThreadInfoCallbacks cb;
+
+ memset (&cb, 0, sizeof(cb));
+ cb.thread_attach = thread_attach;
+ cb.thread_detach = thread_detach;
+ cb.thread_detach_with_lock = thread_detach_with_lock;
+ cb.ip_in_critical_region = ip_in_critical_region;
+ cb.thread_in_critical_region = thread_in_critical_region;
+ mono_thread_info_callbacks_init (&cb);
+}
+
+/**
+ * mono_thread_cleanup:
+ */
+void
+mono_thread_cleanup (void)
{
#if !defined(RUN_IN_SUBTHREAD) && !defined(HOST_WIN32)
/* The main thread must abandon any held mutexes (particularly
mono_thread_cleanup_fn = func;
}
+/**
+ * mono_thread_set_manage_callback:
+ */
void
mono_thread_set_manage_callback (MonoThread *thread, MonoThreadManageCallback func)
{
}
}
-void mono_thread_manage (void)
+/**
+ * mono_thread_manage:
+ */
+void
+mono_thread_manage (void)
{
struct wait_data wait_data;
struct wait_data *wait = &wait_data;
sf->il_offset = location->il_offset;
if (location && location->source_file) {
- MONO_OBJECT_SETREF (sf, filename, mono_string_new (domain, location->source_file));
+ MonoString *filename = mono_string_new_checked (domain, location->source_file, error);
+ if (!is_ok (error))
+ goto leave;
+ MONO_OBJECT_SETREF (sf, filename, filename);
sf->line = location->row;
sf->column = location->column;
}
gboolean
mono_threads_abort_appdomain_threads (MonoDomain *domain, int timeout)
{
-#ifdef __native_client__
- return FALSE;
-#endif
-
abort_appdomain_data user_data;
gint64 start_time;
int orig_timeout = timeout;
LOCK_THREAD (thread);
/* MonoThread::interruption_requested can only be changed with atomics */
- if (mono_thread_clear_interruption_requested (thread)) {
- /* this will consume pending APC calls */
+ if (!mono_thread_clear_interruption_requested (thread)) {
+ UNLOCK_THREAD (thread);
+ return NULL;
+ }
+
+ /* this will consume pending APC calls */
#ifdef HOST_WIN32
- WaitForSingleObjectEx (GetCurrentThread(), 0, TRUE);
+ WaitForSingleObjectEx (GetCurrentThread(), 0, TRUE);
#endif
- InterlockedDecrement (&thread_interruption_requested);
-
- /* Clear the interrupted flag of the thread so it can wait again */
- mono_thread_info_clear_self_interrupt ();
- }
+ /* Clear the interrupted flag of the thread so it can wait again */
+ mono_thread_info_clear_self_interrupt ();
/* If there's a pending exception and an AbortRequested, the pending exception takes precedence */
if (sys_thread->pending_exception) {
if (!mono_thread_set_interruption_requested (thread))
return NULL;
- InterlockedIncrement (&thread_interruption_requested);
if (!running_managed || is_running_protected_wrapper ()) {
/* Can't stop while in unmanaged code. Increase the global interruption
/*This function should be called by a thread after it has exited all of
* its handle blocks at interruption time.*/
MonoException*
-mono_thread_resume_interruption (void)
+mono_thread_resume_interruption (gboolean exec)
{
MonoInternalThread *thread = mono_thread_internal_current ();
gboolean still_aborting;
/*This can happen if the protected block called Thread::ResetAbort*/
if (!still_aborting)
- return FALSE;
+ return NULL;
if (!mono_thread_set_interruption_requested (thread))
return NULL;
- InterlockedIncrement (&thread_interruption_requested);
mono_thread_info_self_interrupt ();
- return mono_thread_execute_interruption ();
+ if (exec)
+ return mono_thread_execute_interruption ();
+ else
+ return NULL;
}
gboolean mono_thread_interruption_requested ()
/**
* mono_thread_test_and_set_state:
- *
- * Test if current state of @thread include @test. If it does not, OR @set into the state.
- *
- * Returns TRUE is @set was OR'd in.
+ * Test if current state of \p thread include \p test. If it does not, OR \p set into the state.
+ * \returns TRUE if \p set was OR'd in.
*/
gboolean
mono_thread_test_and_set_state (MonoInternalThread *thread, MonoThreadState test, MonoThreadState set)
if (!mono_thread_set_interruption_requested (thread))
return MonoResumeThread;
- InterlockedIncrement (&thread_interruption_requested);
-
ji = mono_thread_info_get_last_managed (info);
protected_wrapper = ji && !ji->is_trampoline && !ji->async && mono_threads_is_critical_method (mono_jit_info_get_method (ji));
running_managed = mono_jit_info_match (ji, MONO_CONTEXT_GET_IP (&mono_thread_info_get_suspend_state (info)->ctx));
return KeepSuspended;
}
} else {
- if (mono_thread_set_interruption_requested (thread))
- InterlockedIncrement (&thread_interruption_requested);
+ mono_thread_set_interruption_requested (thread);
if (data->interrupt)
data->interrupt_token = mono_thread_info_prepare_interrupt ((MonoThreadInfo *)thread->thread_info);
mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), FALSE, suspend_for_shutdown_critical, NULL);
}
-/*
+/**
* mono_thread_is_foreign:
- * @thread: the thread to query
+ * \param thread the thread to query
*
* This function allows one to determine if a thread was created by the mono runtime and has
- * a well defined lifecycle or it's a foreigh one, created by the native environment.
+ * a well defined lifecycle or it's a foreign one, created by the native environment.
*
- * Returns: TRUE if @thread was not created by the runtime.
+ * \returns TRUE if \p thread was not created by the runtime.
*/
mono_bool
mono_thread_is_foreign (MonoThread *thread)
/*
* mono_threads_attach_coop: called by native->managed wrappers
*
- * In non-coop mode:
- * - @dummy: is NULL
+ * - @dummy:
+ * - blocking mode: contains gc unsafe transition cookie
+ * - non-blocking mode: contains random data
* - @return: the original domain which needs to be restored, or NULL.
- *
- * In coop mode:
- * - @dummy: contains the original domain
- * - @return: a cookie containing current MonoThreadInfo*.
*/
gpointer
mono_threads_attach_coop (MonoDomain *domain, gpointer *dummy)
{
MonoDomain *orig;
- gboolean fresh_thread = FALSE;
+ MonoThreadInfo *info;
+ gboolean external;
+
+ orig = mono_domain_get ();
if (!domain) {
/* Happens when called from AOTed code which is only used in the root domain. */
domain = mono_get_root_domain ();
+ g_assert (domain);
}
- g_assert (domain);
-
/* On coop, when we detached, we moved the thread from RUNNING->BLOCKING.
* If we try to reattach we do a BLOCKING->RUNNING transition. If the thread
* is fresh, mono_thread_attach() will do a STARTING->RUNNING transition so
* we're only responsible for making the cookie. */
- if (mono_threads_is_coop_enabled ()) {
- MonoThreadInfo *info = mono_thread_info_current_unchecked ();
- fresh_thread = !info || !mono_thread_info_is_live (info);
- }
+ if (mono_threads_is_blocking_transition_enabled ())
+ external = !(info = mono_thread_info_current_unchecked ()) || !mono_thread_info_is_live (info);
if (!mono_thread_internal_current ()) {
mono_thread_attach_full (domain, FALSE);
mono_thread_set_state (mono_thread_internal_current (), ThreadState_Background);
}
- orig = mono_domain_get ();
if (orig != domain)
mono_domain_set (domain, TRUE);
- if (!mono_threads_is_coop_enabled ())
- return orig != domain ? orig : NULL;
-
- if (fresh_thread) {
- *dummy = NULL;
- /* mono_thread_attach put the thread in RUNNING mode from STARTING, but we need to
- * return the right cookie. */
- return mono_threads_enter_gc_unsafe_region_cookie ();
- } else {
- *dummy = orig;
- /* thread state (BLOCKING|RUNNING) -> RUNNING */
- return mono_threads_enter_gc_unsafe_region (dummy);
+ if (mono_threads_is_blocking_transition_enabled ()) {
+ if (external) {
+ /* mono_thread_attach put the thread in RUNNING mode from STARTING, but we need to
+ * return the right cookie. */
+ *dummy = mono_threads_enter_gc_unsafe_region_cookie ();
+ } else {
+ /* thread state (BLOCKING|RUNNING) -> RUNNING */
+ *dummy = mono_threads_enter_gc_unsafe_region (dummy);
+ }
}
+
+ return orig;
}
/*
* mono_threads_detach_coop: called by native->managed wrappers
*
- * In non-coop mode:
* - @cookie: the original domain which needs to be restored, or NULL.
- * - @dummy: is NULL
- *
- * In coop mode:
- * - @cookie: contains current MonoThreadInfo* if it was in BLOCKING mode, NULL otherwise
- * - @dummy: contains the original domain
+ * - @dummy:
+ * - blocking mode: contains gc unsafe transition cookie
+ * - non-blocking mode: contains random data
*/
void
mono_threads_detach_coop (gpointer cookie, gpointer *dummy)
{
MonoDomain *domain, *orig;
- if (!mono_threads_is_coop_enabled ()) {
- orig = (MonoDomain*) cookie;
- if (orig)
- mono_domain_set (orig, TRUE);
- } else {
- orig = (MonoDomain*) *dummy;
+ orig = (MonoDomain*) cookie;
- domain = mono_domain_get ();
- g_assert (domain);
+ domain = mono_domain_get ();
+ g_assert (domain);
+ if (mono_threads_is_blocking_transition_enabled ()) {
/* it won't do anything if cookie is NULL
* thread state RUNNING -> (RUNNING|BLOCKING) */
- mono_threads_exit_gc_unsafe_region (cookie, dummy);
-
- if (orig != domain) {
- if (!orig)
- mono_domain_unset ();
- else
- mono_domain_set (orig, TRUE);
- }
+ mono_threads_exit_gc_unsafe_region (*dummy, dummy);
}
-}
-
-MonoException*
-mono_thread_try_resume_interruption (void)
-{
- MonoInternalThread *thread;
-
- thread = mono_thread_internal_current ();
- if (!mono_get_eh_callbacks ()->mono_above_abort_threshold ())
- return NULL;
- if (mono_thread_get_abort_prot_block_count (thread) > 0 || mono_get_eh_callbacks ()->mono_current_thread_has_handle_block_guard ())
- return NULL;
- return mono_thread_resume_interruption ();
+ if (orig != domain) {
+ if (!orig)
+ mono_domain_unset ();
+ else
+ mono_domain_set (orig, TRUE);
+ }
}
#if 0