blob: 6b2573c01492a438f4c54e8ab964796148e63a00 [file] [log] [blame]
/*
* Copyright (c) 2018 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <zephyr/posix/pthread.h>
#include <zephyr/posix/pthread_key.h>
struct k_sem pthread_key_sem;
K_SEM_DEFINE(pthread_key_sem, 1, 1);
/**
* @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 = NULL;
new_key = k_malloc(sizeof(pthread_key_obj));
if (new_key == NULL) {
return ENOMEM;
}
sys_slist_init(&(new_key->key_data_l));
new_key->destructor = destructor;
*key = (void *)new_key;
return 0;
}
/**
* @brief Delete a key for thread-specific data
*
* See IEEE 1003.1
*/
int pthread_key_delete(pthread_key_t key)
{
pthread_key_obj *key_obj = (pthread_key_obj *)key;
pthread_key_data *key_data;
sys_snode_t *node_l, *next_node_l;
k_sem_take(&pthread_key_sem, K_FOREVER);
/* 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 = (pthread_key_data *)
sys_slist_get(&(key_obj->key_data_l));
/* Deallocate the object's memory */
k_free((void *)key_data);
}
k_free((void *)key_obj);
k_sem_give(&pthread_key_sem);
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 = (pthread_key_obj *)key;
struct posix_thread *thread = (struct posix_thread *)pthread_self();
pthread_key_data *key_data;
pthread_thread_data *thread_spec_data;
sys_snode_t *node_l;
int retval = 0;
/* 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.
*/
k_sem_take(&pthread_key_sem, K_FOREVER);
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;
goto out;
}
}
if (node_l == NULL) {
key_data = k_malloc(sizeof(pthread_key_data));
if (key_data == NULL) {
retval = ENOMEM;
goto out;
} else {
/* Associate thread specific data, initialize new key */
key_data->thread_data.key = key;
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);
}
}
out:
k_sem_give(&pthread_key_sem);
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 = (pthread_key_obj *)key;
struct posix_thread *thread = (struct posix_thread *)pthread_self();
pthread_thread_data *thread_spec_data;
void *value = NULL;
sys_snode_t *node_l;
k_sem_take(&pthread_key_sem, K_FOREVER);
node_l = sys_slist_peek_head(&(thread->key_list));
/* 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_sem_give(&pthread_key_sem);
return value;
}