diff options
| author | Rich Felker <dalias@aerifal.cx> | 2011-02-12 00:22:29 -0500 | 
|---|---|---|
| committer | Rich Felker <dalias@aerifal.cx> | 2011-02-12 00:22:29 -0500 | 
| commit | 0b44a0315b47dd8eced9f3b7f31580cf14bbfc01 (patch) | |
| tree | 6eaef0d8a720fa3da580de87b647fff796fe80b3 /src/thread | |
| download | musl-0b44a0315b47dd8eced9f3b7f31580cf14bbfc01.tar.gz | |
initial check-in, version 0.5.0v0.5.0
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; +} | 
