-/*
- * 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)
//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)) {
mono_hazard_pointer_clear_all (hp, -1);
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");
- mono_native_tls_set_value (thread_info_key, NULL);
- 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
of others witnessing a detached thread.
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;
/* We only enter the GC unsafe region, as when exiting this function, the thread
mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
- mono_threads_platform_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:
*
#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;
- const char *sleepLimit;
+ char *sleepLimit;
#ifdef HOST_WIN32
res = mono_native_tls_alloc (&thread_info_key, NULL);
res = mono_native_tls_alloc (&thread_exited_key, NULL);
#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);
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);
mono_thread_smr_init ();
- mono_threads_platform_init ();
mono_threads_suspend_init ();
mono_threads_coop_init ();
- mono_threads_abort_syscall_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_platform_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 (!ji)
- return FALSE;
-
- method = mono_jit_info_get_method (ji);
+ 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));
- return threads_callbacks.mono_method_is_critical (method);
+ return FALSE;
}
gboolean
if (interrupt_kernel)
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;
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_suspend_needs_abort_syscall ())
+ if (tid == mono_native_thread_id_get ())
return;
hp = mono_hazard_pointer_get ();
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 (MonoThreadStart start, gpointer arg, MonoThreadParm *tp, MonoNativeThreadId *out_tid)
-{
- return mono_threads_platform_create_thread (start, arg, tp, out_tid);
-}
-
/*
* mono_thread_info_get_stack_bounds:
*
{
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_thread_info_detach ();
+
mono_threads_platform_exit (0);
}
/*
* 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.
+ * Duplicate the handle. The handle needs to be closed by calling
+ * mono_threads_close_thread_handle () when it is no longer needed.
*/
-HANDLE
-mono_threads_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
+MonoThreadHandle*
+mono_threads_open_thread_handle (MonoThreadHandle *thread_handle)
+{
+ return mono_refcount_inc (thread_handle);
+}
+
+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_platform_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 ();
}
-void
-mono_thread_info_set_exited (THREAD_INFO_TYPE *info)
+MonoThreadInfoWaitRet
+mono_thread_info_wait_one_handle (MonoThreadHandle *thread_handle, guint32 timeout, gboolean alertable)
{
- g_assert (mono_thread_info_is_current (info));
- mono_threads_platform_set_exited (info);
-}
+ MonoOSEventWaitRet res;
-gpointer
-mono_thread_info_get_handle (THREAD_INFO_TYPE *info)
-{
- g_assert (info->handle);
- return info->handle;
+ 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);
}
-void
-mono_thread_info_describe (MonoThreadInfo *info, GString *text)
+MonoThreadInfoWaitRet
+mono_thread_info_wait_multiple_handle (MonoThreadHandle **thread_handles, gsize nhandles, MonoOSEvent *background_change_event, gboolean waitall, guint32 timeout, gboolean alertable)
{
- mono_threads_platform_describe (info, text);
-}
+ MonoOSEventWaitRet res;
+ MonoOSEvent *thread_events [MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS];
+ gint i;
-void
-mono_thread_info_own_mutex (MonoThreadInfo *info, gpointer mutex_handle)
-{
- mono_threads_platform_own_mutex (info, mutex_handle);
-}
+ g_assert (nhandles <= MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS);
+ if (background_change_event)
+ g_assert (nhandles <= MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS - 1);
-void
-mono_thread_info_disown_mutex (MonoThreadInfo *info, gpointer mutex_handle)
-{
- mono_threads_platform_disown_mutex (info, mutex_handle);
+ 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);
}
-MonoThreadPriority
-mono_thread_info_get_priority (MonoThreadInfo *info)
+/*
+ * 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)
{
- return mono_threads_platform_get_priority (info);
+#ifdef TARGET_OSX
+ mono_os_mutex_lock (&join_mutex);
+#endif
}
-gboolean
-mono_thread_info_set_priority (MonoThreadInfo *info, MonoThreadPriority priority)
+void
+mono_threads_join_unlock (void)
{
- return mono_threads_platform_set_priority (info, priority);
+#ifdef TARGET_OSX
+ mono_os_mutex_unlock (&join_mutex);
+#endif
}