zephyr/lib/posix/options/key.c
Christopher Friedt f3709e20b8 posix: convert all error logs to debug logs
In Zephyr, things are often optimized for size first. That's how
we fit into such tight parking spaces.

This change gives more control to the user about whether the
POSIX API does any logging at all, simultaneously shrinking binary
size while improving speed.

No bytes / cycles left behind!

Signed-off-by: Christopher Friedt <cfriedt@meta.com>
2024-02-24 10:34:05 -05:00

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_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);
}
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;
}