X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Futils%2Fmono-threads.c;h=e6d151ce2566c841cfdba4ae8d1f083d30bfcfa6;hb=f704d56c378b28e252572db4730a6e13edc14aa0;hp=6ead6cfc23d1ba088f9654673d7ee14b45018d64;hpb=f7b73f070a5031f04837461757d8cf9ea34b5f67;p=mono.git diff --git a/mono/utils/mono-threads.c b/mono/utils/mono-threads.c index 6ead6cfc23d..e6d151ce256 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 guint32 tls_small_id; #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) @@ -337,6 +342,17 @@ mono_thread_info_register_small_id (void) return small_id; } +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 void* register_thread (MonoThreadInfo *info, gpointer baseptr) { @@ -347,6 +363,10 @@ register_thread (MonoThreadInfo *info, gpointer baseptr) 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 */ @@ -371,7 +391,6 @@ register_thread (MonoThreadInfo *info, gpointer baseptr) info->stackdata = g_byte_array_new (); - mono_threads_platform_register (info); mono_threads_suspend_register (info); /* @@ -392,6 +411,9 @@ register_thread (MonoThreadInfo *info, gpointer baseptr) 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 +428,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 +451,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. @@ -447,7 +472,10 @@ unregister_thread (void *arg) if (threads_callbacks.thread_unregister) threads_callbacks.thread_unregister (info); - mono_threads_platform_unregister (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); @@ -458,17 +486,12 @@ 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_platform_close_thread_handle (handle); + mono_threads_close_thread_handle (handle); } static void @@ -658,7 +681,7 @@ mono_threads_init (MonoThreadInfoCallbacks *callbacks, 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 +697,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 +705,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); @@ -705,6 +727,12 @@ mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size) g_assert (sizeof (MonoNativeThreadId) <= sizeof (uintptr_t)); } +void +mono_threads_signals_init (void) +{ + mono_threads_suspend_init_signals (); +} + void mono_threads_runtime_init (MonoThreadInfoRuntimeCallbacks *callbacks) { @@ -717,49 +745,6 @@ mono_threads_get_runtime_callbacks (void) return &runtime_callbacks; } -/* -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) -{ - 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); -} - -void -mono_thread_info_end_self_suspend (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; - } -} - static gboolean mono_thread_info_core_resume (MonoThreadInfo *info) { @@ -852,6 +837,9 @@ is_thread_in_critical_region (MonoThreadInfo *info) 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; @@ -871,6 +859,9 @@ is_thread_in_critical_region (MonoThreadInfo *info) if (stack_start < info->stack_start_limit || stack_start >= info->stack_end) return TRUE; + 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)); + ji = mono_jit_info_table_find ( (MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN], (char *) MONO_CONTEXT_GET_IP (&state->ctx)); @@ -994,6 +985,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 +1007,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; @@ -1073,7 +1069,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 +1094,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 +1120,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 +1146,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 +1171,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 +1184,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 +1196,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 +1233,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); @@ -1409,6 +1310,10 @@ mono_thread_info_tls_set (THREAD_INFO_TYPE *info, MonoTlsKey key, gpointer value ((MonoThreadInfo*)info)->tls [key] = value; } +#if defined(__native_client__) +void nacl_shutdown_gc_thread(void); +#endif + /* * mono_thread_info_exit: * @@ -1416,28 +1321,39 @@ 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) { +#if defined(__native_client__) + nacl_shutdown_gc_thread(); +#endif + + 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 +1562,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 }