Use *NSYNC for POSIX threads locking APIs

Condition variables, barriers, and r/w locks now work very well.
This commit is contained in:
Justine Tunney 2022-09-11 11:02:07 -07:00
parent 3de35e196c
commit b5cb71ab84
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
197 changed files with 3734 additions and 3817 deletions

View file

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

View file

@ -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"

View file

@ -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();

View file

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

View file

@ -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));
}

View file

@ -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));
}

View file

@ -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))))

View file

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

View file

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

View file

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

View 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));
}

View file

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

View file

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

View 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));
}

View file

@ -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));
}

View file

@ -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");

View file

@ -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) {

View file

@ -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))))

View file

@ -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));
}

View file

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