Introduce pthread_condattr_setclock()

This is one of the few POSIX APIs that was missing. It lets you choose a
monotonic clock for your condition variables. This might improve perf on
some platforms. It might also grant more flexibility with NTP configs. I
know Qt is one project that believes it needs this. To introduce this, I
needed to change some the *NSYNC APIs, to support passing a clock param.
There's also new benchmarks, demonstrating Cosmopolitan's supremacy over
many libc implementations when it comes to mutex performance. Cygwin has
an alarmingly bad pthread_mutex_t implementation. It is so bad that they
would have been significantly better off if they'd used naive spinlocks.
This commit is contained in:
Justine Tunney 2024-09-02 23:37:50 -07:00
parent 79516bf08e
commit 3c61a541bd
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
55 changed files with 449 additions and 155 deletions

View file

@ -110,6 +110,7 @@
#include "third_party/lua/lua.h"
#include "third_party/lua/luaconf.h"
#include "third_party/nsync/futex.internal.h"
#include "libc/sysv/consts/clock.h"
#include "tool/net/luacheck.h"
#define DNS_NAME_MAX 253
@ -2855,7 +2856,7 @@ static int LuaUnixMemoryWait(lua_State *L) {
}
BEGIN_CANCELATION_POINT;
rc = nsync_futex_wait_((atomic_int *)GetWord(L), expect,
PTHREAD_PROCESS_SHARED, deadline);
PTHREAD_PROCESS_SHARED, CLOCK_REALTIME, deadline);
END_CANCELATION_POINT;
if (rc < 0) errno = -rc, rc = -1;
return SysretInteger(L, "futex_wait", olderr, rc);

View file

@ -17,6 +17,8 @@ LOCAL CHANGES
- Fix nsync_mu_unlock() on Apple Silicon
- Add clock parameter to many NSYNC wait APIs
- Time APIs were so good that they're now in libc
- Double linked list API was so good that it's now in libc

View file

