mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-05-29 00:32:29 +00:00
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:
parent
79516bf08e
commit
3c61a541bd
55 changed files with 449 additions and 155 deletions
|
@ -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_ */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
32
libc/thread/pthread_condattr_getclock.c
Normal file
32
libc/thread/pthread_condattr_getclock.c
Normal 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;
|
||||
}
|
|
@ -28,6 +28,6 @@
|
|||
*/
|
||||
errno_t pthread_condattr_getpshared(const pthread_condattr_t *attr,
|
||||
int *pshared) {
|
||||
*pshared = *attr;
|
||||
*pshared = attr->_pshared;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
38
libc/thread/pthread_condattr_setclock.c
Normal file
38
libc/thread/pthread_condattr_setclock.c
Normal 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;
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue