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
This commit is contained in:
Justine Tunney 2022-11-08 10:09:47 -08:00
parent aee50b1327
commit b407327972
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
47 changed files with 645 additions and 306 deletions

Binary file not shown.

View file

@ -38,10 +38,12 @@ int main(int argc, char *argv[]) {
show(CLOCK_REALTIME); show(CLOCK_REALTIME);
show(CLOCK_REALTIME_FAST); show(CLOCK_REALTIME_FAST);
show(CLOCK_REALTIME_PRECISE); show(CLOCK_REALTIME_PRECISE);
show(CLOCK_REALTIME_COARSE);
show(CLOCK_MONOTONIC); show(CLOCK_MONOTONIC);
show(CLOCK_MONOTONIC_RAW); show(CLOCK_MONOTONIC_RAW);
show(CLOCK_MONOTONIC_FAST); show(CLOCK_MONOTONIC_FAST);
show(CLOCK_MONOTONIC_PRECISE); show(CLOCK_MONOTONIC_PRECISE);
show(CLOCK_MONOTONIC_COARSE);
show(CLOCK_PROCESS_CPUTIME_ID); show(CLOCK_PROCESS_CPUTIME_ID);
show(CLOCK_THREAD_CPUTIME_ID); show(CLOCK_THREAD_CPUTIME_ID);
show(CLOCK_PROF); show(CLOCK_PROF);

43
examples/nanosleep_test.c Normal file
View file

@ -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)));
}
}

View file

