mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-05-23 13:52:28 +00:00
Use *NSYNC for POSIX threads locking APIs
Condition variables, barriers, and r/w locks now work very well.
This commit is contained in:
parent
3de35e196c
commit
b5cb71ab84
197 changed files with 3734 additions and 3817 deletions
|
@ -560,7 +560,7 @@ TEST(pledge_openbsd, bigSyscalls) {
|
|||
|
||||
int LockWorker(void *arg, int tid) {
|
||||
flockfile(stdout);
|
||||
ASSERT_EQ(gettid(), stdout->lock.lock & 0x000fffff);
|
||||
ASSERT_EQ(gettid(), stdout->lock._lock & 0x000fffff);
|
||||
funlockfile(stdout);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -23,9 +23,7 @@
|
|||
#include "libc/calls/struct/rlimit.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/wait0.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/thread/tls.h"
|
||||
#include "libc/runtime/internal.h"
|
||||
#include "libc/runtime/stack.h"
|
||||
#include "libc/stdio/rand.h"
|
||||
|
@ -41,6 +39,8 @@
|
|||
#include "libc/testlib/hyperion.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
#include "libc/thread/spawn.h"
|
||||
#include "libc/thread/tls.h"
|
||||
#include "libc/thread/wait0.internal.h"
|
||||
#include "libc/time/struct/tm.h"
|
||||
#include "libc/time/time.h"
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/atomic.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
|
@ -26,13 +27,14 @@
|
|||
#include "libc/thread/posixthread.internal.h"
|
||||
#include "libc/thread/spawn.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "third_party/nsync/mu.h"
|
||||
|
||||
int THREADS = 16;
|
||||
int ITERATIONS = 100;
|
||||
|
||||
int count;
|
||||
_Atomic(int) started;
|
||||
_Atomic(int) finished;
|
||||
atomic_int started;
|
||||
atomic_int finished;
|
||||
pthread_mutex_t lock;
|
||||
pthread_mutexattr_t attr;
|
||||
|
||||
|
@ -108,29 +110,50 @@ void BenchLockUnlock(pthread_mutex_t *m) {
|
|||
pthread_mutex_unlock(m);
|
||||
}
|
||||
|
||||
void BenchLockUnlockNsync(nsync_mu *m) {
|
||||
nsync_mu_lock(m);
|
||||
nsync_mu_unlock(m);
|
||||
}
|
||||
|
||||
BENCH(pthread_mutex_lock, bench_uncontended) {
|
||||
{
|
||||
pthread_spinlock_t s = {0};
|
||||
EZBENCH2("spin 1x", donothing, BenchSpinUnspin(&s));
|
||||
}
|
||||
{
|
||||
pthread_mutex_t m = {PTHREAD_MUTEX_NORMAL};
|
||||
nsync_mu m = {0};
|
||||
EZBENCH2("nsync 1x", donothing, BenchLockUnlockNsync(&m));
|
||||
}
|
||||
{
|
||||
pthread_mutex_t m;
|
||||
pthread_mutexattr_t attr;
|
||||
pthread_mutexattr_init(&attr);
|
||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
|
||||
pthread_mutex_init(&m, &attr);
|
||||
EZBENCH2("normal 1x", donothing, BenchLockUnlock(&m));
|
||||
}
|
||||
{
|
||||
pthread_mutex_t m = {PTHREAD_MUTEX_RECURSIVE};
|
||||
pthread_mutex_t m;
|
||||
pthread_mutexattr_t attr;
|
||||
pthread_mutexattr_init(&attr);
|
||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
|
||||
pthread_mutex_init(&m, &attr);
|
||||
EZBENCH2("recursive 1x", donothing, BenchLockUnlock(&m));
|
||||
}
|
||||
{
|
||||
pthread_mutex_t m = {PTHREAD_MUTEX_ERRORCHECK};
|
||||
pthread_mutex_t m;
|
||||
pthread_mutexattr_t attr;
|
||||
pthread_mutexattr_init(&attr);
|
||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
|
||||
pthread_mutex_init(&m, &attr);
|
||||
EZBENCH2("errorcheck 1x", donothing, BenchLockUnlock(&m));
|
||||
}
|
||||
}
|
||||
|
||||
struct SpinContentionArgs {
|
||||
pthread_spinlock_t *spin;
|
||||
_Atomic(char) done;
|
||||
_Atomic(char) ready;
|
||||
atomic_char done;
|
||||
atomic_char ready;
|
||||
};
|
||||
|
||||
int SpinContentionWorker(void *arg, int tid) {
|
||||
|
@ -145,8 +168,8 @@ int SpinContentionWorker(void *arg, int tid) {
|
|||
|
||||
struct MutexContentionArgs {
|
||||
pthread_mutex_t *mutex;
|
||||
_Atomic(char) done;
|
||||
_Atomic(char) ready;
|
||||
atomic_char done;
|
||||
atomic_char ready;
|
||||
};
|
||||
|
||||
int MutexContentionWorker(void *arg, int tid) {
|
||||
|
@ -159,6 +182,22 @@ int MutexContentionWorker(void *arg, int tid) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
struct NsyncContentionArgs {
|
||||
nsync_mu *nsync;
|
||||
atomic_char done;
|
||||
atomic_char ready;
|
||||
};
|
||||
|
||||
int NsyncContentionWorker(void *arg, int tid) {
|
||||
struct NsyncContentionArgs *a = arg;
|
||||
while (!atomic_load_explicit(&a->done, memory_order_relaxed)) {
|
||||
nsync_mu_lock(a->nsync);
|
||||
atomic_store_explicit(&a->ready, 1, memory_order_relaxed);
|
||||
nsync_mu_unlock(a->nsync);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
BENCH(pthread_mutex_lock, bench_contended) {
|
||||
struct spawn t;
|
||||
{
|
||||
|
@ -171,7 +210,20 @@ BENCH(pthread_mutex_lock, bench_contended) {
|
|||
_join(&t);
|
||||
}
|
||||
{
|
||||
pthread_mutex_t m = {PTHREAD_MUTEX_NORMAL};
|
||||
nsync_mu m = {0};
|
||||
struct NsyncContentionArgs a = {&m};
|
||||
_spawn(NsyncContentionWorker, &a, &t);
|
||||
while (!a.ready) sched_yield();
|
||||
EZBENCH2("nsync 2x", donothing, BenchLockUnlockNsync(&m));
|
||||
a.done = true;
|
||||
_join(&t);
|
||||
}
|
||||
{
|
||||
pthread_mutex_t m;
|
||||
pthread_mutexattr_t attr;
|
||||
pthread_mutexattr_init(&attr);
|
||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
|
||||
pthread_mutex_init(&m, &attr);
|
||||
struct MutexContentionArgs a = {&m};
|
||||
_spawn(MutexContentionWorker, &a, &t);
|
||||
while (!a.ready) sched_yield();
|
||||
|
@ -180,7 +232,11 @@ BENCH(pthread_mutex_lock, bench_contended) {
|
|||
_join(&t);
|
||||
}
|
||||
{
|
||||
pthread_mutex_t m = {PTHREAD_MUTEX_RECURSIVE};
|
||||
pthread_mutex_t m;
|
||||
pthread_mutexattr_t attr;
|
||||
pthread_mutexattr_init(&attr);
|
||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
|
||||
pthread_mutex_init(&m, &attr);
|
||||
struct MutexContentionArgs a = {&m};
|
||||
_spawn(MutexContentionWorker, &a, &t);
|
||||
while (!a.ready) sched_yield();
|
||||
|
@ -189,7 +245,11 @@ BENCH(pthread_mutex_lock, bench_contended) {
|
|||
_join(&t);
|
||||
}
|
||||
{
|
||||
pthread_mutex_t m = {PTHREAD_MUTEX_ERRORCHECK};
|
||||
pthread_mutex_t m;
|
||||
pthread_mutexattr_t attr;
|
||||
pthread_mutexattr_init(&attr);
|
||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
|
||||
pthread_mutex_init(&m, &attr);
|
||||
struct MutexContentionArgs a = {&m};
|
||||
_spawn(MutexContentionWorker, &a, &t);
|
||||
while (!a.ready) sched_yield();
|
||||
|
|
|
@ -17,12 +17,11 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/atomic.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/futex.internal.h"
|
||||
#include "libc/intrin/wait0.internal.h"
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/math.h"
|
||||
|
@ -40,13 +39,14 @@
|
|||
#include "libc/thread/spawn.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/tls.h"
|
||||
#include "libc/thread/wait0.internal.h"
|
||||
|
||||
#define THREADS 8
|
||||
#define ITERATIONS 512
|
||||
|
||||
int count;
|
||||
_Atomic(int) started;
|
||||
_Atomic(int) finished;
|
||||
atomic_int started;
|
||||
atomic_int finished;
|
||||
pthread_mutex_t mylock;
|
||||
pthread_spinlock_t slock;
|
||||
struct spawn th[THREADS];
|
||||
|
|
|
@ -16,17 +16,19 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/atomic.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/gc.internal.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
#include "libc/thread/spawn.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
int i, n;
|
||||
struct spawn *t;
|
||||
_Atomic(int) x, y;
|
||||
atomic_int x, y;
|
||||
pthread_barrier_t b;
|
||||
static pthread_once_t once = PTHREAD_ONCE_INIT;
|
||||
|
||||
void InitFactory(void) {
|
||||
ASSERT_EQ(0, atomic_load(&x));
|
||||
|
@ -34,7 +36,6 @@ void InitFactory(void) {
|
|||
}
|
||||
|
||||
int Worker(void *arg, int tid) {
|
||||
static pthread_once_t once = PTHREAD_ONCE_INIT;
|
||||
pthread_barrier_wait(&b);
|
||||
ASSERT_EQ(0, pthread_once(&once, InitFactory));
|
||||
ASSERT_EQ(1, atomic_load(&y));
|
||||
|
@ -45,10 +46,11 @@ int Worker(void *arg, int tid) {
|
|||
TEST(pthread_once, test) {
|
||||
n = 32;
|
||||
x = y = 0;
|
||||
pthread_barrier_init(&b, 0, n);
|
||||
ASSERT_EQ(0, pthread_barrier_init(&b, 0, n));
|
||||
t = gc(malloc(sizeof(struct spawn) * n));
|
||||
for (i = 0; i < n; ++i) ASSERT_SYS(0, 0, _spawn(Worker, 0, t + i));
|
||||
for (i = 0; i < n; ++i) EXPECT_SYS(0, 0, _join(t + i));
|
||||
ASSERT_EQ(n, atomic_load(&x));
|
||||
ASSERT_EQ(1, atomic_load(&y));
|
||||
ASSERT_EQ(0, pthread_barrier_destroy(&b));
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
#define ENTRIES 1024
|
||||
|
||||
volatile uint64_t A[THREADS * ENTRIES];
|
||||
pthread_barrier_t barrier = PTHREAD_BARRIER_INITIALIZER;
|
||||
pthread_barrier_t barrier;
|
||||
|
||||
void SetUpOnce(void) {
|
||||
__enable_threads();
|
||||
|
@ -93,7 +93,7 @@ TEST(rand64, testThreadSafety_doesntProduceIdenticalValues) {
|
|||
sigemptyset(&ss);
|
||||
sigaddset(&ss, SIGCHLD);
|
||||
EXPECT_EQ(0, sigprocmask(SIG_BLOCK, &ss, &oldss));
|
||||
pthread_barrier_init(&barrier, 0, THREADS);
|
||||
ASSERT_EQ(0, pthread_barrier_init(&barrier, 0, THREADS));
|
||||
for (i = 0; i < THREADS; ++i) {
|
||||
ASSERT_SYS(0, 0, _spawn(Thrasher, (void *)(intptr_t)i, th + i));
|
||||
}
|
||||
|
@ -109,4 +109,5 @@ TEST(rand64, testThreadSafety_doesntProduceIdenticalValues) {
|
|||
EXPECT_NE(A[i], A[j], "i=%d j=%d", i, j);
|
||||
}
|
||||
}
|
||||
ASSERT_EQ(0, pthread_barrier_destroy(&barrier));
|
||||
}
|
||||
|
|
|
@ -39,7 +39,8 @@ TEST_LIBC_INTRIN_DIRECTDEPS = \
|
|||
LIBC_TINYMATH \
|
||||
LIBC_X \
|
||||
TOOL_VIZ_LIB \
|
||||
THIRD_PARTY_COMPILER_RT
|
||||
THIRD_PARTY_COMPILER_RT \
|
||||
THIRD_PARTY_NSYNC
|
||||
|
||||
TEST_LIBC_INTRIN_DEPS := \
|
||||
$(call uniq,$(foreach x,$(TEST_LIBC_INTRIN_DIRECTDEPS),$($(x))))
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/calls/ucontext.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sysv/consts/sa.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
|
@ -28,15 +30,16 @@
|
|||
|
||||
jmp_buf jb;
|
||||
|
||||
void EscapeSegfault(int sig) {
|
||||
void EscapeSegfault(int sig, struct siginfo *si, void *vctx) {
|
||||
longjmp(jb, 666);
|
||||
}
|
||||
|
||||
TEST(xstack, test) {
|
||||
if (IsXnu()) return; // TODO(jart): what's up with xnu in MODE=tiny?
|
||||
struct sigaction old[2];
|
||||
struct sigaction sa = {
|
||||
.sa_handler = EscapeSegfault,
|
||||
.sa_flags = SA_NODEFER,
|
||||
.sa_sigaction = EscapeSegfault,
|
||||
.sa_flags = SA_SIGINFO,
|
||||
};
|
||||
sigaction(SIGSEGV, &sa, old + 0);
|
||||
sigaction(SIGBUS, &sa, old + 1);
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
#include "libc/calls/struct/sched_param.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/fmt/fmt.h"
|
||||
#include "libc/intrin/wait0.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/math.h"
|
||||
#include "libc/mem/mem.h"
|
||||
|
@ -35,6 +34,7 @@
|
|||
#include "libc/sysv/consts/sched.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
#include "libc/thread/spawn.h"
|
||||
#include "libc/thread/wait0.internal.h"
|
||||
#include "libc/x/x.h"
|
||||
|
||||
#define DUB(i) (union Dub){i}.x
|
||||
|
|
|
@ -16,12 +16,11 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/atomic.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/futex.internal.h"
|
||||
#include "libc/intrin/wait0.internal.h"
|
||||
#include "libc/log/backtrace.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
|
@ -43,10 +42,11 @@
|
|||
#include "libc/thread/spawn.h"
|
||||
#include "libc/thread/tls.h"
|
||||
#include "libc/thread/tls2.h"
|
||||
#include "libc/thread/wait0.internal.h"
|
||||
#include "libc/time/time.h"
|
||||
|
||||
int x, me, tid;
|
||||
_Atomic(int) thechilde;
|
||||
atomic_int thechilde;
|
||||
|
||||
void SetUpOnce(void) {
|
||||
__enable_threads();
|
||||
|
@ -109,7 +109,7 @@ TEST(clone, test1) {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
// TEST THREADS CAN ISSUE SYSTEM CALLS WITH INDEPENDENT ERRNOS
|
||||
|
||||
_Atomic(int) sysbarrier;
|
||||
atomic_int sysbarrier;
|
||||
|
||||
int CloneTestSys(void *arg, int tid) {
|
||||
int i, id = (intptr_t)arg;
|
||||
|
|
119
test/libc/thread/nsync_test.c
Normal file
119
test/libc/thread/nsync_test.c
Normal file
|
@ -0,0 +1,119 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 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/testlib/testlib.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "third_party/nsync/cv.h"
|
||||
#include "third_party/nsync/mu.h"
|
||||
|
||||
int pos;
|
||||
int count;
|
||||
int limit;
|
||||
long data[1000];
|
||||
nsync_mu mu;
|
||||
nsync_cv non_full;
|
||||
nsync_cv non_empty;
|
||||
|
||||
int Put(long v, nsync_time abs_deadline) {
|
||||
int err, added = 0, wake = 0;
|
||||
nsync_mu_lock(&mu);
|
||||
while (count == limit) {
|
||||
if ((err = nsync_cv_wait_with_deadline(&non_full, &mu, abs_deadline, 0))) {
|
||||
ASSERT_EQ(ETIMEDOUT, err);
|
||||
ASSERT_NE(0, nsync_time_cmp(nsync_time_no_deadline, abs_deadline));
|
||||
}
|
||||
}
|
||||
if (count != limit) {
|
||||
int i = pos + count;
|
||||
if (limit <= i) i -= limit;
|
||||
data[i] = v;
|
||||
if (count == 0) wake = 1;
|
||||
count++;
|
||||
added = 1;
|
||||
}
|
||||
nsync_mu_unlock(&mu);
|
||||
if (wake) nsync_cv_broadcast(&non_empty);
|
||||
return added;
|
||||
}
|
||||
|
||||
long Get(nsync_time abs_deadline) {
|
||||
long err, v = 0;
|
||||
nsync_mu_lock(&mu);
|
||||
while (!count) {
|
||||
if ((err = nsync_cv_wait_with_deadline(&non_empty, &mu, abs_deadline, 0))) {
|
||||
ASSERT_EQ(ETIMEDOUT, err);
|
||||
ASSERT_NE(0, nsync_time_cmp(nsync_time_no_deadline, abs_deadline));
|
||||
}
|
||||
}
|
||||
if (count) {
|
||||
v = data[pos];
|
||||
data[pos] = 0;
|
||||
if (count == limit) {
|
||||
nsync_cv_broadcast(&non_full);
|
||||
}
|
||||
pos++;
|
||||
count--;
|
||||
if (pos == limit) {
|
||||
pos = 0;
|
||||
}
|
||||
}
|
||||
nsync_mu_unlock(&mu);
|
||||
return v;
|
||||
}
|
||||
|
||||
#define N 10000
|
||||
|
||||
void *Producer(void *arg) {
|
||||
for (int i = 0; i < N; i++) {
|
||||
ASSERT_EQ(1, Put((i + 1) * 3, nsync_time_no_deadline));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *Consumer(void *arg) {
|
||||
for (int i = 0; i < N; i++) {
|
||||
ASSERT_EQ((i + 1) * 3, Get(nsync_time_no_deadline));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
TEST(cond, test) {
|
||||
pthread_t t;
|
||||
nsync_mu_init(&mu);
|
||||
|
||||
limit = 1;
|
||||
ASSERT_EQ(0, pthread_create(&t, 0, Producer, 0));
|
||||
Consumer(0);
|
||||
ASSERT_EQ(0, pthread_join(t, 0));
|
||||
|
||||
limit = 10;
|
||||
ASSERT_EQ(0, pthread_create(&t, 0, Producer, 0));
|
||||
Consumer(0);
|
||||
ASSERT_EQ(0, pthread_join(t, 0));
|
||||
|
||||
limit = 100;
|
||||
ASSERT_EQ(0, pthread_create(&t, 0, Producer, 0));
|
||||
Consumer(0);
|
||||
ASSERT_EQ(0, pthread_join(t, 0));
|
||||
|
||||
limit = 1000;
|
||||
ASSERT_EQ(0, pthread_create(&t, 0, Producer, 0));
|
||||
Consumer(0);
|
||||
ASSERT_EQ(0, pthread_join(t, 0));
|
||||
}
|
|
@ -17,6 +17,7 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/atomic.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/mem/mem.h"
|
||||
|
@ -26,7 +27,7 @@
|
|||
#include "libc/thread/thread.h"
|
||||
|
||||
int i, n;
|
||||
_Atomic(int) p, w;
|
||||
atomic_int p, w;
|
||||
pthread_barrier_t barrier;
|
||||
|
||||
int Worker(void *arg, int tid) {
|
||||
|
@ -36,9 +37,6 @@ int Worker(void *arg, int tid) {
|
|||
ASSERT_GE(rc, 0);
|
||||
if (rc == PTHREAD_BARRIER_SERIAL_THREAD) {
|
||||
atomic_fetch_add(&p, 1);
|
||||
ASSERT_EQ(0, barrier.popped);
|
||||
ASSERT_EQ(0, barrier.waits);
|
||||
ASSERT_EQ(n, barrier.count);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1,123 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 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/calls/struct/timespec.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/gc.internal.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
#include "libc/thread/spawn.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/thread2.h"
|
||||
|
||||
// TODO(jart): Re-enable me.
|
||||
#if 0
|
||||
_Atomic(int) bReady;
|
||||
pthread_cond_t bCond;
|
||||
pthread_mutex_t bMutex;
|
||||
|
||||
int BroadcastWorker(void *arg, int tid) {
|
||||
ASSERT_EQ(0, pthread_mutex_lock(&bMutex));
|
||||
atomic_fetch_add(&bReady, 1);
|
||||
ASSERT_EQ(0, pthread_cond_wait(&bCond, &bMutex));
|
||||
ASSERT_EQ(0, pthread_mutex_unlock(&bMutex));
|
||||
return 0;
|
||||
}
|
||||
|
||||
TEST(pthread_cond_broadcast, test) {
|
||||
pthread_condattr_t cAttr;
|
||||
pthread_mutexattr_t mAttr;
|
||||
ASSERT_EQ(0, pthread_mutexattr_init(&mAttr));
|
||||
ASSERT_EQ(0, pthread_mutexattr_settype(&mAttr, PTHREAD_MUTEX_ERRORCHECK));
|
||||
ASSERT_EQ(0, pthread_mutex_init(&bMutex, &mAttr));
|
||||
ASSERT_EQ(0, pthread_condattr_init(&cAttr));
|
||||
ASSERT_EQ(0, pthread_condattr_setpshared(&cAttr, PTHREAD_PROCESS_PRIVATE));
|
||||
ASSERT_EQ(0, pthread_cond_init(&bCond, &cAttr));
|
||||
int i, n = 16;
|
||||
struct spawn *t = gc(malloc(sizeof(struct spawn) * n));
|
||||
for (i = 0; i < n; ++i) {
|
||||
ASSERT_SYS(0, 0, _spawn(BroadcastWorker, 0, t + i));
|
||||
}
|
||||
for (;;) {
|
||||
if (atomic_load_explicit(&bReady, memory_order_relaxed) == n) {
|
||||
break;
|
||||
} else {
|
||||
pthread_yield();
|
||||
}
|
||||
}
|
||||
ASSERT_EQ(0, pthread_mutex_lock(&bMutex));
|
||||
ASSERT_EQ(0, pthread_cond_broadcast(&bCond));
|
||||
ASSERT_EQ(0, pthread_mutex_unlock(&bMutex));
|
||||
for (i = 0; i < n; ++i) {
|
||||
EXPECT_SYS(0, 0, _join(t + i));
|
||||
}
|
||||
ASSERT_EQ(0, pthread_mutex_destroy(&bMutex));
|
||||
ASSERT_EQ(0, pthread_cond_destroy(&bCond));
|
||||
}
|
||||
|
||||
TEST(pthread_cond_timedwait, testTimeoutInPast_timesOutImmediately) {
|
||||
struct timespec t = {100000};
|
||||
pthread_cond_t c = PTHREAD_COND_INITIALIZER;
|
||||
pthread_mutex_t m = {PTHREAD_MUTEX_ERRORCHECK};
|
||||
ASSERT_EQ(0, pthread_mutex_lock(&m));
|
||||
ASSERT_EQ(ETIMEDOUT, pthread_cond_timedwait(&c, &m, &t));
|
||||
ASSERT_EQ(0, pthread_mutex_unlock(&m));
|
||||
}
|
||||
|
||||
_Atomic(int) tReady;
|
||||
pthread_cond_t tCond;
|
||||
pthread_mutex_t tMutex;
|
||||
|
||||
int TimedWorker(void *arg, int tid) {
|
||||
struct timespec ts;
|
||||
ASSERT_EQ(0, pthread_mutex_lock(&tMutex));
|
||||
atomic_fetch_add(&tReady, 1);
|
||||
ts = _timespec_add(_timespec_mono(), _timespec_frommillis(30000));
|
||||
ASSERT_EQ(0, pthread_cond_timedwait(&tCond, &tMutex, &ts));
|
||||
ASSERT_EQ(0, pthread_mutex_unlock(&tMutex));
|
||||
return 0;
|
||||
}
|
||||
|
||||
TEST(pthread_cond_timedwait, testThirtySeconds_doesntTimeOut) {
|
||||
pthread_condattr_t cAttr;
|
||||
pthread_mutexattr_t mAttr;
|
||||
ASSERT_EQ(0, pthread_mutexattr_init(&mAttr));
|
||||
ASSERT_EQ(0, pthread_mutexattr_settype(&mAttr, PTHREAD_MUTEX_ERRORCHECK));
|
||||
ASSERT_EQ(0, pthread_mutex_init(&tMutex, &mAttr));
|
||||
ASSERT_EQ(0, pthread_condattr_init(&cAttr));
|
||||
ASSERT_EQ(0, pthread_condattr_setpshared(&cAttr, PTHREAD_PROCESS_PRIVATE));
|
||||
ASSERT_EQ(0, pthread_cond_init(&tCond, &cAttr));
|
||||
struct spawn t;
|
||||
ASSERT_SYS(0, 0, _spawn(TimedWorker, 0, &t));
|
||||
for (;;) {
|
||||
if (atomic_load_explicit(&tReady, memory_order_relaxed)) {
|
||||
break;
|
||||
} else {
|
||||
pthread_yield();
|
||||
}
|
||||
}
|
||||
ASSERT_EQ(0, pthread_mutex_lock(&tMutex));
|
||||
ASSERT_EQ(0, pthread_cond_signal(&tCond));
|
||||
ASSERT_EQ(0, pthread_mutex_unlock(&tMutex));
|
||||
EXPECT_SYS(0, 0, _join(&t));
|
||||
ASSERT_EQ(0, pthread_mutex_destroy(&tMutex));
|
||||
ASSERT_EQ(0, pthread_cond_destroy(&tCond));
|
||||
}
|
||||
#endif
|
118
test/libc/thread/pthread_cond_signal_test.c
Normal file
118
test/libc/thread/pthread_cond_signal_test.c
Normal file
|
@ -0,0 +1,118 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 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/calls/struct/timespec.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/thread2.h"
|
||||
|
||||
int pos;
|
||||
int count;
|
||||
int limit;
|
||||
long data[1000];
|
||||
pthread_mutex_t mu;
|
||||
pthread_cond_t non_full;
|
||||
pthread_cond_t non_empty;
|
||||
|
||||
int Put(long v, struct timespec *abs_deadline) {
|
||||
int err, added = 0, wake = 0;
|
||||
pthread_mutex_lock(&mu);
|
||||
while (count == limit) {
|
||||
if ((err = pthread_cond_timedwait(&non_full, &mu, abs_deadline))) {
|
||||
ASSERT_EQ(ETIMEDOUT, err);
|
||||
ASSERT_NE(NULL, abs_deadline);
|
||||
}
|
||||
}
|
||||
if (count != limit) {
|
||||
int i = pos + count;
|
||||
if (limit <= i) i -= limit;
|
||||
data[i] = v;
|
||||
if (count == 0) wake = 1;
|
||||
count++;
|
||||
added = 1;
|
||||
}
|
||||
pthread_mutex_unlock(&mu);
|
||||
if (wake) pthread_cond_broadcast(&non_empty);
|
||||
return added;
|
||||
}
|
||||
|
||||
long Get(struct timespec *abs_deadline) {
|
||||
long err, v = 0;
|
||||
pthread_mutex_lock(&mu);
|
||||
while (!count) {
|
||||
if ((err = pthread_cond_timedwait(&non_empty, &mu, abs_deadline))) {
|
||||
ASSERT_EQ(ETIMEDOUT, err);
|
||||
ASSERT_NE(NULL, abs_deadline);
|
||||
}
|
||||
}
|
||||
if (count) {
|
||||
v = data[pos];
|
||||
data[pos] = 0;
|
||||
if (count == limit) {
|
||||
pthread_cond_broadcast(&non_full);
|
||||
}
|
||||
pos++;
|
||||
count--;
|
||||
if (pos == limit) {
|
||||
pos = 0;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&mu);
|
||||
return v;
|
||||
}
|
||||
|
||||
#define N 10000
|
||||
|
||||
void *Producer(void *arg) {
|
||||
for (int i = 0; i < N; i++) {
|
||||
ASSERT_EQ(1, Put((i + 1) * 3, 0));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *Consumer(void *arg) {
|
||||
for (int i = 0; i < N; i++) {
|
||||
ASSERT_EQ((i + 1) * 3, Get(0));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
TEST(cond, test) {
|
||||
pthread_t t;
|
||||
|
||||
limit = 1;
|
||||
ASSERT_EQ(0, pthread_create(&t, 0, Producer, 0));
|
||||
Consumer(0);
|
||||
ASSERT_EQ(0, pthread_join(t, 0));
|
||||
|
||||
limit = 10;
|
||||
ASSERT_EQ(0, pthread_create(&t, 0, Producer, 0));
|
||||
Consumer(0);
|
||||
ASSERT_EQ(0, pthread_join(t, 0));
|
||||
|
||||
limit = 100;
|
||||
ASSERT_EQ(0, pthread_create(&t, 0, Producer, 0));
|
||||
Consumer(0);
|
||||
ASSERT_EQ(0, pthread_join(t, 0));
|
||||
|
||||
limit = 1000;
|
||||
ASSERT_EQ(0, pthread_create(&t, 0, Producer, 0));
|
||||
Consumer(0);
|
||||
ASSERT_EQ(0, pthread_join(t, 0));
|
||||
}
|
|
@ -16,6 +16,7 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/atomic.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/gc.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
|
@ -26,17 +27,17 @@
|
|||
#define READERS 8
|
||||
#define WRITERS 2
|
||||
|
||||
_Atomic(int) reads;
|
||||
_Atomic(int) writes;
|
||||
atomic_int reads;
|
||||
atomic_int writes;
|
||||
pthread_rwlock_t lock;
|
||||
pthread_barrier_t barrier;
|
||||
|
||||
int Reader(void *arg, int tid) {
|
||||
pthread_barrier_wait(&barrier);
|
||||
for (int i = 0; i < ITERATIONS; ++i) {
|
||||
pthread_rwlock_rdlock(&lock);
|
||||
ASSERT_EQ(0, pthread_rwlock_rdlock(&lock));
|
||||
++reads;
|
||||
pthread_rwlock_unlock(&lock);
|
||||
ASSERT_EQ(0, pthread_rwlock_unlock(&lock));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -44,9 +45,9 @@ int Reader(void *arg, int tid) {
|
|||
int Writer(void *arg, int tid) {
|
||||
pthread_barrier_wait(&barrier);
|
||||
for (int i = 0; i < ITERATIONS; ++i) {
|
||||
pthread_rwlock_wrlock(&lock);
|
||||
ASSERT_EQ(0, pthread_rwlock_wrlock(&lock));
|
||||
++writes;
|
||||
pthread_rwlock_unlock(&lock);
|
||||
ASSERT_EQ(0, pthread_rwlock_unlock(&lock));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -54,7 +55,7 @@ int Writer(void *arg, int tid) {
|
|||
TEST(pthread_rwlock_rdlock, test) {
|
||||
int i;
|
||||
struct spawn *t = _gc(malloc(sizeof(struct spawn) * (READERS + WRITERS)));
|
||||
pthread_barrier_init(&barrier, 0, READERS + WRITERS);
|
||||
ASSERT_EQ(0, pthread_barrier_init(&barrier, 0, READERS + WRITERS));
|
||||
for (i = 0; i < READERS + WRITERS; ++i) {
|
||||
ASSERT_SYS(0, 0, _spawn(i < READERS ? Reader : Writer, 0, t + i));
|
||||
}
|
||||
|
@ -63,4 +64,5 @@ TEST(pthread_rwlock_rdlock, test) {
|
|||
}
|
||||
EXPECT_EQ(READERS * ITERATIONS, reads);
|
||||
EXPECT_EQ(WRITERS * ITERATIONS, writes);
|
||||
ASSERT_EQ(0, pthread_barrier_destroy(&barrier));
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/atomic.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
|
@ -62,7 +63,7 @@ TEST(pthread_setname_np, GetDefaultName_IsEmptyString) {
|
|||
ASSERT_EQ(0, pthread_join(id, 0));
|
||||
}
|
||||
|
||||
_Atomic(char) sync1, sync2;
|
||||
atomic_char sync1, sync2;
|
||||
|
||||
static void *GetNameOfOtherThreadWorker(void *arg) {
|
||||
pthread_setname_np(pthread_self(), "justine");
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/atomic.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
|
@ -27,7 +28,7 @@
|
|||
#include "libc/testlib/testlib.h"
|
||||
#include "libc/thread/spawn.h"
|
||||
|
||||
_Atomic(int) itworked;
|
||||
atomic_int itworked;
|
||||
_Thread_local int var;
|
||||
|
||||
void SetUpOnce(void) {
|
||||
|
|
|
@ -34,7 +34,8 @@ TEST_LIBC_THREAD_DIRECTDEPS = \
|
|||
LIBC_SYSV \
|
||||
LIBC_THREAD \
|
||||
LIBC_TIME \
|
||||
LIBC_TESTLIB
|
||||
LIBC_TESTLIB \
|
||||
THIRD_PARTY_NSYNC
|
||||
|
||||
TEST_LIBC_THREAD_DEPS := \
|
||||
$(call uniq,$(foreach x,$(TEST_LIBC_THREAD_DIRECTDEPS),$($(x))))
|
||||
|
|
|
@ -16,11 +16,11 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/gc.internal.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
#include "libc/thread/spawn.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/x/x.h"
|
||||
|
||||
#define DIR \
|
||||
|
@ -42,4 +42,5 @@ TEST(makedirs, test) {
|
|||
ASSERT_EQ(0, pthread_barrier_init(&barrier, 0, n));
|
||||
for (i = 0; i < n; ++i) ASSERT_SYS(0, 0, _spawn(Worker, 0, t + i));
|
||||
for (i = 0; i < n; ++i) EXPECT_SYS(0, 0, _join(t + i));
|
||||
ASSERT_EQ(0, pthread_barrier_destroy(&barrier));
|
||||
}
|
||||
|
|
|
@ -29,9 +29,9 @@
|
|||
|
||||
STATIC_YOINK("zip_uri_support");
|
||||
STATIC_YOINK("libc/testlib/hyperion.txt");
|
||||
/* STATIC_YOINK("inflate"); */
|
||||
/* STATIC_YOINK("inflateInit2"); */
|
||||
/* STATIC_YOINK("inflateEnd"); */
|
||||
STATIC_YOINK("inflate");
|
||||
STATIC_YOINK("inflateInit2");
|
||||
STATIC_YOINK("inflateEnd");
|
||||
|
||||
int Worker(void *arg, int tid) {
|
||||
int i, fd;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue