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

@ -47,7 +47,8 @@ struct _umtx_time {
uint32_t _clockid;
};
int sys_umtx_timedwait_uint(_Atomic(int) *, int, bool, const struct timespec *);
int sys_umtx_timedwait_uint(_Atomic(int) *, int, bool, int,
const struct timespec *);
COSMOPOLITAN_C_END_
#endif /* COSMOPOLITAN_LIBC_THREAD_FREEBSD_INTERNAL_H_ */

View file

@ -24,10 +24,12 @@
#include "libc/calls/struct/sigset.internal.h"
#include "libc/calls/struct/timeval.h"
#include "libc/cosmo.h"
#include "libc/intrin/maps.h"
#include "libc/intrin/strace.h"
#include "libc/nt/enum/processcreationflags.h"
#include "libc/nt/thread.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/clock.h"
#include "libc/sysv/consts/sicode.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/errfuns.h"
@ -36,11 +38,17 @@
#include "third_party/nsync/mu.h"
#ifdef __x86_64__
#define STACK_SIZE 65536
struct IntervalTimer __itimer;
static textwindows dontinstrument uint32_t __itimer_worker(void *arg) {
struct CosmoTib tls;
__bootstrap_tls(&tls, __builtin_frame_address(0));
char *sp = __builtin_frame_address(0);
__bootstrap_tls(&tls, sp);
__maps_track(
(char *)(((uintptr_t)sp + __pagesize - 1) & -__pagesize) - STACK_SIZE,
STACK_SIZE);
for (;;) {
bool dosignal = false;
struct timeval now, waituntil;
@ -66,11 +74,10 @@ static textwindows dontinstrument uint32_t __itimer_worker(void *arg) {
}
}
nsync_mu_unlock(&__itimer.lock);
if (dosignal) {
if (dosignal)
__sig_generate(SIGALRM, SI_TIMER);
}
nsync_mu_lock(&__itimer.lock);
nsync_cv_wait_with_deadline(&__itimer.cond, &__itimer.lock,
nsync_cv_wait_with_deadline(&__itimer.cond, &__itimer.lock, CLOCK_REALTIME,
timeval_totimespec(waituntil), 0);
nsync_mu_unlock(&__itimer.lock);
}
@ -78,7 +85,7 @@ static textwindows dontinstrument uint32_t __itimer_worker(void *arg) {
}
static textwindows void __itimer_setup(void) {
__itimer.thread = CreateThread(0, 65536, __itimer_worker, 0,
__itimer.thread = CreateThread(0, STACK_SIZE, __itimer_worker, 0,
kNtStackSizeParamIsAReservation, 0);
}

View file

@ -61,7 +61,7 @@ errno_t pthread_barrier_wait(pthread_barrier_t *barrier) {
// wait for everyone else to arrive at barrier
BLOCK_CANCELATION;
while ((n = atomic_load_explicit(&barrier->_waiters, memory_order_acquire)))
nsync_futex_wait_(&barrier->_waiters, n, barrier->_pshared, 0);
nsync_futex_wait_(&barrier->_waiters, n, barrier->_pshared, 0, 0);
ALLOW_CANCELATION;
return 0;

View file

@ -16,10 +16,11 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/sysv/consts/clock.h"
#include "libc/thread/thread.h"
/**
* Initializes condition.
* Initializes condition variable.
*
* @param attr may be null
* @return 0 on success, or error number on failure
@ -27,7 +28,9 @@
errno_t pthread_cond_init(pthread_cond_t *cond,
const pthread_condattr_t *attr) {
*cond = (pthread_cond_t){0};
if (attr)
cond->_pshared = *attr;
if (attr) {
cond->_pshared = attr->_pshared;
cond->_clock = attr->_clock;
}
return 0;
}

View file

@ -20,6 +20,7 @@
#include "libc/calls/cp.internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/sysv/consts/clock.h"
#include "libc/thread/lock.h"
#include "libc/thread/posixthread.internal.h"
#include "libc/thread/thread.h"
@ -63,7 +64,7 @@ static errno_t pthread_cond_timedwait_impl(pthread_cond_t *cond,
struct PthreadWait waiter = {cond, mutex};
pthread_cleanup_push(pthread_cond_leave, &waiter);
rc = nsync_futex_wait_((atomic_int *)&cond->_sequence, seq1, cond->_pshared,
abstime);
cond->_clock, abstime);
pthread_cleanup_pop(true);
if (rc == -EAGAIN)
rc = 0;
@ -82,8 +83,10 @@ static errno_t pthread_cond_timedwait_impl(pthread_cond_t *cond,
* }
*
* @param mutex needs to be held by thread when calling this function
* @param abstime may be null to wait indefinitely and should contain
* some arbitrary interval added to a `CLOCK_REALTIME` timestamp
* @param abstime is an absolute timestamp, which may be null to wait
* forever; it's relative to `clock_gettime(CLOCK_REALTIME)` by
* default; pthread_condattr_setclock() may be used to customize
* which system clock is used
* @return 0 on success, or errno on error
* @raise ETIMEDOUT if `abstime` was specified and the current time
* exceeded its value
@ -125,7 +128,7 @@ errno_t pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
// if using Mike Burrows' code isn't possible, use a naive impl
if (!cond->_pshared && !IsXnuSilicon()) {
err = nsync_cv_wait_with_deadline(
(nsync_cv *)cond, (nsync_mu *)mutex,
(nsync_cv *)cond, (nsync_mu *)mutex, cond->_clock,
abstime ? *abstime : nsync_time_no_deadline, 0);
} else {
err = pthread_cond_timedwait_impl(cond, mutex, abstime);

View file

@ -0,0 +1,32 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2024 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/thread/thread.h"
/**
* Gets clock on condition variable attributes.
*
* @param clock will be set to one of
* - `CLOCK_REALTIME` (default)
* - `CLOCK_MONOTONIC`
* @return 0 on success, or error on failure
*/
int pthread_condattr_getclock(const pthread_condattr_t *attr, int *clock) {
*clock = attr->_clock;
return 0;
}

View file

@ -28,6 +28,6 @@
*/
errno_t pthread_condattr_getpshared(const pthread_condattr_t *attr,
int *pshared) {
*pshared = *attr;
*pshared = attr->_pshared;
return 0;
}

View file

@ -24,6 +24,6 @@
* @return 0 on success, or error on failure
*/
errno_t pthread_condattr_init(pthread_condattr_t *attr) {
*attr = 0;
*attr = (pthread_condattr_t){0};
return 0;
}

View file

@ -0,0 +1,38 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2024 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/errno.h"
#include "libc/sysv/consts/clock.h"
#include "libc/thread/thread.h"
/**
* Sets clock for condition variable.
*
* @param clock can be one of
* - `CLOCK_REALTIME` (default)
* - `CLOCK_MONOTONIC`
* @return 0 on success, or error on failure
* @raises EINVAL if `clock` is invalid
*/
int pthread_condattr_setclock(pthread_condattr_t *attr, int clock) {
if (clock != CLOCK_REALTIME && //
clock != CLOCK_MONOTONIC)
return EINVAL;
attr->_clock = clock;
return 0;
}

View file

@ -32,7 +32,7 @@ errno_t pthread_condattr_setpshared(pthread_condattr_t *attr, int pshared) {
switch (pshared) {
case PTHREAD_PROCESS_SHARED:
case PTHREAD_PROCESS_PRIVATE:
*attr = pshared;
attr->_pshared = pshared;
return 0;
default:
return EINVAL;

View file

@ -26,6 +26,7 @@
#include "libc/intrin/describeflags.h"
#include "libc/intrin/dll.h"
#include "libc/intrin/strace.h"
#include "libc/sysv/consts/clock.h"
#include "libc/thread/posixthread.internal.h"
#include "libc/thread/thread2.h"
#include "libc/thread/tls.h"
@ -74,7 +75,8 @@ static errno_t _pthread_wait(atomic_int *ctid, struct timespec *abstime) {
if (!(err = pthread_testcancel_np())) {
BEGIN_CANCELATION_POINT;
while ((x = atomic_load_explicit(ctid, memory_order_acquire))) {
e = nsync_futex_wait_(ctid, x, !IsWindows() && !IsXnu(), abstime);
e = nsync_futex_wait_(ctid, x, !IsWindows() && !IsXnu(), CLOCK_REALTIME,
abstime);
if (e == -ECANCELED) {
err = ECANCELED;
break;

View file

@ -28,6 +28,7 @@
#include "libc/intrin/weaken.h"
#include "libc/limits.h"
#include "libc/runtime/syslib.internal.h"
#include "libc/sysv/consts/clock.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/semaphore.h"
#include "libc/thread/thread.h"
@ -121,7 +122,7 @@ int sem_timedwait(sem_t *sem, const struct timespec *abstime) {
do {
if (!(v = atomic_load_explicit(&sem->sem_value, memory_order_relaxed))) {
rc = nsync_futex_wait_(&sem->sem_value, v, true, abstime);
rc = nsync_futex_wait_(&sem->sem_value, v, true, CLOCK_REALTIME, abstime);
if (rc == -EINTR || rc == -ECANCELED) {
errno = -rc;
rc = -1;

View file

@ -56,7 +56,6 @@ COSMOPOLITAN_C_START_
typedef uintptr_t pthread_t;
typedef int pthread_id_np_t;
typedef char pthread_condattr_t;
typedef char pthread_rwlockattr_t;
typedef char pthread_barrierattr_t;
typedef unsigned pthread_key_t;
@ -83,12 +82,18 @@ typedef struct pthread_mutexattr_s {
unsigned _word;
} pthread_mutexattr_t;
typedef struct pthread_condattr_s {
char _pshared;
char _clock;
} pthread_condattr_t;
typedef struct pthread_cond_s {
union {
void *_align;
struct {
uint32_t _nsync;
char _pshared;
char _clock;
};
};
_PTHREAD_ATOMIC(uint32_t) _sequence;
@ -165,10 +170,12 @@ int pthread_cond_destroy(pthread_cond_t *) libcesque paramsnonnull();
int pthread_cond_init(pthread_cond_t *, const pthread_condattr_t *) libcesque paramsnonnull((1));
int pthread_cond_signal(pthread_cond_t *) libcesque paramsnonnull();
int pthread_cond_wait(pthread_cond_t *, pthread_mutex_t *) libcesque paramsnonnull();
int pthread_condattr_destroy(pthread_condattr_t *) libcesque paramsnonnull();
int pthread_condattr_getpshared(const pthread_condattr_t *, int *) libcesque paramsnonnull();
int pthread_condattr_init(pthread_condattr_t *) libcesque paramsnonnull();
int pthread_condattr_destroy(pthread_condattr_t *) libcesque paramsnonnull();
int pthread_condattr_setpshared(pthread_condattr_t *, int) libcesque paramsnonnull();
int pthread_condattr_getpshared(const pthread_condattr_t *, int *) libcesque paramsnonnull();
int pthread_condattr_setclock(pthread_condattr_t *, int) libcesque paramsnonnull();
int pthread_condattr_getclock(const pthread_condattr_t *, int *) libcesque paramsnonnull();
int pthread_create(pthread_t *, const pthread_attr_t *, void *(*)(void *), void *) dontthrow paramsnonnull((1));
int pthread_detach(pthread_t) libcesque;
int pthread_equal(pthread_t, pthread_t) libcesque;