summaryrefslogtreecommitdiff
path: root/3rdparty/plibsys/src/puthread-posix.c
diff options
context:
space:
mode:
Diffstat (limited to '3rdparty/plibsys/src/puthread-posix.c')
-rw-r--r--3rdparty/plibsys/src/puthread-posix.c580
1 files changed, 580 insertions, 0 deletions
diff --git a/3rdparty/plibsys/src/puthread-posix.c b/3rdparty/plibsys/src/puthread-posix.c
new file mode 100644
index 0000000..90e14d5
--- /dev/null
+++ b/3rdparty/plibsys/src/puthread-posix.c
@@ -0,0 +1,580 @@
+/*
+ * The MIT License
+ *
+ * Copyright (C) 2010-2019 Alexander Saprykin <saprykin.spb@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * 'Software'), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "pmem.h"
+#include "patomic.h"
+#include "puthread.h"
+#include "puthread-private.h"
+
+#ifdef P_OS_SCO
+# include "pmutex.h"
+#endif
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <pthread.h>
+#include <time.h>
+
+/* On some OS like OpenBSD it must follow <pthread.h> */
+#ifdef PLIBSYS_NEED_PTHREAD_NP_H
+# include <pthread_np.h>
+#endif
+
+#ifdef PLIBSYS_HAS_POSIX_SCHEDULING
+# ifndef P_OS_VMS
+# include <sched.h>
+# endif
+#endif
+
+#ifdef PLIBSYS_HAS_PTHREAD_PRCTL
+# include <sys/prctl.h>
+# include <linux/prctl.h>
+#endif
+
+#ifdef P_OS_QNX6
+# include <sys/neutrino.h>
+#endif
+
+#ifdef P_OS_HAIKU
+# include <kernel/OS.h>
+#endif
+
+/* Some systems without native pthreads may lack some of the constants,
+ * leave them zero as we are not going to use them anyway */
+
+#ifndef PTHREAD_CREATE_JOINABLE
+# define PTHREAD_CREATE_JOINABLE 0
+#endif
+
+#ifndef PTHREAD_CREATE_DETACHED
+# define PTHREAD_CREATE_DETACHED 0
+#endif
+
+#ifdef PLIBSYS_HAS_POSIX_SCHEDULING
+# ifndef PTHREAD_INHERIT_SCHED
+# define PTHREAD_INHERIT_SCHED 0
+# endif
+
+# ifndef PTHREAD_EXPLICIT_SCHED
+# define PTHREAD_EXPLICIT_SCHED 0
+# endif
+
+/* Old Linux kernels may lack a definition */
+# if defined (P_OS_LINUX) && !defined (SCHED_IDLE)
+# define SCHED_IDLE 5
+# endif
+#endif
+
+/* Max length of a thead name */
+#if defined(P_OS_LINUX)
+# define PUTHREAD_MAX_NAME 16
+#elif defined(P_OS_NETBSD)
+# define PUTHREAD_MAX_NAME PTHREAD_MAX_NAMELEN_NP
+#elif defined(P_OS_DRAGONFLY)
+# define PUTHREAD_MAX_NAME 16
+#elif defined(P_OS_SOLARIS)
+# define PUTHREAD_MAX_NAME 32
+#elif defined(P_OS_TRU64)
+# define PUTHREAD_MAX_NAME 32
+#elif defined(P_OS_VMS)
+# define PUTHREAD_MAX_NAME 32
+#elif defined(P_OS_QNX6)
+# define PUTHREAD_MAX_NAME _NTO_THREAD_NAME_MAX
+#elif defined(P_OS_HAIKU)
+# define PUTHREAD_MAX_NAME 32
+#endif
+
+typedef pthread_t puthread_hdl;
+
+struct PUThread_ {
+ PUThreadBase base;
+ puthread_hdl hdl;
+};
+
+struct PUThreadKey_ {
+ pthread_key_t *key;
+ PDestroyFunc free_func;
+};
+
+#ifdef P_OS_SCO
+static PMutex *pp_uthread_tls_mutex = NULL;
+#endif
+
+#ifdef PLIBSYS_HAS_POSIX_SCHEDULING
+static pboolean pp_uthread_get_unix_priority (PUThreadPriority prio, int *sched_policy, int *sched_priority);
+#endif
+
+static pthread_key_t * pp_uthread_get_tls_key (PUThreadKey *key);
+
+#ifdef PLIBSYS_HAS_POSIX_SCHEDULING
+static pboolean
+pp_uthread_get_unix_priority (PUThreadPriority prio, int *sched_policy, int *sched_priority)
+{
+ pint lowBound, upperBound;
+ pint prio_min, prio_max;
+ pint native_prio;
+
+#ifdef SCHED_IDLE
+ if (prio == P_UTHREAD_PRIORITY_IDLE) {
+ *sched_policy = SCHED_IDLE;
+ *sched_priority = 0;
+ return TRUE;
+ }
+
+ lowBound = (pint) P_UTHREAD_PRIORITY_LOWEST;
+#else
+ lowBound = (pint) P_UTHREAD_PRIORITY_IDLE;
+#endif
+ upperBound = (pint) P_UTHREAD_PRIORITY_TIMECRITICAL;
+
+ prio_min = sched_get_priority_min (*sched_policy);
+ prio_max = sched_get_priority_max (*sched_policy);
+
+ if (P_UNLIKELY (prio_min == -1 || prio_max == -1))
+ return FALSE;
+
+ native_prio = ((pint) prio - lowBound) * (prio_max - prio_min) / upperBound + prio_min;
+
+ if (P_UNLIKELY (native_prio > prio_max))
+ native_prio = prio_max;
+
+ if (P_UNLIKELY (native_prio < prio_min))
+ native_prio = prio_min;
+
+ *sched_priority = native_prio;
+
+ return TRUE;
+}
+#endif
+
+static pthread_key_t *
+pp_uthread_get_tls_key (PUThreadKey *key)
+{
+ pthread_key_t *thread_key;
+
+ thread_key = (pthread_key_t *) p_atomic_pointer_get ((ppointer) &key->key);
+
+ if (P_LIKELY (thread_key != NULL))
+ return thread_key;
+
+#ifdef P_OS_SCO
+ p_mutex_lock (pp_uthread_tls_mutex);
+
+ thread_key = key->key;
+
+ if (P_LIKELY (thread_key == NULL)) {
+#endif
+ if (P_UNLIKELY ((thread_key = p_malloc0 (sizeof (pthread_key_t))) == NULL)) {
+ P_ERROR ("PUThread::pp_uthread_get_tls_key: failed to allocate memory");
+#ifdef P_OS_SCO
+ p_mutex_unlock (pp_uthread_tls_mutex);
+#endif
+ return NULL;
+ }
+
+ if (P_UNLIKELY (pthread_key_create (thread_key, key->free_func) != 0)) {
+ P_ERROR ("PUThread::pp_uthread_get_tls_key: pthread_key_create() failed");
+ p_free (thread_key);
+#ifdef P_OS_SCO
+ p_mutex_unlock (pp_uthread_tls_mutex);
+#endif
+ return NULL;
+ }
+
+#ifndef P_OS_SCO
+ if (P_UNLIKELY (p_atomic_pointer_compare_and_exchange ((ppointer) &key->key,
+ NULL,
+ (ppointer) thread_key) == FALSE)) {
+ if (P_UNLIKELY (pthread_key_delete (*thread_key) != 0)) {
+ P_ERROR ("PUThread::pp_uthread_get_tls_key: pthread_key_delete() failed");
+ p_free (thread_key);
+ return NULL;
+ }
+
+ p_free (thread_key);
+
+ thread_key = key->key;
+ }
+#else
+ key->key = thread_key;
+ }
+
+ p_mutex_unlock (pp_uthread_tls_mutex);
+#endif
+
+ return thread_key;
+}
+
+void
+p_uthread_init_internal (void)
+{
+#ifdef P_OS_SCO
+ if (P_LIKELY (pp_uthread_tls_mutex == NULL))
+ pp_uthread_tls_mutex = p_mutex_new ();
+#endif
+}
+
+void
+p_uthread_shutdown_internal (void)
+{
+#ifdef P_OS_SCO
+ if (P_LIKELY (pp_uthread_tls_mutex != NULL)) {
+ p_mutex_free (pp_uthread_tls_mutex);
+ pp_uthread_tls_mutex = NULL;
+ }
+#endif
+}
+
+void
+p_uthread_win32_thread_detach (void)
+{
+}
+
+PUThread *
+p_uthread_create_internal (PUThreadFunc func,
+ pboolean joinable,
+ PUThreadPriority prio,
+ psize stack_size)
+{
+ PUThread *ret;
+ pthread_attr_t attr;
+ pint create_code;
+#ifdef PLIBSYS_HAS_POSIX_SCHEDULING
+ struct sched_param sched;
+ pint native_prio;
+ pint sched_policy;
+#endif
+
+#if defined (PLIBSYS_HAS_POSIX_STACKSIZE) && defined (_SC_THREAD_STACK_MIN)
+ plong min_stack;
+#endif
+
+ if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PUThread))) == NULL)) {
+ P_ERROR ("PUThread::p_uthread_create_internal: failed to allocate memory");
+ return NULL;
+ }
+
+ ret->base.joinable = joinable;
+
+ if (P_UNLIKELY (pthread_attr_init (&attr) != 0)) {
+ P_ERROR ("PUThread::p_uthread_create_internal: pthread_attr_init() failed");
+ p_free (ret);
+ return NULL;
+ }
+
+ if (P_UNLIKELY (pthread_attr_setdetachstate (&attr,
+ joinable ? PTHREAD_CREATE_JOINABLE
+ : PTHREAD_CREATE_DETACHED) != 0)) {
+ P_ERROR ("PUThread::p_uthread_create_internal: pthread_attr_setdetachstate() failed");
+ pthread_attr_destroy (&attr);
+ p_free (ret);
+ return NULL;
+ }
+
+#ifdef PLIBSYS_HAS_POSIX_SCHEDULING
+ if (prio == P_UTHREAD_PRIORITY_INHERIT) {
+ if (P_UNLIKELY (pthread_attr_setinheritsched (&attr, PTHREAD_INHERIT_SCHED) != 0))
+ P_WARNING ("PUThread::p_uthread_create_internal: pthread_attr_setinheritsched() failed");
+ } else {
+ if (P_LIKELY (pthread_attr_getschedpolicy (&attr, &sched_policy) == 0)) {
+ if (P_LIKELY (pp_uthread_get_unix_priority (prio,
+ &sched_policy,
+ &native_prio) == TRUE)) {
+ memset (&sched, 0, sizeof (sched));
+ sched.sched_priority = native_prio;
+
+ if (P_LIKELY (pthread_attr_setinheritsched (&attr, PTHREAD_EXPLICIT_SCHED) != 0 ||
+ pthread_attr_setschedpolicy (&attr, sched_policy) != 0 ||
+ pthread_attr_setschedparam (&attr, &sched) != 0))
+ P_WARNING ("PUThread::p_uthread_create_internal: failed to set priority");
+ } else
+ P_WARNING ("PUThread::p_uthread_create_internal: pp_uthread_get_unix_priority() failed");
+ } else
+ P_WARNING ("PUThread::p_uthread_create_internal: pthread_attr_getschedpolicy() failed");
+ }
+#endif
+
+#ifdef PLIBSYS_HAS_POSIX_STACKSIZE
+# ifdef _SC_THREAD_STACK_MIN
+ if (stack_size > 0) {
+ min_stack = (plong) sysconf (_SC_THREAD_STACK_MIN);
+
+ if (P_LIKELY (min_stack > 0)) {
+ if (P_UNLIKELY (stack_size < (psize) min_stack))
+ stack_size = (psize) min_stack;
+ } else
+ P_WARNING ("PUThread::p_uthread_create_internal: sysconf() with _SC_THREAD_STACK_MIN failed");
+
+ if (P_UNLIKELY (pthread_attr_setstacksize (&attr, stack_size) != 0))
+ P_WARNING ("PUThread::p_uthread_create_internal: pthread_attr_setstacksize() failed");
+ }
+# endif
+#endif
+
+ create_code = pthread_create (&ret->hdl, &attr, func, ret);
+
+#ifdef EPERM
+ if (create_code == EPERM) {
+# ifdef PLIBSYS_HAS_POSIX_SCHEDULING
+ pthread_attr_setinheritsched (&attr, PTHREAD_INHERIT_SCHED);
+# endif
+ create_code = pthread_create (&ret->hdl, &attr, func, ret);
+ }
+#endif
+
+ if (P_UNLIKELY (create_code != 0)) {
+ P_ERROR ("PUThread::p_uthread_create_internal: pthread_create() failed");
+ pthread_attr_destroy (&attr);
+ p_free (ret);
+ return NULL;
+ }
+
+ ret->base.prio = prio;
+ pthread_attr_destroy (&attr);
+
+ return ret;
+}
+
+void
+p_uthread_exit_internal (void)
+{
+ pthread_exit (P_INT_TO_POINTER (0));
+}
+
+void
+p_uthread_wait_internal (PUThread *thread)
+{
+ if (P_UNLIKELY (pthread_join (thread->hdl, NULL) != 0))
+ P_ERROR ("PUThread::p_uthread_wait_internal: pthread_join() failed");
+}
+
+void
+p_uthread_set_name_internal (PUThread *thread)
+{
+ pchar *thr_name = NULL;
+ pint res = 0;
+ pboolean is_alloc = FALSE;
+#ifdef PUTHREAD_MAX_NAME
+ psize namelen = 0;
+#endif
+
+ thr_name = thread->base.name;
+
+#ifdef PUTHREAD_MAX_NAME
+ namelen = strlen (thr_name);
+
+ if (namelen > PUTHREAD_MAX_NAME - 1) {
+ if (P_UNLIKELY ((thr_name = p_malloc0 (namelen + 1)) == NULL)) {
+ P_ERROR ("PUThread::p_uthread_set_name_internal: failed to allocate memory");
+ return;
+ }
+
+ memcpy (thr_name, thread->base.name, PUTHREAD_MAX_NAME - 1);
+
+ is_alloc = TRUE;
+ }
+#endif
+
+#if defined(PLIBSYS_HAS_PTHREAD_SETNAME)
+# if defined(P_OS_MAC)
+ if (thread->hdl != pthread_self ())
+ P_WARNING ("PUThread::p_uthread_set_name_internal: only calling thread is supported in macOS");
+ else
+ res = pthread_setname_np (thr_name);
+# elif defined(P_OS_NETBSD)
+ res = pthread_setname_np (thread->hdl, "%s", thr_name);
+# elif defined(P_OS_TRU64) || defined(P_OS_VMS)
+ res = pthread_setname_np (thread->hdl, thr_name, 0);
+# else
+ res = pthread_setname_np (thread->hdl, thr_name);
+# endif
+#elif defined(PLIBSYS_HAS_PTHREAD_SET_NAME)
+ pthread_set_name_np (thread->hdl, thr_name);
+#elif defined(PLIBSYS_HAS_PTHREAD_PRCTL)
+ if (thread->hdl != pthread_self ())
+ P_WARNING ("PUThread::p_uthread_set_name_internal: prctl() can be used on calling thread only");
+ else
+ res = prctl (PR_SET_NAME, thr_name, NULL, NULL, NULL);
+#elif defined(P_OS_HAIKU)
+ res = rename_thread (find_thread (NULL), thr_name);
+#endif
+
+ if (is_alloc == TRUE)
+ p_free (thr_name);
+
+ if (res != 0)
+ P_WARNING ("PUThread::p_uthread_set_name_internal: failed to set thread system name");
+}
+
+void
+p_uthread_free_internal (PUThread *thread)
+{
+ p_free (thread);
+}
+
+P_LIB_API void
+p_uthread_yield (void)
+{
+ sched_yield ();
+}
+
+P_LIB_API pboolean
+p_uthread_set_priority (PUThread *thread,
+ PUThreadPriority prio)
+{
+#ifdef PLIBSYS_HAS_POSIX_SCHEDULING
+ struct sched_param sched;
+ pint policy;
+ pint native_prio;
+#endif
+
+ if (P_UNLIKELY (thread == NULL))
+ return FALSE;
+
+#ifdef PLIBSYS_HAS_POSIX_SCHEDULING
+ if (P_UNLIKELY (pthread_getschedparam (thread->hdl, &policy, &sched) != 0)) {
+ P_ERROR ("PUThread::p_uthread_set_priority: pthread_getschedparam() failed");
+ return FALSE;
+ }
+
+ if (P_UNLIKELY (pp_uthread_get_unix_priority (prio, &policy, &native_prio) == FALSE)) {
+ P_ERROR ("PUThread::p_uthread_set_priority: pp_uthread_get_unix_priority() failed");
+ return FALSE;
+ }
+
+ memset (&sched, 0, sizeof (sched));
+ sched.sched_priority = native_prio;
+
+ if (P_UNLIKELY (pthread_setschedparam (thread->hdl, policy, &sched) != 0)) {
+ P_ERROR ("PUThread::p_uthread_set_priority: pthread_setschedparam() failed");
+ return FALSE;
+ }
+#endif
+
+ thread->base.prio = prio;
+ return TRUE;
+}
+
+P_LIB_API P_HANDLE
+p_uthread_current_id (void)
+{
+ return (P_HANDLE) ((psize) pthread_self ());
+}
+
+P_LIB_API PUThreadKey *
+p_uthread_local_new (PDestroyFunc free_func)
+{
+ PUThreadKey *ret;
+
+ if (P_UNLIKELY ((ret = p_malloc0 (sizeof (PUThreadKey))) == NULL)) {
+ P_ERROR ("PUThread::p_uthread_local_new: failed to allocate memory");
+ return NULL;
+ }
+
+ ret->free_func = free_func;
+
+ return ret;
+}
+
+P_LIB_API void
+p_uthread_local_free (PUThreadKey *key)
+{
+ if (P_UNLIKELY (key == NULL))
+ return;
+
+ p_free (key);
+}
+
+P_LIB_API ppointer
+p_uthread_get_local (PUThreadKey *key)
+{
+ pthread_key_t *tls_key;
+#ifdef P_OS_SCO
+ ppointer value;
+#endif
+
+ if (P_UNLIKELY (key == NULL))
+ return NULL;
+
+ if (P_UNLIKELY ((tls_key = pp_uthread_get_tls_key (key)) == NULL))
+ return NULL;
+
+#ifdef P_OS_SCO
+ if (P_UNLIKELY (pthread_getspecific (*tls_key, &value) != 0))
+ return NULL;
+
+ return value;
+#else
+ return pthread_getspecific (*tls_key);
+#endif
+}
+
+P_LIB_API void
+p_uthread_set_local (PUThreadKey *key,
+ ppointer value)
+{
+ pthread_key_t *tls_key;
+
+ if (P_UNLIKELY (key == NULL))
+ return;
+
+ tls_key = pp_uthread_get_tls_key (key);
+
+ if (P_LIKELY (tls_key != NULL)) {
+ if (P_UNLIKELY (pthread_setspecific (*tls_key, value) != 0))
+ P_ERROR ("PUThread::p_uthread_set_local: pthread_setspecific() failed");
+ }
+}
+
+P_LIB_API void
+p_uthread_replace_local (PUThreadKey *key,
+ ppointer value)
+{
+ pthread_key_t *tls_key;
+ ppointer old_value;
+
+ if (P_UNLIKELY (key == NULL))
+ return;
+
+ tls_key = pp_uthread_get_tls_key (key);
+
+ if (P_UNLIKELY (tls_key == NULL))
+ return;
+
+#ifdef P_OS_SCO
+ if (P_UNLIKELY (pthread_getspecific (*tls_key, &old_value) != 0))
+ return;
+#else
+ old_value = pthread_getspecific (*tls_key);
+#endif
+
+ if (old_value != NULL && key->free_func != NULL)
+ key->free_func (old_value);
+
+ if (P_UNLIKELY (pthread_setspecific (*tls_key, value) != 0))
+ P_ERROR ("PUThread::p_uthread_replace_local: pthread_setspecific() failed");
+}