#include <assert.h>
#include <stdlib.h>
#include <errno.h>
#define THREAD_SUPPORT_IMPL
#include "k5-thread.h"
#include "k5-platform.h"
#include "supp-int.h"
MAKE_INIT_FUNCTION(krb5int_thread_support_init);
MAKE_FINI_FUNCTION(krb5int_thread_support_fini);
#ifndef ENABLE_THREADS
static void (*destructors[K5_KEY_MAX])(void *);
struct tsd_block { void *values[K5_KEY_MAX]; };
static struct tsd_block tsd_no_threads;
static unsigned char destructors_set[K5_KEY_MAX];
int krb5int_pthread_loaded (void)
{
return 0;
}
#elif defined(_WIN32)
static DWORD tls_idx;
static CRITICAL_SECTION key_lock;
struct tsd_block {
void *values[K5_KEY_MAX];
};
static void (*destructors[K5_KEY_MAX])(void *);
static unsigned char destructors_set[K5_KEY_MAX];
void krb5int_thread_detach_hook (void)
{
struct tsd_block *t;
int i, err;
err = CALL_INIT_FUNCTION(krb5int_thread_support_init);
if (err)
return;
t = TlsGetValue(tls_idx);
if (t == NULL)
return;
for (i = 0; i < K5_KEY_MAX; i++) {
if (destructors_set[i] && destructors[i] && t->values[i]) {
void *v = t->values[i];
t->values[i] = 0;
(*destructors[i])(v);
}
}
}
int krb5int_pthread_loaded (void)
{
return 0;
}
#else
static k5_mutex_t key_lock = K5_MUTEX_PARTIAL_INITIALIZER;
static void (*destructors[K5_KEY_MAX])(void *);
static unsigned char destructors_set[K5_KEY_MAX];
struct tsd_block {
struct tsd_block *next;
void *values[K5_KEY_MAX];
};
#ifdef HAVE_PRAGMA_WEAK_REF
# pragma weak pthread_getspecific
# pragma weak pthread_setspecific
# pragma weak pthread_key_create
# pragma weak pthread_key_delete
# pragma weak pthread_create
# pragma weak pthread_join
static volatile int flag_pthread_loaded = -1;
static void loaded_test_aux(void)
{
if (flag_pthread_loaded == -1)
flag_pthread_loaded = 1;
else
flag_pthread_loaded = 0;
}
static pthread_once_t loaded_test_once = PTHREAD_ONCE_INIT;
int krb5int_pthread_loaded (void)
{
int x = flag_pthread_loaded;
if (x != -1)
return x;
if (&pthread_getspecific == 0
|| &pthread_setspecific == 0
|| &pthread_key_create == 0
|| &pthread_key_delete == 0
|| &pthread_once == 0
|| &pthread_mutex_lock == 0
|| &pthread_mutex_unlock == 0
|| &pthread_mutex_destroy == 0
|| &pthread_mutex_init == 0
|| &pthread_self == 0
|| &pthread_equal == 0
|| &pthread_create == 0
|| &pthread_join == 0
|| pthread_once(&loaded_test_once, loaded_test_aux) != 0
|| pthread_once(&loaded_test_once, loaded_test_aux) != 0
|| flag_pthread_loaded < 0) {
flag_pthread_loaded = 0;
return 0;
}
return flag_pthread_loaded;
}
static struct tsd_block tsd_if_single;
# define GET_NO_PTHREAD_TSD() (&tsd_if_single)
#else
int krb5int_pthread_loaded (void)
{
return 1;
}
# define GET_NO_PTHREAD_TSD() (abort(),(struct tsd_block *)0)
#endif
static pthread_key_t key;
static void thread_termination(void *);
static void thread_termination (void *tptr)
{
int err = k5_mutex_lock(&key_lock);
if (err == 0) {
int i, pass, none_found;
struct tsd_block *t = tptr;
pass = 0;
none_found = 0;
while (pass < 4 && !none_found) {
none_found = 1;
for (i = 0; i < K5_KEY_MAX; i++) {
if (destructors_set[i] && destructors[i] && t->values[i]) {
void *v = t->values[i];
t->values[i] = 0;
(*destructors[i])(v);
none_found = 0;
}
}
}
free (t);
err = k5_mutex_unlock(&key_lock);
}
}
#endif
void *k5_getspecific (k5_key_t keynum)
{
struct tsd_block *t;
int err;
err = CALL_INIT_FUNCTION(krb5int_thread_support_init);
if (err)
return NULL;
assert(keynum >= 0 && keynum < K5_KEY_MAX);
assert(destructors_set[keynum] == 1);
#ifndef ENABLE_THREADS
t = &tsd_no_threads;
#elif defined(_WIN32)
t = TlsGetValue(tls_idx);
#else
if (K5_PTHREADS_LOADED)
t = pthread_getspecific(key);
else
t = GET_NO_PTHREAD_TSD();
#endif
if (t == NULL)
return NULL;
return t->values[keynum];
}
int k5_setspecific (k5_key_t keynum, void *value)
{
struct tsd_block *t;
int err;
err = CALL_INIT_FUNCTION(krb5int_thread_support_init);
if (err)
return err;
assert(keynum >= 0 && keynum < K5_KEY_MAX);
assert(destructors_set[keynum] == 1);
#ifndef ENABLE_THREADS
t = &tsd_no_threads;
#elif defined(_WIN32)
t = TlsGetValue(tls_idx);
if (t == NULL) {
int i;
t = malloc(sizeof(*t));
if (t == NULL)
return ENOMEM;
for (i = 0; i < K5_KEY_MAX; i++)
t->values[i] = 0;
err = TlsSetValue(tls_idx, t);
if (!err) {
free(t);
return GetLastError();
}
}
#else
if (K5_PTHREADS_LOADED) {
t = pthread_getspecific(key);
if (t == NULL) {
int i;
t = malloc(sizeof(*t));
if (t == NULL)
return ENOMEM;
for (i = 0; i < K5_KEY_MAX; i++)
t->values[i] = 0;
t->next = 0;
err = pthread_setspecific(key, t);
if (err) {
free(t);
return err;
}
}
} else {
t = GET_NO_PTHREAD_TSD();
}
#endif
t->values[keynum] = value;
return 0;
}
int k5_key_register (k5_key_t keynum, void (*destructor)(void *))
{
int err;
err = CALL_INIT_FUNCTION(krb5int_thread_support_init);
if (err)
return err;
assert(keynum >= 0 && keynum < K5_KEY_MAX);
#ifndef ENABLE_THREADS
assert(destructors_set[keynum] == 0);
destructors[keynum] = destructor;
destructors_set[keynum] = 1;
err = 0;
#elif defined(_WIN32)
EnterCriticalSection(&key_lock);
assert(destructors_set[keynum] == 0);
destructors_set[keynum] = 1;
destructors[keynum] = destructor;
LeaveCriticalSection(&key_lock);
err = 0;
#else
err = k5_mutex_lock(&key_lock);
if (err == 0) {
assert(destructors_set[keynum] == 0);
destructors_set[keynum] = 1;
destructors[keynum] = destructor;
err = k5_mutex_unlock(&key_lock);
}
#endif
return 0;
}
int k5_key_delete (k5_key_t keynum)
{
assert(keynum >= 0 && keynum < K5_KEY_MAX);
#ifndef ENABLE_THREADS
assert(destructors_set[keynum] == 1);
if (destructors[keynum] && tsd_no_threads.values[keynum])
(*destructors[keynum])(tsd_no_threads.values[keynum]);
destructors[keynum] = 0;
tsd_no_threads.values[keynum] = 0;
destructors_set[keynum] = 0;
#elif defined(_WIN32)
EnterCriticalSection(&key_lock);
assert(destructors_set[keynum] == 1);
destructors_set[keynum] = 0;
destructors[keynum] = 0;
LeaveCriticalSection(&key_lock);
#else
{
int err;
err = k5_mutex_lock(&key_lock);
if (err == 0) {
assert(destructors_set[keynum] == 1);
destructors_set[keynum] = 0;
destructors[keynum] = NULL;
k5_mutex_unlock(&key_lock);
}
}
#endif
return 0;
}
int krb5int_call_thread_support_init (void)
{
return CALL_INIT_FUNCTION(krb5int_thread_support_init);
}
#include "cache-addrinfo.h"
int krb5int_thread_support_init (void)
{
int err;
#ifdef SHOW_INITFINI_FUNCS
printf("krb5int_thread_support_init\n");
#endif
#ifndef ENABLE_THREADS
#elif defined(_WIN32)
tls_idx = TlsAlloc();
InitializeCriticalSection(&key_lock);
#else
err = k5_mutex_finish_init(&key_lock);
if (err)
return err;
if (K5_PTHREADS_LOADED) {
err = pthread_key_create(&key, thread_termination);
if (err)
return err;
}
#endif
err = krb5int_init_fac();
if (err)
return err;
err = krb5int_err_init();
if (err)
return err;
return 0;
}
void krb5int_thread_support_fini (void)
{
if (! INITIALIZER_RAN (krb5int_thread_support_init))
return;
#ifdef SHOW_INITFINI_FUNCS
printf("krb5int_thread_support_fini\n");
#endif
#ifndef ENABLE_THREADS
#elif defined(_WIN32)
TlsFree(tls_idx);
DeleteCriticalSection(&key_lock);
#else
if (! INITIALIZER_RAN(krb5int_thread_support_init))
return;
if (K5_PTHREADS_LOADED)
pthread_key_delete(key);
k5_mutex_destroy(&key_lock);
#endif
krb5int_fini_fac();
}
int KRB5_CALLCONV
krb5int_mutex_alloc (k5_mutex_t **m)
{
k5_mutex_t *ptr;
int err;
ptr = malloc (sizeof (k5_mutex_t));
if (ptr == NULL)
return ENOMEM;
err = k5_mutex_init (ptr);
if (err) {
free (ptr);
return err;
}
*m = ptr;
return 0;
}
void KRB5_CALLCONV
krb5int_mutex_free (k5_mutex_t *m)
{
(void) k5_mutex_destroy (m);
free (m);
}
int KRB5_CALLCONV
krb5int_mutex_lock (k5_mutex_t *m)
{
return k5_mutex_lock (m);
}
int KRB5_CALLCONV
krb5int_mutex_unlock (k5_mutex_t *m)
{
return k5_mutex_unlock (m);
}