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  | 
