diff --git a/include/zephyr/posix/pthread_key.h b/include/zephyr/posix/pthread_key.h index 179ebda9e9d..a572a045a0d 100644 --- a/include/zephyr/posix/pthread_key.h +++ b/include/zephyr/posix/pthread_key.h @@ -18,30 +18,7 @@ extern "C" { typedef uint32_t pthread_once_t; /* pthread_key */ -typedef void *pthread_key_t; - -typedef struct pthread_key_obj { - /* List of pthread_key_data objects that contain thread - * specific data for the key - */ - sys_slist_t key_data_l; - - /* Optional destructor that is passed to pthread_key_create() */ - void (*destructor)(void *); -} pthread_key_obj; - -typedef struct pthread_thread_data { - sys_snode_t node; - - /* Key and thread specific data passed to pthread_setspecific() */ - pthread_key_obj *key; - void *spec_data; -} pthread_thread_data; - -typedef struct pthread_key_data { - sys_snode_t node; - pthread_thread_data thread_data; -} pthread_key_data; +typedef uint32_t pthread_key_t; #ifdef __cplusplus } diff --git a/lib/posix/Kconfig b/lib/posix/Kconfig index 1221a1c2e69..9fe5830c302 100644 --- a/lib/posix/Kconfig +++ b/lib/posix/Kconfig @@ -49,6 +49,13 @@ config MAX_PTHREAD_COND_COUNT help Maximum number of simultaneously active condition variables in a POSIX application. +config MAX_PTHREAD_KEY_COUNT + int "Maximum simultaneously active keys in a POSIX application" + default 5 + range 0 255 + help + Maximum number of simultaneously active keys in a POSIX application. + config SEM_VALUE_MAX int "Maximum semaphore limit" default 32767 diff --git a/lib/posix/posix_internal.h b/lib/posix/posix_internal.h index e6e04ef0423..eb0d2ff6673 100644 --- a/lib/posix/posix_internal.h +++ b/lib/posix/posix_internal.h @@ -55,6 +55,29 @@ struct posix_thread { pthread_cond_t state_cond; }; +typedef struct pthread_key_obj { + /* List of pthread_key_data objects that contain thread + * specific data for the key + */ + sys_slist_t key_data_l; + + /* Optional destructor that is passed to pthread_key_create() */ + void (*destructor)(void *value); +} pthread_key_obj; + +typedef struct pthread_thread_data { + sys_snode_t node; + + /* Key and thread specific data passed to pthread_setspecific() */ + pthread_key_obj *key; + void *spec_data; +} pthread_thread_data; + +typedef struct pthread_key_data { + sys_snode_t node; + pthread_thread_data thread_data; +} pthread_key_data; + static inline bool is_pthread_obj_initialized(uint32_t obj) { return (obj & PTHREAD_OBJ_MASK_INIT) != 0; @@ -84,4 +107,10 @@ struct posix_cond *to_posix_cond(pthread_cond_t *cvar); /* get a previously initialized posix_cond */ struct posix_cond *get_posix_cond(pthread_cond_t cond); +/* get and possibly initialize a posix_key */ +pthread_key_obj *to_posix_key(pthread_key_t *keyp); + +/* get a previously initialized posix_key */ +pthread_key_obj *get_posix_key(pthread_key_t key); + #endif diff --git a/lib/posix/pthread_key.c b/lib/posix/pthread_key.c index 490fe114342..26ce4d8d453 100644 --- a/lib/posix/pthread_key.c +++ b/lib/posix/pthread_key.c @@ -6,11 +6,84 @@ #include #include #include +#include #include "posix_internal.h" static struct k_spinlock pthread_key_lock; +/* This is non-standard (i.e. an implementation detail) */ +#define PTHREAD_KEY_INITIALIZER (-1) + +/* + * We reserve the MSB to mark a pthread_key_t as initialized (from the + * perspective of the application). With a linear space, this means that + * the theoretical pthread_key_t range is [0,2147483647]. + */ +BUILD_ASSERT(CONFIG_MAX_PTHREAD_KEY_COUNT < PTHREAD_OBJ_MASK_INIT, + "CONFIG_MAX_PTHREAD_KEY_COUNT is too high"); + +static pthread_key_obj posix_key_pool[CONFIG_MAX_PTHREAD_KEY_COUNT]; +SYS_BITARRAY_DEFINE_STATIC(posix_key_bitarray, CONFIG_MAX_PTHREAD_KEY_COUNT); + +static inline size_t posix_key_to_offset(pthread_key_obj *k) +{ + return k - posix_key_pool; +} + +static inline size_t to_posix_key_idx(pthread_key_t key) +{ + return mark_pthread_obj_uninitialized(key); +} + +pthread_key_obj *get_posix_key(pthread_key_t key) +{ + int actually_initialized; + size_t bit = to_posix_key_idx(key); + + /* if the provided cond does not claim to be initialized, its invalid */ + if (!is_pthread_obj_initialized(key)) { + return NULL; + } + + /* Mask off the MSB to get the actual bit index */ + if (sys_bitarray_test_bit(&posix_key_bitarray, bit, &actually_initialized) < 0) { + return NULL; + } + + if (actually_initialized == 0) { + /* The cond claims to be initialized but is actually not */ + return NULL; + } + + return &posix_key_pool[bit]; +} + +pthread_key_obj *to_posix_key(pthread_key_t *key) +{ + size_t bit; + pthread_key_obj *k; + + if (*key != PTHREAD_KEY_INITIALIZER) { + return get_posix_key(*key); + } + + /* Try and automatically associate a pthread_key_obj */ + if (sys_bitarray_alloc(&posix_key_bitarray, 1, &bit) < 0) { + /* No keys left to allocate */ + return NULL; + } + + /* Record the associated posix_cond in mu and mark as initialized */ + *key = mark_pthread_obj_initialized(bit); + k = &posix_key_pool[bit]; + + /* Initialize the condition variable here */ + memset(k, 0, sizeof(*k)); + + return k; +} + /** * @brief Create a key for thread-specific data * @@ -21,10 +94,8 @@ int pthread_key_create(pthread_key_t *key, { pthread_key_obj *new_key; - *key = NULL; - - new_key = k_malloc(sizeof(pthread_key_obj)); - + *key = PTHREAD_KEY_INITIALIZER; + new_key = to_posix_key(key); if (new_key == NULL) { return ENOMEM; } @@ -32,7 +103,6 @@ int pthread_key_create(pthread_key_t *key, sys_slist_init(&(new_key->key_data_l)); new_key->destructor = destructor; - *key = (void *)new_key; return 0; } @@ -44,13 +114,19 @@ int pthread_key_create(pthread_key_t *key, */ int pthread_key_delete(pthread_key_t key) { - pthread_key_obj *key_obj = (pthread_key_obj *)key; + pthread_key_obj *key_obj; pthread_key_data *key_data; sys_snode_t *node_l, *next_node_l; k_spinlock_key_t key_key; key_key = k_spin_lock(&pthread_key_lock); + key_obj = get_posix_key(key); + if (key_obj == NULL) { + k_spin_unlock(&pthread_key_lock, key_key); + return EINVAL; + } + /* Delete thread-specific elements associated with the key */ SYS_SLIST_FOR_EACH_NODE_SAFE(&(key_obj->key_data_l), node_l, next_node_l) { @@ -63,7 +139,7 @@ int pthread_key_delete(pthread_key_t key) k_free((void *)key_data); } - k_free((void *)key_obj); + (void)sys_bitarray_free(&posix_key_bitarray, 1, 0); k_spin_unlock(&pthread_key_lock, key_key); @@ -77,7 +153,7 @@ int pthread_key_delete(pthread_key_t key) */ int pthread_setspecific(pthread_key_t key, const void *value) { - pthread_key_obj *key_obj = (pthread_key_obj *)key; + pthread_key_obj *key_obj; struct posix_thread *thread = to_posix_thread(pthread_self()); pthread_key_data *key_data; pthread_thread_data *thread_spec_data; @@ -91,6 +167,12 @@ int pthread_setspecific(pthread_key_t key, const void *value) */ key_key = k_spin_lock(&pthread_key_lock); + key_obj = get_posix_key(key); + if (key_obj == NULL) { + k_spin_unlock(&pthread_key_lock, key_key); + return EINVAL; + } + SYS_SLIST_FOR_EACH_NODE(&(thread->key_list), node_l) { thread_spec_data = (pthread_thread_data *)node_l; @@ -114,7 +196,7 @@ int pthread_setspecific(pthread_key_t key, const void *value) } else { /* Associate thread specific data, initialize new key */ - key_data->thread_data.key = key; + key_data->thread_data.key = key_obj; key_data->thread_data.spec_data = (void *)value; /* Append new thread key data to thread's key list */ @@ -140,7 +222,7 @@ out: */ void *pthread_getspecific(pthread_key_t key) { - pthread_key_obj *key_obj = (pthread_key_obj *)key; + pthread_key_obj *key_obj; struct posix_thread *thread = to_posix_thread(pthread_self()); pthread_thread_data *thread_spec_data; void *value = NULL; @@ -149,6 +231,12 @@ void *pthread_getspecific(pthread_key_t key) key_key = k_spin_lock(&pthread_key_lock); + key_obj = get_posix_key(key); + if (key_obj == NULL) { + k_spin_unlock(&pthread_key_lock, key_key); + return NULL; + } + node_l = sys_slist_peek_head(&(thread->key_list)); /* Traverse the list of keys set by the thread, looking for key */