mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2025-01-18 12:52:44 +01:00
posix: Add pthread_cleanup handlers
With `pthread_cleanup_(push|pop)` you can define a function that should be ran if the thread is exited while it is inside this scope. A thread can be ended here through an explicit call to `pthread_exit()`, or if cancellation was requested and a cancellation point was hit. `pthread_cleanup_*` is mostly only useful together with cancellation points, and cancellation points are only useful with a cleanup functionality. Cancellation points are at least partially implemented by means of `pthread_testcancel()`. C.f. ["Cancellation Points"][1]. [1]: http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_09_05_02
This commit is contained in:
parent
edd83d1d92
commit
b54962689a
@ -88,6 +88,42 @@ int pthread_cancel(pthread_t th);
|
|||||||
cancelled. */
|
cancelled. */
|
||||||
void pthread_testcancel(void);
|
void pthread_testcancel(void);
|
||||||
|
|
||||||
|
void __pthread_cleanup_push(__pthread_cleanup_datum_t *datum);
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* The pthread_cleanup_push() function shall push the specified cancellation
|
||||||
|
* cleanup handler routine onto the calling thread's cancellation cleanup stack.
|
||||||
|
* The cancellation cleanup handler shall be popped from the cancellation
|
||||||
|
* cleanup stack and invoked with the argument arg when:
|
||||||
|
* The thread exits (that is, calls pthread_exit()).
|
||||||
|
* The thread acts upon a cancellation request.
|
||||||
|
* The thread calls pthread_cleanup_pop() with a non-zero execute argument. */
|
||||||
|
#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)
|
||||||
|
|
||||||
|
/* 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). */
|
||||||
|
#define pthread_cleanup_pop(EXECUTE) \
|
||||||
|
____execute__ = (EXECUTE); \
|
||||||
|
} while (0); \
|
||||||
|
__pthread_cleanup_pop(&____datum__, ____execute__); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
#include "pthread_mutex.h"
|
#include "pthread_mutex.h"
|
||||||
|
|
||||||
#include "pthread_rwlock.h"
|
#include "pthread_rwlock.h"
|
||||||
|
@ -33,4 +33,11 @@ typedef unsigned long int pthread_rwlockattr_t;
|
|||||||
|
|
||||||
typedef volatile int pthread_spinlock_t;
|
typedef volatile int pthread_spinlock_t;
|
||||||
|
|
||||||
|
typedef struct __pthread_cleanup_datum
|
||||||
|
{
|
||||||
|
struct __pthread_cleanup_datum *__next;
|
||||||
|
void (*__routine)(void *arg);
|
||||||
|
void *__arg;
|
||||||
|
} __pthread_cleanup_datum_t;
|
||||||
|
|
||||||
#endif /* PTHREADTYPES_H_ */
|
#endif /* PTHREADTYPES_H_ */
|
||||||
|
@ -62,6 +62,8 @@ typedef struct pthread_thread {
|
|||||||
void *arg;
|
void *arg;
|
||||||
|
|
||||||
char *stack;
|
char *stack;
|
||||||
|
|
||||||
|
__pthread_cleanup_datum_t *cleanup_top;
|
||||||
} pthread_thread_t;
|
} pthread_thread_t;
|
||||||
|
|
||||||
static pthread_thread_t *volatile pthread_sched_threads[MAXTHREADS];
|
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)
|
void pthread_exit(void *retval)
|
||||||
{
|
{
|
||||||
pthread_thread_t *self = pthread_sched_threads[pthread_self()];
|
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;
|
self->thread_pid = -1;
|
||||||
DEBUG("pthread_exit(%p), self == %p\n", retval, (void *) self);
|
DEBUG("pthread_exit(%p), self == %p\n", retval, (void *) self);
|
||||||
if (self->status != PTS_DETACHED) {
|
if (self->status != PTS_DETACHED) {
|
||||||
@ -282,3 +292,23 @@ void pthread_testcancel(void)
|
|||||||
pthread_exit(NULL);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
12
tests/test_pthread_cleanup/Makefile
Normal file
12
tests/test_pthread_cleanup/Makefile
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
PROJECT = test_pthread_cleanup
|
||||||
|
include ../Makefile.tests_common
|
||||||
|
|
||||||
|
USEMODULE += pthread
|
||||||
|
|
||||||
|
ifeq ($(BOARD),native)
|
||||||
|
CFLAGS += -isystem $(RIOTBASE)/sys/posix/pthread/include
|
||||||
|
else
|
||||||
|
INCLUDES += -I$(RIOTBASE)/sys/posix/pthread/include
|
||||||
|
endif
|
||||||
|
|
||||||
|
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