posix: pthread_attr: use space in struct more efficiently

This change reduces the space occupied by struct pthread_attr
which is the internal type used for pthread_attr_t.

We cap the stack size at 16 bits (so up to 65536 bytes) and
since a stack size of 0 is invalid, we can encode the stack
size by simply subtracting 1 or adding 1 when setting or
getting.

The schedpolicy is capped at 2 bits and initialized,
cancellable, and detached are given 1 bit.

Signed-off-by: Christopher Friedt <cfriedt@meta.com>
This commit is contained in:
Christopher Friedt 2023-12-13 19:43:10 -05:00 committed by Fabio Baltieri
commit eb7e4bed98
3 changed files with 84 additions and 78 deletions

View file

@ -37,14 +37,13 @@ typedef unsigned long timer_t;
/* Thread attributes */
struct pthread_attr {
int priority;
void *stack;
uint32_t stacksize;
uint32_t flags;
uint32_t delayedstart;
uint32_t schedpolicy;
int32_t detachstate;
uint32_t initialized;
uint16_t stacksize;
int8_t priority;
uint8_t schedpolicy: 2;
bool initialized: 1;
bool cancelstate: 1;
bool detachstate: 1;
};
#if defined(CONFIG_MINIMAL_LIBC) || defined(CONFIG_PICOLIBC) || defined(CONFIG_ARMCLANG_STD_LIBC) \
|| defined(CONFIG_ARCMWDT_LIBC)

View file

