X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmetadata%2Fthreads.c;h=43c8a61d9ab8159f75591149aa572c470dc52227;hb=HEAD;hp=3461f81e325c6dde6063bb487799a5d24b4baf02;hpb=d899b02874a476290f6e8cd54d40e0eaba2ecf04;p=mono.git diff --git a/mono/metadata/threads.c b/mono/metadata/threads.c index 3461f81e325..43c8a61d9ab 100644 --- a/mono/metadata/threads.c +++ b/mono/metadata/threads.c @@ -1,5 +1,6 @@ -/* - * threads.c: Thread support internal calls +/** + * \file + * Thread support internal calls * * Author: * Dick Porter (dick@ximian.com) @@ -29,7 +30,7 @@ #include #include #include -#include +#include #include #include #include @@ -44,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -52,6 +54,7 @@ #include #include #include +#include #ifdef HAVE_SIGNAL_H #include @@ -59,13 +62,16 @@ #if defined(HOST_WIN32) #include + +extern gboolean +mono_native_thread_join_handle (HANDLE thread_handle, gboolean close_handle); #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 #ifdef USE_TKILL_ON_ANDROID @@ -114,14 +120,6 @@ typedef struct { 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); @@ -142,7 +140,7 @@ static StaticDataInfo context_static_info; 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; @@ -159,7 +157,7 @@ static MonoGHashTable *threads_starting_up = 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 () @@ -205,9 +203,6 @@ static gboolean shutting_down = FALSE; static gint32 managed_thread_id_counter = 0; -/* Class lazy loading functions */ -static GENERATE_GET_CLASS_WITH_CACHE (appdomain_unloaded_exception, "System", "AppDomainUnloadedException") - static void mono_threads_lock (void) { @@ -227,9 +222,18 @@ get_next_managed_thread_id (void) 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) @@ -242,150 +246,141 @@ mono_thread_get_abort_prot_block_count (MonoInternalThread *thread) 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 @@ -469,8 +464,8 @@ typedef union { #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) @@ -656,7 +651,7 @@ static void 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; @@ -665,8 +660,18 @@ mono_thread_attach_internal (MonoThread *thread, gboolean force_attach, gboolean 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 ()); @@ -674,7 +679,6 @@ mono_thread_attach_internal (MonoThread *thread, gboolean force_attach, gboolean 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)); @@ -735,6 +739,158 @@ mono_thread_attach_internal (MonoThread *thread, gboolean force_attach, gboolean 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; + + /* + * Prevent race condition between execution of this method and runtime shutdown. + * Adding runtime thread to the joinable threads list will make sure runtime shutdown + * won't complete until added runtime thread have exited. Owner of threads attached to the + * runtime but not identified as runtime threads needs to make sure thread detach calls won't + * race with runtime shutdown. + */ + mono_threads_add_joinable_runtime_thread (thread->thread_info); + + /* + * 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; @@ -742,6 +898,7 @@ typedef struct { MonoObject *start_delegate_arg; MonoThreadStart start_func; gpointer start_func_arg; + gboolean force_attach; gboolean failed; MonoCoopSem registered; } StartInfo; @@ -768,7 +925,7 @@ static guint32 WINAPI start_wrapper_internal(StartInfo *start_info, gsize *stack 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); @@ -821,12 +978,12 @@ static guint32 WINAPI start_wrapper_internal(StartInfo *start_info, gsize *stack * 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); } @@ -876,16 +1033,28 @@ static guint32 WINAPI start_wrapper_internal(StartInfo *start_info, gsize *stack mono_thread_detach_internal (internal); - internal->tid = 0; - - return(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 (); } /* @@ -896,10 +1065,9 @@ static gsize WINAPI start_wrapper(void *data) */ 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; @@ -909,6 +1077,15 @@ create_thread (MonoThread *thread, MonoInternalThread *internal, MonoObject *sta 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. @@ -918,7 +1095,7 @@ create_thread (MonoThread *thread, MonoInternalThread *internal, MonoObject *sta error_init (error); mono_threads_lock (); - if (shutting_down) { + if (shutting_down && !(flags & MONO_THREAD_CREATE_FLAGS_FORCE_CREATE)) { mono_threads_unlock (); return FALSE; } @@ -928,10 +1105,12 @@ create_thread (MonoThread *thread, MonoInternalThread *internal, MonoObject *sta 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; @@ -939,17 +1118,16 @@ create_thread (MonoThread *thread, MonoInternalThread *internal, MonoObject *sta 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 ((MonoThreadStart)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); @@ -974,8 +1152,6 @@ create_thread (MonoThread *thread, MonoInternalThread *internal, MonoObject *sta 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; @@ -989,19 +1165,31 @@ done: 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; } @@ -1012,7 +1200,7 @@ guint32 mono_threads_get_default_stacksize (void) * 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; @@ -1026,14 +1214,17 @@ mono_thread_create_internal (MonoDomain *domain, gpointer func, gpointer arg, gb 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) { @@ -1045,24 +1236,16 @@ 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 ()) @@ -1071,9 +1254,8 @@ mono_thread_attach_full (MonoDomain *domain, gboolean force_attach) 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 before 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 (); @@ -1081,7 +1263,7 @@ mono_thread_attach_full (MonoDomain *domain, gboolean force_attach) 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); @@ -1089,180 +1271,28 @@ mono_thread_attach_full (MonoDomain *domain, gboolean force_attach) 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) { @@ -1270,12 +1300,12 @@ 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 dealloc: implementations in Objective-C. */ mono_bool mono_thread_detach_if_exiting (void) @@ -1305,6 +1335,9 @@ mono_thread_internal_current_is_attached (void) return TRUE; } +/** + * mono_thread_exit: + */ void mono_thread_exit (void) { @@ -1366,7 +1399,7 @@ ves_icall_System_Threading_Thread_Thread_internal (MonoThread *this_obj, 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); @@ -1497,10 +1530,9 @@ mono_thread_get_name (MonoInternalThread *this_obj, guint32 *name_len) 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. */ @@ -1523,11 +1555,10 @@ mono_thread_get_name_utf8 (MonoThread *thread) 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) @@ -1570,6 +1601,8 @@ ves_icall_System_Threading_Thread_GetName_internal (MonoInternalThread *this_obj void mono_thread_set_name_internal (MonoInternalThread *this_obj, MonoString *name, gboolean permanent, gboolean reset, MonoError *error) { + MonoNativeThreadId tid = 0; + LOCK_THREAD (this_obj); error_init (error); @@ -1596,14 +1629,16 @@ mono_thread_set_name_internal (MonoInternalThread *this_obj, MonoString *name, g else this_obj->name = NULL; - + if (!(this_obj->state & ThreadState_Stopped)) + tid = thread_get_tid (this_obj); + UNLOCK_THREAD (this_obj); - if (this_obj->name && this_obj->tid) { + if (this_obj->name && tid) { char *tname = mono_string_to_utf8_checked (name, error); return_if_nok (error); - mono_profiler_thread_name (this_obj->tid, tname); - mono_native_thread_set_name (thread_get_tid (this_obj), tname); + MONO_PROFILER_RAISE (thread_name, ((uintptr_t)tid, tname)); + mono_native_thread_set_name (tid, tname); mono_free (tname); } } @@ -1692,6 +1727,9 @@ ves_icall_System_Threading_Thread_ByteArrayToCurrentDomain (MonoArray *arr) return result; } +/** + * mono_thread_current: + */ MonoThread * mono_thread_current (void) { @@ -1776,7 +1814,7 @@ mono_join_uninterrupted (MonoThreadHandle* thread_to_join, gint32 ms, MonoError } 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; @@ -1798,28 +1836,31 @@ ves_icall_System_Threading_Thread_Join_internal(MonoThread *this_obj, int ms) 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); + /* Wait for the thread to really exit */ + MonoNativeThreadId tid = thread_get_tid (thread); + mono_thread_join (tid); + + return TRUE; } THREAD_DEBUG (g_message ("%s: join failed", __func__)); - return(FALSE); + return FALSE; } #define MANAGED_WAIT_FAILED 0x7fffffff @@ -1843,28 +1884,40 @@ map_native_wait_result_to_managed (MonoW32HandleWaitRet val, gsize numobjects) } } -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 (mono_win32_wait_for_multiple_objects_ex(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 (mono_win32_wait_for_single_object_ex (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; @@ -1877,128 +1930,26 @@ mono_wait_uninterrupted (MonoInternalThread *thread, guint32 numhandles, gpointe 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) -{ - 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); + if (timeout != MONO_INFINITE_WAIT) { + gint64 elapsed; - 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; - } - - 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); + elapsed = mono_msec_ticks () - start; + if (elapsed >= timeout) { + ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT; + break; + } + + timeoutLeft = timeout - elapsed; + } + } - 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 (); @@ -2013,7 +1964,7 @@ ves_icall_System_Threading_WaitHandle_SignalAndWait_Internal (HANDLE toSignal, H MONO_ENTER_GC_SAFE; #ifdef HOST_WIN32 - ret = mono_w32handle_convert_wait_ret (SignalObjectAndWait (toSignal, toWait, ms, TRUE), 1); + ret = mono_w32handle_convert_wait_ret (mono_win32_signal_object_and_wait (toSignal, toWait, ms, TRUE), 1); #else ret = mono_w32handle_signal_and_wait (toSignal, toWait, ms, TRUE); #endif @@ -2322,10 +2273,8 @@ void ves_icall_System_Threading_Thread_Interrupt_internal (MonoThread *this_obj) /** * 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) @@ -2348,13 +2297,11 @@ mono_thread_current_check_pending_interrupt (void) } static gboolean -request_thread_abort (MonoInternalThread *thread, MonoObject *state) +request_thread_abort (MonoInternalThread *thread, MonoObject *state, gboolean appdomain_unload) { 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; @@ -2367,6 +2314,11 @@ request_thread_abort (MonoInternalThread *thread, MonoObject *state) } thread->state |= ThreadState_AbortRequested; + if (appdomain_unload) + thread->flags |= MONO_THREAD_FLAG_APPDOMAIN_ABORT; + else + thread->flags &= ~MONO_THREAD_FLAG_APPDOMAIN_ABORT; + if (thread->abort_state_handle) mono_gchandle_free (thread->abort_state_handle); if (state) { @@ -2391,7 +2343,7 @@ request_thread_abort (MonoInternalThread *thread, MonoObject *state) void ves_icall_System_Threading_Thread_Abort (MonoInternalThread *thread, MonoObject *state) { - if (!request_thread_abort (thread, state)) + if (!request_thread_abort (thread, state, FALSE)) return; if (thread == mono_thread_internal_current ()) { @@ -2405,17 +2357,15 @@ ves_icall_System_Threading_Thread_Abort (MonoInternalThread *thread, MonoObject /** * 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) +mono_thread_internal_abort (MonoInternalThread *thread, gboolean appdomain_unload) { g_assert (thread != mono_thread_internal_current ()); - if (!request_thread_abort (thread, NULL)) + if (!request_thread_abort (thread, NULL, appdomain_unload)) return; async_abort_internal (thread, TRUE); } @@ -2424,17 +2374,23 @@ void ves_icall_System_Threading_Thread_ResetAbort (MonoThread *this_obj) { MonoInternalThread *thread = mono_thread_internal_current (); - gboolean was_aborting; + gboolean was_aborting, is_domain_abort; LOCK_THREAD (thread); was_aborting = thread->state & ThreadState_AbortRequested; - thread->state &= ~ThreadState_AbortRequested; + is_domain_abort = thread->flags & MONO_THREAD_FLAG_APPDOMAIN_ABORT; + + if (was_aborting && !is_domain_abort) + thread->state &= ~ThreadState_AbortRequested; UNLOCK_THREAD (thread); if (!was_aborting) { const char *msg = "Unable to reset abort because no abort was requested"; mono_set_pending_exception (mono_get_exception_thread_state (msg)); return; + } else if (is_domain_abort) { + /* Silently ignore abort resets in unloading appdomains */ + return; } mono_get_eh_callbacks ()->mono_clear_abort_threshold (); @@ -2506,17 +2462,13 @@ mono_thread_suspend (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; @@ -2630,53 +2582,17 @@ is_running_protected_wrapper (void) 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, FALSE)) return; - + if (internal == mono_thread_internal_current ()) { MonoError error; self_abort_internal (&error); @@ -2985,8 +2901,9 @@ free_context (void *user_data) } 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); @@ -3014,11 +2931,18 @@ ves_icall_System_Runtime_Remoting_Contexts_Context_RegisterContext (MonoAppConte mono_threads_unlock (); - mono_profiler_context_loaded (ctx); + MONO_PROFILER_RAISE (context_loaded, (ctx)); } 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 @@ -3028,7 +2952,14 @@ ves_icall_System_Runtime_Remoting_Contexts_Context_ReleaseContext (MonoAppContex //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, @@ -3048,8 +2979,92 @@ 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) +{ + 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 @@ -3079,6 +3094,9 @@ mono_threads_install_cleanup (MonoThreadCleanupFunc func) mono_thread_cleanup_fn = func; } +/** + * mono_thread_set_manage_callback: + */ void mono_thread_set_manage_callback (MonoThread *thread, MonoThreadManageCallback func) { @@ -3220,7 +3238,7 @@ remove_and_abort_threads (gpointer key, gpointer value, gpointer user) wait->num++; THREAD_DEBUG (g_print ("%s: Aborting id: %"G_GSIZE_FORMAT"\n", __func__, (gsize)thread->tid)); - mono_thread_internal_abort (thread); + mono_thread_internal_abort (thread, FALSE); } return TRUE; @@ -3247,13 +3265,10 @@ mono_threads_set_shutting_down (void) 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); } @@ -3275,7 +3290,11 @@ mono_threads_set_shutting_down (void) } } -void mono_thread_manage (void) +/** + * mono_thread_manage: + */ +void +mono_thread_manage (void) { struct wait_data wait_data; struct wait_data *wait = &wait_data; @@ -3436,9 +3455,7 @@ void mono_thread_suspend_all_other_threads (void) 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; @@ -3727,7 +3744,10 @@ mono_threads_get_thread_dump (MonoArray **out_threads, MonoArray **out_stack_fra 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; } @@ -3913,10 +3933,6 @@ collect_appdomain_thread (gpointer key, gpointer value, gpointer user_data) 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; @@ -3937,7 +3953,7 @@ mono_threads_abort_appdomain_threads (MonoDomain *domain, int timeout) if (user_data.wait.num > 0) { /* Abort the threads outside the threads lock */ for (i = 0; i < user_data.wait.num; ++i) - mono_thread_internal_abort (user_data.wait.threads [i]); + mono_thread_internal_abort (user_data.wait.threads [i], TRUE); /* * We should wait for the threads either to abort, or to leave the @@ -3960,37 +3976,12 @@ mono_threads_abort_appdomain_threads (MonoDomain *domain, int 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); } /* @@ -4465,16 +4456,18 @@ mono_thread_execute_interruption (void) 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); + mono_win32_wait_for_single_object_ex (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) { @@ -4485,7 +4478,7 @@ mono_thread_execute_interruption (void) 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) { @@ -4496,19 +4489,10 @@ mono_thread_execute_interruption (void) 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; @@ -4539,15 +4523,8 @@ mono_thread_request_interruption (gboolean running_managed) 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 @@ -4556,9 +4533,8 @@ mono_thread_request_interruption (gboolean running_managed) /* this will awake the thread if it is in WaitForSingleObject or similar */ - /* Our implementation of this function ignores the func argument */ #ifdef HOST_WIN32 - QueueUserAPC ((PAPCFUNC)dummy_apc, thread->native_handle, (ULONG_PTR)NULL); + mono_win32_interrupt_wait (thread->thread_info, thread->native_handle, (DWORD)thread->tid); #else mono_thread_info_self_interrupt (); #endif @@ -4572,7 +4548,7 @@ mono_thread_request_interruption (gboolean running_managed) /*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; @@ -4582,20 +4558,22 @@ mono_thread_resume_interruption (void) 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 () @@ -4722,10 +4700,8 @@ mono_thread_set_state (MonoInternalThread *thread, MonoThreadState state) /** * 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) @@ -4848,8 +4824,6 @@ async_abort_critical (MonoThreadInfo *info, gpointer ud) 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)); @@ -4940,8 +4914,7 @@ async_suspend_critical (MonoThreadInfo *info, gpointer ud) 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); @@ -5021,14 +4994,14 @@ mono_thread_internal_suspend_for_shutdown (MonoInternalThread *thread) 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) @@ -5037,6 +5010,78 @@ mono_thread_is_foreign (MonoThread *thread) return info->runtime_thread == FALSE; } +#ifndef HOST_WIN32 +static void +threads_native_thread_join_lock (gpointer tid, gpointer value) +{ + pthread_t thread = (pthread_t)tid; + if (thread != pthread_self ()) { + MONO_ENTER_GC_SAFE; + /* This shouldn't block */ + mono_threads_join_lock (); + mono_native_thread_join (thread); + mono_threads_join_unlock (); + MONO_EXIT_GC_SAFE; + } +} +static void +threads_native_thread_join_nolock (gpointer tid, gpointer value) +{ + pthread_t thread = (pthread_t)tid; + MONO_ENTER_GC_SAFE; + mono_native_thread_join (thread); + MONO_EXIT_GC_SAFE; +} + +static void +threads_add_joinable_thread_nolock (gpointer tid) +{ + g_hash_table_insert (joinable_threads, tid, tid); +} +#else +static void +threads_native_thread_join_lock (gpointer tid, gpointer value) +{ + MonoNativeThreadId thread_id = (MonoNativeThreadId)(guint64)tid; + HANDLE thread_handle = (HANDLE)value; + if (thread_id != GetCurrentThreadId () && thread_handle != NULL && thread_handle != INVALID_HANDLE_VALUE) { + MONO_ENTER_GC_SAFE; + /* This shouldn't block */ + mono_threads_join_lock (); + mono_native_thread_join_handle (thread_handle, TRUE); + mono_threads_join_unlock (); + MONO_EXIT_GC_SAFE; + } +} + +static void +threads_native_thread_join_nolock (gpointer tid, gpointer value) +{ + HANDLE thread_handle = (HANDLE)value; + MONO_ENTER_GC_SAFE; + mono_native_thread_join_handle (thread_handle, TRUE); + MONO_EXIT_GC_SAFE; +} + +static void +threads_add_joinable_thread_nolock (gpointer tid) +{ + g_hash_table_insert (joinable_threads, tid, (gpointer)OpenThread (SYNCHRONIZE, TRUE, (MonoNativeThreadId)(guint64)tid)); +} +#endif + +void +mono_threads_add_joinable_runtime_thread (gpointer thread_info) +{ + g_assert (thread_info); + MonoThreadInfo *mono_thread_info = (MonoThreadInfo*)thread_info; + + if (mono_thread_info->runtime_thread) { + if (InterlockedCompareExchange (&mono_thread_info->thread_pending_native_join, TRUE, FALSE) == FALSE) + mono_threads_add_joinable_thread ((gpointer)(MONO_UINT_TO_NATIVE_THREAD_ID (mono_thread_info_get_tid (mono_thread_info)))); + } +} + /* * mono_add_joinable_thread: * @@ -5046,21 +5091,24 @@ mono_thread_is_foreign (MonoThread *thread) void mono_threads_add_joinable_thread (gpointer tid) { -#ifndef HOST_WIN32 /* * We cannot detach from threads because it causes problems like * 2fd16f60/r114307. So we collect them and join them when - * we have time (in he finalizer thread). + * we have time (in the finalizer thread). */ joinable_threads_lock (); if (!joinable_threads) joinable_threads = g_hash_table_new (NULL, NULL); - g_hash_table_insert (joinable_threads, tid, tid); - joinable_thread_count ++; + + gpointer orig_key; + gpointer value; + if (!g_hash_table_lookup_extended (joinable_threads, tid, &orig_key, &value)) { + threads_add_joinable_thread_nolock (tid); + UnlockedIncrement (&joinable_thread_count); + } joinable_threads_unlock (); mono_gc_finalize_notify (); -#endif } /* @@ -5072,15 +5120,13 @@ mono_threads_add_joinable_thread (gpointer tid) void mono_threads_join_threads (void) { -#ifndef HOST_WIN32 GHashTableIter iter; gpointer key; - gpointer tid; - pthread_t thread; + gpointer value; gboolean found; /* Fastpath */ - if (!joinable_thread_count) + if (!UnlockedRead (&joinable_thread_count)) return; while (TRUE) { @@ -5088,27 +5134,17 @@ mono_threads_join_threads (void) found = FALSE; if (g_hash_table_size (joinable_threads)) { g_hash_table_iter_init (&iter, joinable_threads); - g_hash_table_iter_next (&iter, &key, (void**)&tid); - thread = (pthread_t)tid; + g_hash_table_iter_next (&iter, &key, (void**)&value); g_hash_table_remove (joinable_threads, key); - joinable_thread_count --; + UnlockedDecrement (&joinable_thread_count); found = TRUE; } joinable_threads_unlock (); - if (found) { - if (thread != pthread_self ()) { - MONO_ENTER_GC_SAFE; - /* This shouldn't block */ - mono_threads_join_lock (); - mono_native_thread_join (thread); - mono_threads_join_unlock (); - MONO_EXIT_GC_SAFE; - } - } else { + if (found) + threads_native_thread_join_lock (key, value); + else break; - } } -#endif } /* @@ -5120,33 +5156,25 @@ mono_threads_join_threads (void) void mono_thread_join (gpointer tid) { -#ifndef HOST_WIN32 - pthread_t thread; gboolean found = FALSE; + gpointer orig_key; + gpointer value; joinable_threads_lock (); if (!joinable_threads) joinable_threads = g_hash_table_new (NULL, NULL); - if (g_hash_table_lookup (joinable_threads, tid)) { + + if (g_hash_table_lookup_extended (joinable_threads, tid, &orig_key, &value)) { g_hash_table_remove (joinable_threads, tid); - joinable_thread_count --; + UnlockedDecrement (&joinable_thread_count); found = TRUE; } joinable_threads_unlock (); + if (!found) return; - thread = (pthread_t)tid; - MONO_ENTER_GC_SAFE; - mono_native_thread_join (thread); - MONO_EXIT_GC_SAFE; -#endif -} -void -mono_thread_internal_check_for_interruption_critical (MonoInternalThread *thread) -{ - if ((thread->state & (ThreadState_StopRequested | ThreadState_SuspendRequested)) != 0) - mono_thread_interruption_checkpoint (); + threads_native_thread_join_nolock (tid, value); } void @@ -5177,35 +5205,32 @@ ves_icall_System_Threading_Thread_GetStackTraces (MonoArray **out_threads, MonoA /* * 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); @@ -5214,76 +5239,53 @@ mono_threads_attach_coop (MonoDomain *domain, gpointer *dummy) 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 @@ -5295,7 +5297,7 @@ mono_threads_is_ready_to_be_interrupted (void) 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; }