* c threads, v.5.9
@ 2014-08-14 20:55 Jens Gustedt
0 siblings, 0 replies; only message in thread
From: Jens Gustedt @ 2014-08-14 20:55 UTC (permalink / raw)
To: musl
[-- Attachment #1.1: Type: text/plain, Size: 1045 bytes --]
Hello,
this is now a version that I am quite confident of. It is a special
edition that doesn't use spinlocks. The only "principal" thing it
still has that people might not like is dynamic allocation of
auxiliary structures for mtx_t and cnd_t. For the mtx_t, this could
perhaps be avoided, but for the cnd_t it is really convenient, because
it clearly separates different batches of cnd-waiters that should
never be mixed.
In summary:
- (almost) all is private futex
- the internal locks use a minimal test-and-set logic that is modeled
after C's atomic_flag
- mutexes and conditions are simpler (I hope) than their pthread
counterparts because they don't need process sharing or robustness
Best
Jens
--
:: INRIA Nancy Grand Est ::: AlGorille ::: ICube/ICPS :::
:: ::::::::::::::: office Strasbourg : +33 368854536 ::
:: :::::::::::::::::::::: gsm France : +33 651400183 ::
:: ::::::::::::::: gsm international : +49 15737185122 ::
:: http://icube-icps.unistra.fr/index.php/Jens_Gustedt ::
[-- Attachment #1.2: thrd11-v5.9.patch --]
[-- Type: text/x-patch, Size: 53138 bytes --]
diff --git a/include/alltypes.h.in b/include/alltypes.h.in
index 8478fb4..4cd2cb1 100644
--- a/include/alltypes.h.in
+++ b/include/alltypes.h.in
@@ -58,6 +58,10 @@ TYPEDEF struct { unsigned __attr; } pthread_mutexattr_t;
TYPEDEF struct { unsigned __attr; } pthread_condattr_t;
TYPEDEF struct { unsigned __attr; } pthread_barrierattr_t;
TYPEDEF struct { unsigned __attr[2]; } pthread_rwlockattr_t;
+TYPEDEF pthread_cond_t cnd_t;
+TYPEDEF pthread_mutex_t mtx_t;
+
+
TYPEDEF struct _IO_FILE FILE;
diff --git a/include/threads.h b/include/threads.h
new file mode 100644
index 0000000..9486b8c
--- /dev/null
+++ b/include/threads.h
@@ -0,0 +1,198 @@
+#ifndef _THREADS_H
+#define _THREADS_H
+
+/* This one is explicitly allowed to be included. */
+#include <time.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define __NEED_cnd_t
+#define __NEED_mtx_t
+/* Until we come up with a better naming scheme, we need to expose
+ some pthread types. */
+#define __NEED_pthread_cond_t
+#define __NEED_pthread_mutex_t
+#define __NEED_pthread_once_t
+#define __NEED_pthread_t
+#define __NEED_pthread_key_t
+
+#include <bits/alltypes.h>
+#include <bits/errno.h>
+
+typedef struct __mtx_on_heap __mtx_t;
+void __mtx_unref(__mtx_t *) __attribute__((nonnull(1)));
+__mtx_t* __mtx_getref(mtx_t *) __attribute__((nonnull(1)));
+
+int __mtx_lock(__mtx_t *) __attribute__((nonnull(1)));
+int __mtx_trylock(__mtx_t *) __attribute__((nonnull(1)));
+int __mtx_unlock(__mtx_t *) __attribute__((nonnull(1)));
+int __mtx_timedlock(__mtx_t *restrict, const struct timespec *restrict) __attribute__((nonnull(1)));
+
+typedef struct __cnd_on_heap __cnd_t;
+void __cnd_unref(__cnd_t *) __attribute__((nonnull(1)));
+__cnd_t* __cnd_getref(cnd_t *, __mtx_t *, int *) __attribute__((nonnull(1,3)));
+
+int __cnd_wait(__cnd_t *restrict, __mtx_t *restrict) __attribute__((nonnull(1,2)));
+
+typedef pthread_t thrd_t;
+typedef pthread_key_t tss_t;
+typedef int (*thrd_start_t)(void*);
+typedef void (*tss_dtor_t)(void*);
+
+typedef struct once_flag once_flag;
+
+/* Just have a copy of the definition for the moment. This has to be
+ replaced by sharing that declaration in alltypes.h */
+struct __once_cb {
+ void (*__f)(void *);
+ void *__x;
+ struct __ptcb *__next;
+};
+
+struct once_flag {
+ int __cntrl;
+ int __waiters;
+ struct __once_cb __cb;
+};
+
+#ifndef __THRD_EXPERIMENTAL
+# define __THRD_EXPERIMENTAL 1
+#endif
+
+ /* The following list of 9 integer constants makes up for the binary
+ compatibility of this C thread implementation. You must never
+ link code against versions of the C library that do not agree
+ upon these ABI parameters.
+
+ Additionally this implementation assumes that the 5 types have
+ the same size across C libraries and that these types can be
+ initialized by the default initializer.
+
+ The values for the 9 parameters are not fixed for now. Depending
+ on the choices of other implementations and the evolution of the
+ C standard they may still change. This should happen rarely, but
+ you have to consider the C thread feature to be experimental
+ until then, and be prepared to recompile your binary when linking
+ against a different implementation or a new version.
+
+ The macro __THRD_EXPERIMENTAL will be defined as long as we
+ consider this ABI to be unstable. This comes with some link time
+ checks and an overhead of some bytes. At your own risk you may
+ switch this check off by defining __THRD_EXPERIMENTAL macro to
+ 0. */
+
+#define __THRD_SUCCESS 0
+#define __THRD_BUSY EBUSY
+#define __THRD_ERROR EINVAL
+#define __THRD_NOMEM ENOMEM
+#define __THRD_TIMEDOUT ETIMEDOUT
+#define __MTX_PLAIN 0
+#define __MTX_RECURSIVE 1
+#define __MTX_TIMED 2
+#define TSS_DTOR_ITERATIONS 4
+
+#define __THRD_CONCAT10_(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) \
+_0 ## _ ## _1 ## _ ## _2 ## _ ## _3 ## _ ## _4 ## _ ## _5 ## _ ## _6 ## _ ## _7 ## _ ## _8 ## _ ## _9
+
+#define __THRD_CONCAT10(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) \
+ __THRD_CONCAT10_(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9)
+
+
+#define __THRD_ABI \
+__THRD_CONCAT10(__thrd_abi, \
+ __THRD_SUCCESS, \
+ __THRD_BUSY, \
+ __THRD_ERROR, \
+ __THRD_NOMEM, \
+ __THRD_TIMEDOUT, \
+ __MTX_PLAIN, \
+ __MTX_RECURSIVE, \
+ __MTX_TIMED, \
+ TSS_DTOR_ITERATIONS \
+ )
+
+#define __THRD_SHFT(X, Y) (((X) << 3) ^ (Y))
+
+enum {
+ __thrd_abi =
+ __THRD_SHFT(sizeof(cnd_t),
+ __THRD_SHFT(sizeof(mtx_t),
+ __THRD_SHFT(sizeof(once_flag),
+ __THRD_SHFT(sizeof(thrd_t),
+ sizeof(tss_t))))),
+};
+
+extern unsigned const __THRD_ABI;
+
+#define __THRD_ABI_CHECK (1/(__THRD_ABI == __thrd_abi))
+
+#if __THRD_EXPERIMENTAL
+# define __THRD_ABI_MARK __attribute__((used)) static unsigned const*const __thrd_abi_mark = &__THRD_ABI
+#else
+# define __THRD_ABI_MARK typedef unsigned __thrd_abi_dummy_type
+#endif
+
+enum {
+ thrd_success = __THRD_SUCCESS,
+ thrd_busy = __THRD_BUSY,
+ thrd_error = __THRD_ERROR,
+ thrd_nomem = __THRD_NOMEM,
+ thrd_timedout = __THRD_TIMEDOUT,
+};
+
+enum {
+ mtx_plain = __MTX_PLAIN,
+ mtx_recursive = __MTX_RECURSIVE,
+ // all mutexes are timed, here. so this is a no-op
+ mtx_timed = __MTX_TIMED,
+};
+
+#define ONCE_FLAG_INIT { ._cntrl = 0, }
+#define thread_local _Thread_local
+
+int thrd_create(thrd_t *, thrd_start_t, void *);
+_Noreturn void thrd_exit(int);
+
+int thrd_detach(thrd_t);
+int thrd_join(thrd_t, int *);
+
+int thrd_sleep(const struct timespec *, struct timespec *);
+void thrd_yield(void);
+
+thrd_t thrd_current(void);
+int thrd_equal(thrd_t, thrd_t);
+#define thrd_equal(A, B) ((A) == (B))
+
+void call_once(once_flag *, void (*)(void));
+
+int mtx_init(mtx_t *, int);
+void mtx_destroy(mtx_t *);
+
+int mtx_lock(mtx_t *);
+int mtx_timedlock(mtx_t *restrict, const struct timespec *restrict);
+int mtx_trylock(mtx_t *);
+int mtx_unlock(mtx_t *);
+
+int cnd_init(cnd_t *);
+void cnd_destroy(cnd_t *);
+
+int cnd_broadcast(cnd_t *);
+int cnd_signal(cnd_t *);
+
+int cnd_timedwait(cnd_t *restrict, mtx_t *restrict, const struct timespec *restrict);
+int cnd_wait(cnd_t *, mtx_t *);
+
+int tss_create(tss_t *, tss_dtor_t);
+void tss_delete(tss_t key);
+
+int tss_set(tss_t, void *);
+void *tss_get(tss_t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/internal/pthread_impl.h b/src/internal/pthread_impl.h
index 650e811..f967c25 100644
--- a/src/internal/pthread_impl.h
+++ b/src/internal/pthread_impl.h
@@ -51,6 +51,53 @@ struct __timer {
pthread_t thread;
};
+/* For the moment this type has the same layout as
+ pthread_mutex_t. This is subject to change in later versions of the
+ C thread implementation, be it for the simple reason that this
+ would reduce the size of the structure by half. */
+struct __mtx_on_heap {
+ int _mt_typ;
+ int _mt_lck;
+ int _mt_wts;
+ int _mt_pad3;
+ int _mt_pad4;
+ int _mt_cnt;
+ int _mt_rfs;
+ int _mt_pad7;
+ int _mt_pad8;
+ int _mt_pad9;
+};
+
+static inline
+void __mtx_addref(struct __mtx_on_heap *m) {
+ if (m) {
+ a_inc(&m->_mt_rfs);
+ }
+}
+
+struct __cnd_on_heap {
+ int rfs;
+ int tok;
+ int wts;
+ int seq;
+ struct __mtx_on_heap* mtx;
+};
+
+static inline
+void __cnd_addref(struct __cnd_on_heap *c) {
+ if (c) {
+ a_inc(&c->rfs);
+ }
+}
+
+/* Unless told otherwise this C thread implementation will only use
+ private futexes. */
+#ifdef __MTX_SHARED
+#define THRD_PRIVATE 0
+#else
+#define THRD_PRIVATE 128
+#endif
+
#define __SU (sizeof(size_t)/sizeof(int))
#define _a_stacksize __u.__s[0]
@@ -66,6 +113,12 @@ struct __timer {
#define _m_prev __u.__p[3]
#define _m_next __u.__p[4]
#define _m_count __u.__i[5]
+#define _mx_type __u.__i[0]
+#define _mx_lock __u.__i[1]
+#define _mx_mtx __u.__p[2]
+#define _mx_prev __u.__p[3] /* unused */
+#define _mx_next __u.__p[4] /* unused */
+#define _mx_count __u.__i[5] /* unused */
#define _c_mutex __u.__p[0]
#define _c_seq __u.__i[2]
#define _c_waiters __u.__i[3]
@@ -74,6 +127,14 @@ struct __timer {
#define _c_lockwait __u.__i[6]
#define _c_waiters2 __u.__i[7]
#define _c_destroy __u.__i[8]
+#define _cx_cnd __u.__p[0]
+#define _cx_seq __u.__i[2] /* unused */
+#define _cx_waiters __u.__i[3] /* unused */
+#define _cx_clock __u.__i[4] /* unused */
+#define _cx_lock __u.__i[5]
+#define _cx_lockwait __u.__i[6]
+#define _cx_waiters2 __u.__i[7]/* unused */
+#define _cx_destroy __u.__i[8] /* unused */
#define _rw_lock __u.__i[0]
#define _rw_waiters __u.__i[1]
#define _b_lock __u.__i[0]
@@ -83,6 +144,34 @@ struct __timer {
#define _b_waiters2 __u.__i[4]
#define _b_inst __u.__p[3]
+#if defined(__ATOMIC_ACQ_REL) && defined(__ATOMIC_ACQUIRE) && defined(__ATOMIC_RELEASE)
+#define a_tas(X) __atomic_test_and_set((X), __ATOMIC_ACQ_REL)
+#define a_aqf() __atomic_thread_fence(__ATOMIC_ACQUIRE)
+#define a_spunl(X) \
+({ \
+ __asm__ volatile ("# end CS"); \
+ int volatile* _lck = (X); \
+ __atomic_clear(_lck, __ATOMIC_RELEASE); \
+})
+#else
+#define a_tas(X) __sync_lock_test_and_set((X), 1)
+#define a_aqf() __sync_synchronize()
+#define a_spunl(X) \
+({ \
+ __asm__ volatile ("# end CS"); \
+ int volatile* _lck = (X); \
+ __sync_lock_release(_lck); \
+})
+#endif
+
+#define a_splck(X) \
+({ \
+ int volatile* _lck = (X); \
+ while (a_tas(_lck)) (void)0; \
+ a_aqf(); \
+ __asm__ volatile ("# start CS"); \
+ })
+
#include "pthread_arch.h"
#define SIGTIMER 32
@@ -111,6 +200,10 @@ void __wait(volatile int *, volatile int *, int, int);
#define __wake(addr, cnt, priv) \
__syscall(SYS_futex, addr, FUTEX_WAKE, (cnt)<0?INT_MAX:(cnt))
+void __wait_priv(volatile int *, volatile int *, int);
+#define __wake_priv(addr, cnt) \
+ __syscall(SYS_futex, addr, FUTEX_WAKE|THRD_PRIVATE, (cnt)<0?INT_MAX:(cnt))
+
void __acquire_ptc();
void __release_ptc();
void __inhibit_ptc();
diff --git a/src/mman/mprotect.c b/src/mman/mprotect.c
index f488486..535787b 100644
--- a/src/mman/mprotect.c
+++ b/src/mman/mprotect.c
@@ -2,10 +2,12 @@
#include "libc.h"
#include "syscall.h"
-int mprotect(void *addr, size_t len, int prot)
+int __mprotect(void *addr, size_t len, int prot)
{
size_t start, end;
start = (size_t)addr & -PAGE_SIZE;
end = (size_t)((char *)addr + len + PAGE_SIZE-1) & -PAGE_SIZE;
return syscall(SYS_mprotect, start, end-start, prot);
}
+
+weak_alias(__mprotect, mprotect);
diff --git a/src/sched/thrd_yield.c b/src/sched/thrd_yield.c
new file mode 100644
index 0000000..301e953
--- /dev/null
+++ b/src/sched/thrd_yield.c
@@ -0,0 +1,7 @@
+#include <sched.h>
+#include "syscall.h"
+
+void thrd_yield()
+{
+ (void)syscall(SYS_sched_yield);
+}
diff --git a/src/thread/__cnd_getref.c b/src/thread/__cnd_getref.c
new file mode 100644
index 0000000..749eaae
--- /dev/null
+++ b/src/thread/__cnd_getref.c
@@ -0,0 +1,73 @@
+#include "pthread_impl.h"
+#include <threads.h>
+
+static inline __cnd_t* __cnd_init(__cnd_t *c, __mtx_t *m) {
+ if (c) {
+ *c = (__cnd_t) {
+ .rfs = 2,
+ .mtx = m,
+ .wts = 1,
+ .seq = 0,
+ };
+ }
+ if (m) __mtx_addref(m);
+ return c;
+}
+
+static inline void __cnd_delete(__cnd_t *c) __attribute__((nonnull(1)));
+
+static inline void __cnd_delete(__cnd_t *c) {
+ __mtx_unref(c->mtx);
+ c->mtx = 0;
+ free(c);
+}
+
+void __cnd_unref(__cnd_t *c) {
+ if (a_fetch_add(&c->rfs, -1) == 1) {
+ __cnd_delete(c);
+ }
+}
+
+static inline __cnd_t* __cnd_getref_def(cnd_t *cond, __mtx_t* m, __cnd_t* def, int * seq) __attribute__((nonnull(1,2)));
+
+static inline __cnd_t* __cnd_getref_def(cnd_t *cond, __mtx_t* m, __cnd_t* def, int * seq) {
+ __cnd_t * ret;
+ /* Critical section protected by lock. */
+ __lock(&cond->_cx_lock);
+ ret = cond->_cx_cnd;
+ if (ret) {
+ if (m) {
+ /* Has the cnd just been initialized? */
+ if (!ret->mtx) {
+ __mtx_addref(m);
+ ret->mtx = m;
+ /* If not, is it consistent with previous usage? */
+ } else if (ret->mtx != m) {
+ ret = 0;
+ goto UNLOCK;
+ }
+ }
+ ++ret->wts;
+ __cnd_addref(ret);
+ *seq = ret->seq;
+ } else if (def) {
+ ret = def;
+ cond->_cx_cnd = def;
+ }
+ UNLOCK:
+ __unlock(&cond->_cx_lock);
+ return ret;
+}
+
+__cnd_t* __cnd_getref(cnd_t *cond, __mtx_t * m, int * seq) {
+ __cnd_t * ret = __cnd_getref_def(cond, m, 0, seq);
+ if (!ret) {
+ __cnd_t * new = __cnd_init(malloc(sizeof *ret), m);
+ if (new) {
+ ret = __cnd_getref_def(cond, m, new, seq);
+ /* somebody sneaked in between the first and second call */
+ if (ret != new) __cnd_delete(new);
+ }
+ }
+ return ret;
+}
diff --git a/src/thread/__lock.c b/src/thread/__lock.c
index 0874c04..8a42e2d 100644
--- a/src/thread/__lock.c
+++ b/src/thread/__lock.c
@@ -1,15 +1,29 @@
#include "pthread_impl.h"
-void __lock(volatile int *l)
+void __lock(volatile int *addr)
{
- if (libc.threads_minus_1)
- while (a_swap(l, 1)) __wait(l, l+1, 1, 1);
+ if (libc.threads_minus_1) {
+ int volatile* waiters = addr+1;
+ for (;;) {
+ for (int spins = 0; spins < 10000; ++spins) {
+ if (a_tas(addr)) a_spin();
+ else {
+ /* a lock must ensure that all data
+ that is written before will be
+ visible */
+ a_aqf();
+ return;
+ }
+ }
+ a_inc(waiters);
+ __syscall(SYS_futex, addr, FUTEX_WAIT|THRD_PRIVATE, 1, 0);
+ a_dec(waiters);
+ }
+ }
}
void __unlock(volatile int *l)
{
- if (l[0]) {
- a_store(l, 0);
- if (l[1]) __wake(l, 1, 1);
- }
+ a_spunl(l);
+ if (l[1]) __wake_priv(l, 1);
}
diff --git a/src/thread/__mtx_getref.c b/src/thread/__mtx_getref.c
new file mode 100644
index 0000000..4f98abd
--- /dev/null
+++ b/src/thread/__mtx_getref.c
@@ -0,0 +1,49 @@
+#include "pthread_impl.h"
+#include <threads.h>
+
+static inline __mtx_t* __mtx_init(__mtx_t *m, int type) {
+ if (m) {
+ *m = (__mtx_t) {
+ ._mt_rfs = 2,
+ ._mt_typ = type,
+ };
+ }
+ return m;
+}
+
+static inline void __mtx_delete(__mtx_t * m) {
+ free(m);
+}
+
+void __mtx_unref(__mtx_t *m) {
+ if (m) {
+ if (a_fetch_add(&m->_mt_rfs, -1) == 1) {
+ __mtx_delete(m);
+ }
+ }
+}
+
+static inline __mtx_t* __mtx_getref_def(mtx_t *mut, __mtx_t* def) {
+ /* Critical section protected by lock . */
+ __lock(&mut->_mx_lock);
+ __mtx_t * ret = mut->_mx_mtx;
+ if (ret) {
+ __mtx_addref(ret);
+ } else if (def) {
+ ret = def;
+ mut->_mx_mtx = def;
+ }
+ __unlock(&mut->_mx_lock);
+ return ret;
+}
+
+__mtx_t* __mtx_getref(mtx_t *mut) {
+ __mtx_t * ret = __mtx_getref_def(mut, 0);
+ if (!ret) {
+ __mtx_t * new = __mtx_init(malloc(sizeof *ret), mut->_mx_type);
+ ret = __mtx_getref_def(mut, new);
+ /* somebody sneaked in between the first and second call */
+ if (ret != new) __mtx_delete(new);
+ }
+ return ret;
+}
diff --git a/src/thread/__thrd_abi.c b/src/thread/__thrd_abi.c
new file mode 100644
index 0000000..e5674e6
--- /dev/null
+++ b/src/thread/__thrd_abi.c
@@ -0,0 +1,3 @@
+#include <threads.h>
+
+unsigned const __THRD_ABI = __thrd_abi;
diff --git a/src/thread/__thrd_wait.c b/src/thread/__thrd_wait.c
new file mode 100644
index 0000000..6351cb6
--- /dev/null
+++ b/src/thread/__thrd_wait.c
@@ -0,0 +1,26 @@
+#include "pthread_impl.h"
+#include <threads.h>
+
+int __clock_gettime(clockid_t clk, struct timespec *ts);
+
+int __thrd_wait(volatile int *addr, int val, const struct timespec *at)
+{
+ int r;
+ struct timespec to, *top=0;
+
+ if (at) {
+ if (at->tv_nsec >= 1000000000UL) return EINVAL;
+ if (__clock_gettime(CLOCK_REALTIME, &to)) return EINVAL;
+ to.tv_sec = at->tv_sec - to.tv_sec;
+ if ((to.tv_nsec = at->tv_nsec - to.tv_nsec) < 0) {
+ to.tv_sec--;
+ to.tv_nsec += 1000000000;
+ }
+ if (to.tv_sec < 0) return ETIMEDOUT;
+ top = &to;
+ }
+
+ r = -__syscall_cp(SYS_futex, addr, FUTEX_WAIT|THRD_PRIVATE, val, top);
+ if (r == EINTR || r == EINVAL || r == ETIMEDOUT || r == EWOULDBLOCK) return r;
+ return 0;
+}
diff --git a/src/thread/__timedwait.c b/src/thread/__timedwait.c
index 302273a..3aed2bd 100644
--- a/src/thread/__timedwait.c
+++ b/src/thread/__timedwait.c
@@ -4,6 +4,9 @@
#include "futex.h"
#include "syscall.h"
+int __pthread_setcancelstate(int new, int *old);
+int __clock_gettime(clockid_t clk, struct timespec *ts);
+
static int do_wait(volatile int *addr, int val,
clockid_t clk, const struct timespec *at, int priv)
{
@@ -12,7 +15,7 @@ static int do_wait(volatile int *addr, int val,
if (at) {
if (at->tv_nsec >= 1000000000UL) return EINVAL;
- if (clock_gettime(clk, &to)) return EINVAL;
+ if (__clock_gettime(clk, &to)) return EINVAL;
to.tv_sec = at->tv_sec - to.tv_sec;
if ((to.tv_nsec = at->tv_nsec - to.tv_nsec) < 0) {
to.tv_sec--;
@@ -33,13 +36,13 @@ int __timedwait(volatile int *addr, int val,
{
int r, cs;
- if (!cleanup) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
+ if (!cleanup) __pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
pthread_cleanup_push(cleanup, arg);
r = do_wait(addr, val, clk, at, priv);
pthread_cleanup_pop(0);
- if (!cleanup) pthread_setcancelstate(cs, 0);
+ if (!cleanup) __pthread_setcancelstate(cs, 0);
return r;
}
diff --git a/src/thread/__wait_priv.c b/src/thread/__wait_priv.c
new file mode 100644
index 0000000..f8358cd
--- /dev/null
+++ b/src/thread/__wait_priv.c
@@ -0,0 +1,14 @@
+#include "pthread_impl.h"
+
+void __wait_priv(volatile int *addr, volatile int *waiters, int val)
+{
+ int spins=10000;
+ while (spins--) {
+ if (*addr==val) a_spin();
+ else return;
+ }
+ if (waiters) a_inc(waiters);
+ while (*addr==val)
+ __syscall(SYS_futex, addr, FUTEX_WAIT|THRD_PRIVATE, val, 0);
+ if (waiters) a_dec(waiters);
+}
diff --git a/src/thread/call_once.c b/src/thread/call_once.c
new file mode 100644
index 0000000..1d3540e
--- /dev/null
+++ b/src/thread/call_once.c
@@ -0,0 +1,40 @@
+#include "pthread_impl.h"
+#include <threads.h>
+
+#define pthread_cleanup_push_static(f, x) do { static struct __ptcb __cb; _pthread_cleanup_push(&__cb, f, x)
+
+static void undo(void *flg)
+{
+ once_flag * flag = flg;
+ a_store(&flag->__cntrl, 0);
+ __wake_priv(&flag->__cntrl, 1);
+}
+
+void call_once(once_flag *flag, void (*func)(void))
+{
+ __THRD_ABI_MARK;
+
+ /* Return immediately if init finished before */
+ if (flag->__cntrl == 2) return;
+
+ /* Try to enter initializing state. Three possibilities:
+ * 0 - we're the first or the other cancelled; run init
+ * 1 - another thread is running init; wait
+ * 2 - another thread finished running init; just return */
+
+ for (;;) switch (a_cas(&flag->__cntrl, 0, 1)) {
+ case 0:
+ _pthread_cleanup_push((void*)&flag->__cb, undo, flag);
+ func();
+ _pthread_cleanup_pop((void*)&flag->__cb, 0);
+
+ a_store(&flag->__cntrl, 2);
+ if (flag->__waiters) __wake(&flag->__cntrl, -1, 0);
+ return;
+ case 1:
+ __wait_priv(&flag->__cntrl, &flag->__waiters, 1);
+ continue;
+ case 2:
+ return;
+ }
+}
diff --git a/src/thread/cnd_broadcast.c b/src/thread/cnd_broadcast.c
new file mode 100644
index 0000000..12c7181
--- /dev/null
+++ b/src/thread/cnd_broadcast.c
@@ -0,0 +1,68 @@
+#include "pthread_impl.h"
+#include <threads.h>
+
+static int __cnd_broadcast(__cnd_t *c)
+{
+ int ret = 0;
+ __mtx_t * mtx = c->mtx;
+ /* If the mtx isn't set, this __cnd has never been used for a wait,
+ nothing to do. */
+ if (mtx) {
+ /* Perform the futex requeue, waking one waiter unless we know
+ * that the calling thread holds the mutex. */
+ int wakeup = !mtx->_mt_typ || (mtx->_mt_lck&INT_MAX)!=__pthread_self()->tid;
+ ret = __syscall(SYS_futex, &c->tok, FUTEX_REQUEUE|THRD_PRIVATE,
+ 0,
+ INT_MAX, &mtx->_mt_lck);
+ if (ret < 0) goto OUT;
+ int mv = ret;
+ ret = __syscall(SYS_futex, &c->seq, FUTEX_REQUEUE|THRD_PRIVATE,
+ 0,
+ INT_MAX, &mtx->_mt_lck);
+ if (ret < 0) goto OUT;
+ mv += ret;
+ /* Do the bookkeeping for the mutex and wake up one thread eventually. */
+ if (mv > wakeup) a_fetch_add(&mtx->_mt_wts, mv-wakeup);
+ if (mv > 0 && wakeup) ret = __syscall(SYS_futex, &mtx->_mt_lck, FUTEX_WAKE|THRD_PRIVATE, 1);
+ }
+OUT:
+ return ret < 0 ? thrd_error : thrd_success;
+}
+
+int cnd_broadcast(cnd_t * cond)
+{
+ int ret = thrd_success;
+ int wakeup = 0;
+
+ /* Avoid fight for critical section if there is nothing to
+ do. */
+ if (!cond || !cond->_cx_cnd) return ret;
+
+ /* Critical section protected by lock */
+ __lock(&cond->_cx_lock);
+ __cnd_t * c = cond->_cx_cnd;
+ /* If there are waiters, all will be waiting on c. Since we hold the
+ lock no other waiters can sneak in. Lock them permanently out
+ of using this one, here. As a consequence we don't have inc the
+ reference count: there is no change in the total number of
+ references. */
+ if (c) {
+ ++c->seq;
+ wakeup = c->wts;
+ if (wakeup) {
+ cond->_cx_cnd = 0;
+ c->wts = 0;
+ } else {
+ c = 0;
+ }
+ }
+ __unlock(&cond->_cx_lock);
+
+ /* If c is 0, there haven't been any waiters, yet, nothing to do. */
+ if (c) {
+ a_fetch_add(&c->tok, wakeup);
+ ret = __cnd_broadcast(c);
+ __cnd_unref(c);
+ }
+ return ret;
+}
diff --git a/src/thread/cnd_destroy.c b/src/thread/cnd_destroy.c
new file mode 100644
index 0000000..0a99eef
--- /dev/null
+++ b/src/thread/cnd_destroy.c
@@ -0,0 +1,13 @@
+#include "pthread_impl.h"
+#include <threads.h>
+
+void cnd_destroy(cnd_t *cond)
+{
+ /* Critical section protected by lock */
+ __lock(&cond->_cx_lock);
+ __cnd_t * ret = cond->_cx_cnd;
+ cond->_cx_cnd = 0;
+ __unlock(&cond->_cx_lock);
+
+ if (ret) __cnd_unref(ret);
+}
diff --git a/src/thread/cnd_init.c b/src/thread/cnd_init.c
new file mode 100644
index 0000000..69f9d92
--- /dev/null
+++ b/src/thread/cnd_init.c
@@ -0,0 +1,15 @@
+#include "pthread_impl.h"
+#include <threads.h>
+
+int cnd_init(cnd_t * c)
+{
+ *c = (cnd_t) {
+ 0
+ };
+ static int dum_int;
+ __cnd_t* ret = __cnd_getref(c, 0, &dum_int);
+ /* do the bookkeeping for this uncontended case */
+ __cnd_unref(ret);
+ --ret->wts;
+ return ret ? thrd_success : thrd_nomem;
+}
diff --git a/src/thread/cnd_signal.c b/src/thread/cnd_signal.c
new file mode 100644
index 0000000..a80e2e3
--- /dev/null
+++ b/src/thread/cnd_signal.c
@@ -0,0 +1,41 @@
+#include "pthread_impl.h"
+#include <threads.h>
+
+int cnd_signal(cnd_t * cond) {
+ int ret = thrd_success;
+
+ /* Avoid fight for critical section if there is nothing to
+ do. */
+ if (!cond || !cond->_cx_cnd) return ret;
+
+ /* Critical section protected by lock */
+ __lock(&cond->_cx_lock);
+ __cnd_t * c = cond->_cx_cnd;
+ if (c) {
+ if (c->wts) {
+ ++c->seq;
+ /* Move one waiter to the ones to be woken up, later. */
+ --c->wts;
+ /* If this was the last waiter, unlink. */
+ if (!c->wts) cond->_cx_cnd = 0;
+ else __cnd_addref(c);
+ } else {
+ c = 0;
+ }
+ }
+ __unlock(&cond->_cx_lock);
+
+ /* If c is 0, there haven't been any waiters, yet, nothing to do. */
+ if (c) {
+ /* Do the delayed work. */
+ a_inc(&c->tok);
+ /* Move the threads of the previous sequence point to the token queue. */
+ int ret0 = __syscall(SYS_futex, &c->seq, FUTEX_REQUEUE|THRD_PRIVATE,
+ 0,
+ INT_MAX, &c->tok);
+ int ret1 = __syscall(SYS_futex, &c->tok, FUTEX_WAKE|THRD_PRIVATE, 1);
+ ret = ((ret0 < 0) || (ret1 < 0)) ? thrd_error : thrd_success;
+ __cnd_unref(c);
+ }
+ return ret;
+}
diff --git a/src/thread/cnd_timedwait.c b/src/thread/cnd_timedwait.c
new file mode 100644
index 0000000..edc1af6
--- /dev/null
+++ b/src/thread/cnd_timedwait.c
@@ -0,0 +1,73 @@
+#include "pthread_impl.h"
+#include <threads.h>
+
+void __pthread_testcancel(void);
+int __clock_gettime(clockid_t clk, struct timespec *ts);
+int __thrd_wait(volatile int *addr, int val, const struct timespec *at);
+
+static int __cnd_timedwait(__cnd_t *restrict c, __mtx_t *restrict mut, const struct timespec *restrict ts, int seq)
+{
+ int e = EINTR, r;
+ __mtx_unlock(mut);
+
+ /* In a first phase, we ignore all wakeups if there hasn't
+ been any signal or broadcast. No legitimate wakeup will be
+ lost by that, since we can only be target of such a wakeup
+ if we have been rescheduled to the tok queue or the one of
+ the mutex. But this is only the case when a signal or
+ broadcast has occured. */
+ while (c->seq == seq) {
+ e = __thrd_wait(&c->seq, seq, ts);
+ if (e && e != EINTR && e != EWOULDBLOCK) goto RELOCK;
+ }
+
+ if (e == ETIMEDOUT) goto RELOCK;
+
+ /* From here on a signal or broadcast has happened after our sequence
+ point. */
+
+ do {
+ int token = c->tok;
+ /* a wakeup has happened since our entry to
+ cnd_timedwait, get a token if some is
+ available */
+ while (token > 0) {
+ int prev = a_cas(&c->tok, token, token - 1);
+ if (prev == token) goto RELOCK;
+ token = prev;
+ }
+
+ e = __thrd_wait(&c->tok, token, ts);
+
+ } while (!e || e == EINTR || e == EWOULDBLOCK);
+
+ if (e != ETIMEDOUT) return thrd_error;
+
+RELOCK:
+ if ((r=__mtx_lock(mut))) return r;
+
+ switch (e) {
+ case 0:
+ return thrd_success;
+ case ETIMEDOUT:
+ return thrd_timedout;
+ default:
+ return thrd_error;
+ }
+}
+
+int cnd_timedwait(cnd_t *restrict cond, mtx_t *restrict mut, const struct timespec *restrict ts)
+{
+ int ret = thrd_error;
+ int seq = 0;
+ __mtx_t * m = __mtx_getref(mut);
+ if (m) {
+ __cnd_t * c = __cnd_getref(cond, m, &seq);
+ if (c) {
+ ret = __cnd_timedwait(c, m, ts, seq);
+ __cnd_unref(c);
+ }
+ __mtx_unref(m);
+ }
+ return ret;
+}
diff --git a/src/thread/cnd_wait.c b/src/thread/cnd_wait.c
new file mode 100644
index 0000000..91e89db
--- /dev/null
+++ b/src/thread/cnd_wait.c
@@ -0,0 +1,10 @@
+#include <threads.h>
+
+int cnd_wait(cnd_t *cond, mtx_t *mutex)
+{
+ /* Calling cnd_timedwait with a null pointer is an
+ extension. Such a call is convenient, here since it avoids to
+ repeat the case analysis that is already done for
+ cnd_timedwait. */
+ return cnd_timedwait(cond, mutex, 0);
+}
diff --git a/src/thread/mtx_destroy.c b/src/thread/mtx_destroy.c
new file mode 100644
index 0000000..a1bea65
--- /dev/null
+++ b/src/thread/mtx_destroy.c
@@ -0,0 +1,11 @@
+#include "pthread_impl.h"
+#include <threads.h>
+
+void (mtx_destroy)(mtx_t *mut) {
+ /* Critical section protected by lock */
+ __lock(&mut->_mx_lock);
+ __mtx_t * ret = mut->_mx_mtx;
+ mut->_mx_mtx = 0;
+ __unlock(&mut->_mx_lock);
+ if (ret) __mtx_unref(ret);
+}
diff --git a/src/thread/mtx_init.c b/src/thread/mtx_init.c
new file mode 100644
index 0000000..40cc6c1
--- /dev/null
+++ b/src/thread/mtx_init.c
@@ -0,0 +1,12 @@
+#include "pthread_impl.h"
+#include <threads.h>
+
+int mtx_init(mtx_t * m, int type)
+{
+ *m = (mtx_t) {
+ ._mx_type = ((type&mtx_recursive) ? PTHREAD_MUTEX_RECURSIVE : 0),
+ };
+ __mtx_t* ret = __mtx_getref(m);
+ __mtx_unref(ret);
+ return ret ? thrd_success : thrd_error;
+}
diff --git a/src/thread/mtx_lock.c b/src/thread/mtx_lock.c
new file mode 100644
index 0000000..59bc1b2
--- /dev/null
+++ b/src/thread/mtx_lock.c
@@ -0,0 +1,13 @@
+#include "pthread_impl.h"
+#include <threads.h>
+
+int __mtx_lock(__mtx_t *mut)
+{
+ return __mtx_timedlock(mut, 0);
+}
+
+int mtx_lock(mtx_t *mut)
+{
+ __THRD_ABI_MARK;
+ return mtx_timedlock(mut, 0);
+}
diff --git a/src/thread/mtx_timedlock.c b/src/thread/mtx_timedlock.c
new file mode 100644
index 0000000..cf52749
--- /dev/null
+++ b/src/thread/mtx_timedlock.c
@@ -0,0 +1,46 @@
+#include "pthread_impl.h"
+#include <threads.h>
+
+int __thrd_wait(volatile int *addr, int val, const struct timespec *at);
+
+int __mtx_timedlock(__mtx_t *restrict mtx, const struct timespec *restrict at)
+{
+ int r, t;
+
+ if (mtx->_mt_typ == PTHREAD_MUTEX_NORMAL && !a_cas(&mtx->_mt_lck, 0, thrd_busy))
+ return thrd_success;
+
+ for (;;) {
+ r=__mtx_trylock(mtx);
+ if (r != thrd_busy) return r;
+ else {
+ if (!(r=mtx->_mt_lck) || (r&0x40000000)) continue;
+ a_inc(&mtx->_mt_wts);
+ t = r | 0x80000000;
+ a_cas(&mtx->_mt_lck, r, t);
+ r = __thrd_wait(&mtx->_mt_lck, t, at);
+ a_dec(&mtx->_mt_wts);
+ switch (r) {
+ case 0:
+ break;
+ case EINTR:
+ break;
+ case EWOULDBLOCK:
+ break;
+ case ETIMEDOUT:
+ return thrd_timedout;
+ default:
+ return thrd_error;
+ }
+ }
+ }
+}
+
+int mtx_timedlock(mtx_t *restrict mut, const struct timespec *restrict at)
+{
+ __mtx_t * m = __mtx_getref(mut);
+ if (!m) return thrd_error;
+ int ret = __mtx_timedlock(m, at);
+ __mtx_unref(m);
+ return ret;
+}
diff --git a/src/thread/mtx_trylock.c b/src/thread/mtx_trylock.c
new file mode 100644
index 0000000..852fa15
--- /dev/null
+++ b/src/thread/mtx_trylock.c
@@ -0,0 +1,33 @@
+#include "pthread_impl.h"
+#include <threads.h>
+
+int __mtx_trylock(__mtx_t *mtx)
+{
+ int tid, old, own, ret = 0;
+ pthread_t self;
+ if (mtx->_mt_typ == PTHREAD_MUTEX_NORMAL) {
+ return a_cas(&mtx->_mt_lck, 0, thrd_busy) & thrd_busy;
+ } else {
+ self = __pthread_self();
+ tid = self->tid;
+ old = mtx->_mt_lck;
+ own = old & 0x7fffffff;
+ if (own == tid) {
+ if ((unsigned)mtx->_mt_cnt >= INT_MAX) return thrd_error;
+ else mtx->_mt_cnt++;
+ } else {
+ if ((own && !(own & 0x40000000)) || a_cas(&mtx->_mt_lck, old, tid)!=old)
+ return thrd_busy;
+ }
+ }
+ return thrd_success;
+}
+
+int mtx_trylock(mtx_t *mut)
+{
+ __mtx_t * m = __mtx_getref(mut);
+ if (!m) return thrd_error;
+ int ret = __mtx_trylock(m);
+ __mtx_unref(m);
+ return ret;
+}
diff --git a/src/thread/mtx_unlock.c b/src/thread/mtx_unlock.c
new file mode 100644
index 0000000..5c98b07
--- /dev/null
+++ b/src/thread/mtx_unlock.c
@@ -0,0 +1,28 @@
+#include "pthread_impl.h"
+#include <threads.h>
+
+int __mtx_unlock(__mtx_t *mtx)
+{
+ int ret = thrd_success;
+ if ((mtx->_mt_typ&3) == PTHREAD_MUTEX_RECURSIVE && mtx->_mt_cnt) {
+ if ((mtx->_mt_lck&0x1fffffff) != __pthread_self()->tid)
+ ret = thrd_error;
+ /* _m_count is the count of additional locks, no need to real unlock */
+ else --mtx->_mt_cnt;
+ } else {
+ if (a_swap(&mtx->_mt_lck, 0)<0 || mtx->_mt_wts)
+ __syscall(SYS_futex, &mtx->_mt_lck, FUTEX_WAKE|THRD_PRIVATE, 1);
+ }
+ return ret;
+}
+
+int mtx_unlock(mtx_t *mut)
+{
+ int ret = thrd_error;
+ __mtx_t * m = __mtx_getref(mut);
+ if (m) {
+ ret = __mtx_unlock(m);
+ __mtx_unref(m);
+ }
+ return ret;
+}
diff --git a/src/thread/pthread_cond_timedwait.c b/src/thread/pthread_cond_timedwait.c
index 99d62cc..f43ca9e 100644
--- a/src/thread/pthread_cond_timedwait.c
+++ b/src/thread/pthread_cond_timedwait.c
@@ -1,5 +1,7 @@
#include "pthread_impl.h"
+void __pthread_testcancel(void);
+
struct cm {
pthread_cond_t *c;
pthread_mutex_t *m;
@@ -47,7 +49,7 @@ int pthread_cond_timedwait(pthread_cond_t *restrict c, pthread_mutex_t *restrict
if (ts && ts->tv_nsec >= 1000000000UL)
return EINVAL;
- pthread_testcancel();
+ __pthread_testcancel();
a_inc(&c->_c_waiters);
diff --git a/src/thread/pthread_create.c b/src/thread/pthread_create.c
index e77e54a..d19d78b 100644
--- a/src/thread/pthread_create.c
+++ b/src/thread/pthread_create.c
@@ -3,97 +3,21 @@
#include "stdio_impl.h"
#include "libc.h"
#include <sys/mman.h>
+#include <threads.h>
+
+void *__mmap(void *, size_t, int, int, int, off_t);
+int __munmap(void *, size_t);
+int __mprotect(void *, size_t, int);
+void __thread_enable(void);
+_Noreturn void __pthread_exit(void *);
+void *__copy_tls(unsigned char *);
+extern volatile size_t __pthread_tsd_size;
static void dummy_0()
{
}
weak_alias(dummy_0, __acquire_ptc);
weak_alias(dummy_0, __release_ptc);
-weak_alias(dummy_0, __pthread_tsd_run_dtors);
-
-_Noreturn void pthread_exit(void *result)
-{
- pthread_t self = __pthread_self();
- sigset_t set;
-
- self->result = result;
-
- while (self->cancelbuf) {
- void (*f)(void *) = self->cancelbuf->__f;
- void *x = self->cancelbuf->__x;
- self->cancelbuf = self->cancelbuf->__next;
- f(x);
- }
-
- __pthread_tsd_run_dtors();
-
- __lock(self->exitlock);
-
- /* Mark this thread dead before decrementing count */
- __lock(self->killlock);
- self->dead = 1;
-
- /* Block all signals before decrementing the live thread count.
- * This is important to ensure that dynamically allocated TLS
- * is not under-allocated/over-committed, and possibly for other
- * reasons as well. */
- __block_all_sigs(&set);
-
- /* Wait to unlock the kill lock, which governs functions like
- * pthread_kill which target a thread id, until signals have
- * been blocked. This precludes observation of the thread id
- * as a live thread (with application code running in it) after
- * the thread was reported dead by ESRCH being returned. */
- __unlock(self->killlock);
-
- /* It's impossible to determine whether this is "the last thread"
- * until performing the atomic decrement, since multiple threads
- * could exit at the same time. For the last thread, revert the
- * decrement and unblock signals to give the atexit handlers and
- * stdio cleanup code a consistent state. */
- if (a_fetch_add(&libc.threads_minus_1, -1)==0) {
- libc.threads_minus_1 = 0;
- __restore_sigs(&set);
- exit(0);
- }
-
- if (self->locale != &libc.global_locale) {
- a_dec(&libc.uselocale_cnt);
- if (self->locale->ctype_utf8)
- a_dec(&libc.bytelocale_cnt_minus_1);
- }
-
- if (self->detached && self->map_base) {
- /* Detached threads must avoid the kernel clear_child_tid
- * feature, since the virtual address will have been
- * unmapped and possibly already reused by a new mapping
- * at the time the kernel would perform the write. In
- * the case of threads that started out detached, the
- * initial clone flags are correct, but if the thread was
- * detached later (== 2), we need to clear it here. */
- if (self->detached == 2) __syscall(SYS_set_tid_address, 0);
-
- /* The following call unmaps the thread's stack mapping
- * and then exits without touching the stack. */
- __unmapself(self->map_base, self->map_size);
- }
-
- for (;;) __syscall(SYS_exit, 0);
-}
-
-void __do_cleanup_push(struct __ptcb *cb)
-{
- if (!libc.has_thread_pointer) return;
- struct pthread *self = __pthread_self();
- cb->__next = self->cancelbuf;
- self->cancelbuf = cb;
-}
-
-void __do_cleanup_pop(struct __ptcb *cb)
-{
- if (!libc.has_thread_pointer) return;
- __pthread_self()->cancelbuf = cb->__next;
-}
static int start(void *p)
{
@@ -115,28 +39,10 @@ static int start(void *p)
#define ROUND(x) (((x)+PAGE_SIZE-1)&-PAGE_SIZE)
-/* pthread_key_create.c overrides this */
-static volatile size_t dummy = 0;
-weak_alias(dummy, __pthread_tsd_size);
-static void *dummy_tsd[1] = { 0 };
-weak_alias(dummy_tsd, __pthread_tsd_main);
-
-static FILE *volatile dummy_file = 0;
-weak_alias(dummy_file, __stdin_used);
-weak_alias(dummy_file, __stdout_used);
-weak_alias(dummy_file, __stderr_used);
-
-static void init_file_lock(FILE *f)
-{
- if (f && f->lock<0) f->lock = 0;
-}
-
-void *__copy_tls(unsigned char *);
-
int pthread_create(pthread_t *restrict res, const pthread_attr_t *restrict attrp, void *(*entry)(void *), void *restrict arg)
{
int ret;
- size_t size, guard;
+ size_t size, guard = 0;
struct pthread *self, *new;
unsigned char *map = 0, *stack = 0, *tsd = 0, *stack_limit;
unsigned flags = CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND
@@ -147,16 +53,7 @@ int pthread_create(pthread_t *restrict res, const pthread_attr_t *restrict attrp
if (!libc.can_do_threads) return ENOSYS;
self = __pthread_self();
- if (!libc.threaded) {
- for (FILE *f=libc.ofl_head; f; f=f->next)
- init_file_lock(f);
- init_file_lock(__stdin_used);
- init_file_lock(__stdout_used);
- init_file_lock(__stderr_used);
- __syscall(SYS_rt_sigprocmask, SIG_UNBLOCK, SIGPT_SET, 0, _NSIG/8);
- self->tsd = (void **)__pthread_tsd_main;
- libc.threaded = 1;
- }
+ if (!libc.threaded) __thread_enable();
if (attrp) attr = *attrp;
__acquire_ptc();
@@ -184,14 +81,14 @@ int pthread_create(pthread_t *restrict res, const pthread_attr_t *restrict attrp
if (!tsd) {
if (guard) {
- map = mmap(0, size, PROT_NONE, MAP_PRIVATE|MAP_ANON, -1, 0);
+ map = __mmap(0, size, PROT_NONE, MAP_PRIVATE|MAP_ANON, -1, 0);
if (map == MAP_FAILED) goto fail;
- if (mprotect(map+guard, size-guard, PROT_READ|PROT_WRITE)) {
- munmap(map, size);
+ if (__mprotect(map+guard, size-guard, PROT_READ|PROT_WRITE)) {
+ __munmap(map, size);
goto fail;
}
} else {
- map = mmap(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
+ map = __mmap(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
if (map == MAP_FAILED) goto fail;
}
tsd = map + size - __pthread_tsd_size;
@@ -233,7 +130,7 @@ int pthread_create(pthread_t *restrict res, const pthread_attr_t *restrict attrp
if (ret < 0) {
a_dec(&libc.threads_minus_1);
- if (map) munmap(map, size);
+ if (map) __munmap(map, size);
return EAGAIN;
}
diff --git a/src/thread/pthread_exit.c b/src/thread/pthread_exit.c
new file mode 100644
index 0000000..c6f4e60
--- /dev/null
+++ b/src/thread/pthread_exit.c
@@ -0,0 +1,128 @@
+#define _GNU_SOURCE
+#include "pthread_impl.h"
+#include "stdio_impl.h"
+#include "libc.h"
+#include <sys/mman.h>
+#include <threads.h>
+
+static void dummy_0()
+{
+}
+weak_alias(dummy_0, __pthread_tsd_run_dtors);
+weak_alias(dummy_0, __tss_run_dtors);
+
+_Noreturn void __pthread_exit(void *result)
+{
+ pthread_t self = __pthread_self();
+ sigset_t set;
+
+ self->result = result;
+
+ while (self->cancelbuf) {
+ void (*f)(void *) = self->cancelbuf->__f;
+ void *x = self->cancelbuf->__x;
+ self->cancelbuf = self->cancelbuf->__next;
+ f(x);
+ }
+
+ __pthread_tsd_run_dtors();
+
+ __tss_run_dtors();
+
+ __lock(self->exitlock);
+
+ /* Mark this thread dead before decrementing count */
+ __lock(self->killlock);
+ self->dead = 1;
+
+ /* Block all signals before decrementing the live thread count.
+ * This is important to ensure that dynamically allocated TLS
+ * is not under-allocated/over-committed, and possibly for other
+ * reasons as well. */
+ __block_all_sigs(&set);
+
+ /* Wait to unlock the kill lock, which governs functions like
+ * pthread_kill which target a thread id, until signals have
+ * been blocked. This precludes observation of the thread id
+ * as a live thread (with application code running in it) after
+ * the thread was reported dead by ESRCH being returned. */
+ __unlock(self->killlock);
+
+ /* It's impossible to determine whether this is "the last thread"
+ * until performing the atomic decrement, since multiple threads
+ * could exit at the same time. For the last thread, revert the
+ * decrement and unblock signals to give the atexit handlers and
+ * stdio cleanup code a consistent state. */
+ if (a_fetch_add(&libc.threads_minus_1, -1)==0) {
+ libc.threads_minus_1 = 0;
+ __restore_sigs(&set);
+ exit(0);
+ }
+
+ if (self->locale != &libc.global_locale) {
+ a_dec(&libc.uselocale_cnt);
+ if (self->locale->ctype_utf8)
+ a_dec(&libc.bytelocale_cnt_minus_1);
+ }
+
+ if (self->detached && self->map_base) {
+ /* Detached threads must avoid the kernel clear_child_tid
+ * feature, since the virtual address will have been
+ * unmapped and possibly already reused by a new mapping
+ * at the time the kernel would perform the write. In
+ * the case of threads that started out detached, the
+ * initial clone flags are correct, but if the thread was
+ * detached later (== 2), we need to clear it here. */
+ if (self->detached == 2) __syscall(SYS_set_tid_address, 0);
+
+ /* The following call unmaps the thread's stack mapping
+ * and then exits without touching the stack. */
+ __unmapself(self->map_base, self->map_size);
+ }
+
+ for (;;) __syscall(SYS_exit, 0);
+}
+weak_alias(__pthread_exit, pthread_exit);
+
+void __do_cleanup_push(struct __ptcb *cb)
+{
+ if (!libc.has_thread_pointer) return;
+ struct pthread *self = __pthread_self();
+ cb->__next = self->cancelbuf;
+ self->cancelbuf = cb;
+}
+
+void __do_cleanup_pop(struct __ptcb *cb)
+{
+ if (!libc.has_thread_pointer) return;
+ __pthread_self()->cancelbuf = cb->__next;
+}
+
+/* pthread_key_create.c overrides this */
+static volatile size_t dummy = 0;
+weak_alias(dummy, __pthread_tsd_size);
+static void *dummy_tsd[1] = { 0 };
+weak_alias(dummy_tsd, __pthread_tsd_main);
+
+static FILE *volatile dummy_file = 0;
+weak_alias(dummy_file, __stdin_used);
+weak_alias(dummy_file, __stdout_used);
+weak_alias(dummy_file, __stderr_used);
+
+static void init_file_lock(FILE *f)
+{
+ if (f && f->lock<0) f->lock = 0;
+}
+
+void __thread_enable(void)
+{
+ for (FILE *f=libc.ofl_head; f; f=f->next)
+ init_file_lock(f);
+ init_file_lock(__stdin_used);
+ init_file_lock(__stdout_used);
+ init_file_lock(__stderr_used);
+ __syscall(SYS_rt_sigprocmask, SIG_UNBLOCK, SIGPT_SET, 0, _NSIG/8);
+ __pthread_self()->tsd = (void **)__pthread_tsd_main;
+ libc.threaded = 1;
+}
+
diff --git a/src/thread/pthread_setcancelstate.c b/src/thread/pthread_setcancelstate.c
index 060bcdc..2268217 100644
--- a/src/thread/pthread_setcancelstate.c
+++ b/src/thread/pthread_setcancelstate.c
@@ -1,6 +1,6 @@
#include "pthread_impl.h"
-int pthread_setcancelstate(int new, int *old)
+int __pthread_setcancelstate(int new, int *old)
{
if (new > 1U) return EINVAL;
if (!libc.has_thread_pointer) return ENOSYS;
@@ -9,3 +9,5 @@ int pthread_setcancelstate(int new, int *old)
self->canceldisable = new;
return 0;
}
+
+weak_alias(__pthread_setcancelstate, pthread_setcancelstate);
diff --git a/src/thread/pthread_setcanceltype.c b/src/thread/pthread_setcanceltype.c
index bf0a3f3..d493c1b 100644
--- a/src/thread/pthread_setcanceltype.c
+++ b/src/thread/pthread_setcanceltype.c
@@ -1,11 +1,13 @@
#include "pthread_impl.h"
+void __pthread_testcancel(void);
+
int pthread_setcanceltype(int new, int *old)
{
struct pthread *self = __pthread_self();
if (new > 1U) return EINVAL;
if (old) *old = self->cancelasync;
self->cancelasync = new;
- if (new) pthread_testcancel();
+ if (new) __pthread_testcancel();
return 0;
}
diff --git a/src/thread/pthread_testcancel.c b/src/thread/pthread_testcancel.c
index ba5f7c6..ee48e6d 100644
--- a/src/thread/pthread_testcancel.c
+++ b/src/thread/pthread_testcancel.c
@@ -7,7 +7,9 @@ static void dummy()
weak_alias(dummy, __testcancel);
-void pthread_testcancel()
+void __pthread_testcancel()
{
__testcancel();
}
+
+weak_alias(__pthread_testcancel, pthread_testcancel);
diff --git a/src/thread/thrd_create.c b/src/thread/thrd_create.c
new file mode 100644
index 0000000..f72f992
--- /dev/null
+++ b/src/thread/thrd_create.c
@@ -0,0 +1,98 @@
+#define _GNU_SOURCE
+#include "pthread_impl.h"
+#include "stdio_impl.h"
+#include "libc.h"
+#include <sys/mman.h>
+#include <threads.h>
+
+void *__mmap(void *, size_t, int, int, int, off_t);
+int __munmap(void *, size_t);
+int __mprotect(void *, size_t, int);
+void __thread_enable(void);
+_Noreturn void __pthread_exit(void *);
+void *__copy_tls(unsigned char *);
+extern volatile size_t __pthread_tsd_size;
+
+_Noreturn void thrd_exit(int result) {
+ __pthread_exit((void*)(intptr_t)result);
+}
+
+static void dummy_0()
+{
+}
+weak_alias(dummy_0, __acquire_ptc);
+weak_alias(dummy_0, __release_ptc);
+
+static int start(void *p)
+{
+ thrd_t self = p;
+ int (*start)(void*) = (int(*)(void*)) self->start;
+ thrd_exit(start(self->start_arg));
+ return 0;
+}
+
+#define ROUND(x) (((x)+PAGE_SIZE-1)&-PAGE_SIZE)
+
+int thrd_create(thrd_t *res, thrd_start_t entry, void *arg)
+{
+ int ret = -ENOMEM;
+ size_t guard = ROUND(DEFAULT_GUARD_SIZE);
+ size_t size = guard + ROUND(DEFAULT_STACK_SIZE + libc.tls_size + __pthread_tsd_size);
+ struct pthread *self, *new;
+ unsigned char *map, *stack, *tsd, *stack_limit;
+ unsigned flags = CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND
+ | CLONE_THREAD | CLONE_SYSVSEM | CLONE_SETTLS
+ | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID | CLONE_DETACHED;
+
+ if (!libc.can_do_threads) return thrd_error;
+ self = __pthread_self();
+ if (!libc.threaded) __thread_enable();
+
+ __acquire_ptc();
+
+ if (guard) {
+ map = __mmap(0, size, PROT_NONE, MAP_PRIVATE|MAP_ANON, -1, 0);
+ if (map == MAP_FAILED) goto CLEANUP;
+ if (__mprotect(map+guard, size-guard, PROT_READ|PROT_WRITE)) {
+ __munmap(map, size);
+ goto CLEANUP;
+ }
+ } else {
+ map = __mmap(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
+ if (map == MAP_FAILED) goto CLEANUP;
+ }
+ tsd = map + size - __pthread_tsd_size;
+ stack = tsd - libc.tls_size;
+ stack_limit = map + guard;
+
+ new = __copy_tls(tsd - libc.tls_size);
+ new->map_base = map;
+ new->map_size = size;
+ new->stack = stack;
+ new->stack_size = stack - stack_limit;
+ new->start = (void *(*)(void*))entry;
+ new->start_arg = arg;
+ new->self = new;
+ new->tsd = (void *)tsd;
+ new->locale = &libc.global_locale;
+ new->unblock_cancel = self->cancel;
+ new->canary = self->canary;
+
+ a_inc(&libc.threads_minus_1);
+ ret = __clone(start, stack, flags, new, &new->tid, TP_ADJ(new), &new->tid);
+
+ CLEANUP:
+ __release_ptc();
+
+ if (ret > 0) {
+ *res = new;
+ ret = thrd_success;
+ } else if (ret == -ENOMEM) {
+ ret = thrd_nomem;
+ } else {
+ a_dec(&libc.threads_minus_1);
+ if (map) __munmap(map, size);
+ ret = thrd_error;
+ }
+ return ret;
+}
diff --git a/src/thread/thrd_current.c b/src/thread/thrd_current.c
new file mode 100644
index 0000000..1728535
--- /dev/null
+++ b/src/thread/thrd_current.c
@@ -0,0 +1,11 @@
+#include "pthread_impl.h"
+#include <threads.h>
+
+/* Not all arch have __pthread_self as a symbol. On some this results
+ in some magic. So this "call" to __pthread_self is not necessary a
+ tail call. In particular, other than the appearance, thrd_current
+ can not be implemented as a weak symbol. */
+pthread_t thrd_current()
+{
+ return __pthread_self();
+}
diff --git a/src/thread/thrd_detach.c b/src/thread/thrd_detach.c
new file mode 100644
index 0000000..dd29308
--- /dev/null
+++ b/src/thread/thrd_detach.c
@@ -0,0 +1,17 @@
+#include "pthread_impl.h"
+#include <threads.h>
+#include <sys/mman.h>
+
+int thrd_detach(thrd_t t)
+{
+ /* Cannot detach a thread that's already exiting */
+ if (a_swap(t->exitlock, 1)){
+ int tmp;
+ while ((tmp = t->tid)) __timedwait(&t->tid, tmp, 0, 0, 0, 0, 0);
+ if (t->map_base) munmap(t->map_base, t->map_size);
+ } else {
+ t->detached = 2;
+ __unlock(t->exitlock);
+ }
+ return thrd_success;
+}
diff --git a/src/thread/thrd_equal.c b/src/thread/thrd_equal.c
new file mode 100644
index 0000000..ac49a44
--- /dev/null
+++ b/src/thread/thrd_equal.c
@@ -0,0 +1,6 @@
+#include <threads.h>
+
+int (thrd_equal)(thrd_t a, thrd_t b)
+{
+ return a==b;
+}
diff --git a/src/thread/thrd_join.c b/src/thread/thrd_join.c
new file mode 100644
index 0000000..0446975
--- /dev/null
+++ b/src/thread/thrd_join.c
@@ -0,0 +1,14 @@
+#include "pthread_impl.h"
+#include <sys/mman.h>
+#include <threads.h>
+
+/* C11 threads cannot be canceled, so there is no need for a
+ cancelation function pointer, here. */
+int thrd_join(thrd_t t, int *res)
+{
+ int tmp;
+ while ((tmp = t->tid)) __timedwait(&t->tid, tmp, 0, 0, 0, 0, 0);
+ if (res) *res = (int)(intptr_t)t->result;
+ if (t->map_base) munmap(t->map_base, t->map_size);
+ return thrd_success;
+}
diff --git a/src/thread/tss_create.c b/src/thread/tss_create.c
new file mode 100644
index 0000000..2338d59
--- /dev/null
+++ b/src/thread/tss_create.c
@@ -0,0 +1,97 @@
+#include "pthread_impl.h"
+#include <threads.h>
+
+/* future version of C might prescribe a minimum number of tss */
+#define __TSS_MAX_MIN 64
+
+#ifndef TSS_MAX
+# define TSS_MAX __TSS_MAX_MIN
+#endif
+
+#define __LOW_BIT(X) ((X+0U) & -(X+0U))
+
+#define STRINGIFY_(X) #X
+#define STRINGIFY(X) STRINGIFY_(X)
+#define WARN__(X) _Pragma(#X)
+#define WARN_(X) WARN__(message (X))
+#define WARN(X) WARN_(X)
+
+#if (TSS_MAX) < __TSS_MAX_MIN
+WARN("TSS_MAX " STRINGIFY(TSS_MAX) " replaced by minimal value " STRINGIFY(__TSS_MAX_MIN))
+# undef TSS_MAX
+# define TSS_MAX __TSS_MAX_MIN
+#endif
+
+#if (TSS_MAX) != __LOW_BIT(TSS_MAX)
+# error TSS_MAX must be a power of 2
+#endif
+
+enum { tss_max = (TSS_MAX), };
+
+static tss_dtor_t dtors[tss_max];
+
+static void nodtor(void *dummy)
+{
+}
+
+/* This should use _Thread_local which must exist for C11 threads.
+ Since musl might get compiled with pre-C11 options we use gcc's
+ __thread extension, instead. */
+static __thread void * data[tss_max];
+/* Thread local data is accounted for with a usage counter */
+static __thread tss_t used;
+
+int tss_create(tss_t *tss, tss_dtor_t dtor)
+{
+ unsigned start = (uintptr_t)&tss / 16 % tss_max;
+ if (!dtor) dtor = nodtor;
+
+ for (unsigned i = 0; i < tss_max; ++i) {
+ unsigned j = (start+i)%tss_max;
+ if (!a_cas_p(dtors+j, 0, (void *)dtor)) {
+ *tss = j;
+ return thrd_success;
+ }
+ }
+ *tss = -1;
+ return thrd_error;
+}
+
+void tss_delete(tss_t k)
+{
+ /* tss_delete has no failure path, so check the argument. */
+ if (k < tss_max) dtors[k] = 0;
+}
+
+int tss_set(tss_t k, void *x)
+{
+ if (k >= tss_max || !dtors[k]) return thrd_error;
+ if (data[k] != x) {
+ /* tss_set can be used to add or remove a tss. compute
+ the difference in usage. */
+ used += (!!x - !!data[k]);
+ data[k] = x;
+ }
+ return thrd_success;
+}
+
+void * tss_get(tss_t k) {
+ return dtors[k] ? data[k] : 0;
+}
+
+
+void __tss_run_dtors(void)
+{
+ for (unsigned j=0; used && j<TSS_DTOR_ITERATIONS; j++) {
+ for (unsigned i=0; i<tss_max; i++) {
+ if (data[i]) {
+ void *tmp = data[i];
+ data[i] = 0;
+ --used;
+ // slot i might have been deleted, check
+ if (dtors[i]) dtors[i](tmp);
+ if (!used) return;
+ }
+ }
+ }
+}
diff --git a/src/time/thrd_sleep.c b/src/time/thrd_sleep.c
new file mode 100644
index 0000000..6570b0c
--- /dev/null
+++ b/src/time/thrd_sleep.c
@@ -0,0 +1,31 @@
+#include <time.h>
+#include <errno.h>
+#include "syscall.h"
+#include "libc.h"
+#include "threads.h"
+
+/* Roughly __syscall already returns the right thing: 0 if all went
+ well or a negative error indication, otherwise. */
+int thrd_sleep(const struct timespec *req, struct timespec *rem)
+{
+ int ret = __syscall(SYS_nanosleep, req, rem);
+ // compile time comparison, constant propagated
+ if (EINTR != 1) {
+ switch (ret) {
+ case 0: return 0;
+ /* error described by POSIX: */
+ /* EINTR The nanosleep() function was interrupted by a signal. */
+ /* The C11 wording is: */
+ /* -1 if it has been interrupted by a signal */
+ case -EINTR: return -1;
+ /* error described by POSIX */
+ case -EINVAL: return -2;
+ /* described for linux */
+ case -EFAULT: return -3;
+ /* No other error returns are specified. */
+ default: return INT_MIN;
+ }
+ }
+ // potentially a tail call
+ return ret;
+}
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 198 bytes --]
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2014-08-14 20:55 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-08-14 20:55 c threads, v.5.9 Jens Gustedt
Code repositories for project(s) associated with this public inbox
https://git.vuxu.org/mirror/musl/
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).