Clean up some sleep code

This commit is contained in:
Justine Tunney 2022-10-08 02:40:44 -07:00
parent 9849b4c7ba
commit 672ccda37c
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
35 changed files with 310 additions and 598 deletions

View file

@ -0,0 +1,38 @@
/*-*- 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/struct/timespec.h"
#include "libc/errno.h"
#include "libc/sysv/consts/clock.h"
/**
* Sleeps for specified delay.
*
* @return unslept time which may be non-zero if the call was interrupted
*/
struct timespec _timespec_sleep(struct timespec delay) {
int rc;
struct timespec remain;
if (!(rc = clock_nanosleep(CLOCK_REALTIME, 0, &delay, &remain))) {
return (struct timespec){0};
} else {
_npassert(rc == EINTR);
return remain;
}
}

View file

@ -16,5 +16,20 @@
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/struct/timespec.h"
#include "libc/errno.h"
#include "libc/sysv/consts/clock.h"
#include "libc/sysv/consts/timer.h"
bool __time_critical; /**
* Sleeps until the specified time.
*
* @return 0 on success, or EINTR if interrupted
*/
int _timespec_sleep_until(struct timespec abs_deadline) {
int rc;
rc = clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &abs_deadline, 0);
_npassert(!rc || rc == EINTR);
return rc;
}

View file

@ -33,6 +33,7 @@
#include "libc/mem/alloca.h" #include "libc/mem/alloca.h"
#include "libc/nt/synchronization.h" #include "libc/nt/synchronization.h"
#include "libc/sysv/errfuns.h" #include "libc/sysv/errfuns.h"
#include "libc/thread/tls.h"
/** /**
* Returns nanosecond time. * Returns nanosecond time.
@ -83,7 +84,7 @@ int clock_gettime(int clock, struct timespec *ts) {
rc = __clock_gettime(clock, ts); rc = __clock_gettime(clock, ts);
} }
#if SYSDEBUG #if SYSDEBUG
if (!__time_critical) { if (!(__get_tls()->tib_flags & TIB_FLAG_TIME_CRITICAL)) {
STRACE("clock_gettime(%s, [%s]) → %d% m", DescribeClockName(clock), STRACE("clock_gettime(%s, [%s]) → %d% m", DescribeClockName(clock),
DescribeTimespec(rc, ts), rc); DescribeTimespec(rc, ts), rc);
} }

View file

@ -29,6 +29,7 @@
#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/tls.h"
/** /**
* Sleeps for particular amount of time. * Sleeps for particular amount of time.
@ -112,7 +113,7 @@ errno_t clock_nanosleep(int clock, int flags, const struct timespec *req,
} }
#if SYSDEBUG #if SYSDEBUG
if (!__time_critical) { if (!(__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),
DescribeSleepFlags(flags), DescribeTimespec(0, req), DescribeSleepFlags(flags), DescribeTimespec(0, req),
DescribeTimespec(rc, rem), DescribeErrnoResult(rc)); DescribeTimespec(rc, rem), DescribeErrnoResult(rc));

View file

@ -18,7 +18,6 @@
*/ */
#include "libc/assert.h" #include "libc/assert.h"
#include "libc/calls/state.internal.h" #include "libc/calls/state.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/calls/struct/itimerval.internal.h" #include "libc/calls/struct/itimerval.internal.h"
#include "libc/calls/struct/timeval.h" #include "libc/calls/struct/timeval.h"
#include "libc/calls/struct/timeval.internal.h" #include "libc/calls/struct/timeval.internal.h"
@ -26,7 +25,9 @@
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/intrin/asan.internal.h" #include "libc/intrin/asan.internal.h"
#include "libc/intrin/describeflags.internal.h" #include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/sysv/errfuns.h" #include "libc/sysv/errfuns.h"
#include "libc/thread/tls.h"
#include "libc/time/struct/timezone.h" #include "libc/time/struct/timezone.h"
typedef axdx_t gettimeofday_f(struct timeval *, struct timezone *, void *); typedef axdx_t gettimeofday_f(struct timeval *, struct timezone *, void *);
@ -62,7 +63,7 @@ int gettimeofday(struct timeval *tv, struct timezone *tz) {
rc = __gettimeofday(tv, tz, 0).ax; rc = __gettimeofday(tv, tz, 0).ax;
} }
#if SYSDEBUG #if SYSDEBUG
if (!__time_critical) { if (!(__get_tls()->tib_flags & TIB_FLAG_TIME_CRITICAL)) {
STRACE("gettimeofday([%s], %p) → %d% m", DescribeTimeval(rc, tv), tz, rc); STRACE("gettimeofday([%s], %p) → %d% m", DescribeTimeval(rc, tv), tz, rc);
} }
#endif #endif

View file

@ -31,11 +31,12 @@
textwindows bool _check_interrupts(bool restartable, struct Fd *fd) { textwindows bool _check_interrupts(bool restartable, struct Fd *fd) {
bool res; bool res;
if (__time_critical) return false;
if (__threaded && __threaded != gettid()) return false; if (__threaded && __threaded != gettid()) return false;
if (_weaken(_check_sigalrm)) _weaken(_check_sigalrm)(); if (_weaken(_check_sigalrm)) _weaken(_check_sigalrm)();
if (_weaken(_check_sigchld)) _weaken(_check_sigchld)(); if (!(__get_tls()->tib_flags & TIB_FLAG_TIME_CRITICAL)) {
if (fd && _weaken(_check_sigwinch)) _weaken(_check_sigwinch)(fd); if (_weaken(_check_sigchld)) _weaken(_check_sigchld)();
if (fd && _weaken(_check_sigwinch)) _weaken(_check_sigwinch)(fd);
}
res = _weaken(__sig_check) && _weaken(__sig_check)(restartable); res = _weaken(__sig_check) && _weaken(__sig_check)(restartable);
return res; return res;
} }

View file

