about summary refs log tree commit diff
path: root/src/thread
diff options
context:
space:
mode:
authorRich Felker <dalias@aerifal.cx>2011-02-12 00:22:29 -0500
committerRich Felker <dalias@aerifal.cx>2011-02-12 00:22:29 -0500
commit0b44a0315b47dd8eced9f3b7f31580cf14bbfc01 (patch)
tree6eaef0d8a720fa3da580de87b647fff796fe80b3 /src/thread
downloadmusl-0b44a0315b47dd8eced9f3b7f31580cf14bbfc01.tar.gz
musl-0b44a0315b47dd8eced9f3b7f31580cf14bbfc01.tar.xz
musl-0b44a0315b47dd8eced9f3b7f31580cf14bbfc01.zip
initial check-in, version 0.5.0 v0.5.0
Diffstat (limited to 'src/thread')
-rw-r--r--src/thread/__futex.c8
-rw-r--r--src/thread/__lock.c12
-rw-r--r--src/thread/__set_thread_area.c9
-rw-r--r--src/thread/__timedwait.c21
-rw-r--r--src/thread/__unmapself.c0
-rw-r--r--src/thread/__wait.c16
-rw-r--r--src/thread/__wake.c9
-rw-r--r--src/thread/cancellation.c22
-rw-r--r--src/thread/clone.c26
-rw-r--r--src/thread/i386/__unmapself.s22
-rw-r--r--src/thread/i386/clone.s35
-rw-r--r--src/thread/pthread_attr_destroy.c6
-rw-r--r--src/thread/pthread_attr_getdetachstate.c7
-rw-r--r--src/thread/pthread_attr_getguardsize.c7
-rw-r--r--src/thread/pthread_attr_getscope.c6
-rw-r--r--src/thread/pthread_attr_getstacksize.c7
-rw-r--r--src/thread/pthread_attr_init.c7
-rw-r--r--src/thread/pthread_attr_setdetachstate.c7
-rw-r--r--src/thread/pthread_attr_setguardsize.c8
-rw-r--r--src/thread/pthread_attr_setscope.c6
-rw-r--r--src/thread/pthread_attr_setstacksize.c8
-rw-r--r--src/thread/pthread_barrier_destroy.c6
-rw-r--r--src/thread/pthread_barrier_init.c8
-rw-r--r--src/thread/pthread_barrier_wait.c31
-rw-r--r--src/thread/pthread_cancel.c7
-rw-r--r--src/thread/pthread_cond_broadcast.c8
-rw-r--r--src/thread/pthread_cond_destroy.c6
-rw-r--r--src/thread/pthread_cond_init.c7
-rw-r--r--src/thread/pthread_cond_signal.c8
-rw-r--r--src/thread/pthread_cond_timedwait.c26
-rw-r--r--src/thread/pthread_cond_wait.c6
-rw-r--r--src/thread/pthread_create.c189
-rw-r--r--src/thread/pthread_detach.c11
-rw-r--r--src/thread/pthread_equal.c6
-rw-r--r--src/thread/pthread_exit.c25
-rw-r--r--src/thread/pthread_getspecific.c8
-rw-r--r--src/thread/pthread_join.c12
-rw-r--r--src/thread/pthread_key_create.c25
-rw-r--r--src/thread/pthread_key_delete.c7
-rw-r--r--src/thread/pthread_kill.c7
-rw-r--r--src/thread/pthread_mutex_destroy.c6
-rw-r--r--src/thread/pthread_mutex_init.c9
-rw-r--r--src/thread/pthread_mutex_lock.c9
-rw-r--r--src/thread/pthread_mutex_timedlock.c15
-rw-r--r--src/thread/pthread_mutex_trylock.c28
-rw-r--r--src/thread/pthread_mutex_unlock.c19
-rw-r--r--src/thread/pthread_mutexattr_destroy.c6
-rw-r--r--src/thread/pthread_mutexattr_gettype.c7
-rw-r--r--src/thread/pthread_mutexattr_init.c7
-rw-r--r--src/thread/pthread_mutexattr_settype.c8
-rw-r--r--src/thread/pthread_once.c38
-rw-r--r--src/thread/pthread_rwlock_destroy.c6
-rw-r--r--src/thread/pthread_rwlock_init.c9
-rw-r--r--src/thread/pthread_rwlock_rdlock.c8
-rw-r--r--src/thread/pthread_rwlock_timedrdlock.c15
-rw-r--r--src/thread/pthread_rwlock_timedwrlock.c17
-rw-r--r--src/thread/pthread_rwlock_tryrdlock.c13
-rw-r--r--src/thread/pthread_rwlock_trywrlock.c13
-rw-r--r--src/thread/pthread_rwlock_unlock.c17
-rw-r--r--src/thread/pthread_rwlock_wrlock.c13
-rw-r--r--src/thread/pthread_self.c39
-rw-r--r--src/thread/pthread_setcancelstate.c10
-rw-r--r--src/thread/pthread_setcanceltype.c10
-rw-r--r--src/thread/pthread_setspecific.c18
-rw-r--r--src/thread/pthread_spin_destroy.c6
-rw-r--r--src/thread/pthread_spin_init.c6
-rw-r--r--src/thread/pthread_spin_lock.c7
-rw-r--r--src/thread/pthread_spin_trylock.c6
-rw-r--r--src/thread/pthread_spin_unlock.c6
-rw-r--r--src/thread/pthread_testcancel.c7
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;
+}