summaryrefslogtreecommitdiffstats
path: root/libc-top-half/musl/src/thread
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 13:54:38 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 13:54:38 +0000
commit8c1ab65c0f548d20b7f177bdb736daaf603340e1 (patch)
treedf55b7e75bf43f2bf500845b105afe3ac3a5157e /libc-top-half/musl/src/thread
parentInitial commit. (diff)
downloadwasi-libc-b0f726bfa464c79fdf040fa7daed6094ddbffb4c.tar.xz
wasi-libc-b0f726bfa464c79fdf040fa7daed6094ddbffb4c.zip
Adding upstream version 0.0~git20221206.8b7148f.upstream/0.0_git20221206.8b7148f
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'libc-top-half/musl/src/thread')
-rw-r--r--libc-top-half/musl/src/thread/__lock.c62
-rw-r--r--libc-top-half/musl/src/thread/__set_thread_area.c10
-rw-r--r--libc-top-half/musl/src/thread/__syscall_cp.c20
-rw-r--r--libc-top-half/musl/src/thread/__timedwait.c84
-rw-r--r--libc-top-half/musl/src/thread/__tls_get_addr.c7
-rw-r--r--libc-top-half/musl/src/thread/__unmapself.c24
-rw-r--r--libc-top-half/musl/src/thread/__wait.c55
-rw-r--r--libc-top-half/musl/src/thread/aarch64/__set_thread_area.s7
-rw-r--r--libc-top-half/musl/src/thread/aarch64/__unmapself.s7
-rw-r--r--libc-top-half/musl/src/thread/aarch64/clone.s30
-rw-r--r--libc-top-half/musl/src/thread/aarch64/syscall_cp.s32
-rw-r--r--libc-top-half/musl/src/thread/arm/__aeabi_read_tp.s10
-rw-r--r--libc-top-half/musl/src/thread/arm/__set_thread_area.c52
-rw-r--r--libc-top-half/musl/src/thread/arm/__unmapself.s9
-rw-r--r--libc-top-half/musl/src/thread/arm/atomics.s106
-rw-r--r--libc-top-half/musl/src/thread/arm/clone.s28
-rw-r--r--libc-top-half/musl/src/thread/arm/syscall_cp.s29
-rw-r--r--libc-top-half/musl/src/thread/call_once.c7
-rw-r--r--libc-top-half/musl/src/thread/clone.c7
-rw-r--r--libc-top-half/musl/src/thread/cnd_broadcast.c9
-rw-r--r--libc-top-half/musl/src/thread/cnd_destroy.c6
-rw-r--r--libc-top-half/musl/src/thread/cnd_init.c7
-rw-r--r--libc-top-half/musl/src/thread/cnd_signal.c9
-rw-r--r--libc-top-half/musl/src/thread/cnd_timedwait.c14
-rw-r--r--libc-top-half/musl/src/thread/cnd_wait.c9
-rw-r--r--libc-top-half/musl/src/thread/default_attr.c4
-rw-r--r--libc-top-half/musl/src/thread/i386/__set_thread_area.s47
-rw-r--r--libc-top-half/musl/src/thread/i386/__unmapself.s11
-rw-r--r--libc-top-half/musl/src/thread/i386/clone.s49
-rw-r--r--libc-top-half/musl/src/thread/i386/syscall_cp.s41
-rw-r--r--libc-top-half/musl/src/thread/i386/tls.s9
-rw-r--r--libc-top-half/musl/src/thread/lock_ptc.c18
-rw-r--r--libc-top-half/musl/src/thread/m68k/__m68k_read_tp.s8
-rw-r--r--libc-top-half/musl/src/thread/m68k/clone.s25
-rw-r--r--libc-top-half/musl/src/thread/m68k/syscall_cp.s26
-rw-r--r--libc-top-half/musl/src/thread/microblaze/__set_thread_area.s7
-rw-r--r--libc-top-half/musl/src/thread/microblaze/__unmapself.s8
-rw-r--r--libc-top-half/musl/src/thread/microblaze/clone.s30
-rw-r--r--libc-top-half/musl/src/thread/microblaze/syscall_cp.s27
-rw-r--r--libc-top-half/musl/src/thread/mips/__unmapself.s10
-rw-r--r--libc-top-half/musl/src/thread/mips/clone.s36
-rw-r--r--libc-top-half/musl/src/thread/mips/syscall_cp.s53
-rw-r--r--libc-top-half/musl/src/thread/mips64/__unmapself.s9
-rw-r--r--libc-top-half/musl/src/thread/mips64/clone.s34
-rw-r--r--libc-top-half/musl/src/thread/mips64/syscall_cp.s52
-rw-r--r--libc-top-half/musl/src/thread/mipsn32/__unmapself.s9
-rw-r--r--libc-top-half/musl/src/thread/mipsn32/clone.s34
-rw-r--r--libc-top-half/musl/src/thread/mipsn32/syscall_cp.s51
-rw-r--r--libc-top-half/musl/src/thread/mtx_destroy.c5
-rw-r--r--libc-top-half/musl/src/thread/mtx_init.c10
-rw-r--r--libc-top-half/musl/src/thread/mtx_lock.c12
-rw-r--r--libc-top-half/musl/src/thread/mtx_timedlock.c13
-rw-r--r--libc-top-half/musl/src/thread/mtx_trylock.c15
-rw-r--r--libc-top-half/musl/src/thread/mtx_unlock.c10
-rw-r--r--libc-top-half/musl/src/thread/or1k/__set_thread_area.s7
-rw-r--r--libc-top-half/musl/src/thread/or1k/__unmapself.s8
-rw-r--r--libc-top-half/musl/src/thread/or1k/clone.s31
-rw-r--r--libc-top-half/musl/src/thread/or1k/syscall_cp.s29
-rw-r--r--libc-top-half/musl/src/thread/powerpc/__set_thread_area.s12
-rw-r--r--libc-top-half/musl/src/thread/powerpc/__unmapself.s9
-rw-r--r--libc-top-half/musl/src/thread/powerpc/clone.s73
-rw-r--r--libc-top-half/musl/src/thread/powerpc/syscall_cp.s59
-rw-r--r--libc-top-half/musl/src/thread/powerpc64/__set_thread_area.s9
-rw-r--r--libc-top-half/musl/src/thread/powerpc64/__unmapself.s9
-rw-r--r--libc-top-half/musl/src/thread/powerpc64/clone.s48
-rw-r--r--libc-top-half/musl/src/thread/powerpc64/syscall_cp.s44
-rw-r--r--libc-top-half/musl/src/thread/pthread_atfork.c49
-rw-r--r--libc-top-half/musl/src/thread/pthread_attr_destroy.c6
-rw-r--r--libc-top-half/musl/src/thread/pthread_attr_get.c98
-rw-r--r--libc-top-half/musl/src/thread/pthread_attr_init.c11
-rw-r--r--libc-top-half/musl/src/thread/pthread_attr_setdetachstate.c8
-rw-r--r--libc-top-half/musl/src/thread/pthread_attr_setguardsize.c8
-rw-r--r--libc-top-half/musl/src/thread/pthread_attr_setinheritsched.c9
-rw-r--r--libc-top-half/musl/src/thread/pthread_attr_setschedparam.c7
-rw-r--r--libc-top-half/musl/src/thread/pthread_attr_setschedpolicy.c7
-rw-r--r--libc-top-half/musl/src/thread/pthread_attr_setscope.c13
-rw-r--r--libc-top-half/musl/src/thread/pthread_attr_setstack.c9
-rw-r--r--libc-top-half/musl/src/thread/pthread_attr_setstacksize.c9
-rw-r--r--libc-top-half/musl/src/thread/pthread_barrier_destroy.c15
-rw-r--r--libc-top-half/musl/src/thread/pthread_barrier_init.c8
-rw-r--r--libc-top-half/musl/src/thread/pthread_barrier_wait.c111
-rw-r--r--libc-top-half/musl/src/thread/pthread_barrierattr_destroy.c6
-rw-r--r--libc-top-half/musl/src/thread/pthread_barrierattr_init.c7
-rw-r--r--libc-top-half/musl/src/thread/pthread_barrierattr_setpshared.c8
-rw-r--r--libc-top-half/musl/src/thread/pthread_cancel.c101
-rw-r--r--libc-top-half/musl/src/thread/pthread_cleanup_push.c20
-rw-r--r--libc-top-half/musl/src/thread/pthread_cond_broadcast.c10
-rw-r--r--libc-top-half/musl/src/thread/pthread_cond_destroy.c14
-rw-r--r--libc-top-half/musl/src/thread/pthread_cond_init.c11
-rw-r--r--libc-top-half/musl/src/thread/pthread_cond_signal.c10
-rw-r--r--libc-top-half/musl/src/thread/pthread_cond_timedwait.c230
-rw-r--r--libc-top-half/musl/src/thread/pthread_cond_wait.c6
-rw-r--r--libc-top-half/musl/src/thread/pthread_condattr_destroy.c6
-rw-r--r--libc-top-half/musl/src/thread/pthread_condattr_init.c7
-rw-r--r--libc-top-half/musl/src/thread/pthread_condattr_setclock.c21
-rw-r--r--libc-top-half/musl/src/thread/pthread_condattr_setpshared.c9
-rw-r--r--libc-top-half/musl/src/thread/pthread_create.c565
-rw-r--r--libc-top-half/musl/src/thread/pthread_detach.c14
-rw-r--r--libc-top-half/musl/src/thread/pthread_equal.c10
-rw-r--r--libc-top-half/musl/src/thread/pthread_getattr_np.c24
-rw-r--r--libc-top-half/musl/src/thread/pthread_getconcurrency.c6
-rw-r--r--libc-top-half/musl/src/thread/pthread_getcpuclockid.c7
-rw-r--r--libc-top-half/musl/src/thread/pthread_getname_np.c25
-rw-r--r--libc-top-half/musl/src/thread/pthread_getschedparam.c21
-rw-r--r--libc-top-half/musl/src/thread/pthread_getspecific.c11
-rw-r--r--libc-top-half/musl/src/thread/pthread_join.c46
-rw-r--r--libc-top-half/musl/src/thread/pthread_key_create.c95
-rw-r--r--libc-top-half/musl/src/thread/pthread_kill.c18
-rw-r--r--libc-top-half/musl/src/thread/pthread_mutex_consistent.c14
-rw-r--r--libc-top-half/musl/src/thread/pthread_mutex_destroy.c18
-rw-r--r--libc-top-half/musl/src/thread/pthread_mutex_getprioceiling.c6
-rw-r--r--libc-top-half/musl/src/thread/pthread_mutex_init.c8
-rw-r--r--libc-top-half/musl/src/thread/pthread_mutex_lock.c12
-rw-r--r--libc-top-half/musl/src/thread/pthread_mutex_setprioceiling.c6
-rw-r--r--libc-top-half/musl/src/thread/pthread_mutex_timedlock.c96
-rw-r--r--libc-top-half/musl/src/thread/pthread_mutex_trylock.c78
-rw-r--r--libc-top-half/musl/src/thread/pthread_mutex_unlock.c60
-rw-r--r--libc-top-half/musl/src/thread/pthread_mutexattr_destroy.c6
-rw-r--r--libc-top-half/musl/src/thread/pthread_mutexattr_init.c7
-rw-r--r--libc-top-half/musl/src/thread/pthread_mutexattr_setprotocol.c32
-rw-r--r--libc-top-half/musl/src/thread/pthread_mutexattr_setpshared.c9
-rw-r--r--libc-top-half/musl/src/thread/pthread_mutexattr_setrobust.c27
-rw-r--r--libc-top-half/musl/src/thread/pthread_mutexattr_settype.c8
-rw-r--r--libc-top-half/musl/src/thread/pthread_once.c50
-rw-r--r--libc-top-half/musl/src/thread/pthread_rwlock_destroy.c6
-rw-r--r--libc-top-half/musl/src/thread/pthread_rwlock_init.c8
-rw-r--r--libc-top-half/musl/src/thread/pthread_rwlock_rdlock.c8
-rw-r--r--libc-top-half/musl/src/thread/pthread_rwlock_timedrdlock.c25
-rw-r--r--libc-top-half/musl/src/thread/pthread_rwlock_timedwrlock.c25
-rw-r--r--libc-top-half/musl/src/thread/pthread_rwlock_tryrdlock.c15
-rw-r--r--libc-top-half/musl/src/thread/pthread_rwlock_trywrlock.c9
-rw-r--r--libc-top-half/musl/src/thread/pthread_rwlock_unlock.c20
-rw-r--r--libc-top-half/musl/src/thread/pthread_rwlock_wrlock.c8
-rw-r--r--libc-top-half/musl/src/thread/pthread_rwlockattr_destroy.c6
-rw-r--r--libc-top-half/musl/src/thread/pthread_rwlockattr_init.c7
-rw-r--r--libc-top-half/musl/src/thread/pthread_rwlockattr_setpshared.c8
-rw-r--r--libc-top-half/musl/src/thread/pthread_self.c15
-rw-r--r--libc-top-half/musl/src/thread/pthread_setattr_default_np.c37
-rw-r--r--libc-top-half/musl/src/thread/pthread_setcancelstate.c14
-rw-r--r--libc-top-half/musl/src/thread/pthread_setcanceltype.c11
-rw-r--r--libc-top-half/musl/src/thread/pthread_setconcurrency.c9
-rw-r--r--libc-top-half/musl/src/thread/pthread_setname_np.c26
-rw-r--r--libc-top-half/musl/src/thread/pthread_setschedparam.c14
-rw-r--r--libc-top-half/musl/src/thread/pthread_setschedprio.c14
-rw-r--r--libc-top-half/musl/src/thread/pthread_setspecific.c12
-rw-r--r--libc-top-half/musl/src/thread/pthread_sigmask.c19
-rw-r--r--libc-top-half/musl/src/thread/pthread_spin_destroy.c6
-rw-r--r--libc-top-half/musl/src/thread/pthread_spin_init.c6
-rw-r--r--libc-top-half/musl/src/thread/pthread_spin_lock.c8
-rw-r--r--libc-top-half/musl/src/thread/pthread_spin_trylock.c7
-rw-r--r--libc-top-half/musl/src/thread/pthread_spin_unlock.c7
-rw-r--r--libc-top-half/musl/src/thread/pthread_testcancel.c14
-rw-r--r--libc-top-half/musl/src/thread/riscv64/__set_thread_area.s6
-rw-r--r--libc-top-half/musl/src/thread/riscv64/__unmapself.s7
-rw-r--r--libc-top-half/musl/src/thread/riscv64/clone.s34
-rw-r--r--libc-top-half/musl/src/thread/riscv64/syscall_cp.s29
-rw-r--r--libc-top-half/musl/src/thread/s390x/__set_thread_area.s10
-rw-r--r--libc-top-half/musl/src/thread/s390x/__tls_get_offset.s17
-rw-r--r--libc-top-half/musl/src/thread/s390x/__unmapself.s6
-rw-r--r--libc-top-half/musl/src/thread/s390x/clone.s54
-rw-r--r--libc-top-half/musl/src/thread/s390x/syscall_cp.s34
-rw-r--r--libc-top-half/musl/src/thread/sem_destroy.c6
-rw-r--r--libc-top-half/musl/src/thread/sem_getvalue.c8
-rw-r--r--libc-top-half/musl/src/thread/sem_init.c15
-rw-r--r--libc-top-half/musl/src/thread/sem_open.c182
-rw-r--r--libc-top-half/musl/src/thread/sem_post.c17
-rw-r--r--libc-top-half/musl/src/thread/sem_timedwait.c31
-rw-r--r--libc-top-half/musl/src/thread/sem_trywait.c13
-rw-r--r--libc-top-half/musl/src/thread/sem_unlink.c7
-rw-r--r--libc-top-half/musl/src/thread/sem_wait.c6
-rw-r--r--libc-top-half/musl/src/thread/sh/__set_thread_area.c37
-rw-r--r--libc-top-half/musl/src/thread/sh/__unmapself.c24
-rw-r--r--libc-top-half/musl/src/thread/sh/__unmapself_mmu.s23
-rw-r--r--libc-top-half/musl/src/thread/sh/atomics.s65
-rw-r--r--libc-top-half/musl/src/thread/sh/clone.s54
-rw-r--r--libc-top-half/musl/src/thread/sh/syscall_cp.s45
-rw-r--r--libc-top-half/musl/src/thread/synccall.c120
-rw-r--r--libc-top-half/musl/src/thread/syscall_cp.c0
-rw-r--r--libc-top-half/musl/src/thread/thrd_create.c12
-rw-r--r--libc-top-half/musl/src/thread/thrd_exit.c8
-rw-r--r--libc-top-half/musl/src/thread/thrd_join.c11
-rw-r--r--libc-top-half/musl/src/thread/thrd_sleep.c14
-rw-r--r--libc-top-half/musl/src/thread/thrd_yield.c7
-rw-r--r--libc-top-half/musl/src/thread/tls.c0
-rw-r--r--libc-top-half/musl/src/thread/tss_create.c10
-rw-r--r--libc-top-half/musl/src/thread/tss_delete.c7
-rw-r--r--libc-top-half/musl/src/thread/tss_set.c13
-rw-r--r--libc-top-half/musl/src/thread/vmlock.c23
-rw-r--r--libc-top-half/musl/src/thread/x32/__set_thread_area.s11
-rw-r--r--libc-top-half/musl/src/thread/x32/__unmapself.s10
-rw-r--r--libc-top-half/musl/src/thread/x32/clone.s26
-rw-r--r--libc-top-half/musl/src/thread/x32/syscall_cp.s31
-rw-r--r--libc-top-half/musl/src/thread/x86_64/__set_thread_area.s11
-rw-r--r--libc-top-half/musl/src/thread/x86_64/__unmapself.s10
-rw-r--r--libc-top-half/musl/src/thread/x86_64/clone.s28
-rw-r--r--libc-top-half/musl/src/thread/x86_64/syscall_cp.s31
196 files changed, 5231 insertions, 0 deletions
diff --git a/libc-top-half/musl/src/thread/__lock.c b/libc-top-half/musl/src/thread/__lock.c
new file mode 100644
index 0000000..60eece4
--- /dev/null
+++ b/libc-top-half/musl/src/thread/__lock.c
@@ -0,0 +1,62 @@
+#include "pthread_impl.h"
+
+/* This lock primitive combines a flag (in the sign bit) and a
+ * congestion count (= threads inside the critical section, CS) in a
+ * single int that is accessed through atomic operations. The states
+ * of the int for value x are:
+ *
+ * x == 0: unlocked and no thread inside the critical section
+ *
+ * x < 0: locked with a congestion of x-INT_MIN, including the thread
+ * that holds the lock
+ *
+ * x > 0: unlocked with a congestion of x
+ *
+ * or in an equivalent formulation x is the congestion count or'ed
+ * with INT_MIN as a lock flag.
+ */
+
+void __lock(volatile int *l)
+{
+ int need_locks = libc.need_locks;
+ if (!need_locks) return;
+ /* fast path: INT_MIN for the lock, +1 for the congestion */
+ int current = a_cas(l, 0, INT_MIN + 1);
+ if (need_locks < 0) libc.need_locks = 0;
+ if (!current) return;
+ /* A first spin loop, for medium congestion. */
+ for (unsigned i = 0; i < 10; ++i) {
+ if (current < 0) current -= INT_MIN + 1;
+ // assertion: current >= 0
+ int val = a_cas(l, current, INT_MIN + (current + 1));
+ if (val == current) return;
+ current = val;
+ }
+ // Spinning failed, so mark ourselves as being inside the CS.
+ current = a_fetch_add(l, 1) + 1;
+ /* The main lock acquisition loop for heavy congestion. The only
+ * change to the value performed inside that loop is a successful
+ * lock via the CAS that acquires the lock. */
+ for (;;) {
+ /* We can only go into wait, if we know that somebody holds the
+ * lock and will eventually wake us up, again. */
+ if (current < 0) {
+ __futexwait(l, current, 1);
+ current -= INT_MIN + 1;
+ }
+ /* assertion: current > 0, the count includes us already. */
+ int val = a_cas(l, current, INT_MIN + current);
+ if (val == current) return;
+ current = val;
+ }
+}
+
+void __unlock(volatile int *l)
+{
+ /* Check l[0] to see if we are multi-threaded. */
+ if (l[0] < 0) {
+ if (a_fetch_add(l, -(INT_MIN + 1)) != (INT_MIN + 1)) {
+ __wake(l, 1, 1);
+ }
+ }
+}
diff --git a/libc-top-half/musl/src/thread/__set_thread_area.c b/libc-top-half/musl/src/thread/__set_thread_area.c
new file mode 100644
index 0000000..152a6a2
--- /dev/null
+++ b/libc-top-half/musl/src/thread/__set_thread_area.c
@@ -0,0 +1,10 @@
+#include "pthread_impl.h"
+
+int __set_thread_area(void *p)
+{
+#ifdef SYS_set_thread_area
+ return __syscall(SYS_set_thread_area, p);
+#else
+ return -ENOSYS;
+#endif
+}
diff --git a/libc-top-half/musl/src/thread/__syscall_cp.c b/libc-top-half/musl/src/thread/__syscall_cp.c
new file mode 100644
index 0000000..42a0167
--- /dev/null
+++ b/libc-top-half/musl/src/thread/__syscall_cp.c
@@ -0,0 +1,20 @@
+#include "pthread_impl.h"
+#include "syscall.h"
+
+hidden long __syscall_cp_c();
+
+static long sccp(syscall_arg_t nr,
+ syscall_arg_t u, syscall_arg_t v, syscall_arg_t w,
+ syscall_arg_t x, syscall_arg_t y, syscall_arg_t z)
+{
+ return __syscall(nr, u, v, w, x, y, z);
+}
+
+weak_alias(sccp, __syscall_cp_c);
+
+long (__syscall_cp)(syscall_arg_t nr,
+ syscall_arg_t u, syscall_arg_t v, syscall_arg_t w,
+ syscall_arg_t x, syscall_arg_t y, syscall_arg_t z)
+{
+ return __syscall_cp_c(nr, u, v, w, x, y, z);
+}
diff --git a/libc-top-half/musl/src/thread/__timedwait.c b/libc-top-half/musl/src/thread/__timedwait.c
new file mode 100644
index 0000000..7d6f6be
--- /dev/null
+++ b/libc-top-half/musl/src/thread/__timedwait.c
@@ -0,0 +1,84 @@
+#include <pthread.h>
+#include <time.h>
+#include <errno.h>
+#include "futex.h"
+#include "syscall.h"
+#include "pthread_impl.h"
+
+#ifdef __wasilibc_unmodified_upstream
+#define IS32BIT(x) !((x)+0x80000000ULL>>32)
+#define CLAMP(x) (int)(IS32BIT(x) ? (x) : 0x7fffffffU+((0ULL+(x))>>63))
+
+static int __futex4_cp(volatile void *addr, int op, int val, const struct timespec *to)
+{
+ int r;
+#ifdef SYS_futex_time64
+ time_t s = to ? to->tv_sec : 0;
+ long ns = to ? to->tv_nsec : 0;
+ r = -ENOSYS;
+ if (SYS_futex == SYS_futex_time64 || !IS32BIT(s))
+ r = __syscall_cp(SYS_futex_time64, addr, op, val,
+ to ? ((long long[]){s, ns}) : 0);
+ if (SYS_futex == SYS_futex_time64 || r!=-ENOSYS) return r;
+ to = to ? (void *)(long[]){CLAMP(s), ns} : 0;
+#endif
+ r = __syscall_cp(SYS_futex, addr, op, val, to);
+ if (r != -ENOSYS) return r;
+ return __syscall_cp(SYS_futex, addr, op & ~FUTEX_PRIVATE, val, to);
+}
+
+static volatile int dummy = 0;
+weak_alias(dummy, __eintr_valid_flag);
+#else
+static int __futex4_cp(volatile void *addr, int op, int val, const struct timespec *to)
+{
+ int64_t max_wait_ns = -1;
+ if (to) {
+ max_wait_ns = (int64_t)(to->tv_sec * 1000000000 + to->tv_nsec);
+ }
+ return __wasilibc_futex_wait(addr, op, val, max_wait_ns);
+}
+#endif
+
+int __timedwait_cp(volatile int *addr, int val,
+ clockid_t clk, const struct timespec *at, int priv)
+{
+ int r;
+ struct timespec to, *top=0;
+
+ if (priv) priv = FUTEX_PRIVATE;
+
+ if (at) {
+ if (at->tv_nsec >= 1000000000UL) return EINVAL;
+ if (__clock_gettime(clk, &to)) return EINVAL;
+ 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;
+ top = &to;
+ }
+
+ r = -__futex4_cp(addr, FUTEX_WAIT|priv, val, top);
+ if (r != EINTR && r != ETIMEDOUT && r != ECANCELED) r = 0;
+#ifdef __wasilibc_unmodified_upstream
+ /* Mitigate bug in old kernels wrongly reporting EINTR for non-
+ * interrupting (SA_RESTART) signal handlers. This is only practical
+ * when NO interrupting signal handlers have been installed, and
+ * works by sigaction tracking whether that's the case. */
+ if (r == EINTR && !__eintr_valid_flag) r = 0;
+#endif
+
+ return r;
+}
+
+int __timedwait(volatile int *addr, int val,
+ clockid_t clk, const struct timespec *at, int priv)
+{
+ int cs, r;
+ __pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
+ r = __timedwait_cp(addr, val, clk, at, priv);
+ __pthread_setcancelstate(cs, 0);
+ return r;
+}
diff --git a/libc-top-half/musl/src/thread/__tls_get_addr.c b/libc-top-half/musl/src/thread/__tls_get_addr.c
new file mode 100644
index 0000000..19524fe
--- /dev/null
+++ b/libc-top-half/musl/src/thread/__tls_get_addr.c
@@ -0,0 +1,7 @@
+#include "pthread_impl.h"
+
+void *__tls_get_addr(tls_mod_off_t *v)
+{
+ pthread_t self = __pthread_self();
+ return (void *)(self->dtv[v[0]] + v[1]);
+}
diff --git a/libc-top-half/musl/src/thread/__unmapself.c b/libc-top-half/musl/src/thread/__unmapself.c
new file mode 100644
index 0000000..31d94e6
--- /dev/null
+++ b/libc-top-half/musl/src/thread/__unmapself.c
@@ -0,0 +1,24 @@
+#include "pthread_impl.h"
+#include "atomic.h"
+#include "syscall.h"
+/* cheat and reuse CRTJMP macro from dynlink code */
+#include "dynlink.h"
+
+static void *unmap_base;
+static size_t unmap_size;
+static char shared_stack[256];
+
+static void do_unmap()
+{
+ __syscall(SYS_munmap, unmap_base, unmap_size);
+ __syscall(SYS_exit);
+}
+
+void __unmapself(void *base, size_t size)
+{
+ char *stack = shared_stack + sizeof shared_stack;
+ stack -= (uintptr_t)stack % 16;
+ unmap_base = base;
+ unmap_size = size;
+ CRTJMP(do_unmap, stack);
+}
diff --git a/libc-top-half/musl/src/thread/__wait.c b/libc-top-half/musl/src/thread/__wait.c
new file mode 100644
index 0000000..c0e4aac
--- /dev/null
+++ b/libc-top-half/musl/src/thread/__wait.c
@@ -0,0 +1,55 @@
+#include "pthread_impl.h"
+#ifndef __wasilibc_unmodified_upstream
+#include "assert.h"
+#endif
+
+#ifndef __wasilibc_unmodified_upstream
+// Use WebAssembly's `wait` instruction to implement a futex. Note that `op` is
+// unused but retained as a parameter to match the original signature of the
+// syscall and that, for `max_wait_ns`, -1 (or any negative number) means wait
+// indefinitely.
+//
+// Adapted from Emscripten: see
+// https://github.com/emscripten-core/emscripten/blob/058a9fff/system/lib/pthread/emscripten_futex_wait.c#L111-L150.
+int __wasilibc_futex_wait(volatile void *addr, int op, int val, int64_t max_wait_ns)
+{
+ if ((((intptr_t)addr) & 3) != 0) {
+ return -EINVAL;
+ }
+
+ int ret = __builtin_wasm_memory_atomic_wait32((int *)addr, val, max_wait_ns);
+
+ // memory.atomic.wait32 returns:
+ // 0 => "ok", woken by another agent.
+ // 1 => "not-equal", loaded value != expected value
+ // 2 => "timed-out", the timeout expired
+ if (ret == 1) {
+ return -EWOULDBLOCK;
+ }
+ if (ret == 2) {
+ return -ETIMEDOUT;
+ }
+ assert(ret == 0);
+ return 0;
+}
+#endif
+
+void __wait(volatile int *addr, volatile int *waiters, int val, int priv)
+{
+ int spins=100;
+ if (priv) priv = FUTEX_PRIVATE;
+ while (spins-- && (!waiters || !*waiters)) {
+ if (*addr==val) a_spin();
+ else return;
+ }
+ if (waiters) a_inc(waiters);
+ while (*addr==val) {
+#ifdef __wasilibc_unmodified_upstream
+ __syscall(SYS_futex, addr, FUTEX_WAIT|priv, val, 0) != -ENOSYS
+ || __syscall(SYS_futex, addr, FUTEX_WAIT, val, 0);
+#else
+ __wasilibc_futex_wait(addr, FUTEX_WAIT, val, 0);
+#endif
+ }
+ if (waiters) a_dec(waiters);
+}
diff --git a/libc-top-half/musl/src/thread/aarch64/__set_thread_area.s b/libc-top-half/musl/src/thread/aarch64/__set_thread_area.s
new file mode 100644
index 0000000..fd0df34
--- /dev/null
+++ b/libc-top-half/musl/src/thread/aarch64/__set_thread_area.s
@@ -0,0 +1,7 @@
+.global __set_thread_area
+.hidden __set_thread_area
+.type __set_thread_area,@function
+__set_thread_area:
+ msr tpidr_el0,x0
+ mov w0,#0
+ ret
diff --git a/libc-top-half/musl/src/thread/aarch64/__unmapself.s b/libc-top-half/musl/src/thread/aarch64/__unmapself.s
new file mode 100644
index 0000000..2c5d254
--- /dev/null
+++ b/libc-top-half/musl/src/thread/aarch64/__unmapself.s
@@ -0,0 +1,7 @@
+.global __unmapself
+.type __unmapself,%function
+__unmapself:
+ mov x8,#215 // SYS_munmap
+ svc 0
+ mov x8,#93 // SYS_exit
+ svc 0
diff --git a/libc-top-half/musl/src/thread/aarch64/clone.s b/libc-top-half/musl/src/thread/aarch64/clone.s
new file mode 100644
index 0000000..e3c8339
--- /dev/null
+++ b/libc-top-half/musl/src/thread/aarch64/clone.s
@@ -0,0 +1,30 @@
+// __clone(func, stack, flags, arg, ptid, tls, ctid)
+// x0, x1, w2, x3, x4, x5, x6
+
+// syscall(SYS_clone, flags, stack, ptid, tls, ctid)
+// x8, x0, x1, x2, x3, x4
+
+.global __clone
+.hidden __clone
+.type __clone,%function
+__clone:
+ // align stack and save func,arg
+ and x1,x1,#-16
+ stp x0,x3,[x1,#-16]!
+
+ // syscall
+ uxtw x0,w2
+ mov x2,x4
+ mov x3,x5
+ mov x4,x6
+ mov x8,#220 // SYS_clone
+ svc #0
+
+ cbz x0,1f
+ // parent
+ ret
+ // child
+1: ldp x1,x0,[sp],#16
+ blr x1
+ mov x8,#93 // SYS_exit
+ svc #0
diff --git a/libc-top-half/musl/src/thread/aarch64/syscall_cp.s b/libc-top-half/musl/src/thread/aarch64/syscall_cp.s
new file mode 100644
index 0000000..41db68a
--- /dev/null
+++ b/libc-top-half/musl/src/thread/aarch64/syscall_cp.s
@@ -0,0 +1,32 @@
+// __syscall_cp_asm(&self->cancel, nr, u, v, w, x, y, z)
+// x0 x1 x2 x3 x4 x5 x6 x7
+
+// syscall(nr, u, v, w, x, y, z)
+// x8 x0 x1 x2 x3 x4 x5
+
+.global __cp_begin
+.hidden __cp_begin
+.global __cp_end
+.hidden __cp_end
+.global __cp_cancel
+.hidden __cp_cancel
+.hidden __cancel
+.global __syscall_cp_asm
+.hidden __syscall_cp_asm
+.type __syscall_cp_asm,%function
+__syscall_cp_asm:
+__cp_begin:
+ ldr w0,[x0]
+ cbnz w0,__cp_cancel
+ mov x8,x1
+ mov x0,x2
+ mov x1,x3
+ mov x2,x4
+ mov x3,x5
+ mov x4,x6
+ mov x5,x7
+ svc 0
+__cp_end:
+ ret
+__cp_cancel:
+ b __cancel
diff --git a/libc-top-half/musl/src/thread/arm/__aeabi_read_tp.s b/libc-top-half/musl/src/thread/arm/__aeabi_read_tp.s
new file mode 100644
index 0000000..2585620
--- /dev/null
+++ b/libc-top-half/musl/src/thread/arm/__aeabi_read_tp.s
@@ -0,0 +1,10 @@
+.syntax unified
+.global __aeabi_read_tp
+.type __aeabi_read_tp,%function
+__aeabi_read_tp:
+ ldr r0,1f
+ add r0,r0,pc
+ ldr r0,[r0]
+2: bx r0
+ .align 2
+1: .word __a_gettp_ptr - 2b
diff --git a/libc-top-half/musl/src/thread/arm/__set_thread_area.c b/libc-top-half/musl/src/thread/arm/__set_thread_area.c
new file mode 100644
index 0000000..09de65a
--- /dev/null
+++ b/libc-top-half/musl/src/thread/arm/__set_thread_area.c
@@ -0,0 +1,52 @@
+#include <stdint.h>
+#include <elf.h>
+#include "pthread_impl.h"
+#include "libc.h"
+
+#define HWCAP_TLS (1 << 15)
+
+extern hidden const unsigned char
+ __a_barrier_oldkuser[], __a_barrier_v6[], __a_barrier_v7[],
+ __a_cas_v6[], __a_cas_v7[],
+ __a_gettp_cp15[];
+
+#define __a_barrier_kuser 0xffff0fa0
+#define __a_barrier_oldkuser (uintptr_t)__a_barrier_oldkuser
+#define __a_barrier_v6 (uintptr_t)__a_barrier_v6
+#define __a_barrier_v7 (uintptr_t)__a_barrier_v7
+
+#define __a_cas_kuser 0xffff0fc0
+#define __a_cas_v6 (uintptr_t)__a_cas_v6
+#define __a_cas_v7 (uintptr_t)__a_cas_v7
+
+#define __a_gettp_kuser 0xffff0fe0
+#define __a_gettp_cp15 (uintptr_t)__a_gettp_cp15
+
+extern hidden uintptr_t __a_barrier_ptr, __a_cas_ptr, __a_gettp_ptr;
+
+int __set_thread_area(void *p)
+{
+#if !__ARM_ARCH_7A__ && !__ARM_ARCH_7R__ && __ARM_ARCH < 7
+ if (__hwcap & HWCAP_TLS) {
+ size_t *aux;
+ __a_cas_ptr = __a_cas_v7;
+ __a_barrier_ptr = __a_barrier_v7;
+ for (aux=libc.auxv; *aux; aux+=2) {
+ if (*aux != AT_PLATFORM) continue;
+ const char *s = (void *)aux[1];
+ if (s[0]!='v' || s[1]!='6' || s[2]-'0'<10u) break;
+ __a_cas_ptr = __a_cas_v6;
+ __a_barrier_ptr = __a_barrier_v6;
+ break;
+ }
+ } else {
+ int ver = *(int *)0xffff0ffc;
+ __a_gettp_ptr = __a_gettp_kuser;
+ __a_cas_ptr = __a_cas_kuser;
+ __a_barrier_ptr = __a_barrier_kuser;
+ if (ver < 2) a_crash();
+ if (ver < 3) __a_barrier_ptr = __a_barrier_oldkuser;
+ }
+#endif
+ return __syscall(0xf0005, p);
+}
diff --git a/libc-top-half/musl/src/thread/arm/__unmapself.s b/libc-top-half/musl/src/thread/arm/__unmapself.s
new file mode 100644
index 0000000..29c2d07
--- /dev/null
+++ b/libc-top-half/musl/src/thread/arm/__unmapself.s
@@ -0,0 +1,9 @@
+.syntax unified
+.text
+.global __unmapself
+.type __unmapself,%function
+__unmapself:
+ mov r7,#91
+ svc 0
+ mov r7,#1
+ svc 0
diff --git a/libc-top-half/musl/src/thread/arm/atomics.s b/libc-top-half/musl/src/thread/arm/atomics.s
new file mode 100644
index 0000000..da50508
--- /dev/null
+++ b/libc-top-half/musl/src/thread/arm/atomics.s
@@ -0,0 +1,106 @@
+.syntax unified
+.text
+
+.global __a_barrier_dummy
+.hidden __a_barrier_dummy
+.type __a_barrier_dummy,%function
+__a_barrier_dummy:
+ bx lr
+
+.global __a_barrier_oldkuser
+.hidden __a_barrier_oldkuser
+.type __a_barrier_oldkuser,%function
+__a_barrier_oldkuser:
+ push {r0,r1,r2,r3,ip,lr}
+ mov r1,r0
+ mov r2,sp
+ ldr ip,=0xffff0fc0
+ bl 1f
+ pop {r0,r1,r2,r3,ip,lr}
+ bx lr
+1: bx ip
+
+.global __a_barrier_v6
+.hidden __a_barrier_v6
+.type __a_barrier_v6,%function
+__a_barrier_v6:
+ .arch armv6t2
+ mcr p15,0,r0,c7,c10,5
+ bx lr
+
+.global __a_barrier_v7
+.hidden __a_barrier_v7
+.type __a_barrier_v7,%function
+__a_barrier_v7:
+ .arch armv7-a
+ dmb ish
+ bx lr
+
+.global __a_cas_dummy
+.hidden __a_cas_dummy
+.type __a_cas_dummy,%function
+__a_cas_dummy:
+ mov r3,r0
+ ldr r0,[r2]
+ subs r0,r3,r0
+ streq r1,[r2]
+ bx lr
+
+.global __a_cas_v6
+.hidden __a_cas_v6
+.type __a_cas_v6,%function
+__a_cas_v6:
+ .arch armv6t2
+ mov r3,r0
+ mcr p15,0,r0,c7,c10,5
+1: ldrex r0,[r2]
+ subs r0,r3,r0
+ strexeq r0,r1,[r2]
+ teqeq r0,#1
+ beq 1b
+ mcr p15,0,r0,c7,c10,5
+ bx lr
+
+.global __a_cas_v7
+.hidden __a_cas_v7
+.type __a_cas_v7,%function
+__a_cas_v7:
+ .arch armv7-a
+ mov r3,r0
+ dmb ish
+1: ldrex r0,[r2]
+ subs r0,r3,r0
+ strexeq r0,r1,[r2]
+ teqeq r0,#1
+ beq 1b
+ dmb ish
+ bx lr
+
+.global __a_gettp_cp15
+.hidden __a_gettp_cp15
+.type __a_gettp_cp15,%function
+__a_gettp_cp15:
+ mrc p15,0,r0,c13,c0,3
+ bx lr
+
+/* Tag this file with minimum ISA level so as not to affect linking. */
+.object_arch armv4t
+.eabi_attribute 6,2
+
+.data
+.align 2
+
+.global __a_barrier_ptr
+.hidden __a_barrier_ptr
+__a_barrier_ptr:
+ .word __a_barrier_dummy
+
+.global __a_cas_ptr
+.hidden __a_cas_ptr
+__a_cas_ptr:
+ .word __a_cas_dummy
+
+.global __a_gettp_ptr
+.hidden __a_gettp_ptr
+__a_gettp_ptr:
+ .word __a_gettp_cp15
diff --git a/libc-top-half/musl/src/thread/arm/clone.s b/libc-top-half/musl/src/thread/arm/clone.s
new file mode 100644
index 0000000..bb0965d
--- /dev/null
+++ b/libc-top-half/musl/src/thread/arm/clone.s
@@ -0,0 +1,28 @@
+.syntax unified
+.text
+.global __clone
+.hidden __clone
+.type __clone,%function
+__clone:
+ stmfd sp!,{r4,r5,r6,r7}
+ mov r7,#120
+ mov r6,r3
+ mov r5,r0
+ mov r0,r2
+ and r1,r1,#-16
+ ldr r2,[sp,#16]
+ ldr r3,[sp,#20]
+ ldr r4,[sp,#24]
+ svc 0
+ tst r0,r0
+ beq 1f
+ ldmfd sp!,{r4,r5,r6,r7}
+ bx lr
+
+1: mov r0,r6
+ bl 3f
+2: mov r7,#1
+ svc 0
+ b 2b
+
+3: bx r5
diff --git a/libc-top-half/musl/src/thread/arm/syscall_cp.s b/libc-top-half/musl/src/thread/arm/syscall_cp.s
new file mode 100644
index 0000000..e607dd4
--- /dev/null
+++ b/libc-top-half/musl/src/thread/arm/syscall_cp.s
@@ -0,0 +1,29 @@
+.syntax unified
+.global __cp_begin
+.hidden __cp_begin
+.global __cp_end
+.hidden __cp_end
+.global __cp_cancel
+.hidden __cp_cancel
+.hidden __cancel
+.global __syscall_cp_asm
+.hidden __syscall_cp_asm
+.type __syscall_cp_asm,%function
+__syscall_cp_asm:
+ mov ip,sp
+ stmfd sp!,{r4,r5,r6,r7}
+__cp_begin:
+ ldr r0,[r0]
+ cmp r0,#0
+ bne __cp_cancel
+ mov r7,r1
+ mov r0,r2
+ mov r1,r3
+ ldmfd ip,{r2,r3,r4,r5,r6}
+ svc 0
+__cp_end:
+ ldmfd sp!,{r4,r5,r6,r7}
+ bx lr
+__cp_cancel:
+ ldmfd sp!,{r4,r5,r6,r7}
+ b __cancel
diff --git a/libc-top-half/musl/src/thread/call_once.c b/libc-top-half/musl/src/thread/call_once.c
new file mode 100644
index 0000000..5ed3018
--- /dev/null
+++ b/libc-top-half/musl/src/thread/call_once.c
@@ -0,0 +1,7 @@
+#include <threads.h>
+#include <pthread.h>
+
+void call_once(once_flag *flag, void (*func)(void))
+{
+ __pthread_once(flag, func);
+}
diff --git a/libc-top-half/musl/src/thread/clone.c b/libc-top-half/musl/src/thread/clone.c
new file mode 100644
index 0000000..be80c8e
--- /dev/null
+++ b/libc-top-half/musl/src/thread/clone.c
@@ -0,0 +1,7 @@
+#include <errno.h>
+#include "pthread_impl.h"
+
+int __clone(int (*func)(void *), void *stack, int flags, void *arg, ...)
+{
+ return -ENOSYS;
+}
diff --git a/libc-top-half/musl/src/thread/cnd_broadcast.c b/libc-top-half/musl/src/thread/cnd_broadcast.c
new file mode 100644
index 0000000..e76b5a8
--- /dev/null
+++ b/libc-top-half/musl/src/thread/cnd_broadcast.c
@@ -0,0 +1,9 @@
+#include <threads.h>
+#include <pthread.h>
+
+int cnd_broadcast(cnd_t *c)
+{
+ /* This internal function never fails, and always returns zero,
+ * which matches the value thrd_success is defined with. */
+ return __private_cond_signal((pthread_cond_t *)c, -1);
+}
diff --git a/libc-top-half/musl/src/thread/cnd_destroy.c b/libc-top-half/musl/src/thread/cnd_destroy.c
new file mode 100644
index 0000000..453c90b
--- /dev/null
+++ b/libc-top-half/musl/src/thread/cnd_destroy.c
@@ -0,0 +1,6 @@
+#include <threads.h>
+
+void cnd_destroy(cnd_t *c)
+{
+ /* For private cv this is a no-op */
+}
diff --git a/libc-top-half/musl/src/thread/cnd_init.c b/libc-top-half/musl/src/thread/cnd_init.c
new file mode 100644
index 0000000..18c5085
--- /dev/null
+++ b/libc-top-half/musl/src/thread/cnd_init.c
@@ -0,0 +1,7 @@
+#include <threads.h>
+
+int cnd_init(cnd_t *c)
+{
+ *c = (cnd_t){ 0 };
+ return thrd_success;
+}
diff --git a/libc-top-half/musl/src/thread/cnd_signal.c b/libc-top-half/musl/src/thread/cnd_signal.c
new file mode 100644
index 0000000..02cdc6c
--- /dev/null
+++ b/libc-top-half/musl/src/thread/cnd_signal.c
@@ -0,0 +1,9 @@
+#include <threads.h>
+#include <pthread.h>
+
+int cnd_signal(cnd_t *c)
+{
+ /* This internal function never fails, and always returns zero,
+ * which matches the value thrd_success is defined with. */
+ return __private_cond_signal((pthread_cond_t *)c, 1);
+}
diff --git a/libc-top-half/musl/src/thread/cnd_timedwait.c b/libc-top-half/musl/src/thread/cnd_timedwait.c
new file mode 100644
index 0000000..2802af5
--- /dev/null
+++ b/libc-top-half/musl/src/thread/cnd_timedwait.c
@@ -0,0 +1,14 @@
+#include <threads.h>
+#include <pthread.h>
+#include <errno.h>
+
+int cnd_timedwait(cnd_t *restrict c, mtx_t *restrict m, const struct timespec *restrict ts)
+{
+ int ret = __pthread_cond_timedwait((pthread_cond_t *)c, (pthread_mutex_t *)m, ts);
+ switch (ret) {
+ /* May also return EINVAL or EPERM. */
+ default: return thrd_error;
+ case 0: return thrd_success;
+ case ETIMEDOUT: return thrd_timedout;
+ }
+}
diff --git a/libc-top-half/musl/src/thread/cnd_wait.c b/libc-top-half/musl/src/thread/cnd_wait.c
new file mode 100644
index 0000000..602796f
--- /dev/null
+++ b/libc-top-half/musl/src/thread/cnd_wait.c
@@ -0,0 +1,9 @@
+#include <threads.h>
+
+int cnd_wait(cnd_t *c, mtx_t *m)
+{
+ /* Calling cnd_timedwait with a null pointer is an extension.
+ * It is convenient here to avoid duplication of the logic
+ * for return values. */
+ return cnd_timedwait(c, m, 0);
+}
diff --git a/libc-top-half/musl/src/thread/default_attr.c b/libc-top-half/musl/src/thread/default_attr.c
new file mode 100644
index 0000000..dce9640
--- /dev/null
+++ b/libc-top-half/musl/src/thread/default_attr.c
@@ -0,0 +1,4 @@
+#include "pthread_impl.h"
+
+unsigned __default_stacksize = DEFAULT_STACK_SIZE;
+unsigned __default_guardsize = DEFAULT_GUARD_SIZE;
diff --git a/libc-top-half/musl/src/thread/i386/__set_thread_area.s b/libc-top-half/musl/src/thread/i386/__set_thread_area.s
new file mode 100644
index 0000000..aa6852b
--- /dev/null
+++ b/libc-top-half/musl/src/thread/i386/__set_thread_area.s
@@ -0,0 +1,47 @@
+.text
+.global __set_thread_area
+.hidden __set_thread_area
+.type __set_thread_area,@function
+__set_thread_area:
+ push %ebx
+ push $0x51
+ push $0xfffff
+ push 16(%esp)
+ call 1f
+1: addl $4f-1b,(%esp)
+ pop %ecx
+ mov (%ecx),%edx
+ push %edx
+ mov %esp,%ebx
+ xor %eax,%eax
+ mov $243,%al
+ int $128
+ testl %eax,%eax
+ jnz 2f
+ movl (%esp),%edx
+ movl %edx,(%ecx)
+ leal 3(,%edx,8),%edx
+3: movw %dx,%gs
+1:
+ addl $16,%esp
+ popl %ebx
+ ret
+2:
+ mov %ebx,%ecx
+ xor %eax,%eax
+ xor %ebx,%ebx
+ xor %edx,%edx
+ mov %ebx,(%esp)
+ mov $1,%bl
+ mov $16,%dl
+ mov $123,%al
+ int $128
+ testl %eax,%eax
+ jnz 1b
+ mov $7,%dl
+ inc %al
+ jmp 3b
+
+.data
+ .align 4
+4: .long -1
diff --git a/libc-top-half/musl/src/thread/i386/__unmapself.s b/libc-top-half/musl/src/thread/i386/__unmapself.s
new file mode 100644
index 0000000..d656959
--- /dev/null
+++ b/libc-top-half/musl/src/thread/i386/__unmapself.s
@@ -0,0 +1,11 @@
+.text
+.global __unmapself
+.type __unmapself,@function
+__unmapself:
+ movl $91,%eax
+ movl 4(%esp),%ebx
+ movl 8(%esp),%ecx
+ int $128
+ xorl %ebx,%ebx
+ movl $1,%eax
+ int $128
diff --git a/libc-top-half/musl/src/thread/i386/clone.s b/libc-top-half/musl/src/thread/i386/clone.s
new file mode 100644
index 0000000..e237d3c
--- /dev/null
+++ b/libc-top-half/musl/src/thread/i386/clone.s
@@ -0,0 +1,49 @@
+.text
+.global __clone
+.hidden __clone
+.type __clone,@function
+__clone:
+ push %ebp
+ mov %esp,%ebp
+ push %ebx
+ push %esi
+ push %edi
+
+ xor %eax,%eax
+ push $0x51
+ mov %gs,%ax
+ push $0xfffff
+ shr $3,%eax
+ push 28(%ebp)
+ push %eax
+ mov $120,%al
+
+ mov 12(%ebp),%ecx
+ mov 16(%ebp),%ebx
+ and $-16,%ecx
+ sub $16,%ecx
+ mov 20(%ebp),%edi
+ mov %edi,(%ecx)
+ mov 24(%ebp),%edx
+ mov %esp,%esi
+ mov 32(%ebp),%edi
+ mov 8(%ebp),%ebp
+ int $128
+ test %eax,%eax
+ jnz 1f
+
+ mov %ebp,%eax
+ xor %ebp,%ebp
+ call *%eax
+ mov %eax,%ebx
+ xor %eax,%eax
+ inc %eax
+ int $128
+ hlt
+
+1: add $16,%esp
+ pop %edi
+ pop %esi
+ pop %ebx
+ pop %ebp
+ ret
diff --git a/libc-top-half/musl/src/thread/i386/syscall_cp.s b/libc-top-half/musl/src/thread/i386/syscall_cp.s
new file mode 100644
index 0000000..7dce1eb
--- /dev/null
+++ b/libc-top-half/musl/src/thread/i386/syscall_cp.s
@@ -0,0 +1,41 @@
+.text
+.global __cp_begin
+.hidden __cp_begin
+.global __cp_end
+.hidden __cp_end
+.global __cp_cancel
+.hidden __cp_cancel
+.hidden __cancel
+.global __syscall_cp_asm
+.hidden __syscall_cp_asm
+.type __syscall_cp_asm,@function
+__syscall_cp_asm:
+ mov 4(%esp),%ecx
+ pushl %ebx
+ pushl %esi
+ pushl %edi
+ pushl %ebp
+__cp_begin:
+ movl (%ecx),%eax
+ testl %eax,%eax
+ jnz __cp_cancel
+ movl 24(%esp),%eax
+ movl 28(%esp),%ebx
+ movl 32(%esp),%ecx
+ movl 36(%esp),%edx
+ movl 40(%esp),%esi
+ movl 44(%esp),%edi
+ movl 48(%esp),%ebp
+ int $128
+__cp_end:
+ popl %ebp
+ popl %edi
+ popl %esi
+ popl %ebx
+ ret
+__cp_cancel:
+ popl %ebp
+ popl %edi
+ popl %esi
+ popl %ebx
+ jmp __cancel
diff --git a/libc-top-half/musl/src/thread/i386/tls.s b/libc-top-half/musl/src/thread/i386/tls.s
new file mode 100644
index 0000000..6e4c4cb
--- /dev/null
+++ b/libc-top-half/musl/src/thread/i386/tls.s
@@ -0,0 +1,9 @@
+.text
+.global ___tls_get_addr
+.type ___tls_get_addr,@function
+___tls_get_addr:
+ mov %gs:4,%edx
+ mov (%eax),%ecx
+ mov 4(%eax),%eax
+ add (%edx,%ecx,4),%eax
+ ret
diff --git a/libc-top-half/musl/src/thread/lock_ptc.c b/libc-top-half/musl/src/thread/lock_ptc.c
new file mode 100644
index 0000000..7adedab
--- /dev/null
+++ b/libc-top-half/musl/src/thread/lock_ptc.c
@@ -0,0 +1,18 @@
+#include <pthread.h>
+
+static pthread_rwlock_t lock = PTHREAD_RWLOCK_INITIALIZER;
+
+void __inhibit_ptc()
+{
+ pthread_rwlock_wrlock(&lock);
+}
+
+void __acquire_ptc()
+{
+ pthread_rwlock_rdlock(&lock);
+}
+
+void __release_ptc()
+{
+ pthread_rwlock_unlock(&lock);
+}
diff --git a/libc-top-half/musl/src/thread/m68k/__m68k_read_tp.s b/libc-top-half/musl/src/thread/m68k/__m68k_read_tp.s
new file mode 100644
index 0000000..86886da
--- /dev/null
+++ b/libc-top-half/musl/src/thread/m68k/__m68k_read_tp.s
@@ -0,0 +1,8 @@
+.text
+.global __m68k_read_tp
+.type __m68k_read_tp,@function
+__m68k_read_tp:
+ move.l #333,%d0
+ trap #0
+ move.l %d0,%a0
+ rts
diff --git a/libc-top-half/musl/src/thread/m68k/clone.s b/libc-top-half/musl/src/thread/m68k/clone.s
new file mode 100644
index 0000000..f6dfa06
--- /dev/null
+++ b/libc-top-half/musl/src/thread/m68k/clone.s
@@ -0,0 +1,25 @@
+.text
+.global __clone
+.hidden __clone
+.type __clone,@function
+__clone:
+ movem.l %d2-%d5,-(%sp)
+ move.l #120,%d0
+ move.l 28(%sp),%d1
+ move.l 24(%sp),%d2
+ and.l #-16,%d2
+ move.l 36(%sp),%d3
+ move.l 44(%sp),%d4
+ move.l 40(%sp),%d5
+ move.l 20(%sp),%a0
+ move.l 32(%sp),%a1
+ trap #0
+ tst.l %d0
+ beq 1f
+ movem.l (%sp)+,%d2-%d5
+ rts
+1: move.l %a1,-(%sp)
+ jsr (%a0)
+ move.l #1,%d0
+ trap #0
+ clr.b 0
diff --git a/libc-top-half/musl/src/thread/m68k/syscall_cp.s b/libc-top-half/musl/src/thread/m68k/syscall_cp.s
new file mode 100644
index 0000000..5628a89
--- /dev/null
+++ b/libc-top-half/musl/src/thread/m68k/syscall_cp.s
@@ -0,0 +1,26 @@
+.text
+.global __cp_begin
+.hidden __cp_begin
+.global __cp_end
+.hidden __cp_end
+.global __cp_cancel
+.hidden __cp_cancel
+.hidden __cancel
+.global __syscall_cp_asm
+.hidden __syscall_cp_asm
+.type __syscall_cp_asm,@function
+__syscall_cp_asm:
+ movem.l %d2-%d5,-(%sp)
+ movea.l 20(%sp),%a0
+__cp_begin:
+ move.l (%a0),%d0
+ bne __cp_cancel
+ movem.l 24(%sp),%d0-%d5/%a0
+ trap #0
+__cp_end:
+ movem.l (%sp)+,%d2-%d5
+ rts
+__cp_cancel:
+ movem.l (%sp)+,%d2-%d5
+ move.l __cancel-.-8,%a1
+ jmp (%pc,%a1)
diff --git a/libc-top-half/musl/src/thread/microblaze/__set_thread_area.s b/libc-top-half/musl/src/thread/microblaze/__set_thread_area.s
new file mode 100644
index 0000000..9a226a9
--- /dev/null
+++ b/libc-top-half/musl/src/thread/microblaze/__set_thread_area.s
@@ -0,0 +1,7 @@
+.global __set_thread_area
+.hidden __set_thread_area
+.type __set_thread_area,@function
+__set_thread_area:
+ ori r21, r5, 0
+ rtsd r15, 8
+ ori r3, r0, 0
diff --git a/libc-top-half/musl/src/thread/microblaze/__unmapself.s b/libc-top-half/musl/src/thread/microblaze/__unmapself.s
new file mode 100644
index 0000000..b180de6
--- /dev/null
+++ b/libc-top-half/musl/src/thread/microblaze/__unmapself.s
@@ -0,0 +1,8 @@
+.global __unmapself
+.type __unmapself,@function
+__unmapself:
+ ori r12, r0, 91
+ brki r14, 0x8
+ ori r12, r0, 1
+ brki r14, 0x8
+ nop
diff --git a/libc-top-half/musl/src/thread/microblaze/clone.s b/libc-top-half/musl/src/thread/microblaze/clone.s
new file mode 100644
index 0000000..b68cc5f
--- /dev/null
+++ b/libc-top-half/musl/src/thread/microblaze/clone.s
@@ -0,0 +1,30 @@
+.global __clone
+.hidden __clone
+.type __clone,@function
+
+# r5, r6, r7, r8, r9, r10, stack
+# fn, st, fl, ar, pt, tl, ct
+# fl, st, __, pt, ct, tl
+
+__clone:
+ andi r6, r6, -16
+ addi r6, r6, -16
+ swi r5, r6, 0
+ swi r8, r6, 4
+
+ ori r5, r7, 0
+ ori r8, r9, 0
+ lwi r9, r1, 28
+ ori r12, r0, 120
+
+ brki r14, 8
+ beqi r3, 1f
+ rtsd r15, 8
+ nop
+
+1: lwi r3, r1, 0
+ lwi r5, r1, 4
+ brald r15, r3
+ nop
+ ori r12, r0, 1
+ brki r14, 8
diff --git a/libc-top-half/musl/src/thread/microblaze/syscall_cp.s b/libc-top-half/musl/src/thread/microblaze/syscall_cp.s
new file mode 100644
index 0000000..b0df61c
--- /dev/null
+++ b/libc-top-half/musl/src/thread/microblaze/syscall_cp.s
@@ -0,0 +1,27 @@
+.global __cp_begin
+.hidden __cp_begin
+.global __cp_end
+.hidden __cp_end
+.global __cp_cancel
+.hidden __cp_cancel
+.hidden __cancel
+.global __syscall_cp_asm
+.hidden __syscall_cp_asm
+.type __syscall_cp_asm,@function
+__syscall_cp_asm:
+__cp_begin:
+ lwi r5, r5, 0
+ bnei r5, __cp_cancel
+ addi r12, r6, 0
+ add r5, r7, r0
+ add r6, r8, r0
+ add r7, r9, r0
+ add r8, r10, r0
+ lwi r9, r1, 28
+ lwi r10, r1, 32
+ brki r14, 0x8
+__cp_end:
+ rtsd r15, 8
+ nop
+__cp_cancel:
+ bri __cancel
diff --git a/libc-top-half/musl/src/thread/mips/__unmapself.s b/libc-top-half/musl/src/thread/mips/__unmapself.s
new file mode 100644
index 0000000..ba139dc
--- /dev/null
+++ b/libc-top-half/musl/src/thread/mips/__unmapself.s
@@ -0,0 +1,10 @@
+.set noreorder
+.global __unmapself
+.type __unmapself,@function
+__unmapself:
+ move $sp, $25
+ li $2, 4091
+ syscall
+ li $4, 0
+ li $2, 4001
+ syscall
diff --git a/libc-top-half/musl/src/thread/mips/clone.s b/libc-top-half/musl/src/thread/mips/clone.s
new file mode 100644
index 0000000..0446338
--- /dev/null
+++ b/libc-top-half/musl/src/thread/mips/clone.s
@@ -0,0 +1,36 @@
+.set noreorder
+.global __clone
+.hidden __clone
+.type __clone,@function
+__clone:
+ # Save function pointer and argument pointer on new thread stack
+ and $5, $5, -8
+ subu $5, $5, 16
+ sw $4, 0($5)
+ sw $7, 4($5)
+ # Shuffle (fn,sp,fl,arg,ptid,tls,ctid) to (fl,sp,ptid,tls,ctid)
+ move $4, $6
+ lw $6, 16($sp)
+ lw $7, 20($sp)
+ lw $9, 24($sp)
+ subu $sp, $sp, 16
+ sw $9, 16($sp)
+ li $2, 4120
+ syscall
+ beq $7, $0, 1f
+ nop
+ addu $sp, $sp, 16
+ jr $ra
+ subu $2, $0, $2
+1: beq $2, $0, 1f
+ nop
+ addu $sp, $sp, 16
+ jr $ra
+ nop
+1: lw $25, 0($sp)
+ lw $4, 4($sp)
+ jalr $25
+ nop
+ move $4, $2
+ li $2, 4001
+ syscall
diff --git a/libc-top-half/musl/src/thread/mips/syscall_cp.s b/libc-top-half/musl/src/thread/mips/syscall_cp.s
new file mode 100644
index 0000000..d284626
--- /dev/null
+++ b/libc-top-half/musl/src/thread/mips/syscall_cp.s
@@ -0,0 +1,53 @@
+.set noreorder
+
+.global __cp_begin
+.hidden __cp_begin
+.type __cp_begin,@function
+.global __cp_end
+.hidden __cp_end
+.type __cp_end,@function
+.global __cp_cancel
+.hidden __cp_cancel
+.type __cp_cancel,@function
+.hidden __cancel
+.global __syscall_cp_asm
+.hidden __syscall_cp_asm
+.type __syscall_cp_asm,@function
+__syscall_cp_asm:
+ subu $sp, $sp, 32
+__cp_begin:
+ lw $4, 0($4)
+ bne $4, $0, __cp_cancel
+ move $2, $5
+ move $4, $6
+ move $5, $7
+ lw $6, 48($sp)
+ lw $7, 52($sp)
+ lw $8, 56($sp)
+ lw $9, 60($sp)
+ lw $10,64($sp)
+ sw $8, 16($sp)
+ sw $9, 20($sp)
+ sw $10,24($sp)
+ sw $2, 28($sp)
+ lw $2, 28($sp)
+ syscall
+__cp_end:
+ beq $7, $0, 1f
+ addu $sp, $sp, 32
+ subu $2, $0, $2
+1: jr $ra
+ nop
+
+__cp_cancel:
+ move $2, $ra
+ bal 1f
+ addu $sp, $sp, 32
+ .gpword .
+ .gpword __cancel
+1: lw $3, ($ra)
+ subu $3, $ra, $3
+ lw $25, 4($ra)
+ addu $25, $25, $3
+ jr $25
+ move $ra, $2
diff --git a/libc-top-half/musl/src/thread/mips64/__unmapself.s b/libc-top-half/musl/src/thread/mips64/__unmapself.s
new file mode 100644
index 0000000..f6795cd
--- /dev/null
+++ b/libc-top-half/musl/src/thread/mips64/__unmapself.s
@@ -0,0 +1,9 @@
+.set noreorder
+.global __unmapself
+.type __unmapself, @function
+__unmapself:
+ li $2, 5011
+ syscall
+ li $4, 0
+ li $2, 5058
+ syscall
diff --git a/libc-top-half/musl/src/thread/mips64/clone.s b/libc-top-half/musl/src/thread/mips64/clone.s
new file mode 100644
index 0000000..2d86899
--- /dev/null
+++ b/libc-top-half/musl/src/thread/mips64/clone.s
@@ -0,0 +1,34 @@
+.set noreorder
+.global __clone
+.hidden __clone
+.type __clone,@function
+__clone:
+ # Save function pointer and argument pointer on new thread stack
+ and $5, $5, -16 # aligning stack to double word
+ dsubu $5, $5, 16
+ sd $4, 0($5) # save function pointer
+ sd $7, 8($5) # save argument pointer
+
+ # Shuffle (fn,sp,fl,arg,ptid,tls,ctid) to (fl,sp,ptid,tls,ctid)
+ # sys_clone(u64 flags, u64 ustack_base, u64 parent_tidptr, u64 child_tidptr, u64 tls)
+ move $4, $6
+ move $6, $8
+ move $7, $9
+ move $8, $10
+ li $2, 5055
+ syscall
+ beq $7, $0, 1f
+ nop
+ jr $ra
+ dsubu $2, $0, $2
+1: beq $2, $0, 1f
+ nop
+ jr $ra
+ nop
+1: ld $25, 0($sp) # function pointer
+ ld $4, 8($sp) # argument pointer
+ jalr $25 # call the user's function
+ nop
+ move $4, $2
+ li $2, 5058
+ syscall
diff --git a/libc-top-half/musl/src/thread/mips64/syscall_cp.s b/libc-top-half/musl/src/thread/mips64/syscall_cp.s
new file mode 100644
index 0000000..0d4ede7
--- /dev/null
+++ b/libc-top-half/musl/src/thread/mips64/syscall_cp.s
@@ -0,0 +1,52 @@
+.set noreorder
+.global __cp_begin
+.hidden __cp_begin
+.type __cp_begin,@function
+.global __cp_end
+.hidden __cp_end
+.type __cp_end,@function
+.global __cp_cancel
+.hidden __cp_cancel
+.type __cp_cancel,@function
+.global __cp_cancel_data
+.hidden __cp_cancel_data
+.type __cp_cancel_data,@function
+.hidden __cancel
+.global __syscall_cp_asm
+.hidden __syscall_cp_asm
+.type __syscall_cp_asm,@function
+__syscall_cp_asm:
+__cp_begin:
+ lw $4, 0($4)
+ bne $4, $0, __cp_cancel
+ move $2, $5
+ move $4, $6
+ move $5, $7
+ move $6, $8
+ move $7, $9
+ move $8, $10
+ move $9, $11
+ ld $10, 0($sp)
+ syscall
+__cp_end:
+ beq $7, $0, 1f
+ nop
+ dsubu $2, $0, $2
+1: jr $ra
+ nop
+
+ # if cancellation flag is 1 then call __cancel
+__cp_cancel:
+ move $2, $ra
+.align 8
+ bal 1f
+ nop
+__cp_cancel_data:
+ .gpdword __cp_cancel_data
+ .gpdword __cancel
+1: ld $3, ($ra)
+ dsubu $3, $ra, $3
+ ld $25, 8($ra)
+ daddu $25, $25, $3
+ jr $25
+ move $ra, $2
diff --git a/libc-top-half/musl/src/thread/mipsn32/__unmapself.s b/libc-top-half/musl/src/thread/mipsn32/__unmapself.s
new file mode 100644
index 0000000..4b032e5
--- /dev/null
+++ b/libc-top-half/musl/src/thread/mipsn32/__unmapself.s
@@ -0,0 +1,9 @@
+.set noreorder
+.global __unmapself
+.type __unmapself,@function
+__unmapself:
+ li $2, 6011
+ syscall
+ li $4, 0
+ li $2, 6058
+ syscall
diff --git a/libc-top-half/musl/src/thread/mipsn32/clone.s b/libc-top-half/musl/src/thread/mipsn32/clone.s
new file mode 100644
index 0000000..4d3c8c7
--- /dev/null
+++ b/libc-top-half/musl/src/thread/mipsn32/clone.s
@@ -0,0 +1,34 @@
+.set noreorder
+.global __clone
+.hidden __clone
+.type __clone,@function
+__clone:
+ # Save function pointer and argument pointer on new thread stack
+ and $5, $5, -16 # aligning stack to double word
+ subu $5, $5, 16
+ sw $4, 0($5) # save function pointer
+ sw $7, 4($5) # save argument pointer
+
+ # Shuffle (fn,sp,fl,arg,ptid,tls,ctid) to (fl,sp,ptid,tls,ctid)
+ # sys_clone(u64 flags, u64 ustack_base, u64 parent_tidptr, u64 child_tidptr, u64 tls)
+ move $4, $6
+ move $6, $8
+ move $7, $9
+ move $8, $10
+ li $2, 6055
+ syscall
+ beq $7, $0, 1f
+ nop
+ jr $ra
+ subu $2, $0, $2
+1: beq $2, $0, 1f
+ nop
+ jr $ra
+ nop
+1: lw $25, 0($sp) # function pointer
+ lw $4, 4($sp) # argument pointer
+ jalr $25 # call the user's function
+ nop
+ move $4, $2
+ li $2, 6058
+ syscall
diff --git a/libc-top-half/musl/src/thread/mipsn32/syscall_cp.s b/libc-top-half/musl/src/thread/mipsn32/syscall_cp.s
new file mode 100644
index 0000000..e85615b
--- /dev/null
+++ b/libc-top-half/musl/src/thread/mipsn32/syscall_cp.s
@@ -0,0 +1,51 @@
+.set noreorder
+.global __cp_begin
+.hidden __cp_begin
+.type __cp_begin,@function
+.global __cp_end
+.hidden __cp_end
+.type __cp_end,@function
+.global __cp_cancel
+.hidden __cp_cancel
+.type __cp_cancel,@function
+.global __cp_cancel_data
+.hidden __cp_cancel_data
+.type __cp_cancel_data,@function
+.hidden __cancel
+.global __syscall_cp_asm
+.hidden __syscall_cp_asm
+.type __syscall_cp_asm,@function
+__syscall_cp_asm:
+__cp_begin:
+ lw $4, 0($4)
+ bne $4, $0, __cp_cancel
+ move $2, $5
+ move $4, $6
+ move $5, $7
+ move $6, $8
+ move $7, $9
+ move $8, $10
+ move $9, $11
+ lw $10, 0($sp)
+ syscall
+__cp_end:
+ beq $7, $0, 1f
+ nop
+ subu $2, $0, $2
+1: jr $ra
+ nop
+
+ # if cancellation flag is 1 then call __cancel
+__cp_cancel:
+ move $2, $ra
+ bal 1f
+ nop
+__cp_cancel_data:
+ .gpword __cp_cancel_data
+ .gpword __cancel
+1: lw $3, 0($ra)
+ subu $3, $ra, $3
+ lw $25, 4($ra)
+ addu $25, $25, $3
+ jr $25
+ move $ra, $2
diff --git a/libc-top-half/musl/src/thread/mtx_destroy.c b/libc-top-half/musl/src/thread/mtx_destroy.c
new file mode 100644
index 0000000..40a0899
--- /dev/null
+++ b/libc-top-half/musl/src/thread/mtx_destroy.c
@@ -0,0 +1,5 @@
+#include <threads.h>
+
+void mtx_destroy(mtx_t *mtx)
+{
+}
diff --git a/libc-top-half/musl/src/thread/mtx_init.c b/libc-top-half/musl/src/thread/mtx_init.c
new file mode 100644
index 0000000..4826f76
--- /dev/null
+++ b/libc-top-half/musl/src/thread/mtx_init.c
@@ -0,0 +1,10 @@
+#include "pthread_impl.h"
+#include <threads.h>
+
+int mtx_init(mtx_t *m, int type)
+{
+ *m = (mtx_t){
+ ._m_type = ((type&mtx_recursive) ? PTHREAD_MUTEX_RECURSIVE : PTHREAD_MUTEX_NORMAL),
+ };
+ return thrd_success;
+}
diff --git a/libc-top-half/musl/src/thread/mtx_lock.c b/libc-top-half/musl/src/thread/mtx_lock.c
new file mode 100644
index 0000000..5c2415c
--- /dev/null
+++ b/libc-top-half/musl/src/thread/mtx_lock.c
@@ -0,0 +1,12 @@
+#include "pthread_impl.h"
+#include <threads.h>
+
+int mtx_lock(mtx_t *m)
+{
+ if (m->_m_type == PTHREAD_MUTEX_NORMAL && !a_cas(&m->_m_lock, 0, EBUSY))
+ return thrd_success;
+ /* Calling mtx_timedlock with a null pointer is an extension.
+ * It is convenient, here to avoid duplication of the logic
+ * for return values. */
+ return mtx_timedlock(m, 0);
+}
diff --git a/libc-top-half/musl/src/thread/mtx_timedlock.c b/libc-top-half/musl/src/thread/mtx_timedlock.c
new file mode 100644
index 0000000..d22c8cf
--- /dev/null
+++ b/libc-top-half/musl/src/thread/mtx_timedlock.c
@@ -0,0 +1,13 @@
+#include <threads.h>
+#include <pthread.h>
+#include <errno.h>
+
+int mtx_timedlock(mtx_t *restrict m, const struct timespec *restrict ts)
+{
+ int ret = __pthread_mutex_timedlock((pthread_mutex_t *)m, ts);
+ switch (ret) {
+ default: return thrd_error;
+ case 0: return thrd_success;
+ case ETIMEDOUT: return thrd_timedout;
+ }
+}
diff --git a/libc-top-half/musl/src/thread/mtx_trylock.c b/libc-top-half/musl/src/thread/mtx_trylock.c
new file mode 100644
index 0000000..40a8b8c
--- /dev/null
+++ b/libc-top-half/musl/src/thread/mtx_trylock.c
@@ -0,0 +1,15 @@
+#include "pthread_impl.h"
+#include <threads.h>
+
+int mtx_trylock(mtx_t *m)
+{
+ if (m->_m_type == PTHREAD_MUTEX_NORMAL)
+ return (a_cas(&m->_m_lock, 0, EBUSY) & EBUSY) ? thrd_busy : thrd_success;
+
+ int ret = __pthread_mutex_trylock((pthread_mutex_t *)m);
+ switch (ret) {
+ default: return thrd_error;
+ case 0: return thrd_success;
+ case EBUSY: return thrd_busy;
+ }
+}
diff --git a/libc-top-half/musl/src/thread/mtx_unlock.c b/libc-top-half/musl/src/thread/mtx_unlock.c
new file mode 100644
index 0000000..2e5c8cf
--- /dev/null
+++ b/libc-top-half/musl/src/thread/mtx_unlock.c
@@ -0,0 +1,10 @@
+#include <threads.h>
+#include <pthread.h>
+
+int mtx_unlock(mtx_t *mtx)
+{
+ /* The only cases where pthread_mutex_unlock can return an
+ * error are undefined behavior for C11 mtx_unlock, so we can
+ * assume it does not return an error and simply tail call. */
+ return __pthread_mutex_unlock((pthread_mutex_t *)mtx);
+}
diff --git a/libc-top-half/musl/src/thread/or1k/__set_thread_area.s b/libc-top-half/musl/src/thread/or1k/__set_thread_area.s
new file mode 100644
index 0000000..b9ffb93
--- /dev/null
+++ b/libc-top-half/musl/src/thread/or1k/__set_thread_area.s
@@ -0,0 +1,7 @@
+.global __set_thread_area
+.hidden __set_thread_area
+.type __set_thread_area,@function
+__set_thread_area:
+ l.ori r10, r3, 0
+ l.jr r9
+ l.ori r11, r0, 0
diff --git a/libc-top-half/musl/src/thread/or1k/__unmapself.s b/libc-top-half/musl/src/thread/or1k/__unmapself.s
new file mode 100644
index 0000000..6c0fa2a
--- /dev/null
+++ b/libc-top-half/musl/src/thread/or1k/__unmapself.s
@@ -0,0 +1,8 @@
+.global __unmapself
+.type __unmapself,@function
+__unmapself:
+ l.ori r11, r0, 215 /* __NR_munmap */
+ l.sys 1
+ l.ori r3, r0, 0
+ l.ori r11, r0, 93 /* __NR_exit */
+ l.sys 1
diff --git a/libc-top-half/musl/src/thread/or1k/clone.s b/libc-top-half/musl/src/thread/or1k/clone.s
new file mode 100644
index 0000000..2473ac2
--- /dev/null
+++ b/libc-top-half/musl/src/thread/or1k/clone.s
@@ -0,0 +1,31 @@
+/* int clone(fn, stack, flags, arg, ptid, tls, ctid)
+ * r3 r4 r5 r6 sp+0 sp+4 sp+8
+ * sys_clone(flags, stack, ptid, ctid, tls)
+ */
+.global __clone
+.hidden __clone
+.type __clone,@function
+__clone:
+ l.addi r4, r4, -8
+ l.sw 0(r4), r3
+ l.sw 4(r4), r6
+ /* (fn, st, fl, ar, pt, tl, ct) => (fl, st, pt, ct, tl) */
+ l.ori r3, r5, 0
+ l.lwz r5, 0(r1)
+ l.lwz r6, 8(r1)
+ l.lwz r7, 4(r1)
+ l.ori r11, r0, 220 /* __NR_clone */
+ l.sys 1
+
+ l.sfeqi r11, 0
+ l.bf 1f
+ l.nop
+ l.jr r9
+ l.nop
+
+1: l.lwz r11, 0(r1)
+ l.jalr r11
+ l.lwz r3, 4(r1)
+
+ l.ori r11, r0, 93 /* __NR_exit */
+ l.sys 1
diff --git a/libc-top-half/musl/src/thread/or1k/syscall_cp.s b/libc-top-half/musl/src/thread/or1k/syscall_cp.s
new file mode 100644
index 0000000..7951166
--- /dev/null
+++ b/libc-top-half/musl/src/thread/or1k/syscall_cp.s
@@ -0,0 +1,29 @@
+.global __cp_begin
+.hidden __cp_begin
+.global __cp_end
+.hidden __cp_end
+.global __cp_cancel
+.hidden __cp_cancel
+.hidden __cancel
+.global __syscall_cp_asm
+.hidden __syscall_cp_asm
+.type __syscall_cp_asm,@function
+__syscall_cp_asm:
+__cp_begin:
+ l.lwz r3, 0(r3)
+ l.sfeqi r3, 0
+ l.bnf __cp_cancel
+ l.ori r11, r4, 0
+ l.ori r3, r5, 0
+ l.ori r4, r6, 0
+ l.ori r5, r7, 0
+ l.ori r6, r8, 0
+ l.lwz r7, 0(r1)
+ l.lwz r8, 4(r1)
+ l.sys 1
+__cp_end:
+ l.jr r9
+ l.nop
+__cp_cancel:
+ l.j __cancel
+ l.nop
diff --git a/libc-top-half/musl/src/thread/powerpc/__set_thread_area.s b/libc-top-half/musl/src/thread/powerpc/__set_thread_area.s
new file mode 100644
index 0000000..86c498f
--- /dev/null
+++ b/libc-top-half/musl/src/thread/powerpc/__set_thread_area.s
@@ -0,0 +1,12 @@
+.text
+.global __set_thread_area
+.hidden __set_thread_area
+.type __set_thread_area, %function
+__set_thread_area:
+ # mov pointer in reg3 into r2
+ mr 2, 3
+ # put 0 into return reg
+ li 3, 0
+ # return
+ blr
+
diff --git a/libc-top-half/musl/src/thread/powerpc/__unmapself.s b/libc-top-half/musl/src/thread/powerpc/__unmapself.s
new file mode 100644
index 0000000..c9360b4
--- /dev/null
+++ b/libc-top-half/musl/src/thread/powerpc/__unmapself.s
@@ -0,0 +1,9 @@
+ .text
+ .global __unmapself
+ .type __unmapself,%function
+__unmapself:
+ li 0, 91 # __NR_munmap
+ sc
+ li 0, 1 #__NR_exit
+ sc
+ blr
diff --git a/libc-top-half/musl/src/thread/powerpc/clone.s b/libc-top-half/musl/src/thread/powerpc/clone.s
new file mode 100644
index 0000000..da13f44
--- /dev/null
+++ b/libc-top-half/musl/src/thread/powerpc/clone.s
@@ -0,0 +1,73 @@
+.text
+.global __clone
+.hidden __clone
+.type __clone, %function
+__clone:
+# int clone(fn, stack, flags, arg, ptid, tls, ctid)
+# a b c d e f g
+# 3 4 5 6 7 8 9
+# pseudo C code:
+# tid = syscall(SYS_clone,c,b,e,f,g);
+# if (!tid) syscall(SYS_exit, a(d));
+# return tid;
+
+# SYS_clone = 120
+# SYS_exit = 1
+
+# store non-volatile regs r30, r31 on stack in order to put our
+# start func and its arg there
+stwu 30, -16(1)
+stw 31, 4(1)
+
+# save r3 (func) into r30, and r6(arg) into r31
+mr 30, 3
+mr 31, 6
+
+# create initial stack frame for new thread
+clrrwi 4, 4, 4
+li 0, 0
+stwu 0, -16(4)
+
+#move c into first arg
+mr 3, 5
+#mr 4, 4
+mr 5, 7
+mr 6, 8
+mr 7, 9
+
+# move syscall number into r0
+li 0, 120
+
+sc
+
+# check for syscall error
+bns+ 1f # jump to label 1 if no summary overflow.
+#else
+neg 3, 3 #negate the result (errno)
+1:
+# compare sc result with 0
+cmpwi cr7, 3, 0
+
+# if not 0, jump to end
+bne cr7, 2f
+
+#else: we're the child
+#call funcptr: move arg (d) into r3
+mr 3, 31
+#move r30 (funcptr) into CTR reg
+mtctr 30
+# call CTR reg
+bctrl
+# mov SYS_exit into r0 (the exit param is already in r3)
+li 0, 1
+sc
+
+2:
+
+# restore stack
+lwz 30, 0(1)
+lwz 31, 4(1)
+addi 1, 1, 16
+
+blr
+
diff --git a/libc-top-half/musl/src/thread/powerpc/syscall_cp.s b/libc-top-half/musl/src/thread/powerpc/syscall_cp.s
new file mode 100644
index 0000000..77f8938
--- /dev/null
+++ b/libc-top-half/musl/src/thread/powerpc/syscall_cp.s
@@ -0,0 +1,59 @@
+.global __cp_begin
+.hidden __cp_begin
+.global __cp_end
+.hidden __cp_end
+.global __cp_cancel
+.hidden __cp_cancel
+.hidden __cancel
+.global __syscall_cp_asm
+.hidden __syscall_cp_asm
+
+#r0: volatile. may be modified during linkage.
+#r1: stack frame: 16 byte alignment.
+#r2: tls/thread pointer on pp32
+#r3,r4: return values, first args
+#r5-r10: args
+#r11-r12: volatile. may be modified during linkage
+#r13: "small data area" pointer
+#r14 - r30: local vars
+#r31: local or environment pointer
+
+#r1, r14-31: belong to the caller, must be saved and restored
+#r0, r3-r12, ctr, xer: volatile, not preserved
+#r0,r11,r12: may be altered by cross-module call,
+#"a func cannot depend on that these regs have the values placed by the caller"
+
+#the fields CR2,CR2,CR4 of the cond reg must be preserved
+#LR (link reg) shall contain the funcs return address
+ .text
+ .type __syscall_cp_asm,%function
+__syscall_cp_asm:
+ # at enter: r3 = pointer to self->cancel, r4: syscall no, r5: first arg, r6: 2nd, r7: 3rd, r8: 4th, r9: 5th, r10: 6th
+__cp_begin:
+ # r3 holds first argument, its a pointer to self->cancel.
+ # we must compare the dereferenced value with 0 and jump to __cancel if its not
+
+ lwz 0, 0(3) #deref pointer into r0
+
+ cmpwi cr7, 0, 0 #compare r0 with 0, store result in cr7.
+ beq+ cr7, 1f #jump to label 1 if r0 was 0
+
+ b __cp_cancel #else call cancel
+1:
+ #ok, the cancel flag was not set
+ # syscall: number goes to r0, the rest 3-8
+ mr 0, 4 # put the system call number into r0
+ mr 3, 5 # Shift the arguments: arg1
+ mr 4, 6 # arg2
+ mr 5, 7 # arg3
+ mr 6, 8 # arg4
+ mr 7, 9 # arg5
+ mr 8, 10 # arg6
+ sc
+__cp_end:
+ bnslr+ # return if no summary overflow.
+ #else negate result.
+ neg 3, 3
+ blr
+__cp_cancel:
+ b __cancel
diff --git a/libc-top-half/musl/src/thread/powerpc64/__set_thread_area.s b/libc-top-half/musl/src/thread/powerpc64/__set_thread_area.s
new file mode 100644
index 0000000..bb9c55d
--- /dev/null
+++ b/libc-top-half/musl/src/thread/powerpc64/__set_thread_area.s
@@ -0,0 +1,9 @@
+.text
+.global __set_thread_area
+.hidden __set_thread_area
+.type __set_thread_area, %function
+__set_thread_area:
+ mr 13, 3
+ li 3, 0
+ blr
+
diff --git a/libc-top-half/musl/src/thread/powerpc64/__unmapself.s b/libc-top-half/musl/src/thread/powerpc64/__unmapself.s
new file mode 100644
index 0000000..c9360b4
--- /dev/null
+++ b/libc-top-half/musl/src/thread/powerpc64/__unmapself.s
@@ -0,0 +1,9 @@
+ .text
+ .global __unmapself
+ .type __unmapself,%function
+__unmapself:
+ li 0, 91 # __NR_munmap
+ sc
+ li 0, 1 #__NR_exit
+ sc
+ blr
diff --git a/libc-top-half/musl/src/thread/powerpc64/clone.s b/libc-top-half/musl/src/thread/powerpc64/clone.s
new file mode 100644
index 0000000..41cb678
--- /dev/null
+++ b/libc-top-half/musl/src/thread/powerpc64/clone.s
@@ -0,0 +1,48 @@
+.text
+.global __clone
+.hidden __clone
+.type __clone, %function
+__clone:
+ # int clone(fn, stack, flags, arg, ptid, tls, ctid)
+ # a b c d e f g
+ # 3 4 5 6 7 8 9
+ # pseudo C code:
+ # tid = syscall(SYS_clone,c,b,e,f,g);
+ # if (!tid) syscall(SYS_exit, a(d));
+ # return tid;
+
+ # create initial stack frame for new thread
+ clrrdi 4, 4, 4
+ li 0, 0
+ stdu 0,-32(4)
+
+ # save fn and arg to child stack
+ std 3, 8(4)
+ std 6, 16(4)
+
+ # shuffle args into correct registers and call SYS_clone
+ mr 3, 5
+ #mr 4, 4
+ mr 5, 7
+ mr 6, 8
+ mr 7, 9
+ li 0, 120 # SYS_clone = 120
+ sc
+
+ # if error, negate return (errno)
+ bns+ 1f
+ neg 3, 3
+
+1: # if we're the parent, return
+ cmpwi cr7, 3, 0
+ bnelr cr7
+
+ # we're the child. call fn(arg)
+ ld 3, 16(1)
+ ld 12, 8(1)
+ mtctr 12
+ bctrl
+
+ # call SYS_exit. exit code is already in r3 from fn return value
+ li 0, 1 # SYS_exit = 1
+ sc
diff --git a/libc-top-half/musl/src/thread/powerpc64/syscall_cp.s b/libc-top-half/musl/src/thread/powerpc64/syscall_cp.s
new file mode 100644
index 0000000..ef50ed0
--- /dev/null
+++ b/libc-top-half/musl/src/thread/powerpc64/syscall_cp.s
@@ -0,0 +1,44 @@
+ .global __cp_begin
+ .hidden __cp_begin
+ .global __cp_end
+ .hidden __cp_end
+ .global __cp_cancel
+ .hidden __cp_cancel
+ .hidden __cancel
+ .global __syscall_cp_asm
+ .hidden __syscall_cp_asm
+ .text
+ .type __syscall_cp_asm,%function
+__syscall_cp_asm:
+ # at enter: r3 = pointer to self->cancel, r4: syscall no, r5: first arg, r6: 2nd, r7: 3rd, r8: 4th, r9: 5th, r10: 6th
+__cp_begin:
+ # if (self->cancel) goto __cp_cancel
+ lwz 0, 0(3)
+ cmpwi cr7, 0, 0
+ bne- cr7, __cp_cancel
+
+ # make syscall
+ mr 0, 4
+ mr 3, 5
+ mr 4, 6
+ mr 5, 7
+ mr 6, 8
+ mr 7, 9
+ mr 8, 10
+ sc
+
+__cp_end:
+ # return error ? -r3 : r3
+ bnslr+
+ neg 3, 3
+ blr
+
+__cp_cancel:
+ mflr 0
+ bl 1f
+ .long .TOC.-.
+1: mflr 3
+ lwa 2, 0(3)
+ add 2, 2, 3
+ mtlr 0
+ b __cancel
diff --git a/libc-top-half/musl/src/thread/pthread_atfork.c b/libc-top-half/musl/src/thread/pthread_atfork.c
new file mode 100644
index 0000000..7649740
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_atfork.c
@@ -0,0 +1,49 @@
+#include <pthread.h>
+#include "libc.h"
+#include "lock.h"
+
+static struct atfork_funcs {
+ void (*prepare)(void);
+ void (*parent)(void);
+ void (*child)(void);
+ struct atfork_funcs *prev, *next;
+} *funcs;
+
+static volatile int lock[1];
+
+void __fork_handler(int who)
+{
+ struct atfork_funcs *p;
+ if (!funcs) return;
+ if (who < 0) {
+ LOCK(lock);
+ for (p=funcs; p; p = p->next) {
+ if (p->prepare) p->prepare();
+ funcs = p;
+ }
+ } else {
+ for (p=funcs; p; p = p->prev) {
+ if (!who && p->parent) p->parent();
+ else if (who && p->child) p->child();
+ funcs = p;
+ }
+ UNLOCK(lock);
+ }
+}
+
+int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void))
+{
+ struct atfork_funcs *new = malloc(sizeof *new);
+ if (!new) return -1;
+
+ LOCK(lock);
+ new->next = funcs;
+ new->prev = 0;
+ new->prepare = prepare;
+ new->parent = parent;
+ new->child = child;
+ if (funcs) funcs->prev = new;
+ funcs = new;
+ UNLOCK(lock);
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_attr_destroy.c b/libc-top-half/musl/src/thread/pthread_attr_destroy.c
new file mode 100644
index 0000000..b5845dd
--- /dev/null
+++ b/libc-top-half/musl/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/libc-top-half/musl/src/thread/pthread_attr_get.c b/libc-top-half/musl/src/thread/pthread_attr_get.c
new file mode 100644
index 0000000..f12ff44
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_attr_get.c
@@ -0,0 +1,98 @@
+#include "pthread_impl.h"
+
+int pthread_attr_getdetachstate(const pthread_attr_t *a, int *state)
+{
+ *state = a->_a_detach;
+ return 0;
+}
+int pthread_attr_getguardsize(const pthread_attr_t *restrict a, size_t *restrict size)
+{
+ *size = a->_a_guardsize;
+ return 0;
+}
+
+int pthread_attr_getinheritsched(const pthread_attr_t *restrict a, int *restrict inherit)
+{
+ *inherit = a->_a_sched;
+ return 0;
+}
+
+int pthread_attr_getschedparam(const pthread_attr_t *restrict a, struct sched_param *restrict param)
+{
+ param->sched_priority = a->_a_prio;
+ return 0;
+}
+
+int pthread_attr_getschedpolicy(const pthread_attr_t *restrict a, int *restrict policy)
+{
+ *policy = a->_a_policy;
+ return 0;
+}
+
+int pthread_attr_getscope(const pthread_attr_t *restrict a, int *restrict scope)
+{
+ *scope = PTHREAD_SCOPE_SYSTEM;
+ return 0;
+}
+
+int pthread_attr_getstack(const pthread_attr_t *restrict a, void **restrict addr, size_t *restrict size)
+{
+ if (!a->_a_stackaddr)
+ return EINVAL;
+ *size = a->_a_stacksize;
+ *addr = (void *)(a->_a_stackaddr - *size);
+ return 0;
+}
+
+int pthread_attr_getstacksize(const pthread_attr_t *restrict a, size_t *restrict size)
+{
+ *size = a->_a_stacksize;
+ return 0;
+}
+
+int pthread_barrierattr_getpshared(const pthread_barrierattr_t *restrict a, int *restrict pshared)
+{
+ *pshared = !!a->__attr;
+ return 0;
+}
+
+int pthread_condattr_getclock(const pthread_condattr_t *restrict a, clockid_t *restrict clk)
+{
+ *clk = a->__attr & 0x7fffffff;
+ return 0;
+}
+
+int pthread_condattr_getpshared(const pthread_condattr_t *restrict a, int *restrict pshared)
+{
+ *pshared = a->__attr>>31;
+ return 0;
+}
+
+int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *restrict a, int *restrict protocol)
+{
+ *protocol = a->__attr / 8U % 2;
+ return 0;
+}
+int pthread_mutexattr_getpshared(const pthread_mutexattr_t *restrict a, int *restrict pshared)
+{
+ *pshared = a->__attr / 128U % 2;
+ return 0;
+}
+
+int pthread_mutexattr_getrobust(const pthread_mutexattr_t *restrict a, int *restrict robust)
+{
+ *robust = a->__attr / 4U % 2;
+ return 0;
+}
+
+int pthread_mutexattr_gettype(const pthread_mutexattr_t *restrict a, int *restrict type)
+{
+ *type = a->__attr & 3;
+ return 0;
+}
+
+int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *restrict a, int *restrict pshared)
+{
+ *pshared = a->__attr[0];
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_attr_init.c b/libc-top-half/musl/src/thread/pthread_attr_init.c
new file mode 100644
index 0000000..463a8d2
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_attr_init.c
@@ -0,0 +1,11 @@
+#include "pthread_impl.h"
+
+int pthread_attr_init(pthread_attr_t *a)
+{
+ *a = (pthread_attr_t){0};
+ __acquire_ptc();
+ a->_a_stacksize = __default_stacksize;
+ a->_a_guardsize = __default_guardsize;
+ __release_ptc();
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_attr_setdetachstate.c b/libc-top-half/musl/src/thread/pthread_attr_setdetachstate.c
new file mode 100644
index 0000000..1b71278
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_attr_setdetachstate.c
@@ -0,0 +1,8 @@
+#include "pthread_impl.h"
+
+int pthread_attr_setdetachstate(pthread_attr_t *a, int state)
+{
+ if (state > 1U) return EINVAL;
+ a->_a_detach = state;
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_attr_setguardsize.c b/libc-top-half/musl/src/thread/pthread_attr_setguardsize.c
new file mode 100644
index 0000000..1c5c60a
--- /dev/null
+++ b/libc-top-half/musl/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->_a_guardsize = size;
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_attr_setinheritsched.c b/libc-top-half/musl/src/thread/pthread_attr_setinheritsched.c
new file mode 100644
index 0000000..ca264be
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_attr_setinheritsched.c
@@ -0,0 +1,9 @@
+#include "pthread_impl.h"
+#include "syscall.h"
+
+int pthread_attr_setinheritsched(pthread_attr_t *a, int inherit)
+{
+ if (inherit > 1U) return EINVAL;
+ a->_a_sched = inherit;
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_attr_setschedparam.c b/libc-top-half/musl/src/thread/pthread_attr_setschedparam.c
new file mode 100644
index 0000000..d4c1204
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_attr_setschedparam.c
@@ -0,0 +1,7 @@
+#include "pthread_impl.h"
+
+int pthread_attr_setschedparam(pthread_attr_t *restrict a, const struct sched_param *restrict param)
+{
+ a->_a_prio = param->sched_priority;
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_attr_setschedpolicy.c b/libc-top-half/musl/src/thread/pthread_attr_setschedpolicy.c
new file mode 100644
index 0000000..bb71f39
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_attr_setschedpolicy.c
@@ -0,0 +1,7 @@
+#include "pthread_impl.h"
+
+int pthread_attr_setschedpolicy(pthread_attr_t *a, int policy)
+{
+ a->_a_policy = policy;
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_attr_setscope.c b/libc-top-half/musl/src/thread/pthread_attr_setscope.c
new file mode 100644
index 0000000..46b520c
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_attr_setscope.c
@@ -0,0 +1,13 @@
+#include "pthread_impl.h"
+
+int pthread_attr_setscope(pthread_attr_t *a, int scope)
+{
+ switch (scope) {
+ case PTHREAD_SCOPE_SYSTEM:
+ return 0;
+ case PTHREAD_SCOPE_PROCESS:
+ return ENOTSUP;
+ default:
+ return EINVAL;
+ }
+}
diff --git a/libc-top-half/musl/src/thread/pthread_attr_setstack.c b/libc-top-half/musl/src/thread/pthread_attr_setstack.c
new file mode 100644
index 0000000..1eddcbd
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_attr_setstack.c
@@ -0,0 +1,9 @@
+#include "pthread_impl.h"
+
+int pthread_attr_setstack(pthread_attr_t *a, void *addr, size_t size)
+{
+ if (size-PTHREAD_STACK_MIN > SIZE_MAX/4) return EINVAL;
+ a->_a_stackaddr = (size_t)addr + size;
+ a->_a_stacksize = size;
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_attr_setstacksize.c b/libc-top-half/musl/src/thread/pthread_attr_setstacksize.c
new file mode 100644
index 0000000..9c6a880
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_attr_setstacksize.c
@@ -0,0 +1,9 @@
+#include "pthread_impl.h"
+
+int pthread_attr_setstacksize(pthread_attr_t *a, size_t size)
+{
+ if (size-PTHREAD_STACK_MIN > SIZE_MAX/4) return EINVAL;
+ a->_a_stackaddr = 0;
+ a->_a_stacksize = size;
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_barrier_destroy.c b/libc-top-half/musl/src/thread/pthread_barrier_destroy.c
new file mode 100644
index 0000000..4ce0b2e
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_barrier_destroy.c
@@ -0,0 +1,15 @@
+#include "pthread_impl.h"
+
+int pthread_barrier_destroy(pthread_barrier_t *b)
+{
+ if (b->_b_limit < 0) {
+ if (b->_b_lock) {
+ int v;
+ a_or(&b->_b_lock, INT_MIN);
+ while ((v = b->_b_lock) & INT_MAX)
+ __wait(&b->_b_lock, 0, v, 0);
+ }
+ __vm_wait();
+ }
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_barrier_init.c b/libc-top-half/musl/src/thread/pthread_barrier_init.c
new file mode 100644
index 0000000..4c3cb28
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_barrier_init.c
@@ -0,0 +1,8 @@
+#include "pthread_impl.h"
+
+int pthread_barrier_init(pthread_barrier_t *restrict b, const pthread_barrierattr_t *restrict a, unsigned count)
+{
+ if (count-1 > INT_MAX-1) return EINVAL;
+ *b = (pthread_barrier_t){ ._b_limit = count-1 | (a?a->__attr:0) };
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_barrier_wait.c b/libc-top-half/musl/src/thread/pthread_barrier_wait.c
new file mode 100644
index 0000000..cc2a8bb
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_barrier_wait.c
@@ -0,0 +1,111 @@
+#include "pthread_impl.h"
+
+static int pshared_barrier_wait(pthread_barrier_t *b)
+{
+ int limit = (b->_b_limit & INT_MAX) + 1;
+ int ret = 0;
+ int v, w;
+
+ if (limit==1) return PTHREAD_BARRIER_SERIAL_THREAD;
+
+ while ((v=a_cas(&b->_b_lock, 0, limit)))
+ __wait(&b->_b_lock, &b->_b_waiters, v, 0);
+
+ /* Wait for <limit> threads to get to the barrier */
+ if (++b->_b_count == limit) {
+ a_store(&b->_b_count, 0);
+ ret = PTHREAD_BARRIER_SERIAL_THREAD;
+ if (b->_b_waiters2) __wake(&b->_b_count, -1, 0);
+ } else {
+ a_store(&b->_b_lock, 0);
+ if (b->_b_waiters) __wake(&b->_b_lock, 1, 0);
+ while ((v=b->_b_count)>0)
+ __wait(&b->_b_count, &b->_b_waiters2, v, 0);
+ }
+
+ __vm_lock();
+
+ /* Ensure all threads have a vm lock before proceeding */
+ if (a_fetch_add(&b->_b_count, -1)==1-limit) {
+ a_store(&b->_b_count, 0);
+ if (b->_b_waiters2) __wake(&b->_b_count, -1, 0);
+ } else {
+ while ((v=b->_b_count))
+ __wait(&b->_b_count, &b->_b_waiters2, v, 0);
+ }
+
+ /* Perform a recursive unlock suitable for self-sync'd destruction */
+ do {
+ v = b->_b_lock;
+ w = b->_b_waiters;
+ } while (a_cas(&b->_b_lock, v, v==INT_MIN+1 ? 0 : v-1) != v);
+
+ /* Wake a thread waiting to reuse or destroy the barrier */
+ if (v==INT_MIN+1 || (v==1 && w))
+ __wake(&b->_b_lock, 1, 0);
+
+ __vm_unlock();
+
+ return ret;
+}
+
+struct instance
+{
+ volatile int count;
+ volatile int last;
+ volatile int waiters;
+ volatile int finished;
+};
+
+int pthread_barrier_wait(pthread_barrier_t *b)
+{
+ int limit = b->_b_limit;
+ struct instance *inst;
+
+ /* Trivial case: count was set at 1 */
+ if (!limit) return PTHREAD_BARRIER_SERIAL_THREAD;
+
+ /* Process-shared barriers require a separate, inefficient wait */
+ if (limit < 0) return pshared_barrier_wait(b);
+
+ /* Otherwise we need a lock on the barrier object */
+ while (a_swap(&b->_b_lock, 1))
+ __wait(&b->_b_lock, &b->_b_waiters, 1, 1);
+ inst = b->_b_inst;
+
+ /* First thread to enter the barrier becomes the "instance owner" */
+ if (!inst) {
+ struct instance new_inst = { 0 };
+ int spins = 200;
+ b->_b_inst = inst = &new_inst;
+ a_store(&b->_b_lock, 0);
+ if (b->_b_waiters) __wake(&b->_b_lock, 1, 1);
+ while (spins-- && !inst->finished)
+ a_spin();
+ a_inc(&inst->finished);
+ while (inst->finished == 1)
+ __syscall(SYS_futex,&inst->finished,FUTEX_WAIT|FUTEX_PRIVATE,1,0) != -ENOSYS
+ || __syscall(SYS_futex,&inst->finished,FUTEX_WAIT,1,0);
+ return PTHREAD_BARRIER_SERIAL_THREAD;
+ }
+
+ /* Last thread to enter the barrier wakes all non-instance-owners */
+ if (++inst->count == limit) {
+ b->_b_inst = 0;
+ a_store(&b->_b_lock, 0);
+ if (b->_b_waiters) __wake(&b->_b_lock, 1, 1);
+ a_store(&inst->last, 1);
+ if (inst->waiters)
+ __wake(&inst->last, -1, 1);
+ } else {
+ a_store(&b->_b_lock, 0);
+ if (b->_b_waiters) __wake(&b->_b_lock, 1, 1);
+ __wait(&inst->last, &inst->waiters, 0, 1);
+ }
+
+ /* Last thread to exit the barrier wakes the instance owner */
+ if (a_fetch_add(&inst->count,-1)==1 && a_fetch_add(&inst->finished,1))
+ __wake(&inst->finished, 1, 1);
+
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_barrierattr_destroy.c b/libc-top-half/musl/src/thread/pthread_barrierattr_destroy.c
new file mode 100644
index 0000000..adec738
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_barrierattr_destroy.c
@@ -0,0 +1,6 @@
+#include "pthread_impl.h"
+
+int pthread_barrierattr_destroy(pthread_barrierattr_t *a)
+{
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_barrierattr_init.c b/libc-top-half/musl/src/thread/pthread_barrierattr_init.c
new file mode 100644
index 0000000..fa742bb
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_barrierattr_init.c
@@ -0,0 +1,7 @@
+#include "pthread_impl.h"
+
+int pthread_barrierattr_init(pthread_barrierattr_t *a)
+{
+ *a = (pthread_barrierattr_t){0};
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_barrierattr_setpshared.c b/libc-top-half/musl/src/thread/pthread_barrierattr_setpshared.c
new file mode 100644
index 0000000..c2d2929
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_barrierattr_setpshared.c
@@ -0,0 +1,8 @@
+#include "pthread_impl.h"
+
+int pthread_barrierattr_setpshared(pthread_barrierattr_t *a, int pshared)
+{
+ if (pshared > 1U) return EINVAL;
+ a->__attr = pshared ? INT_MIN : 0;
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_cancel.c b/libc-top-half/musl/src/thread/pthread_cancel.c
new file mode 100644
index 0000000..2f9d5e9
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_cancel.c
@@ -0,0 +1,101 @@
+#define _GNU_SOURCE
+#include <string.h>
+#include "pthread_impl.h"
+#include "syscall.h"
+
+hidden long __cancel(), __syscall_cp_asm(), __syscall_cp_c();
+
+long __cancel()
+{
+ pthread_t self = __pthread_self();
+ if (self->canceldisable == PTHREAD_CANCEL_ENABLE || self->cancelasync)
+ pthread_exit(PTHREAD_CANCELED);
+ self->canceldisable = PTHREAD_CANCEL_DISABLE;
+ return -ECANCELED;
+}
+
+long __syscall_cp_asm(volatile void *, syscall_arg_t,
+ syscall_arg_t, syscall_arg_t, syscall_arg_t,
+ syscall_arg_t, syscall_arg_t, syscall_arg_t);
+
+long __syscall_cp_c(syscall_arg_t nr,
+ syscall_arg_t u, syscall_arg_t v, syscall_arg_t w,
+ syscall_arg_t x, syscall_arg_t y, syscall_arg_t z)
+{
+ pthread_t self;
+ long r;
+ int st;
+
+ if ((st=(self=__pthread_self())->canceldisable)
+ && (st==PTHREAD_CANCEL_DISABLE || nr==SYS_close))
+ return __syscall(nr, u, v, w, x, y, z);
+
+ r = __syscall_cp_asm(&self->cancel, nr, u, v, w, x, y, z);
+ if (r==-EINTR && nr!=SYS_close && self->cancel &&
+ self->canceldisable != PTHREAD_CANCEL_DISABLE)
+ r = __cancel();
+ return r;
+}
+
+static void _sigaddset(sigset_t *set, int sig)
+{
+ unsigned s = sig-1;
+ set->__bits[s/8/sizeof *set->__bits] |= 1UL<<(s&8*sizeof *set->__bits-1);
+}
+
+extern hidden const char __cp_begin[1], __cp_end[1], __cp_cancel[1];
+
+static void cancel_handler(int sig, siginfo_t *si, void *ctx)
+{
+ pthread_t self = __pthread_self();
+ ucontext_t *uc = ctx;
+ uintptr_t pc = uc->uc_mcontext.MC_PC;
+
+ a_barrier();
+ if (!self->cancel || self->canceldisable == PTHREAD_CANCEL_DISABLE) return;
+
+ _sigaddset(&uc->uc_sigmask, SIGCANCEL);
+
+ if (self->cancelasync || pc >= (uintptr_t)__cp_begin && pc < (uintptr_t)__cp_end) {
+ uc->uc_mcontext.MC_PC = (uintptr_t)__cp_cancel;
+#ifdef CANCEL_GOT
+ uc->uc_mcontext.MC_GOT = CANCEL_GOT;
+#endif
+ return;
+ }
+
+ __syscall(SYS_tkill, self->tid, SIGCANCEL);
+}
+
+void __testcancel()
+{
+ pthread_t self = __pthread_self();
+ if (self->cancel && !self->canceldisable)
+ __cancel();
+}
+
+static void init_cancellation()
+{
+ struct sigaction sa = {
+ .sa_flags = SA_SIGINFO | SA_RESTART,
+ .sa_sigaction = cancel_handler
+ };
+ memset(&sa.sa_mask, -1, _NSIG/8);
+ __libc_sigaction(SIGCANCEL, &sa, 0);
+}
+
+int pthread_cancel(pthread_t t)
+{
+ static int init;
+ if (!init) {
+ init_cancellation();
+ init = 1;
+ }
+ a_store(&t->cancel, 1);
+ if (t == pthread_self()) {
+ if (t->canceldisable == PTHREAD_CANCEL_ENABLE && t->cancelasync)
+ pthread_exit(PTHREAD_CANCELED);
+ return 0;
+ }
+ return pthread_kill(t, SIGCANCEL);
+}
diff --git a/libc-top-half/musl/src/thread/pthread_cleanup_push.c b/libc-top-half/musl/src/thread/pthread_cleanup_push.c
new file mode 100644
index 0000000..9b21764
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_cleanup_push.c
@@ -0,0 +1,20 @@
+#include "pthread_impl.h"
+
+static void dummy(struct __ptcb *cb)
+{
+}
+weak_alias(dummy, __do_cleanup_push);
+weak_alias(dummy, __do_cleanup_pop);
+
+void _pthread_cleanup_push(struct __ptcb *cb, void (*f)(void *), void *x)
+{
+ cb->__f = f;
+ cb->__x = x;
+ __do_cleanup_push(cb);
+}
+
+void _pthread_cleanup_pop(struct __ptcb *cb, int run)
+{
+ __do_cleanup_pop(cb);
+ if (run) cb->__f(cb->__x);
+}
diff --git a/libc-top-half/musl/src/thread/pthread_cond_broadcast.c b/libc-top-half/musl/src/thread/pthread_cond_broadcast.c
new file mode 100644
index 0000000..6bfab78
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_cond_broadcast.c
@@ -0,0 +1,10 @@
+#include "pthread_impl.h"
+
+int pthread_cond_broadcast(pthread_cond_t *c)
+{
+ if (!c->_c_shared) return __private_cond_signal(c, -1);
+ if (!c->_c_waiters) return 0;
+ a_inc(&c->_c_seq);
+ __wake(&c->_c_seq, -1, 0);
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_cond_destroy.c b/libc-top-half/musl/src/thread/pthread_cond_destroy.c
new file mode 100644
index 0000000..8c55516
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_cond_destroy.c
@@ -0,0 +1,14 @@
+#include "pthread_impl.h"
+
+int pthread_cond_destroy(pthread_cond_t *c)
+{
+ if (c->_c_shared && c->_c_waiters) {
+ int cnt;
+ a_or(&c->_c_waiters, 0x80000000);
+ a_inc(&c->_c_seq);
+ __wake(&c->_c_seq, -1, 0);
+ while ((cnt = c->_c_waiters) & 0x7fffffff)
+ __wait(&c->_c_waiters, 0, cnt, 0);
+ }
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_cond_init.c b/libc-top-half/musl/src/thread/pthread_cond_init.c
new file mode 100644
index 0000000..8c484dd
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_cond_init.c
@@ -0,0 +1,11 @@
+#include "pthread_impl.h"
+
+int pthread_cond_init(pthread_cond_t *restrict c, const pthread_condattr_t *restrict a)
+{
+ *c = (pthread_cond_t){0};
+ if (a) {
+ c->_c_clock = a->__attr & 0x7fffffff;
+ if (a->__attr>>31) c->_c_shared = (void *)-1;
+ }
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_cond_signal.c b/libc-top-half/musl/src/thread/pthread_cond_signal.c
new file mode 100644
index 0000000..575ad54
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_cond_signal.c
@@ -0,0 +1,10 @@
+#include "pthread_impl.h"
+
+int pthread_cond_signal(pthread_cond_t *c)
+{
+ if (!c->_c_shared) return __private_cond_signal(c, 1);
+ if (!c->_c_waiters) return 0;
+ a_inc(&c->_c_seq);
+ __wake(&c->_c_seq, 1, 0);
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_cond_timedwait.c b/libc-top-half/musl/src/thread/pthread_cond_timedwait.c
new file mode 100644
index 0000000..ba985f9
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_cond_timedwait.c
@@ -0,0 +1,230 @@
+#include "pthread_impl.h"
+
+#ifndef __wasilibc_unmodified_upstream
+#include <common/clock.h>
+#endif
+
+/*
+ * struct waiter
+ *
+ * Waiter objects have automatic storage on the waiting thread, and
+ * are used in building a linked list representing waiters currently
+ * waiting on the condition variable or a group of waiters woken
+ * together by a broadcast or signal; in the case of signal, this is a
+ * degenerate list of one member.
+ *
+ * Waiter lists attached to the condition variable itself are
+ * protected by the lock on the cv. Detached waiter lists are never
+ * modified again, but can only be traversed in reverse order, and are
+ * protected by the "barrier" locks in each node, which are unlocked
+ * in turn to control wake order.
+ *
+ * Since process-shared cond var semantics do not necessarily allow
+ * one thread to see another's automatic storage (they may be in
+ * different processes), the waiter list is not used for the
+ * process-shared case, but the structure is still used to store data
+ * needed by the cancellation cleanup handler.
+ */
+
+struct waiter {
+ struct waiter *prev, *next;
+ volatile int state, barrier;
+ volatile int *notify;
+};
+
+/* Self-synchronized-destruction-safe lock functions */
+
+static inline void lock(volatile int *l)
+{
+ if (a_cas(l, 0, 1)) {
+ a_cas(l, 1, 2);
+ do __wait(l, 0, 2, 1);
+ while (a_cas(l, 0, 2));
+ }
+}
+
+static inline void unlock(volatile int *l)
+{
+ if (a_swap(l, 0)==2)
+ __wake(l, 1, 1);
+}
+
+static inline void unlock_requeue(volatile int *l, volatile int *r, int w)
+{
+ a_store(l, 0);
+#ifdef __wasilibc_unmodified_upstream
+ if (w) __wake(l, 1, 1);
+ else __syscall(SYS_futex, l, FUTEX_REQUEUE|FUTEX_PRIVATE, 0, 1, r) != -ENOSYS
+ || __syscall(SYS_futex, l, FUTEX_REQUEUE, 0, 1, r);
+#else
+ // Always wake due to lack of requeue system call in WASI
+ // This can impact the performance, so we might need to re-visit that decision
+ __wake(l, 1, 1);
+#endif
+}
+
+enum {
+ WAITING,
+ SIGNALED,
+ LEAVING,
+};
+
+int __pthread_cond_timedwait(pthread_cond_t *restrict c, pthread_mutex_t *restrict m, const struct timespec *restrict ts)
+{
+ struct waiter node = { 0 };
+ int e, seq, clock = c->_c_clock, cs, shared=0, oldstate, tmp;
+#ifndef __wasilibc_unmodified_upstream
+ struct __clockid clock_id = { .id = clock };
+#endif
+ volatile int *fut;
+
+ if ((m->_m_type&15) && (m->_m_lock&INT_MAX) != __pthread_self()->tid)
+ return EPERM;
+
+ if (ts && ts->tv_nsec >= 1000000000UL)
+ return EINVAL;
+
+ __pthread_testcancel();
+
+ if (c->_c_shared) {
+ shared = 1;
+ fut = &c->_c_seq;
+ seq = c->_c_seq;
+ a_inc(&c->_c_waiters);
+ } else {
+ lock(&c->_c_lock);
+
+ seq = node.barrier = 2;
+ fut = &node.barrier;
+ node.state = WAITING;
+ node.next = c->_c_head;
+ c->_c_head = &node;
+ if (!c->_c_tail) c->_c_tail = &node;
+ else node.next->prev = &node;
+
+ unlock(&c->_c_lock);
+ }
+
+ __pthread_mutex_unlock(m);
+
+ __pthread_setcancelstate(PTHREAD_CANCEL_MASKED, &cs);
+ if (cs == PTHREAD_CANCEL_DISABLE) __pthread_setcancelstate(cs, 0);
+
+#ifdef __wasilibc_unmodified_upstream
+ do e = __timedwait_cp(fut, seq, clock, ts, !shared);
+#else
+ do e = __timedwait_cp(fut, seq, &clock_id, ts, !shared);
+#endif
+ while (*fut==seq && (!e || e==EINTR));
+ if (e == EINTR) e = 0;
+
+ if (shared) {
+ /* Suppress cancellation if a signal was potentially
+ * consumed; this is a legitimate form of spurious
+ * wake even if not. */
+ if (e == ECANCELED && c->_c_seq != seq) e = 0;
+ if (a_fetch_add(&c->_c_waiters, -1) == -0x7fffffff)
+ __wake(&c->_c_waiters, 1, 0);
+ oldstate = WAITING;
+ goto relock;
+ }
+
+ oldstate = a_cas(&node.state, WAITING, LEAVING);
+
+ if (oldstate == WAITING) {
+ /* Access to cv object is valid because this waiter was not
+ * yet signaled and a new signal/broadcast cannot return
+ * after seeing a LEAVING waiter without getting notified
+ * via the futex notify below. */
+
+ lock(&c->_c_lock);
+
+ if (c->_c_head == &node) c->_c_head = node.next;
+ else if (node.prev) node.prev->next = node.next;
+ if (c->_c_tail == &node) c->_c_tail = node.prev;
+ else if (node.next) node.next->prev = node.prev;
+
+ unlock(&c->_c_lock);
+
+ if (node.notify) {
+ if (a_fetch_add(node.notify, -1)==1)
+ __wake(node.notify, 1, 1);
+ }
+ } else {
+ /* Lock barrier first to control wake order. */
+ lock(&node.barrier);
+ }
+
+relock:
+ /* Errors locking the mutex override any existing error or
+ * cancellation, since the caller must see them to know the
+ * state of the mutex. */
+ if ((tmp = pthread_mutex_lock(m))) e = tmp;
+
+ if (oldstate == WAITING) goto done;
+
+ if (!node.next && !(m->_m_type & 8))
+ a_inc(&m->_m_waiters);
+
+ /* Unlock the barrier that's holding back the next waiter, and
+ * either wake it or requeue it to the mutex. */
+ if (node.prev) {
+ int val = m->_m_lock;
+ if (val>0) a_cas(&m->_m_lock, val, val|0x80000000);
+ unlock_requeue(&node.prev->barrier, &m->_m_lock, m->_m_type & (8|128));
+ } else if (!(m->_m_type & 8)) {
+ a_dec(&m->_m_waiters);
+ }
+
+ /* Since a signal was consumed, cancellation is not permitted. */
+ if (e == ECANCELED) e = 0;
+
+done:
+ __pthread_setcancelstate(cs, 0);
+
+ if (e == ECANCELED) {
+ __pthread_testcancel();
+ __pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, 0);
+ }
+
+ return e;
+}
+
+int __private_cond_signal(pthread_cond_t *c, int n)
+{
+ struct waiter *p, *first=0;
+ volatile int ref = 0;
+ int cur;
+
+ lock(&c->_c_lock);
+ for (p=c->_c_tail; n && p; p=p->prev) {
+ if (a_cas(&p->state, WAITING, SIGNALED) != WAITING) {
+ ref++;
+ p->notify = &ref;
+ } else {
+ n--;
+ if (!first) first=p;
+ }
+ }
+ /* Split the list, leaving any remainder on the cv. */
+ if (p) {
+ if (p->next) p->next->prev = 0;
+ p->next = 0;
+ } else {
+ c->_c_head = 0;
+ }
+ c->_c_tail = p;
+ unlock(&c->_c_lock);
+
+ /* Wait for any waiters in the LEAVING state to remove
+ * themselves from the list before returning or allowing
+ * signaled threads to proceed. */
+ while ((cur = ref)) __wait(&ref, 0, cur, 1);
+
+ /* Allow first signaled waiter, if any, to proceed. */
+ if (first) unlock(&first->barrier);
+
+ return 0;
+}
+
+weak_alias(__pthread_cond_timedwait, pthread_cond_timedwait);
diff --git a/libc-top-half/musl/src/thread/pthread_cond_wait.c b/libc-top-half/musl/src/thread/pthread_cond_wait.c
new file mode 100644
index 0000000..8735bf1
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_cond_wait.c
@@ -0,0 +1,6 @@
+#include "pthread_impl.h"
+
+int pthread_cond_wait(pthread_cond_t *restrict c, pthread_mutex_t *restrict m)
+{
+ return pthread_cond_timedwait(c, m, 0);
+}
diff --git a/libc-top-half/musl/src/thread/pthread_condattr_destroy.c b/libc-top-half/musl/src/thread/pthread_condattr_destroy.c
new file mode 100644
index 0000000..c54ec41
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_condattr_destroy.c
@@ -0,0 +1,6 @@
+#include "pthread_impl.h"
+
+int pthread_condattr_destroy(pthread_condattr_t *a)
+{
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_condattr_init.c b/libc-top-half/musl/src/thread/pthread_condattr_init.c
new file mode 100644
index 0000000..a41741b
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_condattr_init.c
@@ -0,0 +1,7 @@
+#include "pthread_impl.h"
+
+int pthread_condattr_init(pthread_condattr_t *a)
+{
+ *a = (pthread_condattr_t){0};
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_condattr_setclock.c b/libc-top-half/musl/src/thread/pthread_condattr_setclock.c
new file mode 100644
index 0000000..21ca070
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_condattr_setclock.c
@@ -0,0 +1,21 @@
+#include "pthread_impl.h"
+
+#ifndef __wasilibc_unmodified_upstream
+#include <common/clock.h>
+#endif
+
+int pthread_condattr_setclock(pthread_condattr_t *a, clockid_t clk)
+{
+#ifdef __wasilibc_unmodified_upstream
+ if (clk < 0 || clk-2U < 2) return EINVAL;
+#else
+ if (clk->id < 0 || clk->id-2U < 2) return EINVAL;
+#endif
+ a->__attr &= 0x80000000;
+#ifdef __wasilibc_unmodified_upstream
+ a->__attr |= clk;
+#else
+ a->__attr |= clk->id;
+#endif
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_condattr_setpshared.c b/libc-top-half/musl/src/thread/pthread_condattr_setpshared.c
new file mode 100644
index 0000000..51453e0
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_condattr_setpshared.c
@@ -0,0 +1,9 @@
+#include "pthread_impl.h"
+
+int pthread_condattr_setpshared(pthread_condattr_t *a, int pshared)
+{
+ if (pshared > 1U) return EINVAL;
+ a->__attr &= 0x7fffffff;
+ a->__attr |= (unsigned)pshared<<31;
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_create.c b/libc-top-half/musl/src/thread/pthread_create.c
new file mode 100644
index 0000000..1aa7be7
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_create.c
@@ -0,0 +1,565 @@
+#define _GNU_SOURCE
+#include "pthread_impl.h"
+#include "stdio_impl.h"
+#include "libc.h"
+#include "lock.h"
+#ifdef __wasilibc_unmodified_upstream
+#include <sys/mman.h>
+#endif
+#include <string.h>
+#include <stddef.h>
+#ifndef __wasilibc_unmodified_upstream
+#include <stdatomic.h>
+#endif
+
+#include <stdalign.h>
+
+static void dummy_0()
+{
+}
+weak_alias(dummy_0, __acquire_ptc);
+weak_alias(dummy_0, __release_ptc);
+weak_alias(dummy_0, __pthread_tsd_run_dtors);
+weak_alias(dummy_0, __do_orphaned_stdio_locks);
+#ifdef __wasilibc_unmodified_upstream
+weak_alias(dummy_0, __dl_thread_cleanup);
+weak_alias(dummy_0, __membarrier_init);
+#endif
+
+static int tl_lock_count;
+static int tl_lock_waiters;
+
+void __tl_lock(void)
+{
+ int tid = __pthread_self()->tid;
+ int val = __thread_list_lock;
+ if (val == tid) {
+ tl_lock_count++;
+ return;
+ }
+ while ((val = a_cas(&__thread_list_lock, 0, tid)))
+ __wait(&__thread_list_lock, &tl_lock_waiters, val, 0);
+}
+
+void __tl_unlock(void)
+{
+ if (tl_lock_count) {
+ tl_lock_count--;
+ return;
+ }
+ a_store(&__thread_list_lock, 0);
+ if (tl_lock_waiters) __wake(&__thread_list_lock, 1, 0);
+}
+
+void __tl_sync(pthread_t td)
+{
+ a_barrier();
+ int val = __thread_list_lock;
+ if (!val) return;
+ __wait(&__thread_list_lock, &tl_lock_waiters, val, 0);
+ if (tl_lock_waiters) __wake(&__thread_list_lock, 1, 0);
+}
+
+_Noreturn void __pthread_exit(void *result)
+{
+ pthread_t self = __pthread_self();
+ sigset_t set;
+
+ self->canceldisable = 1;
+ self->cancelasync = 0;
+ self->result = result;
+
+ while (self->cancelbuf) {
+ void (*f)(void *) = self->cancelbuf->__f;
+ void *x = self->cancelbuf->__x;
+ self->cancelbuf = self->cancelbuf->__next;
+ f(x);
+ }
+
+ __pthread_tsd_run_dtors();
+
+#ifdef __wasilibc_unmodified_upstream
+ __block_app_sigs(&set);
+#endif
+
+ /* This atomic potentially competes with a concurrent pthread_detach
+ * call; the loser is responsible for freeing thread resources. */
+ int state = a_cas(&self->detach_state, DT_JOINABLE, DT_EXITING);
+
+ if (state==DT_DETACHED && self->map_base) {
+ /* Since __unmapself bypasses the normal munmap code path,
+ * explicitly wait for vmlock holders first. This must be
+ * done before any locks are taken, to avoid lock ordering
+ * issues that could lead to deadlock. */
+#ifdef __wasilibc_unmodified_upstream
+ __vm_wait();
+#endif
+ }
+
+ /* Access to target the exiting thread with syscalls that use
+ * its kernel tid is controlled by killlock. For detached threads,
+ * any use past this point would have undefined behavior, but for
+ * joinable threads it's a valid usage that must be handled.
+ * Signals must be blocked since pthread_kill must be AS-safe. */
+ LOCK(self->killlock);
+
+ /* The thread list lock must be AS-safe, and thus depends on
+ * application signals being blocked above. */
+ __tl_lock();
+
+ /* If this is the only thread in the list, don't proceed with
+ * termination of the thread, but restore the previous lock and
+ * signal state to prepare for exit to call atexit handlers. */
+ if (self->next == self) {
+ __tl_unlock();
+ UNLOCK(self->killlock);
+ self->detach_state = state;
+#ifdef __wasilibc_unmodified_upstream
+ __restore_sigs(&set);
+#endif
+ exit(0);
+ }
+
+ /* At this point we are committed to thread termination. */
+
+#ifdef __wasilibc_unmodified_upstream
+ /* Process robust list in userspace to handle non-pshared mutexes
+ * and the detached thread case where the robust list head will
+ * be invalid when the kernel would process it. */
+ __vm_lock();
+#endif
+ volatile void *volatile *rp;
+ while ((rp=self->robust_list.head) && rp != &self->robust_list.head) {
+ pthread_mutex_t *m = (void *)((char *)rp
+ - offsetof(pthread_mutex_t, _m_next));
+ int waiters = m->_m_waiters;
+ int priv = (m->_m_type & 128) ^ 128;
+ self->robust_list.pending = rp;
+ self->robust_list.head = *rp;
+ int cont = a_swap(&m->_m_lock, 0x40000000);
+ self->robust_list.pending = 0;
+ if (cont < 0 || waiters)
+ __wake(&m->_m_lock, 1, priv);
+ }
+#ifdef __wasilibc_unmodified_upstream
+ __vm_unlock();
+#endif
+
+ __do_orphaned_stdio_locks();
+#ifdef __wasilibc_unmodified_upstream
+ __dl_thread_cleanup();
+#endif
+
+ /* Last, unlink thread from the list. This change will not be visible
+ * until the lock is released, which only happens after SYS_exit
+ * has been called, via the exit futex address pointing at the lock.
+ * This needs to happen after any possible calls to LOCK() that might
+ * skip locking if process appears single-threaded. */
+ if (!--libc.threads_minus_1) libc.need_locks = -1;
+ self->next->prev = self->prev;
+ self->prev->next = self->next;
+ self->prev = self->next = self;
+
+#ifndef __wasilibc_unmodified_upstream
+ /* On Linux, the thread is created with CLONE_CHILD_CLEARTID,
+ * and this lock will unlock by kernel when this thread terminates.
+ * So we should unlock it here in WebAssembly.
+ * See also set_tid_address(2) */
+ __tl_unlock();
+#endif
+
+#ifdef __wasilibc_unmodified_upstream
+ if (state==DT_DETACHED && self->map_base) {
+ /* Detached threads must block even implementation-internal
+ * signals, since they will not have a stack in their last
+ * moments of existence. */
+ __block_all_sigs(&set);
+
+ /* Robust list will no longer be valid, and was already
+ * processed above, so unregister it with the kernel. */
+ if (self->robust_list.off)
+ __syscall(SYS_set_robust_list, 0, 3*sizeof(long));
+
+ /* The following call unmaps the thread's stack mapping
+ * and then exits without touching the stack. */
+ __unmapself(self->map_base, self->map_size);
+ }
+#else
+ if (state==DT_DETACHED && self->map_base) {
+ // __syscall(SYS_exit) would unlock the thread, list
+ // do it manually here
+ __tl_unlock();
+ free(self->map_base);
+ // Can't use `exit()` here, because it is too high level
+ for (;;) __wasi_proc_exit(0);
+ }
+#endif
+
+ /* Wake any joiner. */
+ a_store(&self->detach_state, DT_EXITED);
+ __wake(&self->detach_state, 1, 1);
+
+ /* After the kernel thread exits, its tid may be reused. Clear it
+ * to prevent inadvertent use and inform functions that would use
+ * it that it's no longer available. */
+ self->tid = 0;
+ UNLOCK(self->killlock);
+
+#ifdef __wasilibc_unmodified_upstream
+ for (;;) __syscall(SYS_exit, 0);
+#else
+ // __syscall(SYS_exit) would unlock the thread, list
+ // do it manually here
+ __tl_unlock();
+ // Can't use `exit()` here, because it is too high level
+ for (;;) __wasi_proc_exit(0);
+#endif
+}
+
+void __do_cleanup_push(struct __ptcb *cb)
+{
+ struct pthread *self = __pthread_self();
+ cb->__next = self->cancelbuf;
+ self->cancelbuf = cb;
+}
+
+void __do_cleanup_pop(struct __ptcb *cb)
+{
+ __pthread_self()->cancelbuf = cb->__next;
+}
+
+struct start_args {
+#ifdef __wasilibc_unmodified_upstream
+ void *(*start_func)(void *);
+ void *start_arg;
+ volatile int control;
+ unsigned long sig_mask[_NSIG/8/sizeof(long)];
+#else
+ void *(*start_func)(void *);
+ void *start_arg;
+ void *tls_base;
+#endif
+};
+
+#ifdef __wasilibc_unmodified_upstream
+static int start(void *p)
+{
+ struct start_args *args = p;
+ int state = args->control;
+ if (state) {
+ if (a_cas(&args->control, 1, 2)==1)
+ __wait(&args->control, 0, 2, 1);
+ if (args->control) {
+#ifdef __wasilibc_unmodified_upstream
+ __syscall(SYS_set_tid_address, &args->control);
+ for (;;) __syscall(SYS_exit, 0);
+#endif
+ }
+ }
+#ifdef __wasilibc_unmodified_upstream
+ __syscall(SYS_rt_sigprocmask, SIG_SETMASK, &args->sig_mask, 0, _NSIG/8);
+#endif
+ __pthread_exit(args->start_func(args->start_arg));
+ return 0;
+}
+
+static int start_c11(void *p)
+{
+ struct start_args *args = p;
+ int (*start)(void*) = (int(*)(void*)) args->start_func;
+ __pthread_exit((void *)(uintptr_t)start(args->start_arg));
+ return 0;
+}
+#else
+__attribute__((export_name("wasi_thread_start")))
+_Noreturn void wasi_thread_start(int tid, void *p)
+{
+ struct start_args *args = p;
+ __asm__(".globaltype __tls_base, i32\n"
+ "local.get %0\n"
+ "global.set __tls_base\n"
+ :: "r"(args->tls_base));
+ pthread_t self = __pthread_self();
+ // Set the thread ID (TID) on the pthread structure. The TID is stored
+ // atomically since it is also stored by the parent thread; this way,
+ // whichever thread (parent or child) reaches this point first can proceed
+ // without waiting.
+ atomic_store((atomic_int *) &(self->tid), tid);
+ // Set the stack pointer.
+ __asm__(".globaltype __stack_pointer, i32\n"
+ "local.get %0\n"
+ "global.set __stack_pointer\n"
+ :: "r"(self->stack));
+ // Execute the user's start function.
+ int (*start)(void*) = (int(*)(void*)) args->start_func;
+ __pthread_exit((void *)(uintptr_t)start(args->start_arg));
+}
+#endif
+
+#define ROUND(x) (((x)+PAGE_SIZE-1)&-PAGE_SIZE)
+
+/* pthread_key_create.c overrides this */
+static volatile size_t dummy = 0;
+weak_alias(dummy, __pthread_tsd_size);
+static void *dummy_tsd[1] = { 0 };
+weak_alias(dummy_tsd, __pthread_tsd_main);
+
+static FILE *volatile dummy_file = 0;
+weak_alias(dummy_file, __stdin_used);
+weak_alias(dummy_file, __stdout_used);
+weak_alias(dummy_file, __stderr_used);
+
+static void init_file_lock(FILE *f)
+{
+ if (f && f->lock<0) f->lock = 0;
+}
+
+int __pthread_create(pthread_t *restrict res, const pthread_attr_t *restrict attrp, void *(*entry)(void *), void *restrict arg)
+{
+ int ret, c11 = (attrp == __ATTRP_C11_THREAD);
+ size_t size, guard;
+ struct pthread *self, *new;
+ unsigned char *map = 0, *stack = 0, *tsd = 0, *stack_limit;
+#ifdef __wasilibc_unmodified_upstream
+ unsigned flags = CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND
+ | CLONE_THREAD | CLONE_SYSVSEM | CLONE_SETTLS
+ | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID | CLONE_DETACHED;
+#endif
+ pthread_attr_t attr = { 0 };
+ sigset_t set;
+#ifndef __wasilibc_unmodified_upstream
+ size_t tls_size = __builtin_wasm_tls_size();
+ size_t tls_align = __builtin_wasm_tls_align();
+ void* tls_base = __builtin_wasm_tls_base();
+ void* new_tls_base;
+ size_t tls_offset;
+ tls_size += tls_align;
+#endif
+
+#ifdef __wasilibc_unmodified_upstream
+ if (!libc.can_do_threads) return ENOSYS;
+#endif
+ self = __pthread_self();
+ if (!libc.threaded) {
+ for (FILE *f=*__ofl_lock(); f; f=f->next)
+ init_file_lock(f);
+ __ofl_unlock();
+ init_file_lock(__stdin_used);
+ init_file_lock(__stdout_used);
+ init_file_lock(__stderr_used);
+#ifdef __wasilibc_unmodified_upstream
+ __syscall(SYS_rt_sigprocmask, SIG_UNBLOCK, SIGPT_SET, 0, _NSIG/8);
+#endif
+ self->tsd = (void **)__pthread_tsd_main;
+#ifdef __wasilibc_unmodified_upstream
+ __membarrier_init();
+#endif
+ libc.threaded = 1;
+ }
+ if (attrp && !c11) attr = *attrp;
+
+ __acquire_ptc();
+ if (!attrp || c11) {
+ attr._a_stacksize = __default_stacksize;
+ attr._a_guardsize = __default_guardsize;
+ }
+
+ if (attr._a_stackaddr) {
+#ifdef __wasilibc_unmodified_upstream
+ size_t need = libc.tls_size + __pthread_tsd_size;
+#else
+ size_t need = tls_size + __pthread_tsd_size;
+#endif
+ size = attr._a_stacksize;
+ stack = (void *)(attr._a_stackaddr & -16);
+ stack_limit = (void *)(attr._a_stackaddr - size);
+ /* Use application-provided stack for TLS only when
+ * it does not take more than ~12% or 2k of the
+ * application's stack space. */
+ if (need < size/8 && need < 2048) {
+ tsd = stack - __pthread_tsd_size;
+#ifdef __wasilibc_unmodified_upstream
+ stack = tsd - libc.tls_size;
+#else
+ stack = tsd - tls_size;
+#endif
+ memset(stack, 0, need);
+ } else {
+ size = ROUND(need);
+ }
+ guard = 0;
+ } else {
+ guard = ROUND(attr._a_guardsize);
+ size = guard + ROUND(attr._a_stacksize
+#ifdef __wasilibc_unmodified_upstream
+ + libc.tls_size + __pthread_tsd_size);
+#else
+ + tls_size + __pthread_tsd_size);
+#endif
+ }
+
+ if (!tsd) {
+#ifdef __wasilibc_unmodified_upstream
+ if (guard) {
+ map = __mmap(0, size, PROT_NONE, MAP_PRIVATE|MAP_ANON, -1, 0);
+ if (map == MAP_FAILED) goto fail;
+ if (__mprotect(map+guard, size-guard, PROT_READ|PROT_WRITE)
+ && errno != ENOSYS) {
+ __munmap(map, size);
+ goto fail;
+ }
+ } else {
+ map = __mmap(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
+ if (map == MAP_FAILED) goto fail;
+ }
+#else
+ map = malloc(size);
+ if (!map) goto fail;
+#endif
+ tsd = map + size - __pthread_tsd_size;
+ if (!stack) {
+#ifdef __wasilibc_unmodified_upstream
+ stack = tsd - libc.tls_size;
+#else
+ stack = tsd - tls_size;
+#endif
+ stack_limit = map + guard;
+ }
+ }
+
+#ifdef __wasilibc_unmodified_upstream
+ new = __copy_tls(tsd - libc.tls_size);
+#else
+ new_tls_base = __copy_tls(tsd - tls_size);
+ tls_offset = new_tls_base - tls_base;
+ new = (void*)((uintptr_t)self + tls_offset);
+#endif
+ new->map_base = map;
+ new->map_size = size;
+ new->stack = stack;
+ new->stack_size = stack - stack_limit;
+ new->guard_size = guard;
+ new->self = new;
+ new->tsd = (void *)tsd;
+ new->locale = &libc.global_locale;
+ if (attr._a_detach) {
+ new->detach_state = DT_DETACHED;
+ } else {
+ new->detach_state = DT_JOINABLE;
+ }
+ new->robust_list.head = &new->robust_list.head;
+ new->canary = self->canary;
+ new->sysinfo = self->sysinfo;
+
+ /* Setup argument structure for the new thread on its stack.
+ * It's safe to access from the caller only until the thread
+ * list is unlocked. */
+#ifdef __wasilibc_unmodified_upstream
+ stack -= (uintptr_t)stack % sizeof(uintptr_t);
+ stack -= sizeof(struct start_args);
+ struct start_args *args = (void *)stack;
+ args->start_func = entry;
+ args->start_arg = arg;
+ args->control = attr._a_sched ? 1 : 0;
+
+ /* Application signals (but not the synccall signal) must be
+ * blocked before the thread list lock can be taken, to ensure
+ * that the lock is AS-safe. */
+ __block_app_sigs(&set);
+
+ /* Ensure SIGCANCEL is unblocked in new thread. This requires
+ * working with a copy of the set so we can restore the
+ * original mask in the calling thread. */
+ memcpy(&args->sig_mask, &set, sizeof args->sig_mask);
+ args->sig_mask[(SIGCANCEL-1)/8/sizeof(long)] &=
+ ~(1UL<<((SIGCANCEL-1)%(8*sizeof(long))));
+#else
+ /* Align the stack to struct start_args */
+ stack -= sizeof(struct start_args);
+ stack -= (uintptr_t)stack % alignof(struct start_args);
+ struct start_args *args = (void *)stack;
+
+ /* Align the stack to 16 and store it */
+ new->stack = (void *)((uintptr_t) stack & -16);
+ /* Correct the stack size */
+ new->stack_size = stack - stack_limit;
+
+ args->start_func = entry;
+ args->start_arg = arg;
+ args->tls_base = (void*)new_tls_base;
+#endif
+
+ __tl_lock();
+ if (!libc.threads_minus_1++) libc.need_locks = 1;
+#ifdef __wasilibc_unmodified_upstream
+ ret = __clone((c11 ? start_c11 : start), stack, flags, args, &new->tid, TP_ADJ(new), &__thread_list_lock);
+#else
+ /* Instead of `__clone`, WASI uses a host API to instantiate a new version
+ * of the current module and start executing the entry function. The
+ * wasi-threads specification requires the module to export a
+ * `wasi_thread_start` function, which is invoked with `args`. */
+ ret = __wasi_thread_spawn((void *) args);
+#endif
+
+#ifdef __wasilibc_unmodified_upstream
+ /* All clone failures translate to EAGAIN. If explicit scheduling
+ * was requested, attempt it before unlocking the thread list so
+ * that the failed thread is never exposed and so that we can
+ * clean up all transient resource usage before returning. */
+ if (ret < 0) {
+ ret = -EAGAIN;
+ } else if (attr._a_sched) {
+ ret = __syscall(SYS_sched_setscheduler,
+ new->tid, attr._a_policy, &attr._a_prio);
+ if (a_swap(&args->control, ret ? 3 : 0)==2)
+ __wake(&args->control, 1, 1);
+ if (ret)
+ __wait(&args->control, 0, 3, 0);
+ }
+#else
+ /* `wasi_thread_spawn` will either return a host-provided thread ID (TID)
+ * (`>= 0`) or an error code (`< 0`). As in the unmodified version, all
+ * spawn failures translate to EAGAIN; unlike the modified version, there is
+ * no need to "start up" the child thread--the host does this. If the spawn
+ * did succeed, then we store the TID atomically, since this parent thread
+ * is racing with the child thread to set this field; this way, whichever
+ * thread reaches this point first can continue without waiting. */
+ if (ret < 0) {
+ ret = -EAGAIN;
+ } else {
+ atomic_store((atomic_int *) &(new->tid), ret);
+ }
+#endif
+
+ if (ret >= 0) {
+ new->next = self->next;
+ new->prev = self;
+ new->next->prev = new;
+ new->prev->next = new;
+ } else {
+ if (!--libc.threads_minus_1) libc.need_locks = 0;
+ }
+ __tl_unlock();
+#ifdef __wasilibc_unmodified_upstream
+ __restore_sigs(&set);
+#endif
+ __release_ptc();
+
+ if (ret < 0) {
+#ifdef __wasilibc_unmodified_upstream
+ if (map) __munmap(map, size);
+#else
+ free(map);
+#endif
+ return -ret;
+ }
+
+ *res = new;
+ return 0;
+fail:
+ __release_ptc();
+ return EAGAIN;
+}
+
+weak_alias(__pthread_exit, pthread_exit);
+weak_alias(__pthread_create, pthread_create);
diff --git a/libc-top-half/musl/src/thread/pthread_detach.c b/libc-top-half/musl/src/thread/pthread_detach.c
new file mode 100644
index 0000000..77772af
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_detach.c
@@ -0,0 +1,14 @@
+#include "pthread_impl.h"
+#include <threads.h>
+
+static int __pthread_detach(pthread_t t)
+{
+ /* If the cas fails, detach state is either already-detached
+ * or exiting/exited, and pthread_join will trap or cleanup. */
+ if (a_cas(&t->detach_state, DT_JOINABLE, DT_DETACHED) != DT_JOINABLE)
+ return __pthread_join(t, 0);
+ return 0;
+}
+
+weak_alias(__pthread_detach, pthread_detach);
+weak_alias(__pthread_detach, thrd_detach);
diff --git a/libc-top-half/musl/src/thread/pthread_equal.c b/libc-top-half/musl/src/thread/pthread_equal.c
new file mode 100644
index 0000000..dbb7365
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_equal.c
@@ -0,0 +1,10 @@
+#include <pthread.h>
+#include <threads.h>
+
+static int __pthread_equal(pthread_t a, pthread_t b)
+{
+ return a==b;
+}
+
+weak_alias(__pthread_equal, pthread_equal);
+weak_alias(__pthread_equal, thrd_equal);
diff --git a/libc-top-half/musl/src/thread/pthread_getattr_np.c b/libc-top-half/musl/src/thread/pthread_getattr_np.c
new file mode 100644
index 0000000..2881831
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_getattr_np.c
@@ -0,0 +1,24 @@
+#define _GNU_SOURCE
+#include "pthread_impl.h"
+#include "libc.h"
+#include <sys/mman.h>
+
+int pthread_getattr_np(pthread_t t, pthread_attr_t *a)
+{
+ *a = (pthread_attr_t){0};
+ a->_a_detach = t->detach_state>=DT_DETACHED;
+ a->_a_guardsize = t->guard_size;
+ if (t->stack) {
+ a->_a_stackaddr = (uintptr_t)t->stack;
+ a->_a_stacksize = t->stack_size;
+ } else {
+ char *p = (void *)libc.auxv;
+ size_t l = PAGE_SIZE;
+ p += -(uintptr_t)p & PAGE_SIZE-1;
+ a->_a_stackaddr = (uintptr_t)p;
+ while (mremap(p-l-PAGE_SIZE, PAGE_SIZE, 2*PAGE_SIZE, 0)==MAP_FAILED && errno==ENOMEM)
+ l += PAGE_SIZE;
+ a->_a_stacksize = l;
+ }
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_getconcurrency.c b/libc-top-half/musl/src/thread/pthread_getconcurrency.c
new file mode 100644
index 0000000..269429a
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_getconcurrency.c
@@ -0,0 +1,6 @@
+#include <pthread.h>
+
+int pthread_getconcurrency()
+{
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_getcpuclockid.c b/libc-top-half/musl/src/thread/pthread_getcpuclockid.c
new file mode 100644
index 0000000..9df14fb
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_getcpuclockid.c
@@ -0,0 +1,7 @@
+#include "pthread_impl.h"
+
+int pthread_getcpuclockid(pthread_t t, clockid_t *clockid)
+{
+ *clockid = (-t->tid-1)*8U + 6;
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_getname_np.c b/libc-top-half/musl/src/thread/pthread_getname_np.c
new file mode 100644
index 0000000..85504e4
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_getname_np.c
@@ -0,0 +1,25 @@
+#define _GNU_SOURCE
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/prctl.h>
+
+#include "pthread_impl.h"
+
+int pthread_getname_np(pthread_t thread, char *name, size_t len)
+{
+ int fd, cs, status = 0;
+ char f[sizeof "/proc/self/task//comm" + 3*sizeof(int)];
+
+ if (len < 16) return ERANGE;
+
+ if (thread == pthread_self())
+ return prctl(PR_GET_NAME, (unsigned long)name, 0UL, 0UL, 0UL) ? errno : 0;
+
+ snprintf(f, sizeof f, "/proc/self/task/%d/comm", thread->tid);
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
+ if ((fd = open(f, O_RDONLY|O_CLOEXEC)) < 0 || (len = read(fd, name, len)) == -1) status = errno;
+ else name[len-1] = 0; /* remove trailing new line only if successful */
+ if (fd >= 0) close(fd);
+ pthread_setcancelstate(cs, 0);
+ return status;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_getschedparam.c b/libc-top-half/musl/src/thread/pthread_getschedparam.c
new file mode 100644
index 0000000..c098bef
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_getschedparam.c
@@ -0,0 +1,21 @@
+#include "pthread_impl.h"
+#include "lock.h"
+
+int pthread_getschedparam(pthread_t t, int *restrict policy, struct sched_param *restrict param)
+{
+ int r;
+ sigset_t set;
+ __block_app_sigs(&set);
+ LOCK(t->killlock);
+ if (!t->tid) {
+ r = ESRCH;
+ } else {
+ r = -__syscall(SYS_sched_getparam, t->tid, param);
+ if (!r) {
+ *policy = __syscall(SYS_sched_getscheduler, t->tid);
+ }
+ }
+ UNLOCK(t->killlock);
+ __restore_sigs(&set);
+ return r;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_getspecific.c b/libc-top-half/musl/src/thread/pthread_getspecific.c
new file mode 100644
index 0000000..d9342a5
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_getspecific.c
@@ -0,0 +1,11 @@
+#include "pthread_impl.h"
+#include <threads.h>
+
+static void *__pthread_getspecific(pthread_key_t k)
+{
+ struct pthread *self = __pthread_self();
+ return self->tsd[k];
+}
+
+weak_alias(__pthread_getspecific, pthread_getspecific);
+weak_alias(__pthread_getspecific, tss_get);
diff --git a/libc-top-half/musl/src/thread/pthread_join.c b/libc-top-half/musl/src/thread/pthread_join.c
new file mode 100644
index 0000000..b06e7e7
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_join.c
@@ -0,0 +1,46 @@
+#define _GNU_SOURCE
+#include "pthread_impl.h"
+#ifdef __wasilibc_unmodified_upstream
+#include <sys/mman.h>
+#endif
+
+static void dummy1(pthread_t t)
+{
+}
+weak_alias(dummy1, __tl_sync);
+
+static int __pthread_timedjoin_np(pthread_t t, void **res, const struct timespec *at)
+{
+ int state, cs, r = 0;
+ __pthread_testcancel();
+ __pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
+ if (cs == PTHREAD_CANCEL_ENABLE) __pthread_setcancelstate(cs, 0);
+ while ((state = t->detach_state) && r != ETIMEDOUT && r != EINVAL) {
+ if (state >= DT_DETACHED) a_crash();
+ r = __timedwait_cp(&t->detach_state, state, CLOCK_REALTIME, at, 1);
+ }
+ __pthread_setcancelstate(cs, 0);
+ if (r == ETIMEDOUT || r == EINVAL) return r;
+ __tl_sync(t);
+ if (res) *res = t->result;
+#ifdef __wasilibc_unmodified_upstream
+ if (t->map_base) __munmap(t->map_base, t->map_size);
+#else
+ if (t->map_base) free(t->map_base);
+#endif
+ return 0;
+}
+
+int __pthread_join(pthread_t t, void **res)
+{
+ return __pthread_timedjoin_np(t, res, 0);
+}
+
+static int __pthread_tryjoin_np(pthread_t t, void **res)
+{
+ return t->detach_state==DT_JOINABLE ? EBUSY : __pthread_join(t, res);
+}
+
+weak_alias(__pthread_tryjoin_np, pthread_tryjoin_np);
+weak_alias(__pthread_timedjoin_np, pthread_timedjoin_np);
+weak_alias(__pthread_join, pthread_join);
diff --git a/libc-top-half/musl/src/thread/pthread_key_create.c b/libc-top-half/musl/src/thread/pthread_key_create.c
new file mode 100644
index 0000000..dd47caa
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_key_create.c
@@ -0,0 +1,95 @@
+#include "pthread_impl.h"
+
+volatile size_t __pthread_tsd_size = sizeof(void *) * PTHREAD_KEYS_MAX;
+void *__pthread_tsd_main[PTHREAD_KEYS_MAX] = { 0 };
+
+static void (*keys[PTHREAD_KEYS_MAX])(void *);
+
+static pthread_rwlock_t key_lock = PTHREAD_RWLOCK_INITIALIZER;
+
+static pthread_key_t next_key;
+
+static void nodtor(void *dummy)
+{
+}
+
+static void dummy_0(void)
+{
+}
+
+weak_alias(dummy_0, __tl_lock);
+weak_alias(dummy_0, __tl_unlock);
+
+int __pthread_key_create(pthread_key_t *k, void (*dtor)(void *))
+{
+ pthread_t self = __pthread_self();
+
+ /* This can only happen in the main thread before
+ * pthread_create has been called. */
+ if (!self->tsd) self->tsd = __pthread_tsd_main;
+
+ /* Purely a sentinel value since null means slot is free. */
+ if (!dtor) dtor = nodtor;
+
+ __pthread_rwlock_wrlock(&key_lock);
+ pthread_key_t j = next_key;
+ do {
+ if (!keys[j]) {
+ keys[next_key = *k = j] = dtor;
+ __pthread_rwlock_unlock(&key_lock);
+ return 0;
+ }
+ } while ((j=(j+1)%PTHREAD_KEYS_MAX) != next_key);
+
+ __pthread_rwlock_unlock(&key_lock);
+ return EAGAIN;
+}
+
+int __pthread_key_delete(pthread_key_t k)
+{
+ sigset_t set;
+ pthread_t self = __pthread_self(), td=self;
+
+#ifdef __wasilibc_unmodified_upstream
+ __block_app_sigs(&set);
+#endif
+ __pthread_rwlock_wrlock(&key_lock);
+
+ __tl_lock();
+ do td->tsd[k] = 0;
+ while ((td=td->next)!=self);
+ __tl_unlock();
+
+ keys[k] = 0;
+
+ __pthread_rwlock_unlock(&key_lock);
+#ifdef __wasilibc_unmodified_upstream
+ __restore_sigs(&set);
+#endif
+
+ return 0;
+}
+
+void __pthread_tsd_run_dtors()
+{
+ pthread_t self = __pthread_self();
+ int i, j;
+ for (j=0; self->tsd_used && j<PTHREAD_DESTRUCTOR_ITERATIONS; j++) {
+ __pthread_rwlock_rdlock(&key_lock);
+ self->tsd_used = 0;
+ for (i=0; i<PTHREAD_KEYS_MAX; i++) {
+ void *val = self->tsd[i];
+ void (*dtor)(void *) = keys[i];
+ self->tsd[i] = 0;
+ if (val && dtor && dtor != nodtor) {
+ __pthread_rwlock_unlock(&key_lock);
+ dtor(val);
+ __pthread_rwlock_rdlock(&key_lock);
+ }
+ }
+ __pthread_rwlock_unlock(&key_lock);
+ }
+}
+
+weak_alias(__pthread_key_create, pthread_key_create);
+weak_alias(__pthread_key_delete, pthread_key_delete);
diff --git a/libc-top-half/musl/src/thread/pthread_kill.c b/libc-top-half/musl/src/thread/pthread_kill.c
new file mode 100644
index 0000000..79ddb20
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_kill.c
@@ -0,0 +1,18 @@
+#include "pthread_impl.h"
+#include "lock.h"
+
+int pthread_kill(pthread_t t, int sig)
+{
+ int r;
+ sigset_t set;
+ /* Block not just app signals, but internal ones too, since
+ * pthread_kill is used to implement pthread_cancel, which
+ * must be async-cancel-safe. */
+ __block_all_sigs(&set);
+ LOCK(t->killlock);
+ r = t->tid ? -__syscall(SYS_tkill, t->tid, sig)
+ : (sig+0U >= _NSIG ? EINVAL : 0);
+ UNLOCK(t->killlock);
+ __restore_sigs(&set);
+ return r;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_mutex_consistent.c b/libc-top-half/musl/src/thread/pthread_mutex_consistent.c
new file mode 100644
index 0000000..27c74e5
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_mutex_consistent.c
@@ -0,0 +1,14 @@
+#include "pthread_impl.h"
+#include "atomic.h"
+
+int pthread_mutex_consistent(pthread_mutex_t *m)
+{
+ int old = m->_m_lock;
+ int own = old & 0x3fffffff;
+ if (!(m->_m_type & 4) || !own || !(old & 0x40000000))
+ return EINVAL;
+ if (own != __pthread_self()->tid)
+ return EPERM;
+ a_and(&m->_m_lock, ~0x40000000);
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_mutex_destroy.c b/libc-top-half/musl/src/thread/pthread_mutex_destroy.c
new file mode 100644
index 0000000..e53c39c
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_mutex_destroy.c
@@ -0,0 +1,18 @@
+#include "pthread_impl.h"
+
+int pthread_mutex_destroy(pthread_mutex_t *mutex)
+{
+#ifdef __wasilibc_unmodified_upstream
+ /* If the mutex being destroyed is process-shared and has nontrivial
+ * type (tracking ownership), it might be in the pending slot of a
+ * robust_list; wait for quiescence. */
+ if (mutex->_m_type > 128) __vm_wait();
+#else
+ /* For now, wasi-libc chooses to avoid implementing robust mutex support
+ * though this could be added later. The error code indicates that the
+ * mutex was an invalid type, but it would be more accurate as
+ * "unimplemented". */
+ if (mutex->_m_type > 128) return EINVAL;
+#endif
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_mutex_getprioceiling.c b/libc-top-half/musl/src/thread/pthread_mutex_getprioceiling.c
new file mode 100644
index 0000000..8c75a66
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_mutex_getprioceiling.c
@@ -0,0 +1,6 @@
+#include "pthread_impl.h"
+
+int pthread_mutex_getprioceiling(const pthread_mutex_t *restrict m, int *restrict ceiling)
+{
+ return EINVAL;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_mutex_init.c b/libc-top-half/musl/src/thread/pthread_mutex_init.c
new file mode 100644
index 0000000..acf45a7
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_mutex_init.c
@@ -0,0 +1,8 @@
+#include "pthread_impl.h"
+
+int pthread_mutex_init(pthread_mutex_t *restrict m, const pthread_mutexattr_t *restrict a)
+{
+ *m = (pthread_mutex_t){0};
+ if (a) m->_m_type = a->__attr;
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_mutex_lock.c b/libc-top-half/musl/src/thread/pthread_mutex_lock.c
new file mode 100644
index 0000000..638d4b8
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_mutex_lock.c
@@ -0,0 +1,12 @@
+#include "pthread_impl.h"
+
+int __pthread_mutex_lock(pthread_mutex_t *m)
+{
+ if ((m->_m_type&15) == PTHREAD_MUTEX_NORMAL
+ && !a_cas(&m->_m_lock, 0, EBUSY))
+ return 0;
+
+ return __pthread_mutex_timedlock(m, 0);
+}
+
+weak_alias(__pthread_mutex_lock, pthread_mutex_lock);
diff --git a/libc-top-half/musl/src/thread/pthread_mutex_setprioceiling.c b/libc-top-half/musl/src/thread/pthread_mutex_setprioceiling.c
new file mode 100644
index 0000000..681f07c
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_mutex_setprioceiling.c
@@ -0,0 +1,6 @@
+#include "pthread_impl.h"
+
+int pthread_mutex_setprioceiling(pthread_mutex_t *restrict m, int ceiling, int *restrict old)
+{
+ return EINVAL;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_mutex_timedlock.c b/libc-top-half/musl/src/thread/pthread_mutex_timedlock.c
new file mode 100644
index 0000000..d22196a
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_mutex_timedlock.c
@@ -0,0 +1,96 @@
+#include "pthread_impl.h"
+
+#ifdef __wasilibc_unmodified_upstream
+#define IS32BIT(x) !((x)+0x80000000ULL>>32)
+#define CLAMP(x) (int)(IS32BIT(x) ? (x) : 0x7fffffffU+((0ULL+(x))>>63))
+
+static int __futex4(volatile void *addr, int op, int val, const struct timespec *to)
+{
+#ifdef SYS_futex_time64
+ time_t s = to ? to->tv_sec : 0;
+ long ns = to ? to->tv_nsec : 0;
+ int r = -ENOSYS;
+ if (SYS_futex == SYS_futex_time64 || !IS32BIT(s))
+ r = __syscall(SYS_futex_time64, addr, op, val,
+ to ? ((long long[]){s, ns}) : 0);
+ if (SYS_futex == SYS_futex_time64 || r!=-ENOSYS) return r;
+ to = to ? (void *)(long[]){CLAMP(s), ns} : 0;
+#endif
+ return __syscall(SYS_futex, addr, op, val, to);
+}
+
+static int pthread_mutex_timedlock_pi(pthread_mutex_t *restrict m, const struct timespec *restrict at)
+{
+ int type = m->_m_type;
+ int priv = (type & 128) ^ 128;
+ pthread_t self = __pthread_self();
+ int e;
+
+ if (!priv) self->robust_list.pending = &m->_m_next;
+
+ do e = -__futex4(&m->_m_lock, FUTEX_LOCK_PI|priv, 0, at);
+ while (e==EINTR);
+ if (e) self->robust_list.pending = 0;
+
+ switch (e) {
+ case 0:
+ /* Catch spurious success for non-robust mutexes. */
+ if (!(type&4) && ((m->_m_lock & 0x40000000) || m->_m_waiters)) {
+ a_store(&m->_m_waiters, -1);
+ __syscall(SYS_futex, &m->_m_lock, FUTEX_UNLOCK_PI|priv);
+ self->robust_list.pending = 0;
+ break;
+ }
+ /* Signal to trylock that we already have the lock. */
+ m->_m_count = -1;
+ return __pthread_mutex_trylock(m);
+ case ETIMEDOUT:
+ return e;
+ case EDEADLK:
+ if ((type&3) == PTHREAD_MUTEX_ERRORCHECK) return e;
+ }
+ do e = __timedwait(&(int){0}, 0, CLOCK_REALTIME, at, 1);
+ while (e != ETIMEDOUT);
+ return e;
+}
+#endif
+
+int __pthread_mutex_timedlock(pthread_mutex_t *restrict m, const struct timespec *restrict at)
+{
+ if ((m->_m_type&15) == PTHREAD_MUTEX_NORMAL
+ && !a_cas(&m->_m_lock, 0, EBUSY))
+ return 0;
+
+ int type = m->_m_type;
+ int r, t, priv = (type & 128) ^ 128;
+
+ r = __pthread_mutex_trylock(m);
+ if (r != EBUSY) return r;
+
+#ifdef __wasilibc_unmodified_upstream
+ if (type&8) return pthread_mutex_timedlock_pi(m, at);
+#endif
+
+ int spins = 100;
+ while (spins-- && m->_m_lock && !m->_m_waiters) a_spin();
+
+ while ((r=__pthread_mutex_trylock(m)) == EBUSY) {
+ r = m->_m_lock;
+ int own = r & 0x3fffffff;
+ if (!own && (!r || (type&4)))
+ continue;
+ if ((type&3) == PTHREAD_MUTEX_ERRORCHECK
+ && own == __pthread_self()->tid)
+ return EDEADLK;
+
+ a_inc(&m->_m_waiters);
+ t = r | 0x80000000;
+ a_cas(&m->_m_lock, r, t);
+ r = __timedwait(&m->_m_lock, t, CLOCK_REALTIME, at, priv);
+ a_dec(&m->_m_waiters);
+ if (r && r != EINTR) break;
+ }
+ return r;
+}
+
+weak_alias(__pthread_mutex_timedlock, pthread_mutex_timedlock);
diff --git a/libc-top-half/musl/src/thread/pthread_mutex_trylock.c b/libc-top-half/musl/src/thread/pthread_mutex_trylock.c
new file mode 100644
index 0000000..c60b45f
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_mutex_trylock.c
@@ -0,0 +1,78 @@
+#include "pthread_impl.h"
+
+int __pthread_mutex_trylock_owner(pthread_mutex_t *m)
+{
+ int old, own;
+ int type = m->_m_type;
+ pthread_t self = __pthread_self();
+ int tid = self->tid;
+
+ old = m->_m_lock;
+ own = old & 0x3fffffff;
+ if (own == tid) {
+ if ((type&8) && m->_m_count<0) {
+ old &= 0x40000000;
+ m->_m_count = 0;
+ goto success;
+ }
+ if ((type&3) == PTHREAD_MUTEX_RECURSIVE) {
+ if ((unsigned)m->_m_count >= INT_MAX) return EAGAIN;
+ m->_m_count++;
+ return 0;
+ }
+ }
+ if (own == 0x3fffffff) return ENOTRECOVERABLE;
+ if (own || (old && !(type & 4))) return EBUSY;
+
+ if (type & 128) {
+ if (!self->robust_list.off) {
+ self->robust_list.off = (char*)&m->_m_lock-(char *)&m->_m_next;
+#ifdef __wasilibc_unmodified_upstream
+ __syscall(SYS_set_robust_list, &self->robust_list, 3*sizeof(long));
+#endif
+ }
+ if (m->_m_waiters) tid |= 0x80000000;
+ self->robust_list.pending = &m->_m_next;
+ }
+ tid |= old & 0x40000000;
+
+ if (a_cas(&m->_m_lock, old, tid) != old) {
+ self->robust_list.pending = 0;
+ if ((type&12)==12 && m->_m_waiters) return ENOTRECOVERABLE;
+ return EBUSY;
+ }
+
+success:
+ if ((type&8) && m->_m_waiters) {
+ int priv = (type & 128) ^ 128;
+#ifdef __wasilibc_unmodified_upstream
+ __syscall(SYS_futex, &m->_m_lock, FUTEX_UNLOCK_PI|priv);
+#endif
+ self->robust_list.pending = 0;
+ return (type&4) ? ENOTRECOVERABLE : EBUSY;
+ }
+
+ volatile void *next = self->robust_list.head;
+ m->_m_next = next;
+ m->_m_prev = &self->robust_list.head;
+ if (next != &self->robust_list.head) *(volatile void *volatile *)
+ ((char *)next - sizeof(void *)) = &m->_m_next;
+ self->robust_list.head = &m->_m_next;
+ self->robust_list.pending = 0;
+
+ if (old) {
+ m->_m_count = 0;
+ return EOWNERDEAD;
+ }
+
+ return 0;
+}
+
+int __pthread_mutex_trylock(pthread_mutex_t *m)
+{
+ if ((m->_m_type&15) == PTHREAD_MUTEX_NORMAL)
+ return a_cas(&m->_m_lock, 0, EBUSY) & EBUSY;
+ return __pthread_mutex_trylock_owner(m);
+}
+
+weak_alias(__pthread_mutex_trylock, pthread_mutex_trylock);
diff --git a/libc-top-half/musl/src/thread/pthread_mutex_unlock.c b/libc-top-half/musl/src/thread/pthread_mutex_unlock.c
new file mode 100644
index 0000000..6beaacb
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_mutex_unlock.c
@@ -0,0 +1,60 @@
+#include "pthread_impl.h"
+
+int __pthread_mutex_unlock(pthread_mutex_t *m)
+{
+ pthread_t self;
+ int waiters = m->_m_waiters;
+ int cont;
+ int type = m->_m_type & 15;
+ int priv = (m->_m_type & 128) ^ 128;
+ int new = 0;
+ int old;
+
+ if (type != PTHREAD_MUTEX_NORMAL) {
+ self = __pthread_self();
+ old = m->_m_lock;
+ int own = old & 0x3fffffff;
+ if (own != self->tid)
+ return EPERM;
+ if ((type&3) == PTHREAD_MUTEX_RECURSIVE && m->_m_count)
+ return m->_m_count--, 0;
+ if ((type&4) && (old&0x40000000))
+ new = 0x7fffffff;
+ if (!priv) {
+ self->robust_list.pending = &m->_m_next;
+#ifdef __wasilibc_unmodified_upstream
+ __vm_lock();
+#endif
+ }
+ volatile void *prev = m->_m_prev;
+ volatile void *next = m->_m_next;
+ *(volatile void *volatile *)prev = next;
+ if (next != &self->robust_list.head) *(volatile void *volatile *)
+ ((char *)next - sizeof(void *)) = prev;
+ }
+#ifdef __wasilibc_unmodified_upstream
+ if (type&8) {
+ if (old<0 || a_cas(&m->_m_lock, old, new)!=old) {
+ if (new) a_store(&m->_m_waiters, -1);
+ __syscall(SYS_futex, &m->_m_lock, FUTEX_UNLOCK_PI|priv);
+ }
+ cont = 0;
+ waiters = 0;
+ } else {
+ cont = a_swap(&m->_m_lock, new);
+ }
+#else
+ cont = a_swap(&m->_m_lock, new);
+#endif
+ if (type != PTHREAD_MUTEX_NORMAL && !priv) {
+ self->robust_list.pending = 0;
+#ifdef __wasilibc_unmodified_upstream
+ __vm_unlock();
+#endif
+ }
+ if (waiters || cont<0)
+ __wake(&m->_m_lock, 1, priv);
+ return 0;
+}
+
+weak_alias(__pthread_mutex_unlock, pthread_mutex_unlock);
diff --git a/libc-top-half/musl/src/thread/pthread_mutexattr_destroy.c b/libc-top-half/musl/src/thread/pthread_mutexattr_destroy.c
new file mode 100644
index 0000000..9fd6974
--- /dev/null
+++ b/libc-top-half/musl/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/libc-top-half/musl/src/thread/pthread_mutexattr_init.c b/libc-top-half/musl/src/thread/pthread_mutexattr_init.c
new file mode 100644
index 0000000..0b72c1b
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_mutexattr_init.c
@@ -0,0 +1,7 @@
+#include "pthread_impl.h"
+
+int pthread_mutexattr_init(pthread_mutexattr_t *a)
+{
+ *a = (pthread_mutexattr_t){0};
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_mutexattr_setprotocol.c b/libc-top-half/musl/src/thread/pthread_mutexattr_setprotocol.c
new file mode 100644
index 0000000..84b02ba
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_mutexattr_setprotocol.c
@@ -0,0 +1,32 @@
+#include "pthread_impl.h"
+#include "syscall.h"
+
+static volatile int check_pi_result = -1;
+
+int pthread_mutexattr_setprotocol(pthread_mutexattr_t *a, int protocol)
+{
+ int r;
+ switch (protocol) {
+ case PTHREAD_PRIO_NONE:
+ a->__attr &= ~8;
+ return 0;
+ case PTHREAD_PRIO_INHERIT:
+#ifdef __wasilibc_unmodified_upstream
+ r = check_pi_result;
+ if (r < 0) {
+ volatile int lk = 0;
+ r = -__syscall(SYS_futex, &lk, FUTEX_LOCK_PI, 0, 0);
+ a_store(&check_pi_result, r);
+ }
+ if (r) return r;
+ a->__attr |= 8;
+ return 0;
+#else
+ return ENOTSUP;
+#endif
+ case PTHREAD_PRIO_PROTECT:
+ return ENOTSUP;
+ default:
+ return EINVAL;
+ }
+}
diff --git a/libc-top-half/musl/src/thread/pthread_mutexattr_setpshared.c b/libc-top-half/musl/src/thread/pthread_mutexattr_setpshared.c
new file mode 100644
index 0000000..100f6ff
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_mutexattr_setpshared.c
@@ -0,0 +1,9 @@
+#include "pthread_impl.h"
+
+int pthread_mutexattr_setpshared(pthread_mutexattr_t *a, int pshared)
+{
+ if (pshared > 1U) return EINVAL;
+ a->__attr &= ~128U;
+ a->__attr |= pshared<<7;
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_mutexattr_setrobust.c b/libc-top-half/musl/src/thread/pthread_mutexattr_setrobust.c
new file mode 100644
index 0000000..649a891
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_mutexattr_setrobust.c
@@ -0,0 +1,27 @@
+#include "pthread_impl.h"
+#include "syscall.h"
+
+static volatile int check_robust_result = -1;
+
+int pthread_mutexattr_setrobust(pthread_mutexattr_t *a, int robust)
+{
+#ifdef __wasilibc_unmodified_upstream
+ if (robust > 1U) return EINVAL;
+ if (robust) {
+ int r = check_robust_result;
+ if (r < 0) {
+ void *p;
+ size_t l;
+ r = -__syscall(SYS_get_robust_list, 0, &p, &l);
+ a_store(&check_robust_result, r);
+ }
+ if (r) return r;
+ a->__attr |= 4;
+ return 0;
+ }
+ a->__attr &= ~4;
+ return 0;
+#else
+ return EINVAL;
+#endif
+}
diff --git a/libc-top-half/musl/src/thread/pthread_mutexattr_settype.c b/libc-top-half/musl/src/thread/pthread_mutexattr_settype.c
new file mode 100644
index 0000000..cd7a80e
--- /dev/null
+++ b/libc-top-half/musl/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->__attr = (a->__attr & ~3) | type;
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_once.c b/libc-top-half/musl/src/thread/pthread_once.c
new file mode 100644
index 0000000..8e8d40a
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_once.c
@@ -0,0 +1,50 @@
+#include "pthread_impl.h"
+
+static void undo(void *control)
+{
+ /* Wake all waiters, since the waiter status is lost when
+ * resetting control to the initial state. */
+ if (a_swap(control, 0) == 3)
+ __wake(control, -1, 1);
+}
+
+hidden int __pthread_once_full(pthread_once_t *control, void (*init)(void))
+{
+ /* Try to enter initializing state. Four 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
+ * 3 - another thread is running init, waiters present; wait */
+
+ for (;;) switch (a_cas(control, 0, 1)) {
+ case 0:
+ pthread_cleanup_push(undo, control);
+ init();
+ pthread_cleanup_pop(0);
+
+ if (a_swap(control, 2) == 3)
+ __wake(control, -1, 1);
+ return 0;
+ case 1:
+ /* If this fails, so will __wait. */
+ a_cas(control, 1, 3);
+ case 3:
+ __wait(control, 0, 3, 1);
+ continue;
+ case 2:
+ return 0;
+ }
+}
+
+int __pthread_once(pthread_once_t *control, void (*init)(void))
+{
+ /* Return immediately if init finished before, but ensure that
+ * effects of the init routine are visible to the caller. */
+ if (*(volatile int *)control == 2) {
+ a_barrier();
+ return 0;
+ }
+ return __pthread_once_full(control, init);
+}
+
+weak_alias(__pthread_once, pthread_once);
diff --git a/libc-top-half/musl/src/thread/pthread_rwlock_destroy.c b/libc-top-half/musl/src/thread/pthread_rwlock_destroy.c
new file mode 100644
index 0000000..49ecfbd
--- /dev/null
+++ b/libc-top-half/musl/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/libc-top-half/musl/src/thread/pthread_rwlock_init.c b/libc-top-half/musl/src/thread/pthread_rwlock_init.c
new file mode 100644
index 0000000..a2c0b47
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_rwlock_init.c
@@ -0,0 +1,8 @@
+#include "pthread_impl.h"
+
+int pthread_rwlock_init(pthread_rwlock_t *restrict rw, const pthread_rwlockattr_t *restrict a)
+{
+ *rw = (pthread_rwlock_t){0};
+ if (a) rw->_rw_shared = a->__attr[0]*128;
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_rwlock_rdlock.c b/libc-top-half/musl/src/thread/pthread_rwlock_rdlock.c
new file mode 100644
index 0000000..8546c07
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_rwlock_rdlock.c
@@ -0,0 +1,8 @@
+#include "pthread_impl.h"
+
+int __pthread_rwlock_rdlock(pthread_rwlock_t *rw)
+{
+ return __pthread_rwlock_timedrdlock(rw, 0);
+}
+
+weak_alias(__pthread_rwlock_rdlock, pthread_rwlock_rdlock);
diff --git a/libc-top-half/musl/src/thread/pthread_rwlock_timedrdlock.c b/libc-top-half/musl/src/thread/pthread_rwlock_timedrdlock.c
new file mode 100644
index 0000000..8cdd8ec
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_rwlock_timedrdlock.c
@@ -0,0 +1,25 @@
+#include "pthread_impl.h"
+
+int __pthread_rwlock_timedrdlock(pthread_rwlock_t *restrict rw, const struct timespec *restrict at)
+{
+ int r, t;
+
+ r = pthread_rwlock_tryrdlock(rw);
+ if (r != EBUSY) return r;
+
+ int spins = 100;
+ while (spins-- && rw->_rw_lock && !rw->_rw_waiters) a_spin();
+
+ while ((r=__pthread_rwlock_tryrdlock(rw))==EBUSY) {
+ if (!(r=rw->_rw_lock) || (r&0x7fffffff)!=0x7fffffff) continue;
+ t = r | 0x80000000;
+ a_inc(&rw->_rw_waiters);
+ a_cas(&rw->_rw_lock, r, t);
+ r = __timedwait(&rw->_rw_lock, t, CLOCK_REALTIME, at, rw->_rw_shared^128);
+ a_dec(&rw->_rw_waiters);
+ if (r && r != EINTR) return r;
+ }
+ return r;
+}
+
+weak_alias(__pthread_rwlock_timedrdlock, pthread_rwlock_timedrdlock);
diff --git a/libc-top-half/musl/src/thread/pthread_rwlock_timedwrlock.c b/libc-top-half/musl/src/thread/pthread_rwlock_timedwrlock.c
new file mode 100644
index 0000000..d77706e
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_rwlock_timedwrlock.c
@@ -0,0 +1,25 @@
+#include "pthread_impl.h"
+
+int __pthread_rwlock_timedwrlock(pthread_rwlock_t *restrict rw, const struct timespec *restrict at)
+{
+ int r, t;
+
+ r = pthread_rwlock_trywrlock(rw);
+ if (r != EBUSY) return r;
+
+ int spins = 100;
+ while (spins-- && rw->_rw_lock && !rw->_rw_waiters) a_spin();
+
+ while ((r=__pthread_rwlock_trywrlock(rw))==EBUSY) {
+ if (!(r=rw->_rw_lock)) continue;
+ t = r | 0x80000000;
+ a_inc(&rw->_rw_waiters);
+ a_cas(&rw->_rw_lock, r, t);
+ r = __timedwait(&rw->_rw_lock, t, CLOCK_REALTIME, at, rw->_rw_shared^128);
+ a_dec(&rw->_rw_waiters);
+ if (r && r != EINTR) return r;
+ }
+ return r;
+}
+
+weak_alias(__pthread_rwlock_timedwrlock, pthread_rwlock_timedwrlock);
diff --git a/libc-top-half/musl/src/thread/pthread_rwlock_tryrdlock.c b/libc-top-half/musl/src/thread/pthread_rwlock_tryrdlock.c
new file mode 100644
index 0000000..c13bc9c
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_rwlock_tryrdlock.c
@@ -0,0 +1,15 @@
+#include "pthread_impl.h"
+
+int __pthread_rwlock_tryrdlock(pthread_rwlock_t *rw)
+{
+ int val, cnt;
+ do {
+ val = rw->_rw_lock;
+ cnt = val & 0x7fffffff;
+ if (cnt == 0x7fffffff) return EBUSY;
+ if (cnt == 0x7ffffffe) return EAGAIN;
+ } while (a_cas(&rw->_rw_lock, val, val+1) != val);
+ return 0;
+}
+
+weak_alias(__pthread_rwlock_tryrdlock, pthread_rwlock_tryrdlock);
diff --git a/libc-top-half/musl/src/thread/pthread_rwlock_trywrlock.c b/libc-top-half/musl/src/thread/pthread_rwlock_trywrlock.c
new file mode 100644
index 0000000..64d9d31
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_rwlock_trywrlock.c
@@ -0,0 +1,9 @@
+#include "pthread_impl.h"
+
+int __pthread_rwlock_trywrlock(pthread_rwlock_t *rw)
+{
+ if (a_cas(&rw->_rw_lock, 0, 0x7fffffff)) return EBUSY;
+ return 0;
+}
+
+weak_alias(__pthread_rwlock_trywrlock, pthread_rwlock_trywrlock);
diff --git a/libc-top-half/musl/src/thread/pthread_rwlock_unlock.c b/libc-top-half/musl/src/thread/pthread_rwlock_unlock.c
new file mode 100644
index 0000000..9ae27ad
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_rwlock_unlock.c
@@ -0,0 +1,20 @@
+#include "pthread_impl.h"
+
+int __pthread_rwlock_unlock(pthread_rwlock_t *rw)
+{
+ int val, cnt, waiters, new, priv = rw->_rw_shared^128;
+
+ do {
+ val = rw->_rw_lock;
+ cnt = val & 0x7fffffff;
+ waiters = rw->_rw_waiters;
+ new = (cnt == 0x7fffffff || cnt == 1) ? 0 : val-1;
+ } while (a_cas(&rw->_rw_lock, val, new) != val);
+
+ if (!new && (waiters || val<0))
+ __wake(&rw->_rw_lock, cnt, priv);
+
+ return 0;
+}
+
+weak_alias(__pthread_rwlock_unlock, pthread_rwlock_unlock);
diff --git a/libc-top-half/musl/src/thread/pthread_rwlock_wrlock.c b/libc-top-half/musl/src/thread/pthread_rwlock_wrlock.c
new file mode 100644
index 0000000..46a3b3a
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_rwlock_wrlock.c
@@ -0,0 +1,8 @@
+#include "pthread_impl.h"
+
+int __pthread_rwlock_wrlock(pthread_rwlock_t *rw)
+{
+ return __pthread_rwlock_timedwrlock(rw, 0);
+}
+
+weak_alias(__pthread_rwlock_wrlock, pthread_rwlock_wrlock);
diff --git a/libc-top-half/musl/src/thread/pthread_rwlockattr_destroy.c b/libc-top-half/musl/src/thread/pthread_rwlockattr_destroy.c
new file mode 100644
index 0000000..fc8d611
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_rwlockattr_destroy.c
@@ -0,0 +1,6 @@
+#include "pthread_impl.h"
+
+int pthread_rwlockattr_destroy(pthread_rwlockattr_t *a)
+{
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_rwlockattr_init.c b/libc-top-half/musl/src/thread/pthread_rwlockattr_init.c
new file mode 100644
index 0000000..e742069
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_rwlockattr_init.c
@@ -0,0 +1,7 @@
+#include "pthread_impl.h"
+
+int pthread_rwlockattr_init(pthread_rwlockattr_t *a)
+{
+ *a = (pthread_rwlockattr_t){0};
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_rwlockattr_setpshared.c b/libc-top-half/musl/src/thread/pthread_rwlockattr_setpshared.c
new file mode 100644
index 0000000..e706197
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_rwlockattr_setpshared.c
@@ -0,0 +1,8 @@
+#include "pthread_impl.h"
+
+int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *a, int pshared)
+{
+ if (pshared > 1U) return EINVAL;
+ a->__attr[0] = pshared;
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_self.c b/libc-top-half/musl/src/thread/pthread_self.c
new file mode 100644
index 0000000..1f3eee1
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_self.c
@@ -0,0 +1,15 @@
+#include "pthread_impl.h"
+#include <threads.h>
+
+#if !defined(__wasilibc_unmodified_upstream) && defined(__wasm__) && \
+ defined(_REENTRANT)
+_Thread_local struct pthread __wasilibc_pthread_self;
+#endif
+
+static pthread_t __pthread_self_internal()
+{
+ return __pthread_self();
+}
+
+weak_alias(__pthread_self_internal, pthread_self);
+weak_alias(__pthread_self_internal, thrd_current);
diff --git a/libc-top-half/musl/src/thread/pthread_setattr_default_np.c b/libc-top-half/musl/src/thread/pthread_setattr_default_np.c
new file mode 100644
index 0000000..5848622
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_setattr_default_np.c
@@ -0,0 +1,37 @@
+#define _GNU_SOURCE
+#include "pthread_impl.h"
+#include <string.h>
+
+#define MIN(a,b) ((a)<(b) ? (a) : (b))
+#define MAX(a,b) ((a)>(b) ? (a) : (b))
+
+int pthread_setattr_default_np(const pthread_attr_t *attrp)
+{
+ /* Reject anything in the attr object other than stack/guard size. */
+ pthread_attr_t tmp = *attrp, zero = { 0 };
+ tmp._a_stacksize = 0;
+ tmp._a_guardsize = 0;
+ if (memcmp(&tmp, &zero, sizeof tmp))
+ return EINVAL;
+
+ unsigned stack = MIN(attrp->_a_stacksize, DEFAULT_STACK_MAX);
+ unsigned guard = MIN(attrp->_a_guardsize, DEFAULT_GUARD_MAX);
+
+ __inhibit_ptc();
+ __default_stacksize = MAX(__default_stacksize, stack);
+ __default_guardsize = MAX(__default_guardsize, guard);
+ __release_ptc();
+
+ return 0;
+}
+
+int pthread_getattr_default_np(pthread_attr_t *attrp)
+{
+ __acquire_ptc();
+ *attrp = (pthread_attr_t) {
+ ._a_stacksize = __default_stacksize,
+ ._a_guardsize = __default_guardsize,
+ };
+ __release_ptc();
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_setcancelstate.c b/libc-top-half/musl/src/thread/pthread_setcancelstate.c
new file mode 100644
index 0000000..4f7a00e
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_setcancelstate.c
@@ -0,0 +1,14 @@
+#include "pthread_impl.h"
+
+int __pthread_setcancelstate(int new, int *old)
+{
+#if defined(__wasilibc_unmodified_upstream) || defined(_REENTRANT)
+ if (new > 2U) return EINVAL;
+ struct pthread *self = __pthread_self();
+ if (old) *old = self->canceldisable;
+ self->canceldisable = new;
+#endif
+ return 0;
+}
+
+weak_alias(__pthread_setcancelstate, pthread_setcancelstate);
diff --git a/libc-top-half/musl/src/thread/pthread_setcanceltype.c b/libc-top-half/musl/src/thread/pthread_setcanceltype.c
new file mode 100644
index 0000000..bf0a3f3
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_setcanceltype.c
@@ -0,0 +1,11 @@
+#include "pthread_impl.h"
+
+int pthread_setcanceltype(int new, int *old)
+{
+ struct pthread *self = __pthread_self();
+ if (new > 1U) return EINVAL;
+ if (old) *old = self->cancelasync;
+ self->cancelasync = new;
+ if (new) pthread_testcancel();
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_setconcurrency.c b/libc-top-half/musl/src/thread/pthread_setconcurrency.c
new file mode 100644
index 0000000..091abf9
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_setconcurrency.c
@@ -0,0 +1,9 @@
+#include <pthread.h>
+#include <errno.h>
+
+int pthread_setconcurrency(int val)
+{
+ if (val < 0) return EINVAL;
+ if (val > 0) return EAGAIN;
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_setname_np.c b/libc-top-half/musl/src/thread/pthread_setname_np.c
new file mode 100644
index 0000000..fc2d230
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_setname_np.c
@@ -0,0 +1,26 @@
+#define _GNU_SOURCE
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/prctl.h>
+
+#include "pthread_impl.h"
+
+int pthread_setname_np(pthread_t thread, const char *name)
+{
+ int fd, cs, status = 0;
+ char f[sizeof "/proc/self/task//comm" + 3*sizeof(int)];
+ size_t len;
+
+ if ((len = strnlen(name, 16)) > 15) return ERANGE;
+
+ if (thread == pthread_self())
+ return prctl(PR_SET_NAME, (unsigned long)name, 0UL, 0UL, 0UL) ? errno : 0;
+
+ snprintf(f, sizeof f, "/proc/self/task/%d/comm", thread->tid);
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
+ if ((fd = open(f, O_WRONLY|O_CLOEXEC)) < 0 || write(fd, name, len) < 0) status = errno;
+ if (fd >= 0) close(fd);
+ pthread_setcancelstate(cs, 0);
+ return status;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_setschedparam.c b/libc-top-half/musl/src/thread/pthread_setschedparam.c
new file mode 100644
index 0000000..76d4d45
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_setschedparam.c
@@ -0,0 +1,14 @@
+#include "pthread_impl.h"
+#include "lock.h"
+
+int pthread_setschedparam(pthread_t t, int policy, const struct sched_param *param)
+{
+ int r;
+ sigset_t set;
+ __block_app_sigs(&set);
+ LOCK(t->killlock);
+ r = !t->tid ? ESRCH : -__syscall(SYS_sched_setscheduler, t->tid, policy, param);
+ UNLOCK(t->killlock);
+ __restore_sigs(&set);
+ return r;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_setschedprio.c b/libc-top-half/musl/src/thread/pthread_setschedprio.c
new file mode 100644
index 0000000..fc2e13d
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_setschedprio.c
@@ -0,0 +1,14 @@
+#include "pthread_impl.h"
+#include "lock.h"
+
+int pthread_setschedprio(pthread_t t, int prio)
+{
+ int r;
+ sigset_t set;
+ __block_app_sigs(&set);
+ LOCK(t->killlock);
+ r = !t->tid ? ESRCH : -__syscall(SYS_sched_setparam, t->tid, &prio);
+ UNLOCK(t->killlock);
+ __restore_sigs(&set);
+ return r;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_setspecific.c b/libc-top-half/musl/src/thread/pthread_setspecific.c
new file mode 100644
index 0000000..55e46a8
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_setspecific.c
@@ -0,0 +1,12 @@
+#include "pthread_impl.h"
+
+int pthread_setspecific(pthread_key_t k, const void *x)
+{
+ struct pthread *self = __pthread_self();
+ /* Avoid unnecessary COW */
+ if (self->tsd[k] != x) {
+ self->tsd[k] = (void *)x;
+ self->tsd_used = 1;
+ }
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_sigmask.c b/libc-top-half/musl/src/thread/pthread_sigmask.c
new file mode 100644
index 0000000..f188782
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_sigmask.c
@@ -0,0 +1,19 @@
+#include <signal.h>
+#include <errno.h>
+#include "syscall.h"
+
+int pthread_sigmask(int how, const sigset_t *restrict set, sigset_t *restrict old)
+{
+ int ret;
+ if (set && (unsigned)how - SIG_BLOCK > 2U) return EINVAL;
+ ret = -__syscall(SYS_rt_sigprocmask, how, set, old, _NSIG/8);
+ if (!ret && old) {
+ if (sizeof old->__bits[0] == 8) {
+ old->__bits[0] &= ~0x380000000ULL;
+ } else {
+ old->__bits[0] &= ~0x80000000UL;
+ old->__bits[1] &= ~0x3UL;
+ }
+ }
+ return ret;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_spin_destroy.c b/libc-top-half/musl/src/thread/pthread_spin_destroy.c
new file mode 100644
index 0000000..e65a820
--- /dev/null
+++ b/libc-top-half/musl/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/libc-top-half/musl/src/thread/pthread_spin_init.c b/libc-top-half/musl/src/thread/pthread_spin_init.c
new file mode 100644
index 0000000..681881c
--- /dev/null
+++ b/libc-top-half/musl/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/libc-top-half/musl/src/thread/pthread_spin_lock.c b/libc-top-half/musl/src/thread/pthread_spin_lock.c
new file mode 100644
index 0000000..ded2b65
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_spin_lock.c
@@ -0,0 +1,8 @@
+#include "pthread_impl.h"
+#include <errno.h>
+
+int pthread_spin_lock(pthread_spinlock_t *s)
+{
+ while (*(volatile int *)s || a_cas(s, 0, EBUSY)) a_spin();
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_spin_trylock.c b/libc-top-half/musl/src/thread/pthread_spin_trylock.c
new file mode 100644
index 0000000..5284fda
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_spin_trylock.c
@@ -0,0 +1,7 @@
+#include "pthread_impl.h"
+#include <errno.h>
+
+int pthread_spin_trylock(pthread_spinlock_t *s)
+{
+ return a_cas(s, 0, EBUSY);
+}
diff --git a/libc-top-half/musl/src/thread/pthread_spin_unlock.c b/libc-top-half/musl/src/thread/pthread_spin_unlock.c
new file mode 100644
index 0000000..724d9e0
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_spin_unlock.c
@@ -0,0 +1,7 @@
+#include "pthread_impl.h"
+
+int pthread_spin_unlock(pthread_spinlock_t *s)
+{
+ a_store(s, 0);
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/pthread_testcancel.c b/libc-top-half/musl/src/thread/pthread_testcancel.c
new file mode 100644
index 0000000..d772449
--- /dev/null
+++ b/libc-top-half/musl/src/thread/pthread_testcancel.c
@@ -0,0 +1,14 @@
+#include "pthread_impl.h"
+
+static void dummy()
+{
+}
+
+weak_alias(dummy, __testcancel);
+
+void __pthread_testcancel()
+{
+ __testcancel();
+}
+
+weak_alias(__pthread_testcancel, pthread_testcancel);
diff --git a/libc-top-half/musl/src/thread/riscv64/__set_thread_area.s b/libc-top-half/musl/src/thread/riscv64/__set_thread_area.s
new file mode 100644
index 0000000..828154d
--- /dev/null
+++ b/libc-top-half/musl/src/thread/riscv64/__set_thread_area.s
@@ -0,0 +1,6 @@
+.global __set_thread_area
+.type __set_thread_area, %function
+__set_thread_area:
+ mv tp, a0
+ li a0, 0
+ ret
diff --git a/libc-top-half/musl/src/thread/riscv64/__unmapself.s b/libc-top-half/musl/src/thread/riscv64/__unmapself.s
new file mode 100644
index 0000000..2849119
--- /dev/null
+++ b/libc-top-half/musl/src/thread/riscv64/__unmapself.s
@@ -0,0 +1,7 @@
+.global __unmapself
+.type __unmapself, %function
+__unmapself:
+ li a7, 215 # SYS_munmap
+ ecall
+ li a7, 93 # SYS_exit
+ ecall
diff --git a/libc-top-half/musl/src/thread/riscv64/clone.s b/libc-top-half/musl/src/thread/riscv64/clone.s
new file mode 100644
index 0000000..db90824
--- /dev/null
+++ b/libc-top-half/musl/src/thread/riscv64/clone.s
@@ -0,0 +1,34 @@
+# __clone(func, stack, flags, arg, ptid, tls, ctid)
+# a0, a1, a2, a3, a4, a5, a6
+
+# syscall(SYS_clone, flags, stack, ptid, tls, ctid)
+# a7 a0, a1, a2, a3, a4
+
+.global __clone
+.type __clone, %function
+__clone:
+ # Save func and arg to stack
+ addi a1, a1, -16
+ sd a0, 0(a1)
+ sd a3, 8(a1)
+
+ # Call SYS_clone
+ mv a0, a2
+ mv a2, a4
+ mv a3, a5
+ mv a4, a6
+ li a7, 220 # SYS_clone
+ ecall
+
+ beqz a0, 1f
+ # Parent
+ ret
+
+ # Child
+1: ld a1, 0(sp)
+ ld a0, 8(sp)
+ jalr a1
+
+ # Exit
+ li a7, 93 # SYS_exit
+ ecall
diff --git a/libc-top-half/musl/src/thread/riscv64/syscall_cp.s b/libc-top-half/musl/src/thread/riscv64/syscall_cp.s
new file mode 100644
index 0000000..eeef639
--- /dev/null
+++ b/libc-top-half/musl/src/thread/riscv64/syscall_cp.s
@@ -0,0 +1,29 @@
+.global __cp_begin
+.hidden __cp_begin
+.global __cp_end
+.hidden __cp_end
+.global __cp_cancel
+.hidden __cp_cancel
+.hidden __cancel
+.global __syscall_cp_asm
+.hidden __syscall_cp_asm
+.type __syscall_cp_asm, %function
+__syscall_cp_asm:
+__cp_begin:
+ lw t0, 0(a0)
+ bnez t0, __cp_cancel
+
+ mv t0, a1
+ mv a0, a2
+ mv a1, a3
+ mv a2, a4
+ mv a3, a5
+ mv a4, a6
+ mv a5, a7
+ ld a6, 0(sp)
+ mv a7, t0
+ ecall
+__cp_end:
+ ret
+__cp_cancel:
+ tail __cancel
diff --git a/libc-top-half/musl/src/thread/s390x/__set_thread_area.s b/libc-top-half/musl/src/thread/s390x/__set_thread_area.s
new file mode 100644
index 0000000..00a11e2
--- /dev/null
+++ b/libc-top-half/musl/src/thread/s390x/__set_thread_area.s
@@ -0,0 +1,10 @@
+.text
+.global __set_thread_area
+.hidden __set_thread_area
+.type __set_thread_area, %function
+__set_thread_area:
+ sar %a1, %r2
+ srlg %r2, %r2, 32
+ sar %a0, %r2
+ lghi %r2, 0
+ br %r14
diff --git a/libc-top-half/musl/src/thread/s390x/__tls_get_offset.s b/libc-top-half/musl/src/thread/s390x/__tls_get_offset.s
new file mode 100644
index 0000000..8ee92de
--- /dev/null
+++ b/libc-top-half/musl/src/thread/s390x/__tls_get_offset.s
@@ -0,0 +1,17 @@
+ .global __tls_get_offset
+ .type __tls_get_offset,%function
+__tls_get_offset:
+ stmg %r14, %r15, 112(%r15)
+ aghi %r15, -160
+
+ la %r2, 0(%r2, %r12)
+ brasl %r14, __tls_get_addr
+
+ ear %r1, %a0
+ sllg %r1, %r1, 32
+ ear %r1, %a1
+
+ sgr %r2, %r1
+
+ lmg %r14, %r15, 272(%r15)
+ br %r14
diff --git a/libc-top-half/musl/src/thread/s390x/__unmapself.s b/libc-top-half/musl/src/thread/s390x/__unmapself.s
new file mode 100644
index 0000000..48b312c
--- /dev/null
+++ b/libc-top-half/musl/src/thread/s390x/__unmapself.s
@@ -0,0 +1,6 @@
+.text
+.global __unmapself
+.type __unmapself, @function
+__unmapself:
+ svc 91 # SYS_munmap
+ svc 1 # SYS_exit
diff --git a/libc-top-half/musl/src/thread/s390x/clone.s b/libc-top-half/musl/src/thread/s390x/clone.s
new file mode 100644
index 0000000..2125f20
--- /dev/null
+++ b/libc-top-half/musl/src/thread/s390x/clone.s
@@ -0,0 +1,54 @@
+.text
+.global __clone
+.hidden __clone
+.type __clone, %function
+__clone:
+ # int clone(
+ # fn, a = r2
+ # stack, b = r3
+ # flags, c = r4
+ # arg, d = r5
+ # ptid, e = r6
+ # tls, f = *(r15+160)
+ # ctid) g = *(r15+168)
+ #
+ # pseudo C code:
+ # tid = syscall(SYS_clone,b,c,e,g,f);
+ # if (!tid) syscall(SYS_exit, a(d));
+ # return tid;
+
+ # preserve call-saved register used as syscall arg
+ stg %r6, 48(%r15)
+
+ # create initial stack frame for new thread
+ nill %r3, 0xfff8
+ aghi %r3, -160
+ lghi %r0, 0
+ stg %r0, 0(%r3)
+
+ # save fn and arg to child stack
+ stg %r2, 8(%r3)
+ stg %r5, 16(%r3)
+
+ # shuffle args into correct registers and call SYS_clone
+ lgr %r2, %r3
+ lgr %r3, %r4
+ lgr %r4, %r6
+ lg %r5, 168(%r15)
+ lg %r6, 160(%r15)
+ svc 120
+
+ # restore call-saved register
+ lg %r6, 48(%r15)
+
+ # if error or if we're the parent, return
+ ltgr %r2, %r2
+ bnzr %r14
+
+ # we're the child. call fn(arg)
+ lg %r1, 8(%r15)
+ lg %r2, 16(%r15)
+ basr %r14, %r1
+
+ # call SYS_exit. exit code is already in r2 from fn return value
+ svc 1
diff --git a/libc-top-half/musl/src/thread/s390x/syscall_cp.s b/libc-top-half/musl/src/thread/s390x/syscall_cp.s
new file mode 100644
index 0000000..d094cbf
--- /dev/null
+++ b/libc-top-half/musl/src/thread/s390x/syscall_cp.s
@@ -0,0 +1,34 @@
+ .global __cp_begin
+ .hidden __cp_begin
+ .global __cp_end
+ .hidden __cp_end
+ .global __cp_cancel
+ .hidden __cp_cancel
+ .hidden __cancel
+ .global __syscall_cp_asm
+ .hidden __syscall_cp_asm
+ .text
+ .type __syscall_cp_asm,%function
+__syscall_cp_asm:
+__cp_begin:
+ icm %r2, 15, 0(%r2)
+ jne __cp_cancel
+
+ stg %r6, 48(%r15)
+ stg %r7, 56(%r15)
+ lgr %r1, %r3
+ lgr %r2, %r4
+ lgr %r3, %r5
+ lgr %r4, %r6
+ lg %r5, 160(%r15)
+ lg %r6, 168(%r15)
+ lg %r7, 176(%r15)
+ svc 0
+
+__cp_end:
+ lg %r7, 56(%r15)
+ lg %r6, 48(%r15)
+ br %r14
+
+__cp_cancel:
+ jg __cancel
diff --git a/libc-top-half/musl/src/thread/sem_destroy.c b/libc-top-half/musl/src/thread/sem_destroy.c
new file mode 100644
index 0000000..f4aced5
--- /dev/null
+++ b/libc-top-half/musl/src/thread/sem_destroy.c
@@ -0,0 +1,6 @@
+#include <semaphore.h>
+
+int sem_destroy(sem_t *sem)
+{
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/sem_getvalue.c b/libc-top-half/musl/src/thread/sem_getvalue.c
new file mode 100644
index 0000000..d9d8307
--- /dev/null
+++ b/libc-top-half/musl/src/thread/sem_getvalue.c
@@ -0,0 +1,8 @@
+#include <semaphore.h>
+
+int sem_getvalue(sem_t *restrict sem, int *restrict valp)
+{
+ int val = sem->__val[0];
+ *valp = val < 0 ? 0 : val;
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/sem_init.c b/libc-top-half/musl/src/thread/sem_init.c
new file mode 100644
index 0000000..5509243
--- /dev/null
+++ b/libc-top-half/musl/src/thread/sem_init.c
@@ -0,0 +1,15 @@
+#include <semaphore.h>
+#include <limits.h>
+#include <errno.h>
+
+int sem_init(sem_t *sem, int pshared, unsigned value)
+{
+ if (value > SEM_VALUE_MAX) {
+ errno = EINVAL;
+ return -1;
+ }
+ sem->__val[0] = value;
+ sem->__val[1] = 0;
+ sem->__val[2] = pshared ? 0 : 128;
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/sem_open.c b/libc-top-half/musl/src/thread/sem_open.c
new file mode 100644
index 0000000..0ad29de
--- /dev/null
+++ b/libc-top-half/musl/src/thread/sem_open.c
@@ -0,0 +1,182 @@
+#include <semaphore.h>
+#include <sys/mman.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <time.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include "lock.h"
+#include "fork_impl.h"
+
+#define malloc __libc_malloc
+#define calloc __libc_calloc
+#define realloc undef
+#define free undef
+
+static struct {
+ ino_t ino;
+ sem_t *sem;
+ int refcnt;
+} *semtab;
+static volatile int lock[1];
+volatile int *const __sem_open_lockptr = lock;
+
+#define FLAGS (O_RDWR|O_NOFOLLOW|O_CLOEXEC|O_NONBLOCK)
+
+sem_t *sem_open(const char *name, int flags, ...)
+{
+ va_list ap;
+ mode_t mode;
+ unsigned value;
+ int fd, i, e, slot, first=1, cnt, cs;
+ sem_t newsem;
+ void *map;
+ char tmp[64];
+ struct timespec ts;
+ struct stat st;
+ char buf[NAME_MAX+10];
+
+ if (!(name = __shm_mapname(name, buf)))
+ return SEM_FAILED;
+
+ LOCK(lock);
+ /* Allocate table if we don't have one yet */
+ if (!semtab && !(semtab = calloc(sizeof *semtab, SEM_NSEMS_MAX))) {
+ UNLOCK(lock);
+ return SEM_FAILED;
+ }
+
+ /* Reserve a slot in case this semaphore is not mapped yet;
+ * this is necessary because there is no way to handle
+ * failures after creation of the file. */
+ slot = -1;
+ for (cnt=i=0; i<SEM_NSEMS_MAX; i++) {
+ cnt += semtab[i].refcnt;
+ if (!semtab[i].sem && slot < 0) slot = i;
+ }
+ /* Avoid possibility of overflow later */
+ if (cnt == INT_MAX || slot < 0) {
+ errno = EMFILE;
+ UNLOCK(lock);
+ return SEM_FAILED;
+ }
+ /* Dummy pointer to make a reservation */
+ semtab[slot].sem = (sem_t *)-1;
+ UNLOCK(lock);
+
+ flags &= (O_CREAT|O_EXCL);
+
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
+
+ /* Early failure check for exclusive open; otherwise the case
+ * where the semaphore already exists is expensive. */
+ if (flags == (O_CREAT|O_EXCL) && access(name, F_OK) == 0) {
+ errno = EEXIST;
+ goto fail;
+ }
+
+ for (;;) {
+ /* If exclusive mode is not requested, try opening an
+ * existing file first and fall back to creation. */
+ if (flags != (O_CREAT|O_EXCL)) {
+ fd = open(name, FLAGS);
+ if (fd >= 0) {
+ if (fstat(fd, &st) < 0 ||
+ (map = mmap(0, sizeof(sem_t), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
+ close(fd);
+ goto fail;
+ }
+ close(fd);
+ break;
+ }
+ if (errno != ENOENT)
+ goto fail;
+ }
+ if (!(flags & O_CREAT))
+ goto fail;
+ if (first) {
+ first = 0;
+ va_start(ap, flags);
+ mode = va_arg(ap, mode_t) & 0666;
+ value = va_arg(ap, unsigned);
+ va_end(ap);
+ if (value > SEM_VALUE_MAX) {
+ errno = EINVAL;
+ goto fail;
+ }
+ sem_init(&newsem, 1, value);
+ }
+ /* Create a temp file with the new semaphore contents
+ * and attempt to atomically link it as the new name */
+ clock_gettime(CLOCK_REALTIME, &ts);
+ snprintf(tmp, sizeof(tmp), "/dev/shm/tmp-%d", (int)ts.tv_nsec);
+ fd = open(tmp, O_CREAT|O_EXCL|FLAGS, mode);
+ if (fd < 0) {
+ if (errno == EEXIST) continue;
+ goto fail;
+ }
+ if (write(fd, &newsem, sizeof newsem) != sizeof newsem || fstat(fd, &st) < 0 ||
+ (map = mmap(0, sizeof(sem_t), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
+ close(fd);
+ unlink(tmp);
+ goto fail;
+ }
+ close(fd);
+ e = link(tmp, name) ? errno : 0;
+ unlink(tmp);
+ if (!e) break;
+ munmap(map, sizeof(sem_t));
+ /* Failure is only fatal when doing an exclusive open;
+ * otherwise, next iteration will try to open the
+ * existing file. */
+ if (e != EEXIST || flags == (O_CREAT|O_EXCL))
+ goto fail;
+ }
+
+ /* See if the newly mapped semaphore is already mapped. If
+ * so, unmap the new mapping and use the existing one. Otherwise,
+ * add it to the table of mapped semaphores. */
+ LOCK(lock);
+ for (i=0; i<SEM_NSEMS_MAX && semtab[i].ino != st.st_ino; i++);
+ if (i<SEM_NSEMS_MAX) {
+ munmap(map, sizeof(sem_t));
+ semtab[slot].sem = 0;
+ slot = i;
+ map = semtab[i].sem;
+ }
+ semtab[slot].refcnt++;
+ semtab[slot].sem = map;
+ semtab[slot].ino = st.st_ino;
+ UNLOCK(lock);
+ pthread_setcancelstate(cs, 0);
+ return map;
+
+fail:
+ pthread_setcancelstate(cs, 0);
+ LOCK(lock);
+ semtab[slot].sem = 0;
+ UNLOCK(lock);
+ return SEM_FAILED;
+}
+
+int sem_close(sem_t *sem)
+{
+ int i;
+ LOCK(lock);
+ for (i=0; i<SEM_NSEMS_MAX && semtab[i].sem != sem; i++);
+ if (--semtab[i].refcnt) {
+ UNLOCK(lock);
+ return 0;
+ }
+ semtab[i].sem = 0;
+ semtab[i].ino = 0;
+ UNLOCK(lock);
+ munmap(sem, sizeof *sem);
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/sem_post.c b/libc-top-half/musl/src/thread/sem_post.c
new file mode 100644
index 0000000..31e3293
--- /dev/null
+++ b/libc-top-half/musl/src/thread/sem_post.c
@@ -0,0 +1,17 @@
+#include <semaphore.h>
+#include "pthread_impl.h"
+
+int sem_post(sem_t *sem)
+{
+ int val, waiters, priv = sem->__val[2];
+ do {
+ val = sem->__val[0];
+ waiters = sem->__val[1];
+ if (val == SEM_VALUE_MAX) {
+ errno = EOVERFLOW;
+ return -1;
+ }
+ } while (a_cas(sem->__val, val, val+1+(val<0)) != val);
+ if (val<0 || waiters) __wake(sem->__val, 1, priv);
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/sem_timedwait.c b/libc-top-half/musl/src/thread/sem_timedwait.c
new file mode 100644
index 0000000..58d3ebf
--- /dev/null
+++ b/libc-top-half/musl/src/thread/sem_timedwait.c
@@ -0,0 +1,31 @@
+#include <semaphore.h>
+#include "pthread_impl.h"
+
+static void cleanup(void *p)
+{
+ a_dec(p);
+}
+
+int sem_timedwait(sem_t *restrict sem, const struct timespec *restrict at)
+{
+ pthread_testcancel();
+
+ if (!sem_trywait(sem)) return 0;
+
+ int spins = 100;
+ while (spins-- && sem->__val[0] <= 0 && !sem->__val[1]) a_spin();
+
+ while (sem_trywait(sem)) {
+ int r;
+ a_inc(sem->__val+1);
+ a_cas(sem->__val, 0, -1);
+ pthread_cleanup_push(cleanup, (void *)(sem->__val+1));
+ r = __timedwait_cp(sem->__val, -1, CLOCK_REALTIME, at, sem->__val[2]);
+ pthread_cleanup_pop(1);
+ if (r) {
+ errno = r;
+ return -1;
+ }
+ }
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/sem_trywait.c b/libc-top-half/musl/src/thread/sem_trywait.c
new file mode 100644
index 0000000..04edf46
--- /dev/null
+++ b/libc-top-half/musl/src/thread/sem_trywait.c
@@ -0,0 +1,13 @@
+#include <semaphore.h>
+#include "pthread_impl.h"
+
+int sem_trywait(sem_t *sem)
+{
+ int val;
+ while ((val=sem->__val[0]) > 0) {
+ int new = val-1-(val==1 && sem->__val[1]);
+ if (a_cas(sem->__val, val, new)==val) return 0;
+ }
+ errno = EAGAIN;
+ return -1;
+}
diff --git a/libc-top-half/musl/src/thread/sem_unlink.c b/libc-top-half/musl/src/thread/sem_unlink.c
new file mode 100644
index 0000000..c06134b
--- /dev/null
+++ b/libc-top-half/musl/src/thread/sem_unlink.c
@@ -0,0 +1,7 @@
+#include <semaphore.h>
+#include <sys/mman.h>
+
+int sem_unlink(const char *name)
+{
+ return shm_unlink(name);
+}
diff --git a/libc-top-half/musl/src/thread/sem_wait.c b/libc-top-half/musl/src/thread/sem_wait.c
new file mode 100644
index 0000000..264194f
--- /dev/null
+++ b/libc-top-half/musl/src/thread/sem_wait.c
@@ -0,0 +1,6 @@
+#include <semaphore.h>
+
+int sem_wait(sem_t *sem)
+{
+ return sem_timedwait(sem, 0);
+}
diff --git a/libc-top-half/musl/src/thread/sh/__set_thread_area.c b/libc-top-half/musl/src/thread/sh/__set_thread_area.c
new file mode 100644
index 0000000..34264bd
--- /dev/null
+++ b/libc-top-half/musl/src/thread/sh/__set_thread_area.c
@@ -0,0 +1,37 @@
+#include "pthread_impl.h"
+#include "libc.h"
+#include <elf.h>
+
+/* Also perform sh-specific init */
+
+#define CPU_HAS_LLSC 0x0040
+#define CPU_HAS_CAS_L 0x0400
+
+extern hidden const char __sh_cas_gusa[], __sh_cas_llsc[], __sh_cas_imask[], __sh_cas_cas_l[];
+
+hidden const void *__sh_cas_ptr;
+
+hidden unsigned __sh_nommu;
+
+int __set_thread_area(void *p)
+{
+ size_t *aux;
+ __asm__ __volatile__ ( "ldc %0, gbr" : : "r"(p) : "memory" );
+#ifndef __SH4A__
+ __sh_cas_ptr = __sh_cas_gusa;
+#if !defined(__SH3__) && !defined(__SH4__)
+ for (aux=libc.auxv; *aux; aux+=2) {
+ if (*aux != AT_PLATFORM) continue;
+ const char *s = (void *)aux[1];
+ if (s[0]!='s' || s[1]!='h' || s[2]!='2' || s[3]-'0'<10u) break;
+ __sh_cas_ptr = __sh_cas_imask;
+ __sh_nommu = 1;
+ }
+#endif
+ if (__hwcap & CPU_HAS_CAS_L)
+ __sh_cas_ptr = __sh_cas_cas_l;
+ else if (__hwcap & CPU_HAS_LLSC)
+ __sh_cas_ptr = __sh_cas_llsc;
+#endif
+ return 0;
+}
diff --git a/libc-top-half/musl/src/thread/sh/__unmapself.c b/libc-top-half/musl/src/thread/sh/__unmapself.c
new file mode 100644
index 0000000..35fb3c9
--- /dev/null
+++ b/libc-top-half/musl/src/thread/sh/__unmapself.c
@@ -0,0 +1,24 @@
+#include "pthread_impl.h"
+
+hidden void __unmapself_sh_mmu(void *, size_t);
+hidden void __unmapself_sh_nommu(void *, size_t);
+
+#if !defined(__SH3__) && !defined(__SH4__)
+#define __unmapself __unmapself_sh_nommu
+#include "dynlink.h"
+#undef CRTJMP
+#define CRTJMP(pc,sp) __asm__ __volatile__( \
+ "mov.l @%0+,r0 ; mov.l @%0,r12 ; jmp @r0 ; mov %1,r15" \
+ : : "r"(pc), "r"(sp) : "r0", "memory" )
+#include "../__unmapself.c"
+#undef __unmapself
+extern hidden unsigned __sh_nommu;
+#else
+#define __sh_nommu 0
+#endif
+
+void __unmapself(void *base, size_t size)
+{
+ if (__sh_nommu) __unmapself_sh_nommu(base, size);
+ else __unmapself_sh_mmu(base, size);
+}
diff --git a/libc-top-half/musl/src/thread/sh/__unmapself_mmu.s b/libc-top-half/musl/src/thread/sh/__unmapself_mmu.s
new file mode 100644
index 0000000..688087b
--- /dev/null
+++ b/libc-top-half/musl/src/thread/sh/__unmapself_mmu.s
@@ -0,0 +1,23 @@
+.text
+.global __unmapself_sh_mmu
+.hidden __unmapself_sh_mmu
+.type __unmapself_sh_mmu, @function
+__unmapself_sh_mmu:
+ mov #91, r3 ! SYS_munmap
+ trapa #31
+
+ or r0, r0
+ or r0, r0
+ or r0, r0
+ or r0, r0
+ or r0, r0
+
+ mov #1, r3 ! SYS_exit
+ mov #0, r4
+ trapa #31
+
+ or r0, r0
+ or r0, r0
+ or r0, r0
+ or r0, r0
+ or r0, r0
diff --git a/libc-top-half/musl/src/thread/sh/atomics.s b/libc-top-half/musl/src/thread/sh/atomics.s
new file mode 100644
index 0000000..9d9fcb6
--- /dev/null
+++ b/libc-top-half/musl/src/thread/sh/atomics.s
@@ -0,0 +1,65 @@
+/* Contract for all versions is same as cas.l r2,r3,@r0
+ * pr and r1 are also clobbered (by jsr & r1 as temp).
+ * r0,r2,r4-r15 must be preserved.
+ * r3 contains result (==r2 iff cas succeeded). */
+
+ .align 2
+.global __sh_cas_gusa
+.hidden __sh_cas_gusa
+__sh_cas_gusa:
+ mov.l r5,@-r15
+ mov.l r4,@-r15
+ mov r0,r4
+ mova 1f,r0
+ mov r15,r1
+ mov #(0f-1f),r15
+0: mov.l @r4,r5
+ cmp/eq r5,r2
+ bf 1f
+ mov.l r3,@r4
+1: mov r1,r15
+ mov r5,r3
+ mov r4,r0
+ mov.l @r15+,r4
+ rts
+ mov.l @r15+,r5
+
+.global __sh_cas_llsc
+.hidden __sh_cas_llsc
+__sh_cas_llsc:
+ mov r0,r1
+ .word 0x00ab /* synco */
+0: .word 0x0163 /* movli.l @r1,r0 */
+ cmp/eq r0,r2
+ bf 1f
+ mov r3,r0
+ .word 0x0173 /* movco.l r0,@r1 */
+ bf 0b
+ mov r2,r0
+1: .word 0x00ab /* synco */
+ mov r0,r3
+ rts
+ mov r1,r0
+
+.global __sh_cas_imask
+.hidden __sh_cas_imask
+__sh_cas_imask:
+ mov r0,r1
+ stc sr,r0
+ mov.l r0,@-r15
+ or #0xf0,r0
+ ldc r0,sr
+ mov.l @r1,r0
+ cmp/eq r0,r2
+ bf 1f
+ mov.l r3,@r1
+1: ldc.l @r15+,sr
+ mov r0,r3
+ rts
+ mov r1,r0
+
+.global __sh_cas_cas_l
+.hidden __sh_cas_cas_l
+__sh_cas_cas_l:
+ rts
+ .word 0x2323 /* cas.l r2,r3,@r0 */
diff --git a/libc-top-half/musl/src/thread/sh/clone.s b/libc-top-half/musl/src/thread/sh/clone.s
new file mode 100644
index 0000000..9cfd862
--- /dev/null
+++ b/libc-top-half/musl/src/thread/sh/clone.s
@@ -0,0 +1,54 @@
+.text
+.global __clone
+.hidden __clone
+.type __clone, @function
+__clone:
+! incoming: fn stack flags arg ptid tls ctid
+! r4 r5 r6 r7 @r15 @(4,r15) @(8,r15)
+
+ mov #-16, r0
+ and r0, r5
+
+ mov r4, r1 ! r1 = fn
+ mov r7, r2 ! r2 = arg
+
+ mov #120, r3 ! r3 = __NR_clone
+ mov r6, r4 ! r4 = flags
+ !mov r5, r5 ! r5 = stack
+ mov.l @r15, r6 ! r6 = ptid
+ mov.l @(8,r15), r7 ! r7 = ctid
+ mov.l @(4,r15), r0 ! r0 = tls
+ trapa #31
+
+ or r0, r0
+ or r0, r0
+ or r0, r0
+ or r0, r0
+ or r0, r0
+
+ cmp/eq #0, r0
+ bt 1f
+
+ ! we are the parent, return
+ rts
+ nop
+
+1: ! we are the child, call fn(arg)
+ mov.l 1f, r0
+ mov r1, r5
+ bsrf r0
+ mov r2, r4
+
+2: mov #1, r3 ! __NR_exit
+ mov r0, r4
+ trapa #31
+
+ or r0, r0
+ or r0, r0
+ or r0, r0
+ or r0, r0
+ or r0, r0
+
+.align 2
+.hidden __shcall
+1: .long __shcall@PCREL+(.-2b)
diff --git a/libc-top-half/musl/src/thread/sh/syscall_cp.s b/libc-top-half/musl/src/thread/sh/syscall_cp.s
new file mode 100644
index 0000000..bb848ef
--- /dev/null
+++ b/libc-top-half/musl/src/thread/sh/syscall_cp.s
@@ -0,0 +1,45 @@
+.text
+.global __cp_begin
+.hidden __cp_begin
+.global __cp_end
+.hidden __cp_end
+.global __cp_cancel
+.hidden __cp_cancel
+.hidden __cancel
+.global __syscall_cp_asm
+.hidden __syscall_cp_asm
+.type __syscall_cp_asm, @function
+__syscall_cp_asm:
+
+__cp_begin:
+ mov.l @r4, r4
+ tst r4, r4
+ bf __cp_cancel
+ mov r5, r3
+ mov r6, r4
+ mov r7, r5
+ mov.l @r15, r6
+ mov.l @(4,r15), r7
+ mov.l @(8,r15), r0
+ mov.l @(12,r15), r1
+ trapa #31
+
+__cp_end:
+ ! work around hardware bug
+ or r0, r0
+ or r0, r0
+ or r0, r0
+ or r0, r0
+ or r0, r0
+
+ rts
+ nop
+
+__cp_cancel:
+ mov.l 2f, r0
+ braf r0
+ nop
+1:
+
+.align 2
+2: .long __cancel@PCREL-(1b-.)
diff --git a/libc-top-half/musl/src/thread/synccall.c b/libc-top-half/musl/src/thread/synccall.c
new file mode 100644
index 0000000..d58c851
--- /dev/null
+++ b/libc-top-half/musl/src/thread/synccall.c
@@ -0,0 +1,120 @@
+#include "pthread_impl.h"
+#include <semaphore.h>
+#include <string.h>
+
+static void dummy_0(void)
+{
+}
+
+weak_alias(dummy_0, __tl_lock);
+weak_alias(dummy_0, __tl_unlock);
+
+static int target_tid;
+static void (*callback)(void *), *context;
+static sem_t target_sem, caller_sem;
+
+static void dummy(void *p)
+{
+}
+
+static void handler(int sig)
+{
+ if (__pthread_self()->tid != target_tid) return;
+
+ int old_errno = errno;
+
+ /* Inform caller we have received signal and wait for
+ * the caller to let us make the callback. */
+ sem_post(&caller_sem);
+ sem_wait(&target_sem);
+
+ callback(context);
+
+ /* Inform caller we've complered the callback and wait
+ * for the caller to release us to return. */
+ sem_post(&caller_sem);
+ sem_wait(&target_sem);
+
+ /* Inform caller we are returning and state is destroyable. */
+ sem_post(&caller_sem);
+
+ errno = old_errno;
+}
+
+void __synccall(void (*func)(void *), void *ctx)
+{
+ sigset_t oldmask;
+ int cs, i, r;
+ struct sigaction sa = { .sa_flags = SA_RESTART, .sa_handler = handler };
+ pthread_t self = __pthread_self(), td;
+ int count = 0;
+
+ /* Blocking signals in two steps, first only app-level signals
+ * before taking the lock, then all signals after taking the lock,
+ * is necessary to achieve AS-safety. Blocking them all first would
+ * deadlock if multiple threads called __synccall. Waiting to block
+ * any until after the lock would allow re-entry in the same thread
+ * with the lock already held. */
+ __block_app_sigs(&oldmask);
+ __tl_lock();
+ __block_all_sigs(0);
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
+
+ sem_init(&target_sem, 0, 0);
+ sem_init(&caller_sem, 0, 0);
+
+ if (!libc.threads_minus_1 || __syscall(SYS_gettid) != self->tid)
+ goto single_threaded;
+
+ callback = func;
+ context = ctx;
+
+ /* Block even implementation-internal signals, so that nothing
+ * interrupts the SIGSYNCCALL handlers. The main possible source
+ * of trouble is asynchronous cancellation. */
+ memset(&sa.sa_mask, -1, sizeof sa.sa_mask);
+ __libc_sigaction(SIGSYNCCALL, &sa, 0);
+
+
+ for (td=self->next; td!=self; td=td->next) {
+ target_tid = td->tid;
+ while ((r = -__syscall(SYS_tkill, td->tid, SIGSYNCCALL)) == EAGAIN);
+ if (r) {
+ /* If we failed to signal any thread, nop out the
+ * callback to abort the synccall and just release
+ * any threads already caught. */
+ callback = func = dummy;
+ break;
+ }
+ sem_wait(&caller_sem);
+ count++;
+ }
+ target_tid = 0;
+
+ /* Serialize execution of callback in caught threads, or just
+ * release them all if synccall is being aborted. */
+ for (i=0; i<count; i++) {
+ sem_post(&target_sem);
+ sem_wait(&caller_sem);
+ }
+
+ sa.sa_handler = SIG_IGN;
+ __libc_sigaction(SIGSYNCCALL, &sa, 0);
+
+single_threaded:
+ func(ctx);
+
+ /* Only release the caught threads once all threads, including the
+ * caller, have returned from the callback function. */
+ for (i=0; i<count; i++)
+ sem_post(&target_sem);
+ for (i=0; i<count; i++)
+ sem_wait(&caller_sem);
+
+ sem_destroy(&caller_sem);
+ sem_destroy(&target_sem);
+
+ pthread_setcancelstate(cs, 0);
+ __tl_unlock();
+ __restore_sigs(&oldmask);
+}
diff --git a/libc-top-half/musl/src/thread/syscall_cp.c b/libc-top-half/musl/src/thread/syscall_cp.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libc-top-half/musl/src/thread/syscall_cp.c
diff --git a/libc-top-half/musl/src/thread/thrd_create.c b/libc-top-half/musl/src/thread/thrd_create.c
new file mode 100644
index 0000000..76a683d
--- /dev/null
+++ b/libc-top-half/musl/src/thread/thrd_create.c
@@ -0,0 +1,12 @@
+#include "pthread_impl.h"
+#include <threads.h>
+
+int thrd_create(thrd_t *thr, thrd_start_t func, void *arg)
+{
+ int ret = __pthread_create(thr, __ATTRP_C11_THREAD, (void *(*)(void *))func, arg);
+ switch (ret) {
+ case 0: return thrd_success;
+ case EAGAIN: return thrd_nomem;
+ default: return thrd_error;
+ }
+}
diff --git a/libc-top-half/musl/src/thread/thrd_exit.c b/libc-top-half/musl/src/thread/thrd_exit.c
new file mode 100644
index 0000000..9b291ae
--- /dev/null
+++ b/libc-top-half/musl/src/thread/thrd_exit.c
@@ -0,0 +1,8 @@
+#include <threads.h>
+#include <pthread.h>
+#include <stdint.h>
+
+_Noreturn void thrd_exit(int result)
+{
+ __pthread_exit((void*)(intptr_t)result);
+}
diff --git a/libc-top-half/musl/src/thread/thrd_join.c b/libc-top-half/musl/src/thread/thrd_join.c
new file mode 100644
index 0000000..0d5fd30
--- /dev/null
+++ b/libc-top-half/musl/src/thread/thrd_join.c
@@ -0,0 +1,11 @@
+#include <stdint.h>
+#include <threads.h>
+#include <pthread.h>
+
+int thrd_join(thrd_t t, int *res)
+{
+ void *pthread_res;
+ __pthread_join(t, &pthread_res);
+ if (res) *res = (int)(intptr_t)pthread_res;
+ return thrd_success;
+}
diff --git a/libc-top-half/musl/src/thread/thrd_sleep.c b/libc-top-half/musl/src/thread/thrd_sleep.c
new file mode 100644
index 0000000..97de534
--- /dev/null
+++ b/libc-top-half/musl/src/thread/thrd_sleep.c
@@ -0,0 +1,14 @@
+#include <threads.h>
+#include <time.h>
+#include <errno.h>
+#include "syscall.h"
+
+int thrd_sleep(const struct timespec *req, struct timespec *rem)
+{
+ int ret = -__clock_nanosleep(CLOCK_REALTIME, 0, req, rem);
+ switch (ret) {
+ case 0: return 0;
+ case -EINTR: return -1; /* value specified by C11 */
+ default: return -2;
+ }
+}
diff --git a/libc-top-half/musl/src/thread/thrd_yield.c b/libc-top-half/musl/src/thread/thrd_yield.c
new file mode 100644
index 0000000..f7ad132
--- /dev/null
+++ b/libc-top-half/musl/src/thread/thrd_yield.c
@@ -0,0 +1,7 @@
+#include <threads.h>
+#include "syscall.h"
+
+void thrd_yield()
+{
+ __syscall(SYS_sched_yield);
+}
diff --git a/libc-top-half/musl/src/thread/tls.c b/libc-top-half/musl/src/thread/tls.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libc-top-half/musl/src/thread/tls.c
diff --git a/libc-top-half/musl/src/thread/tss_create.c b/libc-top-half/musl/src/thread/tss_create.c
new file mode 100644
index 0000000..6d6ef96
--- /dev/null
+++ b/libc-top-half/musl/src/thread/tss_create.c
@@ -0,0 +1,10 @@
+#include <threads.h>
+#include <pthread.h>
+
+int tss_create(tss_t *tss, tss_dtor_t dtor)
+{
+ /* Different error returns are possible. C glues them together into
+ * just failure notification. Can't be optimized to a tail call,
+ * unless thrd_error equals EAGAIN. */
+ return __pthread_key_create(tss, dtor) ? thrd_error : thrd_success;
+}
diff --git a/libc-top-half/musl/src/thread/tss_delete.c b/libc-top-half/musl/src/thread/tss_delete.c
new file mode 100644
index 0000000..6f51b07
--- /dev/null
+++ b/libc-top-half/musl/src/thread/tss_delete.c
@@ -0,0 +1,7 @@
+#include <threads.h>
+#include <pthread.h>
+
+void tss_delete(tss_t key)
+{
+ __pthread_key_delete(key);
+}
diff --git a/libc-top-half/musl/src/thread/tss_set.c b/libc-top-half/musl/src/thread/tss_set.c
new file mode 100644
index 0000000..70c4fb7
--- /dev/null
+++ b/libc-top-half/musl/src/thread/tss_set.c
@@ -0,0 +1,13 @@
+#include "pthread_impl.h"
+#include <threads.h>
+
+int tss_set(tss_t k, void *x)
+{
+ struct pthread *self = __pthread_self();
+ /* Avoid unnecessary COW */
+ if (self->tsd[k] != x) {
+ self->tsd[k] = x;
+ self->tsd_used = 1;
+ }
+ return thrd_success;
+}
diff --git a/libc-top-half/musl/src/thread/vmlock.c b/libc-top-half/musl/src/thread/vmlock.c
new file mode 100644
index 0000000..fa0a8e3
--- /dev/null
+++ b/libc-top-half/musl/src/thread/vmlock.c
@@ -0,0 +1,23 @@
+#include "pthread_impl.h"
+#include "fork_impl.h"
+
+static volatile int vmlock[2];
+volatile int *const __vmlock_lockptr = vmlock;
+
+void __vm_wait()
+{
+ int tmp;
+ while ((tmp=vmlock[0]))
+ __wait(vmlock, vmlock+1, tmp, 1);
+}
+
+void __vm_lock()
+{
+ a_inc(vmlock);
+}
+
+void __vm_unlock()
+{
+ if (a_fetch_add(vmlock, -1)==1 && vmlock[1])
+ __wake(vmlock, -1, 1);
+}
diff --git a/libc-top-half/musl/src/thread/x32/__set_thread_area.s b/libc-top-half/musl/src/thread/x32/__set_thread_area.s
new file mode 100644
index 0000000..c0fee87
--- /dev/null
+++ b/libc-top-half/musl/src/thread/x32/__set_thread_area.s
@@ -0,0 +1,11 @@
+/* Copyright 2011-2012 Nicholas J. Kain, licensed under standard MIT license */
+.text
+.global __set_thread_area
+.hidden __set_thread_area
+.type __set_thread_area,@function
+__set_thread_area:
+ mov %edi,%esi /* shift for syscall */
+ movl $0x1002,%edi /* SET_FS register */
+ movl $0x4000009e,%eax /* set fs segment to */
+ syscall /* arch_prctl(SET_FS, arg)*/
+ ret
diff --git a/libc-top-half/musl/src/thread/x32/__unmapself.s b/libc-top-half/musl/src/thread/x32/__unmapself.s
new file mode 100644
index 0000000..d925460
--- /dev/null
+++ b/libc-top-half/musl/src/thread/x32/__unmapself.s
@@ -0,0 +1,10 @@
+/* Copyright 2011-2012 Nicholas J. Kain, licensed under standard MIT license */
+.text
+.global __unmapself
+.type __unmapself,@function
+__unmapself:
+ movl $0x4000000b,%eax /* SYS_munmap */
+ syscall /* munmap(arg2,arg3) */
+ xor %rdi,%rdi /* exit() args: always return success */
+ movl $0x4000003c,%eax /* SYS_exit */
+ syscall /* exit(0) */
diff --git a/libc-top-half/musl/src/thread/x32/clone.s b/libc-top-half/musl/src/thread/x32/clone.s
new file mode 100644
index 0000000..b870880
--- /dev/null
+++ b/libc-top-half/musl/src/thread/x32/clone.s
@@ -0,0 +1,26 @@
+.text
+.global __clone
+.hidden __clone
+.type __clone,@function
+__clone:
+ movl $0x40000038,%eax /* SYS_clone */
+ mov %rdi,%r11
+ mov %rdx,%rdi
+ mov %r8,%rdx
+ mov %r9,%r8
+ mov 8(%rsp),%r10
+ mov %r11,%r9
+ and $-16,%rsi
+ sub $8,%rsi
+ mov %rcx,(%rsi)
+ syscall
+ test %eax,%eax
+ jnz 1f
+ xor %ebp,%ebp
+ pop %rdi
+ call *%r9
+ mov %eax,%edi
+ movl $0x4000003c,%eax /* SYS_exit */
+ syscall
+ hlt
+1: ret
diff --git a/libc-top-half/musl/src/thread/x32/syscall_cp.s b/libc-top-half/musl/src/thread/x32/syscall_cp.s
new file mode 100644
index 0000000..4f10171
--- /dev/null
+++ b/libc-top-half/musl/src/thread/x32/syscall_cp.s
@@ -0,0 +1,31 @@
+.text
+.global __cp_begin
+.hidden __cp_begin
+.global __cp_end
+.hidden __cp_end
+.global __cp_cancel
+.hidden __cp_cancel
+.hidden __cancel
+.global __syscall_cp_asm
+.hidden __syscall_cp_asm
+.type __syscall_cp_asm,@function
+__syscall_cp_asm:
+
+__cp_begin:
+ mov (%rdi),%eax
+ test %eax,%eax
+ jnz __cp_cancel
+ mov %rdi,%r11
+ mov %rsi,%rax
+ mov %rdx,%rdi
+ mov %rcx,%rsi
+ mov %r8,%rdx
+ mov %r9,%r10
+ mov 8(%rsp),%r8
+ mov 16(%rsp),%r9
+ mov %r11,8(%rsp)
+ syscall
+__cp_end:
+ ret
+__cp_cancel:
+ jmp __cancel
diff --git a/libc-top-half/musl/src/thread/x86_64/__set_thread_area.s b/libc-top-half/musl/src/thread/x86_64/__set_thread_area.s
new file mode 100644
index 0000000..7347ff4
--- /dev/null
+++ b/libc-top-half/musl/src/thread/x86_64/__set_thread_area.s
@@ -0,0 +1,11 @@
+/* Copyright 2011-2012 Nicholas J. Kain, licensed under standard MIT license */
+.text
+.global __set_thread_area
+.hidden __set_thread_area
+.type __set_thread_area,@function
+__set_thread_area:
+ mov %rdi,%rsi /* shift for syscall */
+ movl $0x1002,%edi /* SET_FS register */
+ movl $158,%eax /* set fs segment to */
+ syscall /* arch_prctl(SET_FS, arg)*/
+ ret
diff --git a/libc-top-half/musl/src/thread/x86_64/__unmapself.s b/libc-top-half/musl/src/thread/x86_64/__unmapself.s
new file mode 100644
index 0000000..e2689e6
--- /dev/null
+++ b/libc-top-half/musl/src/thread/x86_64/__unmapself.s
@@ -0,0 +1,10 @@
+/* Copyright 2011-2012 Nicholas J. Kain, licensed under standard MIT license */
+.text
+.global __unmapself
+.type __unmapself,@function
+__unmapself:
+ movl $11,%eax /* SYS_munmap */
+ syscall /* munmap(arg2,arg3) */
+ xor %rdi,%rdi /* exit() args: always return success */
+ movl $60,%eax /* SYS_exit */
+ syscall /* exit(0) */
diff --git a/libc-top-half/musl/src/thread/x86_64/clone.s b/libc-top-half/musl/src/thread/x86_64/clone.s
new file mode 100644
index 0000000..6e47bc0
--- /dev/null
+++ b/libc-top-half/musl/src/thread/x86_64/clone.s
@@ -0,0 +1,28 @@
+.text
+.global __clone
+.hidden __clone
+.type __clone,@function
+__clone:
+ xor %eax,%eax
+ mov $56,%al
+ mov %rdi,%r11
+ mov %rdx,%rdi
+ mov %r8,%rdx
+ mov %r9,%r8
+ mov 8(%rsp),%r10
+ mov %r11,%r9
+ and $-16,%rsi
+ sub $8,%rsi
+ mov %rcx,(%rsi)
+ syscall
+ test %eax,%eax
+ jnz 1f
+ xor %ebp,%ebp
+ pop %rdi
+ call *%r9
+ mov %eax,%edi
+ xor %eax,%eax
+ mov $60,%al
+ syscall
+ hlt
+1: ret
diff --git a/libc-top-half/musl/src/thread/x86_64/syscall_cp.s b/libc-top-half/musl/src/thread/x86_64/syscall_cp.s
new file mode 100644
index 0000000..4f10171
--- /dev/null
+++ b/libc-top-half/musl/src/thread/x86_64/syscall_cp.s
@@ -0,0 +1,31 @@
+.text
+.global __cp_begin
+.hidden __cp_begin
+.global __cp_end
+.hidden __cp_end
+.global __cp_cancel
+.hidden __cp_cancel
+.hidden __cancel
+.global __syscall_cp_asm
+.hidden __syscall_cp_asm
+.type __syscall_cp_asm,@function
+__syscall_cp_asm:
+
+__cp_begin:
+ mov (%rdi),%eax
+ test %eax,%eax
+ jnz __cp_cancel
+ mov %rdi,%r11
+ mov %rsi,%rax
+ mov %rdx,%rdi
+ mov %rcx,%rsi
+ mov %r8,%rdx
+ mov %r9,%r10
+ mov 8(%rsp),%r8
+ mov 16(%rsp),%r9
+ mov %r11,8(%rsp)
+ syscall
+__cp_end:
+ ret
+__cp_cancel:
+ jmp __cancel