X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmetadata%2Fthreads.c;h=50ac9158aae755b6e03de139dc42989f42aa5e38;hb=fe8558dcfcb48990b64bf7ef62b75687ed8bd377;hp=6dbba62b347d948ad99129603748dc6e967ee124;hpb=3265991ed987c253f71ac61edafdeb39eaf33049;p=mono.git diff --git a/mono/metadata/threads.c b/mono/metadata/threads.c index 6dbba62b347..50ac9158aae 100644 --- a/mono/metadata/threads.c +++ b/mono/metadata/threads.c @@ -41,6 +41,8 @@ #include #include #include +#include +#include #include #include @@ -79,15 +81,6 @@ extern int tkill (pid_t tid, int signal); #define LOCK_THREAD(thread) lock_thread((thread)) #define UNLOCK_THREAD(thread) unlock_thread((thread)) -/* Provide this for systems with glib < 2.6 */ -#ifndef G_GSIZE_FORMAT -# if GLIB_SIZEOF_LONG == 8 -# define G_GSIZE_FORMAT "lu" -# else -# define G_GSIZE_FORMAT "u" -# endif -#endif - typedef struct { guint32 (*func)(void *); @@ -204,7 +197,7 @@ static void mono_init_static_data_info (StaticDataInfo *static_data); static guint32 mono_alloc_static_data_slot (StaticDataInfo *static_data, guint32 size, guint32 align); static gboolean mono_thread_resume (MonoInternalThread* thread); static void async_abort_internal (MonoInternalThread *thread, gboolean install_async_abort); -static void self_abort_internal (void); +static void self_abort_internal (MonoError *error); static void async_suspend_internal (MonoInternalThread *thread, gboolean interrupt); static void self_suspend_internal (void); @@ -257,8 +250,14 @@ mono_thread_get_tls_key (void) gint32 mono_thread_get_tls_offset (void) { - int offset; + int offset = -1; + +#ifdef HOST_WIN32 + if (current_object_key) + offset = current_object_key; +#else MONO_THREAD_VAR_OFFSET (tls_current_object,offset); +#endif return offset; } @@ -458,6 +457,8 @@ static void thread_cleanup (MonoInternalThread *thread) 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); + if (thread == mono_thread_internal_current ()) { /* * This will signal async signal handlers that the thread has exited. @@ -573,21 +574,22 @@ set_current_thread_for_domain (MonoDomain *domain, MonoInternalThread *thread, M } static MonoThread* -create_thread_object (MonoDomain *domain, MonoError *error) +create_thread_object (MonoDomain *domain) { + MonoError error; MonoVTable *vt = mono_class_vtable (domain, mono_defaults.thread_class); - MonoThread *t = (MonoThread*)mono_object_new_mature (vt, error); + MonoThread *t = (MonoThread*)mono_object_new_mature (vt, &error); + /* only possible failure mode is OOM, from which we don't expect to recover. */ + mono_error_assert_ok (&error); return t; } static MonoThread* -new_thread_with_internal (MonoDomain *domain, MonoInternalThread *internal, MonoError *error) +new_thread_with_internal (MonoDomain *domain, MonoInternalThread *internal) { MonoThread *thread; - thread = create_thread_object (domain, error); - if (!mono_error_ok (error)) - return NULL; + thread = create_thread_object (domain); MONO_OBJECT_SETREF (thread, internal_thread, internal); @@ -595,15 +597,16 @@ new_thread_with_internal (MonoDomain *domain, MonoInternalThread *internal, Mono } static MonoInternalThread* -create_internal_thread (MonoError *error) +create_internal_thread () { + MonoError error; MonoInternalThread *thread; MonoVTable *vt; vt = mono_class_vtable (mono_get_root_domain (), mono_defaults.internal_thread_class); - thread = (MonoInternalThread*) mono_object_new_mature (vt, error); - if (!mono_error_ok (error)) - return NULL; + thread = (MonoInternalThread*) mono_object_new_mature (vt, &error); + /* only possible failure mode is OOM, from which we don't exect to recover */ + mono_error_assert_ok (&error); thread->synch_cs = g_new0 (MonoCoopMutex, 1); mono_coop_mutex_init_recursive (thread->synch_cs); @@ -619,14 +622,12 @@ create_internal_thread (MonoError *error) } static gboolean -init_root_domain_thread (MonoInternalThread *thread, MonoThread *candidate, MonoError *error) +init_root_domain_thread (MonoInternalThread *thread, MonoThread *candidate) { MonoDomain *domain = mono_get_root_domain (); - mono_error_init (error); if (!candidate || candidate->obj.vtable->domain != domain) { - candidate = new_thread_with_internal (domain, thread, error); - return_val_if_nok (error, FALSE); + candidate = new_thread_with_internal (domain, thread); } set_current_thread_for_domain (domain, thread, candidate); g_assert (!thread->root_domain_thread); @@ -685,8 +686,7 @@ static guint32 WINAPI start_wrapper_internal(void *data) /* We have to do this here because mono_thread_new_init() requires that root_domain_thread is set up. */ thread_adjust_static_data (internal); - init_root_domain_thread (internal, start_info->obj, &error); - mono_error_raise_exception (&error); /* FIXME don't raise here */ + init_root_domain_thread (internal, start_info->obj); /* This MUST be called before any managed code can be * executed, as it calls the callback function that (for the @@ -727,12 +727,13 @@ static guint32 WINAPI start_wrapper_internal(void *data) mono_profiler_thread_start (tid); /* if the name was set before starting, we didn't invoke the profiler callback */ - if (internal->name && (internal->flags & MONO_THREAD_FLAG_NAME_SET)) { + 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_thread_info_set_name (MONO_UINT_TO_NATIVE_THREAD_ID (internal->tid), tname); + mono_native_thread_set_name (MONO_UINT_TO_NATIVE_THREAD_ID (internal->tid), tname); g_free (tname); } + /* start_func is set only for unmanaged start functions */ if (start_func) { start_func (start_arg); @@ -741,7 +742,8 @@ static guint32 WINAPI start_wrapper_internal(void *data) g_assert (start_delegate != NULL); args [0] = start_arg; /* we may want to handle the exception here. See comment below on unhandled exceptions */ - mono_runtime_delegate_invoke (start_delegate, args, NULL); + mono_runtime_delegate_invoke_checked (start_delegate, args, &error); + mono_error_raise_exception (&error); /* FIXME don't raise here */ } /* If the thread calls ExitThread at all, this remaining code @@ -879,9 +881,9 @@ create_thread (MonoThread *thread, MonoInternalThread *internal, StartInfo *star */ THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") waiting for thread %p (%"G_GSIZE_FORMAT") to start", __func__, mono_native_thread_id_get (), internal, (gsize)internal->tid)); - MONO_PREPARE_BLOCKING; + MONO_ENTER_GC_SAFE; WaitForSingleObjectEx (internal->start_notify, INFINITE, FALSE); - MONO_FINISH_BLOCKING; + MONO_EXIT_GC_SAFE; CloseHandle (internal->start_notify); internal->start_notify = NULL; @@ -924,11 +926,9 @@ mono_thread_create_internal (MonoDomain *domain, gpointer func, gpointer arg, gb mono_error_init (error); - thread = create_thread_object (domain, error); - return_val_if_nok (error, NULL); + thread = create_thread_object (domain); - internal = create_internal_thread (error); - return_val_if_nok (error, NULL); + internal = create_internal_thread (); MONO_OBJECT_SETREF (thread, internal_thread, internal); @@ -966,15 +966,13 @@ mono_thread_create_checked (MonoDomain *domain, gpointer func, gpointer arg, Mon MonoThread * mono_thread_attach (MonoDomain *domain) { - MonoError error; - MonoThread *thread = mono_thread_attach_full (domain, FALSE, &error); - mono_error_raise_exception (&error); + MonoThread *thread = mono_thread_attach_full (domain, FALSE); return thread; } MonoThread * -mono_thread_attach_full (MonoDomain *domain, gboolean force_attach, MonoError *error) +mono_thread_attach_full (MonoDomain *domain, gboolean force_attach) { MonoThreadInfo *info; MonoInternalThread *thread; @@ -982,8 +980,6 @@ mono_thread_attach_full (MonoDomain *domain, gboolean force_attach, MonoError *e HANDLE thread_handle; MonoNativeThreadId tid; - mono_error_init (error); - if ((thread = mono_thread_internal_current ())) { if (domain != mono_domain_get ()) mono_domain_set (domain, TRUE); @@ -995,9 +991,7 @@ mono_thread_attach_full (MonoDomain *domain, gboolean force_attach, MonoError *e 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 ()); } - thread = create_internal_thread (error); - if (!mono_error_ok (error)) - return NULL; + thread = create_internal_thread (); thread_handle = mono_thread_info_open_handle (); g_assert (thread_handle); @@ -1015,9 +1009,7 @@ mono_thread_attach_full (MonoDomain *domain, gboolean force_attach, MonoError *e thread->thread_info = info; thread->small_id = info->small_id; - current_thread = new_thread_with_internal (domain, thread, error); - if (!mono_error_ok (error)) - return NULL; + current_thread = new_thread_with_internal (domain, thread); if (!handle_store (current_thread, force_attach)) { /* Mono is shutting down, so just wait for the end */ @@ -1032,8 +1024,7 @@ mono_thread_attach_full (MonoDomain *domain, gboolean force_attach, MonoError *e thread_adjust_static_data (thread); - init_root_domain_thread (thread, current_thread, error); - return_val_if_nok (error, NULL); + init_root_domain_thread (thread, current_thread); if (domain != mono_get_root_domain ()) set_current_thread_for_domain (domain, thread, current_thread); @@ -1125,11 +1116,9 @@ mono_thread_exit () void ves_icall_System_Threading_Thread_ConstructInternalThread (MonoThread *this_obj) { - MonoError error; MonoInternalThread *internal; - internal = create_internal_thread (&error); - mono_error_raise_exception (&error); + internal = create_internal_thread (); internal->state = ThreadState_Unstarted; @@ -1224,7 +1213,8 @@ ves_icall_System_Threading_Thread_Sleep_internal(gint32 ms) THREAD_DEBUG (g_message ("%s: Sleeping for %d ms", __func__, ms)); - mono_thread_current_check_pending_interrupt (); + if (mono_thread_current_check_pending_interrupt ()) + return; while (TRUE) { gboolean alerted = FALSE; @@ -1358,19 +1348,20 @@ ves_icall_System_Threading_Thread_GetName_internal (MonoInternalThread *this_obj UNLOCK_THREAD (this_obj); - mono_error_raise_exception (&error); + if (mono_error_set_pending_exception (&error)) + return NULL; return str; } void -mono_thread_set_name_internal (MonoInternalThread *this_obj, MonoString *name, gboolean managed, MonoError *error) +mono_thread_set_name_internal (MonoInternalThread *this_obj, MonoString *name, gboolean permanent, MonoError *error) { LOCK_THREAD (this_obj); mono_error_init (error); - if ((this_obj->flags & MONO_THREAD_FLAG_NAME_SET) && !this_obj->threadpool_thread) { + if ((this_obj->flags & MONO_THREAD_FLAG_NAME_SET)) { UNLOCK_THREAD (this_obj); mono_error_set_invalid_operation (error, "Thread.Name can only be set once."); @@ -1384,19 +1375,20 @@ mono_thread_set_name_internal (MonoInternalThread *this_obj, MonoString *name, g this_obj->name = g_new (gunichar2, mono_string_length (name)); memcpy (this_obj->name, mono_string_chars (name), mono_string_length (name) * 2); this_obj->name_len = mono_string_length (name); + + if (permanent) + this_obj->flags |= MONO_THREAD_FLAG_NAME_SET; } else this_obj->name = NULL; - if (managed) - this_obj->flags |= MONO_THREAD_FLAG_NAME_SET; UNLOCK_THREAD (this_obj); if (this_obj->name && this_obj->tid) { char *tname = mono_string_to_utf8 (name); mono_profiler_thread_name (this_obj->tid, tname); - mono_thread_info_set_name (thread_get_tid (this_obj), tname); + mono_native_thread_set_name (thread_get_tid (this_obj), tname); mono_free (tname); } } @@ -1485,7 +1477,6 @@ ves_icall_System_Threading_Thread_ByteArrayToCurrentDomain (MonoArray *arr) MonoThread * mono_thread_current (void) { - MonoError error; MonoDomain *domain = mono_domain_get (); MonoInternalThread *internal = mono_thread_internal_current (); MonoThread **current_thread_ptr; @@ -1495,8 +1486,7 @@ mono_thread_current (void) if (!*current_thread_ptr) { g_assert (domain != mono_get_root_domain ()); - *current_thread_ptr = new_thread_with_internal (domain, internal, &error); - mono_error_raise_exception (&error); /* FIXME don't raise here */ + *current_thread_ptr = new_thread_with_internal (domain, internal); } return *current_thread_ptr; } @@ -1505,7 +1495,6 @@ mono_thread_current (void) static MonoThread * mono_thread_current_for_thread (MonoInternalThread *internal) { - MonoError error; MonoDomain *domain = mono_domain_get (); MonoThread **current_thread_ptr; @@ -1514,8 +1503,7 @@ mono_thread_current_for_thread (MonoInternalThread *internal) if (!*current_thread_ptr) { g_assert (domain != mono_get_root_domain ()); - *current_thread_ptr = new_thread_with_internal (domain, internal, &error); - mono_error_raise_exception (&error); /* FIXME don't raise here */ + *current_thread_ptr = new_thread_with_internal (domain, internal); } return *current_thread_ptr; } @@ -1536,7 +1524,8 @@ ves_icall_System_Threading_Thread_Join_internal(MonoThread *this_obj, int ms) MonoInternalThread *cur_thread = mono_thread_internal_current (); gboolean ret; - mono_thread_current_check_pending_interrupt (); + if (mono_thread_current_check_pending_interrupt ()) + return FALSE; LOCK_THREAD (thread); @@ -1556,9 +1545,9 @@ ves_icall_System_Threading_Thread_Join_internal(MonoThread *this_obj, int ms) mono_thread_set_state (cur_thread, ThreadState_WaitSleepJoin); - MONO_PREPARE_BLOCKING; + MONO_ENTER_GC_SAFE; ret=WaitForSingleObjectEx (handle, ms, TRUE); - MONO_FINISH_BLOCKING; + MONO_EXIT_GC_SAFE; mono_thread_clr_state (cur_thread, ThreadState_WaitSleepJoin); @@ -1573,8 +1562,17 @@ ves_icall_System_Threading_Thread_Join_internal(MonoThread *this_obj, int ms) return(FALSE); } +#define MANAGED_WAIT_FAILED 0x7fffffff + static gint32 -mono_wait_uninterrupted (MonoInternalThread *thread, gboolean multiple, guint32 numhandles, gpointer *handles, gboolean waitall, gint32 ms, gboolean alertable) +map_native_wait_result_to_managed (gint32 val) +{ + /* WAIT_FAILED in waithandle.cs is different from WAIT_FAILED in Win32 API */ + return val == WAIT_FAILED ? MANAGED_WAIT_FAILED : val; +} + +static gint32 +mono_wait_uninterrupted (MonoInternalThread *thread, guint32 numhandles, gpointer *handles, gboolean waitall, gint32 ms, MonoError *error) { MonoException *exc; guint32 ret; @@ -1582,21 +1580,25 @@ mono_wait_uninterrupted (MonoInternalThread *thread, gboolean multiple, guint32 gint32 diff_ms; gint32 wait = ms; + mono_error_init (error); + start = (ms == -1) ? 0 : mono_100ns_ticks (); do { - MONO_PREPARE_BLOCKING; - if (multiple) - ret = WaitForMultipleObjectsEx (numhandles, handles, waitall, wait, alertable); + MONO_ENTER_GC_SAFE; + if (numhandles != 1) + ret = WaitForMultipleObjectsEx (numhandles, handles, waitall, wait, TRUE); else - ret = WaitForSingleObjectEx (handles [0], ms, alertable); - MONO_FINISH_BLOCKING; + ret = WaitForSingleObjectEx (handles [0], ms, TRUE); + MONO_EXIT_GC_SAFE; if (ret != WAIT_IO_COMPLETION) break; exc = mono_thread_execute_interruption (); - if (exc) - mono_raise_exception (exc); + if (exc) { + mono_error_set_exception_instance (error, exc); + break; + } if (ms == -1) continue; @@ -1615,6 +1617,7 @@ mono_wait_uninterrupted (MonoInternalThread *thread, gboolean multiple, guint32 gint32 ves_icall_System_Threading_WaitHandle_WaitAll_internal(MonoArray *mono_handles, gint32 ms) { + MonoError error; HANDLE *handles; guint32 numhandles; guint32 ret; @@ -1623,7 +1626,8 @@ gint32 ves_icall_System_Threading_WaitHandle_WaitAll_internal(MonoArray *mono_ha MonoInternalThread *thread = mono_thread_internal_current (); /* Do this WaitSleepJoin check before creating objects */ - mono_thread_current_check_pending_interrupt (); + if (mono_thread_current_check_pending_interrupt ()) + return map_native_wait_result_to_managed (WAIT_FAILED); /* We fail in managed if the array has more than 64 elements */ numhandles = (guint32)mono_array_length(mono_handles); @@ -1639,19 +1643,22 @@ gint32 ves_icall_System_Threading_WaitHandle_WaitAll_internal(MonoArray *mono_ha } mono_thread_set_state (thread, ThreadState_WaitSleepJoin); - - ret = mono_wait_uninterrupted (thread, TRUE, numhandles, handles, TRUE, ms, TRUE); + + 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); + /* WAIT_FAILED in waithandle.cs is different from WAIT_FAILED in Win32 API */ - return ret == WAIT_FAILED ? 0x7fffffff : ret; + return map_native_wait_result_to_managed (ret); } gint32 ves_icall_System_Threading_WaitHandle_WaitAny_internal(MonoArray *mono_handles, gint32 ms) { + MonoError error; HANDLE handles [MAXIMUM_WAIT_OBJECTS]; uintptr_t numhandles; guint32 ret; @@ -1660,11 +1667,12 @@ gint32 ves_icall_System_Threading_WaitHandle_WaitAny_internal(MonoArray *mono_ha MonoInternalThread *thread = mono_thread_internal_current (); /* Do this WaitSleepJoin check before creating objects */ - mono_thread_current_check_pending_interrupt (); + if (mono_thread_current_check_pending_interrupt ()) + return map_native_wait_result_to_managed (WAIT_FAILED); numhandles = mono_array_length(mono_handles); if (numhandles > MAXIMUM_WAIT_OBJECTS) - return WAIT_FAILED; + return map_native_wait_result_to_managed (WAIT_FAILED); for(i = 0; i < numhandles; i++) { waitHandle = mono_array_get(mono_handles, MonoObject*, i); @@ -1677,29 +1685,31 @@ gint32 ves_icall_System_Threading_WaitHandle_WaitAny_internal(MonoArray *mono_ha mono_thread_set_state (thread, ThreadState_WaitSleepJoin); - ret = mono_wait_uninterrupted (thread, TRUE, numhandles, handles, FALSE, ms, TRUE); + 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); /* * These need to be here. See MSDN dos on WaitForMultipleObjects. */ if (ret >= WAIT_OBJECT_0 && ret <= WAIT_OBJECT_0 + numhandles - 1) { - return ret - WAIT_OBJECT_0; + return map_native_wait_result_to_managed (ret - WAIT_OBJECT_0); } else if (ret >= WAIT_ABANDONED_0 && ret <= WAIT_ABANDONED_0 + numhandles - 1) { - return ret - WAIT_ABANDONED_0; + return map_native_wait_result_to_managed (ret - WAIT_ABANDONED_0); } else { /* WAIT_FAILED in waithandle.cs is different from WAIT_FAILED in Win32 API */ - return ret == WAIT_FAILED ? 0x7fffffff : ret; + return map_native_wait_result_to_managed (ret); } } gint32 ves_icall_System_Threading_WaitHandle_WaitOne_internal(HANDLE handle, gint32 ms) { + MonoError error; guint32 ret; MonoInternalThread *thread = mono_thread_internal_current (); @@ -1709,16 +1719,17 @@ gint32 ves_icall_System_Threading_WaitHandle_WaitOne_internal(HANDLE handle, gin ms=INFINITE; } - mono_thread_current_check_pending_interrupt (); + if (mono_thread_current_check_pending_interrupt ()) + return map_native_wait_result_to_managed (WAIT_FAILED); mono_thread_set_state (thread, ThreadState_WaitSleepJoin); - ret = mono_wait_uninterrupted (thread, FALSE, 1, &handle, FALSE, ms, TRUE); + ret = mono_wait_uninterrupted (thread, 1, &handle, FALSE, ms, &error); mono_thread_clr_state (thread, ThreadState_WaitSleepJoin); - - /* WAIT_FAILED in waithandle.cs is different from WAIT_FAILED in Win32 API */ - return ret == WAIT_FAILED ? 0x7fffffff : ret; + + mono_error_set_pending_exception (&error); + return map_native_wait_result_to_managed (ret); } gint32 @@ -1730,18 +1741,18 @@ ves_icall_System_Threading_WaitHandle_SignalAndWait_Internal (HANDLE toSignal, H if (ms == -1) ms = INFINITE; - mono_thread_current_check_pending_interrupt (); + if (mono_thread_current_check_pending_interrupt ()) + return map_native_wait_result_to_managed (WAIT_FAILED); mono_thread_set_state (thread, ThreadState_WaitSleepJoin); - MONO_PREPARE_BLOCKING; + MONO_ENTER_GC_SAFE; ret = SignalObjectAndWait (toSignal, toWait, ms, TRUE); - MONO_FINISH_BLOCKING; + MONO_EXIT_GC_SAFE; mono_thread_clr_state (thread, ThreadState_WaitSleepJoin); - /* WAIT_FAILED in waithandle.cs is different from WAIT_FAILED in Win32 API */ - return ret == WAIT_FAILED ? 0x7fffffff : ret; + return map_native_wait_result_to_managed (ret); } HANDLE ves_icall_System_Threading_Mutex_CreateMutex_internal (MonoBoolean owned, MonoString *name, MonoBoolean *created) @@ -2172,7 +2183,15 @@ void ves_icall_System_Threading_Thread_Interrupt_internal (MonoThread *this_obj) } } -void mono_thread_current_check_pending_interrupt () +/** + * 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 + */ +gboolean +mono_thread_current_check_pending_interrupt (void) { MonoInternalThread *thread = mono_thread_internal_current (); gboolean throw_ = FALSE; @@ -2186,13 +2205,13 @@ void mono_thread_current_check_pending_interrupt () UNLOCK_THREAD (thread); - if (throw_) { - mono_raise_exception (mono_get_exception_thread_interrupted ()); - } + if (throw_) + mono_set_pending_exception (mono_get_exception_thread_interrupted ()); + return throw_; } -void -ves_icall_System_Threading_Thread_Abort (MonoInternalThread *thread, MonoObject *state) +static gboolean +request_thread_abort (MonoInternalThread *thread, MonoObject *state) { LOCK_THREAD (thread); @@ -2201,13 +2220,13 @@ ves_icall_System_Threading_Thread_Abort (MonoInternalThread *thread, MonoObject (thread->state & ThreadState_Stopped) != 0) { UNLOCK_THREAD (thread); - return; + return FALSE; } if ((thread->state & ThreadState_Unstarted) != 0) { thread->state |= ThreadState_Aborted; UNLOCK_THREAD (thread); - return; + return FALSE; } thread->state |= ThreadState_AbortRequested; @@ -2229,11 +2248,39 @@ ves_icall_System_Threading_Thread_Abort (MonoInternalThread *thread, MonoObject mono_thread_resume (thread); UNLOCK_THREAD (thread); + return TRUE; +} - if (thread == mono_thread_internal_current ()) - self_abort_internal (); - else +void +ves_icall_System_Threading_Thread_Abort (MonoInternalThread *thread, MonoObject *state) +{ + if (!request_thread_abort (thread, state)) + return; + + if (thread == mono_thread_internal_current ()) { + MonoError error; + self_abort_internal (&error); + mono_error_set_pending_exception (&error); + } else { async_abort_internal (thread, TRUE); + } +} + +/** + * mono_thread_internal_abort: + * + * Request thread @thread to be aborted. + * + * @thread MUST NOT be the current thread. + */ +void +mono_thread_internal_abort (MonoInternalThread *thread) +{ + g_assert (thread != mono_thread_internal_current ()); + + if (!request_thread_abort (thread, NULL)) + return; + async_abort_internal (thread, TRUE); } void @@ -2433,7 +2480,8 @@ is_running_protected_wrapper (void) return found; } -void mono_thread_internal_stop (MonoInternalThread *thread) +static gboolean +request_thread_stop (MonoInternalThread *thread) { LOCK_THREAD (thread); @@ -2441,7 +2489,7 @@ void mono_thread_internal_stop (MonoInternalThread *thread) (thread->state & ThreadState_Stopped) != 0) { UNLOCK_THREAD (thread); - return; + return FALSE; } /* Make sure the thread is awake */ @@ -2451,16 +2499,45 @@ void mono_thread_internal_stop (MonoInternalThread *thread) 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. + */ +void +mono_thread_internal_stop (MonoInternalThread *thread) +{ + g_assert (thread != mono_thread_internal_current ()); + + if (!request_thread_stop (thread)) + return; - if (thread == mono_thread_internal_current ()) - self_abort_internal (); - else - async_abort_internal (thread, TRUE); + async_abort_internal (thread, TRUE); } void mono_thread_stop (MonoThread *thread) { - mono_thread_internal_stop (thread->internal_thread); + MonoInternalThread *internal = thread->internal_thread; + + if (!request_thread_stop (internal)) + return; + + if (internal == mono_thread_internal_current ()) { + MonoError error; + self_abort_internal (&error); + /* + This function is part of the embeding API and has no way to return the exception + to be thrown. So what we do is keep the old behavior and raise the exception. + */ + mono_error_raise_exception (&error); + } else { + async_abort_internal (internal, TRUE); + } } gint8 @@ -2905,9 +2982,9 @@ static void wait_for_tids (struct wait_data *wait, guint32 timeout) THREAD_DEBUG (g_message("%s: %d threads to wait for in this batch", __func__, wait->num)); - MONO_PREPARE_BLOCKING; + MONO_ENTER_GC_SAFE; ret=WaitForMultipleObjectsEx(wait->num, wait->handles, TRUE, timeout, TRUE); - MONO_FINISH_BLOCKING; + MONO_EXIT_GC_SAFE; if(ret==WAIT_FAILED) { /* See the comment in build_wait_tids() */ @@ -2968,9 +3045,9 @@ static void wait_for_tids_or_state_change (struct wait_data *wait, guint32 timeo count++; } - MONO_PREPARE_BLOCKING; + MONO_ENTER_GC_SAFE; ret=WaitForMultipleObjectsEx (count, wait->handles, FALSE, timeout, TRUE); - MONO_FINISH_BLOCKING; + MONO_EXIT_GC_SAFE; if(ret==WAIT_FAILED) { /* See the comment in build_wait_tids() */ @@ -3077,9 +3154,8 @@ remove_and_abort_threads (gpointer key, gpointer value, gpointer user) if (handle == NULL) return FALSE; - /* printf ("A: %d\n", wait->num); */ - wait->handles[wait->num]=thread->handle; - wait->threads[wait->num]=thread; + wait->handles[wait->num] = handle; + wait->threads[wait->num] = thread; wait->num++; THREAD_DEBUG (g_print ("%s: Aborting id: %"G_GSIZE_FORMAT"\n", __func__, (gsize)thread->tid)); @@ -3809,7 +3885,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) - ves_icall_System_Threading_Thread_Abort (user_data.wait.threads [i], NULL); + mono_thread_internal_abort (user_data.wait.threads [i]); /* * We should wait for the threads either to abort, or to leave the @@ -4370,8 +4446,18 @@ mono_thread_execute_interruption (void) mono_thread_info_clear_self_interrupt (); } - if ((thread->state & ThreadState_AbortRequested) != 0) { + /* If there's a pending exception and an AbortRequested, the pending exception takes precedence */ + if (sys_thread->pending_exception) { + MonoException *exc; + + exc = sys_thread->pending_exception; + sys_thread->pending_exception = NULL; + + UNLOCK_THREAD (thread); + return exc; + } else if ((thread->state & ThreadState_AbortRequested) != 0) { UNLOCK_THREAD (thread); + g_assert (sys_thread->pending_exception == NULL); if (thread->abort_exc == NULL) { /* * This might be racy, but it has to be called outside the lock @@ -4393,14 +4479,6 @@ mono_thread_execute_interruption (void) mono_thread_exit (); return NULL; - } else if (sys_thread->pending_exception) { - MonoException *exc; - - exc = sys_thread->pending_exception; - sys_thread->pending_exception = NULL; - - UNLOCK_THREAD (thread); - return exc; } else if (thread->thread_interrupt_requested) { thread->thread_interrupt_requested = FALSE; @@ -4764,18 +4842,20 @@ async_abort_internal (MonoInternalThread *thread, gboolean install_async_abort) } static void -self_abort_internal (void) +self_abort_internal (MonoError *error) { MonoException *exc; + mono_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 */ exc = mono_thread_request_interruption (TRUE); if (exc) - mono_raise_exception (exc); - - mono_thread_info_self_interrupt (); + mono_error_set_exception_instance (error, exc); + else + mono_thread_info_self_interrupt (); } typedef struct { @@ -5000,3 +5080,101 @@ ves_icall_System_Threading_Thread_GetStackTraces (MonoArray **out_threads, MonoA { mono_threads_get_thread_dump (out_threads, out_stack_traces); } + +/* + * mono_threads_attach_coop: called by native->managed wrappers + * + * In non-coop mode: + * - @dummy: is NULL + * - @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; + + if (!domain) { + /* Happens when called from AOTed code which is only used in the root domain. */ + domain = mono_get_root_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_thread_internal_current ()) { + mono_thread_attach_full (domain, FALSE); + + // #678164 + 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 (mono_thread_info_current ()); + } else { + *dummy = orig; + /* thread state (BLOCKING|RUNNING) -> RUNNING */ + return mono_threads_enter_gc_unsafe_region (dummy); + } +} + +/* + * 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 + */ +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; + + domain = mono_domain_get (); + g_assert (domain); + + /* 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); + } + } +}