From 597098845ce92fcd4fb853453d18cb2862ccecb9 Mon Sep 17 00:00:00 2001 From: Christian Prochaska Date: Wed, 5 Feb 2020 15:20:41 +0100 Subject: [PATCH] libc: support pthread cleanup handlers Fixes #3642 --- repos/libports/lib/symbols/libc | 2 + .../libports/src/lib/libc/internal/pthread.h | 49 +++++++++++ repos/libports/src/lib/libc/pthread.cc | 14 ++++ repos/libports/src/test/pthread/main.cc | 83 +++++++++++++++++++ 4 files changed, 148 insertions(+) diff --git a/repos/libports/lib/symbols/libc b/repos/libports/lib/symbols/libc index 7c0c7e2b8..0e520a5ff 100644 --- a/repos/libports/lib/symbols/libc +++ b/repos/libports/lib/symbols/libc @@ -19,6 +19,8 @@ __inet_ntop T __inet_pton T __isthreaded B 4 __mb_cur_max D 8 +__pthread_cleanup_pop_imp T +__pthread_cleanup_push_imp T __res_init T __res_query T __res_state T diff --git a/repos/libports/src/lib/libc/internal/pthread.h b/repos/libports/src/lib/libc/internal/pthread.h index a40ea4099..23ef38d64 100644 --- a/repos/libports/src/lib/libc/internal/pthread.h +++ b/repos/libports/src/lib/libc/internal/pthread.h @@ -17,6 +17,7 @@ #define _LIBC__INTERNAL__PTHREAD_H_ /* Genode includes */ +#include #include #include @@ -178,6 +179,23 @@ struct Libc::Pthread : Noncopyable, Thread::Tls::Base void *_stack_addr = nullptr; size_t _stack_size = 0; + /* cleanup handlers */ + + class Cleanup_handler : public List::Element + { + private: + void (*_routine)(void*); + void *_arg; + + public: + Cleanup_handler(void (*routine)(void*), void *arg) + : _routine(routine), _arg(arg) { } + + void execute() { _routine(_arg); } + }; + + List _cleanup_handlers; + public: int thread_local_errno = 0; @@ -229,12 +247,43 @@ struct Libc::Pthread : Noncopyable, Thread::Tls::Base void exit(void *retval) { + while (cleanup_pop(1)) { } _retval = retval; cancel(); } void *stack_addr() const { return _stack_addr; } size_t stack_size() const { return _stack_size; } + + /* + * Push a cleanup handler to the cancellation cleanup stack. + */ + void cleanup_push(void (*routine)(void*), void *arg) + { + Libc::Allocator alloc { }; + _cleanup_handlers.insert(new (alloc) Cleanup_handler(routine, arg)); + } + + /* + * Pop and optionally execute the top-most cleanup handler. + * Returns true if a handler was found. + */ + bool cleanup_pop(int execute) + { + Cleanup_handler *cleanup_handler = _cleanup_handlers.first(); + + if (!cleanup_handler) return false; + + _cleanup_handlers.remove(cleanup_handler); + + if (execute) + cleanup_handler->execute(); + + Libc::Allocator alloc { }; + destroy(alloc, cleanup_handler); + + return true; + } }; diff --git a/repos/libports/src/lib/libc/pthread.cc b/repos/libports/src/lib/libc/pthread.cc index 70f0a7037..a72bfb010 100644 --- a/repos/libports/src/lib/libc/pthread.cc +++ b/repos/libports/src/lib/libc/pthread.cc @@ -636,6 +636,20 @@ extern "C" { } + void __pthread_cleanup_push_imp(void (*routine)(void*), void *arg, + struct _pthread_cleanup_info *) + { + pthread_self()->cleanup_push(routine, arg); + + } + + + void __pthread_cleanup_pop_imp(int execute) + { + pthread_self()->cleanup_pop(execute); + } + + /* Mutex */ int pthread_mutexattr_init(pthread_mutexattr_t *attr) diff --git a/repos/libports/src/test/pthread/main.cc b/repos/libports/src/test/pthread/main.cc index ba7bc48e0..d3a2ed028 100644 --- a/repos/libports/src/test/pthread/main.cc +++ b/repos/libports/src/test/pthread/main.cc @@ -999,6 +999,88 @@ static void test_interplay() } +/* + * Test cleanup handlers + */ + +static bool cleanup1_executed = false; +static bool cleanup2_executed = false; +static bool cleanup3_executed = false; +static bool cleanup4_executed = false; + +static void cleanup1(void *) { cleanup1_executed = true; } +static void cleanup2(void *) { cleanup2_executed = true; } + +static void cleanup3(void *arg) +{ + if (arg != (void*)1) { + printf("Error: cleanup3(): incorrect argument\n"); + exit(-1); + } + + cleanup3_executed = true; +} + +static void cleanup4(void *) { cleanup4_executed = true; } + +static void *thread_cleanup_func(void *) +{ + pthread_cleanup_push(cleanup1, nullptr); + pthread_cleanup_push(cleanup2, nullptr); + pthread_cleanup_push(cleanup3, (void*)1); + pthread_cleanup_push(cleanup4, nullptr); + + /* pop 'cleanup4()', don't execute */ + pthread_cleanup_pop(0); + + if (cleanup4_executed) { + printf("Error: cleanup4() executed\n"); + exit(-1); + } + + /* pop and execute 'cleanup3()' */ + pthread_cleanup_pop(1); + + if (!cleanup3_executed) { + printf("Error: cleanup3() not executed\n"); + exit(-1); + } + + pthread_exit(nullptr); + + /* + * 'cleanup1()' and 'cleanup2()' are executed by 'pthread_exit()', but + * because 'pthread_cleanup_push()' contains opening braces, there must be + * corresponding calls to 'pthread_cleanup_pop()' in the same function, + * even if they are not executed there. + */ + pthread_cleanup_pop(0); + pthread_cleanup_pop(0); +} + +static void test_cleanup() +{ + printf("main thread: test cleanup handlers\n"); + + pthread_t t; + void *retval; + + if (pthread_create(&t, 0, thread_cleanup_func, nullptr) != 0) { + printf("error: pthread_create() failed\n"); + exit(-1); + } + + pthread_join(t, &retval); + + if (!cleanup1_executed || !cleanup2_executed) { + printf("Error: cleanup1() or cleanup2() not executed\n"); + exit(-1); + } + + printf("main thread: cleanup handler testing done\n"); +} + + int main(int argc, char **argv) { printf("--- pthread test ---\n"); @@ -1015,6 +1097,7 @@ int main(int argc, char **argv) test_mutex_stress(); test_lock_and_sleep(); test_cond(); + test_cleanup(); printf("--- returning from main ---\n"); return 0;