diff options
Diffstat (limited to 'src/thread')
70 files changed, 1030 insertions, 0 deletions
diff --git a/src/thread/__futex.c b/src/thread/__futex.c new file mode 100644 index 00000000..93352fa3 --- /dev/null +++ b/src/thread/__futex.c @@ -0,0 +1,8 @@ +#include "futex.h" +#include "syscall.h" + +int __futex(volatile int *addr, int op, int val, void *ts) +{ + return syscall4(__NR_futex, (long)addr, op, val, (long)ts); +} + diff --git a/src/thread/__lock.c b/src/thread/__lock.c new file mode 100644 index 00000000..557c6a62 --- /dev/null +++ b/src/thread/__lock.c @@ -0,0 +1,12 @@ +#define SYSCALL_RETURN_ERRNO +#include "pthread_impl.h" + +void __lock(volatile int *l) +{ + int spins=100000; + /* Do not use futexes because we insist that unlocking is a simple + * assignment to optimize non-pathological code with no contention. */ + while (a_xchg(l, 1)) + if (spins) spins--, a_spin(); + else syscall0(__NR_sched_yield); +} diff --git a/src/thread/__set_thread_area.c b/src/thread/__set_thread_area.c new file mode 100644 index 00000000..576d8b40 --- /dev/null +++ b/src/thread/__set_thread_area.c @@ -0,0 +1,9 @@ +#include "syscall.h" + +int __set_thread_area(unsigned long *desc) +{ + if (syscall1(__NR_set_thread_area, (long)desc) < 0) + return -1; + __asm__ __volatile__ ( "movw %w0,%%gs" : : "r"(desc[0]*8+3) ); + return 0; +} diff --git a/src/thread/__timedwait.c b/src/thread/__timedwait.c new file mode 100644 index 00000000..354def2c --- /dev/null +++ b/src/thread/__timedwait.c @@ -0,0 +1,21 @@ +#include <time.h> +#include <errno.h> +#include "futex.h" +#define SYSCALL_RETURN_ERRNO +#include "syscall.h" +#include <stdio.h> +int __timedwait(volatile int *addr, int val, clockid_t clk, const struct timespec *at, int priv) +{ + struct timespec to; + if (at) { + clock_gettime(clk, &to); + 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; + } + if (priv) priv = 128; priv=0; + return syscall4(__NR_futex, (long)addr, FUTEX_WAIT | priv, val, at ? (long)&to : 0); +} diff --git a/src/thread/__unmapself.c b/src/thread/__unmapself.c new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/thread/__unmapself.c diff --git a/src/thread/__wait.c b/src/thread/__wait.c new file mode 100644 index 00000000..8c249cd3 --- /dev/null +++ b/src/thread/__wait.c @@ -0,0 +1,16 @@ +#define SYSCALL_RETURN_ERRNO +#include "pthread_impl.h" + +void __wait(volatile int *addr, volatile int *waiters, int val, int priv) +{ + int spins=50000; + if (priv) priv = 128; priv=0; + while (spins--) { + if (*addr==val) a_spin(); + else return; + } + if (waiters) a_inc(waiters); + while (*addr==val) + syscall4(__NR_futex, (long)addr, FUTEX_WAIT|priv, val, 0); + if (waiters) a_dec(waiters); +} diff --git a/src/thread/__wake.c b/src/thread/__wake.c new file mode 100644 index 00000000..048ddcc0 --- /dev/null +++ b/src/thread/__wake.c @@ -0,0 +1,9 @@ +#define SYSCALL_RETURN_ERRNO +#include "pthread_impl.h" + +void __wake(volatile int *addr, int cnt, int priv) +{ + if (priv) priv = 128; priv=0; + if (cnt<0) cnt = INT_MAX; + syscall3(__NR_futex, (long)addr, FUTEX_WAKE | priv, cnt); +} diff --git a/src/thread/cancellation.c b/src/thread/cancellation.c new file mode 100644 index 00000000..e35ba824 --- /dev/null +++ b/src/thread/cancellation.c @@ -0,0 +1,22 @@ +#include "pthread_impl.h" + +void __pthread_register_cancel(struct __ptcb *cb) +{ + struct pthread *self = pthread_self(); + cb->__next = self->cancelbuf; + self->cancelbuf = cb; +} + +#define pthread_self __pthread_self + +void __pthread_unregister_cancel(struct __ptcb *cb) +{ + struct pthread *self = pthread_self(); + self->cancelbuf = self->cancelbuf->__next; +} + +void __pthread_unwind_next(struct __ptcb *cb) +{ + if (cb->__next) longjmp((void *)cb->__next->__jb, 1); + pthread_exit(PTHREAD_CANCELLED); +} diff --git a/src/thread/clone.c b/src/thread/clone.c new file mode 100644 index 00000000..971bfeed --- /dev/null +++ b/src/thread/clone.c @@ -0,0 +1,26 @@ +#if 0 + +int clone(int (*start)(void *), void *stack, int flags, void *arg, + pid_t *ptid, struct user_desc *tls, pid_t *ctid) +{ + int ret; + __asm__( + "andl $-16,%%ecx \n\t" + "xchgl %%ebx,%2 \n\t" + "movl %%ebx,(%%ecx) \n\t" + "int $0x80 \n\t" + "testl %%eax,%%eax \n\t" + "jnz 1f \n\t" + "xorl %%ebp,%%ebp \n\t" + "call *%%ebx \n\t" + "\n1: \n\t" + "xchgl %%ebx,%2 \n\t" + : "=a" (ret) + : "a" (__NR_clone), "m" (flags), "c"(stack), "d"(ptid), + "S" (tls), "D" (ctid) + : "memory" + ); + return __syscall_ret(ret); +} + +#endif diff --git a/src/thread/i386/__unmapself.s b/src/thread/i386/__unmapself.s new file mode 100644 index 00000000..5c674966 --- /dev/null +++ b/src/thread/i386/__unmapself.s @@ -0,0 +1,22 @@ +.text +.global __unmapself +.type __unmapself,%function +__unmapself: + call 1f + .long -1 + .long -1 +1: popl %ecx + xorl %ebx,%ebx + xorl %edx,%edx + movl $8,%esi + movl $175,%eax + int $128 + movl $91,%eax + movl 4(%esp),%ebx + movl 8(%esp),%ecx + int $128 + xorl %ebx,%ebx + movl $1,%eax + int $128 + +.size __unmapself,.-__unmapself diff --git a/src/thread/i386/clone.s b/src/thread/i386/clone.s new file mode 100644 index 00000000..4f33366c --- /dev/null +++ b/src/thread/i386/clone.s @@ -0,0 +1,35 @@ +.text +.global __clone +.type __clone,%function +__clone: + movl 8(%esp),%ecx + andl $0xfffffff0, %ecx + subl $28,%ecx + movl 16(%esp),%eax + movl %eax,12(%ecx) + movl 4(%esp),%eax + movl %eax,8(%ecx) + pushl %ebx + pushl %esi + pushl %edi + movl $120,%eax + movl 12+12(%esp),%ebx + movl 20+12(%esp),%edx + movl 24+12(%esp),%esi + movl 28+12(%esp),%edi + int $128 + popl %edi + popl %esi + popl %ebx + test %eax,%eax + jnz 1f + xorl %ebp,%ebp + call *%ebx + movl %eax, %ebx + movl $1, %eax + int $128 +1: + movl %eax, 4(%esp) + ret + +.size __clone,.-__clone diff --git a/src/thread/pthread_attr_destroy.c b/src/thread/pthread_attr_destroy.c new file mode 100644 index 00000000..b5845dd0 --- /dev/null +++ b/src/thread/pthread_attr_destroy.c @@ -0,0 +1,6 @@ +#include "pthread_impl.h" + +int pthread_attr_destroy(pthread_attr_t *a) +{ + return 0; +} diff --git a/src/thread/pthread_attr_getdetachstate.c b/src/thread/pthread_attr_getdetachstate.c new file mode 100644 index 00000000..9cd49536 --- /dev/null +++ b/src/thread/pthread_attr_getdetachstate.c @@ -0,0 +1,7 @@ +#include "pthread_impl.h" + +int pthread_attr_getdetachstate(pthread_attr_t *a, int *state) +{ + *state = a->__detach; + return 0; +} diff --git a/src/thread/pthread_attr_getguardsize.c b/src/thread/pthread_attr_getguardsize.c new file mode 100644 index 00000000..3f089c8c --- /dev/null +++ b/src/thread/pthread_attr_getguardsize.c @@ -0,0 +1,7 @@ +#include "pthread_impl.h" + +int pthread_attr_getguardsize(pthread_attr_t *a, size_t *size) +{ + *size = a->__guardsize + DEFAULT_GUARD_SIZE; + return 0; +} diff --git a/src/thread/pthread_attr_getscope.c b/src/thread/pthread_attr_getscope.c new file mode 100644 index 00000000..0cb224d3 --- /dev/null +++ b/src/thread/pthread_attr_getscope.c @@ -0,0 +1,6 @@ +#include "pthread_impl.h" + +int pthread_attr_getscope(pthread_attr_t *a, int *scope) +{ + return 0; +} diff --git a/src/thread/pthread_attr_getstacksize.c b/src/thread/pthread_attr_getstacksize.c new file mode 100644 index 00000000..40bbf186 --- /dev/null +++ b/src/thread/pthread_attr_getstacksize.c @@ -0,0 +1,7 @@ +#include "pthread_impl.h" + +int pthread_attr_getstacksize(pthread_attr_t *a, size_t *size) +{ + *size = a->__stacksize + DEFAULT_STACK_SIZE; + return 0; +} diff --git a/src/thread/pthread_attr_init.c b/src/thread/pthread_attr_init.c new file mode 100644 index 00000000..d91bf157 --- /dev/null +++ b/src/thread/pthread_attr_init.c @@ -0,0 +1,7 @@ +#include "pthread_impl.h" + +int pthread_attr_init(pthread_attr_t *a) +{ + memset(a, 0, sizeof *a); + return 0; +} diff --git a/src/thread/pthread_attr_setdetachstate.c b/src/thread/pthread_attr_setdetachstate.c new file mode 100644 index 00000000..d23b4778 --- /dev/null +++ b/src/thread/pthread_attr_setdetachstate.c @@ -0,0 +1,7 @@ +#include "pthread_impl.h" + +int pthread_attr_setdetachstate(pthread_attr_t *a, int state) +{ + a->__detach = state; + return 0; +} diff --git a/src/thread/pthread_attr_setguardsize.c b/src/thread/pthread_attr_setguardsize.c new file mode 100644 index 00000000..e0e8d3fb --- /dev/null +++ b/src/thread/pthread_attr_setguardsize.c @@ -0,0 +1,8 @@ +#include "pthread_impl.h" + +int pthread_attr_setguardsize(pthread_attr_t *a, size_t size) +{ + if (size > SIZE_MAX/8) return EINVAL; + a->__guardsize = size - DEFAULT_GUARD_SIZE; + return 0; +} diff --git a/src/thread/pthread_attr_setscope.c b/src/thread/pthread_attr_setscope.c new file mode 100644 index 00000000..a970a819 --- /dev/null +++ b/src/thread/pthread_attr_setscope.c @@ -0,0 +1,6 @@ +#include "pthread_impl.h" + +int pthread_attr_setscope(pthread_attr_t *a, int scope) +{ + return 0; +} diff --git a/src/thread/pthread_attr_setstacksize.c b/src/thread/pthread_attr_setstacksize.c new file mode 100644 index 00000000..58f4bb18 --- /dev/null +++ b/src/thread/pthread_attr_setstacksize.c @@ -0,0 +1,8 @@ +#include "pthread_impl.h" + +int pthread_attr_setstacksize(pthread_attr_t *a, size_t size) +{ + if (size-PAGE_SIZE > SIZE_MAX/4) return EINVAL; + a->__stacksize = size - DEFAULT_STACK_SIZE; + return 0; +} diff --git a/src/thread/pthread_barrier_destroy.c b/src/thread/pthread_barrier_destroy.c new file mode 100644 index 00000000..2898c41a --- /dev/null +++ b/src/thread/pthread_barrier_destroy.c @@ -0,0 +1,6 @@ +#include "pthread_impl.h" + +int pthread_barrier_destroy(pthread_barrier_t *b) +{ + return 0; +} diff --git a/src/thread/pthread_barrier_init.c b/src/thread/pthread_barrier_init.c new file mode 100644 index 00000000..2cc67ed5 --- /dev/null +++ b/src/thread/pthread_barrier_init.c @@ -0,0 +1,8 @@ +#include "pthread_impl.h" + +int pthread_barrier_init(pthread_barrier_t *b, const pthread_barrierattr_t *a, unsigned count) +{ + if (!count) return EINVAL; + *b = (pthread_barrier_t){ .__limit = count-1 }; + return 0; +} diff --git a/src/thread/pthread_barrier_wait.c b/src/thread/pthread_barrier_wait.c new file mode 100644 index 00000000..02c252ad --- /dev/null +++ b/src/thread/pthread_barrier_wait.c @@ -0,0 +1,31 @@ +#include "pthread_impl.h" + +int pthread_barrier_wait(pthread_barrier_t *b) +{ + int cur; + + /* Trivial case: count was set at 1 */ + if (!b->__limit) return PTHREAD_BARRIER_SERIAL_THREAD; + + /* Wait for anyone still suspended at previous use of barrier */ + while ((cur=b->__left)) + __wait(&b->__left, &b->__waiters, cur, 0); + + /* If we are the last to reach barrier, reset it and wake others */ + if (a_fetch_add(&b->__count, 1) == b->__limit) { + b->__left = b->__limit; + b->__count = 0; + __wake(&b->__count, -1, 0); + return PTHREAD_BARRIER_SERIAL_THREAD; + } + + /* Wait for our peers to reach the barrier */ + while ((cur=b->__count)) + __wait(&b->__count, 0, cur, 0); + + /* If we're the last to wake up and barrier is awaiting reuse */ + if (a_fetch_add(&b->__left, -1) == 1 && b->__waiters) + __wake(&b->__left, -1, 0); + + return 0; +} diff --git a/src/thread/pthread_cancel.c b/src/thread/pthread_cancel.c new file mode 100644 index 00000000..9397ffe9 --- /dev/null +++ b/src/thread/pthread_cancel.c @@ -0,0 +1,7 @@ +#define SYSCALL_RETURN_ERRNO +#include "pthread_impl.h" + +int pthread_cancel(pthread_t t) +{ + return syscall3(__NR_tgkill, t->pid, t->tid, SIGCANCEL); +} diff --git a/src/thread/pthread_cond_broadcast.c b/src/thread/pthread_cond_broadcast.c new file mode 100644 index 00000000..7a023b85 --- /dev/null +++ b/src/thread/pthread_cond_broadcast.c @@ -0,0 +1,8 @@ +#include "pthread_impl.h" + +int pthread_cond_broadcast(pthread_cond_t *c) +{ + c->__block = 0; + __wake(&c->__block, -1, 0); + return 0; +} diff --git a/src/thread/pthread_cond_destroy.c b/src/thread/pthread_cond_destroy.c new file mode 100644 index 00000000..1d21a5a8 --- /dev/null +++ b/src/thread/pthread_cond_destroy.c @@ -0,0 +1,6 @@ +#include "pthread_impl.h" + +int pthread_cond_destroy(pthread_cond_t *c) +{ + return 0; +} diff --git a/src/thread/pthread_cond_init.c b/src/thread/pthread_cond_init.c new file mode 100644 index 00000000..33948606 --- /dev/null +++ b/src/thread/pthread_cond_init.c @@ -0,0 +1,7 @@ +#include "pthread_impl.h" + +int pthread_cond_init(pthread_cond_t *c, const pthread_condattr_t *a) +{ + memset(c, 0, sizeof *c); + return 0; +} diff --git a/src/thread/pthread_cond_signal.c b/src/thread/pthread_cond_signal.c new file mode 100644 index 00000000..0dd9416b --- /dev/null +++ b/src/thread/pthread_cond_signal.c @@ -0,0 +1,8 @@ +#include "pthread_impl.h" + +int pthread_cond_signal(pthread_cond_t *c) +{ + c->__block = 0; + __wake(&c->__block, 1, 0); + return 0; +} diff --git a/src/thread/pthread_cond_timedwait.c b/src/thread/pthread_cond_timedwait.c new file mode 100644 index 00000000..b67dded4 --- /dev/null +++ b/src/thread/pthread_cond_timedwait.c @@ -0,0 +1,26 @@ +#include "pthread_impl.h" + +static void relock(void *m) +{ + pthread_mutex_lock(m); +} + +int pthread_cond_timedwait(pthread_cond_t *c, pthread_mutex_t *m, const struct timespec *ts) +{ + int r, e=0; + CANCELPT(0); + + pthread_cleanup_push(relock, m); + c->__block = 1; + if ((r=pthread_mutex_unlock(m))) return r; + + CANCELPT(1); + e = __timedwait(&c->__block, 1, CLOCK_REALTIME, ts, 0); + CANCELPT(0); + + pthread_cleanup_pop(0); + if ((r=pthread_mutex_lock(m))) return r; + + CANCELPT(0); + return e; +} diff --git a/src/thread/pthread_cond_wait.c b/src/thread/pthread_cond_wait.c new file mode 100644 index 00000000..eb70e5f7 --- /dev/null +++ b/src/thread/pthread_cond_wait.c @@ -0,0 +1,6 @@ +#include "pthread_impl.h" + +int pthread_cond_wait(pthread_cond_t *c, pthread_mutex_t *m) +{ + return pthread_cond_timedwait(c, m, 0); +} diff --git a/src/thread/pthread_create.c b/src/thread/pthread_create.c new file mode 100644 index 00000000..6fa484c7 --- /dev/null +++ b/src/thread/pthread_create.c @@ -0,0 +1,189 @@ +#include "pthread_impl.h" + +#define pthread_self __pthread_self + +static void docancel(struct pthread *self) +{ + struct __ptcb cb = { .__next = self->cancelbuf }; + __pthread_unwind_next(&cb); +} + +static void cancel_handler(int sig, siginfo_t *si, void *ctx) +{ + struct pthread *self = pthread_self(); + self->cancel = 1; + if (self->canceldisable || (!self->cancelasync && !self->cancelpoint)) + return; + docancel(self); +} + +/* "rsyscall" is a mechanism by which a thread can synchronously force all + * other threads to perform an arbitrary syscall. It is necessary to work + * around the non-conformant implementation of setuid() et al on Linux, + * which affect only the calling thread and not the whole process. This + * implementation performs some tricks with signal delivery to work around + * the fact that it does not keep any list of threads in userspace. */ + +static struct { + volatile int lock, hold, blocks, cnt; + unsigned long arg[6]; + int nr; + int err; +} rs; + +static void rsyscall_handler(int sig, siginfo_t *si, void *ctx) +{ + if (rs.cnt == libc.threads_minus_1) return; + + if (syscall6(rs.nr, rs.arg[0], rs.arg[1], rs.arg[2], + rs.arg[3], rs.arg[4], rs.arg[5]) < 0 && !rs.err) rs.err=errno; + + a_inc(&rs.cnt); + __wake(&rs.cnt, 1, 1); + while(rs.hold) + __wait(&rs.hold, 0, 1, 1); + a_dec(&rs.cnt); + if (!rs.cnt) __wake(&rs.cnt, 1, 1); +} + +static int rsyscall(int nr, long a, long b, long c, long d, long e, long f) +{ + int i, ret; + sigset_t set = { 0 }; + struct pthread *self = pthread_self(); + sigaddset(&set, SIGSYSCALL); + + LOCK(&rs.lock); + while ((i=rs.blocks)) + __wait(&rs.blocks, 0, i, 1); + + __libc_sigprocmask(SIG_BLOCK, &set, 0); + + rs.nr = nr; + rs.arg[0] = a; rs.arg[1] = b; + rs.arg[2] = c; rs.arg[3] = d; + rs.arg[4] = d; rs.arg[5] = f; + rs.hold = 1; + rs.err = 0; + rs.cnt = 0; + + /* Dispatch signals until all threads respond */ + for (i=libc.threads_minus_1; i; i--) + sigqueue(self->pid, SIGSYSCALL, (union sigval){0}); + while ((i=rs.cnt) < libc.threads_minus_1) { + sigqueue(self->pid, SIGSYSCALL, (union sigval){0}); + __wait(&rs.cnt, 0, i, 1); + } + + /* Handle any lingering signals with no-op */ + __libc_sigprocmask(SIG_UNBLOCK, &set, 0); + + /* Resume other threads' signal handlers and wait for them */ + rs.hold = 0; + __wake(&rs.hold, -1, 0); + while((i=rs.cnt)) __wait(&rs.cnt, 0, i, 1); + + if (rs.err) errno = rs.err, ret = -1; + else ret = syscall6(nr, a, b, c, d, e, f); + + UNLOCK(&rs.lock); + return ret; +} + +static void cancelpt(int x) +{ + struct pthread *self = pthread_self(); + if (self->canceldisable) return; + self->cancelpoint = x; + if (self->cancel) docancel(self); +} + +static void init_threads() +{ + struct sigaction sa = { .sa_flags = SA_SIGINFO | SA_RESTART }; + libc.lock = __lock; + libc.cancelpt = cancelpt; + libc.rsyscall = rsyscall; + sa.sa_sigaction = cancel_handler; + __libc_sigaction(SIGCANCEL, &sa, 0); + sigaddset(&sa.sa_mask, SIGSYSCALL); + sigaddset(&sa.sa_mask, SIGCANCEL); + sa.sa_sigaction = rsyscall_handler; + __libc_sigaction(SIGSYSCALL, &sa, 0); + sigprocmask(SIG_UNBLOCK, &sa.sa_mask, 0); +} + +static int start(void *p) +{ + struct pthread *self = p; + pthread_exit(self->start(self->start_arg)); + return 0; +} + +#undef pthread_self + +#define CLONE_MAGIC 0x7d0f00 +int __clone(int (*)(void *), void *, int, void *, pid_t *, void *, pid_t *); + +#define ROUND(x) (((x)+PAGE_SIZE-1)&-PAGE_SIZE) + +/* pthread_key_create.c overrides this */ +static const size_t dummy = 0; +weak_alias(dummy, __pthread_tsd_size); + +int pthread_create(pthread_t *res, const pthread_attr_t *attr, void *(*entry)(void *), void *arg) +{ + static int init; + int ret; + size_t size, guard; + struct pthread *self = pthread_self(), *new; + unsigned char *map, *stack, *tsd; + static const pthread_attr_t default_attr; + + if (!self) return errno = ENOSYS; + if (!init && ++init) init_threads(); + + if (!attr) attr = &default_attr; + guard = ROUND(attr->__guardsize + DEFAULT_GUARD_SIZE); + size = guard + ROUND(attr->__stacksize + DEFAULT_STACK_SIZE); + size += __pthread_tsd_size; + map = mmap(0, size, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANON, -1, 0); + if (!map) return EAGAIN; + mprotect(map, guard, PROT_NONE); + + tsd = map + size - __pthread_tsd_size; + new = (void *)(tsd - sizeof *new - PAGE_SIZE%sizeof *new); + new->map_base = map; + new->map_size = size; + new->pid = self->pid; + new->errno_ptr = &new->errno_val; + new->start = entry; + new->start_arg = arg; + new->self = new; + new->tsd = (void *)tsd; + new->detached = attr->__detach; + new->attr = *attr; + memcpy(new->tlsdesc, self->tlsdesc, sizeof new->tlsdesc); + new->tlsdesc[1] = (uintptr_t)new; + stack = (void *)((uintptr_t)new-1 & ~(uintptr_t)15); + + /* We must synchronize new thread creation with rsyscall + * delivery. This looks to be the least expensive way: */ + a_inc(&rs.blocks); + while (rs.lock) __wait(&rs.lock, 0, 1, 1); + + a_inc(&libc.threads_minus_1); + ret = __clone(start, stack, CLONE_MAGIC, new, + &new->tid, &new->tlsdesc, &new->tid); + + a_dec(&rs.blocks); + if (rs.lock) __wake(&rs.blocks, 1, 1); + + if (ret < 0) { + a_dec(&libc.threads_minus_1); + munmap(map, size); + return -ret; + } + *res = new; + return 0; +} diff --git a/src/thread/pthread_detach.c b/src/thread/pthread_detach.c new file mode 100644 index 00000000..f0eae3e8 --- /dev/null +++ b/src/thread/pthread_detach.c @@ -0,0 +1,11 @@ +#include "pthread_impl.h" + +int pthread_detach(pthread_t t) +{ + /* Cannot detach a thread that's already exiting */ + if (a_xchg(&t->exitlock, 1)) + return pthread_join(t, 0); + t->detached = 1; + t->exitlock = 0; + return 0; +} diff --git a/src/thread/pthread_equal.c b/src/thread/pthread_equal.c new file mode 100644 index 00000000..a55d280c --- /dev/null +++ b/src/thread/pthread_equal.c @@ -0,0 +1,6 @@ +#include <pthread.h> + +int pthread_equal(pthread_t a, pthread_t b) +{ + return a==b; +} diff --git a/src/thread/pthread_exit.c b/src/thread/pthread_exit.c new file mode 100644 index 00000000..4966e234 --- /dev/null +++ b/src/thread/pthread_exit.c @@ -0,0 +1,25 @@ +#include "pthread_impl.h" + +#undef pthread_self + +void pthread_exit(void *result) +{ + int i; + struct pthread *self = pthread_self(); + self->result = result; + + a_dec(&libc.threads_minus_1); + if (libc.threads_minus_1 < 0) + exit(0); + + LOCK(&self->exitlock); + + if (self->tsd_used) for (i=0; i<PTHREAD_KEYS_MAX; i++) + if (self->tsd[i] && libc.tsd_keys[i]) + libc.tsd_keys[i](self->tsd[i]); + + if (self->detached && self->map_base) + __unmapself(self->map_base, self->map_size); + + __syscall_exit(0); +} diff --git a/src/thread/pthread_getspecific.c b/src/thread/pthread_getspecific.c new file mode 100644 index 00000000..a6ca27d0 --- /dev/null +++ b/src/thread/pthread_getspecific.c @@ -0,0 +1,8 @@ +#include "pthread_impl.h" + +void *pthread_getspecific(pthread_key_t k) +{ + struct pthread *self = pthread_self(); + if (!self->tsd) return 0; + return self->tsd[k]; +} diff --git a/src/thread/pthread_join.c b/src/thread/pthread_join.c new file mode 100644 index 00000000..5210ed48 --- /dev/null +++ b/src/thread/pthread_join.c @@ -0,0 +1,12 @@ +#include "pthread_impl.h" + +int pthread_join(pthread_t t, void **res) +{ + int tmp = t->tid; + CANCELPT_BEGIN; + if (tmp) __wait(&t->tid, 0, tmp, 1); + CANCELPT_END; + if (res) *res = t->result; + if (t->map_base) munmap(t->map_base, t->map_size); + return 0; +} diff --git a/src/thread/pthread_key_create.c b/src/thread/pthread_key_create.c new file mode 100644 index 00000000..efc38046 --- /dev/null +++ b/src/thread/pthread_key_create.c @@ -0,0 +1,25 @@ +#include "pthread_impl.h" + +const size_t __pthread_tsd_size = sizeof(void *) * PTHREAD_KEYS_MAX; + +static void nodtor(void *dummy) +{ +} + +int pthread_key_create(pthread_key_t *k, void (*dtor)(void *)) +{ + static void (*keys[PTHREAD_KEYS_MAX])(void *); + int i = (uintptr_t)&k / 16 % PTHREAD_KEYS_MAX; + int j = i; + + libc.tsd_keys = keys; + if (!dtor) dtor = nodtor; + /* Cheap trick - &k cannot match any destructor pointer */ + while (a_cas_p(keys+j, 0, &k) + && (j=(j+1)%PTHREAD_KEYS_MAX) != i); + if (keys[j] != (void (*)(void *))&k) + return EAGAIN; + keys[j] = dtor; + *k = j; + return 0; +} diff --git a/src/thread/pthread_key_delete.c b/src/thread/pthread_key_delete.c new file mode 100644 index 00000000..4914ebb4 --- /dev/null +++ b/src/thread/pthread_key_delete.c @@ -0,0 +1,7 @@ +#include "pthread_impl.h" + +int pthread_key_delete(pthread_key_t k) +{ + if (libc.tsd_keys) libc.tsd_keys[k] = 0; + return 0; +} diff --git a/src/thread/pthread_kill.c b/src/thread/pthread_kill.c new file mode 100644 index 00000000..9d85fa5b --- /dev/null +++ b/src/thread/pthread_kill.c @@ -0,0 +1,7 @@ +#define SYSCALL_RETURN_ERRNO +#include "pthread_impl.h" + +int pthread_kill(pthread_t t, int sig) +{ + return syscall3(__NR_tgkill, t->pid, t->tid, sig); +} diff --git a/src/thread/pthread_mutex_destroy.c b/src/thread/pthread_mutex_destroy.c new file mode 100644 index 00000000..6d49e689 --- /dev/null +++ b/src/thread/pthread_mutex_destroy.c @@ -0,0 +1,6 @@ +#include <pthread.h> + +int pthread_mutex_destroy(pthread_mutex_t *mutex) +{ + return 0; +} diff --git a/src/thread/pthread_mutex_init.c b/src/thread/pthread_mutex_init.c new file mode 100644 index 00000000..d453543d --- /dev/null +++ b/src/thread/pthread_mutex_init.c @@ -0,0 +1,9 @@ +#include "pthread_impl.h" + +int pthread_mutex_init(pthread_mutex_t *m, const pthread_mutexattr_t *a) +{ + memset(m, 0, sizeof *m); + if (a) { + } + return 0; +} diff --git a/src/thread/pthread_mutex_lock.c b/src/thread/pthread_mutex_lock.c new file mode 100644 index 00000000..6696f17a --- /dev/null +++ b/src/thread/pthread_mutex_lock.c @@ -0,0 +1,9 @@ +#include "pthread_impl.h" + +int pthread_mutex_lock(pthread_mutex_t *m) +{ + int r; + while ((r=pthread_mutex_trylock(m)) == EBUSY) + __wait(&m->__lock, &m->__waiters, 1, 0); + return r; +} diff --git a/src/thread/pthread_mutex_timedlock.c b/src/thread/pthread_mutex_timedlock.c new file mode 100644 index 00000000..5dfad94f --- /dev/null +++ b/src/thread/pthread_mutex_timedlock.c @@ -0,0 +1,15 @@ +#include "pthread_impl.h" + +int pthread_mutex_timedlock(pthread_mutex_t *m, const struct timespec *at) +{ + int r, w=0; + while ((r=pthread_mutex_trylock(m)) == EBUSY) { + if (!w) a_inc(&m->__waiters), w++; + if (__timedwait(&m->__lock, 1, CLOCK_REALTIME, at, 0) == ETIMEDOUT) { + if (w) a_dec(&m->__waiters); + return ETIMEDOUT; + } + } + if (w) a_dec(&m->__waiters); + return r; +} diff --git a/src/thread/pthread_mutex_trylock.c b/src/thread/pthread_mutex_trylock.c new file mode 100644 index 00000000..1e3817bb --- /dev/null +++ b/src/thread/pthread_mutex_trylock.c @@ -0,0 +1,28 @@ +#include "pthread_impl.h" + +int pthread_mutex_trylock(pthread_mutex_t *m) +{ + if (m->__type == PTHREAD_MUTEX_RECURSIVE) { + pthread_t self = pthread_self(); + if (m->__owner == self) { + if ((unsigned)m->__lock >= INT_MAX) return EAGAIN; + a_inc(&m->__lock); + return 0; + } + if (a_fetch_add(&m->__lock, 1)) { + if (a_fetch_add(&m->__lock, -1)==1 && m->__waiters) + __wake(&m->__lock, 1, 0); + return EBUSY; + } + m->__owner = self; + return 0; + } + + if (a_xchg(&m->__lock, 1)) + if (m->__type == PTHREAD_MUTEX_ERRORCHECK + && m->__owner == pthread_self()) return EDEADLK; + else return EBUSY; + if (m->__type == PTHREAD_MUTEX_ERRORCHECK) + m->__owner = pthread_self(); + return 0; +} diff --git a/src/thread/pthread_mutex_unlock.c b/src/thread/pthread_mutex_unlock.c new file mode 100644 index 00000000..23e64ac8 --- /dev/null +++ b/src/thread/pthread_mutex_unlock.c @@ -0,0 +1,19 @@ +#include "pthread_impl.h" + +int pthread_mutex_unlock(pthread_mutex_t *m) +{ + if (m->__type == PTHREAD_MUTEX_RECURSIVE) { + if (a_fetch_add(&m->__lock, -1)==1 && m->__waiters) + __wake(&m->__lock, 1, 0); + return 0; + } + + if (m->__type == PTHREAD_MUTEX_ERRORCHECK + && m->__owner != pthread_self()) + return EPERM; + + m->__owner = 0; + m->__lock = 0; + if (m->__waiters) __wake(&m->__lock, 1, 0); + return 0; +} diff --git a/src/thread/pthread_mutexattr_destroy.c b/src/thread/pthread_mutexattr_destroy.c new file mode 100644 index 00000000..9fd69747 --- /dev/null +++ b/src/thread/pthread_mutexattr_destroy.c @@ -0,0 +1,6 @@ +#include "pthread_impl.h" + +int pthread_mutexattr_destroy(pthread_mutexattr_t *a) +{ + return 0; +} diff --git a/src/thread/pthread_mutexattr_gettype.c b/src/thread/pthread_mutexattr_gettype.c new file mode 100644 index 00000000..9edb16c6 --- /dev/null +++ b/src/thread/pthread_mutexattr_gettype.c @@ -0,0 +1,7 @@ +#include "pthread_impl.h" + +int pthread_mutexattr_gettype(const pthread_mutexattr_t *a, int *type) +{ + *type = *a & 3; + return 0; +} diff --git a/src/thread/pthread_mutexattr_init.c b/src/thread/pthread_mutexattr_init.c new file mode 100644 index 00000000..ea631069 --- /dev/null +++ b/src/thread/pthread_mutexattr_init.c @@ -0,0 +1,7 @@ +#include "pthread_impl.h" + +int pthread_mutexattr_init(pthread_mutexattr_t *a) +{ + memset(a, 0, sizeof *a); + return 0; +} diff --git a/src/thread/pthread_mutexattr_settype.c b/src/thread/pthread_mutexattr_settype.c new file mode 100644 index 00000000..4e85950e --- /dev/null +++ b/src/thread/pthread_mutexattr_settype.c @@ -0,0 +1,8 @@ +#include "pthread_impl.h" + +int pthread_mutexattr_settype(pthread_mutexattr_t *a, int type) +{ + if ((unsigned)type > 2) return EINVAL; + *a = (*a & ~3) | type; + return 0; +} diff --git a/src/thread/pthread_once.c b/src/thread/pthread_once.c new file mode 100644 index 00000000..72230054 --- /dev/null +++ b/src/thread/pthread_once.c @@ -0,0 +1,38 @@ +#include "pthread_impl.h" + +static void undo(void *control) +{ + a_store(control, 0); + __wake(control, 1, 0); +} + +int pthread_once(pthread_once_t *control, void (*init)(void)) +{ + static int waiters; + + /* Return immediately if init finished before */ + if (*control == 2) return 0; + + /* 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_swap(control, 1)) { + case 0: + break; + case 1: + __wait(control, &waiters, 1, 0); + continue; + case 2: + a_store(control, 2); + return 0; + } + + pthread_cleanup_push(undo, control); + init(); + pthread_cleanup_pop(0); + + if (waiters) __wake(control, -1, 0); + return 0; +} diff --git a/src/thread/pthread_rwlock_destroy.c b/src/thread/pthread_rwlock_destroy.c new file mode 100644 index 00000000..49ecfbd0 --- /dev/null +++ b/src/thread/pthread_rwlock_destroy.c @@ -0,0 +1,6 @@ +#include "pthread_impl.h" + +int pthread_rwlock_destroy(pthread_rwlock_t *rw) +{ + return 0; +} diff --git a/src/thread/pthread_rwlock_init.c b/src/thread/pthread_rwlock_init.c new file mode 100644 index 00000000..f87d566c --- /dev/null +++ b/src/thread/pthread_rwlock_init.c @@ -0,0 +1,9 @@ +#include "pthread_impl.h" + +int pthread_rwlock_init(pthread_rwlock_t *rw, const pthread_rwlockattr_t *a) +{ + memset(rw, 0, sizeof *rw); + if (a) { + } + return 0; +} diff --git a/src/thread/pthread_rwlock_rdlock.c b/src/thread/pthread_rwlock_rdlock.c new file mode 100644 index 00000000..6bcdb815 --- /dev/null +++ b/src/thread/pthread_rwlock_rdlock.c @@ -0,0 +1,8 @@ +#include "pthread_impl.h" + +int pthread_rwlock_rdlock(pthread_rwlock_t *rw) +{ + while (pthread_rwlock_tryrdlock(rw)) + __wait(&rw->__wrlock, &rw->__waiters, 1, 0); + return 0; +} diff --git a/src/thread/pthread_rwlock_timedrdlock.c b/src/thread/pthread_rwlock_timedrdlock.c new file mode 100644 index 00000000..290327de --- /dev/null +++ b/src/thread/pthread_rwlock_timedrdlock.c @@ -0,0 +1,15 @@ +#include "pthread_impl.h" + +int pthread_rwlock_timedrdlock(pthread_rwlock_t *rw, const struct timespec *at) +{ + int w=0; + while (pthread_rwlock_tryrdlock(rw)) { + if (!w) a_inc(&rw->__waiters), w++; + if (__timedwait(&rw->__wrlock, 1, CLOCK_REALTIME, at, 0)==ETIMEDOUT) { + if (w) a_dec(&rw->__waiters); + return ETIMEDOUT; + } + } + if (w) a_dec(&rw->__waiters); + return 0; +} diff --git a/src/thread/pthread_rwlock_timedwrlock.c b/src/thread/pthread_rwlock_timedwrlock.c new file mode 100644 index 00000000..9f749648 --- /dev/null +++ b/src/thread/pthread_rwlock_timedwrlock.c @@ -0,0 +1,17 @@ +#include "pthread_impl.h" + +int pthread_rwlock_timedwrlock(pthread_rwlock_t *rw, const struct timespec *at) +{ + int nr, *p, w=0; + while (pthread_rwlock_trywrlock(rw)==EAGAIN) { + if (!w) a_inc(&rw->__waiters), w++; + if ((nr=rw->__readers)) p = &rw->__readers; + else nr=1, p = &rw->__wrlock; + if (__timedwait(p, nr, CLOCK_REALTIME, at, 0)==ETIMEDOUT) { + if (w) a_dec(&rw->__waiters); + return ETIMEDOUT; + } + } + if (w) a_dec(&rw->__waiters); + return 0; +} diff --git a/src/thread/pthread_rwlock_tryrdlock.c b/src/thread/pthread_rwlock_tryrdlock.c new file mode 100644 index 00000000..f59fbbdd --- /dev/null +++ b/src/thread/pthread_rwlock_tryrdlock.c @@ -0,0 +1,13 @@ +#include "pthread_impl.h" + +int pthread_rwlock_tryrdlock(pthread_rwlock_t *rw) +{ + a_inc(&rw->__readers); + if (rw->__wrlock) { + a_dec(&rw->__readers); + if (rw->__waiters && !rw->__readers) + __wake(&rw->__readers, 1, 0); + return EAGAIN; + } + return 0; +} diff --git a/src/thread/pthread_rwlock_trywrlock.c b/src/thread/pthread_rwlock_trywrlock.c new file mode 100644 index 00000000..c029b874 --- /dev/null +++ b/src/thread/pthread_rwlock_trywrlock.c @@ -0,0 +1,13 @@ +#include "pthread_impl.h" + +int pthread_rwlock_trywrlock(pthread_rwlock_t *rw) +{ + if (a_xchg(&rw->__wrlock, 1)) + return EAGAIN; + if (rw->__readers) { + a_store(&rw->__wrlock, 0); + return EAGAIN; + } + rw->__owner = pthread_self()->tid; + return 0; +} diff --git a/src/thread/pthread_rwlock_unlock.c b/src/thread/pthread_rwlock_unlock.c new file mode 100644 index 00000000..f39117e7 --- /dev/null +++ b/src/thread/pthread_rwlock_unlock.c @@ -0,0 +1,17 @@ +#include "pthread_impl.h" + +int pthread_rwlock_unlock(pthread_rwlock_t *rw) +{ + struct pthread *self = pthread_self(); + if (rw->__owner == self->tid) { + rw->__owner = 0; + a_store(&rw->__wrlock, 0); + if (rw->__waiters) + __wake(&rw->__wrlock, -1, 0); + return 0; + } + a_dec(&rw->__readers); + if (rw->__waiters && !rw->__readers) + __wake(&rw->__readers, 1, 0); + return 0; +} diff --git a/src/thread/pthread_rwlock_wrlock.c b/src/thread/pthread_rwlock_wrlock.c new file mode 100644 index 00000000..219e924a --- /dev/null +++ b/src/thread/pthread_rwlock_wrlock.c @@ -0,0 +1,13 @@ +#include "pthread_impl.h" + +int pthread_rwlock_wrlock(pthread_rwlock_t *rw) +{ + int nr; + while (pthread_rwlock_trywrlock(rw)==EAGAIN) { + if ((nr=rw->__readers)) + __wait(&rw->__readers, &rw->__waiters, nr, 0); + else + __wait(&rw->__wrlock, &rw->__waiters, 1, 0); + } + return 0; +} diff --git a/src/thread/pthread_self.c b/src/thread/pthread_self.c new file mode 100644 index 00000000..686d73d5 --- /dev/null +++ b/src/thread/pthread_self.c @@ -0,0 +1,39 @@ +#include "pthread_impl.h" + +static struct pthread main_thread; + +#undef errno +static int *errno_location() +{ + return pthread_self()->errno_ptr; +} + +static int init_main_thread() +{ + main_thread.tlsdesc[0] = -1; + main_thread.tlsdesc[1] = (long)&main_thread; + main_thread.tlsdesc[2] = SIZE_MAX/PAGE_SIZE; + main_thread.tlsdesc[3] = 0x51; + main_thread.self = &main_thread; + main_thread.errno_ptr = __errno_location(); + if (__set_thread_area(main_thread.tlsdesc) < 0) + return -1; + syscall1(__NR_set_tid_address, (long)&main_thread.tid); + libc.errno_location = errno_location; + main_thread.tid = main_thread.pid = getpid(); + return 0; +} + +#undef pthread_self + +pthread_t pthread_self() +{ + static int init, failed; + if (!init) { + if (failed) return 0; + if (init_main_thread() < 0) failed = 1; + if (failed) return 0; + init = 1; + } + return __pthread_self(); +} diff --git a/src/thread/pthread_setcancelstate.c b/src/thread/pthread_setcancelstate.c new file mode 100644 index 00000000..23c38851 --- /dev/null +++ b/src/thread/pthread_setcancelstate.c @@ -0,0 +1,10 @@ +#include "pthread_impl.h" + +int pthread_setcancelstate(int new, int *old) +{ + struct pthread *self = pthread_self(); + if (old) *old = self->canceldisable; + if ((unsigned)new > 1) return EINVAL; + self->canceldisable = new; + return 0; +} diff --git a/src/thread/pthread_setcanceltype.c b/src/thread/pthread_setcanceltype.c new file mode 100644 index 00000000..c73db22f --- /dev/null +++ b/src/thread/pthread_setcanceltype.c @@ -0,0 +1,10 @@ +#include "pthread_impl.h" + +int pthread_setcanceltype(int new, int *old) +{ + struct pthread *self = pthread_self(); + if (old) *old = self->cancelasync; + if ((unsigned)new > 1) return EINVAL; + self->cancelasync = new; + return 0; +} diff --git a/src/thread/pthread_setspecific.c b/src/thread/pthread_setspecific.c new file mode 100644 index 00000000..171cef41 --- /dev/null +++ b/src/thread/pthread_setspecific.c @@ -0,0 +1,18 @@ +#include "pthread_impl.h" + +int pthread_setspecific(pthread_key_t k, const void *x) +{ + struct pthread *self = pthread_self(); + /* Handle the case of the main thread */ + if (!self->tsd) { + if (!x) return 0; + if (!(self->tsd = calloc(sizeof(void *), PTHREAD_KEYS_MAX))) + return ENOMEM; + } + /* Avoid unnecessary COW */ + if (self->tsd[k] != x) { + self->tsd[k] = (void *)x; + self->tsd_used = 1; + } + return 0; +} diff --git a/src/thread/pthread_spin_destroy.c b/src/thread/pthread_spin_destroy.c new file mode 100644 index 00000000..e65a820c --- /dev/null +++ b/src/thread/pthread_spin_destroy.c @@ -0,0 +1,6 @@ +#include "pthread_impl.h" + +int pthread_spin_destroy(pthread_spinlock_t *s) +{ + return 0; +} diff --git a/src/thread/pthread_spin_init.c b/src/thread/pthread_spin_init.c new file mode 100644 index 00000000..681881cf --- /dev/null +++ b/src/thread/pthread_spin_init.c @@ -0,0 +1,6 @@ +#include "pthread_impl.h" + +int pthread_spin_init(pthread_spinlock_t *s, int shared) +{ + return *s = 0; +} diff --git a/src/thread/pthread_spin_lock.c b/src/thread/pthread_spin_lock.c new file mode 100644 index 00000000..59fa6ea8 --- /dev/null +++ b/src/thread/pthread_spin_lock.c @@ -0,0 +1,7 @@ +#include "pthread_impl.h" + +int pthread_spin_lock(pthread_spinlock_t *s) +{ + while (a_xchg(s, 1)); + return 0; +} diff --git a/src/thread/pthread_spin_trylock.c b/src/thread/pthread_spin_trylock.c new file mode 100644 index 00000000..c12696b3 --- /dev/null +++ b/src/thread/pthread_spin_trylock.c @@ -0,0 +1,6 @@ +#include "pthread_impl.h" + +int pthread_spin_trylock(pthread_spinlock_t *s) +{ + return -a_xchg(s, 1) & EBUSY; +} diff --git a/src/thread/pthread_spin_unlock.c b/src/thread/pthread_spin_unlock.c new file mode 100644 index 00000000..a7eab334 --- /dev/null +++ b/src/thread/pthread_spin_unlock.c @@ -0,0 +1,6 @@ +#include "pthread_impl.h" + +int pthread_spin_unlock(pthread_spinlock_t *s) +{ + return *s = 0; +} diff --git a/src/thread/pthread_testcancel.c b/src/thread/pthread_testcancel.c new file mode 100644 index 00000000..774b7068 --- /dev/null +++ b/src/thread/pthread_testcancel.c @@ -0,0 +1,7 @@ +#include "pthread_impl.h" + +void pthread_testcancel() +{ + CANCELPT_BEGIN; + CANCELPT_END; +} |