diff --git a/arch/arm/bits/alltypes.h.in b/arch/arm/bits/alltypes.h.in index 183c4c4..333d43f 100644 --- a/arch/arm/bits/alltypes.h.in +++ b/arch/arm/bits/alltypes.h.in @@ -19,7 +19,7 @@ TYPEDEF long time_t; TYPEDEF long suseconds_t; TYPEDEF struct { union { int __i[9]; unsigned __s[9]; } __u; } pthread_attr_t; -TYPEDEF struct { union { int __i[6]; volatile void *volatile __p[6]; } __u; } pthread_mutex_t; -TYPEDEF struct { union { int __i[12]; void *__p[12]; } __u; } pthread_cond_t; +TYPEDEF struct { union { int __i[6]; volatile void *volatile __p[6]; } __u; } __pthread_mutex_t; +TYPEDEF struct { union { int __i[12]; void *__p[12]; } __u; } __pthread_cond_t; TYPEDEF struct { union { int __i[8]; void *__p[8]; } __u; } pthread_rwlock_t; TYPEDEF struct { union { int __i[5]; void *__p[5]; } __u; } pthread_barrier_t; diff --git a/arch/i386/bits/alltypes.h.in b/arch/i386/bits/alltypes.h.in index 8ba8f6f..67cce3b 100644 --- a/arch/i386/bits/alltypes.h.in +++ b/arch/i386/bits/alltypes.h.in @@ -33,7 +33,7 @@ TYPEDEF long time_t; TYPEDEF long suseconds_t; TYPEDEF struct { union { int __i[9]; unsigned __s[9]; } __u; } pthread_attr_t; -TYPEDEF struct { union { int __i[6]; volatile void *volatile __p[6]; } __u; } pthread_mutex_t; -TYPEDEF struct { union { int __i[12]; void *__p[12]; } __u; } pthread_cond_t; +TYPEDEF struct { union { int __i[6]; volatile void *volatile __p[6]; } __u; } __pthread_mutex_t; +TYPEDEF struct { union { int __i[12]; void *__p[12]; } __u; } __pthread_cond_t; TYPEDEF struct { union { int __i[8]; void *__p[8]; } __u; } pthread_rwlock_t; TYPEDEF struct { union { int __i[5]; void *__p[5]; } __u; } pthread_barrier_t; diff --git a/arch/microblaze/bits/alltypes.h.in b/arch/microblaze/bits/alltypes.h.in index a03e1b8..b27a8fe 100644 --- a/arch/microblaze/bits/alltypes.h.in +++ b/arch/microblaze/bits/alltypes.h.in @@ -19,7 +19,7 @@ TYPEDEF long time_t; TYPEDEF long suseconds_t; TYPEDEF struct { union { int __i[9]; unsigned __s[9]; } __u; } pthread_attr_t; -TYPEDEF struct { union { int __i[6]; volatile void *volatile __p[6]; } __u; } pthread_mutex_t; -TYPEDEF struct { union { int __i[12]; void *__p[12]; } __u; } pthread_cond_t; +TYPEDEF struct { union { int __i[6]; volatile void *volatile __p[6]; } __u; } __pthread_mutex_t; +TYPEDEF struct { union { int __i[12]; void *__p[12]; } __u; } __pthread_cond_t; TYPEDEF struct { union { int __i[8]; void *__p[8]; } __u; } pthread_rwlock_t; TYPEDEF struct { union { int __i[5]; void *__p[5]; } __u; } pthread_barrier_t; diff --git a/arch/mips/bits/alltypes.h.in b/arch/mips/bits/alltypes.h.in index a03e1b8..b27a8fe 100644 --- a/arch/mips/bits/alltypes.h.in +++ b/arch/mips/bits/alltypes.h.in @@ -19,7 +19,7 @@ TYPEDEF long time_t; TYPEDEF long suseconds_t; TYPEDEF struct { union { int __i[9]; unsigned __s[9]; } __u; } pthread_attr_t; -TYPEDEF struct { union { int __i[6]; volatile void *volatile __p[6]; } __u; } pthread_mutex_t; -TYPEDEF struct { union { int __i[12]; void *__p[12]; } __u; } pthread_cond_t; +TYPEDEF struct { union { int __i[6]; volatile void *volatile __p[6]; } __u; } __pthread_mutex_t; +TYPEDEF struct { union { int __i[12]; void *__p[12]; } __u; } __pthread_cond_t; TYPEDEF struct { union { int __i[8]; void *__p[8]; } __u; } pthread_rwlock_t; TYPEDEF struct { union { int __i[5]; void *__p[5]; } __u; } pthread_barrier_t; diff --git a/arch/or1k/bits/alltypes.h.in b/arch/or1k/bits/alltypes.h.in index 183c4c4..333d43f 100644 --- a/arch/or1k/bits/alltypes.h.in +++ b/arch/or1k/bits/alltypes.h.in @@ -19,7 +19,7 @@ TYPEDEF long time_t; TYPEDEF long suseconds_t; TYPEDEF struct { union { int __i[9]; unsigned __s[9]; } __u; } pthread_attr_t; -TYPEDEF struct { union { int __i[6]; volatile void *volatile __p[6]; } __u; } pthread_mutex_t; -TYPEDEF struct { union { int __i[12]; void *__p[12]; } __u; } pthread_cond_t; +TYPEDEF struct { union { int __i[6]; volatile void *volatile __p[6]; } __u; } __pthread_mutex_t; +TYPEDEF struct { union { int __i[12]; void *__p[12]; } __u; } __pthread_cond_t; TYPEDEF struct { union { int __i[8]; void *__p[8]; } __u; } pthread_rwlock_t; TYPEDEF struct { union { int __i[5]; void *__p[5]; } __u; } pthread_barrier_t; diff --git a/arch/powerpc/bits/alltypes.h.in b/arch/powerpc/bits/alltypes.h.in index ee7f137..57d7c07 100644 --- a/arch/powerpc/bits/alltypes.h.in +++ b/arch/powerpc/bits/alltypes.h.in @@ -19,7 +19,7 @@ TYPEDEF long time_t; TYPEDEF long suseconds_t; TYPEDEF struct { union { int __i[9]; unsigned __s[9]; } __u; } pthread_attr_t; -TYPEDEF struct { union { int __i[6]; volatile void *volatile __p[6]; } __u; } pthread_mutex_t; -TYPEDEF struct { union { int __i[12]; void *__p[12]; } __u; } pthread_cond_t; +TYPEDEF struct { union { int __i[6]; volatile void *volatile __p[6]; } __u; } __pthread_mutex_t; +TYPEDEF struct { union { int __i[12]; void *__p[12]; } __u; } __pthread_cond_t; TYPEDEF struct { union { int __i[8]; void *__p[8]; } __u; } pthread_rwlock_t; TYPEDEF struct { union { int __i[5]; void *__p[5]; } __u; } pthread_barrier_t; diff --git a/arch/sh/bits/alltypes.h.in b/arch/sh/bits/alltypes.h.in index ee7f137..57d7c07 100644 --- a/arch/sh/bits/alltypes.h.in +++ b/arch/sh/bits/alltypes.h.in @@ -19,7 +19,7 @@ TYPEDEF long time_t; TYPEDEF long suseconds_t; TYPEDEF struct { union { int __i[9]; unsigned __s[9]; } __u; } pthread_attr_t; -TYPEDEF struct { union { int __i[6]; volatile void *volatile __p[6]; } __u; } pthread_mutex_t; -TYPEDEF struct { union { int __i[12]; void *__p[12]; } __u; } pthread_cond_t; +TYPEDEF struct { union { int __i[6]; volatile void *volatile __p[6]; } __u; } __pthread_mutex_t; +TYPEDEF struct { union { int __i[12]; void *__p[12]; } __u; } __pthread_cond_t; TYPEDEF struct { union { int __i[8]; void *__p[8]; } __u; } pthread_rwlock_t; TYPEDEF struct { union { int __i[5]; void *__p[5]; } __u; } pthread_barrier_t; diff --git a/arch/x32/bits/alltypes.h.in b/arch/x32/bits/alltypes.h.in index 8e396c9..02d9e1c 100644 --- a/arch/x32/bits/alltypes.h.in +++ b/arch/x32/bits/alltypes.h.in @@ -24,7 +24,7 @@ TYPEDEF long long time_t; TYPEDEF long long suseconds_t; TYPEDEF struct { union { int __i[14]; unsigned long __s[7]; } __u; } pthread_attr_t; -TYPEDEF struct { union { int __i[10]; volatile void *volatile __p[5]; } __u; } pthread_mutex_t; -TYPEDEF struct { union { int __i[12]; void *__p[6]; } __u; } pthread_cond_t; +TYPEDEF struct { union { int __i[10]; volatile void *volatile __p[5]; } __u; } __pthread_mutex_t; +TYPEDEF struct { union { int __i[12]; void *__p[6]; } __u; } __pthread_cond_t; TYPEDEF struct { union { int __i[14]; void *__p[7]; } __u; } pthread_rwlock_t; TYPEDEF struct { union { int __i[8]; void *__p[4]; } __u; } pthread_barrier_t; diff --git a/arch/x86_64/bits/alltypes.h.in b/arch/x86_64/bits/alltypes.h.in index 7b4f3e7..ea9fc67 100644 --- a/arch/x86_64/bits/alltypes.h.in +++ b/arch/x86_64/bits/alltypes.h.in @@ -24,7 +24,7 @@ TYPEDEF long time_t; TYPEDEF long suseconds_t; TYPEDEF struct { union { int __i[14]; unsigned long __s[7]; } __u; } pthread_attr_t; -TYPEDEF struct { union { int __i[10]; volatile void *volatile __p[5]; } __u; } pthread_mutex_t; -TYPEDEF struct { union { int __i[12]; void *__p[6]; } __u; } pthread_cond_t; +TYPEDEF struct { union { int __i[10]; volatile void *volatile __p[5]; } __u; } __pthread_mutex_t; +TYPEDEF struct { union { int __i[12]; void *__p[6]; } __u; } __pthread_cond_t; TYPEDEF struct { union { int __i[14]; void *__p[7]; } __u; } pthread_rwlock_t; TYPEDEF struct { union { int __i[8]; void *__p[4]; } __u; } pthread_barrier_t; diff --git a/include/alltypes.h.in b/include/alltypes.h.in index c4ca5d5..5e29046 100644 --- a/include/alltypes.h.in +++ b/include/alltypes.h.in @@ -43,13 +43,24 @@ TYPEDEF unsigned gid_t; TYPEDEF int key_t; TYPEDEF unsigned useconds_t; +/* The pairs of equivalent definitions for pthread and C thread types + * should always be kept in sync. */ #ifdef __cplusplus TYPEDEF unsigned long pthread_t; +TYPEDEF unsigned long thrd_t; #else TYPEDEF struct __pthread * pthread_t; +TYPEDEF struct __pthread * thrd_t; #endif TYPEDEF int pthread_once_t; +TYPEDEF int once_flag; TYPEDEF unsigned pthread_key_t; +TYPEDEF unsigned tss_t; +TYPEDEF __pthread_cond_t pthread_cond_t; +TYPEDEF __pthread_cond_t cnd_t; +TYPEDEF __pthread_mutex_t pthread_mutex_t; +TYPEDEF __pthread_mutex_t mtx_t; + TYPEDEF int pthread_spinlock_t; TYPEDEF struct { unsigned __attr; } pthread_mutexattr_t; TYPEDEF struct { unsigned __attr; } pthread_condattr_t; diff --git a/include/pthread.h b/include/pthread.h index f7c9568..0e8eeaa 100644 --- a/include/pthread.h +++ b/include/pthread.h @@ -16,6 +16,8 @@ extern "C" { #define __NEED_pthread_condattr_t #define __NEED_pthread_rwlockattr_t #define __NEED_pthread_barrierattr_t +#define __NEED___pthread_mutex_t +#define __NEED___pthread_cond_t #define __NEED_pthread_mutex_t #define __NEED_pthread_cond_t #define __NEED_pthread_rwlock_t diff --git a/include/sys/types.h b/include/sys/types.h index f00db03..a3f7271 100644 --- a/include/sys/types.h +++ b/include/sys/types.h @@ -36,6 +36,8 @@ extern "C" { #define __NEED_pthread_condattr_t #define __NEED_pthread_rwlockattr_t #define __NEED_pthread_barrierattr_t +#define __NEED___pthread_mutex_t +#define __NEED___pthread_cond_t #define __NEED_pthread_mutex_t #define __NEED_pthread_cond_t #define __NEED_pthread_rwlock_t diff --git a/include/threads.h b/include/threads.h new file mode 100644 index 0000000..cc396c3 --- /dev/null +++ b/include/threads.h @@ -0,0 +1,170 @@ +#ifndef _THREADS_H +#define _THREADS_H + +/* This one is explicitly allowed to be included. */ +#include + +#ifdef __GNUC__ +#define _Nonnull(...) __attribute__((__nonnull__(__VA_ARGS__))) +#else +#define _Nonnull(...) +#endif + + + +#ifdef __cplusplus +extern "C" { +#endif + +#define __NEED___pthread_cond_t +#define __NEED___pthread_mutex_t +#define __NEED_cnd_t +#define __NEED_mtx_t +#define __NEED_once_flag +#define __NEED_thrd_t +#define __NEED_tss_t + +#include + +typedef int (*thrd_start_t)(void*); +typedef void (*tss_dtor_t)(void*); + + +#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 1 +#define __THRD_ERROR 2 +#define __THRD_NOMEM 3 +#define __THRD_TIMEDOUT 4 +#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 { 0 } +#define thread_local _Thread_local + +int thrd_create(thrd_t *, thrd_start_t, void *) _Nonnull(1); +_Noreturn void thrd_exit(int); + +int thrd_detach(thrd_t); +int thrd_join(thrd_t, int *); + +int thrd_sleep(const struct timespec *, struct timespec *) _Nonnull(1); +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)) _Nonnull(1, 2); + +int mtx_init(mtx_t *, int) _Nonnull(1); +void mtx_destroy(mtx_t *) _Nonnull(1); + +int mtx_lock(mtx_t *) _Nonnull(1); +int mtx_timedlock(mtx_t *restrict, const struct timespec *restrict) _Nonnull(1); +int mtx_trylock(mtx_t *) _Nonnull(1); +int mtx_unlock(mtx_t *) _Nonnull(1); + +int cnd_init(cnd_t *) _Nonnull(1); +void cnd_destroy(cnd_t *) _Nonnull(1); + +int cnd_broadcast(cnd_t *) _Nonnull(1); +int cnd_signal(cnd_t *) _Nonnull(1); + +int cnd_timedwait(cnd_t *restrict, mtx_t *restrict, const struct timespec *restrict) _Nonnull(1, 2); +int cnd_wait(cnd_t *, mtx_t *) _Nonnull(1, 2); + +int tss_create(tss_t *, tss_dtor_t) _Nonnull(1); +void tss_delete(tss_t key); + +int tss_set(tss_t, void *); +void *tss_get(tss_t); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/time.h b/include/time.h index dc88070..13dccaa 100644 --- a/include/time.h +++ b/include/time.h @@ -129,6 +129,19 @@ int stime(const time_t *); time_t timegm(struct tm *); #endif +#if __STDC_VERSION__ >= 201112L + /* Implementation specific choice: The epoch that the TIME_UTC clock + is based upon is the Unix Epoch, that is a struct timespec + represents the number of seconds that have elapsed since 00:00:00 + Coordinated Universal Time (UTC), Thursday, 1 January + 1970. Because of differences in leap seconds this is not + completely equivalent to UTC. */ + /* Beware that the TIME_UTC constant itself per the standard must be + greater than 0. */ +#define TIME_UTC 1 +int timespec_get(struct timespec *, int); +#endif + #ifdef __cplusplus } #endif 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 +#include "syscall.h" + +void thrd_yield() +{ + (void)syscall(SYS_sched_yield); +} 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 + +unsigned const __THRD_ABI = __thrd_abi; diff --git a/src/thread/__timedwait.c b/src/thread/__timedwait.c index d6f1233..3d62272 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); + int __timedwait(volatile int *addr, int val, clockid_t clk, const struct timespec *at, void (*cleanup)(void *), void *arg, int priv) @@ -15,7 +18,7 @@ int __timedwait(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--; @@ -25,7 +28,7 @@ int __timedwait(volatile int *addr, int val, top = &to; } - if (!cleanup) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs); + if (!cleanup) __pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs); pthread_cleanup_push(cleanup, arg); r = -__syscall_cp(SYS_futex, addr, FUTEX_WAIT|priv, val, top); @@ -33,7 +36,7 @@ int __timedwait(volatile int *addr, int val, if (r != EINTR && r != ETIMEDOUT) r = 0; pthread_cleanup_pop(0); - if (!cleanup) pthread_setcancelstate(cs, 0); + if (!cleanup) __pthread_setcancelstate(cs, 0); return r; } diff --git a/src/thread/call_once.c b/src/thread/call_once.c new file mode 100644 index 0000000..742a707 --- /dev/null +++ b/src/thread/call_once.c @@ -0,0 +1,9 @@ +#include "pthread_impl.h" +#include + +int __pthread_once(once_flag *, void (*)(void)); + +void (call_once)(once_flag *flag, void (*func)(void)) { + __THRD_ABI_MARK; + (void)__pthread_once(flag, func); +} diff --git a/src/thread/cnd_broadcast.c b/src/thread/cnd_broadcast.c new file mode 100644 index 0000000..b1db667 --- /dev/null +++ b/src/thread/cnd_broadcast.c @@ -0,0 +1,9 @@ +#include "pthread_impl.h" +#include + +int __pthread_cond_broadcast(pthread_cond_t *); + +int cnd_broadcast(cnd_t * cnd) { + int ret = __pthread_cond_broadcast(cnd); + return ret ? thrd_error : thrd_success; +} diff --git a/src/thread/cnd_destroy.c b/src/thread/cnd_destroy.c new file mode 100644 index 0000000..11cfc19 --- /dev/null +++ b/src/thread/cnd_destroy.c @@ -0,0 +1,17 @@ +#include "pthread_impl.h" +#include + +int __pthread_cond_destroy(pthread_cond_t *); + +/* The behavior of cnd_destroy is undefined if cnd is still in + use. The choice for pthread_cond_destroy in that situation is to + wake up all users before destroying. I am not sure that we should + do it like that here, too. Alternatives would be: + - complain by using perror or equivalent + - assert that there is no waiter + - abort when there is a waiter + - do nothing + */ +void (cnd_destroy)(cnd_t *cnd) { + (void)__pthread_cond_destroy(cnd); +} diff --git a/src/thread/cnd_init.c b/src/thread/cnd_init.c new file mode 100644 index 0000000..c7f1edf --- /dev/null +++ b/src/thread/cnd_init.c @@ -0,0 +1,10 @@ +#include "pthread_impl.h" +#include + +int cnd_init(cnd_t * c) +{ + *c = (cnd_t) { + 0 + }; + return thrd_success; +} diff --git a/src/thread/cnd_signal.c b/src/thread/cnd_signal.c new file mode 100644 index 0000000..a8d942d --- /dev/null +++ b/src/thread/cnd_signal.c @@ -0,0 +1,9 @@ +#include "pthread_impl.h" +#include + +int __pthread_cond_signal(pthread_cond_t *); + +int cnd_signal(cnd_t * cnd) { + int ret = __pthread_cond_signal(cnd); + return ret ? thrd_error : thrd_success; +} diff --git a/src/thread/cnd_timedwait.c b/src/thread/cnd_timedwait.c new file mode 100644 index 0000000..cce47a4 --- /dev/null +++ b/src/thread/cnd_timedwait.c @@ -0,0 +1,14 @@ +#include "pthread_impl.h" +#include + +int __pthread_cond_timedwait(pthread_cond_t *restrict c, pthread_mutex_t *restrict m, const struct timespec *restrict ts); + +int cnd_timedwait(cnd_t *restrict cond, mtx_t *restrict mutex, const struct timespec *restrict ts) { + int ret = __pthread_cond_timedwait(cond, mutex, ts); + switch (ret) { + /* May also return EINVAL or EPERM. */ + default: return thrd_error; + case 0: return thrd_success; + case ETIMEDOUT: return thrd_timedout; + } +} diff --git a/src/thread/cnd_wait.c b/src/thread/cnd_wait.c new file mode 100644 index 0000000..39d0a0d --- /dev/null +++ b/src/thread/cnd_wait.c @@ -0,0 +1,11 @@ +#include + +int cnd_wait(cnd_t *cond, mtx_t *mutex) +{ + __THRD_ABI_MARK; + /* 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..482b781 --- /dev/null +++ b/src/thread/mtx_destroy.c @@ -0,0 +1,5 @@ +#include + +void (mtx_destroy)(mtx_t *mtx) { + /* empty */ +} diff --git a/src/thread/mtx_init.c b/src/thread/mtx_init.c new file mode 100644 index 0000000..827a56a --- /dev/null +++ b/src/thread/mtx_init.c @@ -0,0 +1,10 @@ +#include "pthread_impl.h" +#include + +int mtx_init(mtx_t * m, int type) +{ + *m = (pthread_mutex_t) { + ._m_type = ((type&mtx_recursive) ? PTHREAD_MUTEX_RECURSIVE : 0), + }; + return thrd_success; +} diff --git a/src/thread/mtx_lock.c b/src/thread/mtx_lock.c new file mode 100644 index 0000000..89730f1 --- /dev/null +++ b/src/thread/mtx_lock.c @@ -0,0 +1,14 @@ +#include "pthread_impl.h" +#include + +int mtx_lock(mtx_t *m) +{ + __THRD_ABI_MARK; + if (m->_m_type == PTHREAD_MUTEX_NORMAL && !a_cas(&m->_m_lock, 0, EBUSY)) + return thrd_success; + /* Calling mtx_timedlock 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 + mtx_timedlock. */ + return mtx_timedlock(m, 0); +} diff --git a/src/thread/mtx_timedlock.c b/src/thread/mtx_timedlock.c new file mode 100644 index 0000000..c42a096 --- /dev/null +++ b/src/thread/mtx_timedlock.c @@ -0,0 +1,19 @@ +#include "pthread_impl.h" +#include + +int __pthread_mutex_timedlock(pthread_mutex_t *restrict m, const struct timespec *restrict ts); + +int mtx_timedlock(mtx_t *restrict mutex, const struct timespec *restrict ts) { + /* In the best of all worlds this is a tail call. */ + int ret = __pthread_mutex_timedlock(mutex, ts); + switch (ret) { + /* May also return EINVAL, EPERM, EDEADLK or EAGAIN. EAGAIN is + specially tricky since C11 doesn't define how many recursive + calls can be done. (this *isn't* the maximum amount of nested + calls!) This implementation here deals this with a counter and + detects overflow, so this is definitively UB. */ + default: return thrd_error; + case 0: return thrd_success; + case ETIMEDOUT: return thrd_timedout; + } +} diff --git a/src/thread/mtx_trylock.c b/src/thread/mtx_trylock.c new file mode 100644 index 0000000..5fd823a --- /dev/null +++ b/src/thread/mtx_trylock.c @@ -0,0 +1,22 @@ +#include "pthread_impl.h" +#include + +int __pthread_mutex_trylock(pthread_mutex_t *restrict m); + +int mtx_trylock(mtx_t *restrict m) { + if (m->_m_type == PTHREAD_MUTEX_NORMAL) + return (a_cas(&m->_m_lock, 0, EBUSY) & EBUSY) ? thrd_busy : thrd_success; + + /* In the best of all worlds this is a tail call. */ + int ret = __pthread_mutex_trylock(m); + switch (ret) { + /* In case of UB may also return EINVAL, EPERM, EDEADLK or EAGAIN. EAGAIN is + specially tricky since C11 doesn't define how many recursive + calls can be done. (this *isn't* the maximum amount of nested + calls!) This implementation here deals this with a counter and + detects overflow, so this is definitively UB. */ + default: return thrd_error; + case 0: return thrd_success; + case EBUSY: return thrd_busy; + } +} diff --git a/src/thread/mtx_unlock.c b/src/thread/mtx_unlock.c new file mode 100644 index 0000000..d1721a6 --- /dev/null +++ b/src/thread/mtx_unlock.c @@ -0,0 +1,11 @@ +#include "pthread_impl.h" +#include + +int __pthread_mutex_unlock(pthread_mutex_t *); + +int (mtx_unlock)(mtx_t *mtx) { + /* In the best of all worlds this is a tail call. */ + int ret = __pthread_mutex_unlock(mtx); + /* In case of UB may also return EPERM. */ + return ret ? thrd_error : thrd_success; +} diff --git a/src/thread/pthread_cond_broadcast.c b/src/thread/pthread_cond_broadcast.c index 69f840f..f53b41d 100644 --- a/src/thread/pthread_cond_broadcast.c +++ b/src/thread/pthread_cond_broadcast.c @@ -2,7 +2,7 @@ int __private_cond_signal(pthread_cond_t *, int); -int pthread_cond_broadcast(pthread_cond_t *c) +int __pthread_cond_broadcast(pthread_cond_t *c) { if (!c->_c_shared) return __private_cond_signal(c, -1); if (!c->_c_waiters) return 0; @@ -10,3 +10,5 @@ int pthread_cond_broadcast(pthread_cond_t *c) __wake(&c->_c_seq, -1, 0); return 0; } + +weak_alias(__pthread_cond_broadcast, pthread_cond_broadcast); diff --git a/src/thread/pthread_cond_destroy.c b/src/thread/pthread_cond_destroy.c index 8c55516..b2abeb8 100644 --- a/src/thread/pthread_cond_destroy.c +++ b/src/thread/pthread_cond_destroy.c @@ -1,6 +1,6 @@ #include "pthread_impl.h" -int pthread_cond_destroy(pthread_cond_t *c) +int __pthread_cond_destroy(pthread_cond_t *c) { if (c->_c_shared && c->_c_waiters) { int cnt; @@ -12,3 +12,5 @@ int pthread_cond_destroy(pthread_cond_t *c) } return 0; } + +weak_alias(__pthread_cond_destroy, pthread_cond_destroy); diff --git a/src/thread/pthread_cond_signal.c b/src/thread/pthread_cond_signal.c index 119c00a..d1d3c77 100644 --- a/src/thread/pthread_cond_signal.c +++ b/src/thread/pthread_cond_signal.c @@ -2,7 +2,7 @@ int __private_cond_signal(pthread_cond_t *, int); -int pthread_cond_signal(pthread_cond_t *c) +int __pthread_cond_signal(pthread_cond_t *c) { if (!c->_c_shared) return __private_cond_signal(c, 1); if (!c->_c_waiters) return 0; @@ -10,3 +10,5 @@ int pthread_cond_signal(pthread_cond_t *c) __wake(&c->_c_seq, 1, 0); return 0; } + +weak_alias(__pthread_cond_signal, pthread_cond_signal); diff --git a/src/thread/pthread_cond_timedwait.c b/src/thread/pthread_cond_timedwait.c index 2d192b0..393d60e 100644 --- a/src/thread/pthread_cond_timedwait.c +++ b/src/thread/pthread_cond_timedwait.c @@ -1,5 +1,9 @@ #include "pthread_impl.h" +void __pthread_testcancel(void); +int __pthread_mutex_lock(pthread_mutex_t *); +int __pthread_mutex_unlock(pthread_mutex_t *m); + /* * struct waiter * @@ -119,7 +123,7 @@ static void unwait(void *arg) } } -int pthread_cond_timedwait(pthread_cond_t *restrict c, pthread_mutex_t *restrict m, const struct timespec *restrict ts) +int __pthread_cond_timedwait(pthread_cond_t *restrict c, pthread_mutex_t *restrict m, const struct timespec *restrict ts) { struct waiter node = { .cond = c, .mutex = m }; int e, seq, *fut, clock = c->_c_clock; @@ -130,7 +134,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(); if (c->_c_shared) { node.shared = 1; @@ -151,7 +155,7 @@ int pthread_cond_timedwait(pthread_cond_t *restrict c, pthread_mutex_t *restrict unlock(&c->_c_lock); } - pthread_mutex_unlock(m); + __pthread_mutex_unlock(m); do e = __timedwait(fut, seq, clock, ts, unwait, &node, !node.shared); while (*fut==seq && (!e || e==EINTR)); @@ -197,3 +201,5 @@ int __private_cond_signal(pthread_cond_t *c, int n) return 0; } + +weak_alias(__pthread_cond_timedwait, pthread_cond_timedwait); diff --git a/src/thread/pthread_cond_wait.c b/src/thread/pthread_cond_wait.c index 8735bf1..58656f7 100644 --- a/src/thread/pthread_cond_wait.c +++ b/src/thread/pthread_cond_wait.c @@ -1,6 +1,8 @@ #include "pthread_impl.h" +int __pthread_cond_timedwait(pthread_cond_t *restrict c, pthread_mutex_t *restrict m, const struct timespec *restrict ts); + int pthread_cond_wait(pthread_cond_t *restrict c, pthread_mutex_t *restrict m) { - return pthread_cond_timedwait(c, m, 0); + return __pthread_cond_timedwait(c, m, 0); } diff --git a/src/thread/pthread_create.c b/src/thread/pthread_create.c index e441bda..193b295 100644 --- a/src/thread/pthread_create.c +++ b/src/thread/pthread_create.c @@ -4,102 +4,21 @@ #include "libc.h" #include #include +#include + +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); -weak_alias(dummy_0, __do_private_robust_list); -weak_alias(dummy_0, __do_orphaned_stdio_locks); - -_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); - } - - __do_private_robust_list(); - __do_orphaned_stdio_locks(); - - 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) { @@ -121,28 +40,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 @@ -153,16 +54,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(); @@ -191,14 +83,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; @@ -240,7 +132,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_detach.c b/src/thread/pthread_detach.c index 651c38e..5ca7855 100644 --- a/src/thread/pthread_detach.c +++ b/src/thread/pthread_detach.c @@ -1,11 +1,15 @@ #include "pthread_impl.h" -int pthread_detach(pthread_t t) +int __pthread_join(pthread_t t, void **res); + +int __pthread_detach(pthread_t t) { /* Cannot detach a thread that's already exiting */ if (a_swap(t->exitlock, 1)) - return pthread_join(t, 0); + return __pthread_join(t, 0); t->detached = 2; __unlock(t->exitlock); return 0; } + +weak_alias(__pthread_detach, pthread_detach); diff --git a/src/thread/pthread_equal.c b/src/thread/pthread_equal.c index 3e3df4f..38fb45e 100644 --- a/src/thread/pthread_equal.c +++ b/src/thread/pthread_equal.c @@ -1,4 +1,4 @@ -#include +#include "pthread_impl.h" int (pthread_equal)(pthread_t a, pthread_t b) { diff --git a/src/thread/pthread_exit.c b/src/thread/pthread_exit.c new file mode 100644 index 0000000..4f51269 --- /dev/null +++ b/src/thread/pthread_exit.c @@ -0,0 +1,130 @@ +#define _GNU_SOURCE +#include "pthread_impl.h" +#include "stdio_impl.h" +#include "libc.h" +#include +#include + +static void dummy_0() +{ +} +weak_alias(dummy_0, __pthread_tsd_run_dtors); +weak_alias(dummy_0, __do_private_robust_list); +weak_alias(dummy_0, __do_orphaned_stdio_locks); + +_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); + } + + __do_private_robust_list(); + __do_orphaned_stdio_locks(); + + 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; +} + +/* 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; +} + +weak_alias(__pthread_exit, pthread_exit); diff --git a/src/thread/pthread_getspecific.c b/src/thread/pthread_getspecific.c index b2a282c..bfc4294 100644 --- a/src/thread/pthread_getspecific.c +++ b/src/thread/pthread_getspecific.c @@ -1,7 +1,10 @@ #include "pthread_impl.h" -void *pthread_getspecific(pthread_key_t k) +static void *__pthread_getspecific(pthread_key_t k) { struct pthread *self = __pthread_self(); return self->tsd[k]; } + +weak_alias(__pthread_getspecific, pthread_getspecific); +weak_alias(__pthread_getspecific, tss_get); diff --git a/src/thread/pthread_join.c b/src/thread/pthread_join.c index 719c91c..bca89aa 100644 --- a/src/thread/pthread_join.c +++ b/src/thread/pthread_join.c @@ -1,15 +1,19 @@ #include "pthread_impl.h" #include +int __munmap(void *, size_t); + static void dummy(void *p) { } -int pthread_join(pthread_t t, void **res) +int __pthread_join(pthread_t t, void **res) { int tmp; while ((tmp = t->tid)) __timedwait(&t->tid, tmp, 0, 0, dummy, 0, 0); if (res) *res = t->result; - if (t->map_base) munmap(t->map_base, t->map_size); + if (t->map_base) __munmap(t->map_base, t->map_size); return 0; } + +weak_alias(__pthread_join, pthread_join); diff --git a/src/thread/pthread_key_create.c b/src/thread/pthread_key_create.c index a9187f7..bfcd597 100644 --- a/src/thread/pthread_key_create.c +++ b/src/thread/pthread_key_create.c @@ -9,7 +9,7 @@ static void nodtor(void *dummy) { } -int pthread_key_create(pthread_key_t *k, void (*dtor)(void *)) +int __pthread_key_create(pthread_key_t *k, void (*dtor)(void *)) { unsigned i = (uintptr_t)&k / 16 % PTHREAD_KEYS_MAX; unsigned j = i; @@ -31,7 +31,7 @@ int pthread_key_create(pthread_key_t *k, void (*dtor)(void *)) return EAGAIN; } -int pthread_key_delete(pthread_key_t k) +int __pthread_key_delete(pthread_key_t k) { keys[k] = 0; return 0; @@ -53,3 +53,6 @@ void __pthread_tsd_run_dtors() } } } + +weak_alias(__pthread_key_delete, pthread_key_delete); +weak_alias(__pthread_key_create, pthread_key_create); diff --git a/src/thread/pthread_mutex_lock.c b/src/thread/pthread_mutex_lock.c index 2a9a3aa..993f54c 100644 --- a/src/thread/pthread_mutex_lock.c +++ b/src/thread/pthread_mutex_lock.c @@ -1,10 +1,14 @@ #include "pthread_impl.h" -int pthread_mutex_lock(pthread_mutex_t *m) +int __pthread_mutex_timedlock(pthread_mutex_t *restrict m, const struct timespec *restrict at); + +int __pthread_mutex_lock(pthread_mutex_t *m) { if ((m->_m_type&15) == PTHREAD_MUTEX_NORMAL && !a_cas(&m->_m_lock, 0, EBUSY)) return 0; - return pthread_mutex_timedlock(m, 0); + return __pthread_mutex_timedlock(m, 0); } + +weak_alias(__pthread_mutex_lock, pthread_mutex_lock); diff --git a/src/thread/pthread_mutex_timedlock.c b/src/thread/pthread_mutex_timedlock.c index ae883f9..16241ee 100644 --- a/src/thread/pthread_mutex_timedlock.c +++ b/src/thread/pthread_mutex_timedlock.c @@ -1,6 +1,6 @@ #include "pthread_impl.h" -int pthread_mutex_timedlock(pthread_mutex_t *restrict m, const struct timespec *restrict at) +int __pthread_mutex_timedlock(pthread_mutex_t *restrict m, const struct timespec *restrict at) { if ((m->_m_type&15) == PTHREAD_MUTEX_NORMAL && !a_cas(&m->_m_lock, 0, EBUSY)) @@ -30,3 +30,5 @@ int pthread_mutex_timedlock(pthread_mutex_t *restrict m, const struct timespec * } return r; } + +weak_alias(__pthread_mutex_timedlock, pthread_mutex_timedlock); diff --git a/src/thread/pthread_mutex_trylock.c b/src/thread/pthread_mutex_trylock.c index e851517..9be7930 100644 --- a/src/thread/pthread_mutex_trylock.c +++ b/src/thread/pthread_mutex_trylock.c @@ -50,9 +50,11 @@ int __pthread_mutex_trylock_owner(pthread_mutex_t *m) return 0; } -int pthread_mutex_trylock(pthread_mutex_t *m) +static int __pthread_mutex_trylock(pthread_mutex_t *m) { if ((m->_m_type&15) == PTHREAD_MUTEX_NORMAL) return a_cas(&m->_m_lock, 0, EBUSY) & EBUSY; return __pthread_mutex_trylock_owner(m); } + +weak_alias(__pthread_mutex_trylock, pthread_mutex_trylock); diff --git a/src/thread/pthread_mutex_unlock.c b/src/thread/pthread_mutex_unlock.c index 46761d9..a7f39c7 100644 --- a/src/thread/pthread_mutex_unlock.c +++ b/src/thread/pthread_mutex_unlock.c @@ -3,7 +3,7 @@ void __vm_lock_impl(int); void __vm_unlock_impl(void); -int pthread_mutex_unlock(pthread_mutex_t *m) +int __pthread_mutex_unlock(pthread_mutex_t *m) { pthread_t self; int waiters = m->_m_waiters; @@ -36,3 +36,5 @@ int pthread_mutex_unlock(pthread_mutex_t *m) __wake(&m->_m_lock, 1, priv); return 0; } + +weak_alias(__pthread_mutex_unlock, pthread_mutex_unlock); diff --git a/src/thread/pthread_once.c b/src/thread/pthread_once.c index 2eb0f93..05ebe69 100644 --- a/src/thread/pthread_once.c +++ b/src/thread/pthread_once.c @@ -6,7 +6,7 @@ static void undo(void *control) __wake(control, 1, 1); } -int pthread_once(pthread_once_t *control, void (*init)(void)) +int __pthread_once(pthread_once_t *control, void (*init)(void)) { static int waiters; @@ -34,3 +34,5 @@ int pthread_once(pthread_once_t *control, void (*init)(void)) return 0; } } + +weak_alias(__pthread_once, pthread_once); 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_setspecific.c b/src/thread/pthread_setspecific.c index 55e46a8..dd18b96 100644 --- a/src/thread/pthread_setspecific.c +++ b/src/thread/pthread_setspecific.c @@ -1,6 +1,6 @@ #include "pthread_impl.h" -int pthread_setspecific(pthread_key_t k, const void *x) +int __pthread_setspecific(pthread_key_t k, const void *x) { struct pthread *self = __pthread_self(); /* Avoid unnecessary COW */ @@ -10,3 +10,5 @@ int pthread_setspecific(pthread_key_t k, const void *x) } return 0; } + +weak_alias(__pthread_setspecific, pthread_setspecific); 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 +#include + +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 + +/* 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..211b8fb --- /dev/null +++ b/src/thread/thrd_detach.c @@ -0,0 +1,18 @@ +#include "pthread_impl.h" +#include + +int __munmap(void *, size_t); + +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 + +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..a8c7aed --- /dev/null +++ b/src/thread/thrd_join.c @@ -0,0 +1,16 @@ +#include "pthread_impl.h" +#include +#include + +int __munmap(void *, size_t); + +/* 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..0cbadc8 --- /dev/null +++ b/src/thread/tss_create.c @@ -0,0 +1,13 @@ +#include "pthread_impl.h" +#include + +int __pthread_key_create(pthread_key_t *k, void (*dtor)(void *)); + +int tss_create(tss_t *tss, tss_dtor_t dtor) +{ + __THRD_ABI_MARK; + /* Different error returns are possible. C glues them together into + just failure notification. Can't be optimized to a tail call, + unless thrd_error equals EAGAIN. */ + return __pthread_key_create(tss, dtor) ? thrd_error : thrd_success; +} diff --git a/src/thread/tss_delete.c b/src/thread/tss_delete.c new file mode 100644 index 0000000..d50731f --- /dev/null +++ b/src/thread/tss_delete.c @@ -0,0 +1,8 @@ +#include "pthread_impl.h" +#include + +int __pthread_key_delete(pthread_key_t k); + +void (tss_delete)(tss_t key) { + (void)__pthread_key_delete(key); +} diff --git a/src/thread/tss_set.c b/src/thread/tss_set.c new file mode 100644 index 0000000..70c4fb7 --- /dev/null +++ b/src/thread/tss_set.c @@ -0,0 +1,13 @@ +#include "pthread_impl.h" +#include + +int tss_set(tss_t k, void *x) +{ + struct pthread *self = __pthread_self(); + /* Avoid unnecessary COW */ + if (self->tsd[k] != x) { + self->tsd[k] = x; + self->tsd_used = 1; + } + return thrd_success; +} 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 +#include +#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; +} diff --git a/src/time/timespec_get.c b/src/time/timespec_get.c new file mode 100644 index 0000000..bf78e5a --- /dev/null +++ b/src/time/timespec_get.c @@ -0,0 +1,9 @@ +#include + +int __clock_gettime(clockid_t clk, struct timespec *ts); + +/* the base argument is simply ignored, there is no other implemented + value than TIME_UTC. */ +int timespec_get(struct timespec * ts, int base) { + return __clock_gettime(CLOCK_REALTIME, ts) < 0 ? 0 : base; +}