-/*
- * threads.c: Thread support internal calls
+/**
+ * \file
+ * Thread support internal calls
*
* Author:
* Dick Porter (dick@ximian.com)
#include <mono/metadata/marshal.h>
#include <mono/metadata/runtime.h>
#include <mono/metadata/object-internals.h>
-#include <mono/metadata/mono-debug-debugger.h>
+#include <mono/metadata/debug-internals.h>
#include <mono/utils/monobitset.h>
#include <mono/utils/mono-compiler.h>
#include <mono/utils/mono-mmap.h>
#include <mono/utils/mono-error-internals.h>
#include <mono/utils/os-event.h>
#include <mono/utils/mono-threads-debug.h>
+#include <mono/utils/unlocked.h>
#include <mono/metadata/w32handle.h>
#include <mono/metadata/w32event.h>
#include <mono/metadata/w32mutex.h>
#include <objbase.h>
#endif
-#if defined(PLATFORM_ANDROID) && !defined(TARGET_ARM64) && !defined(TARGET_AMD64)
+#if defined(HOST_ANDROID) && !defined(TARGET_ARM64) && !defined(TARGET_AMD64)
#define USE_TKILL_ON_ANDROID 1
#endif
-#ifdef PLATFORM_ANDROID
+#ifdef HOST_ANDROID
#include <errno.h>
#ifdef USE_TKILL_ON_ANDROID
StaticDataFreeList *freelist;
} StaticDataInfo;
-/* Number of cached culture objects in the MonoThread->cached_culture_info array
- * (per-type): we use the first NUM entries for CultureInfo and the last for
- * UICultureInfo. So the size of the array is really NUM_CACHED_CULTURES * 2.
- */
-#define NUM_CACHED_CULTURES 4
-#define CULTURES_START_IDX 0
-#define UICULTURES_START_IDX NUM_CACHED_CULTURES
-
/* Controls access to the 'threads' hash table */
static void mono_threads_lock (void);
static void mono_threads_unlock (void);
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;
/* Contains tids */
/* Protected by the threads lock */
static GHashTable *joinable_threads;
-static int joinable_thread_count;
+static gint32 joinable_thread_count;
#define SET_CURRENT_OBJECT(x) mono_tls_set_thread (x)
#define GET_CURRENT_OBJECT() (MonoInternalThread*) mono_tls_get_thread ()
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)) {
- if (old_state & INTERRUPT_REQUESTED_BIT)
- printf ("begin prot happy as it demoted interrupt to deferred interrupt\n");
- 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) {
- printf ("end abort on alert, promoted deferred to pront interrupt\n");
- 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) {
- printf ("set interrupt unhappy, as it's only putting a deferred req %d\n", 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
#define SPECIAL_STATIC_OFFSET_TYPE_THREAD 0
#define SPECIAL_STATIC_OFFSET_TYPE_CONTEXT 1
-#define MAKE_SPECIAL_STATIC_OFFSET(index, offset, type) \
- ((SpecialStaticOffset) { .fields = { (index), (offset), (type) } }.raw)
+#define MAKE_SPECIAL_STATIC_OFFSET(idx, off, ty) \
+ ((SpecialStaticOffset) { .fields = { .index = (idx), .offset = (off), .type = (ty) } }.raw)
#define ACCESS_SPECIAL_STATIC_OFFSET(x,f) \
(((SpecialStaticOffset *) &(x))->fields.f)
mono_alloc_static_data (gpointer **static_data_ptr, guint32 offset, gboolean threadlocal);
static gboolean
-mono_thread_attach_internal (MonoThread *thread, gboolean force_attach, gboolean force_domain, gsize *stack_ptr)
+mono_thread_attach_internal (MonoThread *thread, gboolean force_attach, gboolean force_domain)
{
MonoThreadInfo *info;
MonoInternalThread *internal;
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 ());
internal->tid = MONO_NATIVE_THREAD_ID_TO_UINT (mono_native_thread_id_get ());
internal->thread_info = info;
internal->small_id = info->small_id;
- internal->stack_ptr = stack_ptr;
THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Setting current_object_key to %p", __func__, mono_native_thread_id_get (), internal));
if (shutting_down && !force_attach) {
mono_threads_unlock ();
+ mono_thread_pop_appdomain_ref ();
return FALSE;
}
return TRUE;
}
+static 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));
+
+#ifndef HOST_WIN32
+ mono_w32mutex_abandon ();
+#endif
+
+ if (thread->abort_state_handle) {
+ mono_gchandle_free (thread->abort_state_handle);
+ thread->abort_state_handle = 0;
+ }
+
+ thread->abort_exc = NULL;
+ thread->current_appcontext = NULL;
+
+ /*
+ * thread->synch_cs can be NULL if this was called after
+ * ves_icall_System_Threading_InternalThread_Thread_free_internal.
+ * This can happen only during shutdown.
+ * The shutting_down flag is not always set, so we can't assert on it.
+ */
+ if (thread->synch_cs)
+ LOCK_THREAD (thread);
+
+ thread->state |= ThreadState_Stopped;
+ thread->state &= ~ThreadState_Background;
+
+ if (thread->synch_cs)
+ UNLOCK_THREAD (thread);
+
+ /*
+ An interruption request has leaked to cleanup. Adjust the global counter.
+
+ This can happen is the abort source thread finds the abortee (this) thread
+ in unmanaged code. If this thread never trips back to managed code or check
+ the local flag it will be left set and positively unbalance the global counter.
+
+ Leaving the counter unbalanced will cause a performance degradation since all threads
+ will now keep checking their local flags all the time.
+ */
+ mono_thread_clear_interruption_requested (thread);
+
+ mono_threads_lock ();
+
+ if (!threads) {
+ removed = FALSE;
+ } else if (mono_g_hash_table_lookup (threads, (gpointer)thread->tid) != thread) {
+ /* We have to check whether the thread object for the
+ * tid is still the same in the table because the
+ * thread might have been destroyed and the tid reused
+ * in the meantime, in which case the tid would be in
+ * the table, but with another thread object.
+ */
+ removed = FALSE;
+ } else {
+ mono_g_hash_table_remove (threads, (gpointer)thread->tid);
+ removed = TRUE;
+ }
+
+ mono_threads_unlock ();
+
+ /* Don't close the handle here, wait for the object finalizer
+ * to do it. Otherwise, the following race condition applies:
+ *
+ * 1) Thread exits (and mono_thread_detach_internal() closes the handle)
+ *
+ * 2) Some other handle is reassigned the same slot
+ *
+ * 3) Another thread tries to join the first thread, and
+ * blocks waiting for the reassigned handle to be signalled
+ * (which might never happen). This is possible, because the
+ * thread calling Join() still has a reference to the first
+ * thread's object.
+ */
+
+ /* if the thread is not in the hash it has been removed already */
+ if (!removed) {
+ mono_domain_unset ();
+ mono_memory_barrier ();
+
+ if (mono_thread_cleanup_fn)
+ mono_thread_cleanup_fn (thread_get_tid (thread));
+
+ goto done;
+ }
+
+ mono_release_type_locks (thread);
+
+ /* Can happen when we attach the profiler helper thread in order to heapshot. */
+ if (!mono_thread_info_lookup (MONO_UINT_TO_NATIVE_THREAD_ID (thread->tid))->tools_thread)
+ MONO_PROFILER_RAISE (thread_stopped, (thread->tid));
+
+ mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
+
+ /*
+ * This will signal async signal handlers that the thread has exited.
+ * The profiler callback needs this to be set, so it cannot be done earlier.
+ */
+ mono_domain_unset ();
+ mono_memory_barrier ();
+
+ if (thread == mono_thread_internal_current ())
+ mono_thread_pop_appdomain_ref ();
+
+ mono_free_static_data (thread->static_data);
+ thread->static_data = NULL;
+ ref_stack_destroy (thread->appdomain_refs);
+ thread->appdomain_refs = NULL;
+
+ g_assert (thread->suspended);
+ mono_os_event_destroy (thread->suspended);
+ g_free (thread->suspended);
+ thread->suspended = NULL;
+
+ if (mono_thread_cleanup_fn)
+ mono_thread_cleanup_fn (thread_get_tid (thread));
+
+ mono_memory_barrier ();
+
+ if (mono_gc_is_moving ()) {
+ MONO_GC_UNREGISTER_ROOT (thread->thread_pinning_ref);
+ thread->thread_pinning_ref = NULL;
+ }
+
+done:
+ SET_CURRENT_OBJECT (NULL);
+ mono_domain_unset ();
+
+ mono_thread_info_unset_internal_thread_gchandle ((MonoThreadInfo*) thread->thread_info);
+
+ /* Don't need to close the handle to this thread, even though we took a
+ * reference in mono_thread_attach (), because the GC will do it
+ * when the Thread object is finalised.
+ */
+}
+
typedef struct {
gint32 ref;
MonoThread *thread;
MonoObject *start_delegate_arg;
MonoThreadStart start_func;
gpointer start_func_arg;
+ gboolean force_attach;
gboolean failed;
MonoCoopSem registered;
} StartInfo;
THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Start wrapper", __func__, mono_native_thread_id_get ()));
- if (!mono_thread_attach_internal (thread, FALSE, FALSE, stack_ptr)) {
+ if (!mono_thread_attach_internal (thread, start_info->force_attach, FALSE)) {
start_info->failed = TRUE;
mono_coop_sem_post (&start_info->registered);
* to lock the thread, and the lock is held by thread_start () which waits for
* start_notify.
*/
- mono_profiler_thread_start (tid);
+ MONO_PROFILER_RAISE (thread_started, (tid));
/* if the name was set before starting, we didn't invoke the profiler callback */
if (internal->name) {
char *tname = g_utf16_to_utf8 (internal->name, internal->name_len, NULL, NULL, NULL);
- mono_profiler_thread_name (internal->tid, tname);
+ MONO_PROFILER_RAISE (thread_name, (internal->tid, tname));
mono_native_thread_set_name (MONO_UINT_TO_NATIVE_THREAD_ID (internal->tid), tname);
g_free (tname);
}
mono_thread_detach_internal (internal);
- internal->tid = 0;
-
return(0);
}
-static gsize WINAPI start_wrapper(void *data)
+static gsize WINAPI
+start_wrapper (gpointer data)
{
- volatile gsize dummy;
+ StartInfo *start_info;
+ MonoThreadInfo *info;
+ gsize res;
+
+ start_info = (StartInfo*) data;
+ g_assert (start_info);
- return start_wrapper_internal ((StartInfo*) data, (gsize*) &dummy);
+ info = mono_thread_info_attach ();
+ info->runtime_thread = TRUE;
+
+ /* Run the actual main function of the thread */
+ res = start_wrapper_internal (start_info, info->stack_end);
+
+ mono_thread_info_exit (res);
+
+ g_assert_not_reached ();
}
/*
*/
static gboolean
create_thread (MonoThread *thread, MonoInternalThread *internal, MonoObject *start_delegate, MonoThreadStart start_func, gpointer start_func_arg,
- gboolean threadpool_thread, guint32 stack_size, MonoError *error)
+ MonoThreadCreateFlags flags, MonoError *error)
{
StartInfo *start_info = NULL;
- MonoThreadHandle *thread_handle;
MonoNativeThreadId tid;
gboolean ret;
gsize stack_set_size;
if (start_func)
g_assert (!start_delegate);
+ if (flags & MONO_THREAD_CREATE_FLAGS_THREADPOOL) {
+ g_assert (!(flags & MONO_THREAD_CREATE_FLAGS_DEBUGGER));
+ g_assert (!(flags & MONO_THREAD_CREATE_FLAGS_FORCE_CREATE));
+ }
+ if (flags & MONO_THREAD_CREATE_FLAGS_DEBUGGER) {
+ g_assert (!(flags & MONO_THREAD_CREATE_FLAGS_THREADPOOL));
+ g_assert (!(flags & MONO_THREAD_CREATE_FLAGS_FORCE_CREATE));
+ }
+
/*
* Join joinable threads to prevent running out of threads since the finalizer
* thread might be blocked/backlogged.
*/
mono_threads_join_threads ();
- mono_error_init (error);
+ error_init (error);
mono_threads_lock ();
- if (shutting_down) {
+ if (shutting_down && !(flags & MONO_THREAD_CREATE_FLAGS_FORCE_CREATE)) {
mono_threads_unlock ();
return FALSE;
}
mono_g_hash_table_insert (threads_starting_up, thread, thread);
mono_threads_unlock ();
- internal->threadpool_thread = threadpool_thread;
- if (threadpool_thread)
+ internal->threadpool_thread = flags & MONO_THREAD_CREATE_FLAGS_THREADPOOL;
+ if (internal->threadpool_thread)
mono_thread_set_state (internal, ThreadState_Background);
+ internal->debugger_thread = flags & MONO_THREAD_CREATE_FLAGS_DEBUGGER;
+
start_info = g_new0 (StartInfo, 1);
start_info->ref = 2;
start_info->thread = thread;
start_info->start_delegate_arg = thread->start_obj;
start_info->start_func = start_func;
start_info->start_func_arg = start_func_arg;
+ start_info->force_attach = flags & MONO_THREAD_CREATE_FLAGS_FORCE_CREATE;
start_info->failed = FALSE;
mono_coop_sem_init (&start_info->registered, 0);
- if (stack_size == 0)
+ if (flags != MONO_THREAD_CREATE_FLAGS_SMALL_STACK)
stack_set_size = default_stacksize_for_thread (internal);
else
stack_set_size = 0;
- thread_handle = mono_threads_create_thread (start_wrapper, start_info, &stack_set_size, &tid);
-
- if (thread_handle == NULL) {
+ if (!mono_thread_platform_create_thread (start_wrapper, start_info, &stack_set_size, &tid)) {
/* The thread couldn't be created, so set an exception */
mono_threads_lock ();
mono_g_hash_table_remove (threads_starting_up, thread);
mono_coop_sem_wait (&start_info->registered, MONO_SEM_FLAGS_NONE);
- mono_threads_close_thread_handle (thread_handle);
-
THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Done launching thread %p (%"G_GSIZE_FORMAT")", __func__, mono_native_thread_id_get (), internal, (gsize)internal->tid));
ret = !start_info->failed;
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;
}
* ARG should not be a GC reference.
*/
MonoInternalThread*
-mono_thread_create_internal (MonoDomain *domain, gpointer func, gpointer arg, gboolean threadpool_thread, guint32 stack_size, MonoError *error)
+mono_thread_create_internal (MonoDomain *domain, gpointer func, gpointer arg, MonoThreadCreateFlags flags, MonoError *error)
{
MonoThread *thread;
MonoInternalThread *internal;
gboolean res;
- mono_error_init (error);
+ error_init (error);
internal = create_internal_thread_object ();
LOCK_THREAD (internal);
- res = create_thread (thread, internal, NULL, (MonoThreadStart) func, arg, threadpool_thread, stack_size, error);
- return_val_if_nok (error, NULL);
+ res = create_thread (thread, internal, NULL, (MonoThreadStart) func, arg, flags, error);
UNLOCK_THREAD (internal);
+ return_val_if_nok (error, NULL);
return internal;
}
+/**
+ * mono_thread_create:
+ */
void
mono_thread_create (MonoDomain *domain, gpointer func, gpointer arg)
{
gboolean
mono_thread_create_checked (MonoDomain *domain, gpointer func, gpointer arg, MonoError *error)
{
- return (NULL != mono_thread_create_internal (domain, func, arg, FALSE, 0, error));
-}
-
-MonoThread *
-mono_thread_attach (MonoDomain *domain)
-{
- MonoThread *thread = mono_thread_attach_full (domain, FALSE);
-
- return thread;
+ return (NULL != mono_thread_create_internal (domain, func, arg, MONO_THREAD_CREATE_FLAGS_NONE, error));
}
-MonoThread *
+static MonoThread *
mono_thread_attach_full (MonoDomain *domain, gboolean force_attach)
{
MonoInternalThread *internal;
MonoThread *thread;
+ MonoThreadInfo *info;
MonoNativeThreadId tid;
- gsize stack_ptr;
if (mono_thread_internal_current_is_attached ()) {
if (domain != mono_domain_get ())
return mono_thread_current ();
}
- if (!mono_gc_register_thread (&domain)) {
- g_error ("Thread %"G_GSIZE_FORMAT" calling into managed code is not registered with the GC. On UNIX, this can be fixed by #include-ing <gc.h> before <pthread.h> in the file containing the thread creation code.", mono_native_thread_id_get ());
- }
+ info = mono_thread_info_attach ();
+ g_assert (info);
tid=mono_native_thread_id_get ();
thread = create_thread_object (domain, internal);
- if (!mono_thread_attach_internal (thread, force_attach, TRUE, &stack_ptr)) {
+ if (!mono_thread_attach_internal (thread, force_attach, TRUE)) {
/* Mono is shutting down, so just wait for the end */
for (;;)
mono_thread_info_sleep (10000, NULL);
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)
- // FIXME: Need a separate callback
- mono_profiler_thread_start (MONO_NATIVE_THREAD_ID_TO_UINT (tid));
+ MONO_PROFILER_RAISE (thread_started, (MONO_NATIVE_THREAD_ID_TO_UINT (tid)));
return thread;
}
-void
-mono_thread_detach_internal (MonoInternalThread *thread)
+/**
+ * mono_thread_attach:
+ */
+MonoThread *
+mono_thread_attach (MonoDomain *domain)
{
- gboolean removed;
-
- g_assert (thread != NULL);
-
- THREAD_DEBUG (g_message ("%s: mono_thread_detach for %p (%"G_GSIZE_FORMAT")", __func__, thread, (gsize)thread->tid));
-
-#ifndef HOST_WIN32
- mono_w32mutex_abandon ();
-#endif
-
- if (thread->abort_state_handle) {
- mono_gchandle_free (thread->abort_state_handle);
- thread->abort_state_handle = 0;
- }
-
- thread->abort_exc = NULL;
- thread->current_appcontext = NULL;
-
- /*
- * This is necessary because otherwise we might have
- * cross-domain references which will not get cleaned up when
- * the target domain is unloaded.
- */
- if (thread->cached_culture_info) {
- int i;
- for (i = 0; i < NUM_CACHED_CULTURES * 2; ++i)
- mono_array_set (thread->cached_culture_info, MonoObject*, i, NULL);
- }
-
- /*
- * thread->synch_cs can be NULL if this was called after
- * ves_icall_System_Threading_InternalThread_Thread_free_internal.
- * This can happen only during shutdown.
- * The shutting_down flag is not always set, so we can't assert on it.
- */
- if (thread->synch_cs)
- LOCK_THREAD (thread);
-
- thread->state |= ThreadState_Stopped;
- thread->state &= ~ThreadState_Background;
-
- if (thread->synch_cs)
- UNLOCK_THREAD (thread);
-
- /*
- An interruption request has leaked to cleanup. Adjust the global counter.
-
- This can happen is the abort source thread finds the abortee (this) thread
- in unmanaged code. If this thread never trips back to managed code or check
- the local flag it will be left set and positively unbalance the global counter.
-
- 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_threads_lock ();
-
- if (!threads) {
- removed = FALSE;
- } else if (mono_g_hash_table_lookup (threads, (gpointer)thread->tid) != thread) {
- /* We have to check whether the thread object for the
- * tid is still the same in the table because the
- * thread might have been destroyed and the tid reused
- * in the meantime, in which case the tid would be in
- * the table, but with another thread object.
- */
- removed = FALSE;
- } else {
- mono_g_hash_table_remove (threads, (gpointer)thread->tid);
- removed = TRUE;
- }
-
- mono_threads_unlock ();
-
- /* Don't close the handle here, wait for the object finalizer
- * to do it. Otherwise, the following race condition applies:
- *
- * 1) Thread exits (and mono_thread_detach_internal() closes the handle)
- *
- * 2) Some other handle is reassigned the same slot
- *
- * 3) Another thread tries to join the first thread, and
- * blocks waiting for the reassigned handle to be signalled
- * (which might never happen). This is possible, because the
- * thread calling Join() still has a reference to the first
- * thread's object.
- */
-
- /* if the thread is not in the hash it has been removed already */
- if (!removed) {
- mono_domain_unset ();
- mono_memory_barrier ();
-
- if (mono_thread_cleanup_fn)
- mono_thread_cleanup_fn (thread_get_tid (thread));
-
- goto done;
- }
-
- mono_release_type_locks (thread);
-
- /* Can happen when we attach the profiler helper thread in order to heapshot. */
- if (!mono_thread_info_lookup (MONO_UINT_TO_NATIVE_THREAD_ID (thread->tid))->tools_thread)
- mono_profiler_thread_end (thread->tid);
-
- mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
-
- /*
- * This will signal async signal handlers that the thread has exited.
- * The profiler callback needs this to be set, so it cannot be done earlier.
- */
- mono_domain_unset ();
- mono_memory_barrier ();
-
- if (thread == mono_thread_internal_current ())
- mono_thread_pop_appdomain_ref ();
-
- thread->cached_culture_info = NULL;
-
- mono_free_static_data (thread->static_data);
- thread->static_data = NULL;
- ref_stack_destroy (thread->appdomain_refs);
- thread->appdomain_refs = NULL;
-
- g_assert (thread->suspended);
- mono_os_event_destroy (thread->suspended);
- g_free (thread->suspended);
- thread->suspended = NULL;
-
- if (mono_thread_cleanup_fn)
- mono_thread_cleanup_fn (thread_get_tid (thread));
-
- mono_memory_barrier ();
-
- if (mono_gc_is_moving ()) {
- MONO_GC_UNREGISTER_ROOT (thread->thread_pinning_ref);
- thread->thread_pinning_ref = NULL;
- }
-
-done:
- SET_CURRENT_OBJECT (NULL);
- mono_domain_unset ();
-
- /* 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.
- */
+ return mono_thread_attach_full (domain, FALSE);
}
+/**
+ * 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 this_obj;
}
- res = create_thread (this_obj, internal, start, NULL, NULL, FALSE, 0, &error);
+ res = create_thread (this_obj, internal, start, NULL, NULL, MONO_THREAD_CREATE_FLAGS_NONE, &error);
if (!res) {
mono_error_cleanup (&error);
UNLOCK_THREAD (internal);
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)
MonoError error;
MonoString* str;
- mono_error_init (&error);
+ error_init (&error);
LOCK_THREAD (this_obj);
{
LOCK_THREAD (this_obj);
- mono_error_init (error);
+ error_init (error);
if (reset) {
this_obj->flags &= ~MONO_THREAD_FLAG_NAME_SET;
if (this_obj->name && this_obj->tid) {
char *tname = mono_string_to_utf8_checked (name, error);
return_if_nok (error);
- mono_profiler_thread_name (this_obj->tid, tname);
+ MONO_PROFILER_RAISE (thread_name, (this_obj->tid, tname));
mono_native_thread_set_name (thread_get_tid (this_obj), tname);
mono_free (tname);
}
{
MonoArray *copy;
- mono_error_init (error);
+ error_init (error);
if (!arr)
return NULL;
return result;
}
+/**
+ * mono_thread_current:
+ */
MonoThread *
mono_thread_current (void)
{
gint32 diff_ms;
gint32 wait = ms;
- mono_error_init (error);
+ error_init (error);
start = (ms == -1) ? 0 : mono_msec_ticks ();
for (;;) {
}
gboolean
-ves_icall_System_Threading_Thread_Join_internal(MonoThread *this_obj, int ms)
+ves_icall_System_Threading_Thread_Join_internal (MonoThread *this_obj, int ms)
{
MonoInternalThread *thread = this_obj->internal_thread;
MonoThreadHandle *handle = thread->handle;
return FALSE;
}
+ MonoNativeThreadId tid = thread_get_tid (thread);
+
UNLOCK_THREAD (thread);
- if(ms== -1) {
- ms=MONO_INFINITE_WAIT;
- }
+ if (ms == -1)
+ ms = MONO_INFINITE_WAIT;
THREAD_DEBUG (g_message ("%s: joining thread handle %p, %d ms", __func__, handle, ms));
-
+
mono_thread_set_state (cur_thread, ThreadState_WaitSleepJoin);
- ret=mono_join_uninterrupted (handle, ms, &error);
+ ret = mono_join_uninterrupted (handle, ms, &error);
mono_thread_clr_state (cur_thread, ThreadState_WaitSleepJoin);
mono_error_set_pending_exception (&error);
- if(ret==MONO_THREAD_INFO_WAIT_RET_SUCCESS_0) {
+ if (ret == MONO_THREAD_INFO_WAIT_RET_SUCCESS_0) {
THREAD_DEBUG (g_message ("%s: join successful", __func__));
- return(TRUE);
+#ifdef HOST_WIN32
+ /* TODO: Do this on Unix platforms as well. See PR #5454 for context. */
+ /* Wait for the thread to really exit */
+ MONO_ENTER_GC_SAFE;
+ /* This shouldn't block */
+ mono_threads_join_lock ();
+ mono_native_thread_join (tid);
+ mono_threads_join_unlock ();
+ MONO_EXIT_GC_SAFE;
+#endif
+
+ return TRUE;
}
THREAD_DEBUG (g_message ("%s: join failed", __func__));
- return(FALSE);
+ return FALSE;
}
#define MANAGED_WAIT_FAILED 0x7fffffff
}
}
-static MonoW32HandleWaitRet
-mono_wait_uninterrupted (MonoInternalThread *thread, guint32 numhandles, gpointer *handles, gboolean waitall, gint32 ms, MonoError *error)
-{
- MonoException *exc;
- MonoW32HandleWaitRet ret;
- gint64 start;
- gint32 diff_ms;
- gint32 wait = ms;
-
- mono_error_init (error);
-
- start = (ms == -1) ? 0 : mono_100ns_ticks ();
- do {
- MONO_ENTER_GC_SAFE;
-#ifdef HOST_WIN32
- if (numhandles != 1)
- ret = mono_w32handle_convert_wait_ret (WaitForMultipleObjectsEx (numhandles, handles, waitall, wait, TRUE), numhandles);
- else
- ret = mono_w32handle_convert_wait_ret (WaitForSingleObjectEx (handles [0], ms, TRUE), 1);
-#else
- /* mono_w32handle_wait_multiple optimizes the case for numhandles == 1 */
- ret = mono_w32handle_wait_multiple (handles, numhandles, waitall, wait, TRUE);
-#endif /* HOST_WIN32 */
- MONO_EXIT_GC_SAFE;
-
- if (ret != MONO_W32HANDLE_WAIT_RET_ALERTED)
- break;
-
- exc = mono_thread_execute_interruption ();
- if (exc) {
- mono_error_set_exception_instance (error, exc);
- 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);
-
- 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)
+gint32
+ves_icall_System_Threading_WaitHandle_Wait_internal (gpointer *handles, gint32 numhandles, MonoBoolean waitall, gint32 timeout, MonoError *error)
{
- MonoError error;
- HANDLE handles [MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS];
- uintptr_t numhandles;
MonoW32HandleWaitRet ret;
- guint32 i;
- MonoObject *waitHandle;
- MonoInternalThread *thread = mono_thread_internal_current ();
+ MonoInternalThread *thread;
+ MonoException *exc;
+ gint64 start;
+ guint32 timeoutLeft;
/* Do this WaitSleepJoin check before creating objects */
if (mono_thread_current_check_pending_interrupt ())
return map_native_wait_result_to_managed (MONO_W32HANDLE_WAIT_RET_FAILED, 0);
- 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);
-
- 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;
- }
+ thread = mono_thread_internal_current ();
mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
- ret = mono_wait_uninterrupted (thread, numhandles, handles, FALSE, ms, &error);
+ if (timeout == -1)
+ timeout = MONO_INFINITE_WAIT;
+ if (timeout != MONO_INFINITE_WAIT)
+ start = mono_msec_ticks ();
- mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
+ timeoutLeft = timeout;
- THREAD_WAIT_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") returning %d", __func__, mono_native_thread_id_get (), ret));
+ for (;;) {
+ MONO_ENTER_GC_SAFE;
+#ifdef HOST_WIN32
+ if (numhandles != 1)
+ ret = mono_w32handle_convert_wait_ret (WaitForMultipleObjectsEx (numhandles, handles, waitall, timeoutLeft, TRUE), numhandles);
+ else
+ 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, timeoutLeft, TRUE);
+#endif /* HOST_WIN32 */
+ MONO_EXIT_GC_SAFE;
- mono_error_set_pending_exception (&error);
+ if (ret != MONO_W32HANDLE_WAIT_RET_ALERTED)
+ break;
- return map_native_wait_result_to_managed (ret, numhandles);
-}
+ exc = mono_thread_execute_interruption ();
+ if (exc) {
+ mono_error_set_exception_instance (error, exc);
+ break;
+ }
-gint32 ves_icall_System_Threading_WaitHandle_WaitOne_internal(HANDLE handle, gint32 ms)
-{
- MonoError error;
- MonoW32HandleWaitRet ret;
- MonoInternalThread *thread = mono_thread_internal_current ();
+ if (timeout != MONO_INFINITE_WAIT) {
+ gint64 elapsed;
- 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;
+ elapsed = mono_msec_ticks () - start;
+ if (elapsed >= timeout) {
+ ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
+ break;
+ }
+
+ timeoutLeft = timeout - elapsed;
+ }
}
-
- 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);
+ return map_native_wait_result_to_managed (ret, numhandles);
}
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)
{
LOCK_THREAD (thread);
- if ((thread->state & ThreadState_AbortRequested) != 0 ||
- (thread->state & ThreadState_StopRequested) != 0 ||
- (thread->state & ThreadState_Stopped) != 0)
+ if (thread->state & (ThreadState_AbortRequested | ThreadState_Stopped))
{
UNLOCK_THREAD (thread);
return FALSE;
/**
* 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)
{
LOCK_THREAD (thread);
- if ((thread->state & ThreadState_Unstarted) != 0 ||
- (thread->state & ThreadState_Aborted) != 0 ||
- (thread->state & ThreadState_Stopped) != 0)
+ if (thread->state & (ThreadState_Unstarted | ThreadState_Aborted | ThreadState_Stopped))
{
UNLOCK_THREAD (thread);
return FALSE;
}
- if ((thread->state & ThreadState_Suspended) != 0 ||
- (thread->state & ThreadState_SuspendRequested) != 0 ||
- (thread->state & ThreadState_StopRequested) != 0)
+ if (thread->state & (ThreadState_Suspended | ThreadState_SuspendRequested | ThreadState_AbortRequested))
{
UNLOCK_THREAD (thread);
return TRUE;
return found;
}
-static gboolean
-request_thread_stop (MonoInternalThread *thread)
-{
- LOCK_THREAD (thread);
-
- if ((thread->state & ThreadState_StopRequested) != 0 ||
- (thread->state & ThreadState_Stopped) != 0)
- {
- UNLOCK_THREAD (thread);
- return FALSE;
- }
-
- /* Make sure the thread is awake */
- mono_thread_resume (thread);
-
- thread->state |= ThreadState_StopRequested;
- thread->state &= ~ThreadState_AbortRequested;
-
- UNLOCK_THREAD (thread);
- return TRUE;
-}
-
/**
- * mono_thread_internal_stop:
- *
- * Request thread @thread to stop.
- *
- * @thread MUST NOT be the current thread.
+ * mono_thread_stop:
*/
void
-mono_thread_internal_stop (MonoInternalThread *thread)
-{
- g_assert (thread != mono_thread_internal_current ());
-
- if (!request_thread_stop (thread))
- return;
-
- async_abort_internal (thread, TRUE);
-}
-
-void mono_thread_stop (MonoThread *thread)
+mono_thread_stop (MonoThread *thread)
{
MonoInternalThread *internal = thread->internal_thread;
- if (!request_thread_stop (internal))
+ if (!request_thread_abort (internal, NULL))
return;
-
+
if (internal == mono_thread_internal_current ()) {
MonoError error;
self_abort_internal (&error);
}
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);
mono_threads_unlock ();
- mono_profiler_context_loaded (ctx);
+ MONO_PROFILER_RAISE (context_loaded, (ctx));
+}
+
+void
+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
-ves_icall_System_Runtime_Remoting_Contexts_Context_ReleaseContext (MonoAppContext *ctx)
+mono_threads_release_app_context (MonoAppContext* ctx, MonoError *error)
{
/*
* NOTE: Since finalizers are unreliable for the purposes of ensuring
//g_print ("Releasing context %d in domain %d\n", ctx->context_id, ctx->domain_id);
- mono_profiler_context_unloaded (ctx);
+ MONO_PROFILER_RAISE (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,
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)
{
+ mono_threads_join_threads ();
+
#if !defined(RUN_IN_SUBTHREAD) && !defined(HOST_WIN32)
/* The main thread must abandon any held mutexes (particularly
* important for named mutexes as they are shared across
mono_thread_cleanup_fn = func;
}
+/**
+ * mono_thread_set_manage_callback:
+ */
void
mono_thread_set_manage_callback (MonoThread *thread, MonoThreadManageCallback func)
{
if (wait->num >= MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS)
return FALSE;
- /* The finalizer thread is not a background thread */
- if (!mono_native_thread_id_equals (thread_get_tid (thread), self)
- && (thread->state & ThreadState_Background) != 0
- && (thread->flags & MONO_THREAD_FLAG_DONT_MANAGE) == 0
- ) {
+ if (mono_native_thread_id_equals (thread_get_tid (thread), self))
+ return FALSE;
+ if (mono_gc_is_finalizer_internal_thread (thread))
+ return FALSE;
+
+ if ((thread->state & ThreadState_Background) && !(thread->flags & MONO_THREAD_FLAG_DONT_MANAGE)) {
wait->handles[wait->num] = mono_threads_open_thread_handle (thread->handle);
wait->threads[wait->num] = thread;
wait->num++;
THREAD_DEBUG (g_print ("%s: Aborting id: %"G_GSIZE_FORMAT"\n", __func__, (gsize)thread->tid));
mono_thread_internal_abort (thread);
- return TRUE;
}
- return !mono_native_thread_id_equals (thread_get_tid (thread), self)
- && !mono_gc_is_finalizer_internal_thread (thread);
+ return TRUE;
}
/**
LOCK_THREAD (current_thread);
- if ((current_thread->state & ThreadState_SuspendRequested) ||
- (current_thread->state & ThreadState_AbortRequested) ||
- (current_thread->state & ThreadState_StopRequested)) {
+ if (current_thread->state & (ThreadState_SuspendRequested | ThreadState_AbortRequested)) {
UNLOCK_THREAD (current_thread);
mono_thread_execute_interruption ();
} else {
- current_thread->state |= ThreadState_Stopped;
UNLOCK_THREAD (current_thread);
}
}
}
-void mono_thread_manage (void)
+/**
+ * mono_thread_manage:
+ */
+void
+mono_thread_manage (void)
{
struct wait_data wait_data;
struct wait_data *wait = &wait_data;
LOCK_THREAD (thread);
- if ((thread->state & ThreadState_Suspended) != 0 ||
- (thread->state & ThreadState_StopRequested) != 0 ||
- (thread->state & ThreadState_Stopped) != 0) {
+ if (thread->state & (ThreadState_Suspended | ThreadState_Stopped)) {
UNLOCK_THREAD (thread);
mono_threads_close_thread_handle (wait->handles [i]);
wait->threads [i] = NULL;
MonoDebugSourceLocation *location;
int tindex, nthreads;
- mono_error_init (error);
+ error_init (error);
*out_threads = NULL;
*out_stack_frames = NULL;
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;
return TRUE;
}
-static void
-clear_cached_culture (gpointer key, gpointer value, gpointer user_data)
-{
- MonoInternalThread *thread = (MonoInternalThread*)value;
- MonoDomain *domain = (MonoDomain*)user_data;
- int i;
-
- /* No locking needed here */
- /* FIXME: why no locking? writes to the cache are protected with synch_cs above */
-
- if (thread->cached_culture_info) {
- for (i = 0; i < NUM_CACHED_CULTURES * 2; ++i) {
- MonoObject *obj = mono_array_get (thread->cached_culture_info, MonoObject*, i);
- if (obj && obj->vtable->domain == domain)
- mono_array_set (thread->cached_culture_info, MonoObject*, i, NULL);
- }
- }
-}
-
-/*
- * mono_threads_clear_cached_culture:
- *
- * Clear the cached_current_culture from all threads if it is in the
- * given appdomain.
- */
void
-mono_threads_clear_cached_culture (MonoDomain *domain)
+mono_thread_self_abort (void)
{
- mono_threads_lock ();
- mono_g_hash_table_foreach (threads, clear_cached_culture, domain);
- mono_threads_unlock ();
+ MonoError error;
+ self_abort_internal (&error);
+ mono_error_set_pending_exception (&error);
}
/*
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) {
UNLOCK_THREAD (thread);
return exc;
- } else if ((thread->state & ThreadState_AbortRequested) != 0) {
+ } else if (thread->state & (ThreadState_AbortRequested)) {
UNLOCK_THREAD (thread);
g_assert (sys_thread->pending_exception == NULL);
if (thread->abort_exc == NULL) {
MONO_OBJECT_SETREF (thread, abort_exc, mono_get_exception_thread_abort ());
}
return thread->abort_exc;
- }
- else if ((thread->state & ThreadState_SuspendRequested) != 0) {
+ } else if (thread->state & (ThreadState_SuspendRequested)) {
/* calls UNLOCK_THREAD (thread) */
self_suspend_internal ();
return NULL;
- }
- else if ((thread->state & ThreadState_StopRequested) != 0) {
- /* FIXME: do this through the JIT? */
-
- UNLOCK_THREAD (thread);
-
- mono_thread_exit ();
- return NULL;
} else if (thread->thread_interrupt_requested) {
thread->thread_interrupt_requested = FALSE;
if (thread == NULL)
return NULL;
-#ifdef HOST_WIN32
- if (thread->interrupt_on_stop &&
- thread->state & ThreadState_StopRequested &&
- thread->state & ThreadState_Background)
- ExitThread (1);
-#endif
if (!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;
return NULL;
LOCK_THREAD (thread);
- still_aborting = (thread->state & (ThreadState_AbortRequested|ThreadState_StopRequested)) != 0;
+ still_aborting = (thread->state & (ThreadState_AbortRequested)) != 0;
UNLOCK_THREAD (thread);
/*This can happen if the protected block called Thread::ResetAbort*/
if (!still_aborting)
- return FALSE;
+ return NULL;
if (!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_get_eh_callbacks ()->mono_install_handler_block_guard (mono_thread_info_get_suspend_state (info)))
return MonoResumeThread;
- /*
- The target thread is running at least one protected block, which must not be interrupted, so we give up.
- The protected block code will give them a chance when appropriate.
- */
- if (mono_thread_get_abort_prot_block_count (thread) > 0)
- return MonoResumeThread;
-
/*someone is already interrupting it*/
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));
{
MonoException *exc;
- mono_error_init (error);
+ error_init (error);
/* FIXME this is insanely broken, it doesn't cause interruption to happen synchronously
* since passing FALSE to mono_thread_request_interruption makes sure it returns NULL */
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);
event = thread->suspended;
MONO_ENTER_GC_SAFE;
- res = mono_os_event_wait_one (event, MONO_INFINITE_WAIT);
+ res = mono_os_event_wait_one (event, MONO_INFINITE_WAIT, TRUE);
g_assert (res == MONO_OS_EVENT_WAIT_RET_SUCCESS_0 || res == MONO_OS_EVENT_WAIT_RET_ALERTED);
MONO_EXIT_GC_SAFE;
}
-/*
+static void
+suspend_for_shutdown_async_call (gpointer unused)
+{
+ for (;;)
+ mono_thread_info_yield ();
+}
+
+static SuspendThreadResult
+suspend_for_shutdown_critical (MonoThreadInfo *info, gpointer unused)
+{
+ mono_thread_info_setup_async_call (info, suspend_for_shutdown_async_call, NULL);
+ return MonoResumeThread;
+}
+
+void
+mono_thread_internal_suspend_for_shutdown (MonoInternalThread *thread)
+{
+ g_assert (thread != mono_thread_internal_current ());
+
+ mono_thread_info_safe_suspend_and_run (thread_get_tid (thread), FALSE, suspend_for_shutdown_critical, NULL);
+}
+
+/**
* mono_thread_is_foreign:
- * @thread: the thread to query
+ * \param thread the thread to query
*
* This function allows one to determine if a thread was created by the mono runtime and has
- * a well defined lifecycle or it's a foreigh one, created by the native environment.
+ * a well defined lifecycle or it's a foreign one, created by the native environment.
*
- * Returns: TRUE if @thread was not created by the runtime.
+ * \returns TRUE if \p thread was not created by the runtime.
*/
mono_bool
mono_thread_is_foreign (MonoThread *thread)
if (!joinable_threads)
joinable_threads = g_hash_table_new (NULL, NULL);
g_hash_table_insert (joinable_threads, tid, tid);
- joinable_thread_count ++;
+ UnlockedIncrement (&joinable_thread_count);
joinable_threads_unlock ();
mono_gc_finalize_notify ();
gboolean found;
/* Fastpath */
- if (!joinable_thread_count)
+ if (!UnlockedRead (&joinable_thread_count))
return;
while (TRUE) {
g_hash_table_iter_next (&iter, &key, (void**)&tid);
thread = (pthread_t)tid;
g_hash_table_remove (joinable_threads, key);
- joinable_thread_count --;
+ UnlockedDecrement (&joinable_thread_count);
found = TRUE;
}
joinable_threads_unlock ();
joinable_threads = g_hash_table_new (NULL, NULL);
if (g_hash_table_lookup (joinable_threads, tid)) {
g_hash_table_remove (joinable_threads, tid);
- joinable_thread_count --;
+ UnlockedDecrement (&joinable_thread_count);
found = TRUE;
}
joinable_threads_unlock ();
#endif
}
-void
-mono_thread_internal_check_for_interruption_critical (MonoInternalThread *thread)
-{
- if ((thread->state & (ThreadState_StopRequested | ThreadState_SuspendRequested)) != 0)
- mono_thread_interruption_checkpoint ();
-}
-
void
mono_thread_internal_unhandled_exception (MonoObject* exc)
{
/*
* 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
thread = mono_thread_internal_current ();
LOCK_THREAD (thread);
- if (thread->state & (MonoThreadState)(ThreadState_StopRequested | ThreadState_SuspendRequested | ThreadState_AbortRequested)) {
+ if (thread->state & (ThreadState_SuspendRequested | ThreadState_AbortRequested)) {
UNLOCK_THREAD (thread);
return FALSE;
}