1
0
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:
Oleg Hahm 2014-04-09 00:19:17 +02:00
commit 8a86f493b4
6 changed files with 269 additions and 0 deletions

View File

@ -96,4 +96,6 @@ void pthread_testcancel(void);
#include "pthread_barrier.h"
#include "pthread_cleanup.h"
#endif /* pthread.h */

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

View File

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

View File

@ -0,0 +1,6 @@
PROJECT = test_pthread_cleanup
include ../Makefile.tests_common
USEMODULE += pthread
include $(RIOTBASE)/Makefile.include

View 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;
}

View 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