From b40732797257a0e01367390fb65234eb7feea716 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Tue, 8 Nov 2022 10:09:47 -0800 Subject: [PATCH] Make fixes and improvements - clock_nanosleep() is now much faster on OpenBSD and NetBSD - Thread joining is now much faster on NetBSD - FreeBSD timestamps are now more accurate - Thread spawning now goes faster on XNU - Clean up the clone() code --- build/bootstrap/cocmd.com | Bin 94208 -> 73728 bytes examples/clock_getres.c | 2 + examples/{sleep.c => nanosleep.c} | 0 examples/nanosleep_test.c | 43 ++++++ libc/calls/clock_gettime.c | 31 ++-- libc/calls/clock_nanosleep.c | 189 ++++++++++++++++++++---- libc/calls/clock_nanosleep_latency.c | 25 ++++ libc/calls/now.c | 10 +- libc/calls/struct/timespec.internal.h | 2 +- libc/calls/timespec_mono.c | 2 +- libc/calls/timespec_real.c | 2 +- libc/intrin/createthread.c | 5 +- libc/intrin/kclocknames.S | 14 +- libc/runtime/clone-linux.S | 6 +- libc/runtime/clone-xnu.S | 26 ++++ libc/runtime/clone.c | 172 ++++++++++----------- libc/runtime/clone.internal.h | 2 +- libc/runtime/enable_tls.c | 2 +- libc/runtime/fork.c | 13 +- libc/sysv/calls/__sys_clock_nanosleep.s | 2 + libc/sysv/calls/sys_bsdthread_create.s | 2 - libc/sysv/calls/sys_clock_nanosleep.s | 2 - libc/sysv/syscalls.sh | 4 +- libc/thread/posixthread.internal.h | 2 +- libc/thread/pthread_atfork.c | 1 - libc/thread/pthread_cancel.c | 26 ++-- libc/thread/pthread_create.c | 25 +--- libc/thread/pthread_getaffinity_np.c | 52 ++++--- libc/thread/pthread_getname_np.c | 6 +- libc/thread/pthread_getunique_np.c | 22 ++- libc/thread/pthread_kill.c | 19 ++- libc/thread/pthread_reschedule.c | 28 ++-- libc/thread/pthread_setaffinity_np.c | 37 ++--- libc/thread/pthread_setname_np.c | 6 +- libc/thread/spawn.c | 14 +- libc/thread/thread.h | 2 +- libc/thread/tls.h | 2 +- libc/thread/xnu.internal.h | 4 +- libc/time/iso8601us.c | 1 - test/libc/calls/nanosleep_test.c | 6 +- test/libc/calls/tkill_test.c | 5 +- test/libc/stdio/getentropy_test.c | 19 ++- test/libc/stdio/getrandom_test.c | 63 ++++++++ third_party/nsync/futex.c | 30 ++-- tool/emacs/cosmo-c-constants.el | 3 +- tool/net/definitions.lua | 13 +- tool/net/help.txt | 9 +- 47 files changed, 645 insertions(+), 306 deletions(-) rename examples/{sleep.c => nanosleep.c} (100%) create mode 100644 examples/nanosleep_test.c create mode 100644 libc/calls/clock_nanosleep_latency.c create mode 100644 libc/runtime/clone-xnu.S create mode 100644 libc/sysv/calls/__sys_clock_nanosleep.s delete mode 100644 libc/sysv/calls/sys_bsdthread_create.s delete mode 100644 libc/sysv/calls/sys_clock_nanosleep.s diff --git a/build/bootstrap/cocmd.com b/build/bootstrap/cocmd.com index ddbaac8b6024451b0d829992555ea165a654e05d..ff1769d7b538c58da1741ce3235e34a34c1508e6 100755 GIT binary patch literal 73728 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 0) { + return 0; + } + if ((rc = CheckCancel())) { + return rc; + } + } else { + if (timespec_cmp(now, start) < 0) continue; + elapsed = timespec_sub(now, start); + if ((rc = CheckCancel())) { + if (rc == EINTR && rem) { + if (timespec_cmp(elapsed, *req) >= 0) { + bzero(rem, sizeof(*rem)); + } else { + *rem = elapsed; + } + } + return rc; + } + if (timespec_cmp(elapsed, *req) >= 0) { + return 0; + } + } + } +} + +static bool ShouldUseSpinNanosleep(int clock, int flags, + const struct timespec *req) { + errno_t e; + struct timespec now; + if (IsWindows()) { + // Our spin technique here is intended to take advantage of the fact + // that sched_yield() takes about a hundred nanoseconds. But Windows + // SleepEx(0, 0) a.k.a. NtYieldExecution() takes a whole millisecond + // and it matters not whether our intent is to yielding or sleeping, + // since we use the SleepEx() function to implement both. Therefore, + // there's no reason to use SpinNanosleep() on Windows. + return false; + } + if (clock != CLOCK_REALTIME && // + clock != CLOCK_REALTIME_PRECISE && // + clock != CLOCK_MONOTONIC && // + clock != CLOCK_MONOTONIC_RAW && // + clock != CLOCK_MONOTONIC_PRECISE) { + return false; + } + if (!flags) { + return timespec_cmp(*req, GetNanosleepLatency()) < 0; + } + // We need a clock_gettime() system call to perform this check if the + // sleep request is an absolute timestamp. So we avoid doing that on + // systems where sleep latency isn't too outrageous. + if (timespec_cmp(GetNanosleepLatency(), timespec_fromnanos(50 * 1000)) < 0) { + return false; + } + e = errno; + if (__clock_gettime_get(0)(clock, &now)) { + // punt to the nanosleep system call + errno = e; + return false; + } + return timespec_cmp(*req, now) < 0 || + timespec_cmp(timespec_sub(*req, now), GetNanosleepLatency()) < 0; +} + /** * Sleeps for particular amount of time. * @@ -88,36 +242,22 @@ */ errno_t clock_nanosleep(int clock, int flags, const struct timespec *req, struct timespec *rem) { - int rc, e = errno; - BEGIN_CANCELLATION_POINT; - - if (!req || (IsAsan() && (!__asan_is_valid_timespec(req) || - (rem && !__asan_is_valid_timespec(rem))))) { - rc = efault(); + int rc; + if (IsMetal()) { + rc = ENOSYS; + } else if (!req || (IsAsan() && (!__asan_is_valid_timespec(req) || + (rem && !__asan_is_valid_timespec(rem))))) { + rc = EFAULT; } else if (clock == 127 || // (flags & ~TIMER_ABSTIME) || // req->tv_sec < 0 || // !(0 <= req->tv_nsec && req->tv_nsec <= 999999999)) { - rc = einval(); - } else if (IsLinux() || IsFreebsd() || IsNetbsd()) { - rc = sys_clock_nanosleep(clock, flags, req, rem); - } else if (IsXnu()) { - rc = sys_clock_nanosleep_xnu(clock, flags, req, rem); - } else if (IsOpenbsd()) { - rc = sys_clock_nanosleep_openbsd(clock, flags, req, rem); - } else if (IsMetal()) { - rc = enosys(); + rc = EINVAL; + } else if (ShouldUseSpinNanosleep(clock, flags, req)) { + rc = SpinNanosleep(clock, flags, req, rem); } else { - rc = sys_clock_nanosleep_nt(clock, flags, req, rem); + rc = sys_clock_nanosleep(clock, flags, req, rem); } - - if (rc == -1) { - rc = errno; - errno = e; - } - - END_CANCELLATION_POINT; - #if SYSDEBUG if (__tls_enabled && !(__get_tls()->tib_flags & TIB_FLAG_TIME_CRITICAL)) { STRACE("clock_nanosleep(%s, %s, %s, [%s]) → %s", DescribeClockName(clock), @@ -125,6 +265,5 @@ errno_t clock_nanosleep(int clock, int flags, const struct timespec *req, DescribeTimespec(rc, rem), DescribeErrnoResult(rc)); } #endif - return rc; } diff --git a/libc/calls/clock_nanosleep_latency.c b/libc/calls/clock_nanosleep_latency.c new file mode 100644 index 000000000..be5766bf1 --- /dev/null +++ b/libc/calls/clock_nanosleep_latency.c @@ -0,0 +1,25 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" +#include "libc/calls/blockcancel.internal.h" +#include "libc/calls/calls.h" +#include "libc/calls/clock_gettime.internal.h" +#include "libc/errno.h" +#include "libc/macros.internal.h" +#include "libc/sysv/consts/clock.h" diff --git a/libc/calls/now.c b/libc/calls/now.c index 815e152f1..4a6d6da20 100644 --- a/libc/calls/now.c +++ b/libc/calls/now.c @@ -45,10 +45,10 @@ static long double GetTimeSample(void) { uint64_t tick1, tick2; long double time1, time2; sched_yield(); - time1 = dtime(CLOCK_REALTIME_FAST); + time1 = dtime(CLOCK_REALTIME_PRECISE); tick1 = rdtsc(); nanosleep(&(struct timespec){0, 1000000}, NULL); - time2 = dtime(CLOCK_REALTIME_FAST); + time2 = dtime(CLOCK_REALTIME_PRECISE); tick2 = rdtsc(); return (time2 - time1) * 1e9 / MAX(1, tick2 - tick1); } @@ -74,13 +74,13 @@ static long double MeasureNanosPerCycle(void) { void RefreshTime(void) { struct Now now; now.cpn = MeasureNanosPerCycle(); - now.r0 = dtime(CLOCK_REALTIME_FAST); + now.r0 = dtime(CLOCK_REALTIME_PRECISE); now.k0 = rdtsc(); memcpy(&g_now, &now, sizeof(now)); } static long double nowl_sys(void) { - return dtime(CLOCK_REALTIME_FAST); + return dtime(CLOCK_REALTIME_PRECISE); } static long double nowl_art(void) { @@ -92,7 +92,7 @@ static long double nowl_vdso(void) { long double secs; struct timespec tv; _unassert(__gettime); - __gettime(CLOCK_REALTIME_FAST, &tv); + __gettime(CLOCK_REALTIME_PRECISE, &tv); secs = tv.tv_nsec; secs *= 1 / 1e9L; secs += tv.tv_sec; diff --git a/libc/calls/struct/timespec.internal.h b/libc/calls/struct/timespec.internal.h index 768549b7b..88c6b2577 100644 --- a/libc/calls/struct/timespec.internal.h +++ b/libc/calls/struct/timespec.internal.h @@ -6,13 +6,13 @@ COSMOPOLITAN_C_START_ /* clang-format off */ +int __sys_clock_nanosleep(int, int, const struct timespec *, struct timespec *) hidden; int __sys_utimensat(int, const char *, const struct timespec[2], int) hidden; int __utimens(int, const char *, const struct timespec[2], int) hidden; int sys_clock_getres(int, struct timespec *) hidden; int sys_clock_gettime(int, struct timespec *) hidden; int sys_clock_gettime_nt(int, struct timespec *) hidden; int sys_clock_gettime_xnu(int, struct timespec *) hidden; -int sys_clock_nanosleep(int, int, const struct timespec *, struct timespec *) hidden; int sys_clock_nanosleep_nt(int, int, const struct timespec *, struct timespec *) hidden; int sys_clock_nanosleep_openbsd(int, int, const struct timespec *, struct timespec *) hidden; int sys_clock_nanosleep_xnu(int, int, const struct timespec *, struct timespec *) hidden; diff --git a/libc/calls/timespec_mono.c b/libc/calls/timespec_mono.c index 514db06a6..a37e7b2ae 100644 --- a/libc/calls/timespec_mono.c +++ b/libc/calls/timespec_mono.c @@ -29,6 +29,6 @@ */ struct timespec timespec_mono(void) { struct timespec ts; - _npassert(!clock_gettime(CLOCK_MONOTONIC_FAST, &ts)); + _npassert(!clock_gettime(CLOCK_MONOTONIC, &ts)); return ts; } diff --git a/libc/calls/timespec_real.c b/libc/calls/timespec_real.c index 6b97142d2..dbebd20c5 100644 --- a/libc/calls/timespec_real.c +++ b/libc/calls/timespec_real.c @@ -31,6 +31,6 @@ */ struct timespec timespec_real(void) { struct timespec ts; - _npassert(!clock_gettime(CLOCK_REALTIME_FAST, &ts)); + _npassert(!clock_gettime(CLOCK_REALTIME, &ts)); return ts; } diff --git a/libc/intrin/createthread.c b/libc/intrin/createthread.c index 3e5cfad76..5cd8774f2 100644 --- a/libc/intrin/createthread.c +++ b/libc/intrin/createthread.c @@ -16,9 +16,9 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/intrin/strace.internal.h" #include "libc/calls/syscall_support-nt.internal.h" #include "libc/intrin/describeflags.internal.h" +#include "libc/intrin/strace.internal.h" #include "libc/nt/struct/securityattributes.h" #include "libc/nt/thread.h" @@ -29,7 +29,7 @@ __msabi extern typeof(CreateThread) *const __imp_CreateThread; * * @param dwStackSize may be 0 for default per executable * @return thread handle, or 0 on failure - * @note this wrapper takes care of ABI, STRACE(), and __winerr() + * @note this wrapper takes care of ABI, STRACE() */ textwindows int64_t CreateThread( struct NtSecurityAttributes *lpThreadAttributes, size_t dwStackSize, @@ -38,7 +38,6 @@ textwindows int64_t CreateThread( int64_t hHandle; hHandle = __imp_CreateThread(lpThreadAttributes, dwStackSize, lpStartAddress, lpParameter, dwCreationFlags, opt_lpThreadId); - if (hHandle == -1) __winerr(); NTTRACE("CreateThread(%s, %'zu, %p, %p, %s, %p) → %ld% m", DescribeNtSecurityAttributes(lpThreadAttributes), dwStackSize, lpStartAddress, lpParameter, dwCreationFlags, opt_lpThreadId, diff --git a/libc/intrin/kclocknames.S b/libc/intrin/kclocknames.S index 02c6e9f14..2f2f85bb1 100644 --- a/libc/intrin/kclocknames.S +++ b/libc/intrin/kclocknames.S @@ -32,14 +32,14 @@ .underrun kClockNames: .e CLOCK_REALTIME,"REALTIME" - .e CLOCK_REALTIME_FAST,"REALTIME_FAST" - .e CLOCK_REALTIME_PRECISE,"REALTIME_PRECISE" + .e CLOCK_REALTIME_FAST,"REALTIME_FAST" # order matters + .e CLOCK_REALTIME_PRECISE,"REALTIME_PRECISE" # order matters + .e CLOCK_REALTIME_COARSE,"REALTIME_COARSE" # order matters .e CLOCK_MONOTONIC,"MONOTONIC" - .e CLOCK_MONOTONIC_FAST,"MONOTONIC_FAST" - .e CLOCK_MONOTONIC_RAW,"MONOTONIC_RAW" - .e CLOCK_MONOTONIC_PRECISE,"MONOTONIC_PRECISE" - .e CLOCK_REALTIME_COARSE,"REALTIME_COARSE" - .e CLOCK_MONOTONIC_COARSE,"MONOTONIC_COARSE" + .e CLOCK_MONOTONIC_FAST,"MONOTONIC_FAST" # order matters + .e CLOCK_MONOTONIC_RAW,"MONOTONIC_RAW" # order matters + .e CLOCK_MONOTONIC_PRECISE,"MONOTONIC_PRECISE" # order matters + .e CLOCK_MONOTONIC_COARSE,"MONOTONIC_COARSE" # order matters .e CLOCK_PROCESS_CPUTIME_ID,"PROCESS_CPUTIME_ID" .e CLOCK_THREAD_CPUTIME_ID,"THREAD_CPUTIME_ID" .e CLOCK_TAI,"TAI" diff --git a/libc/runtime/clone-linux.S b/libc/runtime/clone-linux.S index bdc5fd028..be1bc422f 100644 --- a/libc/runtime/clone-linux.S +++ b/libc/runtime/clone-linux.S @@ -28,7 +28,7 @@ // @param r8 is tls // @param r9 is func(void*,int)→int // @param 8(rsp) is arg -// @return tid of child on success, or -1 w/ errno +// @return tid of child on success, or -errno on error sys_clone_linux: push %rbp mov %rsp,%rbp @@ -39,13 +39,9 @@ sys_clone_linux: syscall test %rax,%rax jz 2f - cmp $-4095,%rax - jae 1f 0: pop %rbx pop %rbp ret -1: call systemfive_error - jmp 0b 2: xor %ebp,%ebp # child thread mov %rbx,%rdi # arg mov (%r10),%esi # tid diff --git a/libc/runtime/clone-xnu.S b/libc/runtime/clone-xnu.S new file mode 100644 index 000000000..ec5841ac7 --- /dev/null +++ b/libc/runtime/clone-xnu.S @@ -0,0 +1,26 @@ +/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ +│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/macros.internal.h" + +sys_clone_xnu: + mov $0x02000168,%eax # bsdthread_create + mov %rcx,%r10 + syscall + ret + .endfn sys_clone_xnu,globl,hidden diff --git a/libc/runtime/clone.c b/libc/runtime/clone.c index 6dc681bc0..f1a06269f 100644 --- a/libc/runtime/clone.c +++ b/libc/runtime/clone.c @@ -16,12 +16,14 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" #include "libc/calls/calls.h" #include "libc/calls/struct/ucontext-netbsd.internal.h" #include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/errno.h" #include "libc/intrin/asan.internal.h" +#include "libc/intrin/describeflags.internal.h" #include "libc/intrin/strace.internal.h" #include "libc/limits.h" #include "libc/macros.internal.h" @@ -32,6 +34,7 @@ #include "libc/runtime/clone.internal.h" #include "libc/runtime/internal.h" #include "libc/runtime/runtime.h" +#include "libc/sock/internal.h" #include "libc/stdalign.internal.h" #include "libc/str/str.h" #include "libc/sysv/consts/clone.h" @@ -66,7 +69,6 @@ struct CloneArgs { uint32_t utid; int64_t tid64; }; - pthread_spinlock_t lock; int *ptid; int *ctid; int *ztid; @@ -99,7 +101,6 @@ WinThreadEntry(int rdi, // rcx struct CloneArgs *wt) { // r9 int rc; if (wt->tls) __set_tls_win32(wt->tls); - *wt->ptid = wt->tid; *wt->ctid = wt->tid; rc = WinThreadLaunch(wt->arg, wt->tid, wt->func, (intptr_t)wt & -16); // we can now clear ctid directly since we're no longer using our own @@ -112,15 +113,14 @@ WinThreadEntry(int rdi, // rcx notpossible; } -static textwindows int CloneWindows(int (*func)(void *, int), char *stk, - size_t stksz, int flags, void *arg, - void *tls, int *ptid, int *ctid) { +static textwindows errno_t CloneWindows(int (*func)(void *, int), char *stk, + size_t stksz, int flags, void *arg, + void *tls, int *ptid, int *ctid) { int64_t h; struct CloneArgs *wt; wt = (struct CloneArgs *)(((intptr_t)(stk + stksz) - sizeof(struct CloneArgs)) & -alignof(struct CloneArgs)); - wt->ptid = flags & CLONE_PARENT_SETTID ? ptid : &wt->tid; wt->ctid = flags & CLONE_CHILD_SETTID ? ctid : &wt->tid; wt->ztid = flags & CLONE_CHILD_CLEARTID ? ctid : &wt->tid; wt->func = func; @@ -128,9 +128,12 @@ static textwindows int CloneWindows(int (*func)(void *, int), char *stk, wt->tls = flags & CLONE_SETTLS ? tls : 0; if ((h = CreateThread(0, 4096, (void *)WinThreadEntry, wt, 0, &wt->utid))) { CloseHandle(h); - return wt->tid; + if (flags & CLONE_PARENT_SETTID) { + *ptid = wt->tid; + } + return 0; } else { - return -1; + return __dos2errno(GetLastError()); } } @@ -161,9 +164,8 @@ XnuThreadMain(void *pthread, // rdi unsigned xnuflags) { // r9 int ax; wt->tid = tid; - *wt->ptid = tid; *wt->ctid = tid; - pthread_spin_unlock(&wt->lock); + *wt->ptid = tid; if (wt->tls) { // XNU uses the same 0x30 offset as the WIN32 TIB x64. They told the @@ -192,23 +194,16 @@ XnuThreadMain(void *pthread, // rdi notpossible; } -static int CloneXnu(int (*fn)(void *), char *stk, size_t stksz, int flags, - void *arg, void *tls, int *ptid, int *ctid) { +static errno_t CloneXnu(int (*fn)(void *), char *stk, size_t stksz, int flags, + void *arg, void *tls, int *ptid, int *ctid) { int rc; bool failed; static bool once; - static int broken; struct CloneArgs *wt; if (!once) { - if (sys_bsdthread_register(XnuThreadThunk, 0, 0, 0, 0, 0, 0) == -1) { - broken = errno; - } + _npassert(sys_bsdthread_register(XnuThreadThunk, 0, 0, 0, 0, 0, 0) != -1); once = true; } - if (broken) { - errno = broken; - return -1; - } wt = (struct CloneArgs *)(((intptr_t)(stk + stksz) - sizeof(struct CloneArgs)) & -alignof(struct CloneArgs)); @@ -216,14 +211,7 @@ static int CloneXnu(int (*fn)(void *), char *stk, size_t stksz, int flags, wt->ctid = flags & CLONE_CHILD_SETTID ? ctid : &wt->tid; wt->ztid = flags & CLONE_CHILD_CLEARTID ? ctid : &wt->tid; wt->tls = flags & CLONE_SETTLS ? tls : 0; - wt->lock._lock = 1; - if ((rc = sys_bsdthread_create(fn, arg, wt, 0, PTHREAD_START_CUSTOM_XNU)) != - -1) { - pthread_spin_lock(&wt->lock); - rc = wt->tid; - pthread_spin_unlock(&wt->lock); - } - return rc; + return sys_clone_xnu(fn, arg, wt, 0, PTHREAD_START_CUSTOM_XNU); } //////////////////////////////////////////////////////////////////////////////// @@ -231,7 +219,6 @@ static int CloneXnu(int (*fn)(void *), char *stk, size_t stksz, int flags, static wontreturn void FreebsdThreadMain(void *p) { struct CloneArgs *wt = p; - *wt->ptid = wt->tid; *wt->ctid = wt->tid; wt->func(wt->arg, wt->tid); // we no longer use the stack after this point @@ -249,8 +236,9 @@ static wontreturn void FreebsdThreadMain(void *p) { notpossible; } -static int CloneFreebsd(int (*func)(void *, int), char *stk, size_t stksz, - int flags, void *arg, void *tls, int *ptid, int *ctid) { +static errno_t CloneFreebsd(int (*func)(void *, int), char *stk, size_t stksz, + int flags, void *arg, void *tls, int *ptid, + int *ctid) { int ax; bool failed; int64_t tid; @@ -258,7 +246,6 @@ static int CloneFreebsd(int (*func)(void *, int), char *stk, size_t stksz, wt = (struct CloneArgs *)(((intptr_t)(stk + stksz) - sizeof(struct CloneArgs)) & -alignof(struct CloneArgs)); - wt->ptid = flags & CLONE_PARENT_SETTID ? ptid : &wt->tid; wt->ctid = flags & CLONE_CHILD_SETTID ? ctid : &wt->tid; wt->ztid = flags & CLONE_CHILD_CLEARTID ? ctid : &wt->tid; wt->tls = tls; @@ -278,11 +265,9 @@ static int CloneFreebsd(int (*func)(void *, int), char *stk, size_t stksz, : CFLAG_CONSTRAINT(failed), "=a"(ax) : "1"(__NR_thr_new), "D"(¶ms), "S"(sizeof(params)) : "rcx", "rdx", "r8", "r9", "r10", "r11", "memory"); - if (failed) { - errno = ax; - tid = -1; - } - return tid; + if (failed) return ax; + if (flags & CLONE_PARENT_SETTID) *ptid = tid; + return 0; } //////////////////////////////////////////////////////////////////////////////// @@ -292,7 +277,6 @@ static int CloneFreebsd(int (*func)(void *, int), char *stk, size_t stksz, // 1. __asan_handle_no_return wipes stack [todo?] noasan static wontreturn void OpenbsdThreadMain(void *p) { struct CloneArgs *wt = p; - *wt->ptid = wt->tid; *wt->ctid = wt->tid; wt->func(wt->arg, wt->tid); asm volatile("mov\t%2,%%rsp\n\t" // so syscall can validate stack exists @@ -308,9 +292,10 @@ noasan static wontreturn void OpenbsdThreadMain(void *p) { notpossible; } -static int CloneOpenbsd(int (*func)(void *, int), char *stk, size_t stksz, - int flags, void *arg, void *tls, int *ptid, int *ctid) { - int tid; +static errno_t CloneOpenbsd(int (*func)(void *, int), char *stk, size_t stksz, + int flags, void *arg, void *tls, int *ptid, + int *ctid) { + int rc; intptr_t sp; struct __tfork *tf; struct CloneArgs *wt; @@ -321,7 +306,6 @@ static int CloneOpenbsd(int (*func)(void *, int), char *stk, size_t stksz, sp -= sizeof(struct CloneArgs); sp &= -MAX(16, alignof(struct CloneArgs)); wt = (struct CloneArgs *)sp; - wt->ptid = flags & CLONE_PARENT_SETTID ? ptid : &wt->tid; wt->ctid = flags & CLONE_CHILD_SETTID ? ctid : &wt->tid; wt->ztid = flags & CLONE_CHILD_CLEARTID ? ctid : &wt->tid; wt->arg = arg; @@ -329,11 +313,15 @@ static int CloneOpenbsd(int (*func)(void *, int), char *stk, size_t stksz, tf->tf_stack = (char *)wt - 8; tf->tf_tcb = flags & CLONE_SETTLS ? tls : 0; tf->tf_tid = &wt->tid; - if ((tid = __tfork_thread(tf, sizeof(*tf), OpenbsdThreadMain, wt)) < 0) { - errno = -tid; - tid = -1; + if ((rc = __tfork_thread(tf, sizeof(*tf), OpenbsdThreadMain, wt)) >= 0) { + _npassert(rc); + if (flags & CLONE_PARENT_SETTID) { + *ptid = rc; + } + return 0; + } else { + return -rc; } - return tid; } //////////////////////////////////////////////////////////////////////////////// @@ -343,8 +331,7 @@ static wontreturn void NetbsdThreadMain(void *arg, // rdi int (*func)(void *, int), // rsi int *tid, // rdx int *ctid, // rcx - int *ztid, // r8 - int *ptid) { // r9 + int *ztid) { // r9 int ax, dx; // TODO(jart): Why are we seeing flakes where *tid is zero? // ax = *tid; @@ -370,7 +357,6 @@ static int CloneNetbsd(int (*func)(void *, int), char *stk, size_t stksz, int ax, *tid; intptr_t dx, sp; static bool once; - static int broken; struct ucontext_netbsd *ctx; static struct ucontext_netbsd netbsd_clone_template; @@ -380,15 +366,9 @@ static int CloneNetbsd(int (*func)(void *, int), char *stk, size_t stksz, : CFLAG_CONSTRAINT(failed), "=a"(ax) : "1"(__NR_getcontext_netbsd), "D"(&netbsd_clone_template) : "rcx", "rdx", "r8", "r9", "r10", "r11", "memory"); - if (failed) { - broken = ax; - } + _npassert(!failed); once = true; } - if (broken) { - errno = broken; - return -1; - } sp = (intptr_t)(stk + stksz); // allocate memory for tid @@ -420,7 +400,6 @@ static int CloneNetbsd(int (*func)(void *, int), char *stk, size_t stksz, ctx->uc_mcontext.rdx = (intptr_t)tid; ctx->uc_mcontext.rcx = (intptr_t)(flags & CLONE_CHILD_SETTID ? ctid : tid); ctx->uc_mcontext.r8 = (intptr_t)(flags & CLONE_CHILD_CLEARTID ? ctid : tid); - ctx->uc_mcontext.r9 = (intptr_t)(flags & CLONE_PARENT_SETTID ? ptid : tid); ctx->uc_flags |= _UC_STACK; ctx->uc_stack.ss_sp = stk; ctx->uc_stack.ss_size = stksz; @@ -436,10 +415,13 @@ static int CloneNetbsd(int (*func)(void *, int), char *stk, size_t stksz, : "1"(__NR__lwp_create), "D"(ctx), "S"(LWP_DETACHED), "2"(tid) : "rcx", "r8", "r9", "r10", "r11", "memory"); if (!failed) { - return *tid; + _npassert(*tid); + if (flags & CLONE_PARENT_SETTID) { + *ptid = *tid; + } + return 0; } else { - errno = ax; - return -1; + return ax; } } @@ -454,8 +436,9 @@ int sys_clone_linux(int flags, // rdi void *func, // r9 void *arg); // 8(rsp) -static int CloneLinux(int (*func)(void *arg, int tid), char *stk, size_t stksz, +static int CloneLinux(int (*func)(void *arg, int rc), char *stk, size_t stksz, int flags, void *arg, void *tls, int *ptid, int *ctid) { + int rc; long sp; sp = (intptr_t)(stk + stksz); if (~flags & CLONE_CHILD_SETTID) { @@ -465,7 +448,12 @@ static int CloneLinux(int (*func)(void *arg, int tid), char *stk, size_t stksz, ctid = (int *)sp; } sp = sp & -16; // align the stack - return sys_clone_linux(flags, sp, ptid, ctid, tls, func, arg); + if ((rc = sys_clone_linux(flags, sp, ptid, ctid, tls, func, arg)) >= 0) { + // clone() is documented as setting ptid before return + return 0; + } else { + return -rc; + } } //////////////////////////////////////////////////////////////////////////////// @@ -478,14 +466,19 @@ static int CloneLinux(int (*func)(void *arg, int tid), char *stk, size_t stksz, * * int worker(void *arg) { return 0; } * struct CosmoTib tib = {.tib_self = &tib, .tib_tid = -1}; + * atomic_int tid; * char *stk = _mapstack(); - * tid = clone(worker, stk, GetStackSize() - 16, - * CLONE_VM | CLONE_THREAD | CLONE_FS | CLONE_FILES | - * CLONE_SIGHAND | CLONE_CHILD_SETTID | - * CLONE_CHILD_CLEARTID | CLONE_SETTLS, - * arg, 0, &tib, &tib.tib_tid); - * // ... - * while (atomic_load(&tib.tib_tid)) sched_yield(); + * clone(worker, stk, GetStackSize() - 16, + * CLONE_VM | CLONE_THREAD | CLONE_FS | CLONE_FILES | + * CLONE_SIGHAND | CLONE_PARENT_SETTID | CLONE_CHILD_SETTID | + * CLONE_CHILD_CLEARTID | CLONE_SETTLS, + * arg, &tid, &tib, &tib.tib_tid); + * while (atomic_load(&tid) == 0) sched_yield(); + * // thread is known + * while (atomic_load(&tib.tib_tid) < 0) sched_yield(); + * // thread is running + * while (atomic_load(&tib.tib_tid) > 0) sched_yield(); + * // thread has terminated * _freestack(stk); * * Threads are created in a detached manner. They currently can't be @@ -543,14 +536,14 @@ static int CloneLinux(int (*func)(void *arg, int tid), char *stk, size_t stksz, * Your `flags` may also optionally also additionally bitwise-OR any * combination of the following additional flags: * - * - `CLONE_PARENT_SETTID` must be specified if you intend to set - * the `ptid` argument, which is guaranteed to be updated with the - * child tid BEFORE BOTH clone() returns and `func` is invoked - * * - `CLONE_CHILD_SETTID` must be specified if you intend to set the - * `ctid` argument, which is guaranteed to be updated with the - * child tid before `func` is called, however we CAN NOT guarantee - * this will happen BEFORE clone() returns + * `ctid` argument, which will updated with the child tid once the + * child has started. + * + * - `CLONE_PARENT_SETTID` must be specified if you intend to set + * the `ptid` argument, which is updated at the most opportune + * moment. On all platforms except XNU, this happens before + * clone() returns. On XNU, it happens once the thread starts. * * - `CLONE_CHILD_CLEARTID` causes `*ctid = 0` upon child thread * termination. This is used to implement join so that the parent @@ -568,11 +561,11 @@ static int CloneLinux(int (*func)(void *arg, int tid), char *stk, size_t stksz, * this parameter is ignored if `CLONE_SETTLS` is not set * @param ctid lets the child receive its thread id without having to * call gettid() and is ignored if `CLONE_CHILD_SETTID` isn't set - * @return tid of child on success, or -1 w/ errno + * @return 0 on success, or errno on errno * @threadsafe */ -int clone(void *func, void *stk, size_t stksz, int flags, void *arg, int *ptid, - void *tls, void *ctid) { +errno_t clone(void *func, void *stk, size_t stksz, int flags, void *arg, + void *ptid, void *tls, void *ctid) { int rc; if (flags & CLONE_THREAD) { @@ -580,17 +573,17 @@ int clone(void *func, void *stk, size_t stksz, int flags, void *arg, int *ptid, } if (!func) { - rc = einval(); + rc = EINVAL; } else if (!IsTiny() && ((flags & CLONE_VM) && (stksz < PAGESIZE || (stksz & 15)))) { - rc = einval(); + rc = EINVAL; } else if (IsAsan() && (((flags & CLONE_SETTLS) && !__asan_is_valid(tls, 64)) || ((flags & CLONE_PARENT_SETTID) && - !__asan_is_valid(ptid, sizeof(*ptid))) || + !__asan_is_valid(ptid, sizeof(int))) || ((flags & CLONE_CHILD_SETTID) && !__asan_is_valid(ctid, sizeof(int))))) { - rc = efault(); + rc = EFAULT; } else if (IsLinux()) { rc = CloneLinux(func, stk, stksz, flags, arg, tls, ptid, ctid); } else if (!IsTiny() && @@ -599,7 +592,7 @@ int clone(void *func, void *stk, size_t stksz, int flags, void *arg, int *ptid, (CLONE_THREAD | CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND)) { STRACE("clone flag unsupported on this platform"); - rc = einval(); + rc = EINVAL; } else if (IsXnu()) { rc = CloneXnu(func, stk, stksz, flags, arg, tls, ptid, ctid); } else if (IsFreebsd()) { @@ -611,16 +604,11 @@ int clone(void *func, void *stk, size_t stksz, int flags, void *arg, int *ptid, } else if (IsWindows()) { rc = CloneWindows(func, stk, stksz, flags, arg, tls, ptid, ctid); } else { - rc = enosys(); + rc = ENOSYS; } - // TODO(jart): do we need it? - if (rc != -1 && (flags & CLONE_PARENT_SETTID)) { - *ptid = rc; - } - - STRACE("clone(%t, %p, %'zu, %#x, %p, %p, %p, %p) → %d% m", func, stk, stksz, - flags, arg, ptid, tls, ctid, rc); + STRACE("clone(%t, %p, %'zu, %#x, %p, %p, %p, %p) → %s", func, stk, stksz, + flags, arg, ptid, tls, ctid, DescribeErrnoResult(rc)); return rc; } diff --git a/libc/runtime/clone.internal.h b/libc/runtime/clone.internal.h index e1a9142c6..89f4b826a 100644 --- a/libc/runtime/clone.internal.h +++ b/libc/runtime/clone.internal.h @@ -4,7 +4,7 @@ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -int clone(void *, void *, size_t, int, void *, int *, void *, void *); +int clone(void *, void *, size_t, int, void *, void *, void *, void *); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/runtime/enable_tls.c b/libc/runtime/enable_tls.c index 18c7f4cae..35f5fca48 100644 --- a/libc/runtime/enable_tls.c +++ b/libc/runtime/enable_tls.c @@ -117,8 +117,8 @@ void __enable_tls(void) { tid = sys_gettid(); } atomic_store_explicit(&tib->tib_tid, tid, memory_order_relaxed); + _pthread_main.ptid = tid; _pthread_main.tib = tib; - _pthread_main.tid = tid; _pthread_main.flags = PT_MAINTHREAD; __repmovsb(tls, _tdata_start, _TLDZ); diff --git a/libc/runtime/fork.c b/libc/runtime/fork.c index 48da63f7a..10c060fa7 100644 --- a/libc/runtime/fork.c +++ b/libc/runtime/fork.c @@ -34,7 +34,9 @@ #include "libc/thread/tls.h" int _fork(uint32_t dwCreationFlags) { - int ax, dx, parent; + struct CosmoTib *tib; + struct PosixThread *pt; + int ax, dx, tid, parent; BLOCK_SIGNALS; if (__threaded && _weaken(_pthread_onfork_prepare)) { _weaken(_pthread_onfork_prepare)(); @@ -53,9 +55,12 @@ int _fork(uint32_t dwCreationFlags) { parent = __pid; __pid = dx; if (__tls_enabled) { - atomic_store_explicit(&__get_tls()->tib_tid, - IsLinux() ? dx : sys_gettid(), - memory_order_relaxed); + tib = __get_tls(); + tid = IsLinux() ? dx : sys_gettid(); + atomic_store_explicit(&tib->tib_tid, tid, memory_order_relaxed); + if ((pt = (struct PosixThread *)tib->tib_pthread)) { + atomic_store_explicit(&pt->ptid, tid, memory_order_relaxed); + } } if (__threaded && _weaken(_pthread_onfork_child)) { _weaken(_pthread_onfork_child)(); diff --git a/libc/sysv/calls/__sys_clock_nanosleep.s b/libc/sysv/calls/__sys_clock_nanosleep.s new file mode 100644 index 000000000..89ebfbc68 --- /dev/null +++ b/libc/sysv/calls/__sys_clock_nanosleep.s @@ -0,0 +1,2 @@ +.include "o/libc/sysv/macros.internal.inc" +.scall __sys_clock_nanosleep,0x9ddfff8f4ffff8e6,globl,hidden diff --git a/libc/sysv/calls/sys_bsdthread_create.s b/libc/sysv/calls/sys_bsdthread_create.s deleted file mode 100644 index 03f328b4d..000000000 --- a/libc/sysv/calls/sys_bsdthread_create.s +++ /dev/null @@ -1,2 +0,0 @@ -.include "o/libc/sysv/macros.internal.inc" -.scall sys_bsdthread_create,0xfffffffff2168fff,globl,hidden diff --git a/libc/sysv/calls/sys_clock_nanosleep.s b/libc/sysv/calls/sys_clock_nanosleep.s deleted file mode 100644 index 82c21586c..000000000 --- a/libc/sysv/calls/sys_clock_nanosleep.s +++ /dev/null @@ -1,2 +0,0 @@ -.include "o/libc/sysv/macros.internal.inc" -.scall sys_clock_nanosleep,0x9ddfff8f4ffff8e6,globl,hidden diff --git a/libc/sysv/syscalls.sh b/libc/sysv/syscalls.sh index 631fb7aaf..44d6da896 100755 --- a/libc/sysv/syscalls.sh +++ b/libc/sysv/syscalls.sh @@ -71,7 +71,7 @@ scall sys_dup 0x0290290292029020 globl hidden scall sys_dup2 0x05a05a05a205a021 globl hidden scall sys_pause 0xfffffffffffff022 globl hidden scall sys_nanosleep 0x9ae85b8f0ffff823 globl hidden -scall sys_clock_nanosleep 0x9ddfff8f4ffff8e6 globl hidden +scall __sys_clock_nanosleep 0x9ddfff8f4ffff8e6 globl hidden scall sys_getitimer 0x1aa0460562056024 globl hidden scall sys_setitimer 0x1a90450532053026 globl hidden scall sys_alarm 0xfffffffffffff025 globl hidden @@ -492,7 +492,7 @@ scall sys_closefrom 0xfff11f1fdfffffff globl hidden #scall audit_session_join 0xfffffffff21adfff globl #scall audit_session_port 0xfffffffff21b0fff globl #scall audit_session_self 0xfffffffff21acfff globl -scall sys_bsdthread_create 0xfffffffff2168fff globl hidden +#scall sys_bsdthread_create 0xfffffffff2168fff globl #scall bsdthread_ctl 0xfffffffff21defff globl scall sys_bsdthread_register 0xfffffffff216efff globl hidden #scall bsdthread_terminate 0xfffffffff2169fff globl diff --git a/libc/thread/posixthread.internal.h b/libc/thread/posixthread.internal.h index 45629691d..72fd9dd3a 100644 --- a/libc/thread/posixthread.internal.h +++ b/libc/thread/posixthread.internal.h @@ -66,7 +66,7 @@ struct PosixThread { int flags; // 0x00: see PT_* constants _Atomic(int) cancelled; // 0x04: thread has bad beliefs _Atomic(enum PosixThreadStatus) status; - int tid; // clone parent tid + _Atomic(int) ptid; // transitions 0 → tid void *(*start)(void *); // creation callback void *arg; // start's parameter void *rc; // start's return value diff --git a/libc/thread/pthread_atfork.c b/libc/thread/pthread_atfork.c index 5bf8204fa..18a6bceaf 100644 --- a/libc/thread/pthread_atfork.c +++ b/libc/thread/pthread_atfork.c @@ -73,7 +73,6 @@ void _pthread_onfork_child(void) { tib = __get_tls(); pt = (struct PosixThread *)tib->tib_pthread; atomic_store_explicit(&pt->cancelled, false, memory_order_relaxed); - pt->tid = atomic_load_explicit(&tib->tib_tid, memory_order_relaxed); pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&__mmi_lock_obj, &attr); diff --git a/libc/thread/pthread_cancel.c b/libc/thread/pthread_cancel.c index 3c6339245..b3a0b1e64 100644 --- a/libc/thread/pthread_cancel.c +++ b/libc/thread/pthread_cancel.c @@ -87,7 +87,7 @@ static void ListenForSigThr(void) { * * By default, pthread_cancel() can only take effect when a thread * reaches a cancellation point. Such functions are documented with - * @cancellationpoint. They check the cancellation state before the + * `@cancellationpoint`. They check the cancellation state before the * underlying system call is issued. If the system call is issued and * blocks, then pthread_cancel() will interrupt the operation in which * case the syscall wrapper will check the cancelled state a second @@ -277,18 +277,20 @@ errno_t pthread_cancel(pthread_t thread) { } return 0; } - if (IsWindows()) return 0; // no true cancellations on Windows yet - tid = atomic_load_explicit(&pt->tib->tib_tid, memory_order_acquire); - if (tid <= 0) return 0; // -1 means still starting, 0 means exited - e = errno; - if (!__tkill(tid, SIGTHR, pt->tib)) { - return 0; - } else { - rc = errno; - errno = e; - return rc; + if (!(rc = pthread_getunique_np(thread, &tid))) { + if (!IsWindows()) { + e = errno; + if (!__tkill(tid, SIGTHR, pt->tib)) { + rc = 0; + } else { + rc = errno; + errno = e; + } + } else { + rc = 0; + } } - return 0; + return rc; } /** diff --git a/libc/thread/pthread_create.c b/libc/thread/pthread_create.c index 339d32cb6..f41439aab 100644 --- a/libc/thread/pthread_create.c +++ b/libc/thread/pthread_create.c @@ -19,38 +19,27 @@ #include "libc/assert.h" #include "libc/calls/blocksigs.internal.h" #include "libc/calls/calls.h" -#include "libc/calls/sched-sysv.internal.h" -#include "libc/calls/state.internal.h" #include "libc/calls/struct/sigaltstack.h" -#include "libc/calls/struct/sigset.h" #include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/errno.h" #include "libc/intrin/asan.internal.h" #include "libc/intrin/atomic.h" #include "libc/intrin/bits.h" -#include "libc/intrin/kprintf.h" -#include "libc/intrin/weaken.h" #include "libc/log/internal.h" #include "libc/macros.internal.h" #include "libc/mem/mem.h" -#include "libc/nexgen32e/gc.internal.h" #include "libc/runtime/clone.internal.h" #include "libc/runtime/runtime.h" #include "libc/runtime/stack.h" -#include "libc/stdio/stdio.h" #include "libc/sysv/consts/clone.h" #include "libc/sysv/consts/map.h" #include "libc/sysv/consts/prot.h" -#include "libc/sysv/consts/sig.h" #include "libc/sysv/consts/ss.h" -#include "libc/sysv/errfuns.h" #include "libc/thread/posixthread.internal.h" #include "libc/thread/spawn.h" #include "libc/thread/thread.h" -#include "libc/thread/tls.h" #include "libc/thread/wait0.internal.h" -#include "third_party/dlmalloc/dlmalloc.h" STATIC_YOINK("nsync_mu_lock"); STATIC_YOINK("nsync_mu_unlock"); @@ -254,15 +243,13 @@ static errno_t pthread_create_impl(pthread_t *thread, // launch PosixThread(pt) in new thread pt->sigmask = oldsigs; - if (clone(PosixThread, pt->attr.__stackaddr, - pt->attr.__stacksize - (IsOpenbsd() ? 16 : 0), - CLONE_VM | CLONE_THREAD | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | - CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_SETTID | - CLONE_CHILD_CLEARTID, - pt, &pt->tid, pt->tib, &pt->tib->tib_tid) == -1) { - rc = errno; + if ((rc = clone(PosixThread, pt->attr.__stackaddr, + pt->attr.__stacksize - (IsOpenbsd() ? 16 : 0), + CLONE_VM | CLONE_THREAD | CLONE_FS | CLONE_FILES | + CLONE_SIGHAND | CLONE_SETTLS | CLONE_PARENT_SETTID | + CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID, + pt, &pt->ptid, pt->tib, &pt->tib->tib_tid))) { _pthread_free(pt); - errno = e; return rc; } diff --git a/libc/thread/pthread_getaffinity_np.c b/libc/thread/pthread_getaffinity_np.c index 91d8ed55b..20dcdeeed 100644 --- a/libc/thread/pthread_getaffinity_np.c +++ b/libc/thread/pthread_getaffinity_np.c @@ -20,6 +20,7 @@ #include "libc/calls/struct/cpuset.h" #include "libc/dce.h" #include "libc/errno.h" +#include "libc/intrin/atomic.h" #include "libc/intrin/describeflags.internal.h" #include "libc/intrin/strace.internal.h" #include "libc/str/str.h" @@ -33,37 +34,40 @@ * @return 0 on success, or errno on error * @raise EINVAL if `size` or `bitset` is invalid * @raise ENOSYS if not Linux, FreeBSD, or NetBSD + * @raise ESRCH if thread isn't alive */ errno_t pthread_getaffinity_np(pthread_t thread, size_t size, cpu_set_t *bitset) { - int tid, rc, e = errno; - tid = ((struct PosixThread *)thread)->tid; + int e, rc, tid; - if (size != sizeof(cpu_set_t)) { - rc = einval(); - } else if (IsWindows() || IsMetal() || IsOpenbsd()) { - rc = enosys(); - } else if (IsFreebsd()) { - if (!sys_sched_getaffinity_freebsd(CPU_LEVEL_WHICH, CPU_WHICH_TID, tid, 32, - bitset)) { - rc = 32; + if (!(rc = pthread_getunique_np(thread, &tid))) { + e = errno; + if (size != sizeof(cpu_set_t)) { + rc = einval(); + } else if (IsWindows() || IsMetal() || IsOpenbsd()) { + rc = enosys(); + } else if (IsFreebsd()) { + if (!sys_sched_getaffinity_freebsd(CPU_LEVEL_WHICH, CPU_WHICH_TID, tid, + 32, bitset)) { + rc = 32; + } else { + rc = -1; + } + } else if (IsNetbsd()) { + if (!sys_sched_getaffinity_netbsd(tid, 0, 32, bitset)) { + rc = 32; + } else { + rc = -1; + } } else { - rc = -1; + rc = sys_sched_getaffinity(tid, size, bitset); } - } else if (IsNetbsd()) { - if (!sys_sched_getaffinity_netbsd(tid, 0, 32, bitset)) { - rc = 32; - } else { - rc = -1; + if (rc > 0) { + if (rc < size) { + bzero((char *)bitset + rc, size - rc); + } + rc = 0; } - } else { - rc = sys_sched_getaffinity(tid, size, bitset); - } - if (rc > 0) { - if (rc < size) { - bzero((char *)bitset + rc, size - rc); - } - rc = 0; } STRACE("pthread_getaffinity_np(%d, %'zu, %p) → %s", tid, size, bitset, diff --git a/libc/thread/pthread_getname_np.c b/libc/thread/pthread_getname_np.c index 02eeb89f7..4170d7c3f 100644 --- a/libc/thread/pthread_getname_np.c +++ b/libc/thread/pthread_getname_np.c @@ -23,6 +23,7 @@ #include "libc/errno.h" #include "libc/fmt/itoa.h" #include "libc/intrin/asmflag.h" +#include "libc/intrin/atomic.h" #include "libc/macros.internal.h" #include "libc/str/str.h" #include "libc/sysv/consts/o.h" @@ -30,11 +31,12 @@ #include "libc/thread/posixthread.internal.h" static errno_t pthread_getname_impl(pthread_t thread, char *name, size_t size) { - int fd, rc, tid, len, e = errno; + int e, fd, rc, tid, len; + if ((rc = pthread_getunique_np(thread, &tid))) return rc; if (!size) return 0; bzero(name, size); - tid = ((struct PosixThread *)thread)->tid; + e = errno; if (IsLinux()) { // TASK_COMM_LEN is 16 on Linux so we're just being paranoid. diff --git a/libc/thread/pthread_getunique_np.c b/libc/thread/pthread_getunique_np.c index d3bf75032..753d66eb7 100644 --- a/libc/thread/pthread_getunique_np.c +++ b/libc/thread/pthread_getunique_np.c @@ -16,12 +16,26 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/errno.h" +#include "libc/intrin/atomic.h" #include "libc/thread/posixthread.internal.h" +#include "libc/thread/thread.h" /** - * Returns thread id of POSIX thread. + * Returns system thread id of POSIX thread. + * + * @return 0 on success, or errno on error */ -int64_t pthread_getunique_np(pthread_t thread) { - struct PosixThread *pt = (struct PosixThread *)thread; - return pt->tid; +errno_t pthread_getunique_np(pthread_t thread, pthread_id_np_t *out_tid) { + int tid; + struct PosixThread *pt; + for (pt = (struct PosixThread *)thread;;) { + tid = atomic_load_explicit(&pt->ptid, memory_order_acquire); + if (!tid) { + pthread_yield(); + } else { + *out_tid = tid; + return 0; + } + } } diff --git a/libc/thread/pthread_kill.c b/libc/thread/pthread_kill.c index 570302615..f4390cede 100644 --- a/libc/thread/pthread_kill.c +++ b/libc/thread/pthread_kill.c @@ -19,6 +19,7 @@ #include "libc/calls/calls.h" #include "libc/calls/syscall_support-sysv.internal.h" #include "libc/errno.h" +#include "libc/intrin/atomic.h" #include "libc/thread/posixthread.internal.h" #include "libc/thread/thread.h" @@ -33,13 +34,17 @@ * @asyncsignalsafe */ errno_t pthread_kill(pthread_t thread, int sig) { - int rc, e = errno; - struct PosixThread *pt = (struct PosixThread *)thread; - if (!__tkill(pt->tid, sig, pt->tib)) { - rc = 0; - } else { - rc = errno; - errno = e; + int e, rc, tid; + struct PosixThread *p; + if (!(rc = pthread_getunique_np(thread, &tid))) { + e = errno; + p = (struct PosixThread *)thread; + if (!__tkill(tid, sig, p->tib)) { + rc = 0; + } else { + rc = errno; + errno = e; + } } return rc; } diff --git a/libc/thread/pthread_reschedule.c b/libc/thread/pthread_reschedule.c index 4aca1c16e..18461a53c 100644 --- a/libc/thread/pthread_reschedule.c +++ b/libc/thread/pthread_reschedule.c @@ -20,23 +20,29 @@ #include "libc/calls/struct/sched_param.h" #include "libc/dce.h" #include "libc/errno.h" +#include "libc/intrin/atomic.h" #include "libc/sysv/errfuns.h" #include "libc/thread/posixthread.internal.h" errno_t _pthread_reschedule(struct PosixThread *pt) { - int rc, e = errno; + int e, rc, tid; int policy = pt->attr.__schedpolicy; struct sched_param param = {pt->attr.__schedparam}; - if (IsNetbsd()) { - rc = sys_sched_setparam_netbsd(0, pt->tid, policy, ¶m); - } else if (IsLinux()) { - rc = sys_sched_setscheduler(pt->tid, policy, ¶m); - } else if (IsFreebsd()) { - rc = _pthread_setschedparam_freebsd(pt->tid, policy, ¶m); - } else { - rc = enosys(); + if (!(rc = pthread_getunique_np((pthread_t)pt, &tid))) { + e = errno; + if (IsNetbsd()) { + rc = sys_sched_setparam_netbsd(0, tid, policy, ¶m); + } else if (IsLinux()) { + rc = sys_sched_setscheduler(tid, policy, ¶m); + } else if (IsFreebsd()) { + rc = _pthread_setschedparam_freebsd(tid, policy, ¶m); + } else { + rc = enosys(); + } + if (rc == -1) { + rc = errno; + errno = e; + } } - rc = rc != -1 ? 0 : errno; - errno = e; return rc; } diff --git a/libc/thread/pthread_setaffinity_np.c b/libc/thread/pthread_setaffinity_np.c index 415e85ff4..a4ed86415 100644 --- a/libc/thread/pthread_setaffinity_np.c +++ b/libc/thread/pthread_setaffinity_np.c @@ -21,6 +21,7 @@ #include "libc/calls/syscall_support-nt.internal.h" #include "libc/dce.h" #include "libc/errno.h" +#include "libc/intrin/atomic.h" #include "libc/intrin/describeflags.internal.h" #include "libc/intrin/strace.internal.h" #include "libc/nt/enum/threadaccess.h" @@ -52,23 +53,25 @@ static dontinline textwindows int sys_pthread_setaffinity_nt( */ errno_t pthread_setaffinity_np(pthread_t thread, size_t size, const cpu_set_t *bitset) { - int tid, rc, e = errno; - tid = ((struct PosixThread *)thread)->tid; - if (size != sizeof(cpu_set_t)) { - rc = einval(); - } else if (IsWindows()) { - rc = sys_pthread_setaffinity_nt(tid, size, bitset); - } else if (IsFreebsd()) { - rc = sys_sched_setaffinity_freebsd(CPU_LEVEL_WHICH, CPU_WHICH_TID, tid, 32, - bitset); - } else if (IsNetbsd()) { - rc = sys_sched_setaffinity_netbsd(tid, 0, 32, bitset); - } else { - rc = sys_sched_setaffinity(tid, size, bitset); - } - if (rc == -1) { - rc = errno; - errno = e; + int e, rc, tid; + if (!(rc = pthread_getunique_np(thread, &tid))) { + e = errno; + if (size != sizeof(cpu_set_t)) { + rc = einval(); + } else if (IsWindows()) { + rc = sys_pthread_setaffinity_nt(tid, size, bitset); + } else if (IsFreebsd()) { + rc = sys_sched_setaffinity_freebsd(CPU_LEVEL_WHICH, CPU_WHICH_TID, tid, + 32, bitset); + } else if (IsNetbsd()) { + rc = sys_sched_setaffinity_netbsd(tid, 0, 32, bitset); + } else { + rc = sys_sched_setaffinity(tid, size, bitset); + } + if (rc == -1) { + rc = errno; + errno = e; + } } STRACE("pthread_setaffinity_np(%d, %'zu, %p) → %s", tid, size, bitset, DescribeErrnoResult(rc)); diff --git a/libc/thread/pthread_setname_np.c b/libc/thread/pthread_setname_np.c index 3123dce94..1effca9a7 100644 --- a/libc/thread/pthread_setname_np.c +++ b/libc/thread/pthread_setname_np.c @@ -24,6 +24,7 @@ #include "libc/fmt/itoa.h" #include "libc/intrin/asan.internal.h" #include "libc/intrin/asmflag.h" +#include "libc/intrin/atomic.h" #include "libc/str/str.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/pr.h" @@ -31,12 +32,13 @@ static errno_t pthread_setname_impl(pthread_t thread, const char *name) { char path[128], *p; - int fd, rc, tid, len, e = errno; + int e, fd, rc, tid, len; - tid = ((struct PosixThread *)thread)->tid; + if ((rc = pthread_getunique_np(thread, &tid))) return rc; len = strlen(name); if (IsLinux()) { + e = errno; if (tid == gettid()) { if (prctl(PR_SET_NAME, name) == -1) { rc = errno; diff --git a/libc/thread/spawn.c b/libc/thread/spawn.c index 21a72aba1..20bedfb53 100644 --- a/libc/thread/spawn.c +++ b/libc/thread/spawn.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" +#include "libc/errno.h" #include "libc/macros.internal.h" #include "libc/mem/mem.h" #include "libc/runtime/clone.internal.h" @@ -92,6 +93,7 @@ static int Spawner(void *arg, int tid) { * @return 0 on success, or -1 w/ errno */ int _spawn(int fun(void *, int), void *arg, struct spawn *opt_out_thread) { + errno_t rc; struct spawn *th, ths; struct spawner *spawner; __require_tls(); @@ -122,11 +124,13 @@ int _spawn(int fun(void *, int), void *arg, struct spawn *opt_out_thread) { spawner = malloc(sizeof(struct spawner)); spawner->fun = fun; spawner->arg = arg; - if (clone(Spawner, th->stk, GetStackSize() - 16 /* openbsd:stackbound */, - CLONE_VM | CLONE_THREAD | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | - CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_SETTID | - CLONE_CHILD_CLEARTID, - spawner, &th->ptid, th->tib, &th->tib->tib_tid) == -1) { + rc = clone(Spawner, th->stk, GetStackSize() - 16 /* openbsd:stackbound */, + CLONE_VM | CLONE_THREAD | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | + CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_SETTID | + CLONE_CHILD_CLEARTID, + spawner, &th->ptid, th->tib, &th->tib->tib_tid); + if (rc) { + errno = rc; _freestack(th->stk); free(th->tls); return -1; diff --git a/libc/thread/thread.h b/libc/thread/thread.h index b3f70496b..b6d7330aa 100644 --- a/libc/thread/thread.h +++ b/libc/thread/thread.h @@ -115,7 +115,7 @@ int pthread_testcancel_np(void); void pthread_exit(void *) wontreturn; pthread_t pthread_self(void) pureconst; pthread_id_np_t pthread_getthreadid_np(void); -int64_t pthread_getunique_np(pthread_t); +int pthread_getunique_np(pthread_t, pthread_id_np_t *); int pthread_setname_np(pthread_t, const char *); int pthread_getname_np(pthread_t, char *, size_t); int pthread_getattr_np(pthread_t, pthread_attr_t *); diff --git a/libc/thread/tls.h b/libc/thread/tls.h index d6b773169..a78a3c426 100644 --- a/libc/thread/tls.h +++ b/libc/thread/tls.h @@ -24,7 +24,7 @@ struct CosmoTib { intptr_t tib_locale; /* 0x20 */ intptr_t tib_pthread; /* 0x28 */ struct CosmoTib *tib_self2; /* 0x30 */ - _Atomic(int32_t) tib_tid; /* 0x38 */ + _Atomic(int32_t) tib_tid; /* 0x38 transitions -1 → tid → 0 */ int32_t tib_errno; /* 0x3c */ uint64_t tib_flags; /* 0x40 */ void *tib_nsync; diff --git a/libc/thread/xnu.internal.h b/libc/thread/xnu.internal.h index 6462f8cf4..71d5d5fba 100644 --- a/libc/thread/xnu.internal.h +++ b/libc/thread/xnu.internal.h @@ -8,8 +8,8 @@ COSMOPOLITAN_C_START_ * See darwin-libpthread/kern/kern_support.c */ -int sys_bsdthread_create(void *func, void *func_arg, void *stack, void *pthread, - uint32_t flags); +errno_t sys_clone_xnu(void *func, void *func_arg, void *stack, void *pthread, + uint32_t flags); int sys_bsdthread_register( void (*threadstart)(void *pthread, int machport, void *(*func)(void *), void *arg, intptr_t *, unsigned), diff --git a/libc/time/iso8601us.c b/libc/time/iso8601us.c index e993be55b..77aff570c 100644 --- a/libc/time/iso8601us.c +++ b/libc/time/iso8601us.c @@ -65,7 +65,6 @@ * @threadsafe */ char *iso8601us(char p[hasatleast 27], struct tm *tm, long ns) { - int x; p = iso8601(p, tm); _unassert(0 <= ns && ns < 1000000000); *p++ = '.'; diff --git a/test/libc/calls/nanosleep_test.c b/test/libc/calls/nanosleep_test.c index 8b8ca9603..5bd1ea2a6 100644 --- a/test/libc/calls/nanosleep_test.c +++ b/test/libc/calls/nanosleep_test.c @@ -22,6 +22,7 @@ #include "libc/calls/struct/timespec.h" #include "libc/errno.h" #include "libc/intrin/describeflags.internal.h" +#include "libc/intrin/strace.internal.h" #include "libc/sysv/consts/clock.h" #include "libc/sysv/consts/itimer.h" #include "libc/sysv/consts/sa.h" @@ -31,6 +32,7 @@ void OnAlrm(int sig) { // do nothing + STRACE("OnAlrm()"); } TEST(nanosleep, testFault) { @@ -54,7 +56,7 @@ TEST(nanosleep, testInterrupt_remIsUpdated) { .sa_flags = SA_RESETHAND, }; ASSERT_SYS(0, 0, sigaction(SIGALRM, &sa, 0)); - struct itimerval it = {{0, 0}, {0, 10000}}; // 10ms singleshot + struct itimerval it = {{0, 0}, {0, 100000}}; // 100ms singleshot ASSERT_SYS(0, 0, setitimer(ITIMER_REAL, &it, 0)); struct timespec ts = {500, 0}; ASSERT_SYS(EINTR, -1, nanosleep(&ts, &ts)); @@ -75,7 +77,7 @@ TEST(clock_nanosleep, testInterrupt_remIsUpdated) { .sa_flags = SA_RESETHAND, }; ASSERT_SYS(0, 0, sigaction(SIGALRM, &sa, 0)); - struct itimerval it = {{0, 0}, {0, 10000}}; // 10ms singleshot + struct itimerval it = {{0, 0}, {0, 100000}}; // 100ms singleshot ASSERT_SYS(0, 0, setitimer(ITIMER_REAL, &it, 0)); struct timespec ts = {500, 0}; ASSERT_EQ(EINTR, clock_nanosleep(CLOCK_REALTIME, 0, &ts, &ts)); diff --git a/test/libc/calls/tkill_test.c b/test/libc/calls/tkill_test.c index defed8bef..b7f0be24d 100644 --- a/test/libc/calls/tkill_test.c +++ b/test/libc/calls/tkill_test.c @@ -39,8 +39,8 @@ void *Worker(void *arg) { TEST(tkill, test) { if (IsWindows()) return; // TODO(jart): fix me - int i; void *res; + int i, tid; pthread_t t; sigset_t ss, oldss; sighandler_t oldsig; @@ -49,7 +49,8 @@ TEST(tkill, test) { oldsig = signal(SIGUSR1, OnSig); ASSERT_SYS(0, 0, sigprocmask(SIG_BLOCK, &ss, &oldss)); ASSERT_EQ(0, pthread_create(&t, 0, Worker, 0)); - ASSERT_SYS(0, 0, tkill(pthread_getunique_np(t), SIGUSR1)); + ASSERT_EQ(0, pthread_getunique_np(t, &tid)); + ASSERT_SYS(0, 0, tkill(tid, SIGUSR1)); ASSERT_EQ(0, pthread_join(t, &res)); ASSERT_EQ(SIGUSR1, (intptr_t)res); ASSERT_SYS(0, 0, sigprocmask(SIG_SETMASK, &oldss, 0)); diff --git a/test/libc/stdio/getentropy_test.c b/test/libc/stdio/getentropy_test.c index 4e324f0fc..3881cf850 100644 --- a/test/libc/stdio/getentropy_test.c +++ b/test/libc/stdio/getentropy_test.c @@ -22,6 +22,7 @@ #include "libc/calls/struct/sigset.h" #include "libc/dce.h" #include "libc/errno.h" +#include "libc/macros.internal.h" #include "libc/mem/gc.h" #include "libc/mem/mem.h" #include "libc/runtime/runtime.h" @@ -50,9 +51,9 @@ void *TortureWorker(void *arg) { ready = true; while (!done) { if (!IsWindows()) pthread_kill(parent, SIGUSR1); - usleep(3); + usleep(1); if (!IsWindows()) pthread_kill(parent, SIGUSR2); - usleep(3); + usleep(1); } return 0; } @@ -61,7 +62,7 @@ TEST(getentropy, test) { pthread_t child; double e, w = 7.7; struct sigaction sa; - int i, j, k, n = 999; + int i, j, k, m, n = 999; char *buf = _gc(calloc(1, n)); sa.sa_flags = 0; sa.sa_handler = OnSig; @@ -71,11 +72,13 @@ TEST(getentropy, test) { parent = pthread_self(); ASSERT_EQ(0, pthread_create(&child, 0, TortureWorker, 0)); while (!ready) pthread_yield(); - for (k = 0; k < 200; ++k) { - ASSERT_SYS(0, 0, getrandom(0, 0, 0)); - ASSERT_SYS(0, n, getrandom(buf, n, 0)); - ASSERT_SYS(EFAULT, -1, getrandom(0, n, 0)); - ASSERT_SYS(EINVAL, -1, getrandom(buf, n, -1)); + for (k = 0; k < 10; ++k) { + ASSERT_SYS(0, 0, getentropy(0, 0)); + for (i = 0; i < n; i += m) { + m = MIN(n - i, 256); + ASSERT_SYS(0, 0, getentropy(buf + i, m)); + ASSERT_SYS(EFAULT, -1, getentropy(0, m)); + } if ((e = MeasureEntropy(buf, n)) < w) { fprintf(stderr, "error: entropy suspect! got %g but want >=%g\n", e, w); for (i = 0; i < n;) { diff --git a/test/libc/stdio/getrandom_test.c b/test/libc/stdio/getrandom_test.c index 430fa2dc0..04b13eea6 100644 --- a/test/libc/stdio/getrandom_test.c +++ b/test/libc/stdio/getrandom_test.c @@ -24,6 +24,7 @@ #include "libc/errno.h" #include "libc/intrin/bits.h" #include "libc/log/check.h" +#include "libc/macros.internal.h" #include "libc/math.h" #include "libc/mem/gc.h" #include "libc/mem/mem.h" @@ -41,6 +42,29 @@ #include "libc/testlib/testlib.h" #include "libc/thread/thread.h" +atomic_int done; +atomic_int ready; +pthread_t parent; +atomic_int gotsome; + +void OnSig(int sig) { + ++gotsome; +} + +void *TortureWorker(void *arg) { + sigset_t ss; + sigfillset(&ss); + ASSERT_SYS(0, 0, sigprocmask(SIG_SETMASK, &ss, 0)); + ready = true; + while (!done) { + if (!IsWindows()) pthread_kill(parent, SIGUSR1); + usleep(1); + if (!IsWindows()) pthread_kill(parent, SIGUSR2); + usleep(1); + } + return 0; +} + TEST(getrandom, test) { double e, w = 7.7; int i, j, n = 999; @@ -61,6 +85,45 @@ TEST(getrandom, test) { } } +TEST(getrandom, test2) { + pthread_t child; + double e, w = 7.7; + struct sigaction sa; + int i, j, k, m, n = 999; + char *buf = _gc(calloc(1, n)); + sa.sa_flags = 0; + sa.sa_handler = OnSig; + sigemptyset(&sa.sa_mask); + ASSERT_SYS(0, 0, sigaction(SIGUSR1, &sa, 0)); + ASSERT_SYS(0, 0, sigaction(SIGUSR2, &sa, 0)); + parent = pthread_self(); + ASSERT_EQ(0, pthread_create(&child, 0, TortureWorker, 0)); + while (!ready) pthread_yield(); + for (k = 0; k < 10; ++k) { + ASSERT_SYS(0, 0, getrandom(0, 0, 0)); + for (i = 0; i < n; i += m) { + ASSERT_NE(-1, (m = getrandom(buf + i, n - i, 0))); + } + ASSERT_SYS(EFAULT, -1, getrandom(0, n, 0)); + ASSERT_SYS(EINVAL, -1, getrandom(buf, n, -1)); + if ((e = MeasureEntropy(buf, n)) < w) { + fprintf(stderr, "error: entropy suspect! got %g but want >=%g\n", e, w); + for (i = 0; i < n;) { + if (!(i % 16)) fprintf(stderr, "%6x ", i); + fprintf(stderr, "%lc", kCp437[buf[i] & 255]); + if (!(++i % 16)) fprintf(stderr, "\n"); + } + fprintf(stderr, "\n"); + done = true; + pthread_join(child, 0); + exit(1); + } + } + done = true; + ASSERT_EQ(0, pthread_join(child, 0)); + if (!IsWindows()) ASSERT_GT(gotsome, 0); +} + /* JustReturnZero */ /* entropy: 0 */ /* chi-square: 2.55e+07 */ diff --git a/third_party/nsync/futex.c b/third_party/nsync/futex.c index b432e8531..f630a3a35 100644 --- a/third_party/nsync/futex.c +++ b/third_party/nsync/futex.c @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" #include "libc/atomic.h" #include "libc/calls/calls.h" #include "libc/calls/internal.h" @@ -111,29 +112,36 @@ __attribute__((__constructor__)) static void nsync_futex_init_ (void) { } } -static int nsync_futex_polyfill_ (atomic_int *w, int expect, struct timespec *timeout) { +static int nsync_futex_polyfill_ (atomic_int *w, int expect, struct timespec *abstime) { int rc; int64_t nanos, maxnanos; - struct timespec ts, deadline; + struct timespec now, wait, remain, deadline; - ts = timespec_real (); - if (!timeout) { + if (!abstime) { deadline = timespec_max; } else { - deadline = *timeout; + deadline = *abstime; } nanos = 100; maxnanos = __SIG_POLLING_INTERVAL_MS * 1000L * 1000; - while (timespec_cmp (deadline, ts) > 0) { - ts = timespec_add (ts, timespec_fromnanos (nanos)); - if (timespec_cmp (ts, deadline) > 0) { - ts = deadline; - } + for (;;) { if (atomic_load_explicit (w, memory_order_acquire) != expect) { return 0; } - if ((rc = timespec_sleep_until (ts))) { + now = timespec_real (); + if (atomic_load_explicit (w, memory_order_acquire) != expect) { + return 0; + } + if (timespec_cmp (now, deadline) >= 0) { + break; + } + wait = timespec_fromnanos (nanos); + remain = timespec_sub (deadline, now); + if (timespec_cmp(wait, remain) > 0) { + wait = remain; + } + if ((rc = clock_nanosleep (CLOCK_REALTIME, 0, &wait, 0))) { return -rc; } if (nanos < maxnanos) { diff --git a/tool/emacs/cosmo-c-constants.el b/tool/emacs/cosmo-c-constants.el index 824aab5ca..e9cd247e4 100644 --- a/tool/emacs/cosmo-c-constants.el +++ b/tool/emacs/cosmo-c-constants.el @@ -103,7 +103,8 @@ "DBL_MIN" "DBL_MAX" "FLT_MIN" - "FLT_MAX")) + "FLT_MAX" + "PIPE_BUF")) (defconst cosmo-c-constants-math '("NAN" diff --git a/tool/net/definitions.lua b/tool/net/definitions.lua index c33411f41..0e6a2d24e 100644 --- a/tool/net/definitions.lua +++ b/tool/net/definitions.lua @@ -3322,6 +3322,8 @@ unix = { --- @type integer CLOCK_MONOTONIC_COARSE = nil, --- @type integer + CLOCK_MONOTONIC_PRECISE = nil, + --- @type integer CLOCK_MONOTONIC_FAST = nil, --- @type integer CLOCK_MONOTONIC_RAW = nil, @@ -3332,6 +3334,8 @@ unix = { --- @type integer CLOCK_REALTIME = nil, --- @type integer + CLOCK_REALTIME_PRECISE = nil, + --- @type integer CLOCK_REALTIME_ALARM = nil, --- @type integer CLOCK_REALTIME_COARSE = nil, @@ -5247,13 +5251,16 @@ function unix.syslog(priority, msg) end --- --- - `CLOCK_REALTIME`: universally supported --- - `CLOCK_REALTIME_FAST`: ditto but faster on freebsd +--- - `CLOCK_REALTIME_PRECISE`: ditto but better on freebsd +--- - `CLOCK_REALTIME_COARSE`: : like `CLOCK_REALTIME_FAST` but needs Linux 2.6.32+ --- - `CLOCK_MONOTONIC`: universally supported --- - `CLOCK_MONOTONIC_FAST`: ditto but faster on freebsd ---- - `CLOCK_MONOTONIC_RAW`: nearly universally supported +--- - `CLOCK_MONOTONIC_PRECISE`: ditto but better on freebsd +--- - `CLOCK_MONOTONIC_COARSE`: : like `CLOCK_MONOTONIC_FAST` but needs Linux 2.6.32+ +--- - `CLOCK_MONOTONIC_RAW`: is actually monotonic but needs Linux 2.6.28+ --- - `CLOCK_PROCESS_CPUTIME_ID`: linux and bsd --- - `CLOCK_THREAD_CPUTIME_ID`: linux and bsd ---- - `CLOCK_REALTIME_COARSE`: : linux and openbsd ---- - `CLOCK_MONOTONIC_COARSE`: linux +--- - `CLOCK_MONOTONIC_COARSE`: linux, freebsd --- - `CLOCK_PROF`: linux and netbsd --- - `CLOCK_BOOTTIME`: linux and openbsd --- - `CLOCK_REALTIME_ALARM`: linux-only diff --git a/tool/net/help.txt b/tool/net/help.txt index 92230e863..defc8c38d 100644 --- a/tool/net/help.txt +++ b/tool/net/help.txt @@ -3597,13 +3597,16 @@ UNIX MODULE - `CLOCK_REALTIME`: universally supported - `CLOCK_REALTIME_FAST`: ditto but faster on freebsd + - `CLOCK_REALTIME_PRECISE`: ditto but better on freebsd + - `CLOCK_REALTIME_COARSE`: : like `CLOCK_REALTIME_FAST` but needs Linux 2.6.32+ - `CLOCK_MONOTONIC`: universally supported - `CLOCK_MONOTONIC_FAST`: ditto but faster on freebsd - - `CLOCK_MONOTONIC_RAW`: nearly universally supported + - `CLOCK_MONOTONIC_PRECISE`: ditto but better on freebsd + - `CLOCK_MONOTONIC_COARSE`: : like `CLOCK_MONOTONIC_FAST` but needs Linux 2.6.32+ + - `CLOCK_MONOTONIC_RAW`: is actually monotonic but needs Linux 2.6.28+ - `CLOCK_PROCESS_CPUTIME_ID`: linux and bsd - `CLOCK_THREAD_CPUTIME_ID`: linux and bsd - - `CLOCK_REALTIME_COARSE`: : linux and openbsd - - `CLOCK_MONOTONIC_COARSE`: linux + - `CLOCK_MONOTONIC_COARSE`: linux, freebsd - `CLOCK_PROF`: linux and netbsd - `CLOCK_BOOTTIME`: linux and openbsd - `CLOCK_REALTIME_ALARM`: linux-only