Skip to content

Commit 3c61a54

Browse files
committed
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.
1 parent 79516bf commit 3c61a54

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+449
-155
lines changed

libc/calls/clock_gettime.c

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "libc/intrin/describeflags.h"
2525
#include "libc/intrin/strace.h"
2626
#include "libc/runtime/syslib.internal.h"
27+
#include "libc/sysv/consts/clock.h"
2728

2829
#ifdef __aarch64__
2930
#define CGT_VDSO __vdsosym("LINUX_2.6.39", "__kernel_clock_gettime")
@@ -58,14 +59,43 @@ static int __clock_gettime_init(int clockid, struct timespec *ts) {
5859
return cgt(clockid, ts);
5960
}
6061

62+
static int clock_gettime_impl(int clock, struct timespec *ts) {
63+
int rc;
64+
if (!IsLinux())
65+
return __clock_gettime(clock, ts);
66+
TryAgain:
67+
68+
// Ensure fallback for old Linux sticks.
69+
if (clock == 4 /* CLOCK_MONOTONIC_RAW */)
70+
clock = CLOCK_MONOTONIC_RAW;
71+
72+
// Call appropriate implementation.
73+
rc = __clock_gettime(clock, ts);
74+
75+
// CLOCK_MONOTONIC_RAW is Linux 2.6.28+ so not available on RHEL5
76+
if (rc == -EINVAL && clock == 4 /* CLOCK_MONOTONIC_RAW */) {
77+
CLOCK_MONOTONIC_RAW = CLOCK_MONOTONIC;
78+
CLOCK_MONOTONIC_RAW_APPROX = CLOCK_MONOTONIC;
79+
goto TryAgain;
80+
}
81+
82+
return rc;
83+
}
84+
6185
/**
6286
* Returns nanosecond time.
6387
*
6488
* @param clock supports the following values across OSes:
6589
* - `CLOCK_REALTIME`
6690
* - `CLOCK_MONOTONIC`
91+
* - `CLOCK_MONOTONIC_RAW`
92+
* - `CLOCK_MONOTONIC_RAW_APPROX`
93+
* - `CLOCK_REALTIME_FAST`
6794
* - `CLOCK_REALTIME_COARSE`
95+
* - `CLOCK_REALTIME_PRECISE`
96+
* - `CLOCK_MONOTONIC_FAST`
6897
* - `CLOCK_MONOTONIC_COARSE`
98+
* - `CLOCK_MONOTONIC_PRECISE`
6999
* - `CLOCK_THREAD_CPUTIME_ID`
70100
* - `CLOCK_PROCESS_CPUTIME_ID`
71101
* @param ts is where the result is stored (or null to do clock check)
@@ -80,7 +110,7 @@ static int __clock_gettime_init(int clockid, struct timespec *ts) {
80110
*/
81111
int clock_gettime(int clock, struct timespec *ts) {
82112
// threads on win32 stacks call this so we can't asan check *ts
83-
int rc = __clock_gettime(clock, ts);
113+
int rc = clock_gettime_impl(clock, ts);
84114
if (rc) {
85115
errno = -rc;
86116
rc = -1;

libc/calls/clock_nanosleep.c

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "libc/calls/struct/timespec.h"
2020
#include "libc/dce.h"
2121
#include "libc/errno.h"
22+
#include "libc/sysv/consts/clock.h"
2223
#include "libc/sysv/consts/timer.h"
2324

2425
/**
@@ -79,18 +80,32 @@
7980
errno_t clock_nanosleep(int clock, int flags, //
8081
const struct timespec *req, //
8182
struct timespec *rem) {
82-
if (IsMetal()) {
83+
if (IsMetal())
8384
return ENOSYS;
84-
}
8585
if (clock == 127 || //
8686
(flags & ~TIMER_ABSTIME) || //
8787
req->tv_sec < 0 || //
88-
!(0 <= req->tv_nsec && req->tv_nsec <= 999999999)) {
88+
!(0 <= req->tv_nsec && req->tv_nsec <= 999999999))
8989
return EINVAL;
90+
int rc;
91+
errno_t err, old = errno;
92+
93+
TryAgain:
94+
// Ensure fallback for old Linux sticks.
95+
if (IsLinux() && clock == 4 /* CLOCK_MONOTONIC_RAW */)
96+
clock = CLOCK_MONOTONIC_RAW;
97+
98+
rc = sys_clock_nanosleep(clock, flags, req, rem);
99+
100+
// CLOCK_MONOTONIC_RAW is Linux 2.6.28+ so not available on RHEL5
101+
if (IsLinux() && rc && errno == EINVAL &&
102+
clock == 4 /* CLOCK_MONOTONIC_RAW */) {
103+
CLOCK_MONOTONIC_RAW = CLOCK_MONOTONIC;
104+
CLOCK_MONOTONIC_RAW_APPROX = CLOCK_MONOTONIC;
105+
goto TryAgain;
90106
}
91-
errno_t old = errno;
92-
int rc = sys_clock_nanosleep(clock, flags, req, rem);
93-
errno_t err = !rc ? 0 : errno;
107+
108+
err = !rc ? 0 : errno;
94109
errno = old;
95110
return err;
96111
}

