mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
Merge pull request #821 from Kijewski/pthrad_cleanup
posix: Add pthread_cleanup handlers
This commit is contained in:
commit
8a86f493b4
@ -96,4 +96,6 @@ void pthread_testcancel(void);
|
||||
|
||||
#include "pthread_barrier.h"
|
||||
|
||||
#include "pthread_cleanup.h"
|
||||
|
||||
#endif /* pthread.h */
|
||||
|
69
sys/posix/pthread/include/pthread_cleanup.h
Normal file
69
sys/posix/pthread/include/pthread_cleanup.h
Normal file
@ -0,0 +1,69 @@
|
||||
/**
|
||||
* @brief Internal structure for pthread_cleanup_push()
|
||||
*/
|
||||
typedef struct __pthread_cleanup_datum
|
||||
{
|
||||
/** Cleanup handler to call next. */
|
||||
struct __pthread_cleanup_datum *__next;
|
||||
|
||||
/** Cleanup routine to call. */
|
||||
void (*__routine)(void *arg);
|
||||
|
||||
/** Argument to supply. */
|
||||
void *__arg;
|
||||
} __pthread_cleanup_datum_t;
|
||||
|
||||
/**
|
||||
* @brief Internal function to be called by pthread_cleanup_push()
|
||||
* @details The previous top of the stack gets stored in `datum->next`.
|
||||
* @param[in] datum Allocated in the stack, datum tells how to cleanup.
|
||||
*/
|
||||
void __pthread_cleanup_push(__pthread_cleanup_datum_t *datum);
|
||||
|
||||
/**
|
||||
* @brief Internal function to be called by pthread_cleanup_push()
|
||||
* @details This function leaves the cleanup frame that was opened with __pthread_cleanup_push().
|
||||
* @param[in] datum Parameter that was supplied to __pthread_cleanup_push().
|
||||
* @param[in] execute Iff `!= 0` call cleanup handler.
|
||||
*/
|
||||
void __pthread_cleanup_pop(__pthread_cleanup_datum_t *datum, int execute);
|
||||
|
||||
/*
|
||||
* Notice: `pthread_cleanup_*` has to be defined as a macro, because the cleanup
|
||||
* stack needs extra data. The standard explicitly allows defining these
|
||||
* functions as macros. The alternative would be malloc.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Open a cleanup frame.
|
||||
* @details This function must be paired with pthread_cleanup_pop().
|
||||
* @details The cleanup function is called if the current thread exists
|
||||
* inside the frame by means of pthread_exit(), or if the thread
|
||||
* was cancelled.
|
||||
* @details You must not `return`, `continue`, or `break` out of the cleanup frame.
|
||||
* @details The frame opens a scope. Variables declared inside this scope
|
||||
* won't be visible outside.
|
||||
* @param[in] ROUTINE Function to call on cleanup.
|
||||
* @param[in] ARG Argument to supply to the cleanup handler.
|
||||
*/
|
||||
#define pthread_cleanup_push(ROUTINE, ARG) \
|
||||
do { \
|
||||
__extension__ __pthread_cleanup_datum_t ____datum__ = { \
|
||||
.__routine = (ROUTINE), \
|
||||
.__arg = (ARG), \
|
||||
}; \
|
||||
__extension__ int ____execute__ = 1; \
|
||||
__pthread_cleanup_push(&____datum__); \
|
||||
do { \
|
||||
do { } while (0)
|
||||
|
||||
/**
|
||||
* @brief Closes a cleaup frame
|
||||
* @details Must be paired with pthread_cleanup_push().
|
||||
* @param[in] EXECUTE Iff `!= 0` call cleanup handler.
|
||||
*/
|
||||
#define pthread_cleanup_pop(EXECUTE) \
|
||||
____execute__ = (EXECUTE); \
|
||||
} while (0); \
|
||||
__pthread_cleanup_pop(&____datum__, ____execute__); \
|
||||
} while (0)
|
@ -62,6 +62,8 @@ typedef struct pthread_thread {
|
||||
void *arg;
|
||||
|
||||
char *stack;
|
||||
|
||||
__pthread_cleanup_datum_t *cleanup_top;
|
||||
} pthread_thread_t;
|
||||
|
||||
static pthread_thread_t *volatile pthread_sched_threads[MAXTHREADS];
|
||||
@ -160,6 +162,14 @@ int pthread_create(pthread_t *newthread, const pthread_attr_t *attr, void *(*sta
|
||||
void pthread_exit(void *retval)
|
||||
{
|
||||
pthread_thread_t *self = pthread_sched_threads[pthread_self()];
|
||||
|
||||
while (self->cleanup_top) {
|
||||
__pthread_cleanup_datum_t *ct = self->cleanup_top;
|
||||
self->cleanup_top = ct->__next;
|
||||
|
||||
ct->__routine(ct->__arg);
|
||||
}
|
||||
|
||||
self->thread_pid = -1;
|
||||
DEBUG("pthread_exit(%p), self == %p\n", retval, (void *) self);
|
||||
if (self->status != PTS_DETACHED) {
|
||||
@ -282,3 +292,23 @@ void pthread_testcancel(void)
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void __pthread_cleanup_push(__pthread_cleanup_datum_t *datum)
|
||||
{
|
||||
pthread_thread_t *self = pthread_sched_threads[pthread_self()];
|
||||
datum->__next = self->cleanup_top;
|
||||
self->cleanup_top = datum;
|
||||
}
|
||||
|
||||
void __pthread_cleanup_pop(__pthread_cleanup_datum_t *datum, int execute)
|
||||
{
|
||||
pthread_thread_t *self = pthread_sched_threads[pthread_self()];
|
||||
self->cleanup_top = datum->__next;
|
||||
|
||||
if (execute != 0) {
|
||||
/* "The pthread_cleanup_pop() function shall remove the routine at the
|
||||
* top of the calling thread's cancellation cleanup stack and optionally
|
||||
* invoke it (if execute is non-zero)." */
|
||||
datum->__routine(datum->__arg);
|
||||
}
|
||||
}
|
||||
|
6
tests/test_pthread_cleanup/Makefile
Normal file
6
tests/test_pthread_cleanup/Makefile
Normal file
@ -0,0 +1,6 @@
|
||||
PROJECT = test_pthread_cleanup
|
||||
include ../Makefile.tests_common
|
||||
|
||||
USEMODULE += pthread
|
||||
|
||||
include $(RIOTBASE)/Makefile.include
|
75
tests/test_pthread_cleanup/main.c
Normal file
75
tests/test_pthread_cleanup/main.c
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Freie Universität Berlin
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser General
|
||||
* Public License. See the file LICENSE in the top level directory for more
|
||||
* details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup tests
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief pthread test application
|
||||
*
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "pthread.h"
|
||||
|
||||
#define RET_EXIT ((void *) 1234)
|
||||
#define RET_FAIL ((void *) 5678)
|
||||
|
||||
static void cleanup(void *arg)
|
||||
{
|
||||
printf("Cleanup: <%s>\n", (const char *) arg);
|
||||
}
|
||||
|
||||
static void *run(void *unused) {
|
||||
(void) unused;
|
||||
|
||||
/* indentation for visibility */
|
||||
puts("<SCOPE 0>");
|
||||
pthread_cleanup_push(cleanup, "1");
|
||||
puts("<SCOPE 1>");
|
||||
pthread_cleanup_push(cleanup, "2");
|
||||
puts("<SCOPE 2>");
|
||||
pthread_cleanup_push(cleanup, "3");
|
||||
puts("<SCOPE 3>");
|
||||
pthread_cleanup_push(cleanup, "4");
|
||||
puts("<SCOPE 4>");
|
||||
pthread_cleanup_push(cleanup, "5");
|
||||
puts("<SCOPE 5 />");
|
||||
pthread_cleanup_pop(1);
|
||||
puts("</SCOPE 4>");
|
||||
pthread_cleanup_pop(0); /* cleanup 4 should not be executed */
|
||||
puts("</SCOPE 3>");
|
||||
pthread_cleanup_pop(1);
|
||||
pthread_exit(RET_EXIT);
|
||||
puts("/<SCOPE 2>"); /* thread exited, should not be printed */
|
||||
pthread_cleanup_pop(0); /* should be printed nevertheless */
|
||||
puts("</SCOPE 1>");
|
||||
pthread_cleanup_pop(1);
|
||||
puts("</SCOPE 0>");
|
||||
|
||||
return RET_FAIL;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
puts("Start.");
|
||||
|
||||
pthread_t th_id;
|
||||
pthread_create(&th_id, NULL, run, NULL);
|
||||
|
||||
void *res;
|
||||
pthread_join(th_id, (void **) &res);
|
||||
|
||||
printf("Result: %u\n", (int) (intptr_t) res);
|
||||
puts("Done.");
|
||||
return 0;
|
||||
}
|
||||
|
87
tests/test_pthread_cleanup/tests/01-test
Executable file
87
tests/test_pthread_cleanup/tests/01-test
Executable file
@ -0,0 +1,87 @@
|
||||
#!/usr/bin/env expect
|
||||
|
||||
set timeout 5
|
||||
|
||||
set pid [spawn make term]
|
||||
puts "-*- Spawened $pid -*-\n"
|
||||
|
||||
set once 0
|
||||
set result 1
|
||||
while { $once == 0 } {
|
||||
set once 1
|
||||
|
||||
expect {
|
||||
"Start." {}
|
||||
timeout { break }
|
||||
}
|
||||
expect {
|
||||
"<SCOPE 0>" {}
|
||||
timeout { break }
|
||||
}
|
||||
expect {
|
||||
"<SCOPE 1>" {}
|
||||
timeout { break }
|
||||
}
|
||||
expect {
|
||||
"<SCOPE 2>" {}
|
||||
timeout { break }
|
||||
}
|
||||
expect {
|
||||
"<SCOPE 3>" {}
|
||||
timeout { break }
|
||||
}
|
||||
expect {
|
||||
"<SCOPE 4>" {}
|
||||
timeout { break }
|
||||
}
|
||||
expect {
|
||||
"<SCOPE 5 />" {}
|
||||
timeout { break }
|
||||
}
|
||||
expect {
|
||||
"Cleanup: <5>" {}
|
||||
timeout { break }
|
||||
}
|
||||
expect {
|
||||
"</SCOPE 4>" {}
|
||||
timeout { break }
|
||||
}
|
||||
# Cleanup 4 has execute == 0.
|
||||
expect {
|
||||
"</SCOPE 3>" {}
|
||||
timeout { break }
|
||||
}
|
||||
# pthread_exit is called here
|
||||
expect {
|
||||
"Cleanup: <3>" {}
|
||||
timeout { break }
|
||||
}
|
||||
expect {
|
||||
"Cleanup: <2>" {}
|
||||
timeout { break }
|
||||
}
|
||||
expect {
|
||||
"Cleanup: <1>" {}
|
||||
timeout { break }
|
||||
}
|
||||
expect {
|
||||
"Result: 1234" {}
|
||||
timeout { break }
|
||||
}
|
||||
expect {
|
||||
"Done." {}
|
||||
timeout { break }
|
||||
}
|
||||
|
||||
set result 0
|
||||
}
|
||||
|
||||
if { $result == 0 } {
|
||||
puts "\n-*- Test successful! -*-\n"
|
||||
} else {
|
||||
puts "\n-*- TEST HAD ERRORS! -*-\n"
|
||||
}
|
||||
spawn kill -9 $pid
|
||||
wait
|
||||
close
|
||||
exit $result
|
Loading…
Reference in New Issue
Block a user