Linux Audio

Check our new training course

Loading...
/*
 * 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;
}