From 95fee8614d399cb5c6e82fb789fe8f3eb4d28fdb Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Mon, 9 Sep 2024 00:18:54 -0700 Subject: [PATCH] Test recursive mutex code more --- libc/thread/lock.h | 20 +++++++++-- libc/thread/thread.h | 1 + test/libc/thread/footek_test.c | 33 +++++++++++++----- third_party/nsync/testing/mu_test.c | 52 +++++++++++++++++++++++++++++ 4 files changed, 95 insertions(+), 11 deletions(-) diff --git a/libc/thread/lock.h b/libc/thread/lock.h index 5a095679f1c..2947da75fb0 100644 --- a/libc/thread/lock.h +++ b/libc/thread/lock.h @@ -2,17 +2,33 @@ #define COSMOPOLITAN_LIBC_THREAD_LOCK_H_ COSMOPOLITAN_C_START_ -#define MUTEX_DEPTH_MIN 0x00000010ull -#define MUTEX_DEPTH_MAX 0x000003f0ull +// +// ┌depth +// │ +// COSMOPOLITAN MUTEXES │ ┌waited +// │ │ +// │ │┌locked +// │ ││ +// │ ││┌pshared +// owner │ │││ +// tid │ │││┌type +// │ │ ││││ +// ┌──────────────┴───────────────┐ ┌─┴──┐│││├┐ +// 0b0000000000000000000000000000000000000000000000000000000000000000 + +#define MUTEX_DEPTH_MIN 0x00000020ull +#define MUTEX_DEPTH_MAX 0x000007e0ull #define MUTEX_TYPE(word) ((word) & 3) #define MUTEX_PSHARED(word) ((word) & 4) #define MUTEX_LOCKED(word) ((word) & 8) +#define MUTEX_WAITED(word) ((word) & 16) #define MUTEX_DEPTH(word) ((word) & MUTEX_DEPTH_MAX) #define MUTEX_OWNER(word) ((word) >> 32) #define MUTEX_LOCK(word) (((word) & 7) | 8) #define MUTEX_UNLOCK(word) ((word) & 7) +#define MUTEX_SET_WAITED(word) ((word) | 16) #define MUTEX_SET_TYPE(word, type) (((word) & ~3ull) | (type)) #define MUTEX_SET_PSHARED(word, pshared) (((word) & ~4ull) | (pshared)) #define MUTEX_INC_DEPTH(word) ((word) + MUTEX_DEPTH_MIN) diff --git a/libc/thread/thread.h b/libc/thread/thread.h index fd70f30fb5f..a840cb6eb78 100644 --- a/libc/thread/thread.h +++ b/libc/thread/thread.h @@ -75,6 +75,7 @@ typedef struct pthread_mutex_s { int32_t _pid; _PTHREAD_ATOMIC(int32_t) _futex; }; + /* this cleverly overlaps with NSYNC struct Dll *waiters; */ _PTHREAD_ATOMIC(uint64_t) _word; } pthread_mutex_t; diff --git a/test/libc/thread/footek_test.c b/test/libc/thread/footek_test.c index bb72b93e3f1..9973d38e64b 100644 --- a/test/libc/thread/footek_test.c +++ b/test/libc/thread/footek_test.c @@ -1,15 +1,15 @@ #define USE POSIX -#define ITERATIONS 50000 -#define THREADS 10 -// #define ITERATIONS 100000 -// #define THREADS 30 +#define ITERATIONS 100000 +#define THREADS 30 -#define SPIN 1 -#define FUTEX 2 -#define POSIX 3 +#define SPIN 1 +#define FUTEX 2 +#define POSIX 3 +#define POSIX_RECURSIVE 4 #ifdef __COSMOPOLITAN__ #include +#include "libc/thread/thread.h" #include "third_party/nsync/futex.internal.h" #endif @@ -278,6 +278,8 @@ void lock(atomic_int *futex) { pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs); #if USE == FUTEX nsync_futex_wait_(futex, 2, 0, 0, 0); +#else + pthread_yield_np(); #endif pthread_setcancelstate(cs, 0); word = atomic_exchange_explicit(futex, 2, memory_order_acquire); @@ -296,11 +298,11 @@ void unlock(atomic_int *futex) { int g_chores; atomic_int g_lock; -pthread_mutex_t g_locker = PTHREAD_MUTEX_INITIALIZER; +pthread_mutex_t g_locker; void *worker(void *arg) { for (int i = 0; i < ITERATIONS; ++i) { -#if USE == POSIX +#if USE == POSIX || USE == POSIX_RECURSIVE pthread_mutex_lock(&g_locker); ++g_chores; pthread_mutex_unlock(&g_locker); @@ -331,6 +333,17 @@ int main() { struct timeval start; gettimeofday(&start, 0); + pthread_mutex_t lock; + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); +#if USE == POSIX_RECURSIVE + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); +#else + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL); +#endif + pthread_mutex_init(&g_locker, &attr); + pthread_mutexattr_destroy(&attr); + pthread_t th[THREADS]; for (int i = 0; i < THREADS; ++i) pthread_create(&th[i], 0, worker, 0); @@ -349,6 +362,8 @@ int main() { tomicros(ru.ru_utime), // tomicros(ru.ru_stime)); + pthread_mutex_destroy(&lock); + #ifdef __COSMOPOLITAN__ CheckForMemoryLeaks(); #endif diff --git a/third_party/nsync/testing/mu_test.c b/third_party/nsync/testing/mu_test.c index 5a0228804df..de7b84b7684 100644 --- a/third_party/nsync/testing/mu_test.c +++ b/third_party/nsync/testing/mu_test.c @@ -177,6 +177,41 @@ static void test_mutex_nthread (testing t) { } while (nsync_time_cmp (nsync_time_now (NSYNC_CLOCK), deadline) < 0); } +/* Create a few threads, each of which increments an integer a fixed + number of times, using a recursive pthread_mutex_t for mutual exclusion. + It checks that the integer is incremented the correct number of times. */ +static void test_xmutex_nthread (testing t) { + int loop_count = 100000; + nsync_time deadline; + deadline = nsync_time_add (nsync_time_now (NSYNC_CLOCK), nsync_time_ms (1500)); + do { + int i; + test_data td; + pthread_mutexattr_t attr; + bzero ((void *) &td, sizeof (td)); + td.t = t; + td.n_threads = 5; + td.loop_count = loop_count; + td.mu_in_use = &td.mutex; + td.lock = &void_pthread_mutex_lock; + td.unlock = &void_pthread_mutex_unlock; + pthread_mutexattr_init (&attr); + pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init (&td.mutex, &attr); + pthread_mutexattr_destroy (&attr); + for (i = 0; i != td.n_threads; i++) { + closure_fork (closure_counting (&counting_loop, &td, i)); + } + test_data_wait_for_all_threads (&td); + if (td.i != td.n_threads*td.loop_count) { + TEST_FATAL (t, ("test_mutex_nthread final count inconsistent: want %d, got %d", + td.n_threads*td.loop_count, td.i)); + } + pthread_mutex_destroy (&td.mutex); + loop_count *= 2; + } while (nsync_time_cmp (nsync_time_now (NSYNC_CLOCK), deadline) < 0); +} + /* void pthread_rwlock_wrlock */ static void void_pthread_rwlock_wrlock (void *mu) { pthread_rwlock_wrlock ((pthread_rwlock_t *) mu); @@ -1040,6 +1075,21 @@ static void benchmark_mutex_contended (testing t) { pthread_mutex_destroy (&cs.mutex); } +/* Measure the performance of highly contended recursive + pthread_mutex_t locks, with small critical sections. */ +static void benchmark_xmutex_contended (testing t) { + contended_state cs; + pthread_mutexattr_t attr; + bzero ((void *) &cs, sizeof (cs)); + pthread_mutexattr_init (&attr); + pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init (&cs.mutex, &attr); + pthread_mutexattr_destroy (&attr); + contended_state_run_test (&cs, t, &cs.mutex, &void_pthread_mutex_lock, + &void_pthread_mutex_unlock); + pthread_mutex_destroy (&cs.mutex); +} + /* Measure the performance of highly contended pthread_rwlock_t locks, with small critical sections. */ static void benchmark_wmutex_contended (testing t) { @@ -1057,11 +1107,13 @@ int main (int argc, char *argv[]) { TEST_RUN (tb, test_rlock); TEST_RUN (tb, test_mu_nthread); TEST_RUN (tb, test_mutex_nthread); + TEST_RUN (tb, test_xmutex_nthread); TEST_RUN (tb, test_rwmutex_nthread); TEST_RUN (tb, test_try_mu_nthread); BENCHMARK_RUN (tb, benchmark_mu_contended); BENCHMARK_RUN (tb, benchmark_mutex_contended); + BENCHMARK_RUN (tb, benchmark_xmutex_contended); BENCHMARK_RUN (tb, benchmark_wmutex_contended); BENCHMARK_RUN (tb, benchmark_mu_uncontended);