sys/posix/pthread: added dynamic pthread thread local storage

This commit is contained in:
Martin Landsmann 2014-05-21 17:12:46 +02:00 committed by BytesGalore
parent 319f1b25ae
commit 6e90ad6d73
6 changed files with 457 additions and 1 deletions

View File

@ -39,6 +39,7 @@
#include "pthread_scheduling.h" #include "pthread_scheduling.h"
#include "pthread_cancellation.h" #include "pthread_cancellation.h"
#include "pthread_cond.h" #include "pthread_cond.h"
#include "pthread_tls.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {

View File

@ -0,0 +1,91 @@
/*
* 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>
*/
#ifndef __SYS__POSIX__PTHREAD_TLS__H
#define __SYS__POSIX__PTHREAD_TLS__H
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Internal representation of a thread-specific key.
* @internal
*/
struct __pthread_tls_key;
/**
* @brief A single thread-specific datum.
* @internal
*/
struct __pthread_tls_datum;
/**
* @brief A thread-specific key.
*/
typedef struct __pthread_tls_key *pthread_key_t;
/**
* @brief Returns the requested tls
* @param[in] key the identifier for the requested tls
* @return returns pointer to the storage on success, a 0 value otherwise
*/
void *pthread_getspecific(pthread_key_t key);
/**
* @brief Set and binds a specific tls to a key
* @param[in] key the identifier for the tls
* @param[in] value pointer to the location of the tls
* @return returns 0 on success, an errorcode otherwise
*/
int pthread_setspecific(pthread_key_t key, const void *value);
/**
* @brief Creates a new key to be used to identify a specific tls
* @param[out] key the created key is scribed to the given pointer
* @param[in] destructor function pointer called when non NULL just befor the pthread exits
* @return returns 0 on success, an errorcode otherwise
*/
int pthread_key_create(pthread_key_t *key, void (*destructor)(void *));
/**
* @brief Deletes a pthread_key_t that was previously created with pthread_key_create.
* @details does not call the destructor of the key
* @param[in] key the identifier of the key to be deleted
* @return returns 0 on success, an errorcode otherwise
*/
int pthread_key_delete(pthread_key_t key);
/**
* @brief Destroys all thread-specific keys for pthread `self_id`.
* @param[in] self_id the result of pthread_self().
* @internal
*/
void __pthread_keys_exit(int self_id);
/**
* @brief Returns the pointer to the head of the list of thread-specific data.
* @internal
*/
struct __pthread_tls_datum **__pthread_get_tls_head(int self_id) PURE;
#ifdef __cplusplus
}
#endif
#endif /* __SYS__POSIX__PTHREAD_TLS__H */
/** @} */

View File

@ -66,6 +66,8 @@ typedef struct pthread_thread {
char *stack; char *stack;
struct __pthread_tls_datum *tls_head;
__pthread_cleanup_datum_t *cleanup_top; __pthread_cleanup_datum_t *cleanup_top;
} pthread_thread_t; } pthread_thread_t;
@ -87,6 +89,7 @@ static int insert(pthread_thread_t *pt)
{ {
int result = -1; int result = -1;
mutex_lock(&pthread_mutex); mutex_lock(&pthread_mutex);
for (int i = 0; i < MAXTHREADS; i++){ for (int i = 0; i < MAXTHREADS; i++){
if (!pthread_sched_threads[i]) { if (!pthread_sched_threads[i]) {
pthread_sched_threads[i] = pt; pthread_sched_threads[i] = pt;
@ -94,6 +97,7 @@ static int insert(pthread_thread_t *pt)
break; break;
} }
} }
mutex_unlock(&pthread_mutex); mutex_unlock(&pthread_mutex);
return result; return result;
} }
@ -175,7 +179,7 @@ void pthread_exit(void *retval)
DEBUG("ERROR called pthread_self() returned 0 in \"%s\"!\n", __func__); DEBUG("ERROR called pthread_self() returned 0 in \"%s\"!\n", __func__);
} }
else { else {
pthread_thread_t *self = pthread_sched_threads[self_id-1]; pthread_thread_t *self = pthread_sched_threads[self_id - 1];
while (self->cleanup_top) { while (self->cleanup_top) {
__pthread_cleanup_datum_t *ct = self->cleanup_top; __pthread_cleanup_datum_t *ct = self->cleanup_top;
@ -184,6 +188,12 @@ void pthread_exit(void *retval)
ct->__routine(ct->__arg); ct->__routine(ct->__arg);
} }
/* Prevent linking in pthread_tls.o if no TSS functions were used. */
extern void __pthread_keys_exit(int self_id) __attribute__((weak));
if (__pthread_keys_exit) {
__pthread_keys_exit(self_id);
}
self->thread_pid = KERNEL_PID_UNDEF; self->thread_pid = KERNEL_PID_UNDEF;
DEBUG("pthread_exit(%p), self == %p\n", retval, (void *) self); DEBUG("pthread_exit(%p), self == %p\n", retval, (void *) self);
if (self->status != PTS_DETACHED) { if (self->status != PTS_DETACHED) {
@ -353,3 +363,9 @@ void __pthread_cleanup_pop(__pthread_cleanup_datum_t *datum, int execute)
datum->__routine(datum->__arg); datum->__routine(datum->__arg);
} }
} }
struct __pthread_tls_datum **__pthread_get_tls_head(int self_id)
{
pthread_thread_t *self = pthread_sched_threads[self_id-1];
return self ? &self->tls_head : NULL;
}

