From 131f8015f1e44ee746fa3ce83e2c5816b31c4617 Mon Sep 17 00:00:00 2001 From: Christian Helmuth <christian.helmuth@genode-labs.com> Date: Thu, 5 Sep 2024 11:59:01 +0200 Subject: [PATCH] test-pthread: pthread_once stress test Issue #5336 --- repos/libports/src/test/pthread/main.cc | 152 ++++++++++++++++++++++++ 1 file changed, 152 insertions(+) diff --git a/repos/libports/src/test/pthread/main.cc b/repos/libports/src/test/pthread/main.cc index 0f99d2fbb9..a9f6e89c45 100644 --- a/repos/libports/src/test/pthread/main.cc +++ b/repos/libports/src/test/pthread/main.cc @@ -25,6 +25,7 @@ /* Genode includes */ #include <base/log.h> #include <base/sleep.h> +#include <cpu/atomic.h> struct Thread_args { @@ -1193,6 +1194,156 @@ static void test_thread_local_destructor() } +/* test_pthread_once() counters */ +static int volatile once_init, once_round, once_round_complete; + +static int inc(int volatile *counter) +{ + int next = *counter + 1; + while (!Genode::cmpxchg(counter, next - 1, next)) + next++; + return next; +} + +static void init_once1() { inc(&once_init); } +static void init_once2() { inc(&once_init); } +static void init_once3() { inc(&once_init); } + +static void init_once_nested() +{ + pthread_once_t once_nested = PTHREAD_ONCE_INIT; + + pthread_once(&once_nested, init_once1); + + inc(&once_init); +} + +struct Once_thread +{ + enum { ROUNDS = 1'000 }; + + struct Arg + { + pthread_once_t *once; + void (*init_once)(void); + }; + + unsigned _tag; + + Cond &_cond; + Mutex<PTHREAD_MUTEX_NORMAL> &_mutex; + + Arg _once1, _once2, _once3; + + pthread_t _thread; + + static void * _entry_trampoline(void *arg) + { + Once_thread *t = (Once_thread *)arg; + t->_entry(); + return nullptr; + } + + void _entry() + { + for (int i = 1; i <= ROUNDS; ++i) { + pthread_mutex_lock(_mutex.mutex()); + while (once_round != i) + pthread_cond_wait(_cond.cond(), _mutex.mutex()); + pthread_mutex_unlock(_mutex.mutex()); + + pthread_once(_once1.once, _once1.init_once); + pthread_once(_once2.once, _once2.init_once); + pthread_once(_once3.once, _once3.init_once); + + inc(&once_round_complete); + } + } + + Once_thread(unsigned tag, Cond &cond, Mutex<PTHREAD_MUTEX_NORMAL> &mutex, + Arg once1, Arg once2, Arg once3) + : _tag(tag), _cond(cond), _mutex(mutex), _once1(once1), _once2(once2), _once3(once3) + { + if (pthread_create(&_thread, 0, _entry_trampoline, this) != 0) { + printf("Error: pthread_create() failed\n"); + exit(-1); + } + } + + void join() { pthread_join(_thread, nullptr); } +}; + + +static void check_pthread_once(int num_init) +{ + if (once_init == num_init) return; + + Genode::error("unxpected pthread_once initializer call count ", + once_init, "/", num_init); + exit(-1); +} + +static void test_pthread_once() +{ + printf("main thread: test pthread_once()\n"); + + pthread_once_t once1, once2, once3; + + /* test one thread and double-init prevention */ + once_init = 0; once1 = once2 = once3 = PTHREAD_ONCE_INIT; + pthread_once(&once1, init_once1); + pthread_once(&once1, init_once1); + check_pthread_once(1); + + /* test one thread with consecutive onces */ + once_init = 0; once1 = once2 = once3 = PTHREAD_ONCE_INIT; + pthread_once(&once1, init_once1); + pthread_once(&once2, init_once2); + pthread_once(&once3, init_once3); + check_pthread_once(3); + + /* test one thread and nested pthread_once() with different onces */ + once_init = 0; once1 = once2 = once3 = PTHREAD_ONCE_INIT; + pthread_once(&once1, init_once_nested); + check_pthread_once(2); + + Cond cond; + + Mutex<PTHREAD_MUTEX_NORMAL> mutex; + + enum { NUM_THREADS = 6 }; + Once_thread threads[NUM_THREADS] = { + { 1, cond, mutex, { &once1, init_once1 }, { &once2, init_once2 }, { &once3, init_once3 } }, + { 2, cond, mutex, { &once3, init_once3 }, { &once1, init_once1 }, { &once2, init_once2 } }, + { 3, cond, mutex, { &once2, init_once2 }, { &once3, init_once3 }, { &once1, init_once1 } }, + { 4, cond, mutex, { &once1, init_once1 }, { &once2, init_once2 }, { &once3, init_once3 } }, + { 5, cond, mutex, { &once3, init_once3 }, { &once1, init_once1 }, { &once2, init_once2 } }, + { 6, cond, mutex, { &once2, init_once2 }, { &once3, init_once3 }, { &once1, init_once1 } }, + }; + + for (int i = 1; i <= Once_thread::ROUNDS; ++i) { + pthread_mutex_lock(mutex.mutex()); + + once_round = i; once_init = 0; once_round_complete = 0; + once1 = once2 = once3 = PTHREAD_ONCE_INIT; + pthread_cond_broadcast(cond.cond()); + + pthread_mutex_unlock(mutex.mutex()); + + pthread_once(&once1, init_once1); + pthread_once(&once2, init_once2); + pthread_once(&once3, init_once3); + + while (once_round_complete != NUM_THREADS) + usleep(1000); + + check_pthread_once(3); + } + + for (Once_thread &t : threads) t.join(); +} + + int main(int argc, char **argv) { printf("--- pthread test ---\n"); @@ -1212,6 +1363,7 @@ int main(int argc, char **argv) test_cleanup(); test_tls(); test_thread_local_destructor(); + test_pthread_once(); printf("--- returning from main ---\n"); return 0;