Skip to content

Commit 15b9e8a

Browse files
committed
pthread: facilitate dynamically allocated thread stacks
This change allows users to call pthread_create(3) with the pthread_attr_t argument equal to NULL, or with the pthread_attr_t argument specifying a NULL stack but a custom stack size. If either of the above to requirements are met, then a stack will be heap-allocated internally and freed again after the thread has terminated. This makes the Zephyr implementation of pthread_create(3) more compliant with the normative spec. Fixes zephyrproject-rtos#25973 Signed-off-by: Christopher Friedt <[email protected]>
1 parent 1323b1a commit 15b9e8a

File tree

2 files changed

+103
-35
lines changed

2 files changed

+103
-35
lines changed

lib/posix/posix_internal.h

+8-7
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,16 @@ struct posix_thread {
3333
/* Exit status */
3434
void *retval;
3535

36-
/* Pthread cancellation */
37-
uint8_t cancel_state;
38-
bool cancel_pending;
39-
40-
/* Detach state */
41-
uint8_t detachstate;
36+
/* Dynamic stack */
37+
k_thread_stack_t *dynamic_stack;
4238

4339
/* Queue ID (internal-only) */
44-
uint8_t qid;
40+
uint8_t qid: 2;
41+
/* Pthread cancellation */
42+
bool cancel_state: 1;
43+
bool cancel_pending: 1;
44+
/* Detach state */
45+
bool detachstate: 1;
4546
};
4647

