diff --git a/build/bootstrap/cocmd.com b/build/bootstrap/cocmd.com index ddbaac8b6..ff1769d7b 100755 Binary files a/build/bootstrap/cocmd.com and b/build/bootstrap/cocmd.com differ diff --git a/examples/clock_getres.c b/examples/clock_getres.c index e45e97f44..a6fcb996d 100644 --- a/examples/clock_getres.c +++ b/examples/clock_getres.c @@ -38,10 +38,12 @@ int main(int argc, char *argv[]) { show(CLOCK_REALTIME); show(CLOCK_REALTIME_FAST); show(CLOCK_REALTIME_PRECISE); + show(CLOCK_REALTIME_COARSE); show(CLOCK_MONOTONIC); show(CLOCK_MONOTONIC_RAW); show(CLOCK_MONOTONIC_FAST); show(CLOCK_MONOTONIC_PRECISE); + show(CLOCK_MONOTONIC_COARSE); show(CLOCK_PROCESS_CPUTIME_ID); show(CLOCK_THREAD_CPUTIME_ID); show(CLOCK_PROF); diff --git a/examples/sleep.c b/examples/nanosleep.c similarity index 100% rename from examples/sleep.c rename to examples/nanosleep.c diff --git a/examples/nanosleep_test.c b/examples/nanosleep_test.c new file mode 100644 index 000000000..dbeea1c20 --- /dev/null +++ b/examples/nanosleep_test.c @@ -0,0 +1,43 @@ +#if 0 +/*─────────────────────────────────────────────────────────────────╗ +│ To the extent possible under law, Justine Tunney has waived │ +│ all copyright and related or neighboring rights to this file, │ +│ as it is written in the following disclaimers: │ +│ • http://unlicense.org/ │ +│ • http://creativecommons.org/publicdomain/zero/1.0/ │ +╚─────────────────────────────────────────────────────────────────*/ +#endif +#include "libc/assert.h" +#include "libc/calls/struct/timespec.h" +#include "libc/stdio/stdio.h" +#include "libc/sysv/consts/clock.h" + +/** + * @fileoverview shows how accurate clock_nanosleep() is + */ + +int main(int argc, char *argv[]) { + long i, ns; + struct timespec x, y, w; + timespec_sleep(timespec_fromnanos(0)); // warmup + + printf("\nrelative sleep\n"); + for (i = 0; i < 28; ++i) { + ns = 1l << i; + x = timespec_real(); + timespec_sleep(timespec_fromnanos(ns)); + y = timespec_real(); + printf("%,11ld ns sleep took %,ld ns\n", ns, + timespec_tonanos(timespec_sub(y, x))); + } + + printf("\nabsolute sleep\n"); + for (i = 0; i < 28; ++i) { + ns = 1l << i; + x = timespec_real(); + timespec_sleep_until(timespec_add(x, timespec_fromnanos(ns))); + y = timespec_real(); + printf("%,11ld ns sleep took %,ld ns\n", ns, + timespec_tonanos(timespec_sub(y, x))); + } +} diff --git a/libc/calls/clock_gettime.c b/libc/calls/clock_gettime.c index 64134ad11..6c19dd221 100644 --- a/libc/calls/clock_gettime.c +++ b/libc/calls/clock_gettime.c @@ -50,20 +50,23 @@ * sys_clock_gettime l: 220𝑐 71𝑛𝑠 * * @param clock can be one of: - * - `CLOCK_REALTIME`: universally supported - * - `CLOCK_REALTIME_FAST`: ditto but faster on freebsd - * - `CLOCK_MONOTONIC`: universally supported - * - `CLOCK_MONOTONIC_FAST`: ditto but faster on freebsd - * - `CLOCK_MONOTONIC_RAW`: nearly universally supported - * - `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_PROF`: linux and netbsd - * - `CLOCK_BOOTTIME`: linux and openbsd - * - `CLOCK_REALTIME_ALARM`: linux-only - * - `CLOCK_BOOTTIME_ALARM`: linux-only - * - `CLOCK_TAI`: linux-only + * - `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` w/ Linux 2.6.32+ + * - `CLOCK_MONOTONIC`: universally supported + * - `CLOCK_MONOTONIC_FAST`: ditto but faster on freebsd + * - `CLOCK_MONOTONIC_PRECISE`: ditto but better on freebsd + * - `CLOCK_MONOTONIC_COARSE`: : like `CLOCK_MONOTONIC_FAST` w/ 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_MONOTONIC_COARSE`: linux, freebsd + * - `CLOCK_PROF`: linux and netbsd + * - `CLOCK_BOOTTIME`: linux and openbsd + * - `CLOCK_REALTIME_ALARM`: linux-only + * - `CLOCK_BOOTTIME_ALARM`: linux-only + * - `CLOCK_TAI`: linux-only * @param ts is where the result is stored * @return 0 on success, or -1 w/ errno * @error EPERM if pledge() is in play without stdio promise diff --git a/libc/calls/clock_nanosleep.c b/libc/calls/clock_nanosleep.c index 892db647f..29d9d6218 100644 --- a/libc/calls/clock_nanosleep.c +++ b/libc/calls/clock_nanosleep.c @@ -16,7 +16,11 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" #include "libc/calls/asan.internal.h" +#include "libc/calls/blockcancel.internal.h" +#include "libc/calls/calls.h" +#include "libc/calls/clock_gettime.internal.h" #include "libc/calls/cp.internal.h" #include "libc/calls/state.internal.h" #include "libc/calls/struct/timespec.h" @@ -27,11 +31,161 @@ #include "libc/errno.h" #include "libc/intrin/describeflags.internal.h" #include "libc/intrin/strace.internal.h" +#include "libc/intrin/weaken.h" +#include "libc/macros.internal.h" +#include "libc/nt/ntdll.h" +#include "libc/str/str.h" #include "libc/sysv/consts/clock.h" #include "libc/sysv/consts/timer.h" #include "libc/sysv/errfuns.h" +#include "libc/thread/thread.h" #include "libc/thread/tls.h" +static int64_t g_nanosleep_latency; + +static errno_t sys_clock_nanosleep(int clock, int flags, + const struct timespec *req, + struct timespec *rem) { + int e, rc; + BEGIN_CANCELLATION_POINT; + e = errno; + STRACE("clock_nanosleep(%s, %s, %s, %s) → ...", DescribeClockName(clock), + DescribeSleepFlags(flags), DescribeTimespec(0, req), + DescribeTimespec(0, rem)); + 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 { + rc = sys_clock_nanosleep_nt(clock, flags, req, rem); + } + if (rc == -1) { + rc = errno; + errno = e; + } + END_CANCELLATION_POINT; + return rc; +} + +// determine sched_yield() vs. clock_nanosleep() threshold +// 1ns sys_clock_nanosleep() on Windows takes milliseconds :'( +// 1ns sys_clock_nanosleep() on Linux/FreeBSD takes tens of microseconds +// 1ns sys_clock_nanosleep() on OpenBSD/NetBSD takes tens of milliseconds D: +static struct timespec GetNanosleepLatency(void) { + errno_t rc; + int64_t nanos; + clock_gettime_f *cgt; + struct timespec x, y, w = {0, 1}; + if (!(nanos = g_nanosleep_latency)) { + BLOCK_CANCELLATIONS; + for (cgt = __clock_gettime_get(0);;) { + _npassert(!cgt(CLOCK_REALTIME_PRECISE, &x)); + rc = sys_clock_nanosleep(CLOCK_REALTIME, 0, &w, 0); + _npassert(!rc || rc == EINTR); + if (!rc) { + _npassert(!cgt(CLOCK_REALTIME_PRECISE, &y)); + nanos = timespec_tonanos(timespec_sub(y, x)); + g_nanosleep_latency = nanos; + break; + } + } + ALLOW_CANCELLATIONS; + } + return timespec_fromnanos(nanos); +} + +static errno_t CheckCancel(void) { + if (_weaken(pthread_testcancel_np)) { + return _weaken(pthread_testcancel_np)(); + } else { + return 0; + } +} + +static errno_t SpinNanosleep(int clock, int flags, const struct timespec *req, + struct timespec *rem) { + errno_t rc; + clock_gettime_f *cgt; + struct timespec now, start, elapsed; + if ((rc = CheckCancel())) { + if (rc == EINTR && !flags && rem) { + *rem = *req; + } + return rc; + } + cgt = __clock_gettime_get(0); + _npassert(!cgt(CLOCK_REALTIME, &start)); + for (;;) { + sched_yield(); + _npassert(!cgt(CLOCK_REALTIME, &now)); + if (flags & TIMER_ABSTIME) { + if (timespec_cmp(now, *req) >= 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