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_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);

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𝑛𝑠
*
* @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

View file

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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
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"(&params), "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;
}

View file

@ -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) */

View file

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

View file

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

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

View file

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

View file

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

View file

@ -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;
}
/**

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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, &param);
} else if (IsLinux()) {
rc = sys_sched_setscheduler(pt->tid, policy, &param);
} else if (IsFreebsd()) {
rc = _pthread_setschedparam_freebsd(pt->tid, policy, &param);
} else {
rc = enosys();
if (!(rc = pthread_getunique_np((pthread_t)pt, &tid))) {
e = errno;
if (IsNetbsd()) {
rc = sys_sched_setparam_netbsd(0, tid, policy, &param);
} else if (IsLinux()) {
rc = sys_sched_setscheduler(tid, policy, &param);
} else if (IsFreebsd()) {
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;
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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++ = '.';

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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