@ -1,99 +0,0 @@
/*-*- 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 2021 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/internal.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/struct/timespec.h"
#include "libc/calls/struct/timespec.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/limits.h"
#include "libc/macros.internal.h"
#include "libc/nexgen32e/rdtsc.h"
#include "libc/nt/errors.h"
#include "libc/nt/synchronization.h"
#include "libc/sysv/consts/clock.h"
#include "libc/sysv/errfuns.h"
#include "libc/time/clockstonanos.internal.h"
textwindows int sys_nanosleep_nt(const struct timespec *req,
struct timespec *rem) {
bool alertable;
uint32_t slice;
uint64_t begin;
int64_t ms, toto, nanos;
struct timespec elapsed;
// check req is legal timespec
if (!(0 <= req->tv_nsec && req->tv_nsec < 1000000000)) {
return einval();
}
// save beginning timestamp
if (!__time_critical && rem) {
begin = rdtsc();
} else {
begin = 0; // to prevent uninitialized warning
}
// convert timespec to milliseconds
ms = _timespec_tomillis(*req);
for (toto = ms;;) {
// check if signal was delivered
if (!__time_critical && _check_interrupts(false, g_fds.p)) {
if (rem) {
nanos = ClocksToNanos(rdtsc(), begin);
elapsed.tv_sec = nanos / 1000000000;
elapsed.tv_nsec = nanos % 1000000000;
*rem = _timespec_sub(*req, elapsed);
if (rem->tv_sec < 0) {
rem->tv_sec = 0;
rem->tv_nsec = 0;
}
}
return eintr();
}
// configure the sleep
slice = MIN(__SIG_POLLING_INTERVAL_MS, ms);
if (__time_critical) {
alertable = false;
} else {
alertable = true;
POLLTRACE("... sleeping %'ldms of %'ld", toto - ms, toto);
}
// perform the sleep
if (SleepEx(slice, alertable) == kNtWaitIoCompletion) {
POLLTRACE("IOCP EINTR"); // in case we ever figure it out
continue;
}
// check if full duration has elapsed
if ((ms -= slice) <= 0) {
if (rem) {
rem->tv_sec = 0;
rem->tv_nsec = 0;
}
return 0;
}
}
}

View file

@ -16,18 +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/calls/asan.internal.h" #include "libc/calls/struct/timespec.h"
#include "libc/calls/calls.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/struct/timespec.internal.h"
#include "libc/dce.h"
#include "libc/errno.h" #include "libc/errno.h"
#include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/sysv/consts/clock.h" #include "libc/sysv/consts/clock.h"
#include "libc/sysv/errfuns.h"
// TODO(jart): Just delegate to clock_nanosleep()
/** /**
* Sleeps for relative amount of time. * Sleeps for relative amount of time.
@ -45,32 +36,10 @@
*/ */
int nanosleep(const struct timespec *req, struct timespec *rem) { int nanosleep(const struct timespec *req, struct timespec *rem) {
int rc; int rc;
if (!(rc = clock_nanosleep(CLOCK_REALTIME, 0, req, rem))) {
if (!req || (IsAsan() && (!__asan_is_valid_timespec(req) || return 0;
(rem && !__asan_is_valid_timespec(rem))))) {
rc = efault();
} else if (req->tv_sec < 0 ||
!(0 <= req->tv_nsec && req->tv_nsec <= 999999999)) {
rc = einval();
} else if (IsLinux() || IsFreebsd() || IsNetbsd()) {
rc = sys_clock_nanosleep(CLOCK_REALTIME, 0, req, rem);
if (rc > 0) errno = rc, rc = -1;
} else if (IsOpenbsd()) {
rc = sys_nanosleep(req, rem);
} else if (IsXnu()) {
rc = sys_nanosleep_xnu(req, rem);
} else if (IsMetal()) {
rc = enosys();
} else { } else {
rc = sys_nanosleep_nt(req, rem); errno = rc;
return -1;
} }
#ifdef SYSDEBUG
if (!__time_critical) {
STRACE("nanosleep(%s, [%s]) → %d% m", DescribeTimespec(rc, req),
DescribeTimespec(rc, rem), rc);
}
#endif
return rc;
} }

View file

