#include <errno.h>
#include <pthread.h>
#include <stdlib.h>
#include "rthread.h"
struct rthread_key {
int used;
void (*destructor)(void *);
};
static struct rthread_key rkeys[PTHREAD_KEYS_MAX];
static struct __cmtx rkeyslock = __CMTX_INITIALIZER();
int
pthread_key_create(pthread_key_t *key, void (*destructor)(void*))
{
static int hint;
int i;
__cmtx_enter(&rkeyslock);
if (rkeys[hint].used) {
for (i = 0; i < PTHREAD_KEYS_MAX; i++) {
if (!rkeys[i].used)
break;
}
if (i == PTHREAD_KEYS_MAX) {
__cmtx_leave(&rkeyslock);
return (EAGAIN);
}
hint = i;
}
rkeys[hint].used = 1;
rkeys[hint].destructor = destructor;
*key = hint++;
if (hint >= PTHREAD_KEYS_MAX)
hint = 0;
__cmtx_leave(&rkeyslock);
return (0);
}
DEF_STRONG(pthread_key_create);
int
pthread_key_delete(pthread_key_t key)
{
struct rthread_storage *rs;
int rv = 0;
if (key < 0 || key >= PTHREAD_KEYS_MAX)
return (EINVAL);
__cmtx_enter(&rkeyslock);
if (!rkeys[key].used) {
rv = EINVAL;
goto out;
}
rkeys[key].used = 0;
rkeys[key].destructor = NULL;
if (_thread_cb.tc_thread_key_zero != NULL)
_thread_cb.tc_thread_key_zero(key);
else {
for (rs = _initial_thread.local_storage; rs; rs = rs->next) {
if (rs->keyid == key)
rs->data = NULL;
}
}
out:
__cmtx_leave(&rkeyslock);
return (rv);
}
static struct rthread_storage *
_rthread_findstorage(pthread_key_t key)
{
struct rthread_storage *rs;
pthread_t self;
if (!rkeys[key].used) {
rs = NULL;
goto out;
}
self = pthread_self();
for (rs = self->local_storage; rs; rs = rs->next) {
if (rs->keyid == key)
break;
}
if (!rs) {
rs = calloc(1, sizeof(*rs));
if (!rs)
goto out;
rs->keyid = key;
rs->data = NULL;
rs->next = self->local_storage;
self->local_storage = rs;
}
out:
return (rs);
}
void *
pthread_getspecific(pthread_key_t key)
{
struct rthread_storage *rs;
if (key < 0 || key >= PTHREAD_KEYS_MAX)
return (NULL);
rs = _rthread_findstorage(key);
if (!rs)
return (NULL);
return (rs->data);
}
DEF_STRONG(pthread_getspecific);
int
pthread_setspecific(pthread_key_t key, const void *data)
{
struct rthread_storage *rs;
if (key < 0 || key >= PTHREAD_KEYS_MAX)
return (EINVAL);
rs = _rthread_findstorage(key);
if (!rs)
return (ENOMEM);
rs->data = (void *)data;
return (0);
}
DEF_STRONG(pthread_setspecific);
void
_rthread_tls_destructors(pthread_t thread)
{
struct rthread_storage *rs;
int i;
__cmtx_enter(&rkeyslock);
for (i = 0; i < PTHREAD_DESTRUCTOR_ITERATIONS; i++) {
for (rs = thread->local_storage; rs; rs = rs->next) {
if (!rs->data)
continue;
if (rkeys[rs->keyid].destructor) {
void (*destructor)(void *) =
rkeys[rs->keyid].destructor;
void *data = rs->data;
rs->data = NULL;
__cmtx_leave(&rkeyslock);
destructor(data);
__cmtx_enter(&rkeyslock);
}
}
}
for (rs = thread->local_storage; rs; rs = thread->local_storage) {
thread->local_storage = rs->next;
free(rs);
}
__cmtx_leave(&rkeyslock);
}