/* * Copyright (c) 2018 Intel Corporation * Copyright (c) 2023 Meta * * SPDX-License-Identifier: Apache-2.0 */ #include "posix_internal.h" #include #include #include #include #include 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; }