/* * Copyright (c) 2018 Intel Corporation * Copyright (c) 2023 Meta * * SPDX-License-Identifier: Apache-2.0 */ #include "posix_internal.h" #include #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 SYS_SEM_DEFINE(pthread_key_lock, 1, 1); /* 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; int ret = EINVAL; pthread_key_obj *key_obj = NULL; struct pthread_key_data *key_data; sys_snode_t *node_l, *next_node_l; SYS_SEM_LOCK(&pthread_key_lock) { key_obj = get_posix_key(key); if (key_obj == NULL) { ret = EINVAL; SYS_SEM_LOCK_BREAK; } /* 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); } if (ret == 0) { LOG_DBG("Deleted key %p (%x)", key_obj, key); } return ret; } /** * @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 = NULL; struct posix_thread *thread; struct pthread_key_data *key_data; sys_snode_t *node_l = NULL; 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. */ SYS_SEM_LOCK(&pthread_key_lock) { key_obj = get_posix_key(key); if (key_obj == NULL) { retval = EINVAL; SYS_SEM_LOCK_BREAK; } SYS_SLIST_FOR_EACH_NODE(&(thread->key_list), node_l) { pthread_thread_data *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()); break; } } retval = 0; if (node_l != NULL) { /* Key is already present, so we are done */ SYS_SEM_LOCK_BREAK; } /* Key and data need to be added */ 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; SYS_SEM_LOCK_BREAK; } 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()); } 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; thread = to_posix_thread(pthread_self()); if (thread == NULL) { return NULL; } SYS_SEM_LOCK(&pthread_key_lock) { key_obj = get_posix_key(key); if (key_obj == NULL) { value = NULL; SYS_SEM_LOCK_BREAK; } /* 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; } } } return value; }