diff options
Diffstat (limited to 'src/thread')
-rw-r--r-- | src/thread/__timedwait.c | 2 | ||||
-rw-r--r-- | src/thread/__timedwait_cp.c | 23 | ||||
-rw-r--r-- | src/thread/cancel_dummy.c | 8 | ||||
-rw-r--r-- | src/thread/cancel_impl.c | 70 | ||||
-rw-r--r-- | src/thread/i386/syscall_cp.s | 36 | ||||
-rw-r--r-- | src/thread/pthread_cancel.c | 7 | ||||
-rw-r--r-- | src/thread/pthread_cond_timedwait.c | 11 | ||||
-rw-r--r-- | src/thread/pthread_create.c | 48 | ||||
-rw-r--r-- | src/thread/pthread_join.c | 4 | ||||
-rw-r--r-- | src/thread/pthread_testcancel.c | 3 | ||||
-rw-r--r-- | src/thread/sem_timedwait.c | 5 | ||||
-rw-r--r-- | src/thread/syscall_cp.c | 0 | ||||
-rw-r--r-- | src/thread/x86_64/syscall_cp.s | 24 |
13 files changed, 182 insertions, 59 deletions
diff --git a/src/thread/__timedwait.c b/src/thread/__timedwait.c index 5c84e80c..b1d3af23 100644 --- a/src/thread/__timedwait.c +++ b/src/thread/__timedwait.c @@ -2,7 +2,7 @@ #include <errno.h> #include "futex.h" #include "syscall.h" -#include <stdio.h> + int __timedwait(volatile int *addr, int val, clockid_t clk, const struct timespec *at, int priv) { int r; diff --git a/src/thread/__timedwait_cp.c b/src/thread/__timedwait_cp.c new file mode 100644 index 00000000..c2890985 --- /dev/null +++ b/src/thread/__timedwait_cp.c @@ -0,0 +1,23 @@ +#include <time.h> +#include <errno.h> +#include "futex.h" +#include "syscall.h" + +int __timedwait_cp(volatile int *addr, int val, clockid_t clk, const struct timespec *at, int priv) +{ + int r; + 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; + r = -__syscall_cp(SYS_futex, (long)addr, FUTEX_WAIT | priv, val, at ? (long)&to : 0); + if (r == ETIMEDOUT || r == EINTR) return r; + return 0; +} diff --git a/src/thread/cancel_dummy.c b/src/thread/cancel_dummy.c new file mode 100644 index 00000000..a39117e7 --- /dev/null +++ b/src/thread/cancel_dummy.c @@ -0,0 +1,8 @@ +#include "pthread_impl.h" + +static long sccp(long nr, long u, long v, long w, long x, long y, long z) +{ + return (__syscall)(nr, u, v, w, x, y, z); +} + +weak_alias(sccp, __syscall_cp); diff --git a/src/thread/cancel_impl.c b/src/thread/cancel_impl.c new file mode 100644 index 00000000..5ce545d7 --- /dev/null +++ b/src/thread/cancel_impl.c @@ -0,0 +1,70 @@ +#include "pthread_impl.h" + +long __syscall_cp_asm(volatile void *, long, long, long, long, long, long, long); + +long (__syscall_cp)(long nr, long u, long v, long w, long x, long y, long z) +{ + pthread_t self; + uintptr_t old_sp, old_ip; + long r; + + if (!libc.lock || (self = __pthread_self())->canceldisable) + return __syscall(nr, u, v, w, x, y, z); + + old_sp = self->cp_sp; + old_ip = self->cp_ip; + self->cp_sp = 0; + self->cp_ip = 0; + r = __syscall_cp_asm(&self->cp_sp, nr, u, v, w, x, y, z); + self->cp_sp = old_sp; + self->cp_ip = old_ip; + if (r == -EINTR && self->cancel) pthread_exit(PTHREAD_CANCELED); + return r; +} + +static void cancel_handler(int sig, siginfo_t *si, void *ctx) +{ + pthread_t self = __pthread_self(); + ucontext_t *uc = ctx; + uintptr_t sp = ((uintptr_t *)&uc->uc_mcontext)[CANCEL_REG_SP]; + uintptr_t ip = ((uintptr_t *)&uc->uc_mcontext)[CANCEL_REG_IP]; + + if (!self->cancel || self->canceldisable) return; + + if (self->cancelasync) pthread_exit(PTHREAD_CANCELED); + + if (sp != self->cp_sp) { + if (!sp) return; + sigaddset(&uc->uc_sigmask, SIGCANCEL); + __syscall(SYS_tgkill, self->pid, self->tid, SIGCANCEL); + return; + } + + if (ip <= self->cp_ip) pthread_exit(PTHREAD_CANCELED); +} + +static void testcancel() +{ + pthread_t self = __pthread_self(); + if (self->cancel && !self->canceldisable) + pthread_exit(PTHREAD_CANCELED); +} + +static void init_cancellation() +{ + struct sigaction sa = { + .sa_flags = SA_SIGINFO | SA_RESTART, + .sa_sigaction = cancel_handler + }; + sigfillset(&sa.sa_mask); + __libc_sigaction(SIGCANCEL, &sa, 0); + libc.testcancel = testcancel; +} + +int pthread_cancel(pthread_t t) +{ + static pthread_once_t once; + pthread_once(&once, init_cancellation); + a_store(&t->cancel, 1); + return pthread_kill(t, SIGCANCEL); +} diff --git a/src/thread/i386/syscall_cp.s b/src/thread/i386/syscall_cp.s new file mode 100644 index 00000000..6f98a779 --- /dev/null +++ b/src/thread/i386/syscall_cp.s @@ -0,0 +1,36 @@ +.text +.global __syscall_cp_asm +.type __syscall_cp_asm,%function +__syscall_cp_asm: + pushl %ebx + pushl %esi + pushl %edi + pushl %ebp + leal 20(%esp),%ebp + call 1f +1: popl %eax + movl (%ebp),%ecx + addl $[1f-1b],%eax + movl %eax,4(%ecx) + movl %esp,(%ecx) + movl 8(%ecx),%eax + testl %eax,%eax + jnz 2f + movl 4(%ebp),%eax + movl 8(%ebp),%ebx + movl 12(%ebp),%ecx + movl 16(%ebp),%edx + movl 20(%ebp),%esi + movl 24(%ebp),%edi + movl 28(%ebp),%ebp +1: int $128 + popl %ebp + popl %edi + popl %esi + popl %ebx + ret +2: xorl %eax,%eax + movl %eax,4(%ecx) + movl %eax,(%ecx) + pushl $-1 + call pthread_exit diff --git a/src/thread/pthread_cancel.c b/src/thread/pthread_cancel.c deleted file mode 100644 index c497dbe6..00000000 --- a/src/thread/pthread_cancel.c +++ /dev/null @@ -1,7 +0,0 @@ -#include "pthread_impl.h" - -int pthread_cancel(pthread_t t) -{ - a_store(&t->cancel, 1); - return pthread_kill(t, SIGCANCEL); -} diff --git a/src/thread/pthread_cond_timedwait.c b/src/thread/pthread_cond_timedwait.c index 7a19fc55..13728282 100644 --- a/src/thread/pthread_cond_timedwait.c +++ b/src/thread/pthread_cond_timedwait.c @@ -8,22 +8,19 @@ static void relock(void *m) int pthread_cond_timedwait(pthread_cond_t *c, pthread_mutex_t *m, const struct timespec *ts) { int r, e=0; - CANCELPT_BEGIN; - CANCELPT_END; + + pthread_testcancel(); pthread_cleanup_push(relock, m); c->_c_block = 1; if ((r=pthread_mutex_unlock(m))) return r; - CANCELPT_BEGIN; - do e = __timedwait(&c->_c_block, 1, c->_c_clock, ts, 0); + do e = __timedwait_cp(&c->_c_block, 1, c->_c_clock, ts, 0); while (e == EINTR); - CANCELPT_END; pthread_cleanup_pop(0); if ((r=pthread_mutex_lock(m))) return r; - CANCELPT_BEGIN; - CANCELPT_END; + pthread_testcancel(); return e; } diff --git a/src/thread/pthread_create.c b/src/thread/pthread_create.c index a722a2d6..844d7bf9 100644 --- a/src/thread/pthread_create.c +++ b/src/thread/pthread_create.c @@ -19,6 +19,7 @@ weak_alias(dummy_1, __pthread_tsd_run_dtors); void __pthread_unwind_next(struct __ptcb *cb) { pthread_t self; + int n; if (cb->__next) longjmp((void *)cb->__next->__jb, 1); @@ -31,8 +32,9 @@ void __pthread_unwind_next(struct __ptcb *cb) /* Mark this thread dead before decrementing count */ self->dead = 1; - if (!a_fetch_add(&libc.threads_minus_1, -1)) - exit(0); + do n = libc.threads_minus_1; + while (n && a_cas(&libc.threads_minus_1, n, n-1)!=n); + if (!n) exit(0); if (self->detached && self->map_base) { __syscall(SYS_rt_sigprocmask, SIG_BLOCK, (long)(uint64_t[1]){-1},0,8); @@ -42,43 +44,16 @@ void __pthread_unwind_next(struct __ptcb *cb) __syscall(SYS_exit, 0); } -static void docancel(struct pthread *self) -{ - struct __ptcb cb = { .__next = self->cancelbuf }; - self->canceldisable = 1; - self->cancelasync = 0; - __pthread_unwind_next(&cb); -} - -static void cancel_handler(int sig, siginfo_t *si, void *ctx) -{ - struct pthread *self = __pthread_self(); - if (self->cancel && !self->canceldisable && - (self->cancelasync || (self->cancelpoint==1 && PC_AT_SYS(ctx)))) - docancel(self); -} - -static void cancelpt(int x) -{ - struct pthread *self = __pthread_self(); - if ((self->cancelpoint+=x)==1 && self->cancel - && x<2U && !self->canceldisable) docancel(self); -} - static void init_threads() { - struct sigaction sa = { .sa_flags = SA_SIGINFO | SA_RESTART }; + sigset_t set; libc.lock = __lock; libc.lockfile = __lockfile; - libc.cancelpt = cancelpt; - - sigemptyset(&sa.sa_mask); - sa.sa_sigaction = cancel_handler; - __libc_sigaction(SIGCANCEL, &sa, 0); - sigaddset(&sa.sa_mask, SIGSYSCALL); - sigaddset(&sa.sa_mask, SIGCANCEL); - __libc_sigprocmask(SIG_UNBLOCK, &sa.sa_mask, 0); + sigemptyset(&set); + sigaddset(&set, SIGSYSCALL); + sigaddset(&set, SIGCANCEL); + __libc_sigprocmask(SIG_UNBLOCK, &set, 0); } static int start(void *p) @@ -159,6 +134,9 @@ int pthread_create(pthread_t *res, const pthread_attr_t *attr, void *(*entry)(vo void pthread_exit(void *result) { struct pthread *self = pthread_self(); + struct __ptcb cb = { .__next = self->cancelbuf }; self->result = result; - docancel(self); + self->canceldisable = 1; + self->cancelasync = 0; + __pthread_unwind_next(&cb); } diff --git a/src/thread/pthread_join.c b/src/thread/pthread_join.c index 5210ed48..ba7bb7d5 100644 --- a/src/thread/pthread_join.c +++ b/src/thread/pthread_join.c @@ -3,9 +3,7 @@ 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 (tmp) __timedwait_cp(&t->tid, tmp, 0, 0, 1); if (res) *res = t->result; if (t->map_base) munmap(t->map_base, t->map_size); return 0; diff --git a/src/thread/pthread_testcancel.c b/src/thread/pthread_testcancel.c index 774b7068..c6b250b2 100644 --- a/src/thread/pthread_testcancel.c +++ b/src/thread/pthread_testcancel.c @@ -2,6 +2,5 @@ void pthread_testcancel() { - CANCELPT_BEGIN; - CANCELPT_END; + if (libc.testcancel) libc.testcancel(); } diff --git a/src/thread/sem_timedwait.c b/src/thread/sem_timedwait.c index 4f45c172..db05b417 100644 --- a/src/thread/sem_timedwait.c +++ b/src/thread/sem_timedwait.c @@ -21,19 +21,16 @@ int sem_timedwait(sem_t *sem, const struct timespec *at) a_inc(sem->__val+1); pthread_cleanup_push(cleanup, sem->__val+1) - CANCELPT_BEGIN; for (;;) { r = 0; if (!sem_trywait(sem)) break; - r = __timedwait(sem->__val, 0, CLOCK_REALTIME, at, 0); + r = __timedwait_cp(sem->__val, 0, CLOCK_REALTIME, at, 0); if (r) { errno = r; r = -1; break; } - CANCELPT_TRY; } - CANCELPT_END; pthread_cleanup_pop(1); diff --git a/src/thread/syscall_cp.c b/src/thread/syscall_cp.c new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/thread/syscall_cp.c diff --git a/src/thread/x86_64/syscall_cp.s b/src/thread/x86_64/syscall_cp.s new file mode 100644 index 00000000..1894ce19 --- /dev/null +++ b/src/thread/x86_64/syscall_cp.s @@ -0,0 +1,24 @@ +.text +.global __syscall_cp_asm +.type __syscall_cp_asm,%function +__syscall_cp_asm: + lea 1f(%rip),%rax + mov %rax,8(%rdi) + mov %rsp,(%rdi) + mov 16(%rdi),%eax + test %eax,%eax + jnz 2f + mov %rsi,%rax + mov %rdx,%rdi + mov %rcx,%rsi + mov %r8,%rdx + mov %r9,%r10 + mov 8(%rsp),%r8 + mov 16(%rsp),%r9 +1: syscall + ret +2: xor %edi,%edi + mov %rdi,8(%r10) + mov %rdi,(%r10) + dec %rdi + jmp pthread_exit |