X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Futils%2Fmono-threads.c;h=9ba005e01b5bd26800a8e5d16c9d7ababe8eeb43;hb=df03eb3fac273fc8687c26324e45c3a5b4a1bc1b;hp=033e4670851677870a93ce842b1a1c629a2cb79a;hpb=256e3ee192da85cf7c09a3890c06f7bc448ac817;p=mono.git diff --git a/mono/utils/mono-threads.c b/mono/utils/mono-threads.c index 033e4670851..9ba005e01b5 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) @@ -31,6 +32,7 @@ #include #include #include +#include #include @@ -64,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 @@ -74,6 +76,8 @@ static gboolean mono_threads_inited = FALSE; static MonoSemType suspend_semaphore; static size_t pending_suspends; +static mono_mutex_t join_mutex; + #define mono_thread_info_run_state(info) (((MonoThreadInfo*)info)->thread_state & THREAD_STATE_MASK) /*warn at 50 ms*/ @@ -326,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 @@ -349,15 +366,15 @@ thread_handle_destroy (gpointer data) g_free (thread_handle); } -static void* -register_thread (MonoThreadInfo *info, gpointer baseptr) +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); @@ -368,17 +385,6 @@ register_thread (MonoThreadInfo *info, gpointer baseptr) /*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); @@ -387,8 +393,22 @@ register_thread (MonoThreadInfo *info, gpointer baseptr) info->stackdata = g_byte_array_new (); + 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. @@ -401,7 +421,8 @@ 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 @@ -424,6 +445,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 @@ -462,8 +486,8 @@ 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); @@ -479,14 +503,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); mono_threads_signal_thread_handle (handle); mono_threads_close_thread_handle (handle); + + mono_native_tls_set_value (thread_info_key, NULL); } static void @@ -544,6 +568,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) { @@ -578,7 +612,6 @@ mono_thread_info_list_head (void) void mono_threads_attach_tools_thread (void) { - int dummy = 0; MonoThreadInfo *info; /* Must only be called once */ @@ -588,36 +621,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; } @@ -625,21 +661,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: * @@ -671,12 +739,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); @@ -700,16 +767,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_suspend_init (); - mono_threads_suspend_init_signals (); mono_threads_coop_init (); + mono_threads_platform_init (); #if defined(__MACH__) mono_mach_init (thread_info_key); @@ -721,7 +790,19 @@ mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size) } void -mono_threads_runtime_init (MonoThreadInfoRuntimeCallbacks *callbacks) +mono_thread_info_callbacks_init (MonoThreadInfoCallbacks *callbacks) +{ + threads_callbacks = *callbacks; +} + +void +mono_thread_info_signals_init (void) +{ + mono_threads_suspend_init_signals (); +} + +void +mono_thread_info_runtime_init (MonoThreadInfoRuntimeCallbacks *callbacks) { runtime_callbacks = *callbacks; } @@ -819,17 +900,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; } @@ -846,16 +928,7 @@ is_thread_in_critical_region (MonoThreadInfo *info) 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)); - - if (!ji) - return FALSE; - - method = mono_jit_info_get_method (ji); - - return threads_callbacks.mono_method_is_critical (method); + return FALSE; } gboolean @@ -889,10 +962,10 @@ suspend_sync (MonoNativeThreadId tid, gboolean interrupt_kernel) } break; case AsyncSuspendBlocking: - if (interrupt_kernel && mono_threads_suspend_needs_abort_syscall ()) + if (interrupt_kernel) mono_threads_suspend_abort_syscall (info); - break; + return info; default: g_assert_not_reached (); } @@ -991,8 +1064,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; @@ -1024,7 +1101,20 @@ mono_thread_info_suspend_lock_with_info (MonoThreadInfo *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 @@ -1049,7 +1139,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 (); @@ -1100,100 +1190,6 @@ mono_thread_info_is_async_context (void) return FALSE; } -typedef struct { - MonoRefCount ref; - MonoThreadStart start_routine; - gpointer start_routine_arg; - MonoCoopSem registered; - MonoThreadHandle *handle; -} CreateThreadData; - -static void -create_thread_data_destroy (gpointer data) -{ - CreateThreadData *thread_data; - - thread_data = (CreateThreadData*) data; - - mono_coop_sem_destroy (&thread_data->registered); - g_free (thread_data); -} - -static gsize WINAPI -inner_start_thread (gpointer data) -{ - CreateThreadData *thread_data; - MonoThreadInfo *info; - MonoThreadStart start_routine; - gpointer start_routine_arg; - gsize 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_threads_open_thread_handle (info->handle); - - mono_coop_sem_post (&thread_data->registered); - - mono_refcount_dec (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_thread_info_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. - */ -MonoThreadHandle* -mono_threads_create_thread (MonoThreadStart start, gpointer arg, gsize * const stack_size, MonoNativeThreadId *out_tid) -{ - CreateThreadData *thread_data; - gint res; - MonoThreadHandle *ret; - - thread_data = g_new0 (CreateThreadData, 1); - mono_refcount_init (thread_data, create_thread_data_destroy); - 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) mono_refcount_inc (thread_data), stack_size, out_tid); - if (res != 0) { - /* ref is not going to be decremented in inner_start_thread */ - mono_refcount_dec (thread_data); - 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: - mono_refcount_dec (thread_data); - - return ret; -} - /* * mono_thread_info_get_stack_bounds: * @@ -1220,6 +1216,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; @@ -1316,7 +1313,7 @@ mono_thread_info_sleep (guint32 ms, gboolean *alerted) } while (1); } else { int ret; -#if defined (__linux__) && !defined(PLATFORM_ANDROID) +#if defined (__linux__) && !defined(HOST_ANDROID) struct timespec start, target; /* Use clock_nanosleep () to prevent time drifting problems when nanosleep () is interrupted by signals */ @@ -1383,10 +1380,6 @@ 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: * @@ -1396,10 +1389,6 @@ void nacl_shutdown_gc_thread(void); 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); @@ -1640,7 +1629,7 @@ mono_thread_info_wait_one_handle (MonoThreadHandle *thread_handle, guint32 timeo { MonoOSEventWaitRet res; - res = mono_os_event_wait_one (&thread_handle->event, timeout); + 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) @@ -1668,7 +1657,7 @@ mono_thread_info_wait_multiple_handle (MonoThreadHandle **thread_handles, gsize if (background_change_event) thread_events [nhandles ++] = background_change_event; - res = mono_os_event_wait_multiple (thread_events, nhandles, waitall, timeout); + 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) @@ -1678,3 +1667,26 @@ mono_thread_info_wait_multiple_handle (MonoThreadHandle **thread_handles, gsize 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 +}