4748
typedef struct pthread_key_obj {

lib/posix/pthread.c

+95-28
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@
1616
#include <zephyr/posix/pthread.h>
1717
#include <zephyr/sys/slist.h>
1818

19+
#ifdef CONFIG_PTHREAD_DYNAMIC_STACK_DEFAULT_SIZE
20+
#define DYNAMIC_STACK_SIZE CONFIG_PTHREAD_DYNAMIC_STACK_DEFAULT_SIZE
21+
#else
22+
#define DYNAMIC_STACK_SIZE 0
23+
#endif
24+
1925
#define PTHREAD_INIT_FLAGS PTHREAD_CANCEL_ENABLE
2026
#define PTHREAD_CANCELED ((void *) -1)
2127

@@ -34,6 +40,7 @@ BUILD_ASSERT((PTHREAD_CREATE_DETACHED == 0 || PTHREAD_CREATE_JOINABLE == 0) &&
3440
BUILD_ASSERT((PTHREAD_CANCEL_ENABLE == 0 || PTHREAD_CANCEL_DISABLE == 0) &&
3541
(PTHREAD_CANCEL_ENABLE == 1 || PTHREAD_CANCEL_DISABLE == 1));
3642

43+
static void posix_thread_recycle(void);
3744
static sys_dlist_t ready_q = SYS_DLIST_STATIC_INIT(&ready_q);
3845
static sys_dlist_t run_q = SYS_DLIST_STATIC_INIT(&run_q);
3946
static sys_dlist_t done_q = SYS_DLIST_STATIC_INIT(&done_q);
@@ -205,13 +212,13 @@ int pthread_attr_setstack(pthread_attr_t *_attr, void *stackaddr, size_t stacksi
205212

206213
static bool pthread_attr_is_valid(const struct pthread_attr *attr)
207214
{
208-
/*
209-
* FIXME: Pthread attribute must be non-null and it provides stack
210-
* pointer and stack size. So even though POSIX 1003.1 spec accepts
211-
* attrib as NULL but zephyr needs it initialized with valid stack.
212-
*/
213-
if (attr == NULL || attr->initialized == 0U || attr->stack == NULL ||
214-
attr->stacksize == 0) {
215+
/* auto-alloc thread stack */
216+
if (attr == NULL) {
217+
return true;
218+
}
219+
220+
/* caller-provided thread stack */
221+
if (attr->initialized == 0U || attr->stack == NULL || attr->stacksize == 0) {
215222
return false;
216223
}
217224

@@ -234,6 +241,13 @@ static bool pthread_attr_is_valid(const struct pthread_attr *attr)
234241
return true;
235242
}
236243

244+
static void posix_thread_recycle_work_handler(struct k_work *work)
245+
{
246+
ARG_UNUSED(work);
247+
posix_thread_recycle();
248+
}
249+
static K_WORK_DELAYABLE_DEFINE(posix_thread_recycle_work, posix_thread_recycle_work_handler);
250+
237251
static void posix_thread_finalize(struct posix_thread *t, void *retval)
238252
{
239253
sys_snode_t *node_l;
@@ -259,6 +273,9 @@ static void posix_thread_finalize(struct posix_thread *t, void *retval)
259273
t->retval = retval;
260274
k_spin_unlock(&pthread_pool_lock, key);
261275

276+
/* trigger recycle work */
277+
(void)k_work_schedule(&posix_thread_recycle_work, K_MSEC(100));
278+
262279
/* abort the underlying k_thread */
263280
k_thread_abort(&t->thread);
264281
}
@@ -283,6 +300,50 @@ static void zephyr_thread_wrapper(void *arg1, void *arg2, void *arg3)
283300
CODE_UNREACHABLE;
284301
}
285302

303+
static void posix_thread_recycle(void)
304+
{
305+
k_spinlock_key_t key;
306+
struct posix_thread *t;
307+
struct posix_thread *safe_t;
308+
sys_dlist_t recyclables = SYS_DLIST_STATIC_INIT(&recyclables);
309+
310+
key = k_spin_lock(&pthread_pool_lock);
311+
SYS_DLIST_FOR_EACH_CONTAINER_SAFE(&done_q, t, safe_t, q_node) {
312+
if (t->detachstate == PTHREAD_CREATE_JOINABLE) {
313+
/* thread has not been joined yet */
314+
continue;
315+
}
316+
317+
sys_dlist_remove(&t->q_node);
318+
sys_dlist_append(&recyclables, &t->q_node);
319+
}
320+
k_spin_unlock(&pthread_pool_lock, key);
321+
322+
if (sys_dlist_is_empty(&recyclables)) {
323+
return;
324+
}
325+
326+
if (IS_ENABLED(CONFIG_DYNAMIC_THREAD)) {
327+
SYS_DLIST_FOR_EACH_CONTAINER(&recyclables, t, q_node) {
328+
if (t->dynamic_stack != NULL) {
329+
if (k_is_user_context()) {
330+
(void)k_thread_stack_free(t->dynamic_stack);
331+
} else {
332+
(void)z_impl_k_thread_stack_free(t->dynamic_stack);
333+
}
334+
335+
t->dynamic_stack = NULL;
336+
}
337+
}
338+
}
339+
340+
key = k_spin_lock(&pthread_pool_lock);
341+
while (!sys_dlist_is_empty(&recyclables)) {
342+
sys_dlist_append(&ready_q, sys_dlist_get(&recyclables));
343+
}
344+
k_spin_unlock(&pthread_pool_lock, key);
345+
}
346+
286347
/**
287348
* @brief Create a new thread.
288349
*
@@ -297,32 +358,33 @@ int pthread_create(pthread_t *th, const pthread_attr_t *_attr, void *(*threadrou
297358
int err;
298359
k_spinlock_key_t key;
299360
pthread_barrier_t barrier;
300-
struct posix_thread *safe_t;
301361
struct posix_thread *t = NULL;
302-
const struct pthread_attr *attr = (const struct pthread_attr *)_attr;
362+
struct pthread_attr attr_storage = init_pthread_attrs;
363+
struct pthread_attr *attr = (struct pthread_attr *)_attr;
303364

304365
if (!pthread_attr_is_valid(attr)) {
305366
return EINVAL;
306367
}
307368

369+
if (attr == NULL && IS_ENABLED(CONFIG_DYNAMIC_THREAD)) {
370+
attr = &attr_storage;
371+
attr->stacksize = DYNAMIC_STACK_SIZE;
372+
attr->stack =
373+
k_thread_stack_alloc(attr->stacksize, k_is_user_context() ? K_USER : 0);
374+
if (attr->stack == NULL) {
375+
return EAGAIN;
376+
}
377+
} else {
378+
__ASSERT_NO_MSG(attr != &attr_storage);
379+
}
380+
381+
/* reclaim resources greedily */
382+
posix_thread_recycle();
383+
308384
key = k_spin_lock(&pthread_pool_lock);
309385
if (!sys_dlist_is_empty(&ready_q)) {
310-
/* spawn thread 't' directly from ready_q */
311386
t = CONTAINER_OF(sys_dlist_get(&ready_q), struct posix_thread, q_node);
312-
} else {
313-
SYS_DLIST_FOR_EACH_CONTAINER_SAFE(&done_q, t, safe_t, q_node) {
314-
if (t->detachstate == PTHREAD_CREATE_JOINABLE) {
315-
/* thread has not been joined yet */
316-
continue;
317-
}
318387

319-
/* spawn thread 't' from done_q */
320-
sys_dlist_remove(&t->q_node);
321-
break;
322-
}
323-
}
324-
325-
if (t != NULL) {
326388
/* initialize thread state */
327389
sys_dlist_append(&run_q, &t->q_node);
328390
t->qid = POSIX_THREAD_RUN_Q;
@@ -332,12 +394,22 @@ int pthread_create(pthread_t *th, const pthread_attr_t *_attr, void *(*threadrou
332394
}
333395
t->cancel_pending = false;
334396
sys_slist_init(&t->key_list);
397+
t->dynamic_stack = attr == &attr_storage ? attr->stack : NULL;
335398
}
336399
k_spin_unlock(&pthread_pool_lock, key);
337400

401+
if (t == NULL) {
402+
/* no threads are ready */
403+
return EAGAIN;
404+
}
405+
338406
if (IS_ENABLED(CONFIG_PTHREAD_CREATE_BARRIER)) {
339407
err = pthread_barrier_init(&barrier, NULL, 2);
340408
if (err != 0) {
409+
if (t->dynamic_stack != NULL) {
410+
(void)k_thread_stack_free(attr->stack);
411+
}
412+
341413
/* cannot allocate barrier. move thread back to ready_q */
342414
key = k_spin_lock(&pthread_pool_lock);
343415
sys_dlist_remove(&t->q_node);
@@ -348,11 +420,6 @@ int pthread_create(pthread_t *th, const pthread_attr_t *_attr, void *(*threadrou
348420
}
349421
}
350422

351-
if (t == NULL) {
352-
/* no threads are ready */
353-
return EAGAIN;
354-
}
355-
356423
/* spawn the thread */
357424
k_thread_create(&t->thread, attr->stack, attr->stacksize, zephyr_thread_wrapper,
358425
(void *)arg, threadroutine,

0 commit comments

Comments
 (0)