diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 13:54:38 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 13:54:38 +0000 |
commit | 8c1ab65c0f548d20b7f177bdb736daaf603340e1 (patch) | |
tree | df55b7e75bf43f2bf500845b105afe3ac3a5157e /libc-top-half/musl/src/thread | |
parent | Initial commit. (diff) | |
download | wasi-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')
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 |