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

@ -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++;