posix: pthread: implement pthread_cleanup_push() / pop()

pthread_cleanup_push() and pthread_cleanup_pop() are required
by the POSIX_THREADS_BASE Option Group as detailed in Section
E.1 of IEEE-1003.1-2017.

The POSIX_THREADS_BASE Option Group is required for PSE51,
PSE52, PSE53, and PSE54 conformance, and is otherwise mandatory
for any POSIX conforming system as per Section A.2.1.3 of
IEEE-1003-1.2017.

In this change, we require the addition of a dedicated
pthread_key_t that will not be available for applilcation usage.

Rather than including that as part of
CONFIG_MAX_PTHREAD_KEY_COUNT, we increase the storage by 1 in
order to be least invasive from the application perspective.

Signed-off-by: Christopher Friedt <cfriedt@meta.com>
This commit is contained in:
Christopher Friedt 2023-11-23 12:04:16 -05:00 committed by Carles Cufí
parent f4a039e218
commit fb695c42fb
3 changed files with 62 additions and 0 deletions

View file

@ -480,6 +480,18 @@ int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(vo
int pthread_getconcurrency(void);
int pthread_setconcurrency(int new_level);
void __z_pthread_cleanup_push(void *cleanup[3], void (*routine)(void *arg), void *arg);
void __z_pthread_cleanup_pop(int execute);
#define pthread_cleanup_push(_rtn, _arg) \
do /* enforce '{'-like behaviour */ { \
void *_z_pthread_cleanup[3]; \
__z_pthread_cleanup_push(_z_pthread_cleanup, _rtn, _arg)
#define pthread_cleanup_pop(_ex) \
__z_pthread_cleanup_pop(_ex); \
} /* enforce '}'-like behaviour */ while (0)
/* Glibc / Oracle Extension Functions */
/**

View file

@ -24,6 +24,9 @@
struct posix_thread {
struct k_thread thread;
/* List nodes for pthread_cleanup_push() / pthread_cleanup_pop() */
sys_slist_t cleanup_list;
/* List node for ready_q, run_q, or done_q */
sys_dnode_t q_node;

View file

@ -30,6 +30,12 @@ LOG_MODULE_REGISTER(pthread, CONFIG_PTHREAD_LOG_LEVEL);
#define PTHREAD_INIT_FLAGS PTHREAD_CANCEL_ENABLE
struct __pthread_cleanup {
void (*routine)(void *arg);
void *arg;
sys_snode_t node;
};
enum posix_thread_qid {
/* ready to be started via pthread_create() */
POSIX_THREAD_READY_Q,
@ -142,6 +148,46 @@ int pthread_equal(pthread_t pt1, pthread_t pt2)
return (pt1 == pt2);
}
static inline void __z_pthread_cleanup_init(struct __pthread_cleanup *c, void (*routine)(void *arg),
void *arg)
{
*c = (struct __pthread_cleanup){
.routine = routine,
.arg = arg,
.node = {0},
};
}
void __z_pthread_cleanup_push(void *cleanup[3], void (*routine)(void *arg), void *arg)
{
struct posix_thread *const t = to_posix_thread(pthread_self());
struct __pthread_cleanup *const c = (struct __pthread_cleanup *)cleanup;
BUILD_ASSERT(3 * sizeof(void *) == sizeof(*c));
__ASSERT_NO_MSG(t != NULL);
__ASSERT_NO_MSG(c != NULL);
__ASSERT_NO_MSG(routine != NULL);
__z_pthread_cleanup_init(c, routine, arg);
sys_slist_prepend(&t->cleanup_list, &c->node);
}
void __z_pthread_cleanup_pop(int execute)
{
sys_snode_t *node;
struct __pthread_cleanup *c;
struct posix_thread *const t = to_posix_thread(pthread_self());
__ASSERT_NO_MSG(t != NULL);
node = sys_slist_get(&t->cleanup_list);
__ASSERT_NO_MSG(node != NULL);
c = CONTAINER_OF(node, struct __pthread_cleanup, node);
__ASSERT_NO_MSG(c != NULL);
__ASSERT_NO_MSG(c->routine != NULL);
if (execute) {
c->routine(c->arg);
}
}
static bool is_posix_policy_prio_valid(uint32_t priority, int policy)
{
if (priority >= sched_get_priority_min(policy) &&
@ -414,6 +460,7 @@ int pthread_create(pthread_t *th, const pthread_attr_t *_attr, void *(*threadrou
}
t->cancel_pending = false;
sys_slist_init(&t->key_list);
sys_slist_init(&t->cleanup_list);
t->dynamic_stack = _attr == NULL ? attr->stack : NULL;
}
k_spin_unlock(&pthread_pool_lock, key);