summaryrefslogtreecommitdiff
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
initial check-in, version 0.5.0v0.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;
+}