libc/intrin/maps.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ void *__maps_pickaddr(size_t);
5353
void __maps_add(struct Map *);
5454
void __maps_free(struct Map *);
5555
void __maps_insert(struct Map *);
56+
bool __maps_track(char *, size_t);
5657
struct Map *__maps_alloc(void);
5758
struct Map *__maps_floor(const char *);
5859
void __maps_stack(char *, int, int, size_t, int, intptr_t);

libc/intrin/mmap.c

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,28 @@ void __maps_insert(struct Map *map) {
305305
__maps_check();
306306
}
307307

308+
static void __maps_track_insert(struct Map *map, char *addr, size_t size,
309+
uintptr_t map_handle) {
310+
map->addr = addr;
311+
map->size = size;
312+
map->prot = PROT_READ | PROT_WRITE;
313+
map->flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_NOFORK;
314+
map->hand = map_handle;
315+
__maps_lock();
316+
__maps_insert(map);
317+
__maps_unlock();
318+
}
319+
320+
bool __maps_track(char *addr, size_t size) {
321+
struct Map *map;
322+
do {
323+
if (!(map = __maps_alloc()))
324+
return false;
325+
} while (map == MAPS_RETRY);
326+
__maps_track_insert(map, addr, size, -1);
327+
return true;
328+
}
329+
308330
struct Map *__maps_alloc(void) {
309331
struct Map *map;
310332
uintptr_t tip = atomic_load_explicit(&__maps.freed, memory_order_relaxed);
@@ -321,14 +343,7 @@ struct Map *__maps_alloc(void) {
321343
if (sys.addr == MAP_FAILED)
322344
return 0;
323345
map = sys.addr;
324-
map->addr = sys.addr;
325-
map->size = gransz;
326-
map->prot = PROT_READ | PROT_WRITE;
327-
map->flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_NOFORK;
328-
map->hand = sys.maphandle;
329-
__maps_lock();
330-
__maps_insert(map);
331-
__maps_unlock();
346+
__maps_track_insert(map, sys.addr, gransz, sys.maphandle);
332347
for (int i = 1; i < gransz / sizeof(struct Map); ++i)
333348
__maps_free(map + i);
334349
return MAPS_RETRY;

libc/intrin/pthread_mutex_lock.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,12 @@ static void pthread_mutex_lock_drepper(atomic_int *futex, char pshare) {
5757
LOCKTRACE("acquiring pthread_mutex_lock_drepper(%t)...", futex);
5858
if (word == 1)
5959
word = atomic_exchange_explicit(futex, 2, memory_order_acquire);
60+
BLOCK_CANCELATION;
6061
while (word > 0) {
61-
BLOCK_CANCELATION;
62-
_weaken(nsync_futex_wait_)(futex, 2, pshare, 0);
63-
ALLOW_CANCELATION;
62+
_weaken(nsync_futex_wait_)(futex, 2, pshare, 0, 0);
6463
word = atomic_exchange_explicit(futex, 2, memory_order_acquire);
6564
}
65+
ALLOW_CANCELATION;
6666
}
6767

6868
static errno_t pthread_mutex_lock_recursive(pthread_mutex_t *mutex,

libc/intrin/sys_umtx_timedwait_uint.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
int sys_umtx_timedwait_uint_cp(atomic_int *, int, int, size_t,
2424
struct _umtx_time *) asm("sys_futex_cp");
2525

26-
int sys_umtx_timedwait_uint(atomic_int *p, int expect, bool pshare,
26+
int sys_umtx_timedwait_uint(atomic_int *p, int expect, bool pshare, int clock,
2727
const struct timespec *abstime) {
2828
int op;
2929
size_t size;
@@ -32,7 +32,7 @@ int sys_umtx_timedwait_uint(atomic_int *p, int expect, bool pshare,
3232
tm_p = 0;
3333
size = 0;
3434
} else {
35-
timo._clockid = CLOCK_REALTIME;
35+
timo._clockid = clock;
3636
timo._flags = UMTX_ABSTIME;
3737
timo._timeout = *abstime;
3838
tm_p = &timo;

libc/proc/proc.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "libc/errno.h"
2828
#include "libc/fmt/wintime.internal.h"
2929
#include "libc/intrin/dll.h"
30+
#include "libc/intrin/maps.h"
3031
#include "libc/intrin/strace.h"
3132
#include "libc/intrin/weaken.h"
3233
#include "libc/mem/leaks.h"
@@ -60,6 +61,8 @@
6061
* @fileoverview Windows Subprocess Management.
6162
*/
6263

64+
#define STACK_SIZE 65536
65+
6366
struct Procs __proc;
6467

6568
static textwindows void __proc_stats(int64_t h, struct rusage *ru) {
@@ -130,7 +133,11 @@ textwindows int __proc_harvest(struct Proc *pr, bool iswait4) {
130133

131134
static textwindows dontinstrument uint32_t __proc_worker(void *arg) {
132135
struct CosmoTib tls;
136+
char *sp = __builtin_frame_address(0);
133137
__bootstrap_tls(&tls, __builtin_frame_address(0));
138+
__maps_track(
139+
(char *)(((uintptr_t)sp + __pagesize - 1) & -__pagesize) - STACK_SIZE,
140+
STACK_SIZE);
134141
for (;;) {
135142

136143
// assemble a group of processes to wait on. if more than 64
@@ -238,7 +245,7 @@ static textwindows dontinstrument uint32_t __proc_worker(void *arg) {
238245
static textwindows void __proc_setup(void) {
239246
__proc.onbirth = CreateEvent(0, 0, 0, 0); // auto reset
240247
__proc.haszombies = CreateEvent(0, 1, 0, 0); // manual reset
241-
__proc.thread = CreateThread(0, 65536, __proc_worker, 0,
248+
__proc.thread = CreateThread(0, STACK_SIZE, __proc_worker, 0,
242249
kNtStackSizeParamIsAReservation, 0);
243250
}
244251

libc/sysv/consts.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -577,10 +577,11 @@ syscon clock CLOCK_REALTIME_PRECISE 0 0 0 0 9 0 0 0 #
577577
syscon clock CLOCK_REALTIME_FAST 0 0 0 0 10 0 0 0 #
578578
syscon clock CLOCK_REALTIME_COARSE 5 5 0 0 10 0 0 2 # Linux 2.6.32+; bsd consensus; not available on RHEL5
579579
syscon clock CLOCK_MONOTONIC 1 1 6 6 4 3 3 1 # XNU/NT faked; could move backwards if NTP introduces negative leap second
580+
syscon clock CLOCK_MONOTONIC_RAW 4 4 4 4 4 3 3 1 # actually monotonic; not subject to NTP adjustments; Linux 2.6.28+; XNU/NT/FreeBSD/OpenBSD faked; not available on RHEL5 (will fallback to CLOCK_MONOTONIC)
581+
syscon clock CLOCK_MONOTONIC_RAW_APPROX 4 4 5 5 4 3 3 1 # goes faster on xnu, otherwise faked
580582
syscon clock CLOCK_MONOTONIC_PRECISE 1 1 6 6 11 3 3 1 #
581583
syscon clock CLOCK_MONOTONIC_FAST 1 1 6 6 12 3 3 1 #
582584
syscon clock CLOCK_MONOTONIC_COARSE 6 6 5 5 12 3 3 1 # Linux 2.6.32+; bsd consensus; not available on RHEL5
583-
syscon clock CLOCK_MONOTONIC_RAW 4 4 4 4 127 127 127 127 # actually monotonic; not subject to NTP adjustments; Linux 2.6.28+; XNU/NT/FreeBSD/OpenBSD faked; not available on RHEL5
584585
syscon clock CLOCK_PROCESS_CPUTIME_ID 2 2 12 12 15 2 0x40000000 4 # NetBSD lets you bitwise a PID into clockid_t
585586
syscon clock CLOCK_THREAD_CPUTIME_ID 3 3 16 16 14 4 0x20000000 5 #
586587
syscon clock CLOCK_PROF 127 127 127 127 2 127 2 127 #
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
#include "libc/sysv/consts/syscon.internal.h"
2-
.syscon clock,CLOCK_MONOTONIC_RAW,4,4,4,4,127,127,127,127
2+
.syscon clock,CLOCK_MONOTONIC_RAW,4,4,4,4,4,3,3,1
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#include "libc/sysv/consts/syscon.internal.h"
2+
.syscon clock,CLOCK_MONOTONIC_RAW_APPROX,4,4,5,5,4,3,3,1

0 commit comments

Comments
 (0)