#include <mono/utils/mono-tls.h>
#include <mono/utils/atomic.h>
#include <mono/utils/mono-memory-model.h>
+#include <mono/utils/mono-threads-coop.h>
+#include <mono/utils/mono-error-internals.h>
#include <mono/metadata/gc-internals.h>
#include <mono/metadata/reflection-internals.h>
#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 *);
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);
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;
}
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.
}
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);
}
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);
}
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);
/* 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
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);
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
*/
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;
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);
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;
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);
g_error ("Thread %"G_GSIZE_FORMAT" calling into managed code is not registered with the GC. On UNIX, this can be fixed by #include-ing <gc.h> before <pthread.h> in the file containing the thread creation code.", mono_native_thread_id_get ());
}
- 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);
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 */
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);
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;
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;
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.");
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);
}
}
MonoThread *
mono_thread_current (void)
{
- MonoError error;
MonoDomain *domain = mono_domain_get ();
MonoInternalThread *internal = mono_thread_internal_current ();
MonoThread **current_thread_ptr;
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;
}
static MonoThread *
mono_thread_current_for_thread (MonoInternalThread *internal)
{
- MonoError error;
MonoDomain *domain = mono_domain_get ();
MonoThread **current_thread_ptr;
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;
}
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);
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);
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;
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;
gint32 ves_icall_System_Threading_WaitHandle_WaitAll_internal(MonoArray *mono_handles, gint32 ms)
{
+ MonoError error;
HANDLE *handles;
guint32 numhandles;
guint32 ret;
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);
}
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;
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);
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 ();
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
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)
}
}
-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;
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);
(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;
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
return found;
}
-void mono_thread_internal_stop (MonoInternalThread *thread)
+static gboolean
+request_thread_stop (MonoInternalThread *thread)
{
LOCK_THREAD (thread);
(thread->state & ThreadState_Stopped) != 0)
{
UNLOCK_THREAD (thread);
- return;
+ return FALSE;
}
/* Make sure the thread is awake */
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
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() */
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() */
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));
#endif
abort_appdomain_data user_data;
- guint32 start_time;
+ gint64 start_time;
int orig_timeout = timeout;
int i;
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
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
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;
}
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 {
{
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);
+ }
+ }
+}