libc: support pthread cleanup handlers

Fixes #3642
This commit is contained in:
Christian Prochaska 2020-02-05 15:20:41 +01:00 committed by Christian Helmuth
parent 8a7deae238
commit 597098845c
4 changed files with 148 additions and 0 deletions

View File

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

View File

@ -17,6 +17,7 @@
#define _LIBC__INTERNAL__PTHREAD_H_
/* Genode includes */
#include <libc/allocator.h>
#include <libc/component.h>
#include <util/reconstructible.h>
@ -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<Cleanup_handler>::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_handler> _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;
}
};

View File

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

View File

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