@ -50,20 +50,23 @@
* sys_clock_gettime l: 220𝑐 71𝑛𝑠 * sys_clock_gettime l: 220𝑐 71𝑛𝑠
* *
* @param clock can be one of: * @param clock can be one of:
* - `CLOCK_REALTIME`: universally supported * - `CLOCK_REALTIME`: universally supported
* - `CLOCK_REALTIME_FAST`: ditto but faster on freebsd * - `CLOCK_REALTIME_FAST`: ditto but faster on freebsd
* - `CLOCK_MONOTONIC`: universally supported * - `CLOCK_REALTIME_PRECISE`: ditto but better on freebsd
* - `CLOCK_MONOTONIC_FAST`: ditto but faster on freebsd * - `CLOCK_REALTIME_COARSE`: : like `CLOCK_REALTIME_FAST` w/ Linux 2.6.32+
* - `CLOCK_MONOTONIC_RAW`: nearly universally supported * - `CLOCK_MONOTONIC`: universally supported
* - `CLOCK_PROCESS_CPUTIME_ID`: linux and bsd * - `CLOCK_MONOTONIC_FAST`: ditto but faster on freebsd
* - `CLOCK_THREAD_CPUTIME_ID`: linux and bsd * - `CLOCK_MONOTONIC_PRECISE`: ditto but better on freebsd
* - `CLOCK_REALTIME_COARSE`: : linux and openbsd * - `CLOCK_MONOTONIC_COARSE`: : like `CLOCK_MONOTONIC_FAST` w/ Linux 2.6.32+
* - `CLOCK_MONOTONIC_COARSE`: linux * - `CLOCK_MONOTONIC_RAW`: is actually monotonic but needs Linux 2.6.28+
* - `CLOCK_PROF`: linux and netbsd * - `CLOCK_PROCESS_CPUTIME_ID`: linux and bsd
* - `CLOCK_BOOTTIME`: linux and openbsd * - `CLOCK_THREAD_CPUTIME_ID`: linux and bsd
* - `CLOCK_REALTIME_ALARM`: linux-only * - `CLOCK_MONOTONIC_COARSE`: linux, freebsd
* - `CLOCK_BOOTTIME_ALARM`: linux-only * - `CLOCK_PROF`: linux and netbsd
* - `CLOCK_TAI`: linux-only * - `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 * @param ts is where the result is stored
* @return 0 on success, or -1 w/ errno * @return 0 on success, or -1 w/ errno
* @error EPERM if pledge() is in play without stdio promise * @error EPERM if pledge() is in play without stdio promise

View file

@ -16,7 +16,11 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/assert.h"
#include "libc/calls/asan.internal.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/cp.internal.h"
#include "libc/calls/state.internal.h" #include "libc/calls/state.internal.h"
#include "libc/calls/struct/timespec.h" #include "libc/calls/struct/timespec.h"
@ -27,11 +31,161 @@
#include "libc/errno.h" #include "libc/errno.h"
#include "libc/intrin/describeflags.internal.h" #include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/strace.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/clock.h"
#include "libc/sysv/consts/timer.h" #include "libc/sysv/consts/timer.h"
#include "libc/sysv/errfuns.h" #include "libc/sysv/errfuns.h"
#include "libc/thread/thread.h"
#include "libc/thread/tls.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. * Sleeps for particular amount of time.
* *
@ -88,36 +242,22 @@
*/ */
errno_t clock_nanosleep(int clock, int flags, const struct timespec *req, errno_t clock_nanosleep(int clock, int flags, const struct timespec *req,
struct timespec *rem) { struct timespec *rem) {
int rc, e = errno; int rc;
BEGIN_CANCELLATION_POINT; if (IsMetal()) {
rc = ENOSYS;
if (!req || (IsAsan() && (!__asan_is_valid_timespec(req) || } else if (!req || (IsAsan() && (!__asan_is_valid_timespec(req) ||
(rem && !__asan_is_valid_timespec(rem))))) { (rem && !__asan_is_valid_timespec(rem))))) {
rc = efault(); rc = EFAULT;
} else if (clock == 127 || // } else if (clock == 127 || //
(flags & ~TIMER_ABSTIME) || // (flags & ~TIMER_ABSTIME) || //
req->tv_sec < 0 || // req->tv_sec < 0 || //
!(0 <= req->tv_nsec && req->tv_nsec <= 999999999)) { !(0 <= req->tv_nsec && req->tv_nsec <= 999999999)) {
rc = einval(); rc = EINVAL;
} else if (IsLinux() || IsFreebsd() || IsNetbsd()) { } else if (ShouldUseSpinNanosleep(clock, flags, req)) {
rc = sys_clock_nanosleep(clock, flags, req, rem); rc = SpinNanosleep(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();
} else { } 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 SYSDEBUG
if (__tls_enabled && !(__get_tls()->tib_flags & TIB_FLAG_TIME_CRITICAL)) { if (__tls_enabled && !(__get_tls()->tib_flags & TIB_FLAG_TIME_CRITICAL)) {
STRACE("clock_nanosleep(%s, %s, %s, [%s]) → %s", DescribeClockName(clock), 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)); DescribeTimespec(rc, rem), DescribeErrnoResult(rc));
} }
#endif #endif
return rc; return rc;
} }

View file

@ -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"

View file

@ -45,10 +45,10 @@ static long double GetTimeSample(void) {
uint64_t tick1, tick2; uint64_t tick1, tick2;
long double time1, time2; long double time1, time2;
sched_yield(); sched_yield();
time1 = dtime(CLOCK_REALTIME_FAST); time1 = dtime(CLOCK_REALTIME_PRECISE);
tick1 = rdtsc(); tick1 = rdtsc();
nanosleep(&(struct timespec){0, 1000000}, NULL); nanosleep(&(struct timespec){0, 1000000}, NULL);
time2 = dtime(CLOCK_REALTIME_FAST); time2 = dtime(CLOCK_REALTIME_PRECISE);
tick2 = rdtsc(); tick2 = rdtsc();
return (time2 - time1) * 1e9 / MAX(1, tick2 - tick1); return (time2 - time1) * 1e9 / MAX(1, tick2 - tick1);
} }
@ -74,13 +74,13 @@ static long double MeasureNanosPerCycle(void) {
void RefreshTime(void) { void RefreshTime(void) {
struct Now now; struct Now now;
now.cpn = MeasureNanosPerCycle(); now.cpn = MeasureNanosPerCycle();
now.r0 = dtime(CLOCK_REALTIME_FAST); now.r0 = dtime(CLOCK_REALTIME_PRECISE);
now.k0 = rdtsc(); now.k0 = rdtsc();
memcpy(&g_now, &now, sizeof(now)); memcpy(&g_now, &now, sizeof(now));
} }
static long double nowl_sys(void) { static long double nowl_sys(void) {
return dtime(CLOCK_REALTIME_FAST); return dtime(CLOCK_REALTIME_PRECISE);
} }
static long double nowl_art(void) { static long double nowl_art(void) {
@ -92,7 +92,7 @@ static long double nowl_vdso(void) {
long double secs; long double secs;
struct timespec tv; struct timespec tv;
_unassert(__gettime); _unassert(__gettime);
__gettime(CLOCK_REALTIME_FAST, &tv); __gettime(CLOCK_REALTIME_PRECISE, &tv);
secs = tv.tv_nsec; secs = tv.tv_nsec;
secs *= 1 / 1e9L; secs *= 1 / 1e9L;
secs += tv.tv_sec; secs += tv.tv_sec;

View file

@ -6,13 +6,13 @@
COSMOPOLITAN_C_START_ COSMOPOLITAN_C_START_
/* clang-format off */ /* 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 __sys_utimensat(int, const char *, const struct timespec[2], int) hidden;
int __utimens(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_getres(int, struct timespec *) hidden;
int sys_clock_gettime(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_nt(int, struct timespec *) hidden;
int sys_clock_gettime_xnu(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_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_openbsd(int, int, const struct timespec *, struct timespec *) hidden;
int sys_clock_nanosleep_xnu(int, int, const struct timespec *, struct timespec *) hidden; int sys_clock_nanosleep_xnu(int, int, const struct timespec *, struct timespec *) hidden;

View file

@ -29,6 +29,6 @@
*/ */
struct timespec timespec_mono(void) { struct timespec timespec_mono(void) {
struct timespec ts; struct timespec ts;
_npassert(!clock_gettime(CLOCK_MONOTONIC_FAST, &ts)); _npassert(!clock_gettime(CLOCK_MONOTONIC, &ts));
return ts; return ts;
} }

View file

@ -31,6 +31,6 @@
*/ */
struct timespec timespec_real(void) { struct timespec timespec_real(void) {
struct timespec ts; struct timespec ts;
_npassert(!clock_gettime(CLOCK_REALTIME_FAST, &ts)); _npassert(!clock_gettime(CLOCK_REALTIME, &ts));
return ts; return ts;
} }

View file

@ -16,9 +16,9 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/intrin/strace.internal.h"
#include "libc/calls/syscall_support-nt.internal.h" #include "libc/calls/syscall_support-nt.internal.h"
#include "libc/intrin/describeflags.internal.h" #include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/nt/struct/securityattributes.h" #include "libc/nt/struct/securityattributes.h"
#include "libc/nt/thread.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 * @param dwStackSize may be 0 for default per executable
* @return thread handle, or 0 on failure * @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( textwindows int64_t CreateThread(
struct NtSecurityAttributes *lpThreadAttributes, size_t dwStackSize, struct NtSecurityAttributes *lpThreadAttributes, size_t dwStackSize,
@ -38,7 +38,6 @@ textwindows int64_t CreateThread(
int64_t hHandle; int64_t hHandle;
hHandle = __imp_CreateThread(lpThreadAttributes, dwStackSize, lpStartAddress, hHandle = __imp_CreateThread(lpThreadAttributes, dwStackSize, lpStartAddress,
lpParameter, dwCreationFlags, opt_lpThreadId); lpParameter, dwCreationFlags, opt_lpThreadId);
if (hHandle == -1) __winerr();
NTTRACE("CreateThread(%s, %'zu, %p, %p, %s, %p) → %ld% m", NTTRACE("CreateThread(%s, %'zu, %p, %p, %s, %p) → %ld% m",
DescribeNtSecurityAttributes(lpThreadAttributes), dwStackSize, DescribeNtSecurityAttributes(lpThreadAttributes), dwStackSize,
lpStartAddress, lpParameter, dwCreationFlags, opt_lpThreadId, lpStartAddress, lpParameter, dwCreationFlags, opt_lpThreadId,

View file

@ -32,14 +32,14 @@
.underrun .underrun
kClockNames: kClockNames:
.e CLOCK_REALTIME,"REALTIME" .e CLOCK_REALTIME,"REALTIME"
.e CLOCK_REALTIME_FAST,"REALTIME_FAST" .e CLOCK_REALTIME_FAST,"REALTIME_FAST" # order matters
.e CLOCK_REALTIME_PRECISE,"REALTIME_PRECISE" .e CLOCK_REALTIME_PRECISE,"REALTIME_PRECISE" # order matters
.e CLOCK_REALTIME_COARSE,"REALTIME_COARSE" # order matters
.e CLOCK_MONOTONIC,"MONOTONIC" .e CLOCK_MONOTONIC,"MONOTONIC"
.e CLOCK_MONOTONIC_FAST,"MONOTONIC_FAST" .e CLOCK_MONOTONIC_FAST,"MONOTONIC_FAST" # order matters
.e CLOCK_MONOTONIC_RAW,"MONOTONIC_RAW" .e CLOCK_MONOTONIC_RAW,"MONOTONIC_RAW" # order matters
.e CLOCK_MONOTONIC_PRECISE,"MONOTONIC_PRECISE" .e CLOCK_MONOTONIC_PRECISE,"MONOTONIC_PRECISE" # order matters
.e CLOCK_REALTIME_COARSE,"REALTIME_COARSE" .e CLOCK_MONOTONIC_COARSE,"MONOTONIC_COARSE" # order matters
.e CLOCK_MONOTONIC_COARSE,"MONOTONIC_COARSE"
.e CLOCK_PROCESS_CPUTIME_ID,"PROCESS_CPUTIME_ID" .e CLOCK_PROCESS_CPUTIME_ID,"PROCESS_CPUTIME_ID"
.e CLOCK_THREAD_CPUTIME_ID,"THREAD_CPUTIME_ID" .e CLOCK_THREAD_CPUTIME_ID,"THREAD_CPUTIME_ID"
.e CLOCK_TAI,"TAI" .e CLOCK_TAI,"TAI"

View file

@ -28,7 +28,7 @@
// @param r8 is tls // @param r8 is tls
// @param r9 is func(void*,int)→int // @param r9 is func(void*,int)→int
// @param 8(rsp) is arg // @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: sys_clone_linux:
push %rbp push %rbp
mov %rsp,%rbp mov %rsp,%rbp
@ -39,13 +39,9 @@ sys_clone_linux:
syscall syscall
test %rax,%rax test %rax,%rax
jz 2f jz 2f
cmp $-4095,%rax
jae 1f
0: pop %rbx 0: pop %rbx
pop %rbp pop %rbp
ret ret
1: call systemfive_error
jmp 0b
2: xor %ebp,%ebp # child thread 2: xor %ebp,%ebp # child thread
mov %rbx,%rdi # arg mov %rbx,%rdi # arg
mov (%r10),%esi # tid mov (%r10),%esi # tid

26
libc/runtime/clone-xnu.S Normal file
View file

@ -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

View file

@ -16,12 +16,14 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/assert.h"
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/calls/struct/ucontext-netbsd.internal.h" #include "libc/calls/struct/ucontext-netbsd.internal.h"
#include "libc/calls/syscall-sysv.internal.h" #include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/errno.h" #include "libc/errno.h"
#include "libc/intrin/asan.internal.h" #include "libc/intrin/asan.internal.h"
#include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/strace.internal.h" #include "libc/intrin/strace.internal.h"
#include "libc/limits.h" #include "libc/limits.h"
#include "libc/macros.internal.h" #include "libc/macros.internal.h"
@ -32,6 +34,7 @@
#include "libc/runtime/clone.internal.h" #include "libc/runtime/clone.internal.h"
#include "libc/runtime/internal.h" #include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
#include "libc/sock/internal.h"
#include "libc/stdalign.internal.h" #include "libc/stdalign.internal.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "libc/sysv/consts/clone.h" #include "libc/sysv/consts/clone.h"
@ -66,7 +69,6 @@ struct CloneArgs {
uint32_t utid; uint32_t utid;
int64_t tid64; int64_t tid64;
}; };
pthread_spinlock_t lock;
int *ptid; int *ptid;
int *ctid; int *ctid;
int *ztid; int *ztid;
@ -99,7 +101,6 @@ WinThreadEntry(int rdi, // rcx
struct CloneArgs *wt) { // r9 struct CloneArgs *wt) { // r9
int rc; int rc;
if (wt->tls) __set_tls_win32(wt->tls); if (wt->tls) __set_tls_win32(wt->tls);
*wt->ptid = wt->tid;
*wt->ctid = wt->tid; *wt->ctid = wt->tid;
rc = WinThreadLaunch(wt->arg, wt->tid, wt->func, (intptr_t)wt & -16); 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 // we can now clear ctid directly since we're no longer using our own
@ -112,15 +113,14 @@ WinThreadEntry(int rdi, // rcx
notpossible; notpossible;
} }
static textwindows int CloneWindows(int (*func)(void *, int), char *stk, static textwindows errno_t CloneWindows(int (*func)(void *, int), char *stk,
size_t stksz, int flags, void *arg, size_t stksz, int flags, void *arg,
void *tls, int *ptid, int *ctid) { void *tls, int *ptid, int *ctid) {
int64_t h; int64_t h;
struct CloneArgs *wt; struct CloneArgs *wt;
wt = (struct CloneArgs *)(((intptr_t)(stk + stksz) - wt = (struct CloneArgs *)(((intptr_t)(stk + stksz) -
sizeof(struct CloneArgs)) & sizeof(struct CloneArgs)) &
-alignof(struct CloneArgs)); -alignof(struct CloneArgs));
wt->ptid = flags & CLONE_PARENT_SETTID ? ptid : &wt->tid;
wt->ctid = flags & CLONE_CHILD_SETTID ? ctid : &wt->tid; wt->ctid = flags & CLONE_CHILD_SETTID ? ctid : &wt->tid;
wt->ztid = flags & CLONE_CHILD_CLEARTID ? ctid : &wt->tid; wt->ztid = flags & CLONE_CHILD_CLEARTID ? ctid : &wt->tid;
wt->func = func; wt->func = func;
@ -128,9 +128,12 @@ static textwindows int CloneWindows(int (*func)(void *, int), char *stk,
wt->tls = flags & CLONE_SETTLS ? tls : 0; wt->tls = flags & CLONE_SETTLS ? tls : 0;
if ((h = CreateThread(0, 4096, (void *)WinThreadEntry, wt, 0, &wt->utid))) { if ((h = CreateThread(0, 4096, (void *)WinThreadEntry, wt, 0, &wt->utid))) {
CloseHandle(h); CloseHandle(h);
return wt->tid; if (flags & CLONE_PARENT_SETTID) {
*ptid = wt->tid;
}
return 0;
} else { } else {
return -1; return __dos2errno(GetLastError());
} }
} }
@ -161,9 +164,8 @@ XnuThreadMain(void *pthread, // rdi
unsigned xnuflags) { // r9 unsigned xnuflags) { // r9
int ax; int ax;
wt->tid = tid; wt->tid = tid;
*wt->ptid = tid;
*wt->ctid = tid; *wt->ctid = tid;
pthread_spin_unlock(&wt->lock); *wt->ptid = tid;
if (wt->tls) { if (wt->tls) {
// XNU uses the same 0x30 offset as the WIN32 TIB x64. They told the // XNU uses the same 0x30 offset as the WIN32 TIB x64. They told the
@ -192,23 +194,16 @@ XnuThreadMain(void *pthread, // rdi
notpossible; notpossible;
} }
static int CloneXnu(int (*fn)(void *), char *stk, size_t stksz, int flags, static errno_t CloneXnu(int (*fn)(void *), char *stk, size_t stksz, int flags,
void *arg, void *tls, int *ptid, int *ctid) { void *arg, void *tls, int *ptid, int *ctid) {
int rc; int rc;
bool failed; bool failed;
static bool once; static bool once;
static int broken;
struct CloneArgs *wt; struct CloneArgs *wt;
if (!once) { if (!once) {
if (sys_bsdthread_register(XnuThreadThunk, 0, 0, 0, 0, 0, 0) == -1) { _npassert(sys_bsdthread_register(XnuThreadThunk, 0, 0, 0, 0, 0, 0) != -1);
broken = errno;
}
once = true; once = true;
} }
if (broken) {
errno = broken;
return -1;
}
wt = (struct CloneArgs *)(((intptr_t)(stk + stksz) - wt = (struct CloneArgs *)(((intptr_t)(stk + stksz) -
sizeof(struct CloneArgs)) & sizeof(struct CloneArgs)) &
-alignof(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->ctid = flags & CLONE_CHILD_SETTID ? ctid : &wt->tid;
wt->ztid = flags & CLONE_CHILD_CLEARTID ? ctid : &wt->tid; wt->ztid = flags & CLONE_CHILD_CLEARTID ? ctid : &wt->tid;
wt->tls = flags & CLONE_SETTLS ? tls : 0; wt->tls = flags & CLONE_SETTLS ? tls : 0;
wt->lock._lock = 1; return sys_clone_xnu(fn, arg, wt, 0, PTHREAD_START_CUSTOM_XNU);
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;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -231,7 +219,6 @@ static int CloneXnu(int (*fn)(void *), char *stk, size_t stksz, int flags,
static wontreturn void FreebsdThreadMain(void *p) { static wontreturn void FreebsdThreadMain(void *p) {
struct CloneArgs *wt = p; struct CloneArgs *wt = p;
*wt->ptid = wt->tid;
*wt->ctid = wt->tid; *wt->ctid = wt->tid;
wt->func(wt->arg, wt->tid); wt->func(wt->arg, wt->tid);
// we no longer use the stack after this point // we no longer use the stack after this point
@ -249,8 +236,9 @@ static wontreturn void FreebsdThreadMain(void *p) {
notpossible; notpossible;
} }
static int CloneFreebsd(int (*func)(void *, int), char *stk, size_t stksz, static errno_t CloneFreebsd(int (*func)(void *, int), char *stk, size_t stksz,
int flags, void *arg, void *tls, int *ptid, int *ctid) { int flags, void *arg, void *tls, int *ptid,
int *ctid) {
int ax; int ax;
bool failed; bool failed;
int64_t tid; 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) - wt = (struct CloneArgs *)(((intptr_t)(stk + stksz) -
sizeof(struct CloneArgs)) & sizeof(struct CloneArgs)) &
-alignof(struct CloneArgs)); -alignof(struct CloneArgs));
wt->ptid = flags & CLONE_PARENT_SETTID ? ptid : &wt->tid;
wt->ctid = flags & CLONE_CHILD_SETTID ? ctid : &wt->tid; wt->ctid = flags & CLONE_CHILD_SETTID ? ctid : &wt->tid;
wt->ztid = flags & CLONE_CHILD_CLEARTID ? ctid : &wt->tid; wt->ztid = flags & CLONE_CHILD_CLEARTID ? ctid : &wt->tid;
wt->tls = tls; wt->tls = tls;
@ -278,11 +265,9 @@ static int CloneFreebsd(int (*func)(void *, int), char *stk, size_t stksz,
: CFLAG_CONSTRAINT(failed), "=a"(ax) : CFLAG_CONSTRAINT(failed), "=a"(ax)
: "1"(__NR_thr_new), "D"(&params), "S"(sizeof(params)) : "1"(__NR_thr_new), "D"(&params), "S"(sizeof(params))
: "rcx", "rdx", "r8", "r9", "r10", "r11", "memory"); : "rcx", "rdx", "r8", "r9", "r10", "r11", "memory");
if (failed) { if (failed) return ax;
errno = ax; if (flags & CLONE_PARENT_SETTID) *ptid = tid;
tid = -1; return 0;
}
return tid;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -292,7 +277,6 @@ static int CloneFreebsd(int (*func)(void *, int), char *stk, size_t stksz,
// 1. __asan_handle_no_return wipes stack [todo?] // 1. __asan_handle_no_return wipes stack [todo?]
noasan static wontreturn void OpenbsdThreadMain(void *p) { noasan static wontreturn void OpenbsdThreadMain(void *p) {
struct CloneArgs *wt = p; struct CloneArgs *wt = p;
*wt->ptid = wt->tid;
*wt->ctid = wt->tid; *wt->ctid = wt->tid;
wt->func(wt->arg, wt->tid); wt->func(wt->arg, wt->tid);
asm volatile("mov\t%2,%%rsp\n\t" // so syscall can validate stack exists 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; notpossible;
} }
static int CloneOpenbsd(int (*func)(void *, int), char *stk, size_t stksz, static errno_t CloneOpenbsd(int (*func)(void *, int), char *stk, size_t stksz,
int flags, void *arg, void *tls, int *ptid, int *ctid) { int flags, void *arg, void *tls, int *ptid,
int tid; int *ctid) {
int rc;
intptr_t sp; intptr_t sp;
struct __tfork *tf; struct __tfork *tf;
struct CloneArgs *wt; struct CloneArgs *wt;
@ -321,7 +306,6 @@ static int CloneOpenbsd(int (*func)(void *, int), char *stk, size_t stksz,
sp -= sizeof(struct CloneArgs); sp -= sizeof(struct CloneArgs);
sp &= -MAX(16, alignof(struct CloneArgs)); sp &= -MAX(16, alignof(struct CloneArgs));
wt = (struct CloneArgs *)sp; wt = (struct CloneArgs *)sp;
wt->ptid = flags & CLONE_PARENT_SETTID ? ptid : &wt->tid;
wt->ctid = flags & CLONE_CHILD_SETTID ? ctid : &wt->tid; wt->ctid = flags & CLONE_CHILD_SETTID ? ctid : &wt->tid;
wt->ztid = flags & CLONE_CHILD_CLEARTID ? ctid : &wt->tid; wt->ztid = flags & CLONE_CHILD_CLEARTID ? ctid : &wt->tid;
wt->arg = arg; 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_stack = (char *)wt - 8;
tf->tf_tcb = flags & CLONE_SETTLS ? tls : 0; tf->tf_tcb = flags & CLONE_SETTLS ? tls : 0;
tf->tf_tid = &wt->tid; tf->tf_tid = &wt->tid;
if ((tid = __tfork_thread(tf, sizeof(*tf), OpenbsdThreadMain, wt)) < 0) { if ((rc = __tfork_thread(tf, sizeof(*tf), OpenbsdThreadMain, wt)) >= 0) {
errno = -tid; _npassert(rc);
tid = -1; 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 (*func)(void *, int), // rsi
int *tid, // rdx int *tid, // rdx
int *ctid, // rcx int *ctid, // rcx
int *ztid, // r8 int *ztid) { // r9
int *ptid) { // r9
int ax, dx; int ax, dx;
// TODO(jart): Why are we seeing flakes where *tid is zero? // TODO(jart): Why are we seeing flakes where *tid is zero?
// ax = *tid; // ax = *tid;
@ -370,7 +357,6 @@ static int CloneNetbsd(int (*func)(void *, int), char *stk, size_t stksz,
int ax, *tid; int ax, *tid;
intptr_t dx, sp; intptr_t dx, sp;
static bool once; static bool once;
static int broken;
struct ucontext_netbsd *ctx; struct ucontext_netbsd *ctx;
static struct ucontext_netbsd netbsd_clone_template; 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) : CFLAG_CONSTRAINT(failed), "=a"(ax)
: "1"(__NR_getcontext_netbsd), "D"(&netbsd_clone_template) : "1"(__NR_getcontext_netbsd), "D"(&netbsd_clone_template)
: "rcx", "rdx", "r8", "r9", "r10", "r11", "memory"); : "rcx", "rdx", "r8", "r9", "r10", "r11", "memory");
if (failed) { _npassert(!failed);
broken = ax;
}
once = true; once = true;
} }
if (broken) {
errno = broken;
return -1;
}
sp = (intptr_t)(stk + stksz); sp = (intptr_t)(stk + stksz);
// allocate memory for tid // 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.rdx = (intptr_t)tid;
ctx->uc_mcontext.rcx = (intptr_t)(flags & CLONE_CHILD_SETTID ? ctid : 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.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_flags |= _UC_STACK;
ctx->uc_stack.ss_sp = stk; ctx->uc_stack.ss_sp = stk;
ctx->uc_stack.ss_size = stksz; 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) : "1"(__NR__lwp_create), "D"(ctx), "S"(LWP_DETACHED), "2"(tid)
: "rcx", "r8", "r9", "r10", "r11", "memory"); : "rcx", "r8", "r9", "r10", "r11", "memory");
if (!failed) { if (!failed) {
return *tid; _npassert(*tid);
if (flags & CLONE_PARENT_SETTID) {
*ptid = *tid;
}
return 0;
} else { } else {
errno = ax; return ax;
return -1;
} }
} }
@ -454,8 +436,9 @@ int sys_clone_linux(int flags, // rdi
void *func, // r9 void *func, // r9
void *arg); // 8(rsp) 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 flags, void *arg, void *tls, int *ptid, int *ctid) {
int rc;
long sp; long sp;
sp = (intptr_t)(stk + stksz); sp = (intptr_t)(stk + stksz);
if (~flags & CLONE_CHILD_SETTID) { 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; ctid = (int *)sp;
} }
sp = sp & -16; // align the stack 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; } * int worker(void *arg) { return 0; }
* struct CosmoTib tib = {.tib_self = &tib, .tib_tid = -1}; * struct CosmoTib tib = {.tib_self = &tib, .tib_tid = -1};
* atomic_int tid;
* char *stk = _mapstack(); * char *stk = _mapstack();
* tid = clone(worker, stk, GetStackSize() - 16, * clone(worker, stk, GetStackSize() - 16,
* CLONE_VM | CLONE_THREAD | CLONE_FS | CLONE_FILES | * CLONE_VM | CLONE_THREAD | CLONE_FS | CLONE_FILES |
* CLONE_SIGHAND | CLONE_CHILD_SETTID | * CLONE_SIGHAND | CLONE_PARENT_SETTID | CLONE_CHILD_SETTID |
* CLONE_CHILD_CLEARTID | CLONE_SETTLS, * CLONE_CHILD_CLEARTID | CLONE_SETTLS,
* arg, 0, &tib, &tib.tib_tid); * arg, &tid, &tib, &tib.tib_tid);
* // ... * while (atomic_load(&tid) == 0) sched_yield();
* while (atomic_load(&tib.tib_tid)) 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); * _freestack(stk);
* *
* Threads are created in a detached manner. They currently can't be * 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 * Your `flags` may also optionally also additionally bitwise-OR any
* combination of the following additional flags: * 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 * - `CLONE_CHILD_SETTID` must be specified if you intend to set the
* `ctid` argument, which is guaranteed to be updated with the * `ctid` argument, which will updated with the child tid once the
* child tid before `func` is called, however we CAN NOT guarantee * child has started.
* this will happen BEFORE clone() returns *
* - `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 * - `CLONE_CHILD_CLEARTID` causes `*ctid = 0` upon child thread
* termination. This is used to implement join so that the parent * 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 * this parameter is ignored if `CLONE_SETTLS` is not set
* @param ctid lets the child receive its thread id without having to * @param ctid lets the child receive its thread id without having to
* call gettid() and is ignored if `CLONE_CHILD_SETTID` isn't set * 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 * @threadsafe
*/ */
int clone(void *func, void *stk, size_t stksz, int flags, void *arg, int *ptid, errno_t clone(void *func, void *stk, size_t stksz, int flags, void *arg,
void *tls, void *ctid) { void *ptid, void *tls, void *ctid) {
int rc; int rc;
if (flags & CLONE_THREAD) { 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) { if (!func) {
rc = einval(); rc = EINVAL;
} else if (!IsTiny() && } else if (!IsTiny() &&
((flags & CLONE_VM) && (stksz < PAGESIZE || (stksz & 15)))) { ((flags & CLONE_VM) && (stksz < PAGESIZE || (stksz & 15)))) {
rc = einval(); rc = EINVAL;
} else if (IsAsan() && } else if (IsAsan() &&
(((flags & CLONE_SETTLS) && !__asan_is_valid(tls, 64)) || (((flags & CLONE_SETTLS) && !__asan_is_valid(tls, 64)) ||
((flags & CLONE_PARENT_SETTID) && ((flags & CLONE_PARENT_SETTID) &&
!__asan_is_valid(ptid, sizeof(*ptid))) || !__asan_is_valid(ptid, sizeof(int))) ||
((flags & CLONE_CHILD_SETTID) && ((flags & CLONE_CHILD_SETTID) &&
!__asan_is_valid(ctid, sizeof(int))))) { !__asan_is_valid(ctid, sizeof(int))))) {
rc = efault(); rc = EFAULT;
} else if (IsLinux()) { } else if (IsLinux()) {
rc = CloneLinux(func, stk, stksz, flags, arg, tls, ptid, ctid); rc = CloneLinux(func, stk, stksz, flags, arg, tls, ptid, ctid);
} else if (!IsTiny() && } 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_THREAD | CLONE_VM | CLONE_FS | CLONE_FILES |
CLONE_SIGHAND)) { CLONE_SIGHAND)) {
STRACE("clone flag unsupported on this platform"); STRACE("clone flag unsupported on this platform");
rc = einval(); rc = EINVAL;
} else if (IsXnu()) { } else if (IsXnu()) {
rc = CloneXnu(func, stk, stksz, flags, arg, tls, ptid, ctid); rc = CloneXnu(func, stk, stksz, flags, arg, tls, ptid, ctid);
} else if (IsFreebsd()) { } 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()) { } else if (IsWindows()) {
rc = CloneWindows(func, stk, stksz, flags, arg, tls, ptid, ctid); rc = CloneWindows(func, stk, stksz, flags, arg, tls, ptid, ctid);
} else { } else {
rc = enosys(); rc = ENOSYS;
} }
// TODO(jart): do we need it? STRACE("clone(%t, %p, %'zu, %#x, %p, %p, %p, %p) → %s", func, stk, stksz,
if (rc != -1 && (flags & CLONE_PARENT_SETTID)) { flags, arg, ptid, tls, ctid, DescribeErrnoResult(rc));
*ptid = rc;
}
STRACE("clone(%t, %p, %'zu, %#x, %p, %p, %p, %p) → %d% m", func, stk, stksz,
flags, arg, ptid, tls, ctid, rc);
return rc; return rc;
} }