@ -17,20 +17,21 @@
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/assert.h" #include "libc/assert.h"
#include "libc/intrin/bits.h"
#include "libc/intrin/initializer.internal.h"
#include "libc/intrin/safemacros.internal.h"
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/calls/clock_gettime.internal.h" #include "libc/calls/clock_gettime.internal.h"
#include "libc/calls/state.internal.h" #include "libc/calls/state.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/calls/syscall_support-sysv.internal.h" #include "libc/calls/syscall_support-sysv.internal.h"
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/intrin/bits.h"
#include "libc/intrin/initializer.internal.h"
#include "libc/intrin/safemacros.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/macros.internal.h" #include "libc/macros.internal.h"
#include "libc/nexgen32e/rdtsc.h" #include "libc/nexgen32e/rdtsc.h"
#include "libc/nexgen32e/x86feature.h" #include "libc/nexgen32e/x86feature.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "libc/sysv/consts/clock.h" #include "libc/sysv/consts/clock.h"
#include "libc/thread/tls.h"
#include "libc/time/time.h" #include "libc/time/time.h"
static clock_gettime_f *__gettime; static clock_gettime_f *__gettime;
@ -53,11 +54,9 @@ static long double GetTimeSample(void) {
} }
static long double MeasureNanosPerCycle(void) { static long double MeasureNanosPerCycle(void) {
bool tc;
int i, n; int i, n;
long double avg, samp; long double avg, samp;
tc = __time_critical; __get_tls()->tib_flags |= TIB_FLAG_TIME_CRITICAL;
__time_critical = true;
if (IsWindows()) { if (IsWindows()) {
n = 10; n = 10;
} else { } else {
@ -67,7 +66,7 @@ static long double MeasureNanosPerCycle(void) {
samp = GetTimeSample(); samp = GetTimeSample();
avg += (samp - avg) / i; avg += (samp - avg) / i;
} }
__time_critical = tc; __get_tls()->tib_flags &= ~TIB_FLAG_TIME_CRITICAL;
STRACE("MeasureNanosPerCycle cpn*1000=%d", (long)(avg * 1000)); STRACE("MeasureNanosPerCycle cpn*1000=%d", (long)(avg * 1000));
return avg; return avg;
} }

View file

@ -18,17 +18,18 @@
*/ */
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/calls/state.internal.h" #include "libc/calls/state.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/calls/struct/timespec.h" #include "libc/calls/struct/timespec.h"
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/intrin/bits.h" #include "libc/intrin/bits.h"
#include "libc/intrin/initializer.internal.h" #include "libc/intrin/initializer.internal.h"
#include "libc/intrin/safemacros.internal.h" #include "libc/intrin/safemacros.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/macros.internal.h" #include "libc/macros.internal.h"
#include "libc/nexgen32e/rdtsc.h" #include "libc/nexgen32e/rdtsc.h"
#include "libc/nexgen32e/x86feature.h" #include "libc/nexgen32e/x86feature.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "libc/sysv/consts/clock.h" #include "libc/sysv/consts/clock.h"
#include "libc/thread/tls.h"
#include "libc/time/time.h" #include "libc/time/time.h"
static struct Now { static struct Now {
@ -50,11 +51,9 @@ static long double GetTimeSample(void) {
} }
static long double MeasureNanosPerCycle(void) { static long double MeasureNanosPerCycle(void) {
bool tc;
int i, n; int i, n;
long double avg, samp; long double avg, samp;
tc = __time_critical; __get_tls()->tib_flags |= TIB_FLAG_TIME_CRITICAL;
__time_critical = true;
if (IsWindows()) { if (IsWindows()) {
n = 30; n = 30;
} else { } else {
@ -64,7 +63,7 @@ static long double MeasureNanosPerCycle(void) {
samp = GetTimeSample(); samp = GetTimeSample();
avg += (samp - avg) / i; avg += (samp - avg) / i;
} }
__time_critical = tc; __get_tls()->tib_flags &= ~TIB_FLAG_TIME_CRITICAL;
STRACE("MeasureNanosPerCycle cpn*1000=%d", (long)(avg * 1000)); STRACE("MeasureNanosPerCycle cpn*1000=%d", (long)(avg * 1000));
return avg; return avg;
} }

View file

@ -17,8 +17,8 @@
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/calls/struct/timespec.h" #include "libc/calls/struct/timespec.h"
#include "libc/errno.h"
#include "libc/limits.h" #include "libc/limits.h"
#include "libc/sysv/consts/clock.h"
#include "libc/time/time.h" #include "libc/time/time.h"
/** /**
@ -33,12 +33,9 @@
* @norestart * @norestart
*/ */
unsigned sleep(unsigned seconds) { unsigned sleep(unsigned seconds) {
int err;
unsigned unslept; unsigned unslept;
struct timespec tv = {seconds}; struct timespec tv = {seconds};
err = errno; if (!clock_nanosleep(CLOCK_REALTIME, 0, &tv, &tv)) return 0;
nanosleep(&tv, &tv);
errno = err;
unslept = tv.tv_sec; unslept = tv.tv_sec;
if (tv.tv_nsec && unslept < UINT_MAX) { if (tv.tv_nsec && unslept < UINT_MAX) {
++unslept; ++unslept;

View file

@ -3,6 +3,9 @@
#if !(__ASSEMBLER__ + __LINKER__ + 0) #if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_ COSMOPOLITAN_C_START_
#define _timespec_zero ((struct timespec){0})
#define _timespec_max ((struct timespec){0x7fffffffffffffff, 999999999})
struct timespec { struct timespec {
int64_t tv_sec; int64_t tv_sec;
int64_t tv_nsec; /* nanoseconds */ int64_t tv_nsec; /* nanoseconds */
@ -29,8 +32,10 @@ struct timespec _timespec_add(struct timespec, struct timespec) pureconst;
struct timespec _timespec_fromnanos(int64_t) pureconst; struct timespec _timespec_fromnanos(int64_t) pureconst;
struct timespec _timespec_frommicros(int64_t) pureconst; struct timespec _timespec_frommicros(int64_t) pureconst;
struct timespec _timespec_frommillis(int64_t) pureconst; struct timespec _timespec_frommillis(int64_t) pureconst;
struct timespec _timespec_mono(void);
struct timespec _timespec_real(void); struct timespec _timespec_real(void);
struct timespec _timespec_mono(void);
struct timespec _timespec_sleep(struct timespec);
int _timespec_sleep_until(struct timespec);
struct timespec _timespec_sub(struct timespec, struct timespec) pureconst; struct timespec _timespec_sub(struct timespec, struct timespec) pureconst;
COSMOPOLITAN_C_END_ COSMOPOLITAN_C_END_

View file

@ -17,6 +17,8 @@
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/calls/struct/timespec.h" #include "libc/calls/struct/timespec.h"
#include "libc/sysv/consts/clock.h"
#include "libc/sysv/errfuns.h"
#include "libc/time/time.h" #include "libc/time/time.h"
/** /**
@ -28,7 +30,7 @@
* @norestart * @norestart
*/ */
int usleep(uint32_t micros) { int usleep(uint32_t micros) {
struct timespec ts; struct timespec ts = _timespec_frommicros(micros);
ts = _timespec_frommicros(micros); if (clock_nanosleep(CLOCK_REALTIME, 0, &ts, 0)) return eintr();
return nanosleep(&ts, 0); return 0;
} }

View file

@ -1314,7 +1314,7 @@ syscon rusage RUSAGE_BOTH -2 99 99 99 99 99 # woop
# #
# group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary # group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary
syscon futex FUTEX_WAIT 0 0 0 1 0 0 syscon futex FUTEX_WAIT 0 0 0 1 0 0
syscon futex FUTEX_WAKE 1 0 1 2 0 1 syscon futex FUTEX_WAKE 1 0 1 2 1 1
syscon futex FUTEX_REQUEUE 3 0 0 3 0 0 syscon futex FUTEX_REQUEUE 3 0 0 3 0 0
syscon futex FUTEX_PRIVATE_FLAG 128 0 128 128 0 0 syscon futex FUTEX_PRIVATE_FLAG 128 0 128 128 0 0

View file

@ -1,2 +1,2 @@
.include "o/libc/sysv/consts/syscon.internal.inc" .include "o/libc/sysv/consts/syscon.internal.inc"
.syscon futex,FUTEX_WAKE,1,0,1,2,0,1 .syscon futex,FUTEX_WAKE,1,0,1,2,1,1

View file

@ -4,6 +4,8 @@
#define TLS_ALIGNMENT 64 #define TLS_ALIGNMENT 64
#define TIB_FLAG_TIME_CRITICAL 1
#if !(__ASSEMBLER__ + __LINKER__ + 0) #if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_ COSMOPOLITAN_C_START_
@ -24,7 +26,7 @@ struct CosmoTib {
_Atomic(int32_t) tib_tid; /* 0x38 */ _Atomic(int32_t) tib_tid; /* 0x38 */
int32_t tib_errno; /* 0x3c */ int32_t tib_errno; /* 0x3c */
void *tib_nsync; void *tib_nsync;
void *tib_reserved1; uint64_t tib_flags;
void *tib_reserved2; void *tib_reserved2;
void *tib_reserved3; void *tib_reserved3;
void *tib_reserved4; void *tib_reserved4;

View file

@ -1,5 +1,5 @@
#ifndef NSYNC_TESTING_ARRAY_H_ #ifndef NSYNC_ARRAY_H_
#define NSYNC_TESTING_ARRAY_H_ #define NSYNC_ARRAY_H_
/* clang-format off */ /* clang-format off */
/* Return the number of elements in a C array a. /* Return the number of elements in a C array a.
@ -58,4 +58,4 @@ struct a_hdr_ {
(a)->a_ = NULL; (a)->h_.max_ = 0; (a)->h_.len_ = 0; \ (a)->a_ = NULL; (a)->h_.max_ = 0; (a)->h_.len_ = 0; \
} while (0) } while (0)
#endif /*NSYNC_TESTING_ARRAY_H_*/ #endif /*NSYNC_ARRAY_H_*/

View file

@ -41,3 +41,11 @@ nsync_time_ms:
nsync_time_us: nsync_time_us:
jmp _timespec_frommicros jmp _timespec_frommicros
.endfn nsync_time_us,globl .endfn nsync_time_us,globl
nsync_time_sleep:
jmp _timespec_sleep
.endfn nsync_time_us,globl
nsync_time_sleep_until:
jmp _timespec_sleep_until
.endfn nsync_time_us,globl

View file

@ -17,7 +17,9 @@
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/atomic.h" #include "libc/atomic.h"
#include "libc/calls/calls.h"
#include "libc/calls/sig.internal.h" #include "libc/calls/sig.internal.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/struct/timespec.h" #include "libc/calls/struct/timespec.h"
#include "libc/calls/struct/timespec.internal.h" #include "libc/calls/struct/timespec.internal.h"
#include "libc/dce.h" #include "libc/dce.h"
@ -33,8 +35,10 @@
#include "libc/sysv/consts/timer.h" #include "libc/sysv/consts/timer.h"
#include "libc/thread/freebsd.internal.h" #include "libc/thread/freebsd.internal.h"
#include "libc/thread/thread.h" #include "libc/thread/thread.h"
#include "libc/thread/tls.h"
#include "third_party/nsync/atomic.h"
#include "third_party/nsync/common.internal.h" #include "third_party/nsync/common.internal.h"
#include "third_party/nsync/mu_semaphore.internal.h" #include "third_party/nsync/time.h"
// clang-format off // clang-format off
/* futex() polyfill w/ sched_yield() fallback */ /* futex() polyfill w/ sched_yield() fallback */
@ -51,15 +55,15 @@ bool FUTEX_TIMEOUT_IS_ABSOLUTE;
__attribute__((__constructor__)) static void nsync_futex_init_ (void) { __attribute__((__constructor__)) static void nsync_futex_init_ (void) {
int x = 0; int x = 0;
if (NSYNC_FUTEX_WIN32 && IsWindows ()) { FUTEX_WAIT_ = FUTEX_WAIT;
if (IsWindows ()) {
FUTEX_IS_SUPPORTED = true; FUTEX_IS_SUPPORTED = true;
FUTEX_WAIT_ = FUTEX_WAIT;
return; return;
} }
if (IsFreebsd ()) { if (IsFreebsd ()) {
FUTEX_IS_SUPPORTED = true; FUTEX_IS_SUPPORTED = true;
FUTEX_WAIT_ = FUTEX_WAIT;
FUTEX_TIMEOUT_IS_ABSOLUTE = true; FUTEX_TIMEOUT_IS_ABSOLUTE = true;
FUTEX_PRIVATE_FLAG_ = FUTEX_PRIVATE_FLAG; FUTEX_PRIVATE_FLAG_ = FUTEX_PRIVATE_FLAG;
return; return;
@ -105,44 +109,41 @@ __attribute__((__constructor__)) static void nsync_futex_init_ (void) {
} }
static int nsync_futex_polyfill_ (int *p, int expect, struct timespec *timeout) { static int nsync_futex_polyfill_ (int *p, int expect, struct timespec *timeout) {
int rc; int64_t nanos, maxnanos;
atomic_int *w; nsync_atomic_uint32_ *w;
int64_t max, nanos;
struct timespec ts, deadline; struct timespec ts, deadline;
w = (atomic_int *)p; w = (nsync_atomic_uint32_ *)p;
if (atomic_load_explicit (p, memory_order_relaxed) != expect) { if (ATM_LOAD (p) != expect) {
return -EAGAIN; return -EAGAIN;
} }
ts = _timespec_real (); ts = nsync_time_now ();
if (!timeout) { if (!timeout) {
deadline = nsync_time_no_deadline; deadline = nsync_time_no_deadline;
} else if (FUTEX_TIMEOUT_IS_ABSOLUTE) { } else if (FUTEX_TIMEOUT_IS_ABSOLUTE) {
deadline = *timeout; deadline = *timeout;
} else { } else {
deadline = _timespec_add (ts, *timeout); deadline = nsync_time_add (ts, *timeout);
} }
nanos = 100; nanos = 100;
max = __SIG_POLLING_INTERVAL_MS * 1000L * 1000; maxnanos = __SIG_POLLING_INTERVAL_MS * 1000L * 1000;
while (_timespec_gt (deadline, ts)) { while (nsync_time_cmp (deadline, ts) > 0) {
if (atomic_load_explicit (p, memory_order_relaxed) != expect) { if (ATM_LOAD (p) != expect) {
return 0; return 0;
} }
ts = _timespec_add (ts, _timespec_fromnanos (nanos)); ts = nsync_time_add (ts, _timespec_fromnanos (nanos));
if (_timespec_gt (ts, deadline)) { if (nsync_time_cmp (ts, deadline) > 0) {
ts = deadline; ts = deadline;
} }
rc = clock_nanosleep (CLOCK_REALTIME, TIMER_ABSTIME, &ts, 0); if (nsync_time_sleep_until (ts)) {
if (rc) {
ASSERT (rc == EINTR);
return -EINTR; return -EINTR;
} }
if (nanos < max) { if (nanos < maxnanos) {
nanos <<= 1; nanos <<= 1;
if (nanos > max) { if (nanos > maxnanos) {
nanos = max; nanos = maxnanos;
} }
} }
} }
@ -154,38 +155,42 @@ int nsync_futex_wait_ (int *p, int expect, char pshare, struct timespec *timeout
uint32_t ms; uint32_t ms;
int rc, op, fop; int rc, op, fop;
if (!FUTEX_IS_SUPPORTED) {
return nsync_futex_polyfill_ (p, expect, timeout);
}
op = FUTEX_WAIT_; op = FUTEX_WAIT_;
if (pshare == PTHREAD_PROCESS_PRIVATE) { if (pshare == PTHREAD_PROCESS_PRIVATE) {
op |= FUTEX_PRIVATE_FLAG_; op |= FUTEX_PRIVATE_FLAG_;
} }
if (NSYNC_FUTEX_WIN32 && IsWindows ()) { if (FUTEX_IS_SUPPORTED) {
// Windows 8 futexes don't support multiple processes :( if (IsWindows ()) {
if (pshare) { // Windows 8 futexes don't support multiple processes :(
return nsync_futex_polyfill_ (p, expect, timeout); if (pshare) {
} goto Polyfill;
if (timeout) { }
ms = _timespec_tomillis (*timeout); if (timeout) {
ms = _timespec_tomillis (*timeout);
} else {
ms = -1;
}
if (WaitOnAddress (p, &expect, sizeof(int), ms)) {
rc = 0;
} else {
rc = -GetLastError ();
}
} else if (IsFreebsd ()) {
rc = sys_umtx_timedwait_uint (
p, expect, pshare, timeout);
} else { } else {
ms = -1; rc = _futex (p, op, expect, timeout, 0,
FUTEX_WAIT_BITS_);
if (IsOpenbsd() && rc > 0) {
rc = -rc;
}
} }
if (WaitOnAddress (p, &expect, sizeof(int), ms)) {
rc = 0;
} else {
rc = -GetLastError ();
}
} else if (IsFreebsd ()) {
rc = sys_umtx_timedwait_uint (p, expect, pshare, timeout);
} else { } else {
rc = _futex (p, op, expect, timeout, 0, FUTEX_WAIT_BITS_); Polyfill:
if (IsOpenbsd() && rc > 0) { __get_tls()->tib_flags |= TIB_FLAG_TIME_CRITICAL;
// [jart] openbsd does this without setting carry flag rc = nsync_futex_polyfill_ (p, expect, timeout);
rc = -rc; __get_tls()->tib_flags &= ~TIB_FLAG_TIME_CRITICAL;
}
} }
STRACE ("futex(%t, %s, %d, %s) → %s", STRACE ("futex(%t, %s, %d, %s) → %s",
@ -202,40 +207,40 @@ int nsync_futex_wake_ (int *p, int count, char pshare) {
ASSERT (count == 1 || count == INT_MAX); ASSERT (count == 1 || count == INT_MAX);
if (!FUTEX_IS_SUPPORTED) {
nsync_yield_ ();
return 0;
}
op = FUTEX_WAKE; op = FUTEX_WAKE;
if (pshare == PTHREAD_PROCESS_PRIVATE) { if (pshare == PTHREAD_PROCESS_PRIVATE) {
op |= FUTEX_PRIVATE_FLAG_; op |= FUTEX_PRIVATE_FLAG_;
} }
if (NSYNC_FUTEX_WIN32 && IsWindows ()) { if (FUTEX_IS_SUPPORTED) {
if (pshare) { if (IsWindows ()) {
nsync_yield_ (); if (pshare) {
return 0; goto Polyfill;
} }
if (count == 1) { if (count == 1) {
WakeByAddressSingle (p); WakeByAddressSingle (p);
} else {
WakeByAddressAll (p);
}
rc = 0;
} else if (IsFreebsd ()) {
if (pshare) {
fop = UMTX_OP_WAKE;
} else {
fop = UMTX_OP_WAKE_PRIVATE;
}
rc = sys_umtx_op (p, fop, count, 0, 0);
} else { } else {
WakeByAddressAll (p); rc = wake (p, op, count);
} }
rc = 0;
} else if (IsFreebsd ()) {
if (pshare) {
fop = UMTX_OP_WAKE;
} else {
fop = UMTX_OP_WAKE_PRIVATE;
}
rc = sys_umtx_op (p, fop, count, 0, 0);
} else { } else {
rc = wake (p, op, count); Polyfill:
sched_yield ();
rc = 0;
} }
STRACE ("futex(%t, %s, %d) → %s", p, STRACE ("futex(%t, %s, %d) → %s",
DescribeFutexOp(op), p, DescribeFutexOp(op),
count, DescribeErrnoResult(rc)); count, DescribeErrnoResult(rc));
return rc; return rc;

View file

@ -17,7 +17,7 @@
*/ */
#include "libc/mem/mem.h" #include "libc/mem/mem.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "third_party/nsync/testing/array.h" #include "third_party/nsync/array.internal.h"
// clang-format off // clang-format off
void a_ensure_ (void *v, int delta, int sz) { void a_ensure_ (void *v, int delta, int sz) {

View file

@ -1,56 +1,128 @@
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ /*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│
vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi
Copyright 2022 Justine Alexandra Roberts Tunney Copyright 2016 Google Inc.
Permission to use, copy, modify, and/or distribute this software for Licensed under the Apache License, Version 2.0 (the "License");
any purpose with or without fee is hereby granted, provided that the you may not use this file except in compliance with the License.
above copyright notice and this permission notice appear in all copies. You may obtain a copy of the License at
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL http://www.apache.org/licenses/LICENSE-2.0 │
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE Unless required by applicable law or agreed to in writing, software
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL distributed under the License is distributed on an "AS IS" BASIS,
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER See the License for the specific language governing permissions and
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR limitations under the License.
PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/dce.h" #include "libc/assert.h"
#include "libc/errno.h"
#include "libc/str/str.h"
#include "libc/thread/thread.h"
#include "third_party/nsync/atomic.h"
#include "third_party/nsync/atomic.internal.h"
#include "third_party/nsync/futex.internal.h"
#include "third_party/nsync/mu_semaphore.h" #include "third_party/nsync/mu_semaphore.h"
#include "third_party/nsync/mu_semaphore.internal.h"
asm(".ident\t\"\\n\\n\
*NSYNC (Apache 2.0)\\n\
Copyright 2016 Google, Inc.\\n\
https://github.com/google/nsync\"");
// clang-format off // clang-format off
#ifdef TINY
#define ASSERT(x) _unassert(x)
#else
#define ASSERT(x) _npassert(x)
#endif
/* Check that atomic operations on nsync_atomic_uint32_ can be applied to int. */
static const int assert_int_size = 1 /
(sizeof (assert_int_size) == sizeof (uint32_t) &&
sizeof (nsync_atomic_uint32_) == sizeof (uint32_t));
struct futex {
int i; /* lo half=count; hi half=waiter count */
};
static nsync_semaphore *sem_big_enough_for_futex = (nsync_semaphore *) (uintptr_t)(1 /
(sizeof (struct futex) <= sizeof (*sem_big_enough_for_futex)));
/* Initialize *s; the initial value is 0. */ /* Initialize *s; the initial value is 0. */
void nsync_mu_semaphore_init (nsync_semaphore *s) { void nsync_mu_semaphore_init (nsync_semaphore *s) {
if (NSYNC_FUTEX_WIN32 || !IsWindows ()) struct futex *f = (struct futex *) s;
nsync_mu_semaphore_init_futex (s); f->i = 0;
else
nsync_mu_semaphore_init_win32 (s);
} }
/* Wait until the count of *s exceeds 0, and decrement it. */ /* Wait until the count of *s exceeds 0, and decrement it. */
void nsync_mu_semaphore_p (nsync_semaphore *s) { void nsync_mu_semaphore_p (nsync_semaphore *s) {
if (NSYNC_FUTEX_WIN32 || !IsWindows ()) struct futex *f = (struct futex *) s;
nsync_mu_semaphore_p_futex (s); int i;
else do {
nsync_mu_semaphore_p_win32 (s); i = ATM_LOAD ((nsync_atomic_uint32_ *) &f->i);
if (i == 0) {
int futex_result = nsync_futex_wait_ (&f->i, i, PTHREAD_PROCESS_PRIVATE, NULL);
ASSERT (futex_result == 0 ||
futex_result == -EINTR ||
futex_result == -EWOULDBLOCK);
}
} while (i == 0 || !ATM_CAS_ACQ ((nsync_atomic_uint32_ *) &f->i, i, i-1));
} }
/* Wait until one of: /* Wait until one of:
the count of *s is non-zero, in which case decrement *s and return 0; the count of *s is non-zero, in which case decrement *s and return 0;
or abs_deadline expires, in which case return ETIMEDOUT. */ or abs_deadline expires, in which case return ETIMEDOUT. */
int nsync_mu_semaphore_p_with_deadline (nsync_semaphore *s, nsync_time abs_deadline) { int nsync_mu_semaphore_p_with_deadline (nsync_semaphore *s, nsync_time abs_deadline) {
if (NSYNC_FUTEX_WIN32 || !IsWindows ()) struct futex *f = (struct futex *)s;
return nsync_mu_semaphore_p_with_deadline_futex (s, abs_deadline); int i;
else int result = 0;
return nsync_mu_semaphore_p_with_deadline_win32 (s, abs_deadline); do {
i = ATM_LOAD ((nsync_atomic_uint32_ *) &f->i);
if (i == 0) {
int futex_result;
struct timespec ts_buf;
const struct timespec *ts = NULL;
if (nsync_time_cmp (abs_deadline, nsync_time_no_deadline) != 0) {
memset (&ts_buf, 0, sizeof (ts_buf));
if (FUTEX_TIMEOUT_IS_ABSOLUTE) {
ts_buf.tv_sec = NSYNC_TIME_SEC (abs_deadline);
ts_buf.tv_nsec = NSYNC_TIME_NSEC (abs_deadline);
} else {
nsync_time now;
now = nsync_time_now ();
if (nsync_time_cmp (now, abs_deadline) > 0) {
ts_buf.tv_sec = 0;
ts_buf.tv_nsec = 0;
} else {
nsync_time rel_deadline;
rel_deadline = nsync_time_sub (abs_deadline, now);
ts_buf.tv_sec = NSYNC_TIME_SEC (rel_deadline);
ts_buf.tv_nsec = NSYNC_TIME_NSEC (rel_deadline);
}
}
ts = &ts_buf;
}
futex_result = nsync_futex_wait_ (&f->i, i, PTHREAD_PROCESS_PRIVATE, ts);
ASSERT (futex_result == 0 ||
futex_result == -EINTR ||
futex_result == -ETIMEDOUT ||
futex_result == -EWOULDBLOCK);
/* Some systems don't wait as long as they are told. */
if (futex_result == -ETIMEDOUT &&
nsync_time_cmp (abs_deadline, nsync_time_now ()) <= 0) {
result = ETIMEDOUT;
}
}
} while (result == 0 && (i == 0 || !ATM_CAS_ACQ ((nsync_atomic_uint32_ *) &f->i, i, i - 1)));
return (result);
} }
/* Ensure that the count of *s is at least 1. */ /* Ensure that the count of *s is at least 1. */
void nsync_mu_semaphore_v (nsync_semaphore *s) { void nsync_mu_semaphore_v (nsync_semaphore *s) {
if (NSYNC_FUTEX_WIN32 || !IsWindows ()) struct futex *f = (struct futex *) s;
nsync_mu_semaphore_v_futex (s); uint32_t old_value;
else do {
nsync_mu_semaphore_v_win32 (s); old_value = ATM_LOAD ((nsync_atomic_uint32_ *) &f->i);
} while (!ATM_CAS_REL ((nsync_atomic_uint32_ *) &f->i, old_value, old_value+1));
ASSERT (nsync_futex_wake_ (&f->i, 1, PTHREAD_PROCESS_PRIVATE) >= 0);
} }

25
third_party/nsync/mu_semaphore.internal.h vendored Normal file → Executable file
View file

@ -1,25 +0,0 @@
#ifndef NSYNC_MU_SEMAPHORE_INTERNAL_H_
#define NSYNC_MU_SEMAPHORE_INTERNAL_H_
#include "third_party/nsync/mu_semaphore.h"
#include "third_party/nsync/time.h"
#ifndef NSYNC_FUTEX_WIN32
#define NSYNC_FUTEX_WIN32 1
#endif
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
void nsync_mu_semaphore_init_futex(nsync_semaphore *);
void nsync_mu_semaphore_p_futex(nsync_semaphore *);
int nsync_mu_semaphore_p_with_deadline_futex(nsync_semaphore *, nsync_time);
void nsync_mu_semaphore_v_futex(nsync_semaphore *);
void nsync_mu_semaphore_init_win32(nsync_semaphore *);
void nsync_mu_semaphore_p_win32(nsync_semaphore *);
int nsync_mu_semaphore_p_with_deadline_win32(nsync_semaphore *, nsync_time);
void nsync_mu_semaphore_v_win32(nsync_semaphore *);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* NSYNC_MU_SEMAPHORE_INTERNAL_H_ */

View file

@ -1,129 +0,0 @@
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│
vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi
Copyright 2016 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0 │
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "libc/assert.h"
#include "libc/errno.h"
#include "libc/str/str.h"
#include "libc/thread/thread.h"
#include "third_party/nsync/atomic.h"
#include "third_party/nsync/atomic.internal.h"
#include "third_party/nsync/futex.internal.h"
#include "third_party/nsync/mu_semaphore.h"
#include "third_party/nsync/mu_semaphore.internal.h"
asm(".ident\t\"\\n\\n\
*NSYNC (Apache 2.0)\\n\
Copyright 2016 Google, Inc.\\n\
https://github.com/google/nsync\"");
// clang-format off
#ifdef TINY
#define ASSERT(x) _unassert(x)
#else
#define ASSERT(x) _npassert(x)
#endif
/* Check that atomic operations on nsync_atomic_uint32_ can be applied to int. */
static const int assert_int_size = 1 /
(sizeof (assert_int_size) == sizeof (uint32_t) &&
sizeof (nsync_atomic_uint32_) == sizeof (uint32_t));
struct futex {
int i; /* lo half=count; hi half=waiter count */
};
static nsync_semaphore *sem_big_enough_for_futex = (nsync_semaphore *) (uintptr_t)(1 /
(sizeof (struct futex) <= sizeof (*sem_big_enough_for_futex)));
/* Initialize *s; the initial value is 0. */
void nsync_mu_semaphore_init_futex (nsync_semaphore *s) {
struct futex *f = (struct futex *) s;
f->i = 0;
}
/* Wait until the count of *s exceeds 0, and decrement it. */
void nsync_mu_semaphore_p_futex (nsync_semaphore *s) {
struct futex *f = (struct futex *) s;
int i;
do {
i = ATM_LOAD ((nsync_atomic_uint32_ *) &f->i);
if (i == 0) {
int futex_result = nsync_futex_wait_ (&f->i, i, PTHREAD_PROCESS_PRIVATE, NULL);
ASSERT (futex_result == 0 ||
futex_result == -EINTR ||
futex_result == -EWOULDBLOCK);
}
} while (i == 0 || !ATM_CAS_ACQ ((nsync_atomic_uint32_ *) &f->i, i, i-1));
}
/* Wait until one of:
the count of *s is non-zero, in which case decrement *s and return 0;
or abs_deadline expires, in which case return ETIMEDOUT. */
int nsync_mu_semaphore_p_with_deadline_futex (nsync_semaphore *s, nsync_time abs_deadline) {
struct futex *f = (struct futex *)s;
int i;
int result = 0;
do {
i = ATM_LOAD ((nsync_atomic_uint32_ *) &f->i);
if (i == 0) {
int futex_result;
struct timespec ts_buf;
const struct timespec *ts = NULL;
if (nsync_time_cmp (abs_deadline, nsync_time_no_deadline) != 0) {
memset (&ts_buf, 0, sizeof (ts_buf));
if (FUTEX_TIMEOUT_IS_ABSOLUTE) {
ts_buf.tv_sec = NSYNC_TIME_SEC (abs_deadline);
ts_buf.tv_nsec = NSYNC_TIME_NSEC (abs_deadline);
} else {
nsync_time now;
now = nsync_time_now ();
if (nsync_time_cmp (now, abs_deadline) > 0) {
ts_buf.tv_sec = 0;
ts_buf.tv_nsec = 0;
} else {
nsync_time rel_deadline;
rel_deadline = nsync_time_sub (abs_deadline, now);
ts_buf.tv_sec = NSYNC_TIME_SEC (rel_deadline);
ts_buf.tv_nsec = NSYNC_TIME_NSEC (rel_deadline);
}
}
ts = &ts_buf;
}
futex_result = nsync_futex_wait_ (&f->i, i, PTHREAD_PROCESS_PRIVATE, ts);
ASSERT (futex_result == 0 ||
futex_result == -EINTR ||
futex_result == -ETIMEDOUT ||
futex_result == -EWOULDBLOCK);
/* Some systems don't wait as long as they are told. */
if (futex_result == -ETIMEDOUT &&
nsync_time_cmp (abs_deadline, nsync_time_now ()) <= 0) {
result = ETIMEDOUT;
}
}
} while (result == 0 && (i == 0 || !ATM_CAS_ACQ ((nsync_atomic_uint32_ *) &f->i, i, i - 1)));
return (result);
}
/* Ensure that the count of *s is at least 1. */
void nsync_mu_semaphore_v_futex (nsync_semaphore *s) {
struct futex *f = (struct futex *) s;
uint32_t old_value;
do {
old_value = ATM_LOAD ((nsync_atomic_uint32_ *) &f->i);
} while (!ATM_CAS_REL ((nsync_atomic_uint32_ *) &f->i, old_value, old_value+1));
ASSERT (nsync_futex_wake_ (&f->i, 1, PTHREAD_PROCESS_PRIVATE) >= 0);
}

View file

@ -1,85 +0,0 @@
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│
vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi
Copyright 2016 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0 │
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "libc/calls/state.internal.h"
#include "libc/errno.h"
#include "libc/nt/enum/wait.h"
#include "libc/nt/synchronization.h"
#include "libc/runtime/runtime.h"
#include "third_party/nsync/mu_semaphore.h"
#include "third_party/nsync/mu_semaphore.internal.h"
#include "third_party/nsync/time.h"
asm(".ident\t\"\\n\\n\
*NSYNC (Apache 2.0)\\n\
Copyright 2016 Google, Inc.\\n\
https://github.com/google/nsync\"");
// clang-format off
/* Initialize *s; the initial value is 0. */
void nsync_mu_semaphore_init_win32 (nsync_semaphore *s) {
int64_t *h = (int64_t *) s;
*h = CreateSemaphore (&kNtIsInheritable, 0, 1, NULL);
if (!*h) notpossible;
}
/* Wait until the count of *s exceeds 0, and decrement it. */
void nsync_mu_semaphore_p_win32 (nsync_semaphore *s) {
int64_t *h = (int64_t *) s;
WaitForSingleObject (*h, -1u);
}
/* Wait until one of:
the count of *s is non-zero, in which case decrement *s and return 0;
or abs_deadline expires, in which case return ETIMEDOUT. */
int nsync_mu_semaphore_p_with_deadline_win32 (nsync_semaphore *s, nsync_time abs_deadline) {
int64_t *h = (int64_t *) s;
int result;
if (nsync_time_cmp (abs_deadline, nsync_time_no_deadline) == 0) {
result = WaitForSingleObject (*h, -1u);
} else {
nsync_time now;
now = nsync_time_now ();
do {
if (nsync_time_cmp (abs_deadline, now) <= 0) {
result = WaitForSingleObject (*h, 0);
} else {
nsync_time delay;
delay = nsync_time_sub (abs_deadline, now);
if (NSYNC_TIME_SEC (delay) > 1000*1000) {
result = WaitForSingleObject (*h, 1000*1000);
} else {
result = WaitForSingleObject (*h,
(unsigned) (NSYNC_TIME_SEC (delay) * 1000 +
(NSYNC_TIME_NSEC (delay) + 999999) / (1000 * 1000)));
}
}
if (result == kNtWaitTimeout) {
now = nsync_time_now ();
}
} while (result == kNtWaitTimeout && /* Windows generates early wakeups. */
nsync_time_cmp (abs_deadline, now) > 0);
}
return (result == kNtWaitTimeout ? ETIMEDOUT : 0);
}
/* Ensure that the count of *s is at least 1. */
void nsync_mu_semaphore_v_win32 (nsync_semaphore *s) {
int64_t *h = (int64_t *) s;
ReleaseSemaphore(*h, 1, NULL);
}

View file

@ -17,11 +17,11 @@
*/ */
#include "libc/fmt/fmt.h" #include "libc/fmt/fmt.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "third_party/nsync/array.internal.h"
#include "third_party/nsync/cv.h" #include "third_party/nsync/cv.h"
#include "third_party/nsync/heap.internal.h"
#include "third_party/nsync/mu.h" #include "third_party/nsync/mu.h"
#include "third_party/nsync/testing/array.h"
#include "third_party/nsync/testing/closure.h" #include "third_party/nsync/testing/closure.h"
#include "third_party/nsync/testing/heap.h"
#include "third_party/nsync/testing/smprintf.h" #include "third_party/nsync/testing/smprintf.h"
#include "third_party/nsync/testing/testing.h" #include "third_party/nsync/testing/testing.h"
#include "third_party/nsync/testing/time_extra.h" #include "third_party/nsync/testing/time_extra.h"

View file

@ -18,8 +18,8 @@
#include "libc/fmt/fmt.h" #include "libc/fmt/fmt.h"
#include "libc/mem/mem.h" #include "libc/mem/mem.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "third_party/nsync/array.internal.h"
#include "third_party/nsync/dll.h" #include "third_party/nsync/dll.h"
#include "third_party/nsync/testing/array.h"
#include "third_party/nsync/testing/smprintf.h" #include "third_party/nsync/testing/smprintf.h"
#include "third_party/nsync/testing/testing.h" #include "third_party/nsync/testing/testing.h"
// clang-format off // clang-format off

View file

@ -17,11 +17,11 @@
*/ */
#include "libc/fmt/fmt.h" #include "libc/fmt/fmt.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "third_party/nsync/array.internal.h"
#include "third_party/nsync/heap.internal.h"
#include "third_party/nsync/mu.h" #include "third_party/nsync/mu.h"
#include "third_party/nsync/mu_wait.h" #include "third_party/nsync/mu_wait.h"
#include "third_party/nsync/testing/array.h"
#include "third_party/nsync/testing/closure.h" #include "third_party/nsync/testing/closure.h"
#include "third_party/nsync/testing/heap.h"
#include "third_party/nsync/testing/smprintf.h" #include "third_party/nsync/testing/smprintf.h"
#include "third_party/nsync/testing/testing.h" #include "third_party/nsync/testing/testing.h"
#include "third_party/nsync/testing/time_extra.h" #include "third_party/nsync/testing/time_extra.h"

View file

@ -12,8 +12,8 @@ THIRD_PARTY_NSYNC_TESTING_SRCS_TEST = $(filter %_test.c,$(THIRD_PARTY_NSYNC_TEST
THIRD_PARTY_NSYNC_TESTING_OBJS = $(THIRD_PARTY_NSYNC_TESTING_SRCS:%.c=o/$(MODE)/%.o) THIRD_PARTY_NSYNC_TESTING_OBJS = $(THIRD_PARTY_NSYNC_TESTING_SRCS:%.c=o/$(MODE)/%.o)
THIRD_PARTY_NSYNC_TESTING_COMS = $(THIRD_PARTY_NSYNC_TESTING_SRCS_TEST:%.c=o/$(MODE)/%.com) THIRD_PARTY_NSYNC_TESTING_COMS = $(THIRD_PARTY_NSYNC_TESTING_SRCS_TEST:%.c=o/$(MODE)/%.com)
THIRD_PARTY_NSYNC_TESTING_BINS = $(THIRD_PARTY_NSYNC_TESTING_COMS) $(THIRD_PARTY_NSYNC_TESTING_COMS:%=%.dbg) THIRD_PARTY_NSYNC_TESTING_BINS = $(THIRD_PARTY_NSYNC_TESTING_COMS) $(THIRD_PARTY_NSYNC_TESTING_COMS:%=%.dbg)
# THIRD_PARTY_NSYNC_TESTING_TESTS = $(THIRD_PARTY_NSYNC_TESTING_SRCS_TEST:%.c=o/$(MODE)/%.com.ok) THIRD_PARTY_NSYNC_TESTING_TESTS_ = $(THIRD_PARTY_NSYNC_TESTING_SRCS_TEST:%.c=o/$(MODE)/%.com.ok)
# THIRD_PARTY_NSYNC_TESTING_CHECKS = $(THIRD_PARTY_NSYNC_TESTING_SRCS_TEST:%.c=o/$(MODE)/%.com.runs) THIRD_PARTY_NSYNC_TESTING_CHECKS_ = $(THIRD_PARTY_NSYNC_TESTING_SRCS_TEST:%.c=o/$(MODE)/%.com.runs)
THIRD_PARTY_NSYNC_TESTING_DIRECTDEPS = \ THIRD_PARTY_NSYNC_TESTING_DIRECTDEPS = \
LIBC_CALLS \ LIBC_CALLS \
@ -57,10 +57,10 @@ o/$(MODE)/third_party/nsync/testing/mu_test.com.runs: private QUOTA = -C64
.PHONY: o/$(MODE)/third_party/nsync/testing .PHONY: o/$(MODE)/third_party/nsync/testing
o/$(MODE)/third_party/nsync/testing: \ o/$(MODE)/third_party/nsync/testing: \
$(THIRD_PARTY_NSYNC_TESTING_CHECKS) \ $(THIRD_PARTY_NSYNC_TESTING_CHECKS_) \
$(THIRD_PARTY_NSYNC_TESTING_BINS) $(THIRD_PARTY_NSYNC_TESTING_BINS_)
.PHONY: o/$(MODE)/third_party/nsync/test .PHONY: o/$(MODE)/third_party/nsync/test
o/$(MODE)/third_party/nsync/test: \ o/$(MODE)/third_party/nsync/test: \
$(THIRD_PARTY_NSYNC_TESTING_CHECKS) \ $(THIRD_PARTY_NSYNC_TESTING_CHECKS_) \
$(THIRD_PARTY_NSYNC_TESTING_TESTS) $(THIRD_PARTY_NSYNC_TESTING_TESTS_)

View file

@ -41,20 +41,6 @@ char *nsync_time_str (nsync_time t, int decimals) {
return (smprintf ("%.*f%s", decimals, s/scale[i].multiplier, scale[i].suffix)); return (smprintf ("%.*f%s", decimals, s/scale[i].multiplier, scale[i].suffix));
} }
int nsync_time_sleep_until (nsync_time abs_deadline) {
int result = 0;
nsync_time now;
now = nsync_time_now ();
if (nsync_time_cmp (abs_deadline, now) > 0) {
nsync_time remaining;
remaining = nsync_time_sleep (nsync_time_sub (abs_deadline, now));
if (nsync_time_cmp (remaining, nsync_time_zero) > 0) {
result = EINTR;
}
}
return (result);
}
double nsync_time_to_dbl (nsync_time t) { double nsync_time_to_dbl (nsync_time t) {
return (((double) NSYNC_TIME_SEC (t)) + ((double) NSYNC_TIME_NSEC (t) * 1e-9)); return (((double) NSYNC_TIME_SEC (t)) + ((double) NSYNC_TIME_NSEC (t) * 1e-9));
} }

View file

@ -6,10 +6,6 @@
"decimals" decimal places. */ "decimals" decimal places. */
char *nsync_time_str(nsync_time t, int decimals); char *nsync_time_str(nsync_time t, int decimals);
/* Sleep until the specified time. Returns 0 on success, and EINTR
if the call was interrupted. */
int nsync_time_sleep_until(nsync_time abs_deadline);
/* Return t as a double. */ /* Return t as a double. */
double nsync_time_to_dbl(nsync_time t); double nsync_time_to_dbl(nsync_time t);

View file

@ -16,9 +16,9 @@
limitations under the License. limitations under the License.
*/ */
#include "libc/str/str.h" #include "libc/str/str.h"
#include "third_party/nsync/array.internal.h"
#include "third_party/nsync/counter.h" #include "third_party/nsync/counter.h"
#include "third_party/nsync/note.h" #include "third_party/nsync/note.h"
#include "third_party/nsync/testing/array.h"
#include "third_party/nsync/testing/closure.h" #include "third_party/nsync/testing/closure.h"
#include "third_party/nsync/testing/smprintf.h" #include "third_party/nsync/testing/smprintf.h"
#include "third_party/nsync/testing/testing.h" #include "third_party/nsync/testing/testing.h"

View file

@ -1,54 +0,0 @@
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│
vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi
Copyright 2016 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0 │
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "libc/calls/struct/timespec.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/clock.h"
#include "third_party/nsync/time.h"
asm(".ident\t\"\\n\\n\
*NSYNC (Apache 2.0)\\n\
Copyright 2016 Google, Inc.\\n\
https://github.com/google/nsync\"");
// clang-format off
#define NSYNC_NS_IN_S_ (1000 * 1000 * 1000)
/* Return the maximum t, assuming it's an integral
type, and the representation is not too strange. */
#define MAX_INT_TYPE(t) (((t)~(t)0) > 1? /*is t unsigned?*/ \
(t)~(t)0 : /*unsigned*/ \
(t) ((((uintmax_t)1) << (sizeof (t) * CHAR_BIT - 1)) - 1)) /*signed*/
const nsync_time nsync_time_no_deadline =
NSYNC_TIME_STATIC_INIT (MAX_INT_TYPE (int64_t), NSYNC_NS_IN_S_ - 1);
const nsync_time nsync_time_zero = NSYNC_TIME_STATIC_INIT (0, 0);
nsync_time nsync_time_sleep (nsync_time delay) {
struct timespec ts;
struct timespec remain;
memset (&ts, 0, sizeof (ts));
ts.tv_sec = NSYNC_TIME_SEC (delay);
ts.tv_nsec = NSYNC_TIME_NSEC (delay);
if (nanosleep (&ts, &remain) == 0) {
/* nanosleep() is not required to fill in "remain"
if it returns 0. */
memset (&remain, 0, sizeof (remain));
}
return (remain);
}

View file

@ -17,17 +17,21 @@ COSMOPOLITAN_C_START_
typedef struct timespec nsync_time; typedef struct timespec nsync_time;
/* A deadline infinitely far in the future. */ /* A deadline infinitely far in the future. */
extern const nsync_time nsync_time_no_deadline; #define nsync_time_no_deadline _timespec_max
/* The zero delay, or an expired deadline. */ /* The zero delay, or an expired deadline. */
extern const nsync_time nsync_time_zero; #define nsync_time_zero _timespec_zero
/* Return the current time since the epoch. */ /* Return the current time since the epoch. */
#define nsync_time_now() _timespec_real() #define nsync_time_now() _timespec_real()
/* Sleep for the specified delay. Returns the unslept time which may be /* Sleep for the specified delay. Returns the unslept time which may be
non-zero if the call was interrupted. */ non-zero if the call was interrupted. */
nsync_time nsync_time_sleep(nsync_time delay); #define nsync_time_sleep(a) _timespec_sleep(a)
/* Sleep until the specified time. Returns 0 on success, and EINTR
if the call was interrupted. */
#define nsync_time_sleep_until(a) _timespec_sleep_until(a)
/* Return a+b */ /* Return a+b */
#define nsync_time_add(a, b) _timespec_add(a, b) #define nsync_time_add(a, b) _timespec_add(a, b)
@ -44,6 +48,9 @@ nsync_time nsync_time_sleep(nsync_time delay);
/* Return the specified number of microseconds as a time. */ /* Return the specified number of microseconds as a time. */
#define nsync_time_us(a) _timespec_frommicros(a) #define nsync_time_us(a) _timespec_frommicros(a)
/* Return the specified number of nanoseconds as a time. */
#define nsync_time_ns(a) _timespec_fromnanos(a)
/* Return an nsync_time constructed from second and nanosecond /* Return an nsync_time constructed from second and nanosecond
components */ components */
#define nsync_time_s_ns(s, ns) ((nsync_time){(int64_t)(s), (unsigned)(ns)}) #define nsync_time_s_ns(s, ns) ((nsync_time){(int64_t)(s), (unsigned)(ns)})