#include <mono/utils/mono-memory-model.h>
#include <mono/utils/mono-threads-coop.h>
#include <mono/utils/mono-error-internals.h>
-#include <mono/utils/w32handle.h>
+#include <mono/utils/os-event.h>
+#include <mono/utils/mono-threads-debug.h>
+#include <mono/metadata/w32handle.h>
#include <mono/metadata/w32event.h>
#include <mono/metadata/w32mutex.h>
static gint32 thread_interruption_requested = 0;
/* Event signaled when a thread changes its background mode */
-static HANDLE background_change_event;
+static MonoOSEvent background_change_event;
static gboolean shutting_down = FALSE;
return klass == mono_defaults.threadabortexception_class;
}
-/*
- * NOTE: this function can be called also for threads different from the current one:
- * make sure no code called from it will ever assume it is run on the thread that is
- * getting cleaned up.
- */
-static void thread_cleanup (MonoInternalThread *thread)
-{
- gboolean ret;
-
- g_assert (thread != NULL);
-
- 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 (InterlockedExchange (&thread->interruption_requested, 0))
- InterlockedDecrement (&thread_interruption_requested);
-
- mono_threads_lock ();
-
- if (!threads) {
- ret = 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.
- */
- ret = FALSE;
- } else {
- mono_g_hash_table_remove (threads, (gpointer)thread->tid);
- ret = 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 thread_cleanup() 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 (!ret) {
- if (thread == mono_thread_internal_current ()) {
- mono_domain_unset ();
- mono_memory_barrier ();
- }
- if (mono_thread_cleanup_fn)
- mono_thread_cleanup_fn (thread_get_tid (thread));
- return;
- }
- 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);
-
- if (thread == mono_thread_internal_current ()) {
- /*
- * 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;
-
- if (mono_thread_cleanup_fn)
- mono_thread_cleanup_fn (thread_get_tid (thread));
-
- if (mono_gc_is_moving ()) {
- MONO_GC_UNREGISTER_ROOT (thread->thread_pinning_ref);
- thread->thread_pinning_ref = NULL;
- }
-
-}
-
/*
* A special static data offset (guint32) consists of 3 parts:
*
}
static MonoThread*
-create_thread_object (MonoDomain *domain)
+create_thread_object (MonoDomain *domain, MonoInternalThread *internal)
{
+ MonoThread *thread;
+ MonoVTable *vtable;
MonoError error;
- MonoVTable *vt = mono_class_vtable (domain, mono_defaults.thread_class);
- 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)
-{
- MonoThread *thread;
+ vtable = mono_class_vtable (domain, mono_defaults.thread_class);
+ g_assert (vtable);
- thread = create_thread_object (domain);
+ thread = (MonoThread*)mono_object_new_mature (vtable, &error);
+ /* only possible failure mode is OOM, from which we don't expect to recover. */
+ mono_error_assert_ok (&error);
MONO_OBJECT_SETREF (thread, internal_thread, internal);
}
static MonoInternalThread*
-create_internal_thread (void)
+create_internal_thread_object (void)
{
MonoError error;
MonoInternalThread *thread;
thread->priority = MONO_THREAD_PRIORITY_NORMAL;
+ thread->suspended = g_new0 (MonoOSEvent, 1);
+ mono_os_event_init (thread->suspended, TRUE);
+
return thread;
}
mono_thread_internal_set_priority (MonoInternalThread *internal, MonoThreadPriority priority)
{
g_assert (internal);
- g_assert (internal->handle);
g_assert (priority >= MONO_THREAD_PRIORITY_LOWEST);
g_assert (priority <= MONO_THREAD_PRIORITY_HIGHEST);
#ifdef HOST_WIN32
BOOL res;
- res = SetThreadPriority (internal->handle, priority - 2);
+ g_assert (internal->native_handle);
+
+ res = SetThreadPriority (internal->native_handle, priority - 2);
if (!res)
g_error ("%s: SetThreadPriority failed, error %d", __func__, GetLastError ());
#else /* HOST_WIN32 */
info = mono_thread_info_current ();
internal = thread->internal_thread;
- internal->handle = mono_thread_info_duplicate_handle (info);
+ internal->handle = mono_threads_open_thread_handle (info->handle);
+#ifdef HOST_WIN32
+ internal->native_handle = OpenThread (THREAD_ALL_ACCESS, FALSE, GetCurrentThreadId ());
+#endif
internal->tid = MONO_NATIVE_THREAD_ID_TO_UINT (mono_native_thread_id_get ());
internal->thread_info = info;
internal->small_id = info->small_id;
g_assert (!internal->root_domain_thread);
if (domain != root_domain)
- MONO_OBJECT_SETREF (internal, root_domain_thread, new_thread_with_internal (root_domain, internal));
+ MONO_OBJECT_SETREF (internal, root_domain_thread, create_thread_object (root_domain, internal));
else
MONO_OBJECT_SETREF (internal, root_domain_thread, thread);
/* If the thread calls ExitThread at all, this remaining code
* will not be executed, but the main thread will eventually
- * call thread_cleanup() on this thread's behalf.
+ * call mono_thread_detach_internal() on this thread's behalf.
*/
THREAD_DEBUG (g_message ("%s: (%"G_GSIZE_FORMAT") Start wrapper terminating", __func__, mono_native_thread_id_get ()));
/* Do any cleanup needed for apartment state. This
- * cannot be done in thread_cleanup since thread_cleanup could be
- * called for a thread other than the current thread.
+ * cannot be done in mono_thread_detach_internal since
+ * mono_thread_detach_internal could be called for a
+ * thread other than the current thread.
* mono_thread_cleanup_apartment_state cleans up apartment
* for the current thead */
mono_thread_cleanup_apartment_state ();
{
volatile gsize dummy;
- /* Avoid scanning the frames above this frame during a GC */
- mono_gc_set_stack_end ((void*)&dummy);
-
return start_wrapper_internal ((StartInfo*) data, (gsize*) &dummy);
}
gboolean threadpool_thread, guint32 stack_size, MonoError *error)
{
StartInfo *start_info = NULL;
- HANDLE thread_handle;
+ MonoThreadHandle *thread_handle;
MonoNativeThreadId tid;
gboolean ret;
gsize stack_set_size;
mono_error_init (error);
- thread = create_thread_object (domain);
+ internal = create_internal_thread_object ();
- internal = create_internal_thread ();
-
- MONO_OBJECT_SETREF (thread, internal_thread, internal);
+ thread = create_thread_object (domain, internal);
LOCK_THREAD (internal);
tid=mono_native_thread_id_get ();
- internal = create_internal_thread ();
+ internal = create_internal_thread_object ();
- thread = new_thread_with_internal (domain, internal);
+ thread = create_thread_object (domain, internal);
if (!mono_thread_attach_internal (thread, force_attach, TRUE, &stack_ptr)) {
/* Mono is shutting down, so just wait for the end */
void
mono_thread_detach_internal (MonoInternalThread *thread)
{
- g_return_if_fail (thread != NULL);
+ gboolean removed;
+
+ g_assert (thread != NULL);
THREAD_DEBUG (g_message ("%s: mono_thread_detach for %p (%"G_GSIZE_FORMAT")", __func__, thread, (gsize)thread->tid));
mono_w32mutex_abandon ();
#endif
- thread_cleanup (thread);
+ 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 (InterlockedExchange (&thread->interruption_requested, 0) != 0)
+ 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));
+
+ 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 ();
if (mono_thread_get_main () && (thread == mono_thread_get_main ()->internal_thread))
exit (mono_environment_exitcode_get ());
- mono_thread_info_exit ();
+ mono_thread_info_exit (0);
}
void
{
MonoInternalThread *internal;
- internal = create_internal_thread ();
+ internal = create_internal_thread_object ();
internal->state = ThreadState_Unstarted;
InterlockedCompareExchangePointer ((volatile gpointer *)&this_obj->internal_thread, internal, NULL);
}
+MonoThread *
+ves_icall_System_Threading_Thread_GetCurrentThread (void)
+{
+ return mono_thread_current ();
+}
+
HANDLE
ves_icall_System_Threading_Thread_Thread_internal (MonoThread *this_obj,
MonoObject *start)
* This is called from the finalizer of the internal thread object.
*/
void
-ves_icall_System_Threading_InternalThread_Thread_free_internal (MonoInternalThread *this_obj, HANDLE thread)
+ves_icall_System_Threading_InternalThread_Thread_free_internal (MonoInternalThread *this_obj)
{
- THREAD_DEBUG (g_message ("%s: Closing thread %p, handle %p", __func__, this, thread));
+ THREAD_DEBUG (g_message ("%s: Closing thread %p, handle %p", __func__, this, this_obj->handle));
/*
- * Since threads keep a reference to their thread object while running, by the time this function is called,
- * the thread has already exited/detached, i.e. thread_cleanup () has ran. The exception is during shutdown,
- * when thread_cleanup () can be called after this.
+ * Since threads keep a reference to their thread object while running, by
+ * the time this function is called, the thread has already exited/detached,
+ * i.e. mono_thread_detach_internal () has ran. The exception is during
+ * shutdown, when mono_thread_detach_internal () can be called after this.
*/
- if (thread)
- mono_threads_close_thread_handle (thread);
+ if (this_obj->handle) {
+ mono_threads_close_thread_handle (this_obj->handle);
+ this_obj->handle = NULL;
+ }
+
+#if HOST_WIN32
+ CloseHandle (this_obj->native_handle);
+#endif
if (this_obj->synch_cs) {
MonoCoopMutex *synch_cs = this_obj->synch_cs;
if (exc) {
mono_raise_exception (exc);
} else {
- // FIXME: !INFINITE
- if (ms != INFINITE)
+ // FIXME: !MONO_INFINITE_WAIT
+ if (ms != MONO_INFINITE_WAIT)
break;
}
} else {
LOCK_THREAD (internal);
internal->priority = priority;
- if (internal->handle != NULL)
+ if (internal->thread_info != NULL)
mono_thread_internal_set_priority (internal, priority);
UNLOCK_THREAD (internal);
}
if (!*current_thread_ptr) {
g_assert (domain != mono_get_root_domain ());
- *current_thread_ptr = new_thread_with_internal (domain, internal);
+ *current_thread_ptr = create_thread_object (domain, internal);
}
return *current_thread_ptr;
}
if (!*current_thread_ptr) {
g_assert (domain != mono_get_root_domain ());
- *current_thread_ptr = new_thread_with_internal (domain, internal);
+ *current_thread_ptr = create_thread_object (domain, internal);
}
return *current_thread_ptr;
}
return res;
}
+static MonoThreadInfoWaitRet
+mono_join_uninterrupted (MonoThreadHandle* thread_to_join, gint32 ms, MonoError *error)
+{
+ MonoException *exc;
+ MonoThreadInfoWaitRet ret;
+ gint64 start;
+ gint32 diff_ms;
+ gint32 wait = ms;
+
+ mono_error_init (error);
+
+ start = (ms == -1) ? 0 : mono_msec_ticks ();
+ for (;;) {
+ MONO_ENTER_GC_SAFE;
+ ret = mono_thread_info_wait_one_handle (thread_to_join, ms, TRUE);
+ MONO_EXIT_GC_SAFE;
+
+ if (ret != MONO_THREAD_INFO_WAIT_RET_ALERTED)
+ return ret;
+
+ exc = mono_thread_execute_interruption ();
+ if (exc) {
+ mono_error_set_exception_instance (error, exc);
+ return ret;
+ }
+
+ if (ms == -1)
+ continue;
+
+ /* Re-calculate ms according to the time passed */
+ diff_ms = (gint32)(mono_msec_ticks () - start);
+ if (diff_ms >= ms) {
+ ret = MONO_THREAD_INFO_WAIT_RET_TIMEOUT;
+ return ret;
+ }
+ wait = ms - diff_ms;
+ }
+
+ return ret;
+}
+
gboolean
ves_icall_System_Threading_Thread_Join_internal(MonoThread *this_obj, int ms)
{
MonoInternalThread *thread = this_obj->internal_thread;
- HANDLE handle = thread->handle;
+ MonoThreadHandle *handle = thread->handle;
MonoInternalThread *cur_thread = mono_thread_internal_current ();
gboolean ret;
+ MonoError error;
if (mono_thread_current_check_pending_interrupt ())
return FALSE;
UNLOCK_THREAD (thread);
if(ms== -1) {
- ms=INFINITE;
+ 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);
- MONO_ENTER_GC_SAFE;
- ret=WaitForSingleObjectEx (handle, ms, TRUE);
- MONO_EXIT_GC_SAFE;
+ ret=mono_join_uninterrupted (handle, ms, &error);
mono_thread_clr_state (cur_thread, ThreadState_WaitSleepJoin);
-
- if(ret==WAIT_OBJECT_0) {
+
+ mono_error_set_pending_exception (&error);
+
+ if(ret==MONO_THREAD_INFO_WAIT_RET_SUCCESS_0) {
THREAD_DEBUG (g_message ("%s: join successful", __func__));
return(TRUE);
#define MANAGED_WAIT_FAILED 0x7fffffff
static gint32
-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;
+map_native_wait_result_to_managed (MonoW32HandleWaitRet val, gsize numobjects)
+{
+ if (val >= MONO_W32HANDLE_WAIT_RET_SUCCESS_0 && val < MONO_W32HANDLE_WAIT_RET_SUCCESS_0 + numobjects) {
+ return WAIT_OBJECT_0 + (val - MONO_W32HANDLE_WAIT_RET_SUCCESS_0);
+ } else if (val >= MONO_W32HANDLE_WAIT_RET_ABANDONED_0 && val < MONO_W32HANDLE_WAIT_RET_ABANDONED_0 + numobjects) {
+ return WAIT_ABANDONED_0 + (val - MONO_W32HANDLE_WAIT_RET_ABANDONED_0);
+ } else if (val == MONO_W32HANDLE_WAIT_RET_ALERTED) {
+ return WAIT_IO_COMPLETION;
+ } else if (val == MONO_W32HANDLE_WAIT_RET_TIMEOUT) {
+ return WAIT_TIMEOUT;
+ } else if (val == MONO_W32HANDLE_WAIT_RET_FAILED) {
+ /* WAIT_FAILED in waithandle.cs is different from WAIT_FAILED in Win32 API */
+ return MANAGED_WAIT_FAILED;
+ } else {
+ g_error ("%s: unknown val value %d", __func__, val);
+ }
}
-static gint32
+static MonoW32HandleWaitRet
mono_wait_uninterrupted (MonoInternalThread *thread, guint32 numhandles, gpointer *handles, gboolean waitall, gint32 ms, MonoError *error)
{
MonoException *exc;
- guint32 ret;
+ MonoW32HandleWaitRet ret;
gint64 start;
gint32 diff_ms;
gint32 wait = ms;
start = (ms == -1) ? 0 : mono_100ns_ticks ();
do {
MONO_ENTER_GC_SAFE;
+#ifdef HOST_WIN32
if (numhandles != 1)
- ret = WaitForMultipleObjectsEx (numhandles, handles, waitall, wait, TRUE);
+ ret = mono_w32handle_convert_wait_ret (WaitForMultipleObjectsEx (numhandles, handles, waitall, wait, TRUE), numhandles);
else
- ret = WaitForSingleObjectEx (handles [0], ms, TRUE);
+ ret = mono_w32handle_convert_wait_ret (WaitForSingleObjectEx (handles [0], ms, TRUE), 1);
+#else
+ /* mono_w32handle_wait_multiple optimizes the case for numhandles == 1 */
+ ret = mono_w32handle_wait_multiple (handles, numhandles, waitall, wait, TRUE);
+#endif /* HOST_WIN32 */
MONO_EXIT_GC_SAFE;
- if (ret != WAIT_IO_COMPLETION)
+ if (ret != MONO_W32HANDLE_WAIT_RET_ALERTED)
break;
exc = mono_thread_execute_interruption ();
/* Re-calculate ms according to the time passed */
diff_ms = (gint32)((mono_100ns_ticks () - start) / 10000);
if (diff_ms >= ms) {
- ret = WAIT_TIMEOUT;
+ ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
break;
}
wait = ms - diff_ms;
MonoError error;
HANDLE *handles;
guint32 numhandles;
- guint32 ret;
+ 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 (WAIT_FAILED);
+ 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);
}
if(ms== -1) {
- ms=INFINITE;
+ ms=MONO_INFINITE_WAIT;
}
mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
mono_error_set_pending_exception (&error);
- /* WAIT_FAILED in waithandle.cs is different from WAIT_FAILED in Win32 API */
- return map_native_wait_result_to_managed (ret);
+ 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;
- guint32 ret;
+ 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 (WAIT_FAILED);
+ return map_native_wait_result_to_managed (MONO_W32HANDLE_WAIT_RET_FAILED, 0);
numhandles = mono_array_length(mono_handles);
if (numhandles > MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS)
- return map_native_wait_result_to_managed (WAIT_FAILED);
+ 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);
}
if(ms== -1) {
- ms=INFINITE;
+ ms=MONO_INFINITE_WAIT;
}
mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
mono_error_set_pending_exception (&error);
- /* WAIT_FAILED in waithandle.cs is different from WAIT_FAILED in Win32 API */
- return map_native_wait_result_to_managed (ret);
+ return map_native_wait_result_to_managed (ret, numhandles);
}
gint32 ves_icall_System_Threading_WaitHandle_WaitOne_internal(HANDLE handle, gint32 ms)
{
MonoError error;
- guint32 ret;
+ 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=INFINITE;
+ ms=MONO_INFINITE_WAIT;
}
if (mono_thread_current_check_pending_interrupt ())
- return map_native_wait_result_to_managed (WAIT_FAILED);
+ return map_native_wait_result_to_managed (MONO_W32HANDLE_WAIT_RET_FAILED, 0);
mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
mono_error_set_pending_exception (&error);
- return map_native_wait_result_to_managed (ret);
+ return map_native_wait_result_to_managed (ret, 1);
}
gint32
ves_icall_System_Threading_WaitHandle_SignalAndWait_Internal (HANDLE toSignal, HANDLE toWait, gint32 ms)
{
- guint32 ret;
+ MonoW32HandleWaitRet ret;
MonoInternalThread *thread = mono_thread_internal_current ();
if (ms == -1)
- ms = INFINITE;
+ ms = MONO_INFINITE_WAIT;
if (mono_thread_current_check_pending_interrupt ())
- return map_native_wait_result_to_managed (WAIT_FAILED);
+ return map_native_wait_result_to_managed (MONO_W32HANDLE_WAIT_RET_FAILED, 0);
mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
MONO_ENTER_GC_SAFE;
- ret = SignalObjectAndWait (toSignal, toWait, ms, TRUE);
+#ifdef HOST_WIN32
+ ret = mono_w32handle_convert_wait_ret (SignalObjectAndWait (toSignal, toWait, ms, TRUE), 1);
+#else
+ ret = mono_w32handle_signal_and_wait (toSignal, toWait, ms, TRUE);
+#endif
MONO_EXIT_GC_SAFE;
mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
- return map_native_wait_result_to_managed (ret);
+ return map_native_wait_result_to_managed (ret, 1);
}
gint32 ves_icall_System_Threading_Interlocked_Increment_Int (gint32 *location)
* be notified, since it has to rebuild the list of threads to
* wait for.
*/
- mono_w32event_set (background_change_event);
+ mono_os_event_set (&background_change_event);
}
}
* be notified, since it has to rebuild the list of threads to
* wait for.
*/
- mono_w32event_set (background_change_event);
+ mono_os_event_set (&background_change_event);
}
}
mono_set_pending_exception (mono_get_exception_thread_state (msg));
return;
}
+
+ mono_get_eh_callbacks ()->mono_clear_abort_threshold ();
thread->abort_exc = NULL;
if (thread->abort_state_handle) {
mono_gchandle_free (thread->abort_state_handle);
thread->state &= ~ThreadState_AbortRequested;
if (thread->abort_exc) {
+ mono_get_eh_callbacks ()->mono_clear_abort_threshold ();
thread->abort_exc = NULL;
if (thread->abort_state_handle) {
mono_gchandle_free (thread->abort_state_handle);
}
thread->state |= ThreadState_SuspendRequested;
+ mono_os_event_reset (thread->suspended);
if (thread == mono_thread_internal_current ()) {
/* calls UNLOCK_THREAD (thread) */
mono_thread_resume (MonoInternalThread *thread)
{
if ((thread->state & ThreadState_SuspendRequested) != 0) {
+ // MOSTLY_ASYNC_SAFE_PRINTF ("RESUME (1) thread %p\n", thread_get_tid (thread));
thread->state &= ~ThreadState_SuspendRequested;
+ mono_os_event_set (thread->suspended);
return TRUE;
}
(thread->state & ThreadState_Aborted) != 0 ||
(thread->state & ThreadState_Stopped) != 0)
{
+ // MOSTLY_ASYNC_SAFE_PRINTF ("RESUME (2) thread %p\n", thread_get_tid (thread));
return FALSE;
}
- UNLOCK_THREAD (thread);
+ // MOSTLY_ASYNC_SAFE_PRINTF ("RESUME (3) thread %p\n", thread_get_tid (thread));
- /* Awake the thread */
- if (!mono_thread_info_resume (thread_get_tid (thread)))
- return FALSE;
+ mono_os_event_set (thread->suspended);
- LOCK_THREAD (thread);
+ if (!thread->self_suspended) {
+ UNLOCK_THREAD (thread);
+
+ /* Awake the thread */
+ if (!mono_thread_info_resume (thread_get_tid (thread)))
+ return FALSE;
+
+ LOCK_THREAD (thread);
+ }
thread->state &= ~ThreadState_Suspended;
mono_os_mutex_init_recursive(&interlocked_mutex);
mono_os_mutex_init_recursive(&joinable_threads_mutex);
- background_change_event = mono_w32event_create (TRUE, FALSE);
- g_assert(background_change_event != NULL);
+ mono_os_event_init (&background_change_event, FALSE);
mono_init_static_data_info (&thread_static_info);
mono_init_static_data_info (&context_static_info);
mono_thread_start_cb = start_cb;
mono_thread_attach_cb = attach_cb;
-
- /* Get a pseudo handle to the current process. This is just a
- * kludge so that wapi can build a process handle if needed.
- * As a pseudo handle is returned, we don't need to clean
- * anything up.
- */
- GetCurrentProcess ();
}
void mono_thread_cleanup (void)
{
-#if !defined(RUN_IN_SUBTHREAD)
+#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
* processes, see bug 74680.) This will happen when the
* thread exits, but if it's not running in a subthread it
* won't exit in time.
*/
- mono_thread_info_set_exited (mono_thread_info_current ());
+ mono_w32mutex_abandon ();
#endif
#if 0
mono_os_mutex_destroy (&interlocked_mutex);
mono_os_mutex_destroy (&delayed_free_table_mutex);
mono_os_mutex_destroy (&small_id_mutex);
- CloseHandle (background_change_event);
+ mono_os_event_destroy (&background_change_event);
#endif
mono_native_tls_free (current_object_key);
struct wait_data
{
- HANDLE handles[MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS];
+ MonoThreadHandle *handles[MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS];
MonoInternalThread *threads[MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS];
guint32 num;
};
static void
-wait_for_tids (struct wait_data *wait, guint32 timeout)
+wait_for_tids (struct wait_data *wait, guint32 timeout, gboolean check_state_change)
{
- guint32 i, ret;
+ guint32 i;
+ MonoThreadInfoWaitRet ret;
THREAD_DEBUG (g_message("%s: %d threads to wait for in this batch", __func__, wait->num));
- MONO_ENTER_GC_SAFE;
- ret=WaitForMultipleObjectsEx(wait->num, wait->handles, TRUE, timeout, TRUE);
- MONO_EXIT_GC_SAFE;
-
- if(ret==WAIT_FAILED) {
- /* See the comment in build_wait_tids() */
- THREAD_DEBUG (g_message ("%s: Wait failed", __func__));
- return;
- }
-
- for(i=0; i<wait->num; i++)
- mono_threads_close_thread_handle (wait->handles [i]);
-
- if (ret == WAIT_TIMEOUT)
- return;
-
- for(i=0; i<wait->num; i++) {
- MonoInternalThread *internal;
-
- internal = wait->threads [i];
-
- mono_threads_lock ();
- if (mono_g_hash_table_lookup (threads, (gpointer) internal->tid) == internal)
- g_error ("%s: failed to call mono_thread_detach_internal on thread %p, InternalThread: %p", __func__, internal->tid, internal);
- mono_threads_unlock ();
- }
-}
-
-static void wait_for_tids_or_state_change (struct wait_data *wait, guint32 timeout)
-{
- guint32 i, ret, count;
-
- THREAD_DEBUG (g_message("%s: %d threads to wait for in this batch", __func__, wait->num));
-
- /* Add the thread state change event, so it wakes up if a thread changes
- * to background mode.
- */
- count = wait->num;
- if (count < MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS) {
- wait->handles [count] = background_change_event;
- count++;
- }
+ /* Add the thread state change event, so it wakes
+ * up if a thread changes to background mode. */
MONO_ENTER_GC_SAFE;
- ret=WaitForMultipleObjectsEx (count, wait->handles, FALSE, timeout, TRUE);
+ if (check_state_change)
+ ret = mono_thread_info_wait_multiple_handle (wait->handles, wait->num, &background_change_event, FALSE, timeout, TRUE);
+ else
+ ret = mono_thread_info_wait_multiple_handle (wait->handles, wait->num, NULL, TRUE, timeout, TRUE);
MONO_EXIT_GC_SAFE;
- if(ret==WAIT_FAILED) {
+ if (ret == MONO_THREAD_INFO_WAIT_RET_FAILED) {
/* See the comment in build_wait_tids() */
THREAD_DEBUG (g_message ("%s: Wait failed", __func__));
return;
}
- for(i=0; i<wait->num; i++)
+ for( i = 0; i < wait->num; i++)
mono_threads_close_thread_handle (wait->handles [i]);
- if (ret == WAIT_TIMEOUT)
+ if (ret == MONO_THREAD_INFO_WAIT_RET_TIMEOUT)
return;
if (ret < wait->num) {
{
struct wait_data *wait=(struct wait_data *)user;
- if(wait->num<MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS) {
- HANDLE handle;
+ if(wait->num<MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS - 1) {
MonoInternalThread *thread=(MonoInternalThread *)value;
/* Ignore background threads, we abort them later */
return;
}
- handle = mono_threads_open_thread_handle (thread->handle, thread_get_tid (thread));
- if (handle == NULL) {
- THREAD_DEBUG (g_message ("%s: ignoring unopenable thread %"G_GSIZE_FORMAT, __func__, (gsize)thread->tid));
- return;
- }
-
THREAD_DEBUG (g_message ("%s: Invoking mono_thread_manage callback on thread %p", __func__, thread));
if ((thread->manage_callback == NULL) || (thread->manage_callback (thread->root_domain_thread) == TRUE)) {
- wait->handles[wait->num]=handle;
+ wait->handles[wait->num]=mono_threads_open_thread_handle (thread->handle);
wait->threads[wait->num]=thread;
wait->num++;
struct wait_data *wait=(struct wait_data *)user;
MonoNativeThreadId self = mono_native_thread_id_get ();
MonoInternalThread *thread = (MonoInternalThread *)value;
- HANDLE handle;
if (wait->num >= MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS)
return FALSE;
&& (thread->state & ThreadState_Background) != 0
&& (thread->flags & MONO_THREAD_FLAG_DONT_MANAGE) == 0
) {
- handle = mono_threads_open_thread_handle (thread->handle, thread_get_tid (thread));
- if (handle == NULL)
- return FALSE;
-
- wait->handles[wait->num] = handle;
+ wait->handles[wait->num] = mono_threads_open_thread_handle (thread->handle);
wait->threads[wait->num] = thread;
wait->num++;
mono_thread_detach_internal (current_thread);
/* Wake up other threads potentially waiting for us */
- mono_thread_info_exit ();
+ mono_thread_info_exit (0);
} else {
shutting_down = TRUE;
* interrupt the main thread if it is waiting for all
* the other threads.
*/
- mono_w32event_set (background_change_event);
+ mono_os_event_set (&background_change_event);
mono_threads_unlock ();
}
THREAD_DEBUG (g_message ("%s: There are %d threads to join", __func__, mono_g_hash_table_size (threads));
mono_g_hash_table_foreach (threads, print_tids, NULL));
- mono_w32event_reset (background_change_event);
+ mono_os_event_reset (&background_change_event);
wait->num=0;
- /*We must zero all InternalThread pointers to avoid making the GC unhappy.*/
+ /* We must zero all InternalThread pointers to avoid making the GC unhappy. */
memset (wait->threads, 0, MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS * SIZEOF_VOID_P);
mono_g_hash_table_foreach (threads, build_wait_tids, wait);
mono_threads_unlock ();
- if(wait->num>0) {
+ if (wait->num > 0)
/* Something to wait for */
- wait_for_tids_or_state_change (wait, INFINITE);
- }
+ wait_for_tids (wait, MONO_INFINITE_WAIT, TRUE);
THREAD_DEBUG (g_message ("%s: I have %d threads after waiting.", __func__, wait->num));
} while(wait->num>0);
mono_threads_unlock ();
THREAD_DEBUG (g_message ("%s: wait->num is now %d", __func__, wait->num));
- if(wait->num>0) {
+ if (wait->num > 0) {
/* Something to wait for */
- wait_for_tids (wait, INFINITE);
+ wait_for_tids (wait, MONO_INFINITE_WAIT, FALSE);
}
} while (wait->num > 0);
{
MonoInternalThread *thread = (MonoInternalThread*)value;
struct wait_data *wait = (struct wait_data*)user_data;
- HANDLE handle;
/*
* We try to exclude threads early, to avoid running into the MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS
return;
if (wait->num<MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS) {
- handle = mono_threads_open_thread_handle (thread->handle, thread_get_tid (thread));
- if (handle == NULL)
- return;
-
- wait->handles [wait->num] = handle;
+ wait->handles [wait->num] = mono_threads_open_thread_handle (thread->handle);
wait->threads [wait->num] = thread;
wait->num++;
}
|| mono_gc_is_finalizer_internal_thread (thread)
|| (thread->flags & MONO_THREAD_FLAG_DONT_MANAGE)
) {
- //mono_threads_close_thread_handle (wait->handles [i]);
- wait->threads [i] = NULL; /* ignore this thread in next loop */
+ mono_threads_close_thread_handle (wait->handles [i]);
+ wait->threads [i] = NULL;
continue;
}
(thread->state & ThreadState_Stopped) != 0) {
UNLOCK_THREAD (thread);
mono_threads_close_thread_handle (wait->handles [i]);
- wait->threads [i] = NULL; /* ignore this thread in next loop */
+ wait->threads [i] = NULL;
continue;
}
thread->state &= ~ThreadState_AbortRequested;
thread->state |= ThreadState_SuspendRequested;
+ mono_os_event_reset (thread->suspended);
/* Signal the thread to suspend + calls UNLOCK_THREAD (thread) */
async_suspend_internal (thread, TRUE);
+
+ mono_threads_close_thread_handle (wait->handles [i]);
+ wait->threads [i] = NULL;
}
if (eventidx <= 0) {
/*
/* printf ("ABORTING THREAD %p BECAUSE IT REFERENCES DOMAIN %s.\n", thread->tid, domain->friendly_name); */
if(data->wait.num<MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS) {
- HANDLE handle = mono_threads_open_thread_handle (thread->handle, thread_get_tid (thread));
- if (handle == NULL)
- return;
- data->wait.handles [data->wait.num] = handle;
+ data->wait.handles [data->wait.num] = mono_threads_open_thread_handle (thread->handle);
data->wait.threads [data->wait.num] = thread;
data->wait.num++;
} else {
* We should wait for the threads either to abort, or to leave the
* domain. We can't do the latter, so we wait with a timeout.
*/
- wait_for_tids (&user_data.wait, 100);
+ wait_for_tids (&user_data.wait, 100, FALSE);
}
/* Update remaining time */
{
MonoInternalThread *thread = mono_thread_internal_current ();
- if (thread && thread->abort_exc && !is_running_protected_wrapper ()) {
- /*
- * FIXME: Clear the abort exception and return an AppDomainUnloaded
- * exception if the thread no longer references a dying appdomain.
- */
- thread->abort_exc->trace_ips = NULL;
- thread->abort_exc->stack_trace = NULL;
- return thread->abort_exc;
- }
+ if (!(thread && thread->abort_exc && !is_running_protected_wrapper ()))
+ return NULL;
- return NULL;
+ // We don't want to have our exception effect calls made by
+ // the catching block
+
+ if (!mono_get_eh_callbacks ()->mono_above_abort_threshold ())
+ return NULL;
+
+ /*
+ * FIXME: Clear the abort exception and return an AppDomainUnloaded
+ * exception if the thread no longer references a dying appdomain.
+ */
+ thread->abort_exc->trace_ips = NULL;
+ thread->abort_exc->stack_trace = NULL;
+ return thread->abort_exc;
}
#if MONO_SMALL_CONFIG
or similar */
/* Our implementation of this function ignores the func argument */
#ifdef HOST_WIN32
- QueueUserAPC ((PAPCFUNC)dummy_apc, thread->handle, (ULONG_PTR)NULL);
+ QueueUserAPC ((PAPCFUNC)dummy_apc, thread->native_handle, (ULONG_PTR)NULL);
#else
mono_thread_info_self_interrupt ();
#endif
MonoInternalThread *thread = mono_thread_internal_current ();
/* The thread may already be stopping */
- if (thread == NULL)
+ if (!thread)
+ return NULL;
+ if (!thread->interruption_requested)
+ return NULL;
+ if (!bypass_abort_protection && is_running_protected_wrapper ())
return NULL;
- if (thread->interruption_requested && (bypass_abort_protection || !is_running_protected_wrapper ())) {
- MonoException* exc = mono_thread_execute_interruption ();
- if (exc)
- return exc;
- }
- return NULL;
+ return mono_thread_execute_interruption ();
}
/*
static void
self_interrupt_thread (void *_unused)
{
- MonoThreadInfo *info = mono_thread_info_current ();
- MonoException *exc = mono_thread_execute_interruption ();
- if (exc) /*We must use _with_context since we didn't trampoline into the runtime*/
- mono_raise_exception_with_context (exc, &info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX].ctx); /* FIXME using thread_saved_state [ASYNC_SUSPEND_STATE_INDEX] can race with another suspend coming in. */
- g_assert_not_reached (); /*this MUST not happen since we can't resume from an async call*/
+ MonoException *exc;
+ MonoThreadInfo *info;
+
+ exc = mono_thread_execute_interruption ();
+ if (!exc) {
+ if (mono_threads_is_coop_enabled ()) {
+ /* We can return from an async call in coop, as
+ * it's simply called when exiting the safepoint */
+ return;
+ }
+
+ g_error ("%s: we can't resume from an async call", __func__);
+ }
+
+ info = mono_thread_info_current ();
+
+ /* We must use _with_context since we didn't trampoline into the runtime */
+ mono_raise_exception_with_context (exc, &info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX].ctx); /* FIXME using thread_saved_state [ASYNC_SUSPEND_STATE_INDEX] can race with another suspend coming in. */
}
static gboolean
running_managed = mono_jit_info_match (ji, MONO_CONTEXT_GET_IP (&mono_thread_info_get_suspend_state (info)->ctx));
if (running_managed && !protected_wrapper) {
- thread->state &= ~ThreadState_SuspendRequested;
- thread->state |= ThreadState_Suspended;
- return KeepSuspended;
+ if (mono_threads_is_coop_enabled ()) {
+ mono_thread_info_setup_async_call (info, self_interrupt_thread, NULL);
+ return MonoResumeThread;
+ } else {
+ thread->state &= ~ThreadState_SuspendRequested;
+ thread->state |= ThreadState_Suspended;
+ return KeepSuspended;
+ }
} else {
if (InterlockedCompareExchange (&thread->interruption_requested, 1, 0) == 0)
InterlockedIncrement (&thread_interruption_requested);
g_assert (thread != mono_thread_internal_current ());
+ // MOSTLY_ASYNC_SAFE_PRINTF ("ASYNC SUSPEND thread %p\n", thread_get_tid (thread));
+
+ thread->self_suspended = FALSE;
+
data.thread = thread;
data.interrupt = interrupt;
data.interrupt_token = NULL;
self_suspend_internal (void)
{
MonoInternalThread *thread;
+ MonoOSEvent *event;
+ MonoOSEventWaitRet res;
thread = mono_thread_internal_current ();
- mono_thread_info_begin_self_suspend ();
+ // MOSTLY_ASYNC_SAFE_PRINTF ("SELF SUSPEND thread %p\n", thread_get_tid (thread));
+
+ thread->self_suspended = TRUE;
+
thread->state &= ~ThreadState_SuspendRequested;
thread->state |= ThreadState_Suspended;
UNLOCK_THREAD (thread);
- mono_thread_info_end_self_suspend ();
+ event = thread->suspended;
+
+ MONO_ENTER_GC_SAFE;
+ res = mono_os_event_wait_one (event, MONO_INFINITE_WAIT);
+ g_assert (res == MONO_OS_EVENT_WAIT_RET_SUCCESS_0 || res == MONO_OS_EVENT_WAIT_RET_ALERTED);
+ MONO_EXIT_GC_SAFE;
}
/*