summaryrefslogtreecommitdiff
path: root/src/thread
diff options
context:
space:
mode:
Diffstat (limited to 'src/thread')
-rw-r--r--src/thread/__timedwait.c2
-rw-r--r--src/thread/__timedwait_cp.c23
-rw-r--r--src/thread/cancel_dummy.c8
-rw-r--r--src/thread/cancel_impl.c70
-rw-r--r--src/thread/i386/syscall_cp.s36
-rw-r--r--src/thread/pthread_cancel.c7
-rw-r--r--src/thread/pthread_cond_timedwait.c11
-rw-r--r--src/thread/pthread_create.c48
-rw-r--r--src/thread/pthread_join.c4
-rw-r--r--src/thread/pthread_testcancel.c3
-rw-r--r--src/thread/sem_timedwait.c5
-rw-r--r--src/thread/syscall_cp.c0
-rw-r--r--src/thread/x86_64/syscall_cp.s24
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