Merge pull request #4621 from alexanderkyte/strdup_env
[mono.git] / mono / utils / mono-threads.c
index e344ec1ed1f11728d88fe90db446891adaaae43a..e6d151ce2566c841cfdba4ae8d1f083d30bfcfa6 100644 (file)
@@ -1,5 +1,6 @@
-/*
- * mono-threads.c: Low-level threading
+/**
+ * \file
+ * Low-level threading
  *
  * Author:
  *     Rodrigo Kumpera (kumpera@gmail.com)
@@ -31,8 +32,7 @@
 #include <mono/utils/mono-threads-coop.h>
 #include <mono/utils/mono-threads-debug.h>
 #include <mono/utils/os-event.h>
-
-#include <mono/io-layer/io-layer.h>
+#include <mono/utils/w32api.h>
 
 #include <errno.h>
 
@@ -66,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
@@ -76,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*/
@@ -426,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
@@ -481,8 +486,6 @@ 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);
 
@@ -678,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);
@@ -702,15 +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_suspend_init ();
        mono_threads_coop_init ();
+       mono_threads_platform_init ();
 
 #if defined(__MACH__)
        mono_mach_init (thread_info_key);
@@ -831,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;
@@ -896,7 +905,7 @@ 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;
@@ -998,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;
@@ -1056,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 ();
@@ -1107,100 +1120,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:
  *
@@ -1227,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;
@@ -1647,7 +1567,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)
@@ -1675,7 +1595,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)
@@ -1685,3 +1605,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
+}