1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/sys/posix/pthread/pthread_tls.c
2020-10-23 11:27:48 +02:00

201 lines
4.8 KiB
C

/*
* Copyright (C) 2014 Hamburg University of Applied Sciences (HAW)
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup pthread
* @{
* @file
* @brief RIOT POSIX thread local storage
* @author Martin Landsmann <martin.landsmann@haw-hamburg.de>
* @author René Kijewski <rene.kijewski@fu-berlin.de>
* @}
*/
#include "pthread.h"
#ifdef HAVE_MALLOC_H
#include <malloc.h>
#endif
#define ENABLE_DEBUG 0
#include "debug.h"
typedef struct __pthread_tls_datum {
pthread_key_t key;
struct __pthread_tls_datum *next;
void *value;
} tls_data_t;
struct __pthread_tls_key {
void (*destructor)(void *);
};
/**
* @brief Used while manipulating the TLS of a pthread.
*/
static mutex_t tls_mutex;
/**
* @brief Find a thread-specific datum.
* @param[in] tls Pointer to the list of the thread-specific datums.
* @param[in] key The key to look up.
* @param[out] prev The datum before the result. `NULL` if the result is the first key. Spurious if the key was not found.
* @returns The datum or `NULL`.
*/
static tls_data_t *find_specific(tls_data_t **tls, pthread_key_t key, tls_data_t **prev)
{
tls_data_t *specific = *tls;
*prev = NULL;
while (specific) {
if (specific->key == key) {
return specific;
}
*prev = specific;
specific = specific->next;
}
return 0;
}
/**
* @brief Find or allocate a thread specific datum.
* @details The `key` must be initialized.
* The result will be the head of the thread-specific datums afterwards.
* @param[in] key The key to lookup.
* @returns The datum. `NULL` on ENOMEM or if the caller is not a pthread.
*/
static tls_data_t *get_specific(pthread_key_t key)
{
pthread_t self_id = pthread_self();
if (self_id == 0) {
DEBUG("ERROR called pthread_self() returned 0 in \"%s\"!\n", __func__);
return NULL;
}
tls_data_t **tls = __pthread_get_tls_head(self_id);
tls_data_t *prev, *specific = find_specific(tls, key, &prev);
/* Did the datum already exist? */
if (specific) {
if (prev) {
/* Move the datum to the front for a faster next lookup. */
/* Let's pretend that we have a totally degenerated splay tree. ;-) */
prev->next = specific->next;
specific->next = *tls;
*tls = specific;
}
return specific;
}
/* Allocate new datum. */
specific = malloc(sizeof (*specific));
if (specific) {
specific->key = key;
specific->next = *tls;
specific->value = NULL;
*tls = specific;
}
else {
DEBUG("ERROR out of memory in %s!\n", __func__);
}
return specific;
}
int pthread_key_create(pthread_key_t *key, void (*destructor)(void *))
{
*key = malloc(sizeof (**key));
if (!*key) {
return ENOMEM;
}
(*key)->destructor = destructor;
return 0;
}
int pthread_key_delete(pthread_key_t key)
{
if (!key) {
return EINVAL;
}
mutex_lock(&tls_mutex);
for (unsigned i = 1; i <= MAXTHREADS; ++i) {
tls_data_t **tls = __pthread_get_tls_head(i);
if (!tls) {
continue;
}
tls_data_t *prev, *specific = find_specific(tls, key, &prev);
if (specific) {
if (prev) {
prev->next = specific->next;
}
else {
*tls = specific->next;
}
free(specific);
}
}
mutex_unlock(&tls_mutex);
return 0;
}
void *pthread_getspecific(pthread_key_t key)
{
if (!key) {
return NULL;
}
mutex_lock(&tls_mutex);
tls_data_t *specific = get_specific(key);
void *result = specific ? specific->value : NULL;
mutex_unlock(&tls_mutex);
return result;
}
int pthread_setspecific(pthread_key_t key, const void *value)
{
if (!key) {
return EINVAL;
}
mutex_lock(&tls_mutex);
tls_data_t *specific = get_specific(key);
if (specific) {
specific->value = (void *) value;
}
mutex_unlock(&tls_mutex);
return specific ? 0 : ENOMEM;
}
void __pthread_keys_exit(int self_id)
{
tls_data_t **tls = __pthread_get_tls_head(self_id);
/* Calling the dtor could cause another pthread_exit(), so we dehead and free before calling it. */
mutex_lock(&tls_mutex);
for (tls_data_t *specific; (specific = *tls); ) {
*tls = specific->next;
void *value = specific->value;
void (*destructor)(void *) = specific->key->destructor;
free(specific);
if (value && destructor) {
mutex_unlock(&tls_mutex);
destructor(value);
mutex_lock(&tls_mutex);
}
}
mutex_unlock(&tls_mutex);
}