X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Futils%2Fmono-threads.c;h=1cf4bb334c94b84e95602d8bdb7b8f5fffb93a6e;hb=bdfa3ef2cafa954f76a405ce3dc9d6ce39f46d42;hp=6ead6cfc23d1ba088f9654673d7ee14b45018d64;hpb=c866c3eefb79ea86be468e4f95c7012fa3d2db4f;p=mono.git diff --git a/mono/utils/mono-threads.c b/mono/utils/mono-threads.c index 6ead6cfc23d..1cf4bb334c9 100644 --- a/mono/utils/mono-threads.c +++ b/mono/utils/mono-threads.c @@ -1,5 +1,6 @@ -/* - * mono-threads.c: Low-level threading +/** + * \file + * Low-level threading * * Author: * Rodrigo Kumpera (kumpera@gmail.com) @@ -29,6 +30,9 @@ #include #include #include +#include +#include +#include #include @@ -62,7 +66,7 @@ static MonoThreadInfoCallbacks threads_callbacks; 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 @@ -71,7 +75,8 @@ static gboolean mono_threads_inited = FALSE; 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) @@ -325,10 +330,23 @@ free_thread_info (gpointer mem) 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 @@ -337,32 +355,36 @@ mono_thread_info_register_small_id (void) 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); @@ -371,9 +393,20 @@ register_thread (MonoThreadInfo *info, gpointer baseptr) info->stackdata = g_byte_array_new (); - mono_threads_platform_register (info); + info->internal_thread_gchandle = G_MAXUINT32; + 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. @@ -386,12 +419,16 @@ register_thread (MonoThreadInfo *info, gpointer baseptr) 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) { @@ -406,6 +443,9 @@ unregister_thread (void *arg) 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 @@ -426,7 +466,7 @@ unregister_thread (void *arg) /* we need to duplicate it, as the info->handle is going * to be closed when unregistering from the platform */ - handle = mono_threads_platform_duplicate_handle (info); + handle = mono_threads_open_thread_handle (info->handle); /* First perform the callback that requires no locks. @@ -444,10 +484,13 @@ unregister_thread (void *arg) 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); + 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; - mono_threads_platform_unregister (info); result = mono_thread_info_remove (info); g_assert (result); mono_threads_transition_detach (info); @@ -458,17 +501,14 @@ unregister_thread (void *arg) /*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); - /* Signal the w32handle. It can be done as late as here - * because w32handle does not access the current MonoThreadInfo, - * neither does it switch state to BLOCKING. */ - mono_threads_platform_set_exited (handle); + mono_threads_signal_thread_handle (handle); + + mono_threads_close_thread_handle (handle); - mono_threads_platform_close_thread_handle (handle); + mono_native_tls_set_value (thread_info_key, NULL); } static void @@ -526,6 +566,16 @@ mono_thread_info_current (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) { @@ -560,7 +610,6 @@ mono_thread_info_list_head (void) void mono_threads_attach_tools_thread (void) { - int dummy = 0; MonoThreadInfo *info; /* Must only be called once */ @@ -570,36 +619,39 @@ mono_threads_attach_tools_thread (void) 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; } @@ -607,21 +659,53 @@ void 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: * @@ -653,12 +737,11 @@ thread_info_key_dtor (void *arg) #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); @@ -674,8 +757,6 @@ mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size) #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); @@ -684,17 +765,18 @@ mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size) 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); @@ -706,58 +788,27 @@ mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size) } 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 @@ -847,17 +898,18 @@ WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is 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; } @@ -871,16 +923,10 @@ is_thread_in_critical_region (MonoThreadInfo *info) 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 @@ -994,6 +1040,7 @@ mono_thread_info_safe_suspend_and_run (MonoNativeThreadId id, gboolean interrupt 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); @@ -1015,8 +1062,12 @@ currently used only to deliver exceptions. 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; @@ -1033,16 +1084,21 @@ STW to make sure no unsafe pending suspend is in progress. static void 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)); + if (mono_threads_is_coop_enabled ()) { + 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); + MONO_ENTER_GC_SAFE_WITH_INFO(info); - int res = mono_os_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE); - g_assert (res != -1); + int res = mono_os_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE); + g_assert (res != -1); - MONO_EXIT_GC_SAFE_WITH_INFO; + MONO_EXIT_GC_SAFE_WITH_INFO; + } else { + int res = mono_os_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE); + g_assert (res != -1); + } } void @@ -1073,7 +1129,7 @@ mono_thread_info_abort_socket_syscall_for_close (MonoNativeThreadId tid) 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 (); @@ -1098,12 +1154,6 @@ mono_thread_info_abort_socket_syscall_for_close (MonoNativeThreadId tid) mono_thread_info_suspend_unlock (); } -gboolean -mono_thread_info_unified_management_enabled (void) -{ - return unified_suspend_enabled; -} - /* * mono_thread_info_set_is_async_context: * @@ -1130,96 +1180,6 @@ mono_thread_info_is_async_context (void) return FALSE; } -typedef struct { - gint32 ref; - MonoThreadStart start_routine; - gpointer start_routine_arg; - gint32 priority; - MonoCoopSem registered; - gpointer handle; -} CreateThreadData; - -static gsize WINAPI -inner_start_thread (gpointer data) -{ - CreateThreadData *thread_data; - MonoThreadInfo *info; - MonoThreadStart start_routine; - gpointer start_routine_arg; - guint32 start_routine_res; - gsize dummy; - - thread_data = (CreateThreadData*) data; - g_assert (thread_data); - - start_routine = thread_data->start_routine; - start_routine_arg = thread_data->start_routine_arg; - - info = mono_thread_info_attach (&dummy); - info->runtime_thread = TRUE; - - thread_data->handle = mono_thread_info_duplicate_handle (info); - - mono_coop_sem_post (&thread_data->registered); - - if (InterlockedDecrement (&thread_data->ref) == 0) { - mono_coop_sem_destroy (&thread_data->registered); - g_free (thread_data); - } - - /* thread_data is not valid anymore */ - thread_data = NULL; - - /* Run the actual main function of the thread */ - start_routine_res = start_routine (start_routine_arg); - - mono_threads_platform_exit (start_routine_res); - - g_assert_not_reached (); -} - -/* - * 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, gsize * const stack_size, MonoNativeThreadId *out_tid) -{ - CreateThreadData *thread_data; - gint res; - gpointer ret; - - thread_data = g_new0 (CreateThreadData, 1); - thread_data->ref = 2; - thread_data->start_routine = start; - thread_data->start_routine_arg = arg; - mono_coop_sem_init (&thread_data->registered, 0); - - res = mono_threads_platform_create_thread (inner_start_thread, (gpointer) thread_data, stack_size, out_tid); - if (res != 0) { - /* ref is not going to be decremented in inner_start_thread */ - InterlockedDecrement (&thread_data->ref); - ret = NULL; - goto done; - } - - res = mono_coop_sem_wait (&thread_data->registered, MONO_SEM_FLAGS_NONE); - g_assert (res == 0); - - ret = thread_data->handle; - g_assert (ret); - -done: - if (InterlockedDecrement (&thread_data->ref) == 0) { - mono_coop_sem_destroy (&thread_data->registered); - g_free (thread_data); - } - - return ret; -} - /* * mono_thread_info_get_stack_bounds: * @@ -1246,6 +1206,7 @@ mono_thread_info_yield (void) { 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; @@ -1270,12 +1231,12 @@ sleep_interruptable (guint32 ms, gboolean *alerted) { gint64 now, end; - g_assert (INFINITE == G_MAXUINT32); + g_assert (MONO_INFINITE_WAIT == G_MAXUINT32); g_assert (alerted); *alerted = FALSE; - if (ms != INFINITE) + if (ms != MONO_INFINITE_WAIT) end = mono_msec_ticks() + ms; mono_lazy_initialize (&sleep_init, sleep_initialize); @@ -1283,7 +1244,7 @@ sleep_interruptable (guint32 ms, gboolean *alerted) mono_coop_mutex_lock (&sleep_mutex); for (;;) { - if (ms != INFINITE) { + if (ms != MONO_INFINITE_WAIT) { now = mono_msec_ticks(); if (now >= end) break; @@ -1295,7 +1256,7 @@ sleep_interruptable (guint32 ms, gboolean *alerted) return WAIT_IO_COMPLETION; } - if (ms != INFINITE) + if (ms != MONO_INFINITE_WAIT) mono_coop_cond_timedwait (&sleep_cond, &sleep_mutex, end - now); else mono_coop_cond_wait (&sleep_cond, &sleep_mutex); @@ -1332,7 +1293,7 @@ mono_thread_info_sleep (guint32 ms, gboolean *alerted) MONO_ENTER_GC_SAFE; - if (ms == INFINITE) { + if (ms == MONO_INFINITE_WAIT) { do { #ifdef HOST_WIN32 Sleep (G_MAXUINT32); @@ -1416,28 +1377,35 @@ mono_thread_info_tls_set (THREAD_INFO_TYPE *info, MonoTlsKey key, gpointer value * 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_threads_platform_open_thread_handle (handle, tid); + return mono_refcount_inc (thread_handle); } void -mono_threads_close_thread_handle (HANDLE handle) +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_close_thread_handle (handle); + mono_os_event_set (&thread_handle->event); } #define INTERRUPT_STATE ((MonoThreadInfoInterruptToken*) (size_t) -1) @@ -1646,18 +1614,69 @@ mono_thread_info_is_current (MonoThreadInfo *info) 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)); + MonoOSEventWaitRet res; - g_assert (info->handle); - mono_threads_platform_set_exited (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); } -gpointer -mono_thread_info_duplicate_handle (MonoThreadInfo *info) +MonoThreadInfoWaitRet +mono_thread_info_wait_multiple_handle (MonoThreadHandle **thread_handles, gsize nhandles, MonoOSEvent *background_change_event, gboolean waitall, guint32 timeout, gboolean alertable) { - g_assert (mono_thread_info_is_current (info)); - return mono_threads_platform_duplicate_handle (info); + 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 }