about summary refs log tree commit diff
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