mirror of
https://github.com/genodelabs/genode.git
synced 2025-02-20 17:52:52 +00:00
parent
8a7deae238
commit
597098845c
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user