-/*
- * mono-threads.c: Low-level threading
+/**
+ * \file
+ * Low-level threading
*
* Author:
* Rodrigo Kumpera (kumpera@gmail.com)
#include <mono/utils/mono-coop-mutex.h>
#include <mono/utils/mono-coop-semaphore.h>
#include <mono/utils/mono-threads-coop.h>
+#include <mono/utils/mono-threads-debug.h>
+#include <mono/utils/os-event.h>
+#include <mono/utils/w32api.h>
#include <errno.h>
static MonoThreadInfoRuntimeCallbacks runtime_callbacks;
static MonoNativeTlsKey thread_info_key, thread_exited_key;
#ifdef HAVE_KW_THREAD
-static __thread guint32 tls_small_id MONO_TLS_FAST;
+static __thread gint32 tls_small_id = -1;
#else
static MonoNativeTlsKey small_id_key;
#endif
static MonoSemType suspend_semaphore;
static size_t pending_suspends;
-static gboolean unified_suspend_enabled;
+
+static mono_mutex_t join_mutex;
#define mono_thread_info_run_state(info) (((MonoThreadInfo*)info)->thread_state & THREAD_STATE_MASK)
/*warn at 50 ms*/
-#define SLEEP_DURATION_BEFORE_WARNING (10)
-/*abort at 1 sec*/
-#define SLEEP_DURATION_BEFORE_ABORT 200
+#define SLEEP_DURATION_BEFORE_WARNING (50)
+/*never aborts */
+#define SLEEP_DURATION_BEFORE_ABORT MONO_INFINITE_WAIT
-static long sleepWarnDuration = SLEEP_DURATION_BEFORE_WARNING,
+static guint32 sleepWarnDuration = SLEEP_DURATION_BEFORE_WARNING,
sleepAbortDuration = SLEEP_DURATION_BEFORE_ABORT;
static int suspend_posts, resume_posts, abort_posts, waits_done, pending_ops;
return TRUE;
}
- return mono_threads_core_begin_async_suspend (info, interrupt_kernel);
+ return mono_threads_suspend_begin_async_suspend (info, interrupt_kernel);
}
static gboolean
return TRUE;
}
- return mono_threads_core_check_suspend_result (info);
+ return mono_threads_suspend_check_suspend_result (info);
}
static void
if (mono_threads_is_coop_enabled ())
g_assert_not_reached ();
- g_assert (mono_threads_core_begin_async_resume (info));
+ g_assert (mono_threads_suspend_begin_async_resume (info));
}
static void
for (i = 0; i < pending_suspends; ++i) {
THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-WAITING]\n");
InterlockedIncrement (&waits_done);
- if (!mono_os_sem_timedwait (&suspend_semaphore, sleepAbortDuration, MONO_SEM_FLAGS_NONE))
+ if (mono_os_sem_timedwait (&suspend_semaphore, sleepAbortDuration, MONO_SEM_FLAGS_NONE) == MONO_SEM_TIMEDWAIT_RET_SUCCESS)
continue;
mono_stopwatch_stop (&suspension_time);
//Thread initialization code
-static void mono_threads_unregister_current_thread (MonoThreadInfo *info);
-
static inline void
mono_hazard_pointer_clear_all (MonoThreadHazardPointers *hp, int retain)
{
MonoThreadInfo*
mono_thread_info_lookup (MonoNativeThreadId id)
{
- MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
+ MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
- if (!mono_lls_find (&thread_list, hp, (uintptr_t)id, HAZARD_FREE_ASYNC_CTX)) {
+ if (!mono_lls_find (&thread_list, hp, (uintptr_t)id)) {
mono_hazard_pointer_clear_all (hp, -1);
return NULL;
}
{
MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
- if (!mono_lls_insert (&thread_list, hp, (MonoLinkedListSetNode*)info, HAZARD_FREE_SAFE_CTX)) {
+ if (!mono_lls_insert (&thread_list, hp, (MonoLinkedListSetNode*)info)) {
mono_hazard_pointer_clear_all (hp, -1);
return FALSE;
}
gboolean res;
THREADS_DEBUG ("removing info %p\n", info);
- res = mono_lls_remove (&thread_list, hp, (MonoLinkedListSetNode*)info, HAZARD_FREE_SAFE_CTX);
+ res = mono_lls_remove (&thread_list, hp, (MonoLinkedListSetNode*)info);
mono_hazard_pointer_clear_all (hp, -1);
return res;
}
MonoThreadInfo *info = (MonoThreadInfo *) mem;
mono_os_sem_destroy (&info->resume_semaphore);
- mono_threads_platform_free (info);
+ mono_threads_suspend_free (info);
g_free (info);
}
+/*
+ * mono_thread_info_register_small_id
+ *
+ * Registers a small ID for the current thread. This is a 16-bit value uniquely
+ * identifying the current thread. If the current thread already has a small ID
+ * assigned, that small ID will be returned; otherwise, the newly assigned small
+ * ID is returned.
+ */
int
mono_thread_info_register_small_id (void)
{
- int small_id = mono_thread_small_id_alloc ();
+ int small_id = mono_thread_info_get_small_id ();
+
+ if (small_id != -1)
+ return small_id;
+
+ small_id = mono_thread_small_id_alloc ();
#ifdef HAVE_KW_THREAD
tls_small_id = small_id;
#else
return small_id;
}
-static void*
-register_thread (MonoThreadInfo *info, gpointer baseptr)
+static void
+thread_handle_destroy (gpointer data)
+{
+ MonoThreadHandle *thread_handle;
+
+ thread_handle = (MonoThreadHandle*) data;
+
+ mono_os_event_destroy (&thread_handle->event);
+ g_free (thread_handle);
+}
+
+static gboolean
+register_thread (MonoThreadInfo *info)
{
size_t stsize = 0;
guint8 *staddr = NULL;
- int small_id = mono_thread_info_register_small_id ();
gboolean result;
+
+ info->small_id = mono_thread_info_register_small_id ();
mono_thread_info_set_tid (info, mono_native_thread_id_get ());
- info->small_id = small_id;
+
+ info->handle = g_new0 (MonoThreadHandle, 1);
+ mono_refcount_init (info->handle, thread_handle_destroy);
+ mono_os_event_init (&info->handle->event, FALSE);
mono_os_sem_init (&info->resume_semaphore, 0);
/*set TLS early so SMR works */
mono_native_tls_set_value (thread_info_key, info);
- THREADS_DEBUG ("registering info %p tid %p small id %x\n", info, mono_thread_info_get_tid (info), info->small_id);
-
- if (threads_callbacks.thread_register) {
- if (threads_callbacks.thread_register (info, baseptr) == NULL) {
- // g_warning ("thread registation failed\n");
- g_free (info);
- return NULL;
- }
- }
-
mono_thread_info_get_stack_bounds (&staddr, &stsize);
g_assert (staddr);
g_assert (stsize);
info->stackdata = g_byte_array_new ();
- mono_threads_platform_register (info);
+ info->internal_thread_gchandle = G_MAXUINT32;
+
+ info->profiler_signal_ack = 1;
+
+ mono_threads_suspend_register (info);
+
+ THREADS_DEBUG ("registering info %p tid %p small id %x\n", info, mono_thread_info_get_tid (info), info->small_id);
+
+ if (threads_callbacks.thread_attach) {
+ if (!threads_callbacks.thread_attach (info)) {
+ // g_warning ("thread registation failed\n");
+ mono_native_tls_set_value (thread_info_key, NULL);
+ return FALSE;
+ }
+ }
/*
Transition it before taking any locks or publishing itself to reduce the chance
result = mono_thread_info_insert (info);
g_assert (result);
mono_thread_info_suspend_unlock ();
- return info;
+
+ return TRUE;
}
static void
mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info);
+static void
+mono_threads_signal_thread_handle (MonoThreadHandle* thread_handle);
+
static void
unregister_thread (void *arg)
{
gpointer gc_unsafe_stackdata;
MonoThreadInfo *info;
int small_id;
+ gboolean result;
+ gpointer handle;
info = (MonoThreadInfo *) arg;
g_assert (info);
+ g_assert (mono_thread_info_is_current (info));
+ g_assert (mono_thread_info_is_live (info));
+
+ /* Pump the HP queue while the thread is alive.*/
+ mono_thread_hazardous_try_free_some ();
small_id = info->small_id;
mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
- mono_threads_core_unregister (info);
-
/*
* TLS destruction order is not reliable so small_id might be cleaned up
* before us.
mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (info->small_id + 1));
#endif
+ /* we need to duplicate it, as the info->handle is going
+ * to be closed when unregistering from the platform */
+ handle = mono_threads_open_thread_handle (info->handle);
+
/*
First perform the callback that requires no locks.
This callback has the potential of taking other locks, so we do it before.
be done while holding the suspend lock to give no other thread chance
to suspend it.
*/
- if (threads_callbacks.thread_unregister)
- threads_callbacks.thread_unregister (info);
- mono_threads_unregister_current_thread (info);
+ if (threads_callbacks.thread_detach_with_lock)
+ threads_callbacks.thread_detach_with_lock (info);
+
+ /* The thread is no longer active, so unref its handle */
+ mono_threads_close_thread_handle (info->handle);
+ info->handle = NULL;
+
+ result = mono_thread_info_remove (info);
+ g_assert (result);
mono_threads_transition_detach (info);
mono_thread_info_suspend_unlock ();
/*now it's safe to free the thread info.*/
mono_thread_hazardous_try_free (info, free_thread_info);
- /* Pump the HP queue */
- mono_thread_hazardous_try_free_some ();
mono_thread_small_id_free (small_id);
+
+ mono_threads_signal_thread_handle (handle);
+
+ mono_threads_close_thread_handle (handle);
+
+ mono_native_tls_set_value (thread_info_key, NULL);
}
static void
#endif
}
-/**
- * Removes the current thread from the thread list.
- * This must be called from the thread unregister callback and nowhere else.
- * The current thread must be passed as TLS might have already been cleaned up.
-*/
-static void
-mono_threads_unregister_current_thread (MonoThreadInfo *info)
-{
- gboolean result;
- g_assert (mono_thread_info_get_tid (info) == mono_native_thread_id_get ());
- result = mono_thread_info_remove (info);
- g_assert (result);
-}
-
MonoThreadInfo*
mono_thread_info_current_unchecked (void)
{
return info;
}
+/*
+ * mono_thread_info_get_small_id
+ *
+ * Retrieve the small ID for the current thread. This is a 16-bit value uniquely
+ * identifying the current thread. Returns -1 if the current thread doesn't have
+ * a small ID assigned.
+ *
+ * To ensure that the calling thread has a small ID assigned, call either
+ * mono_thread_info_attach or mono_thread_info_register_small_id.
+ */
int
mono_thread_info_get_small_id (void)
{
void
mono_threads_attach_tools_thread (void)
{
- int dummy = 0;
MonoThreadInfo *info;
/* Must only be called once */
mono_thread_info_usleep (10);
}
- info = mono_thread_info_attach (&dummy);
+ info = mono_thread_info_attach ();
g_assert (info);
info->tools_thread = TRUE;
}
MonoThreadInfo*
-mono_thread_info_attach (void *baseptr)
+mono_thread_info_attach (void)
{
MonoThreadInfo *info;
+
+#ifdef HOST_WIN32
if (!mono_threads_inited)
{
-#ifdef HOST_WIN32
/* This can happen from DllMain(DLL_THREAD_ATTACH) on Windows, if a
* thread is created before an embedding API user initialized Mono. */
- THREADS_DEBUG ("mono_thread_info_attach called before mono_threads_init\n");
+ THREADS_DEBUG ("mono_thread_info_attach called before mono_thread_info_init\n");
return NULL;
-#else
- g_assert (mono_threads_inited);
-#endif
}
+#endif
+
+ g_assert (mono_threads_inited);
+
info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
if (!info) {
info = (MonoThreadInfo *) g_malloc0 (thread_info_size);
THREADS_DEBUG ("attaching %p\n", info);
- if (!register_thread (info, baseptr))
+ if (!register_thread (info)) {
+ g_free (info);
return NULL;
- } else if (threads_callbacks.thread_attach) {
- threads_callbacks.thread_attach (info);
+ }
}
+
return info;
}
mono_thread_info_detach (void)
{
MonoThreadInfo *info;
+
+#ifdef HOST_WIN32
if (!mono_threads_inited)
{
/* This can happen from DllMain(THREAD_DETACH) on Windows, if a thread
* is created before an embedding API user initialized Mono. */
- THREADS_DEBUG ("mono_thread_info_detach called before mono_threads_init\n");
+ THREADS_DEBUG ("mono_thread_info_detach called before mono_thread_info_init\n");
return;
}
+#endif
+
+ g_assert (mono_threads_inited);
+
info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
if (info) {
THREADS_DEBUG ("detaching %p\n", info);
unregister_thread (info);
- mono_native_tls_set_value (thread_info_key, NULL);
}
}
+gboolean
+mono_thread_info_try_get_internal_thread_gchandle (MonoThreadInfo *info, guint32 *gchandle)
+{
+ g_assert (info);
+
+ if (info->internal_thread_gchandle == G_MAXUINT32)
+ return FALSE;
+
+ *gchandle = info->internal_thread_gchandle;
+ return TRUE;
+}
+
+void
+mono_thread_info_set_internal_thread_gchandle (MonoThreadInfo *info, guint32 gchandle)
+{
+ g_assert (info);
+ g_assert (gchandle != G_MAXUINT32);
+ info->internal_thread_gchandle = gchandle;
+}
+
+void
+mono_thread_info_unset_internal_thread_gchandle (THREAD_INFO_TYPE *info)
+{
+ g_assert (info);
+ info->internal_thread_gchandle = G_MAXUINT32;
+}
+
/*
* mono_thread_info_is_exiting:
*
return FALSE;
}
+#ifndef HOST_WIN32
+static void
+thread_info_key_dtor (void *arg)
+{
+ /* Put the MonoThreadInfo back for the duration of the
+ * unregister code. In some circumstances the thread needs to
+ * take the GC lock which may block which requires a coop
+ * state transition. */
+ mono_native_tls_set_value (thread_info_key, arg);
+ unregister_thread (arg);
+ mono_native_tls_set_value (thread_info_key, NULL);
+}
+#endif
+
void
-mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size)
+mono_thread_info_init (size_t info_size)
{
gboolean res;
- threads_callbacks = *callbacks;
thread_info_size = info_size;
char *sleepLimit;
#ifdef HOST_WIN32
res = mono_native_tls_alloc (&thread_info_key, NULL);
res = mono_native_tls_alloc (&thread_exited_key, NULL);
#else
- res = mono_native_tls_alloc (&thread_info_key, (void *) unregister_thread);
+ res = mono_native_tls_alloc (&thread_info_key, (void *) thread_info_key_dtor);
res = mono_native_tls_alloc (&thread_exited_key, (void *) thread_exited_dtor);
#endif
#endif
g_assert (res);
- unified_suspend_enabled = g_getenv ("MONO_ENABLE_UNIFIED_SUSPEND") != NULL || mono_threads_is_coop_enabled ();
-
if ((sleepLimit = g_getenv ("MONO_SLEEP_ABORT_LIMIT")) != NULL) {
+ errno = 0;
long threshold = strtol(sleepLimit, NULL, 10);
if ((errno == 0) && (threshold >= 40)) {
sleepAbortDuration = threshold;
sleepWarnDuration = threshold / 20;
} else
g_warning("MONO_SLEEP_ABORT_LIMIT must be a number >= 40");
+ g_free (sleepLimit);
}
mono_os_sem_init (&global_suspend_semaphore, 1);
mono_os_sem_init (&suspend_semaphore, 0);
+ mono_os_mutex_init (&join_mutex);
- mono_lls_init (&thread_list, NULL, HAZARD_FREE_NO_LOCK);
+ mono_lls_init (&thread_list, NULL);
mono_thread_smr_init ();
- mono_threads_init_platform ();
- mono_threads_init_coop ();
- mono_threads_init_abort_syscall ();
+ mono_threads_suspend_init ();
+ mono_threads_coop_init ();
+ mono_threads_platform_init ();
#if defined(__MACH__)
mono_mach_init (thread_info_key);
}
void
-mono_threads_runtime_init (MonoThreadInfoRuntimeCallbacks *callbacks)
+mono_thread_info_callbacks_init (MonoThreadInfoCallbacks *callbacks)
{
- runtime_callbacks = *callbacks;
+ threads_callbacks = *callbacks;
}
-MonoThreadInfoRuntimeCallbacks *
-mono_threads_get_runtime_callbacks (void)
+void
+mono_thread_info_signals_init (void)
{
- return &runtime_callbacks;
+ mono_threads_suspend_init_signals ();
}
-/*
-Signal that the current thread wants to be suspended.
-This function can be called without holding the suspend lock held.
-To finish suspending, call mono_suspend_check.
-*/
void
-mono_thread_info_begin_self_suspend (void)
+mono_thread_info_runtime_init (MonoThreadInfoRuntimeCallbacks *callbacks)
{
- MonoThreadInfo *info = mono_thread_info_current_unchecked ();
- if (!info)
- return;
-
- THREADS_SUSPEND_DEBUG ("BEGIN SELF SUSPEND OF %p\n", info);
- mono_threads_transition_request_self_suspension (info);
+ runtime_callbacks = *callbacks;
}
-void
-mono_thread_info_end_self_suspend (void)
+MonoThreadInfoRuntimeCallbacks *
+mono_threads_get_runtime_callbacks (void)
{
- MonoThreadInfo *info;
-
- info = mono_thread_info_current ();
- if (!info)
- return;
- THREADS_SUSPEND_DEBUG ("FINISH SELF SUSPEND OF %p\n", info);
-
- mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
-
- /* commit the saved state and notify others if needed */
- switch (mono_threads_transition_state_poll (info)) {
- case SelfSuspendResumed:
- return;
- case SelfSuspendWait:
- mono_thread_info_wait_for_resume (info);
- break;
- case SelfSuspendNotifyAndWait:
- mono_threads_notify_initiator_of_suspend (info);
- mono_thread_info_wait_for_resume (info);
- mono_threads_notify_initiator_of_resume (info);
- break;
- }
+ return &runtime_callbacks;
}
static gboolean
mono_thread_info_core_resume (MonoThreadInfo *info)
{
gboolean res = FALSE;
- if (info->create_suspended) {
- MonoNativeThreadId tid = mono_thread_info_get_tid (info);
- /* Have to special case this, as the normal suspend/resume pair are racy, they don't work if he resume is received before the suspend */
- info->create_suspended = FALSE;
- mono_threads_core_resume_created (info, tid);
- return TRUE;
- }
switch (mono_threads_transition_request_resume (info)) {
case ResumeError:
static gboolean
is_thread_in_critical_region (MonoThreadInfo *info)
{
- MonoMethod *method;
- MonoJitInfo *ji;
gpointer stack_start;
MonoThreadUnwindState *state;
+ if (mono_threads_platform_in_critical_region (mono_thread_info_get_tid (info)))
+ return TRUE;
+
/* Are we inside a system critical region? */
if (info->inside_critical_region)
return TRUE;
/* Are we inside a GC critical region? */
- if (threads_callbacks.mono_thread_in_critical_region && threads_callbacks.mono_thread_in_critical_region (info)) {
+ if (threads_callbacks.thread_in_critical_region && threads_callbacks.thread_in_critical_region (info)) {
return TRUE;
}
if (stack_start < info->stack_start_limit || stack_start >= info->stack_end)
return TRUE;
- ji = mono_jit_info_table_find (
- (MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN],
- (char *) MONO_CONTEXT_GET_IP (&state->ctx));
+ if (threads_callbacks.ip_in_critical_region)
+ return threads_callbacks.ip_in_critical_region ((MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN], (char *) MONO_CONTEXT_GET_IP (&state->ctx));
- if (!ji)
- return FALSE;
-
- method = mono_jit_info_get_method (ji);
-
- return threads_callbacks.mono_method_is_critical (method);
+ return FALSE;
}
gboolean
break;
case AsyncSuspendBlocking:
if (interrupt_kernel)
- mono_threads_core_abort_syscall (info);
+ mono_threads_suspend_abort_syscall (info);
- break;
+ return info;
default:
g_assert_not_reached ();
}
mono_threads_wait_pending_operations ();
break;
case KeepSuspended:
+ g_assert (!mono_threads_is_coop_enabled ());
break;
default:
g_error ("Invalid suspend_and_run callback return value %d", result);
void
mono_thread_info_setup_async_call (MonoThreadInfo *info, void (*target_func)(void*), void *user_data)
{
- /* An async call can only be setup on an async suspended thread */
- g_assert (mono_thread_info_run_state (info) == STATE_ASYNC_SUSPENDED);
+ if (!mono_threads_is_coop_enabled ()) {
+ /* In non-coop mode, an async call can only be setup on an async suspended thread, but in coop mode, a thread
+ * may be in blocking state, and will execute the async call when leaving the safepoint, leaving a gc safe
+ * region or entering a gc unsafe region */
+ g_assert (mono_thread_info_run_state (info) == STATE_ASYNC_SUSPENDED);
+ }
/*FIXME this is a bad assert, we probably should do proper locking and fail if one is already set*/
g_assert (!info->async_target);
info->async_target = target_func;
mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info)
{
g_assert (info);
+ g_assert (mono_thread_info_is_current (info));
+ g_assert (mono_thread_info_is_live (info));
MONO_ENTER_GC_SAFE_WITH_INFO(info);
void
mono_thread_info_suspend_lock (void)
{
- mono_thread_info_suspend_lock_with_info (mono_thread_info_current_unchecked ());
+ MonoThreadInfo *info;
+ gint res;
+
+ info = mono_thread_info_current_unchecked ();
+ if (info && mono_thread_info_is_live (info)) {
+ mono_thread_info_suspend_lock_with_info (info);
+ return;
+ }
+
+ /* mono_thread_info_suspend_lock () can be called from boehm-gc.c on_gc_notification before the new thread's
+ * start_wrapper calls mono_thread_info_attach but after pthread_create calls the start wrapper. */
+
+ res = mono_os_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
+ g_assert (res != -1);
}
void
MonoThreadHazardPointers *hp;
MonoThreadInfo *info;
- if (tid == mono_native_thread_id_get () || !mono_threads_core_needs_abort_syscall ())
+ if (tid == mono_native_thread_id_get ())
return;
hp = mono_hazard_pointer_get ();
mono_thread_info_suspend_lock ();
mono_threads_begin_global_suspend ();
- mono_threads_core_abort_syscall (info);
+ mono_threads_suspend_abort_syscall (info);
mono_threads_wait_pending_operations ();
mono_hazard_pointer_clear (hp, 1);
mono_thread_info_suspend_unlock ();
}
-gboolean
-mono_thread_info_unified_management_enabled (void)
-{
- return unified_suspend_enabled;
-}
-
/*
* mono_thread_info_set_is_async_context:
*
return FALSE;
}
-/*
- * mono_threads_create_thread:
- *
- * Create a new thread executing START with argument ARG. Store its id into OUT_TID.
- * Returns: a windows or io-layer handle for the thread.
- */
-HANDLE
-mono_threads_create_thread (LPTHREAD_START_ROUTINE start, gpointer arg, MonoThreadParm *tp, MonoNativeThreadId *out_tid)
-{
- return mono_threads_core_create_thread (start, arg, tp, out_tid);
-}
-
/*
* mono_thread_info_get_stack_bounds:
*
mono_thread_info_get_stack_bounds (guint8 **staddr, size_t *stsize)
{
guint8 *current = (guint8 *)&stsize;
- mono_threads_core_get_stack_bounds (staddr, stsize);
+ mono_threads_platform_get_stack_bounds (staddr, stsize);
if (!*staddr)
return;
gboolean
mono_thread_info_yield (void)
{
- return mono_threads_core_yield ();
+ return mono_threads_platform_yield ();
}
+
static mono_lazy_init_t sleep_init = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED;
static MonoCoopMutex sleep_mutex;
static MonoCoopCond sleep_cond;
{
gint64 now, end;
- g_assert (INFINITE == G_MAXUINT32);
+ g_assert (MONO_INFINITE_WAIT == G_MAXUINT32);
g_assert (alerted);
*alerted = FALSE;
- if (ms != INFINITE)
- end = mono_100ns_ticks () + (ms * 1000 * 10);
+ if (ms != MONO_INFINITE_WAIT)
+ end = mono_msec_ticks() + ms;
mono_lazy_initialize (&sleep_init, sleep_initialize);
mono_coop_mutex_lock (&sleep_mutex);
for (;;) {
- if (ms != INFINITE) {
- now = mono_100ns_ticks ();
- if (now > end)
+ if (ms != MONO_INFINITE_WAIT) {
+ now = mono_msec_ticks();
+ if (now >= end)
break;
}
return WAIT_IO_COMPLETION;
}
- if (ms != INFINITE)
- mono_coop_cond_timedwait (&sleep_cond, &sleep_mutex, (end - now) / 10 / 1000);
+ if (ms != MONO_INFINITE_WAIT)
+ mono_coop_cond_timedwait (&sleep_cond, &sleep_mutex, end - now);
else
mono_coop_cond_wait (&sleep_cond, &sleep_mutex);
MONO_ENTER_GC_SAFE;
- if (ms == INFINITE) {
+ if (ms == MONO_INFINITE_WAIT) {
do {
#ifdef HOST_WIN32
Sleep (G_MAXUINT32);
* This function doesn't return.
*/
void
-mono_thread_info_exit (void)
+mono_thread_info_exit (gsize exit_code)
{
- mono_threads_core_exit (0);
+ mono_thread_info_detach ();
+
+ mono_threads_platform_exit (0);
}
/*
- * mono_thread_info_open_handle:
+ * mono_threads_open_thread_handle:
*
- * Return a io-layer/win32 handle for the current thread.
- * The handle need to be closed by calling CloseHandle () when it is no
- * longer needed.
+ * Duplicate the handle. The handle needs to be closed by calling
+ * mono_threads_close_thread_handle () when it is no longer needed.
*/
-HANDLE
-mono_thread_info_open_handle (void)
+MonoThreadHandle*
+mono_threads_open_thread_handle (MonoThreadHandle *thread_handle)
{
- return mono_threads_core_open_handle ();
+ return mono_refcount_inc (thread_handle);
}
-/*
- * mono_threads_open_thread_handle:
- *
- * Return a io-layer/win32 handle for the thread identified by HANDLE/TID.
- * The handle need to be closed by calling CloseHandle () when it is no
- * longer needed.
- */
-HANDLE
-mono_threads_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
+void
+mono_threads_close_thread_handle (MonoThreadHandle *thread_handle)
+{
+ mono_refcount_dec (thread_handle);
+}
+
+static void
+mono_threads_signal_thread_handle (MonoThreadHandle* thread_handle)
{
- return mono_threads_core_open_thread_handle (handle, tid);
+ mono_os_event_set (&thread_handle->event);
}
#define INTERRUPT_STATE ((MonoThreadInfoInterruptToken*) (size_t) -1)
{
return mono_thread_info_get_tid (info) == mono_native_thread_id_get ();
}
+
+MonoThreadInfoWaitRet
+mono_thread_info_wait_one_handle (MonoThreadHandle *thread_handle, guint32 timeout, gboolean alertable)
+{
+ MonoOSEventWaitRet res;
+
+ res = mono_os_event_wait_one (&thread_handle->event, timeout, alertable);
+ if (res == MONO_OS_EVENT_WAIT_RET_SUCCESS_0)
+ return MONO_THREAD_INFO_WAIT_RET_SUCCESS_0;
+ else if (res == MONO_OS_EVENT_WAIT_RET_ALERTED)
+ return MONO_THREAD_INFO_WAIT_RET_ALERTED;
+ else if (res == MONO_OS_EVENT_WAIT_RET_TIMEOUT)
+ return MONO_THREAD_INFO_WAIT_RET_TIMEOUT;
+ else
+ g_error ("%s: unknown res value %d", __func__, res);
+}
+
+MonoThreadInfoWaitRet
+mono_thread_info_wait_multiple_handle (MonoThreadHandle **thread_handles, gsize nhandles, MonoOSEvent *background_change_event, gboolean waitall, guint32 timeout, gboolean alertable)
+{
+ MonoOSEventWaitRet res;
+ MonoOSEvent *thread_events [MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS];
+ gint i;
+
+ g_assert (nhandles <= MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS);
+ if (background_change_event)
+ g_assert (nhandles <= MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS - 1);
+
+ for (i = 0; i < nhandles; ++i)
+ thread_events [i] = &thread_handles [i]->event;
+
+ if (background_change_event)
+ thread_events [nhandles ++] = background_change_event;
+
+ res = mono_os_event_wait_multiple (thread_events, nhandles, waitall, timeout, alertable);
+ if (res >= MONO_OS_EVENT_WAIT_RET_SUCCESS_0 && res <= MONO_OS_EVENT_WAIT_RET_SUCCESS_0 + nhandles - 1)
+ return MONO_THREAD_INFO_WAIT_RET_SUCCESS_0 + (res - MONO_OS_EVENT_WAIT_RET_SUCCESS_0);
+ else if (res == MONO_OS_EVENT_WAIT_RET_ALERTED)
+ return MONO_THREAD_INFO_WAIT_RET_ALERTED;
+ else if (res == MONO_OS_EVENT_WAIT_RET_TIMEOUT)
+ return MONO_THREAD_INFO_WAIT_RET_TIMEOUT;
+ else
+ g_error ("%s: unknown res value %d", __func__, res);
+}
+
+/*
+ * mono_threads_join_mutex:
+ *
+ * This mutex is used to avoid races between pthread_create () and pthread_join () on osx, see
+ * https://bugzilla.xamarin.com/show_bug.cgi?id=50529
+ * The code inside the lock should not block.
+ */
+void
+mono_threads_join_lock (void)
+{
+#ifdef TARGET_OSX
+ mono_os_mutex_lock (&join_mutex);
+#endif
+}
+
+void
+mono_threads_join_unlock (void)
+{
+#ifdef TARGET_OSX
+ mono_os_mutex_unlock (&join_mutex);
+#endif
+}