Zephyr's POSIX API is moving toward using the standard nomenclature from IEEE 1003.1-2017 for as much as possible. In particular, we want to have consistent naming between Zephyr's POSIX API Kconfig options and the naming for POSIX Options and Option Groups. The Kconfig option CONFIG_PTHREAD_IPC has been (ab)used for a very long time for a variety of different purposes. However, the standard Option / feature test macro for POSIX Threads is, intuitively _POSIX_THREADS. There is a corresponding sysconf() key named _SC_POSIX_THREADS. Annoyingly, the POSIX Option Group that corresponds to the Option is POSIX_THREADS_BASE, which is a minor inconsistency in the standard. The _POSIX_THREADS Option already includes mutexes, condition variables, and thread-specific storage (keys). So with this change, we also deprecate the redundant Kconfig variables that do not have a corresponding match in the standard. - CONFIG_PTHREAD_IPC - CONFIG_PTHREAD - CONFIG_PTHREAD_COND - CONFIG_PTHREAD_MUTEX - CONFIG_PTHREAD_KEY Additionally, create Kconfig variables for those configurables which we are lacking: - CONFIG_POSIX_THREADS_EXT - CONFIG_POSIX_THREAD_ATTR_STACKSIZE - CONFIG_POSIX_THREAD_ATTR_STACKADDR - CONFIG_POSIX_THREAD_PRIORITY_SCHEDULING - CONFIG_POSIX_THREAD_PRIO_INHERIT - CONFIG_POSIX_THREAD_PRIO_PROTECT - CONFIG_POSIX_THREAD_SAFE_FUNCTIONS Some Kconfig variables were renamed to more properly match the spec: - CONFIG_MAX_PTHREAD_COUNT -> CONFIG_POSIX_THREAD_THREADS_MAX - CONFIG_MAX_PTHREAD_KEY_COUNT -> CONFIG_POSIX_THREAD_KEYS_MAX Signed-off-by: Chris Friedt <cfriedt@tenstorrent.com>
290 lines
6.9 KiB
C
290 lines
6.9 KiB
C
/*
|
|
* Copyright (c) 2018 Intel Corporation
|
|
* Copyright (c) 2023 Meta
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
#include "posix_internal.h"
|
|
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/logging/log.h>
|
|
#include <zephyr/posix/pthread.h>
|
|
#include <zephyr/sys/bitarray.h>
|
|
#include <zephyr/sys/__assert.h>
|
|
|
|
struct pthread_key_data {
|
|
sys_snode_t node;
|
|
pthread_thread_data thread_data;
|
|
};
|
|
|
|
LOG_MODULE_REGISTER(pthread_key, CONFIG_PTHREAD_KEY_LOG_LEVEL);
|
|
|
|
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_POSIX_THREAD_KEYS_MAX < PTHREAD_OBJ_MASK_INIT,
|
|
"CONFIG_MAX_PTHREAD_KEY_COUNT is too high");
|
|
|
|
static pthread_key_obj posix_key_pool[CONFIG_POSIX_THREAD_KEYS_MAX];
|
|
SYS_BITARRAY_DEFINE_STATIC(posix_key_bitarray, CONFIG_POSIX_THREAD_KEYS_MAX);
|
|
|
|
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);
|
|
}
|
|
|
|
static 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)) {
|
|
LOG_DBG("Key is uninitialized (%x)", 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) {
|
|
LOG_DBG("Key is invalid (%x)", key);
|
|
return NULL;
|
|
}
|
|
|
|
if (actually_initialized == 0) {
|
|
/* The cond claims to be initialized but is actually not */
|
|
LOG_DBG("Key claims to be initialized (%x)", key);
|
|
return NULL;
|
|
}
|
|
|
|
return &posix_key_pool[bit];
|
|
}
|
|
|
|
static 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
|
|
*
|
|
* See IEEE 1003.1
|
|
*/
|
|
int pthread_key_create(pthread_key_t *key,
|
|
void (*destructor)(void *))
|
|
{
|
|
pthread_key_obj *new_key;
|
|
|
|
*key = PTHREAD_KEY_INITIALIZER;
|
|
new_key = to_posix_key(key);
|
|
if (new_key == NULL) {
|
|
return ENOMEM;
|
|
}
|
|
|
|
sys_slist_init(&(new_key->key_data_l));
|
|
|
|
new_key->destructor = destructor;
|
|
LOG_DBG("Initialized key %p (%x)", new_key, *key);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Delete a key for thread-specific data
|
|
*
|
|
* See IEEE 1003.1
|
|
*/
|
|
int pthread_key_delete(pthread_key_t key)
|
|
{
|
|
size_t bit;
|
|
__unused int ret;
|
|
pthread_key_obj *key_obj;
|
|
struct 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) {
|
|
|
|
/* Remove the object from the list key_data_l */
|
|
key_data = (struct pthread_key_data *)
|
|
sys_slist_get(&(key_obj->key_data_l));
|
|
|
|
/* Deallocate the object's memory */
|
|
k_free((void *)key_data);
|
|
LOG_DBG("Freed key data %p for key %x in thread %x", key_data, key, pthread_self());
|
|
}
|
|
|
|
bit = posix_key_to_offset(key_obj);
|
|
ret = sys_bitarray_free(&posix_key_bitarray, 1, bit);
|
|
__ASSERT_NO_MSG(ret == 0);
|
|
|
|
k_spin_unlock(&pthread_key_lock, key_key);
|
|
|
|
LOG_DBG("Deleted key %p (%x)", key_obj, key);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Associate a thread-specific value with a key
|
|
*
|
|
* See IEEE 1003.1
|
|
*/
|
|
int pthread_setspecific(pthread_key_t key, const void *value)
|
|
{
|
|
pthread_key_obj *key_obj;
|
|
struct posix_thread *thread;
|
|
struct pthread_key_data *key_data;
|
|
pthread_thread_data *thread_spec_data;
|
|
k_spinlock_key_t key_key;
|
|
sys_snode_t *node_l;
|
|
int retval = 0;
|
|
|
|
thread = to_posix_thread(pthread_self());
|
|
if (thread == NULL) {
|
|
return EINVAL;
|
|
}
|
|
|
|
/* Traverse the list of keys set by the thread, looking for key.
|
|
* If the key is already in the list, re-assign its value.
|
|
* Else add the key to the thread's list.
|
|
*/
|
|
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;
|
|
|
|
if (thread_spec_data->key == key_obj) {
|
|
|
|
/* Key is already present so
|
|
* associate thread specific data
|
|
*/
|
|
thread_spec_data->spec_data = (void *)value;
|
|
LOG_DBG("Paired key %x to value %p for thread %x", key, value,
|
|
pthread_self());
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
if (node_l == NULL) {
|
|
key_data = k_malloc(sizeof(struct pthread_key_data));
|
|
|
|
if (key_data == NULL) {
|
|
LOG_DBG("Failed to allocate key data for key %x", key);
|
|
retval = ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
LOG_DBG("Allocated key data %p for key %x in thread %x", key_data, key,
|
|
pthread_self());
|
|
|
|
/* Associate thread specific data, initialize new 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 */
|
|
sys_slist_append((&thread->key_list), (sys_snode_t *)(&key_data->thread_data));
|
|
|
|
/* Append new key data to the key object's list */
|
|
sys_slist_append(&(key_obj->key_data_l), (sys_snode_t *)key_data);
|
|
|
|
LOG_DBG("Paired key %x to value %p for thread %x", key, value, pthread_self());
|
|
}
|
|
|
|
out:
|
|
k_spin_unlock(&pthread_key_lock, key_key);
|
|
|
|
return retval;
|
|
}
|
|
|
|
/**
|
|
* @brief Get the thread-specific value associated with the key
|
|
*
|
|
* See IEEE 1003.1
|
|
*/
|
|
void *pthread_getspecific(pthread_key_t key)
|
|
{
|
|
pthread_key_obj *key_obj;
|
|
struct posix_thread *thread;
|
|
pthread_thread_data *thread_spec_data;
|
|
void *value = NULL;
|
|
sys_snode_t *node_l;
|
|
k_spinlock_key_t key_key;
|
|
|
|
thread = to_posix_thread(pthread_self());
|
|
if (thread == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/* Traverse the list of keys set by the thread, looking for key */
|
|
|
|
SYS_SLIST_FOR_EACH_NODE(&(thread->key_list), node_l) {
|
|
thread_spec_data = (pthread_thread_data *)node_l;
|
|
if (thread_spec_data->key == key_obj) {
|
|
/* Key is present, so get the set thread data */
|
|
value = thread_spec_data->spec_data;
|
|
break;
|
|
}
|
|
}
|
|
|
|
k_spin_unlock(&pthread_key_lock, key_key);
|
|
|
|
return value;
|
|
}
|