mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 03:27:39 +00:00
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:
parent
aee50b1327
commit
b407327972
47 changed files with 645 additions and 306 deletions
Binary file not shown.
|
@ -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
43
examples/nanosleep_test.c
Normal 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)));
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
25
libc/calls/clock_nanosleep_latency.c
Normal file
25
libc/calls/clock_nanosleep_latency.c
Normal 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"
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
26
libc/runtime/clone-xnu.S
Normal 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
|
|
@ -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"(¶ms), "S"(sizeof(params))
|
: "1"(__NR_thr_new), "D"(¶ms), "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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) */
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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)();
|
||||||
|
|
2
libc/sysv/calls/__sys_clock_nanosleep.s
Normal file
2
libc/sysv/calls/__sys_clock_nanosleep.s
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
.include "o/libc/sysv/macros.internal.inc"
|
||||||
|
.scall __sys_clock_nanosleep,0x9ddfff8f4ffff8e6,globl,hidden
|
|
@ -1,2 +0,0 @@
|
||||||
.include "o/libc/sysv/macros.internal.inc"
|
|
||||||
.scall sys_bsdthread_create,0xfffffffff2168fff,globl,hidden
|
|
|
@ -1,2 +0,0 @@
|
||||||
.include "o/libc/sysv/macros.internal.inc"
|
|
||||||
.scall sys_clock_nanosleep,0x9ddfff8f4ffff8e6,globl,hidden
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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, ¶m);
|
e = errno;
|
||||||
} else if (IsLinux()) {
|
if (IsNetbsd()) {
|
||||||
rc = sys_sched_setscheduler(pt->tid, policy, ¶m);
|
rc = sys_sched_setparam_netbsd(0, tid, policy, ¶m);
|
||||||
} else if (IsFreebsd()) {
|
} else if (IsLinux()) {
|
||||||
rc = _pthread_setschedparam_freebsd(pt->tid, policy, ¶m);
|
rc = sys_sched_setscheduler(tid, policy, ¶m);
|
||||||
} else {
|
} else if (IsFreebsd()) {
|
||||||
rc = enosys();
|
rc = _pthread_setschedparam_freebsd(tid, policy, ¶m);
|
||||||
|
} else {
|
||||||
|
rc = enosys();
|
||||||
|
}
|
||||||
|
if (rc == -1) {
|
||||||
|
rc = errno;
|
||||||
|
errno = e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
rc = rc != -1 ? 0 : errno;
|
|
||||||
errno = e;
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 *);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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++ = '.';
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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;) {
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
30
third_party/nsync/futex.c
vendored
30
third_party/nsync/futex.c
vendored
|
@ -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) {
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue