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. */
|
||||
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_rwlock.h"
|
||||
|
@ -33,4 +33,11 @@ typedef unsigned long int pthread_rwlockattr_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_ */
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
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