@ -266,7 +266,7 @@ void nsync_mu_unlock_slow_(nsync_mu *mu, lock_type *l_type);
struct Dll *nsync_remove_from_mu_queue_(struct Dll *mu_queue, struct Dll *e);
void nsync_maybe_merge_conditions_(struct Dll *p, struct Dll *n);
nsync_time nsync_note_notified_deadline_(nsync_note n);
int nsync_sem_wait_with_cancel_(waiter *w, nsync_time abs_deadline,
int nsync_sem_wait_with_cancel_(waiter *w, int clock, nsync_time abs_deadline,
nsync_note cancel_note);
COSMOPOLITAN_C_END_

View file

@ -144,7 +144,7 @@ int nsync_cv_wait(nsync_cv *cv, nsync_mu *mu);
mostly in tests and trivial examples than they are in real
programmes. */
int nsync_cv_wait_with_deadline(nsync_cv *cv, nsync_mu *mu,
nsync_time abs_deadline,
int clock, nsync_time abs_deadline,
struct nsync_note_s_ *cancel_note);
/* Like nsync_cv_wait_with_deadline(), but allow an arbitrary lock *v to be
@ -152,7 +152,7 @@ int nsync_cv_wait_with_deadline(nsync_cv *cv, nsync_mu *mu,
int nsync_cv_wait_with_deadline_generic(nsync_cv *cv, void *mu,
void (*lock)(void *),
void (*unlock)(void *),
nsync_time abs_deadline,
int clock, nsync_time abs_deadline,
struct nsync_note_s_ *cancel_note);
COSMOPOLITAN_C_END_

View file

@ -52,7 +52,6 @@
#include "third_party/nsync/atomic.h"
#include "third_party/nsync/common.internal.h"
#include "third_party/nsync/futex.internal.h"
#include "libc/intrin/kprintf.h"
#include "third_party/nsync/time.h"
#define FUTEX_WAIT_BITS_ FUTEX_BITSET_MATCH_ANY
@ -65,6 +64,7 @@ static struct NsyncFutex {
atomic_uint once;
int FUTEX_WAIT_;
int FUTEX_PRIVATE_FLAG_;
int FUTEX_CLOCK_REALTIME_;
bool is_supported;
bool timeout_is_relative;
} nsync_futex_;
@ -92,9 +92,8 @@ static void nsync_futex_init_ (void) {
return;
}
if (!(nsync_futex_.is_supported = IsLinux () || IsOpenbsd ())) {
if (!(nsync_futex_.is_supported = IsLinux () || IsOpenbsd ()))
return;
}
// In our testing, we found that the monotonic clock on various
// popular systems (such as Linux, and some BSD variants) was no
@ -111,16 +110,11 @@ static void nsync_futex_init_ (void) {
if (IsLinux () &&
_futex (&x, FUTEX_WAIT_BITSET | FUTEX_CLOCK_REALTIME,
1, 0, 0, FUTEX_BITSET_MATCH_ANY) == -EAGAIN) {
nsync_futex_.FUTEX_WAIT_ =
FUTEX_WAIT_BITSET | FUTEX_CLOCK_REALTIME;
nsync_futex_.FUTEX_PRIVATE_FLAG_ = FUTEX_PRIVATE_FLAG;
} else if (!IsTiny () && IsLinux () &&
_futex (&x, FUTEX_WAIT_BITSET, 1, 0, 0,
FUTEX_BITSET_MATCH_ANY) == -EAGAIN) {
nsync_futex_.FUTEX_WAIT_ = FUTEX_WAIT_BITSET;
nsync_futex_.FUTEX_PRIVATE_FLAG_ = FUTEX_PRIVATE_FLAG;
nsync_futex_.FUTEX_CLOCK_REALTIME_ = FUTEX_CLOCK_REALTIME;
} else if (IsOpenbsd () ||
(!IsTiny () && IsLinux () &&
(IsLinux () &&
!_futex_wake (&x, FUTEX_WAKE_PRIVATE, 1))) {
nsync_futex_.FUTEX_WAIT_ = FUTEX_WAIT;
nsync_futex_.FUTEX_PRIVATE_FLAG_ = FUTEX_PRIVATE_FLAG;
@ -132,24 +126,24 @@ static void nsync_futex_init_ (void) {
errno = e;
}
static int nsync_futex_polyfill_ (atomic_int *w, int expect, struct timespec *abstime) {
static int nsync_futex_polyfill_ (atomic_int *w, int expect, int clock, struct timespec *abstime) {
for (;;) {
if (atomic_load_explicit (w, memory_order_acquire) != expect) {
if (atomic_load_explicit (w, memory_order_acquire) != expect)
return 0;
}
if (_weaken (pthread_testcancel_np) &&
_weaken (pthread_testcancel_np) ()) {
_weaken (pthread_testcancel_np) ())
return -ECANCELED;
}
if (abstime && timespec_cmp (timespec_real (), *abstime) >= 0) {
struct timespec now;
if (clock_gettime (clock, &now))
return -EINVAL;
if (abstime && timespec_cmp (now, *abstime) >= 0)
return -ETIMEDOUT;
}
pthread_yield_np ();
}
}
static int nsync_futex_wait_win32_ (atomic_int *w, int expect, char pshare,
const struct timespec *timeout,
int clock, const struct timespec *timeout,
struct PosixThread *pt,
sigset_t waitmask) {
#ifdef __x86_64__
@ -164,23 +158,20 @@ static int nsync_futex_wait_win32_ (atomic_int *w, int expect, char pshare,
}
for (;;) {
now = timespec_real ();
if (timespec_cmp (now, deadline) >= 0) {
return etimedout();
}
if (clock_gettime (clock, &now))
return einval ();
if (timespec_cmp (now, deadline) >= 0)
return etimedout ();
wait = timespec_sub (deadline, now);
if (atomic_load_explicit (w, memory_order_acquire) != expect) {
if (atomic_load_explicit (w, memory_order_acquire) != expect)
return 0;
}
if (pt) {
if (_check_cancel () == -1) {
if (_check_cancel () == -1)
return -1; /* ECANCELED */
}
if ((sig = __sig_get (waitmask))) {
__sig_relay (sig, SI_KERNEL, waitmask);
if (_check_cancel () == -1) {
if (_check_cancel () == -1)
return -1; /* ECANCELED */
}
return eintr ();
}
pt->pt_blkmask = waitmask;
@ -192,9 +183,8 @@ static int nsync_futex_wait_win32_ (atomic_int *w, int expect, char pshare,
atomic_store_explicit (&pt->pt_blocker, 0, memory_order_release);
if (ok && atomic_load_explicit (w, memory_order_acquire) == expect && (sig = __sig_get (waitmask))) {
__sig_relay (sig, SI_KERNEL, waitmask);
if (_check_cancel () == -1) {
if (_check_cancel () == -1)
return -1; /* ECANCELED */
}
return eintr ();
}
}
@ -209,33 +199,41 @@ static int nsync_futex_wait_win32_ (atomic_int *w, int expect, char pshare,
#endif /* __x86_64__ */
}
static struct timespec *nsync_futex_timeout_ (struct timespec *memory,
const struct timespec *abstime) {
static int nsync_futex_fix_timeout_ (struct timespec *memory, int clock,
const struct timespec *abstime,
struct timespec **result) {
struct timespec now;
if (!abstime) {
*result = 0;
return 0;
} else if (!nsync_futex_.timeout_is_relative) {
*memory = *abstime;
return memory;
*result = memory;
return 0;
} else {
now = timespec_real ();
if (clock_gettime (clock, &now))
return -EINVAL;
*memory = timespec_subz (*abstime, now);
return memory;
*result = memory;
return 0;
}
}
int nsync_futex_wait_ (atomic_int *w, int expect, char pshare, const struct timespec *abstime) {
int nsync_futex_wait_ (atomic_int *w, int expect, char pshare,
int clock, const struct timespec *abstime) {
int e, rc, op;
struct CosmoTib *tib;
struct PosixThread *pt;
struct timespec tsmem, *timeout;
struct timespec tsmem;
struct timespec *timeout = 0;
cosmo_once (&nsync_futex_.once, nsync_futex_init_);
op = nsync_futex_.FUTEX_WAIT_;
if (pshare == PTHREAD_PROCESS_PRIVATE) {
if (pshare == PTHREAD_PROCESS_PRIVATE)
op |= nsync_futex_.FUTEX_PRIVATE_FLAG_;
}
if (clock == CLOCK_REALTIME)
op |= nsync_futex_.FUTEX_CLOCK_REALTIME_;
if (abstime && timespec_cmp (*abstime, timespec_zero) <= 0) {
rc = -ETIMEDOUT;
@ -247,7 +245,8 @@ int nsync_futex_wait_ (atomic_int *w, int expect, char pshare, const struct time
goto Finished;
}
timeout = nsync_futex_timeout_ (&tsmem, abstime);
if ((rc = nsync_futex_fix_timeout_ (&tsmem, clock, abstime, &timeout)))
goto Finished;
LOCKTRACE ("futex(%t [%d], %s, %#x, %s) → ...",
w, atomic_load_explicit (w, memory_order_relaxed),
@ -263,7 +262,7 @@ int nsync_futex_wait_ (atomic_int *w, int expect, char pshare, const struct time
// Windows 8 futexes don't support multiple processes :(
if (pshare) goto Polyfill;
sigset_t m = __sig_block ();
rc = nsync_futex_wait_win32_ (w, expect, pshare, timeout, pt, m);
rc = nsync_futex_wait_win32_ (w, expect, pshare, clock, timeout, pt, m);
__sig_unblock (m);
} else if (IsXnu ()) {
uint32_t op, us;
@ -280,7 +279,7 @@ int nsync_futex_wait_ (atomic_int *w, int expect, char pshare, const struct time
rc = ulock_wait (op, w, expect, us);
if (rc > 0) rc = 0; // don't care about #waiters
} else if (IsFreebsd ()) {
rc = sys_umtx_timedwait_uint (w, expect, pshare, timeout);
rc = sys_umtx_timedwait_uint (w, expect, pshare, clock, timeout);
} else {
if (IsOpenbsd()) {
// OpenBSD 6.8 futex() returns errors as
@ -313,7 +312,7 @@ int nsync_futex_wait_ (atomic_int *w, int expect, char pshare, const struct time
}
} else {
Polyfill:
rc = nsync_futex_polyfill_ (w, expect, timeout);
rc = nsync_futex_polyfill_ (w, expect, clock, timeout);
}
Finished:
@ -334,9 +333,8 @@ int nsync_futex_wake_ (atomic_int *w, int count, char pshare) {
cosmo_once (&nsync_futex_.once, nsync_futex_init_);
op = FUTEX_WAKE;
if (pshare == PTHREAD_PROCESS_PRIVATE) {
if (pshare == PTHREAD_PROCESS_PRIVATE)
op |= nsync_futex_.FUTEX_PRIVATE_FLAG_;
}
if (nsync_futex_.is_supported) {
if (IsWindows ()) {

View file

@ -11,7 +11,7 @@ COSMOPOLITAN_C_START_
#endif
int nsync_futex_wake_(_FUTEX_ATOMIC(int) *, int, char);
int nsync_futex_wait_(_FUTEX_ATOMIC(int) *, int, char, const struct timespec *);
int nsync_futex_wait_(_FUTEX_ATOMIC(int) *, int, char, int, const struct timespec *);
COSMOPOLITAN_C_END_
#endif /* NSYNC_FUTEX_INTERNAL_H_ */

View file

@ -25,6 +25,7 @@
#include "third_party/nsync/mu_semaphore.h"
#include "third_party/nsync/races.internal.h"
#include "third_party/nsync/wait_s.internal.h"
#include "libc/sysv/consts/clock.h"
#include "third_party/nsync/waiter.h"
__static_yoink("nsync_notice");
@ -100,7 +101,7 @@ uint32_t nsync_counter_wait (nsync_counter c, nsync_time abs_deadline) {
uint32_t result = 0;
waitable.v = c;
waitable.funcs = &nsync_counter_waitable_funcs;
if (nsync_wait_n (NULL, NULL, NULL, abs_deadline, 1, &pwaitable) != 0) {
if (nsync_wait_n (NULL, NULL, NULL, CLOCK_REALTIME, abs_deadline, 1, &pwaitable) != 0) {
IGNORE_RACES_START ();
result = ATM_LOAD_ACQ (&c->value);
IGNORE_RACES_END ();

View file

@ -175,6 +175,7 @@ struct nsync_cv_wait_with_deadline_s {
void *pmu;
void (*lock) (void *);
nsync_mu *cv_mu;
int clock;
nsync_time abs_deadline;
nsync_note cancel_note;
waiter *w;
@ -187,7 +188,7 @@ static int nsync_cv_wait_with_deadline_impl_ (struct nsync_cv_wait_with_deadline
IGNORE_RACES_START ();
while (ATM_LOAD_ACQ (&c->w->nw.waiting) != 0) { /* acquire load */
if (c->sem_outcome == 0) {
c->sem_outcome = nsync_sem_wait_with_cancel_ (c->w, c->abs_deadline, c->cancel_note);
c->sem_outcome = nsync_sem_wait_with_cancel_ (c->w, c->clock, c->abs_deadline, c->cancel_note);
}
if (c->sem_outcome != 0 && ATM_LOAD (&c->w->nw.waiting) != 0) {
/* A timeout or cancellation occurred, and no wakeup.
@ -278,13 +279,14 @@ static void nsync_cv_wait_with_deadline_unwind_ (void *arg) {
programmes. */
int nsync_cv_wait_with_deadline_generic (nsync_cv *pcv, void *pmu,
void (*lock) (void *), void (*unlock) (void *),
nsync_time abs_deadline,
int clock, nsync_time abs_deadline,
nsync_note cancel_note) {
int outcome;
struct nsync_cv_wait_with_deadline_s c;
IGNORE_RACES_START ();
c.w = nsync_waiter_new_ ();
c.clock = clock;
c.abs_deadline = abs_deadline;
c.cancel_note = cancel_note;
c.cv_mu = NULL;
@ -470,10 +472,10 @@ void nsync_cv_broadcast (nsync_cv *pcv) {
/* Wait with deadline, using an nsync_mu. */
errno_t nsync_cv_wait_with_deadline (nsync_cv *pcv, nsync_mu *pmu,
nsync_time abs_deadline,
int clock, nsync_time abs_deadline,
nsync_note cancel_note) {
return (nsync_cv_wait_with_deadline_generic (pcv, pmu, &void_mu_lock,
&void_mu_unlock,
&void_mu_unlock, clock,
abs_deadline, cancel_note));
}
@ -486,7 +488,7 @@ errno_t nsync_cv_wait_with_deadline (nsync_cv *pcv, nsync_mu *pmu,
ECANCELED may be returned if calling POSIX thread is cancelled only when
the PTHREAD_CANCEL_MASKED mode is in play. */
errno_t nsync_cv_wait (nsync_cv *pcv, nsync_mu *pmu) {
return nsync_cv_wait_with_deadline (pcv, pmu, nsync_time_no_deadline, NULL);
return nsync_cv_wait_with_deadline (pcv, pmu, 0, nsync_time_no_deadline, NULL);
}
static nsync_time cv_ready_time (void *v, struct nsync_waiter_s *nw) {

View file

@ -141,7 +141,7 @@ int nsync_mu_wait_with_deadline (nsync_mu *mu,
int (*condition) (const void *condition_arg),
const void *condition_arg,
int (*condition_arg_eq) (const void *a, const void *b),
nsync_time abs_deadline, nsync_note cancel_note) {
int clock, nsync_time abs_deadline, nsync_note cancel_note) {
lock_type *l_type;
int first_wait;
int condition_is_true;
@ -231,7 +231,7 @@ int nsync_mu_wait_with_deadline (nsync_mu *mu,
have_lock = 0;
while (ATM_LOAD_ACQ (&w->nw.waiting) != 0) { /* acquire load */
if (sem_outcome == 0) {
sem_outcome = nsync_sem_wait_with_cancel_ (w, abs_deadline,
sem_outcome = nsync_sem_wait_with_cancel_ (w, clock, abs_deadline,
cancel_note);
if (sem_outcome != 0 && ATM_LOAD (&w->nw.waiting) != 0) {
/* A timeout or cancellation occurred, and no wakeup.
@ -280,7 +280,7 @@ void nsync_mu_wait (nsync_mu *mu, int (*condition) (const void *condition_arg),
const void *condition_arg,
int (*condition_arg_eq) (const void *a, const void *b)) {
if (nsync_mu_wait_with_deadline (mu, condition, condition_arg, condition_arg_eq,
nsync_time_no_deadline, NULL) != 0) {
0, nsync_time_no_deadline, NULL) != 0) {
nsync_panic_ ("nsync_mu_wait woke but condition not true\n");
}
}

View file

@ -24,6 +24,7 @@
#include "third_party/nsync/mu_wait.h"
#include "third_party/nsync/races.internal.h"
#include "third_party/nsync/wait_s.internal.h"
#include "libc/sysv/consts/clock.h"
#include "third_party/nsync/waiter.h"
__static_yoink("nsync_notice");
@ -247,7 +248,7 @@ int nsync_note_wait (nsync_note n, nsync_time abs_deadline) {
struct nsync_waitable_s *pwaitable = &waitable;
waitable.v = n;
waitable.funcs = &nsync_note_waitable_funcs;
return (nsync_wait_n (NULL, NULL, NULL, abs_deadline, 1, &pwaitable) == 0);
return (nsync_wait_n (NULL, NULL, NULL, CLOCK_REALTIME, abs_deadline, 1, &pwaitable) == 0);
}
nsync_time nsync_note_expiry (nsync_note n) {

View file

@ -22,6 +22,7 @@
#include "third_party/nsync/once.h"
#include "third_party/nsync/races.internal.h"
#include "libc/thread/thread.h"
#include "libc/sysv/consts/clock.h"
#include "third_party/nsync/wait_s.internal.h"
__static_yoink("nsync_notice");
@ -91,7 +92,7 @@ static void nsync_run_once_impl (nsync_once *once, struct once_sync_s *s,
attempts += 10;
}
deadline = nsync_time_add (nsync_time_now (), nsync_time_ms (attempts));
nsync_cv_wait_with_deadline (&s->once_cv, &s->once_mu, deadline, NULL);
nsync_cv_wait_with_deadline (&s->once_cv, &s->once_mu, CLOCK_REALTIME, deadline, NULL);
} else {
attempts = pthread_delay_np (once, attempts);
}

View file

@ -29,11 +29,11 @@ __static_yoink("nsync_notice");
w->sem is non-zero----decrement it and return 0.
abs_deadline expires---return ETIMEDOUT.
cancel_note is non-NULL and *cancel_note becomes notified---return ECANCELED. */
int nsync_sem_wait_with_cancel_ (waiter *w, nsync_time abs_deadline,
int nsync_sem_wait_with_cancel_ (waiter *w, int clock, nsync_time abs_deadline,
nsync_note cancel_note) {
int sem_outcome;
if (cancel_note == NULL) {
sem_outcome = nsync_mu_semaphore_p_with_deadline (&w->sem, abs_deadline);
sem_outcome = nsync_mu_semaphore_p_with_deadline (&w->sem, clock, abs_deadline);
} else {
nsync_time cancel_time;
cancel_time = nsync_note_notified_deadline_ (cancel_note);
@ -58,7 +58,7 @@ int nsync_sem_wait_with_cancel_ (waiter *w, nsync_time abs_deadline,
}
nsync_mu_unlock (&cancel_note->note_mu);
sem_outcome = nsync_mu_semaphore_p_with_deadline (&w->sem,
local_abs_deadline);
clock, local_abs_deadline);
if (sem_outcome == ETIMEDOUT && !deadline_is_nearer) {
sem_outcome = ECANCELED;
nsync_note_notify (cancel_note);

View file

@ -28,7 +28,7 @@
__static_yoink("nsync_notice");
int nsync_wait_n (void *mu, void (*lock) (void *), void (*unlock) (void *),
nsync_time abs_deadline,
int clock, nsync_time abs_deadline,
int count, struct nsync_waitable_s *waitable[]) {
int ready;
IGNORE_RACES_START ();
@ -77,7 +77,7 @@ int nsync_wait_n (void *mu, void (*lock) (void *), void (*unlock) (void *),
}
} while (nsync_time_cmp (min_ntime, nsync_time_zero) > 0 &&
nsync_mu_semaphore_p_with_deadline (&w->sem,
min_ntime) == 0);
clock, min_ntime) == 0);
}
/* An attempt was made above to enqueue waitable[0..i-1].

View file

@ -54,15 +54,15 @@ errno_t nsync_mu_semaphore_p (nsync_semaphore *s) {
while additionally supporting a time parameter specifying at what point
in the future ETIMEDOUT should be returned, if neither cancelation, or
semaphore release happens. */
errno_t nsync_mu_semaphore_p_with_deadline (nsync_semaphore *s, nsync_time abs_deadline) {
errno_t nsync_mu_semaphore_p_with_deadline (nsync_semaphore *s, int clock, nsync_time abs_deadline) {
errno_t err;
BEGIN_CANCELATION_POINT;
if (NSYNC_USE_GRAND_CENTRAL && IsXnuSilicon ()) {
err = nsync_mu_semaphore_p_with_deadline_gcd (s, abs_deadline);
err = nsync_mu_semaphore_p_with_deadline_gcd (s, clock, abs_deadline);
} else if (IsNetbsd ()) {
err = nsync_mu_semaphore_p_with_deadline_sem (s, abs_deadline);
err = nsync_mu_semaphore_p_with_deadline_sem (s, clock, abs_deadline);
} else {
err = nsync_mu_semaphore_p_with_deadline_futex (s, abs_deadline);
err = nsync_mu_semaphore_p_with_deadline_futex (s, clock, abs_deadline);
}
END_CANCELATION_POINT;
return err;

View file

@ -16,7 +16,7 @@ errno_t nsync_mu_semaphore_p(nsync_semaphore *s);
/* 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. */
errno_t nsync_mu_semaphore_p_with_deadline(nsync_semaphore *s,
errno_t nsync_mu_semaphore_p_with_deadline(nsync_semaphore *s, int clock,
nsync_time abs_deadline);
/* Ensure that the count of *s is at least 1. */

View file

@ -20,17 +20,17 @@ COSMOPOLITAN_C_START_
bool nsync_mu_semaphore_init_futex(nsync_semaphore *);
errno_t nsync_mu_semaphore_p_futex(nsync_semaphore *);
errno_t nsync_mu_semaphore_p_with_deadline_futex(nsync_semaphore *, nsync_time);
errno_t nsync_mu_semaphore_p_with_deadline_futex(nsync_semaphore *, int, nsync_time);
void nsync_mu_semaphore_v_futex(nsync_semaphore *);
bool nsync_mu_semaphore_init_sem(nsync_semaphore *);
errno_t nsync_mu_semaphore_p_sem(nsync_semaphore *);
errno_t nsync_mu_semaphore_p_with_deadline_sem(nsync_semaphore *, nsync_time);
errno_t nsync_mu_semaphore_p_with_deadline_sem(nsync_semaphore *, int, nsync_time);
void nsync_mu_semaphore_v_sem(nsync_semaphore *);
bool nsync_mu_semaphore_init_gcd(nsync_semaphore *);
errno_t nsync_mu_semaphore_p_gcd(nsync_semaphore *);
errno_t nsync_mu_semaphore_p_with_deadline_gcd(nsync_semaphore *, nsync_time);
errno_t nsync_mu_semaphore_p_with_deadline_gcd(nsync_semaphore *, int, nsync_time);
void nsync_mu_semaphore_v_gcd(nsync_semaphore *);
COSMOPOLITAN_C_END_

View file

@ -63,7 +63,7 @@ errno_t nsync_mu_semaphore_p_futex (nsync_semaphore *s) {
int futex_result;
futex_result = -nsync_futex_wait_ (
(atomic_int *)&f->i, i,
PTHREAD_PROCESS_PRIVATE, 0);
PTHREAD_PROCESS_PRIVATE, 0, 0);
ASSERT (futex_result == 0 ||
futex_result == EINTR ||
futex_result == EAGAIN ||
@ -81,7 +81,7 @@ errno_t nsync_mu_semaphore_p_futex (nsync_semaphore *s) {
while additionally supporting a time parameter specifying at what point
in the future ETIMEDOUT should be returned, if neither cancellation, or
semaphore release happens. */
errno_t nsync_mu_semaphore_p_with_deadline_futex (nsync_semaphore *s, nsync_time abs_deadline) {
errno_t nsync_mu_semaphore_p_with_deadline_futex (nsync_semaphore *s, int clock, nsync_time abs_deadline) {
struct futex *f = (struct futex *)s;
int i;
int result = 0;
@ -98,7 +98,8 @@ errno_t nsync_mu_semaphore_p_with_deadline_futex (nsync_semaphore *s, nsync_time
ts = &ts_buf;
}
futex_result = nsync_futex_wait_ ((atomic_int *)&f->i, i,
PTHREAD_PROCESS_PRIVATE, ts);
PTHREAD_PROCESS_PRIVATE,
clock, ts);
ASSERT (futex_result == 0 ||
futex_result == -EINTR ||
futex_result == -EAGAIN ||
@ -106,9 +107,12 @@ errno_t nsync_mu_semaphore_p_with_deadline_futex (nsync_semaphore *s, nsync_time
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;
if (futex_result == -ETIMEDOUT) {
nsync_time now;
if (clock_gettime (clock, &now))
result = EINVAL;
if (nsync_time_cmp (now, abs_deadline) >= 0)
result = ETIMEDOUT;
}
if (futex_result == -ECANCELED) {
result = ECANCELED;

View file

@ -94,14 +94,14 @@ bool nsync_mu_semaphore_init_gcd (nsync_semaphore *s) {
they're enabled in MASKED mode, this function may return ECANCELED. Otherwise,
cancellation will occur by unwinding cleanup handlers pushed to the stack. */
errno_t nsync_mu_semaphore_p_gcd (nsync_semaphore *s) {
return nsync_mu_semaphore_p_with_deadline_gcd (s, nsync_time_no_deadline);
return nsync_mu_semaphore_p_with_deadline_gcd (s, 0, nsync_time_no_deadline);
}
/* Like nsync_mu_semaphore_p() this waits for the count of *s to exceed 0,
while additionally supporting a time parameter specifying at what point
in the future ETIMEDOUT should be returned, if neither cancellation, or
semaphore release happens. */
errno_t nsync_mu_semaphore_p_with_deadline_gcd (nsync_semaphore *s,
errno_t nsync_mu_semaphore_p_with_deadline_gcd (nsync_semaphore *s, int clock,
nsync_time abs_deadline) {
errno_t result = 0;
struct PosixThread *pt;
@ -117,7 +117,10 @@ errno_t nsync_mu_semaphore_p_with_deadline_gcd (nsync_semaphore *s,
result = ECANCELED;
break;
}
now = timespec_real();
if (clock_gettime (clock, &now)) {
result = EINVAL;
break;
}
if (timespec_cmp (now, abs_deadline) >= 0) {
result = ETIMEDOUT;
break;

View file

@ -33,6 +33,7 @@
#include "third_party/nsync/mu_semaphore.h"
#include "libc/intrin/atomic.h"
#include "libc/atomic.h"
#include "libc/sysv/consts/clock.h"
#include "third_party/nsync/time.h"
/**
@ -126,10 +127,22 @@ errno_t nsync_mu_semaphore_p_sem (nsync_semaphore *s) {
while additionally supporting a time parameter specifying at what point
in the future ETIMEDOUT should be returned, if neither cancellation, or
semaphore release happens. */
errno_t nsync_mu_semaphore_p_with_deadline_sem (nsync_semaphore *s, nsync_time abs_deadline) {
errno_t nsync_mu_semaphore_p_with_deadline_sem (nsync_semaphore *s, int clock,
nsync_time abs_deadline) {
int e, rc;
errno_t result;
struct sem *f = (struct sem *) s;
// convert monotonic back to realtime just for netbsd
if (clock && nsync_time_cmp (abs_deadline, nsync_time_no_deadline)) {
struct timespec now, delta;
if (clock_gettime (clock, &now))
return EINVAL;
delta = timespec_subz (abs_deadline, now);
clock_gettime (CLOCK_REALTIME, &now);
abs_deadline = timespec_add (now, delta);
}
e = errno;
rc = sys_sem_timedwait (f->id, &abs_deadline);
STRACE ("sem_timedwait(%ld, %s) → %d% m", f->id,

View file

@ -97,7 +97,7 @@ void nsync_mu_wait(nsync_mu *mu, int (*condition)(const void *condition_arg),
int nsync_mu_wait_with_deadline(
nsync_mu *mu, int (*condition)(const void *condition_arg),
const void *condition_arg,
int (*condition_arg_eq)(const void *a, const void *b),
int (*condition_arg_eq)(const void *a, const void *b), int clock,
nsync_time abs_deadline, struct nsync_note_s_ *cancel_note);
/* Unlock *mu, which must be held in write mode, and wake waiters, if

View file

@ -22,6 +22,7 @@
#include "third_party/nsync/mu_wait.h"
#include "third_party/nsync/testing/closure.h"
#include "third_party/nsync/testing/smprintf.h"
#include "libc/sysv/consts/clock.h"
#include "third_party/nsync/testing/testing.h"
/* A cv_stress_data represents the data used by the threads of the tests below. */
@ -79,7 +80,7 @@ static void cv_stress_inc_loop (cv_stress_data *s, uintmax_t count_imod4) {
nsync_time_us (rand () % STRESS_MAX_DELAY_MICROS));
while (nsync_cv_wait_with_deadline (
&s->count_is_imod4[count_imod4],
&s->mu, abs_deadline, NULL) != 0 &&
&s->mu, CLOCK_REALTIME, abs_deadline, NULL) != 0 &&
(s->count&3) != count_imod4) {
nsync_mu_assert_held (&s->mu);
s->timeouts++;
@ -130,7 +131,8 @@ static void cv_stress_reader_loop (cv_stress_data *s, uintmax_t count_imod4) {
abs_deadline = nsync_time_add (nsync_time_now (),
nsync_time_us (rand () % STRESS_MAX_DELAY_MICROS));
while (nsync_cv_wait_with_deadline (&s->count_is_imod4[count_imod4],
&s->mu, abs_deadline, NULL) != 0 &&
&s->mu, CLOCK_REALTIME,
abs_deadline, NULL) != 0 &&
(s->count&3) != count_imod4 && s->refs != 0) {
nsync_mu_rassert_held (&s->mu);

View file

@ -29,6 +29,7 @@
#include "third_party/nsync/testing/smprintf.h"
#include "third_party/nsync/testing/testing.h"
#include "third_party/nsync/testing/time_extra.h"
#include "libc/sysv/consts/clock.h"
#include "third_party/nsync/time.h"
/* --------------------------- */
@ -63,7 +64,7 @@ static int cv_queue_put (cv_queue *q, void *v, nsync_time abs_deadline) {
int wake = 0;
nsync_mu_lock (&q->mu);
while (q->count == q->limit &&
nsync_cv_wait_with_deadline (&q->non_full, &q->mu, abs_deadline, NULL) == 0) {
nsync_cv_wait_with_deadline (&q->non_full, &q->mu, CLOCK_REALTIME, abs_deadline, NULL) == 0) {
}
if (q->count != q->limit) {
int i = q->pos + q->count;
@ -91,7 +92,7 @@ static void *cv_queue_get (cv_queue *q, nsync_time abs_deadline) {
void *v = NULL;
nsync_mu_lock (&q->mu);
while (q->count == 0 &&
nsync_cv_wait_with_deadline (&q->non_empty, &q->mu, abs_deadline, NULL) == 0) {
nsync_cv_wait_with_deadline (&q->non_empty, &q->mu, CLOCK_REALTIME, abs_deadline, NULL) == 0) {
}
if (q->count != 0) {
v = q->data[q->pos];
@ -237,7 +238,7 @@ static void test_cv_deadline (testing t) {
nsync_time expected_end_time;
start_time = nsync_time_now ();
expected_end_time = nsync_time_add (start_time, nsync_time_ms (87));
if (nsync_cv_wait_with_deadline (&cv, &mu, expected_end_time,
if (nsync_cv_wait_with_deadline (&cv, &mu, CLOCK_REALTIME, expected_end_time,
NULL) != ETIMEDOUT) {
TEST_FATAL (t, ("nsync_cv_wait() returned non-expired for a timeout"));
}
@ -289,7 +290,7 @@ static void test_cv_cancel (testing t) {
cancel = nsync_note_new (NULL, expected_end_time);
x = nsync_cv_wait_with_deadline (&cv, &mu, future_time, cancel);
x = nsync_cv_wait_with_deadline (&cv, &mu, CLOCK_REALTIME, future_time, cancel);
if (x != ECANCELED) {
TEST_FATAL (t, ("nsync_cv_wait() returned non-cancelled (%d) for "
"a cancellation; expected %d",
@ -308,7 +309,7 @@ static void test_cv_cancel (testing t) {
/* Check that an already cancelled wait returns immediately. */
start_time = nsync_time_now ();
x = nsync_cv_wait_with_deadline (&cv, &mu, nsync_time_no_deadline, cancel);
x = nsync_cv_wait_with_deadline (&cv, &mu, CLOCK_REALTIME, nsync_time_no_deadline, cancel);
if (x != ECANCELED) {
TEST_FATAL (t, ("nsync_cv_wait() returned non-cancelled (%d) for "
"a cancellation; expected %d",

View file

@ -24,6 +24,7 @@
#include "third_party/nsync/testing/closure.h"
#include "third_party/nsync/testing/smprintf.h"
#include "third_party/nsync/testing/testing.h"
#include "libc/sysv/consts/clock.h"
#include "third_party/nsync/testing/time_extra.h"
/* Example use of CV.wait(): A priority queue of strings whose
@ -74,7 +75,8 @@ static const char *string_priority_queue_cv_remove_with_deadline (string_priorit
const char *s = NULL;
nsync_mu_lock (&q->mu);
while (A_LEN (&q->heap) == 0 &&
nsync_cv_wait_with_deadline (&q->non_empty, &q->mu, abs_deadline, NULL) == 0) {
nsync_cv_wait_with_deadline (&q->non_empty, &q->mu, CLOCK_REALTIME,
abs_deadline, NULL) == 0) {
}
alen = A_LEN (&q->heap);
if (alen != 0) {

View file

@ -24,6 +24,7 @@
#include "third_party/nsync/testing/closure.h"
#include "third_party/nsync/testing/smprintf.h"
#include "third_party/nsync/testing/testing.h"
#include "libc/sysv/consts/clock.h"
#include "third_party/nsync/testing/time_extra.h"
/* The state shared between the threads in each of the tests below. */
@ -67,6 +68,7 @@ static void test_data_wait_for_all_threads (test_data *td) {
while (td->finished_threads != td->n_threads) {
nsync_cv_wait_with_deadline_generic (&td->done, td->mu_in_use,
td->lock, td->unlock,
CLOCK_REALTIME,
nsync_time_no_deadline, NULL);
}
(*td->unlock) (td->mu_in_use);
@ -303,7 +305,7 @@ static int counter_wait_for_zero_with_deadline (counter *c, nsync_time abs_deadl
int value;
nsync_mu_rlock (&c->mu);
while (c->value != 0 &&
nsync_cv_wait_with_deadline (&c->cv, &c->mu, abs_deadline, NULL) == 0) {
nsync_cv_wait_with_deadline (&c->cv, &c->mu, CLOCK_REALTIME, abs_deadline, NULL) == 0) {
}
value = c->value;
nsync_mu_runlock (&c->mu);

View file

@ -107,6 +107,7 @@ static void mutex_cv_ping_pong (ping_pong *pp, int parity) {
nsync_cv_wait_with_deadline_generic (&pp->cv[parity], &pp->mutex,
&void_pthread_mutex_lock,
&void_pthread_mutex_unlock,
CLOCK_REALTIME,
nsync_time_no_deadline, NULL);
}
pp->i++;
@ -163,7 +164,8 @@ static void mu_cv_unexpired_deadline_ping_pong (ping_pong *pp, int parity) {
while (pp->i < pp->limit) {
while ((pp->i & 1) == parity) {
nsync_cv_wait_with_deadline (&pp->cv[parity], &pp->mu,
deadline_in1hour, NULL);
CLOCK_REALTIME, deadline_in1hour,
NULL);
}
pp->i++;
nsync_cv_signal (&pp->cv[1 - parity]);
@ -318,6 +320,7 @@ static void rw_mutex_cv_ping_pong (ping_pong *pp, int parity) {
nsync_cv_wait_with_deadline_generic (&pp->cv[parity], &pp->rwmutex,
&void_pthread_rwlock_wrlock,
&void_pthread_rwlock_unlock,
CLOCK_REALTIME,
nsync_time_no_deadline, NULL);
}
pp->i++;

View file

@ -102,7 +102,7 @@ struct nsync_waitable_s {
mu/lock/unlock are used to acquire and release the relevant locks
whan waiting on condition variables. */
int nsync_wait_n(void *mu, void (*lock)(void *), void (*unlock)(void *),
nsync_time abs_deadline, int count,
int clock, nsync_time abs_deadline, int count,
struct nsync_waitable_s *waitable[]);
/* A "struct nsync_waitable_s" implementation must implement these

View file

@ -380,7 +380,7 @@ __kmp_acquire_futex_lock_timed_template(kmp_futex_lock_t *lck, kmp_int32 gtid) {
long rc;
#ifdef __COSMOPOLITAN__
if ((rc = nsync_futex_wait_((int *)&(lck->lk.poll), poll_val, false, NULL)) != 0) {
if ((rc = nsync_futex_wait_((int *)&(lck->lk.poll), poll_val, false, 0, NULL)) != 0) {
#else
if ((rc = syscall(__NR_futex, (int *)&(lck->lk.poll), FUTEX_WAIT, poll_val, NULL,
NULL, 0)) != 0) {