View File

@ -0,0 +1,198 @@
/*
* 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 <malloc.h>
#include "pthread.h"
#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 struct 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 defore 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);
}

View File

@ -0,0 +1,12 @@
APPLICATION = pthread_tls
include ../Makefile.tests_common
BOARD_BLACKLIST := arduino-mega2560
# arduino-mega2560: unknown type name: clockid_t
USEMODULE += posix
USEMODULE += pthread
DISABLE_MODULE += auto_init
include $(RIOTBASE)/Makefile.include

138
tests/pthread_tls/main.c Normal file
View File

@ -0,0 +1,138 @@
/*
* 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 tests
* @{
*
* @file
* @brief pthread tls test application
*
* @author Martin Landsmann <martin.landsmann@haw-hamburg.de>
*
* @}
*/
#include <stdio.h>
#include "pthread.h"
#define NUMBER_OF_TLS (20)
void *run(void *parameter)
{
pthread_key_t aKeys[NUMBER_OF_TLS];
int aTLS_values[NUMBER_OF_TLS];
(void)parameter;
printf("\n-= TEST 1 - create %d tls with sequencial values 0...%d =-\n", NUMBER_OF_TLS, NUMBER_OF_TLS - 1);
for (int i = 0; i < NUMBER_OF_TLS; ++i) {
aTLS_values[i] = i;
pthread_key_create(&(aKeys[i]), NULL);
pthread_setspecific(aKeys[i], &aTLS_values[i]);
}
printf("now rise sequencial by one values 1...%d\n", NUMBER_OF_TLS);
for (int i = 0; i < NUMBER_OF_TLS; ++i) {
aTLS_values[i]++;
}
printf("pick deliberate storage (key[3]:%d) and change the value\n", (int)aKeys[3]);
void *val = pthread_getspecific(aKeys[3]);
*((int *)val) = 42;
printf("show tls values:\n");
for (int i = 0; i < NUMBER_OF_TLS; ++i) {
void *val = pthread_getspecific(aKeys[i]);
int x = *(int *)val;
printf("key[%d]: %d, val: %d\n",i, (int)aKeys[i], x);
}
printf("\n -= TEST 2 - delete deliberate key (key[5]:%d) =-\n", (int)aKeys[5]);
pthread_key_delete(aKeys[5]);
printf("show tls values:\n");
for (int i = 0; i < NUMBER_OF_TLS; ++i) {
void *val = pthread_getspecific(aKeys[i]);
if (val != NULL) {
int x = *(int *)val;
printf("key[%d]: %d, val: %d\n",i, (int)aKeys[i], x);
}
}
printf("\n-= TEST 3 - create new tls =-\n");
int new_val = 99;
pthread_key_t new_key;
pthread_key_create(&new_key, NULL);
pthread_setspecific(new_key, &new_val);
printf("added new tls, key: %d, val: %d\n", (int)new_key, new_val);
printf("show tls values:\n");
for (int i = 0; i < NUMBER_OF_TLS; ++i) {
void *val = pthread_getspecific(aKeys[i]);
if (val != NULL) {
int x = *(int *)val;
printf("key[%d]: %d, val: %d\n",i, (int)aKeys[i], x);
}
}
printf("\n-= TEST 4 - delete all keys =-\n");
for (int i = 0; i < NUMBER_OF_TLS; ++i) {
pthread_key_delete(aKeys[i]);
}
printf("show tls values:\n");
for (int i = 0; i < NUMBER_OF_TLS; ++i) {
void *val = pthread_getspecific(aKeys[i]);
if (val != NULL) {
int x = *(int *)val;
printf("key[%d]: %d, val: %d\n",i, (int)aKeys[i], x);
}
}
printf("\n-= TEST 5 - try delete non-existing key =-\n");
printf("try to delete returns: %d\n", pthread_key_delete((pthread_key_t)99));
printf("\n-= TEST 6 - add key and delete without a tls =-\n");
pthread_key_create(&new_key, NULL);
printf("crated key: %d\n", (int)new_key);
printf("try to delete returns: %d\n", pthread_key_delete(new_key));
printf("\n-= TEST 7 - add key without tls =-\n");
pthread_key_create(&new_key, NULL);
printf("crated key: %d\n", (int)new_key);
void* test_7_val = pthread_getspecific(new_key);
printf("test_7_val: %p\n", test_7_val);
return NULL;
}
int main(void)
{
pthread_t th_id;
pthread_attr_t th_attr;
pthread_attr_init(&th_attr);
pthread_create(&th_id, &th_attr, run, NULL);
size_t res;
pthread_join(th_id, (void **) &res);
puts("\ntls tests finished.");
return 0;
}