View file

@ -4,7 +4,7 @@
#if !(__ASSEMBLER__ + __LINKER__ + 0) #if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_ 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_ COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -117,8 +117,8 @@ void __enable_tls(void) {
tid = sys_gettid(); tid = sys_gettid();
} }
atomic_store_explicit(&tib->tib_tid, tid, memory_order_relaxed); atomic_store_explicit(&tib->tib_tid, tid, memory_order_relaxed);
_pthread_main.ptid = tid;
_pthread_main.tib = tib; _pthread_main.tib = tib;
_pthread_main.tid = tid;
_pthread_main.flags = PT_MAINTHREAD; _pthread_main.flags = PT_MAINTHREAD;
__repmovsb(tls, _tdata_start, _TLDZ); __repmovsb(tls, _tdata_start, _TLDZ);

View file

@ -34,7 +34,9 @@
#include "libc/thread/tls.h" #include "libc/thread/tls.h"
int _fork(uint32_t dwCreationFlags) { int _fork(uint32_t dwCreationFlags) {
int ax, dx, parent; struct CosmoTib *tib;
struct PosixThread *pt;
int ax, dx, tid, parent;
BLOCK_SIGNALS; BLOCK_SIGNALS;
if (__threaded && _weaken(_pthread_onfork_prepare)) { if (__threaded && _weaken(_pthread_onfork_prepare)) {
_weaken(_pthread_onfork_prepare)(); _weaken(_pthread_onfork_prepare)();
@ -53,9 +55,12 @@ int _fork(uint32_t dwCreationFlags) {
parent = __pid; parent = __pid;
__pid = dx; __pid = dx;
if (__tls_enabled) { if (__tls_enabled) {
atomic_store_explicit(&__get_tls()->tib_tid, tib = __get_tls();
IsLinux() ? dx : sys_gettid(), tid = IsLinux() ? dx : sys_gettid();
memory_order_relaxed); 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)) { if (__threaded && _weaken(_pthread_onfork_child)) {
_weaken(_pthread_onfork_child)(); _weaken(_pthread_onfork_child)();

View file

@ -0,0 +1,2 @@
.include "o/libc/sysv/macros.internal.inc"
.scall __sys_clock_nanosleep,0x9ddfff8f4ffff8e6,globl,hidden

View file

@ -1,2 +0,0 @@
.include "o/libc/sysv/macros.internal.inc"
.scall sys_bsdthread_create,0xfffffffff2168fff,globl,hidden

View file

@ -1,2 +0,0 @@
.include "o/libc/sysv/macros.internal.inc"
.scall sys_clock_nanosleep,0x9ddfff8f4ffff8e6,globl,hidden

View file

@ -71,7 +71,7 @@ scall sys_dup 0x0290290292029020 globl hidden
scall sys_dup2 0x05a05a05a205a021 globl hidden scall sys_dup2 0x05a05a05a205a021 globl hidden
scall sys_pause 0xfffffffffffff022 globl hidden scall sys_pause 0xfffffffffffff022 globl hidden
scall sys_nanosleep 0x9ae85b8f0ffff823 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_getitimer 0x1aa0460562056024 globl hidden
scall sys_setitimer 0x1a90450532053026 globl hidden scall sys_setitimer 0x1a90450532053026 globl hidden
scall sys_alarm 0xfffffffffffff025 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_join 0xfffffffff21adfff globl
#scall audit_session_port 0xfffffffff21b0fff globl #scall audit_session_port 0xfffffffff21b0fff globl
#scall audit_session_self 0xfffffffff21acfff 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 bsdthread_ctl 0xfffffffff21defff globl
scall sys_bsdthread_register 0xfffffffff216efff globl hidden scall sys_bsdthread_register 0xfffffffff216efff globl hidden
#scall bsdthread_terminate 0xfffffffff2169fff globl #scall bsdthread_terminate 0xfffffffff2169fff globl

View file

@ -66,7 +66,7 @@ struct PosixThread {
int flags; // 0x00: see PT_* constants int flags; // 0x00: see PT_* constants
_Atomic(int) cancelled; // 0x04: thread has bad beliefs _Atomic(int) cancelled; // 0x04: thread has bad beliefs
_Atomic(enum PosixThreadStatus) status; _Atomic(enum PosixThreadStatus) status;
int tid; // clone parent tid _Atomic(int) ptid; // transitions 0 → tid
void *(*start)(void *); // creation callback void *(*start)(void *); // creation callback
void *arg; // start's parameter void *arg; // start's parameter
void *rc; // start's return value void *rc; // start's return value

View file

@ -73,7 +73,6 @@ void _pthread_onfork_child(void) {
tib = __get_tls(); tib = __get_tls();
pt = (struct PosixThread *)tib->tib_pthread; pt = (struct PosixThread *)tib->tib_pthread;
atomic_store_explicit(&pt->cancelled, false, memory_order_relaxed); 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_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&__mmi_lock_obj, &attr); pthread_mutex_init(&__mmi_lock_obj, &attr);

View file

@ -87,7 +87,7 @@ static void ListenForSigThr(void) {
* *
* By default, pthread_cancel() can only take effect when a thread * By default, pthread_cancel() can only take effect when a thread
* reaches a cancellation point. Such functions are documented with * 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 * underlying system call is issued. If the system call is issued and
* blocks, then pthread_cancel() will interrupt the operation in which * blocks, then pthread_cancel() will interrupt the operation in which
* case the syscall wrapper will check the cancelled state a second * case the syscall wrapper will check the cancelled state a second
@ -277,18 +277,20 @@ errno_t pthread_cancel(pthread_t thread) {
} }
return 0; return 0;
} }
if (IsWindows()) return 0; // no true cancellations on Windows yet if (!(rc = pthread_getunique_np(thread, &tid))) {
tid = atomic_load_explicit(&pt->tib->tib_tid, memory_order_acquire); if (!IsWindows()) {
if (tid <= 0) return 0; // -1 means still starting, 0 means exited e = errno;
e = errno; if (!__tkill(tid, SIGTHR, pt->tib)) {
if (!__tkill(tid, SIGTHR, pt->tib)) { rc = 0;
return 0; } else {
} else { rc = errno;
rc = errno; errno = e;
errno = e; }
return rc; } else {
rc = 0;
}
} }
return 0; return rc;
} }
/** /**

View file

@ -19,38 +19,27 @@
#include "libc/assert.h" #include "libc/assert.h"
#include "libc/calls/blocksigs.internal.h" #include "libc/calls/blocksigs.internal.h"
#include "libc/calls/calls.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/sigaltstack.h"
#include "libc/calls/struct/sigset.h"
#include "libc/calls/syscall-sysv.internal.h" #include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/errno.h" #include "libc/errno.h"
#include "libc/intrin/asan.internal.h" #include "libc/intrin/asan.internal.h"
#include "libc/intrin/atomic.h" #include "libc/intrin/atomic.h"
#include "libc/intrin/bits.h" #include "libc/intrin/bits.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/weaken.h"
#include "libc/log/internal.h" #include "libc/log/internal.h"
#include "libc/macros.internal.h" #include "libc/macros.internal.h"
#include "libc/mem/mem.h" #include "libc/mem/mem.h"
#include "libc/nexgen32e/gc.internal.h"
#include "libc/runtime/clone.internal.h" #include "libc/runtime/clone.internal.h"
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
#include "libc/runtime/stack.h" #include "libc/runtime/stack.h"
#include "libc/stdio/stdio.h"
#include "libc/sysv/consts/clone.h" #include "libc/sysv/consts/clone.h"
#include "libc/sysv/consts/map.h" #include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/prot.h" #include "libc/sysv/consts/prot.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/consts/ss.h" #include "libc/sysv/consts/ss.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/posixthread.internal.h" #include "libc/thread/posixthread.internal.h"
#include "libc/thread/spawn.h" #include "libc/thread/spawn.h"
#include "libc/thread/thread.h" #include "libc/thread/thread.h"
#include "libc/thread/tls.h"
#include "libc/thread/wait0.internal.h" #include "libc/thread/wait0.internal.h"
#include "third_party/dlmalloc/dlmalloc.h"
STATIC_YOINK("nsync_mu_lock"); STATIC_YOINK("nsync_mu_lock");
STATIC_YOINK("nsync_mu_unlock"); STATIC_YOINK("nsync_mu_unlock");
@ -254,15 +243,13 @@ static errno_t pthread_create_impl(pthread_t *thread,
// launch PosixThread(pt) in new thread // launch PosixThread(pt) in new thread
pt->sigmask = oldsigs; pt->sigmask = oldsigs;
if (clone(PosixThread, pt->attr.__stackaddr, if ((rc = clone(PosixThread, pt->attr.__stackaddr,
pt->attr.__stacksize - (IsOpenbsd() ? 16 : 0), pt->attr.__stacksize - (IsOpenbsd() ? 16 : 0),
CLONE_VM | CLONE_THREAD | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_VM | CLONE_THREAD | CLONE_FS | CLONE_FILES |
CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_SETTID | CLONE_SIGHAND | CLONE_SETTLS | CLONE_PARENT_SETTID |
CLONE_CHILD_CLEARTID, CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID,
pt, &pt->tid, pt->tib, &pt->tib->tib_tid) == -1) { pt, &pt->ptid, pt->tib, &pt->tib->tib_tid))) {
rc = errno;
_pthread_free(pt); _pthread_free(pt);
errno = e;
return rc; return rc;
} }

View file

@ -20,6 +20,7 @@
#include "libc/calls/struct/cpuset.h" #include "libc/calls/struct/cpuset.h"
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/errno.h" #include "libc/errno.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/describeflags.internal.h" #include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/strace.internal.h" #include "libc/intrin/strace.internal.h"
#include "libc/str/str.h" #include "libc/str/str.h"
@ -33,37 +34,40 @@
* @return 0 on success, or errno on error * @return 0 on success, or errno on error
* @raise EINVAL if `size` or `bitset` is invalid * @raise EINVAL if `size` or `bitset` is invalid
* @raise ENOSYS if not Linux, FreeBSD, or NetBSD * @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, errno_t pthread_getaffinity_np(pthread_t thread, size_t size,
cpu_set_t *bitset) { cpu_set_t *bitset) {
int tid, rc, e = errno; int e, rc, tid;
tid = ((struct PosixThread *)thread)->tid;
if (size != sizeof(cpu_set_t)) { if (!(rc = pthread_getunique_np(thread, &tid))) {
rc = einval(); e = errno;
} else if (IsWindows() || IsMetal() || IsOpenbsd()) { if (size != sizeof(cpu_set_t)) {
rc = enosys(); rc = einval();
} else if (IsFreebsd()) { } else if (IsWindows() || IsMetal() || IsOpenbsd()) {
if (!sys_sched_getaffinity_freebsd(CPU_LEVEL_WHICH, CPU_WHICH_TID, tid, 32, rc = enosys();
bitset)) { } else if (IsFreebsd()) {
rc = 32; 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 { } else {
rc = -1; rc = sys_sched_getaffinity(tid, size, bitset);
} }
} else if (IsNetbsd()) { if (rc > 0) {
if (!sys_sched_getaffinity_netbsd(tid, 0, 32, bitset)) { if (rc < size) {
rc = 32; bzero((char *)bitset + rc, size - rc);
} else { }
rc = -1; 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, STRACE("pthread_getaffinity_np(%d, %'zu, %p) → %s", tid, size, bitset,

View file

@ -23,6 +23,7 @@
#include "libc/errno.h" #include "libc/errno.h"
#include "libc/fmt/itoa.h" #include "libc/fmt/itoa.h"
#include "libc/intrin/asmflag.h" #include "libc/intrin/asmflag.h"
#include "libc/intrin/atomic.h"
#include "libc/macros.internal.h" #include "libc/macros.internal.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "libc/sysv/consts/o.h" #include "libc/sysv/consts/o.h"
@ -30,11 +31,12 @@
#include "libc/thread/posixthread.internal.h" #include "libc/thread/posixthread.internal.h"
static errno_t pthread_getname_impl(pthread_t thread, char *name, size_t size) { 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; if (!size) return 0;
bzero(name, size); bzero(name, size);
tid = ((struct PosixThread *)thread)->tid; e = errno;
if (IsLinux()) { if (IsLinux()) {
// TASK_COMM_LEN is 16 on Linux so we're just being paranoid. // TASK_COMM_LEN is 16 on Linux so we're just being paranoid.

View file

@ -16,12 +16,26 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/errno.h"
#include "libc/intrin/atomic.h"
#include "libc/thread/posixthread.internal.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) { errno_t pthread_getunique_np(pthread_t thread, pthread_id_np_t *out_tid) {
struct PosixThread *pt = (struct PosixThread *)thread; int tid;
return pt->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;
}
}
} }

View file

@ -19,6 +19,7 @@
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/calls/syscall_support-sysv.internal.h" #include "libc/calls/syscall_support-sysv.internal.h"
#include "libc/errno.h" #include "libc/errno.h"
#include "libc/intrin/atomic.h"
#include "libc/thread/posixthread.internal.h" #include "libc/thread/posixthread.internal.h"
#include "libc/thread/thread.h" #include "libc/thread/thread.h"
@ -33,13 +34,17 @@
* @asyncsignalsafe * @asyncsignalsafe
*/ */
errno_t pthread_kill(pthread_t thread, int sig) { errno_t pthread_kill(pthread_t thread, int sig) {
int rc, e = errno; int e, rc, tid;
struct PosixThread *pt = (struct PosixThread *)thread; struct PosixThread *p;
if (!__tkill(pt->tid, sig, pt->tib)) { if (!(rc = pthread_getunique_np(thread, &tid))) {
rc = 0; e = errno;
} else { p = (struct PosixThread *)thread;
rc = errno; if (!__tkill(tid, sig, p->tib)) {
errno = e; rc = 0;
} else {
rc = errno;
errno = e;
}
} }
return rc; return rc;
} }

View file

@ -20,23 +20,29 @@
#include "libc/calls/struct/sched_param.h" #include "libc/calls/struct/sched_param.h"
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/errno.h" #include "libc/errno.h"
#include "libc/intrin/atomic.h"
#include "libc/sysv/errfuns.h" #include "libc/sysv/errfuns.h"
#include "libc/thread/posixthread.internal.h" #include "libc/thread/posixthread.internal.h"
errno_t _pthread_reschedule(struct PosixThread *pt) { errno_t _pthread_reschedule(struct PosixThread *pt) {
int rc, e = errno; int e, rc, tid;
int policy = pt->attr.__schedpolicy; int policy = pt->attr.__schedpolicy;
struct sched_param param = {pt->attr.__schedparam}; struct sched_param param = {pt->attr.__schedparam};
if (IsNetbsd()) { if (!(rc = pthread_getunique_np((pthread_t)pt, &tid))) {
rc = sys_sched_setparam_netbsd(0, pt->tid, policy, &param); e = errno;
} else if (IsLinux()) { if (IsNetbsd()) {
rc = sys_sched_setscheduler(pt->tid, policy, &param); rc = sys_sched_setparam_netbsd(0, tid, policy, &param);
} else if (IsFreebsd()) { } else if (IsLinux()) {
rc = _pthread_setschedparam_freebsd(pt->tid, policy, &param); rc = sys_sched_setscheduler(tid, policy, &param);
} else { } else if (IsFreebsd()) {
rc = enosys(); rc = _pthread_setschedparam_freebsd(tid, policy, &param);
} else {
rc = enosys();
}
if (rc == -1) {
rc = errno;
errno = e;
}
} }
rc = rc != -1 ? 0 : errno;
errno = e;
return rc; return rc;
} }

View file

@ -21,6 +21,7 @@
#include "libc/calls/syscall_support-nt.internal.h" #include "libc/calls/syscall_support-nt.internal.h"
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/errno.h" #include "libc/errno.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/describeflags.internal.h" #include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/strace.internal.h" #include "libc/intrin/strace.internal.h"
#include "libc/nt/enum/threadaccess.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, errno_t pthread_setaffinity_np(pthread_t thread, size_t size,
const cpu_set_t *bitset) { const cpu_set_t *bitset) {
int tid, rc, e = errno; int e, rc, tid;
tid = ((struct PosixThread *)thread)->tid; if (!(rc = pthread_getunique_np(thread, &tid))) {
if (size != sizeof(cpu_set_t)) { e = errno;
rc = einval(); if (size != sizeof(cpu_set_t)) {
} else if (IsWindows()) { rc = einval();
rc = sys_pthread_setaffinity_nt(tid, size, bitset); } else if (IsWindows()) {
} else if (IsFreebsd()) { rc = sys_pthread_setaffinity_nt(tid, size, bitset);
rc = sys_sched_setaffinity_freebsd(CPU_LEVEL_WHICH, CPU_WHICH_TID, tid, 32, } else if (IsFreebsd()) {
bitset); rc = sys_sched_setaffinity_freebsd(CPU_LEVEL_WHICH, CPU_WHICH_TID, tid,
} else if (IsNetbsd()) { 32, bitset);
rc = sys_sched_setaffinity_netbsd(tid, 0, 32, bitset); } else if (IsNetbsd()) {
} else { rc = sys_sched_setaffinity_netbsd(tid, 0, 32, bitset);
rc = sys_sched_setaffinity(tid, size, bitset); } else {
} rc = sys_sched_setaffinity(tid, size, bitset);
if (rc == -1) { }
rc = errno; if (rc == -1) {
errno = e; rc = errno;
errno = e;
}
} }
STRACE("pthread_setaffinity_np(%d, %'zu, %p) → %s", tid, size, bitset, STRACE("pthread_setaffinity_np(%d, %'zu, %p) → %s", tid, size, bitset,
DescribeErrnoResult(rc)); DescribeErrnoResult(rc));

View file

@ -24,6 +24,7 @@
#include "libc/fmt/itoa.h" #include "libc/fmt/itoa.h"
#include "libc/intrin/asan.internal.h" #include "libc/intrin/asan.internal.h"
#include "libc/intrin/asmflag.h" #include "libc/intrin/asmflag.h"
#include "libc/intrin/atomic.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "libc/sysv/consts/o.h" #include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/pr.h" #include "libc/sysv/consts/pr.h"
@ -31,12 +32,13 @@
static errno_t pthread_setname_impl(pthread_t thread, const char *name) { static errno_t pthread_setname_impl(pthread_t thread, const char *name) {
char path[128], *p; 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); len = strlen(name);
if (IsLinux()) { if (IsLinux()) {
e = errno;
if (tid == gettid()) { if (tid == gettid()) {
if (prctl(PR_SET_NAME, name) == -1) { if (prctl(PR_SET_NAME, name) == -1) {
rc = errno; rc = errno;

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/errno.h"
#include "libc/macros.internal.h" #include "libc/macros.internal.h"
#include "libc/mem/mem.h" #include "libc/mem/mem.h"
#include "libc/runtime/clone.internal.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 * @return 0 on success, or -1 w/ errno
*/ */
int _spawn(int fun(void *, int), void *arg, struct spawn *opt_out_thread) { int _spawn(int fun(void *, int), void *arg, struct spawn *opt_out_thread) {
errno_t rc;
struct spawn *th, ths; struct spawn *th, ths;
struct spawner *spawner; struct spawner *spawner;
__require_tls(); __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 = malloc(sizeof(struct spawner));
spawner->fun = fun; spawner->fun = fun;
spawner->arg = arg; spawner->arg = arg;
if (clone(Spawner, th->stk, GetStackSize() - 16 /* openbsd:stackbound */, rc = clone(Spawner, th->stk, GetStackSize() - 16 /* openbsd:stackbound */,
CLONE_VM | CLONE_THREAD | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_VM | CLONE_THREAD | CLONE_FS | CLONE_FILES | CLONE_SIGHAND |
CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_SETTID | CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_SETTID |
CLONE_CHILD_CLEARTID, CLONE_CHILD_CLEARTID,
spawner, &th->ptid, th->tib, &th->tib->tib_tid) == -1) { spawner, &th->ptid, th->tib, &th->tib->tib_tid);
if (rc) {
errno = rc;
_freestack(th->stk); _freestack(th->stk);
free(th->tls); free(th->tls);
return -1; return -1;

View file

@ -115,7 +115,7 @@ int pthread_testcancel_np(void);
void pthread_exit(void *) wontreturn; void pthread_exit(void *) wontreturn;
pthread_t pthread_self(void) pureconst; pthread_t pthread_self(void) pureconst;
pthread_id_np_t pthread_getthreadid_np(void); 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_setname_np(pthread_t, const char *);
int pthread_getname_np(pthread_t, char *, size_t); int pthread_getname_np(pthread_t, char *, size_t);
int pthread_getattr_np(pthread_t, pthread_attr_t *); int pthread_getattr_np(pthread_t, pthread_attr_t *);

View file

@ -24,7 +24,7 @@ struct CosmoTib {
intptr_t tib_locale; /* 0x20 */ intptr_t tib_locale; /* 0x20 */
intptr_t tib_pthread; /* 0x28 */ intptr_t tib_pthread; /* 0x28 */
struct CosmoTib *tib_self2; /* 0x30 */ 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 */ int32_t tib_errno; /* 0x3c */
uint64_t tib_flags; /* 0x40 */ uint64_t tib_flags; /* 0x40 */
void *tib_nsync; void *tib_nsync;

View file

@ -8,8 +8,8 @@ COSMOPOLITAN_C_START_
* See darwin-libpthread/kern/kern_support.c * See darwin-libpthread/kern/kern_support.c
*/ */
int sys_bsdthread_create(void *func, void *func_arg, void *stack, void *pthread, errno_t sys_clone_xnu(void *func, void *func_arg, void *stack, void *pthread,
uint32_t flags); uint32_t flags);
int sys_bsdthread_register( int sys_bsdthread_register(
void (*threadstart)(void *pthread, int machport, void *(*func)(void *), void (*threadstart)(void *pthread, int machport, void *(*func)(void *),
void *arg, intptr_t *, unsigned), void *arg, intptr_t *, unsigned),

View file

@ -65,7 +65,6 @@
* @threadsafe * @threadsafe
*/ */
char *iso8601us(char p[hasatleast 27], struct tm *tm, long ns) { char *iso8601us(char p[hasatleast 27], struct tm *tm, long ns) {
int x;
p = iso8601(p, tm); p = iso8601(p, tm);
_unassert(0 <= ns && ns < 1000000000); _unassert(0 <= ns && ns < 1000000000);
*p++ = '.'; *p++ = '.';

View file

@ -22,6 +22,7 @@
#include "libc/calls/struct/timespec.h" #include "libc/calls/struct/timespec.h"
#include "libc/errno.h" #include "libc/errno.h"
#include "libc/intrin/describeflags.internal.h" #include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/sysv/consts/clock.h" #include "libc/sysv/consts/clock.h"
#include "libc/sysv/consts/itimer.h" #include "libc/sysv/consts/itimer.h"
#include "libc/sysv/consts/sa.h" #include "libc/sysv/consts/sa.h"
@ -31,6 +32,7 @@
void OnAlrm(int sig) { void OnAlrm(int sig) {
// do nothing // do nothing
STRACE("OnAlrm()");
} }
TEST(nanosleep, testFault) { TEST(nanosleep, testFault) {
@ -54,7 +56,7 @@ TEST(nanosleep, testInterrupt_remIsUpdated) {
.sa_flags = SA_RESETHAND, .sa_flags = SA_RESETHAND,
}; };
ASSERT_SYS(0, 0, sigaction(SIGALRM, &sa, 0)); 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)); ASSERT_SYS(0, 0, setitimer(ITIMER_REAL, &it, 0));
struct timespec ts = {500, 0}; struct timespec ts = {500, 0};
ASSERT_SYS(EINTR, -1, nanosleep(&ts, &ts)); ASSERT_SYS(EINTR, -1, nanosleep(&ts, &ts));
@ -75,7 +77,7 @@ TEST(clock_nanosleep, testInterrupt_remIsUpdated) {
.sa_flags = SA_RESETHAND, .sa_flags = SA_RESETHAND,
}; };
ASSERT_SYS(0, 0, sigaction(SIGALRM, &sa, 0)); 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)); ASSERT_SYS(0, 0, setitimer(ITIMER_REAL, &it, 0));
struct timespec ts = {500, 0}; struct timespec ts = {500, 0};
ASSERT_EQ(EINTR, clock_nanosleep(CLOCK_REALTIME, 0, &ts, &ts)); ASSERT_EQ(EINTR, clock_nanosleep(CLOCK_REALTIME, 0, &ts, &ts));

View file

@ -39,8 +39,8 @@ void *Worker(void *arg) {
TEST(tkill, test) { TEST(tkill, test) {
if (IsWindows()) return; // TODO(jart): fix me if (IsWindows()) return; // TODO(jart): fix me
int i;
void *res; void *res;
int i, tid;
pthread_t t; pthread_t t;
sigset_t ss, oldss; sigset_t ss, oldss;
sighandler_t oldsig; sighandler_t oldsig;
@ -49,7 +49,8 @@ TEST(tkill, test) {
oldsig = signal(SIGUSR1, OnSig); oldsig = signal(SIGUSR1, OnSig);
ASSERT_SYS(0, 0, sigprocmask(SIG_BLOCK, &ss, &oldss)); ASSERT_SYS(0, 0, sigprocmask(SIG_BLOCK, &ss, &oldss));
ASSERT_EQ(0, pthread_create(&t, 0, Worker, 0)); 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(0, pthread_join(t, &res));
ASSERT_EQ(SIGUSR1, (intptr_t)res); ASSERT_EQ(SIGUSR1, (intptr_t)res);
ASSERT_SYS(0, 0, sigprocmask(SIG_SETMASK, &oldss, 0)); ASSERT_SYS(0, 0, sigprocmask(SIG_SETMASK, &oldss, 0));

View file

@ -22,6 +22,7 @@
#include "libc/calls/struct/sigset.h" #include "libc/calls/struct/sigset.h"
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/errno.h" #include "libc/errno.h"
#include "libc/macros.internal.h"
#include "libc/mem/gc.h" #include "libc/mem/gc.h"
#include "libc/mem/mem.h" #include "libc/mem/mem.h"
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
@ -50,9 +51,9 @@ void *TortureWorker(void *arg) {
ready = true; ready = true;
while (!done) { while (!done) {
if (!IsWindows()) pthread_kill(parent, SIGUSR1); if (!IsWindows()) pthread_kill(parent, SIGUSR1);
usleep(3); usleep(1);
if (!IsWindows()) pthread_kill(parent, SIGUSR2); if (!IsWindows()) pthread_kill(parent, SIGUSR2);
usleep(3); usleep(1);
} }
return 0; return 0;
} }
@ -61,7 +62,7 @@ TEST(getentropy, test) {
pthread_t child; pthread_t child;
double e, w = 7.7; double e, w = 7.7;
struct sigaction sa; struct sigaction sa;
int i, j, k, n = 999; int i, j, k, m, n = 999;
char *buf = _gc(calloc(1, n)); char *buf = _gc(calloc(1, n));
sa.sa_flags = 0; sa.sa_flags = 0;
sa.sa_handler = OnSig; sa.sa_handler = OnSig;
@ -71,11 +72,13 @@ TEST(getentropy, test) {
parent = pthread_self(); parent = pthread_self();
ASSERT_EQ(0, pthread_create(&child, 0, TortureWorker, 0)); ASSERT_EQ(0, pthread_create(&child, 0, TortureWorker, 0));
while (!ready) pthread_yield(); while (!ready) pthread_yield();
for (k = 0; k < 200; ++k) { for (k = 0; k < 10; ++k) {
ASSERT_SYS(0, 0, getrandom(0, 0, 0)); ASSERT_SYS(0, 0, getentropy(0, 0));
ASSERT_SYS(0, n, getrandom(buf, n, 0)); for (i = 0; i < n; i += m) {
ASSERT_SYS(EFAULT, -1, getrandom(0, n, 0)); m = MIN(n - i, 256);
ASSERT_SYS(EINVAL, -1, getrandom(buf, n, -1)); ASSERT_SYS(0, 0, getentropy(buf + i, m));
ASSERT_SYS(EFAULT, -1, getentropy(0, m));
}
if ((e = MeasureEntropy(buf, n)) < w) { if ((e = MeasureEntropy(buf, n)) < w) {
fprintf(stderr, "error: entropy suspect! got %g but want >=%g\n", e, w); fprintf(stderr, "error: entropy suspect! got %g but want >=%g\n", e, w);
for (i = 0; i < n;) { for (i = 0; i < n;) {

View file

@ -24,6 +24,7 @@
#include "libc/errno.h" #include "libc/errno.h"
#include "libc/intrin/bits.h" #include "libc/intrin/bits.h"
#include "libc/log/check.h" #include "libc/log/check.h"
#include "libc/macros.internal.h"
#include "libc/math.h" #include "libc/math.h"
#include "libc/mem/gc.h" #include "libc/mem/gc.h"
#include "libc/mem/mem.h" #include "libc/mem/mem.h"
@ -41,6 +42,29 @@
#include "libc/testlib/testlib.h" #include "libc/testlib/testlib.h"
#include "libc/thread/thread.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) { TEST(getrandom, test) {
double e, w = 7.7; double e, w = 7.7;
int i, j, n = 999; 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 */ /* JustReturnZero */
/* entropy: 0 */ /* entropy: 0 */
/* chi-square: 2.55e+07 */ /* chi-square: 2.55e+07 */

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/assert.h"
#include "libc/atomic.h" #include "libc/atomic.h"
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/calls/internal.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; int rc;
int64_t nanos, maxnanos; int64_t nanos, maxnanos;
struct timespec ts, deadline; struct timespec now, wait, remain, deadline;
ts = timespec_real (); if (!abstime) {
if (!timeout) {
deadline = timespec_max; deadline = timespec_max;
} else { } else {
deadline = *timeout; deadline = *abstime;
} }
nanos = 100; nanos = 100;
maxnanos = __SIG_POLLING_INTERVAL_MS * 1000L * 1000; maxnanos = __SIG_POLLING_INTERVAL_MS * 1000L * 1000;
while (timespec_cmp (deadline, ts) > 0) { for (;;) {
ts = timespec_add (ts, timespec_fromnanos (nanos));
if (timespec_cmp (ts, deadline) > 0) {
ts = deadline;
}
if (atomic_load_explicit (w, memory_order_acquire) != expect) { if (atomic_load_explicit (w, memory_order_acquire) != expect) {
return 0; 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; return -rc;
} }
if (nanos < maxnanos) { if (nanos < maxnanos) {

View file

@ -103,7 +103,8 @@
"DBL_MIN" "DBL_MIN"
"DBL_MAX" "DBL_MAX"
"FLT_MIN" "FLT_MIN"
"FLT_MAX")) "FLT_MAX"
"PIPE_BUF"))
(defconst cosmo-c-constants-math (defconst cosmo-c-constants-math
'("NAN" '("NAN"

View file

@ -3322,6 +3322,8 @@ unix = {
--- @type integer --- @type integer
CLOCK_MONOTONIC_COARSE = nil, CLOCK_MONOTONIC_COARSE = nil,
--- @type integer --- @type integer
CLOCK_MONOTONIC_PRECISE = nil,
--- @type integer
CLOCK_MONOTONIC_FAST = nil, CLOCK_MONOTONIC_FAST = nil,
--- @type integer --- @type integer
CLOCK_MONOTONIC_RAW = nil, CLOCK_MONOTONIC_RAW = nil,
@ -3332,6 +3334,8 @@ unix = {
--- @type integer --- @type integer
CLOCK_REALTIME = nil, CLOCK_REALTIME = nil,
--- @type integer --- @type integer
CLOCK_REALTIME_PRECISE = nil,
--- @type integer
CLOCK_REALTIME_ALARM = nil, CLOCK_REALTIME_ALARM = nil,
--- @type integer --- @type integer
CLOCK_REALTIME_COARSE = nil, CLOCK_REALTIME_COARSE = nil,
@ -5247,13 +5251,16 @@ function unix.syslog(priority, msg) end
--- ---
--- - `CLOCK_REALTIME`: universally supported --- - `CLOCK_REALTIME`: universally supported
--- - `CLOCK_REALTIME_FAST`: ditto but faster on freebsd --- - `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`: universally supported
--- - `CLOCK_MONOTONIC_FAST`: ditto but faster on freebsd --- - `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_PROCESS_CPUTIME_ID`: linux and bsd
--- - `CLOCK_THREAD_CPUTIME_ID`: linux and bsd --- - `CLOCK_THREAD_CPUTIME_ID`: linux and bsd
--- - `CLOCK_REALTIME_COARSE`: : linux and openbsd --- - `CLOCK_MONOTONIC_COARSE`: linux, freebsd
--- - `CLOCK_MONOTONIC_COARSE`: linux
--- - `CLOCK_PROF`: linux and netbsd --- - `CLOCK_PROF`: linux and netbsd
--- - `CLOCK_BOOTTIME`: linux and openbsd --- - `CLOCK_BOOTTIME`: linux and openbsd
--- - `CLOCK_REALTIME_ALARM`: linux-only --- - `CLOCK_REALTIME_ALARM`: linux-only

View file

@ -3597,13 +3597,16 @@ UNIX MODULE
- `CLOCK_REALTIME`: universally supported - `CLOCK_REALTIME`: universally supported
- `CLOCK_REALTIME_FAST`: ditto but faster on freebsd - `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`: universally supported
- `CLOCK_MONOTONIC_FAST`: ditto but faster on freebsd - `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_PROCESS_CPUTIME_ID`: linux and bsd
- `CLOCK_THREAD_CPUTIME_ID`: linux and bsd - `CLOCK_THREAD_CPUTIME_ID`: linux and bsd
- `CLOCK_REALTIME_COARSE`: : linux and openbsd - `CLOCK_MONOTONIC_COARSE`: linux, freebsd
- `CLOCK_MONOTONIC_COARSE`: linux
- `CLOCK_PROF`: linux and netbsd - `CLOCK_PROF`: linux and netbsd
- `CLOCK_BOOTTIME`: linux and openbsd - `CLOCK_BOOTTIME`: linux and openbsd
- `CLOCK_REALTIME_ALARM`: linux-only - `CLOCK_REALTIME_ALARM`: linux-only