@ -19,9 +19,14 @@
extern "C" {
#endif
/* Pthread detach/joinable */
#define PTHREAD_CREATE_DETACHED 0
#define PTHREAD_CREATE_JOINABLE 1
/*
* Pthread detach/joinable
* Undefine possibly predefined values by external toolchain headers
*/
#undef PTHREAD_CREATE_DETACHED
#define PTHREAD_CREATE_DETACHED 1
#undef PTHREAD_CREATE_JOINABLE
#define PTHREAD_CREATE_JOINABLE 0
/* Pthread resource visibility */
#define PTHREAD_PROCESS_PRIVATE 0

View file

@ -18,6 +18,17 @@
#include <zephyr/sys/slist.h>
#include <zephyr/sys/util.h>
#define ZEPHYR_TO_POSIX_PRIORITY(_zprio) \
(((_zprio) < 0) ? (-1 * ((_zprio) + 1)) : (CONFIG_NUM_PREEMPT_PRIORITIES - (_zprio)-1))
#define POSIX_TO_ZEPHYR_PRIORITY(_prio, _pol) \
(((_pol) == SCHED_FIFO) ? (-1 * ((_prio) + 1)) \
: (CONFIG_NUM_PREEMPT_PRIORITIES - (_prio)-1))
#define DEFAULT_PTHREAD_PRIORITY \
POSIX_TO_ZEPHYR_PRIORITY(K_LOWEST_APPLICATION_THREAD_PRIO, DEFAULT_PTHREAD_POLICY)
#define DEFAULT_PTHREAD_POLICY (IS_ENABLED(CONFIG_PREEMPT_ENABLED) ? SCHED_RR : SCHED_FIFO)
LOG_MODULE_REGISTER(pthread, CONFIG_PTHREAD_LOG_LEVEL);
#ifdef CONFIG_DYNAMIC_THREAD_STACK_SIZE
@ -26,9 +37,18 @@ LOG_MODULE_REGISTER(pthread, CONFIG_PTHREAD_LOG_LEVEL);
#define DYNAMIC_STACK_SIZE 0
#endif
#define _pthread_cancel_pos LOG2(PTHREAD_CANCEL_DISABLE)
/* The maximum allowed stack size (for this implementation) */
#define PTHREAD_STACK_MAX (UINT16_MAX + 1)
#define PTHREAD_INIT_FLAGS PTHREAD_CANCEL_ENABLE
static inline size_t __get_attr_stacksize(const struct pthread_attr *attr)
{
return attr->stacksize + 1;
}
static inline void __set_attr_stacksize(struct pthread_attr *attr, size_t size)
{
attr->stacksize = size - 1;
}
struct __pthread_cleanup {
void (*routine)(void *arg);
@ -45,6 +65,9 @@ enum posix_thread_qid {
POSIX_THREAD_DONE_Q,
};
/* only 2 bits in struct pthread_attr for schedpolicy */
BUILD_ASSERT(SCHED_OTHER < BIT(2) && SCHED_FIFO < BIT(2) && SCHED_RR < BIT(2));
BUILD_ASSERT((PTHREAD_CREATE_DETACHED == 0 || PTHREAD_CREATE_JOINABLE == 0) &&
(PTHREAD_CREATE_DETACHED == 1 || PTHREAD_CREATE_JOINABLE == 1));
@ -60,18 +83,13 @@ static struct k_spinlock pthread_pool_lock;
static int pthread_concurrency;
static const struct pthread_attr init_pthread_attrs = {
.priority = 0,
.stack = NULL,
.stacksize = 0,
.flags = PTHREAD_INIT_FLAGS,
.delayedstart = 0,
#if defined(CONFIG_PREEMPT_ENABLED)
.schedpolicy = SCHED_RR,
#else
.schedpolicy = SCHED_FIFO,
#endif
.detachstate = PTHREAD_CREATE_JOINABLE,
.priority = DEFAULT_PTHREAD_PRIORITY,
.schedpolicy = DEFAULT_PTHREAD_POLICY,
.initialized = true,
.cancelstate = PTHREAD_CANCEL_ENABLE,
.detachstate = PTHREAD_CREATE_JOINABLE,
};
/*
@ -201,35 +219,27 @@ static bool is_posix_policy_prio_valid(uint32_t priority, int policy)
static uint32_t zephyr_to_posix_priority(int32_t z_prio, int *policy)
{
uint32_t prio;
if (z_prio < 0) {
*policy = SCHED_FIFO;
prio = -1 * (z_prio + 1);
__ASSERT_NO_MSG(prio < CONFIG_NUM_COOP_PRIORITIES);
__ASSERT_NO_MSG(z_prio < CONFIG_NUM_COOP_PRIORITIES);
} else {
*policy = SCHED_RR;
prio = (CONFIG_NUM_PREEMPT_PRIORITIES - z_prio - 1);
__ASSERT_NO_MSG(prio < CONFIG_NUM_PREEMPT_PRIORITIES);
__ASSERT_NO_MSG(z_prio < CONFIG_NUM_PREEMPT_PRIORITIES);
}
return prio;
*policy = (z_prio < 0) ? SCHED_FIFO : SCHED_RR;
return ZEPHYR_TO_POSIX_PRIORITY(z_prio);
}
static int32_t posix_to_zephyr_priority(uint32_t priority, int policy)
{
int32_t prio;
if (policy == SCHED_FIFO) {
/* Zephyr COOP priority starts from -1 */
/* COOP: highest [CONFIG_NUM_COOP_PRIORITIES, -1] lowest */
__ASSERT_NO_MSG(priority < CONFIG_NUM_COOP_PRIORITIES);
prio = -1 * (priority + 1);
} else {
/* PREEMPT: lowest [0, CONFIG_NUM_PREEMPT_PRIORITIES - 1] highest */
__ASSERT_NO_MSG(priority < CONFIG_NUM_PREEMPT_PRIORITIES);
prio = (CONFIG_NUM_PREEMPT_PRIORITIES - priority - 1);
}
return prio;
return POSIX_TO_ZEPHYR_PRIORITY(priority, policy);
}
/**
@ -242,8 +252,8 @@ int pthread_attr_setschedparam(pthread_attr_t *_attr, const struct sched_param *
struct pthread_attr *attr = (struct pthread_attr *)_attr;
int priority = schedparam->sched_priority;
if ((attr == NULL) || (attr->initialized == 0U) ||
(is_posix_policy_prio_valid(priority, attr->schedpolicy) == false)) {
if (attr == NULL || !attr->initialized ||
is_posix_policy_prio_valid(priority, attr->schedpolicy == false)) {
LOG_ERR("Invalid pthread_attr_t or sched_param");
return EINVAL;
}
@ -266,8 +276,13 @@ int pthread_attr_setstack(pthread_attr_t *_attr, void *stackaddr, size_t stacksi
return EACCES;
}
if (stacksize == 0 || stacksize < PTHREAD_STACK_MIN || stacksize > PTHREAD_STACK_MAX) {
LOG_ERR("Invalid stacksize %zu", stacksize);
return EINVAL;
}
attr->stack = stackaddr;
attr->stacksize = stacksize;
__set_attr_stacksize(attr, stacksize);
return 0;
}
@ -279,8 +294,9 @@ static bool pthread_attr_is_valid(const struct pthread_attr *attr)
}
/* caller-provided thread stack */
if (attr->initialized == 0U || attr->stack == NULL || attr->stacksize == 0) {
LOG_ERR("pthread_attr_t is not initialized, has a NULL stack, or is of size 0");
if (!attr->initialized || attr->stack == NULL || attr->stacksize == 0 ||
__get_attr_stacksize(attr) < PTHREAD_STACK_MIN) {
LOG_ERR("pthread_attr_t is not initialized, has a NULL stack, or invalid size");
return false;
}
@ -290,19 +306,6 @@ static bool pthread_attr_is_valid(const struct pthread_attr *attr)
return false;
}
/* require a valid detachstate */
if (!(attr->detachstate == PTHREAD_CREATE_JOINABLE ||
attr->detachstate == PTHREAD_CREATE_DETACHED)) {
LOG_ERR("Invalid detachstate %d", attr->detachstate);
return false;
}
/* we cannot create an essential thread (i.e. one that may not abort) */
if ((attr->flags & K_ESSENTIAL) != 0) {
LOG_ERR("Cannot create an essential thread");
return false;
}
return true;
}
@ -431,11 +434,12 @@ int pthread_create(pthread_t *th, const pthread_attr_t *_attr, void *(*threadrou
if (attr == NULL) {
attr = &attr_storage;
attr->stacksize = DYNAMIC_STACK_SIZE;
attr->stack =
k_thread_stack_alloc(attr->stacksize, k_is_user_context() ? K_USER : 0);
BUILD_ASSERT(DYNAMIC_STACK_SIZE <= PTHREAD_STACK_MAX);
__set_attr_stacksize(attr, DYNAMIC_STACK_SIZE);
attr->stack = k_thread_stack_alloc(__get_attr_stacksize(attr),
k_is_user_context() ? K_USER : 0);
if (attr->stack == NULL) {
LOG_ERR("Unable to allocate stack of size %u", attr->stacksize);
LOG_ERR("Unable to allocate stack of size %u", DYNAMIC_STACK_SIZE);
return EAGAIN;
}
LOG_DBG("Allocated thread stack %p", attr->stack);
@ -454,9 +458,7 @@ int pthread_create(pthread_t *th, const pthread_attr_t *_attr, void *(*threadrou
sys_dlist_append(&run_q, &t->q_node);
t->qid = POSIX_THREAD_RUN_Q;
t->detachstate = attr->detachstate;
if ((BIT(_pthread_cancel_pos) & attr->flags) != 0) {
t->cancel_state = PTHREAD_CANCEL_ENABLE;
}
t->cancel_state = attr->cancelstate;
t->cancel_pending = false;
sys_slist_init(&t->key_list);
sys_slist_init(&t->cleanup_list);
@ -491,10 +493,8 @@ int pthread_create(pthread_t *th, const pthread_attr_t *_attr, void *(*threadrou
/* spawn the thread */
k_thread_create(&t->thread, attr->stack, attr->stacksize, zephyr_thread_wrapper,
(void *)arg, threadroutine,
IS_ENABLED(CONFIG_PTHREAD_CREATE_BARRIER) ? UINT_TO_POINTER(barrier)
: NULL,
posix_to_zephyr_priority(attr->priority, attr->schedpolicy), attr->flags,
K_MSEC(attr->delayedstart));
IS_ENABLED(CONFIG_PTHREAD_CREATE_BARRIER) ? UINT_TO_POINTER(barrier) : NULL,
posix_to_zephyr_priority(attr->priority, attr->schedpolicy), 0, K_NO_WAIT);
if (IS_ENABLED(CONFIG_PTHREAD_CREATE_BARRIER)) {
/* wait for the spawned thread to cross our barrier */
@ -664,15 +664,16 @@ int pthread_setschedparam(pthread_t pthread, int policy, const struct sched_para
*
* See IEEE 1003.1
*/
int pthread_attr_init(pthread_attr_t *attr)
int pthread_attr_init(pthread_attr_t *_attr)
{
struct pthread_attr *const attr = (struct pthread_attr *)_attr;
if (attr == NULL) {
LOG_ERR("Invalid attr pointer");
return ENOMEM;
}
(void)memcpy(attr, &init_pthread_attrs, sizeof(pthread_attr_t));
(void)memcpy(attr, &init_pthread_attrs, sizeof(struct pthread_attr));
return 0;
}
@ -747,7 +748,7 @@ void pthread_exit(void *retval)
CODE_UNREACHABLE;
}
/* Make a thread as cancelable before exiting */
/* Mark a thread as cancellable before exiting */
key = k_spin_lock(&pthread_pool_lock);
self->cancel_state = PTHREAD_CANCEL_ENABLE;
k_spin_unlock(&pthread_pool_lock, key);
@ -870,7 +871,7 @@ int pthread_attr_getdetachstate(const pthread_attr_t *_attr, int *detachstate)
{
const struct pthread_attr *attr = (const struct pthread_attr *)_attr;
if ((attr == NULL) || (attr->initialized == 0U)) {
if ((attr == NULL) || (attr->initialized == false)) {
return EINVAL;
}
@ -887,8 +888,9 @@ int pthread_attr_setdetachstate(pthread_attr_t *_attr, int detachstate)
{
struct pthread_attr *attr = (struct pthread_attr *)_attr;
if ((attr == NULL) || (attr->initialized == 0U) ||
(detachstate != PTHREAD_CREATE_DETACHED && detachstate != PTHREAD_CREATE_JOINABLE)) {
if ((attr == NULL) || (attr->initialized == false) ||
((detachstate != PTHREAD_CREATE_DETACHED) &&
(detachstate != PTHREAD_CREATE_JOINABLE))) {
return EINVAL;
}
@ -939,11 +941,11 @@ int pthread_attr_getstacksize(const pthread_attr_t *_attr, size_t *stacksize)
{
const struct pthread_attr *attr = (const struct pthread_attr *)_attr;
if ((attr == NULL) || (attr->initialized == 0U)) {
if ((attr == NULL) || (attr->initialized == false)) {
return EINVAL;
}
*stacksize = attr->stacksize;
*stacksize = __get_attr_stacksize(attr);
return 0;
}
@ -960,11 +962,11 @@ int pthread_attr_setstacksize(pthread_attr_t *_attr, size_t stacksize)
return EINVAL;
}
if (stacksize < PTHREAD_STACK_MIN) {
if (stacksize == 0 || stacksize < PTHREAD_STACK_MIN || stacksize > PTHREAD_STACK_MAX) {
return EINVAL;
}
attr->stacksize = stacksize;
__set_attr_stacksize(attr, stacksize);
return 0;
}
@ -977,12 +979,12 @@ int pthread_attr_getstack(const pthread_attr_t *_attr, void **stackaddr, size_t
{
const struct pthread_attr *attr = (const struct pthread_attr *)_attr;
if ((attr == NULL) || (attr->initialized == 0U)) {
if ((attr == NULL) || (attr->initialized == false)) {
return EINVAL;
}
*stackaddr = attr->stack;
*stacksize = attr->stacksize;
*stacksize = __get_attr_stacksize(attr);
return 0;
}
@ -995,7 +997,7 @@ int pthread_attr_getschedparam(const pthread_attr_t *_attr, struct sched_param *
{
struct pthread_attr *attr = (struct pthread_attr *)_attr;
if ((attr == NULL) || (attr->initialized == 0U)) {
if ((attr == NULL) || (attr->initialized == false)) {
return EINVAL;
}