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