1505d05c7Sgtb #pragma ident "%Z%%M% %I% %E% SMI" 2505d05c7Sgtb 3505d05c7Sgtb /* 4505d05c7Sgtb * util/support/threads.c 5505d05c7Sgtb * 6505d05c7Sgtb * Copyright 2004 by the Massachusetts Institute of Technology. 7505d05c7Sgtb * All Rights Reserved. 8505d05c7Sgtb * 9505d05c7Sgtb * Export of this software from the United States of America may 10505d05c7Sgtb * require a specific license from the United States Government. 11505d05c7Sgtb * It is the responsibility of any person or organization contemplating 12505d05c7Sgtb * export to obtain such a license before exporting. 13505d05c7Sgtb * 14505d05c7Sgtb * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 15505d05c7Sgtb * distribute this software and its documentation for any purpose and 16505d05c7Sgtb * without fee is hereby granted, provided that the above copyright 17505d05c7Sgtb * notice appear in all copies and that both that copyright notice and 18505d05c7Sgtb * this permission notice appear in supporting documentation, and that 19505d05c7Sgtb * the name of M.I.T. not be used in advertising or publicity pertaining 20505d05c7Sgtb * to distribution of the software without specific, written prior 21505d05c7Sgtb * permission. Furthermore if you modify this software you must label 22505d05c7Sgtb * your software as modified software and not distribute it in such a 23505d05c7Sgtb * fashion that it might be confused with the original M.I.T. software. 24505d05c7Sgtb * M.I.T. makes no representations about the suitability of 25505d05c7Sgtb * this software for any purpose. It is provided "as is" without express 26505d05c7Sgtb * or implied warranty. 27505d05c7Sgtb * 28505d05c7Sgtb * 29505d05c7Sgtb * Preliminary thread support. 30505d05c7Sgtb */ 31505d05c7Sgtb 32505d05c7Sgtb #include <assert.h> 33505d05c7Sgtb #include <stdlib.h> 34505d05c7Sgtb #include <errno.h> 35505d05c7Sgtb #include <k5-thread.h> 36505d05c7Sgtb #include <k5-platform.h> 37505d05c7Sgtb 38505d05c7Sgtb MAKE_INIT_FUNCTION(krb5int_thread_support_init); 39505d05c7Sgtb MAKE_FINI_FUNCTION(krb5int_thread_support_fini); 40505d05c7Sgtb 41505d05c7Sgtb #ifndef ENABLE_THREADS /* no thread support */ 42505d05c7Sgtb 43505d05c7Sgtb static void (*destructors[K5_KEY_MAX])(void *); 44505d05c7Sgtb struct tsd_block { void *values[K5_KEY_MAX]; }; 45505d05c7Sgtb static struct tsd_block tsd_no_threads; 46505d05c7Sgtb static unsigned char destructors_set[K5_KEY_MAX]; 47505d05c7Sgtb 48505d05c7Sgtb #elif defined(_WIN32) 49505d05c7Sgtb 50505d05c7Sgtb static DWORD tls_idx; 51505d05c7Sgtb static CRITICAL_SECTION key_lock; 52505d05c7Sgtb struct tsd_block { 53505d05c7Sgtb void *values[K5_KEY_MAX]; 54505d05c7Sgtb }; 55505d05c7Sgtb static void (*destructors[K5_KEY_MAX])(void *); 56505d05c7Sgtb static unsigned char destructors_set[K5_KEY_MAX]; 57505d05c7Sgtb 58505d05c7Sgtb void krb5int_thread_detach_hook (void) 59505d05c7Sgtb { 60505d05c7Sgtb /* XXX Memory leak here! 61505d05c7Sgtb Need to destroy all TLS objects we know about for this thread. */ 62505d05c7Sgtb struct tsd_block *t; 63505d05c7Sgtb int i, err; 64505d05c7Sgtb 65505d05c7Sgtb err = CALL_INIT_FUNCTION(krb5int_thread_support_init); 66505d05c7Sgtb if (err) 67505d05c7Sgtb return; 68505d05c7Sgtb 69505d05c7Sgtb t = TlsGetValue(tls_idx); 70505d05c7Sgtb if (t == NULL) 71505d05c7Sgtb return; 72505d05c7Sgtb for (i = 0; i < K5_KEY_MAX; i++) { 73505d05c7Sgtb if (destructors_set[i] && destructors[i] && t->values[i]) { 74505d05c7Sgtb void *v = t->values[i]; 75505d05c7Sgtb t->values[i] = 0; 76505d05c7Sgtb (*destructors[i])(v); 77505d05c7Sgtb } 78505d05c7Sgtb } 79505d05c7Sgtb } 80505d05c7Sgtb 81505d05c7Sgtb #else /* POSIX threads */ 82505d05c7Sgtb 83505d05c7Sgtb /* Must support register/delete/register sequence, e.g., if krb5 is 84505d05c7Sgtb loaded so this support code stays in the process, and gssapi is 85505d05c7Sgtb loaded, unloaded, and loaded again. */ 86505d05c7Sgtb 87505d05c7Sgtb static k5_mutex_t key_lock = K5_MUTEX_PARTIAL_INITIALIZER; 88505d05c7Sgtb static void (*destructors[K5_KEY_MAX])(void *); 89505d05c7Sgtb static unsigned char destructors_set[K5_KEY_MAX]; 90505d05c7Sgtb 91505d05c7Sgtb /* This is not safe yet! 92505d05c7Sgtb 93505d05c7Sgtb Thread termination concurrent with key deletion can cause two 94505d05c7Sgtb threads to interfere. It's a bit tricky, since one of the threads 95505d05c7Sgtb will want to remove this structure from the list being walked by 96505d05c7Sgtb the other. 97505d05c7Sgtb 98505d05c7Sgtb Other cases, like looking up data while the library owning the key 99505d05c7Sgtb is in the process of being unloaded, we don't worry about. */ 100505d05c7Sgtb 101505d05c7Sgtb struct tsd_block { 102505d05c7Sgtb struct tsd_block *next; 103505d05c7Sgtb void *values[K5_KEY_MAX]; 104505d05c7Sgtb }; 105505d05c7Sgtb 106505d05c7Sgtb #ifdef HAVE_PRAGMA_WEAK_REF 107505d05c7Sgtb # pragma weak pthread_getspecific 108505d05c7Sgtb # pragma weak pthread_setspecific 109505d05c7Sgtb # pragma weak pthread_key_create 110505d05c7Sgtb # pragma weak pthread_key_delete 111505d05c7Sgtb static struct tsd_block tsd_if_single; 112505d05c7Sgtb # define GET_NO_PTHREAD_TSD() (&tsd_if_single) 113505d05c7Sgtb #else 114505d05c7Sgtb # define GET_NO_PTHREAD_TSD() (abort(),(struct tsd_block *)0) 115505d05c7Sgtb #endif 116505d05c7Sgtb 117505d05c7Sgtb static pthread_key_t key; 118505d05c7Sgtb static void thread_termination(void *); 119505d05c7Sgtb 120505d05c7Sgtb static void thread_termination (void *tptr) 121505d05c7Sgtb { 122505d05c7Sgtb int i, pass, none_found; 123505d05c7Sgtb struct tsd_block *t = tptr; 124505d05c7Sgtb 125505d05c7Sgtb /* Make multiple passes in case, for example, a libkrb5 cleanup 126505d05c7Sgtb function wants to print out an error message, which causes 127505d05c7Sgtb com_err to allocate a thread-specific buffer, after we just 128505d05c7Sgtb freed up the old one. 129505d05c7Sgtb 130505d05c7Sgtb Shouldn't actually happen, if we're careful, but check just in 131505d05c7Sgtb case. */ 132505d05c7Sgtb 133505d05c7Sgtb pass = 0; 134505d05c7Sgtb none_found = 0; 135505d05c7Sgtb while (pass < 4 && !none_found) { 136505d05c7Sgtb none_found = 1; 137505d05c7Sgtb for (i = 0; i < K5_KEY_MAX; i++) { 138505d05c7Sgtb if (destructors_set[i] && destructors[i] && t->values[i]) { 139505d05c7Sgtb void *v = t->values[i]; 140505d05c7Sgtb t->values[i] = 0; 141505d05c7Sgtb (*destructors[i])(v); 142505d05c7Sgtb none_found = 0; 143505d05c7Sgtb } 144505d05c7Sgtb } 145505d05c7Sgtb } 146505d05c7Sgtb /* remove thread from global linked list */ 147505d05c7Sgtb } 148505d05c7Sgtb 149505d05c7Sgtb #endif /* no threads vs Win32 vs POSIX */ 150505d05c7Sgtb 151505d05c7Sgtb void *k5_getspecific (k5_key_t keynum) 152505d05c7Sgtb { 153505d05c7Sgtb struct tsd_block *t; 154505d05c7Sgtb int err; 155505d05c7Sgtb 156505d05c7Sgtb err = CALL_INIT_FUNCTION(krb5int_thread_support_init); 157505d05c7Sgtb if (err) 158505d05c7Sgtb return NULL; 159505d05c7Sgtb 160505d05c7Sgtb assert(keynum >= 0 && keynum < K5_KEY_MAX); 161505d05c7Sgtb assert(destructors_set[keynum] == 1); 162505d05c7Sgtb 163505d05c7Sgtb #ifndef ENABLE_THREADS 164505d05c7Sgtb 165505d05c7Sgtb t = &tsd_no_threads; 166505d05c7Sgtb 167505d05c7Sgtb #elif defined(_WIN32) 168505d05c7Sgtb 169505d05c7Sgtb t = TlsGetValue(tls_idx); 170505d05c7Sgtb 171505d05c7Sgtb #else /* POSIX */ 172505d05c7Sgtb 173505d05c7Sgtb if (K5_PTHREADS_LOADED) 174505d05c7Sgtb t = pthread_getspecific(key); 175505d05c7Sgtb else 176505d05c7Sgtb t = GET_NO_PTHREAD_TSD(); 177505d05c7Sgtb 178505d05c7Sgtb #endif 179505d05c7Sgtb 180505d05c7Sgtb if (t == NULL) 181505d05c7Sgtb return NULL; 182505d05c7Sgtb return t->values[keynum]; 183505d05c7Sgtb } 184505d05c7Sgtb 185505d05c7Sgtb int k5_setspecific (k5_key_t keynum, void *value) 186505d05c7Sgtb { 187505d05c7Sgtb struct tsd_block *t; 188505d05c7Sgtb int err; 189505d05c7Sgtb 190505d05c7Sgtb err = CALL_INIT_FUNCTION(krb5int_thread_support_init); 191505d05c7Sgtb if (err) 192505d05c7Sgtb return err; 193505d05c7Sgtb 194505d05c7Sgtb assert(keynum >= 0 && keynum < K5_KEY_MAX); 195505d05c7Sgtb assert(destructors_set[keynum] == 1); 196505d05c7Sgtb 197505d05c7Sgtb #ifndef ENABLE_THREADS 198505d05c7Sgtb 199505d05c7Sgtb t = &tsd_no_threads; 200505d05c7Sgtb 201505d05c7Sgtb #elif defined(_WIN32) 202505d05c7Sgtb 203505d05c7Sgtb t = TlsGetValue(tls_idx); 204505d05c7Sgtb if (t == NULL) { 205505d05c7Sgtb int i; 206505d05c7Sgtb t = malloc(sizeof(*t)); 207505d05c7Sgtb if (t == NULL) 208505d05c7Sgtb return errno; 209505d05c7Sgtb for (i = 0; i < K5_KEY_MAX; i++) 210505d05c7Sgtb t->values[i] = 0; 211505d05c7Sgtb /* add to global linked list */ 212505d05c7Sgtb /* t->next = 0; */ 213505d05c7Sgtb err = TlsSetValue(tls_idx, t); 214505d05c7Sgtb if (err) { 215505d05c7Sgtb free(t); 216505d05c7Sgtb return err; 217505d05c7Sgtb } 218505d05c7Sgtb } 219505d05c7Sgtb 220505d05c7Sgtb #else /* POSIX */ 221505d05c7Sgtb 222505d05c7Sgtb if (K5_PTHREADS_LOADED) { 223505d05c7Sgtb t = pthread_getspecific(key); 224505d05c7Sgtb if (t == NULL) { 225505d05c7Sgtb int i; 226505d05c7Sgtb t = malloc(sizeof(*t)); 227505d05c7Sgtb if (t == NULL) 228505d05c7Sgtb return errno; 229505d05c7Sgtb for (i = 0; i < K5_KEY_MAX; i++) 230505d05c7Sgtb t->values[i] = 0; 231505d05c7Sgtb /* add to global linked list */ 232505d05c7Sgtb t->next = 0; 233505d05c7Sgtb err = pthread_setspecific(key, t); 234505d05c7Sgtb if (err) { 235505d05c7Sgtb free(t); 236505d05c7Sgtb return err; 237505d05c7Sgtb } 238505d05c7Sgtb } 239505d05c7Sgtb } else { 240505d05c7Sgtb t = GET_NO_PTHREAD_TSD(); 241505d05c7Sgtb } 242505d05c7Sgtb 243505d05c7Sgtb #endif 244505d05c7Sgtb 245505d05c7Sgtb t->values[keynum] = value; 246505d05c7Sgtb return 0; 247505d05c7Sgtb } 248505d05c7Sgtb 249505d05c7Sgtb int k5_key_register (k5_key_t keynum, void (*destructor)(void *)) 250505d05c7Sgtb { 251505d05c7Sgtb int err; 252505d05c7Sgtb 253505d05c7Sgtb err = CALL_INIT_FUNCTION(krb5int_thread_support_init); 254505d05c7Sgtb if (err) 255505d05c7Sgtb return err; 256505d05c7Sgtb 257505d05c7Sgtb assert(keynum >= 0 && keynum < K5_KEY_MAX); 258505d05c7Sgtb 259505d05c7Sgtb #ifndef ENABLE_THREADS 260505d05c7Sgtb 261505d05c7Sgtb assert(destructors_set[keynum] == 0); 262505d05c7Sgtb destructors[keynum] = destructor; 263505d05c7Sgtb destructors_set[keynum] = 1; 264505d05c7Sgtb err = 0; 265505d05c7Sgtb 266505d05c7Sgtb #elif defined(_WIN32) 267505d05c7Sgtb 268505d05c7Sgtb /* XXX: This can raise EXCEPTION_POSSIBLE_DEADLOCK. */ 269505d05c7Sgtb EnterCriticalSection(&key_lock); 270505d05c7Sgtb assert(destructors_set[keynum] == 0); 271505d05c7Sgtb destructors_set[keynum] = 1; 272505d05c7Sgtb destructors[keynum] = destructor; 273505d05c7Sgtb LeaveCriticalSection(&key_lock); 274505d05c7Sgtb err = 0; 275505d05c7Sgtb 276505d05c7Sgtb #else /* POSIX */ 277505d05c7Sgtb 278505d05c7Sgtb err = k5_mutex_lock(&key_lock); 279505d05c7Sgtb if (err == 0) { 280505d05c7Sgtb assert(destructors_set[keynum] == 0); 281505d05c7Sgtb destructors_set[keynum] = 1; 282505d05c7Sgtb destructors[keynum] = destructor; 283505d05c7Sgtb err = k5_mutex_unlock(&key_lock); 284505d05c7Sgtb } 285505d05c7Sgtb 286505d05c7Sgtb #endif 287505d05c7Sgtb return 0; 288505d05c7Sgtb } 289505d05c7Sgtb 290505d05c7Sgtb int k5_key_delete (k5_key_t keynum) 291505d05c7Sgtb { 292505d05c7Sgtb assert(keynum >= 0 && keynum < K5_KEY_MAX); 293505d05c7Sgtb 294505d05c7Sgtb #ifndef ENABLE_THREADS 295505d05c7Sgtb 296505d05c7Sgtb assert(destructors_set[keynum] == 1); 297505d05c7Sgtb if (destructors[keynum] && tsd_no_threads.values[keynum]) 298505d05c7Sgtb (*destructors[keynum])(tsd_no_threads.values[keynum]); 299505d05c7Sgtb destructors[keynum] = 0; 300505d05c7Sgtb tsd_no_threads.values[keynum] = 0; 301505d05c7Sgtb destructors_set[keynum] = 0; 302505d05c7Sgtb 303505d05c7Sgtb #elif defined(_WIN32) 304505d05c7Sgtb 305505d05c7Sgtb /* XXX: This can raise EXCEPTION_POSSIBLE_DEADLOCK. */ 306505d05c7Sgtb EnterCriticalSection(&key_lock); 307505d05c7Sgtb /* XXX Memory leak here! 308505d05c7Sgtb Need to destroy the associated data for all threads. 309505d05c7Sgtb But watch for race conditions in case threads are going away too. */ 310505d05c7Sgtb LeaveCriticalSection(&key_lock); 311505d05c7Sgtb 312505d05c7Sgtb #else /* POSIX */ 313505d05c7Sgtb 314505d05c7Sgtb /* Not written yet. */ 315505d05c7Sgtb abort(); 316505d05c7Sgtb 317505d05c7Sgtb #endif 318505d05c7Sgtb 319505d05c7Sgtb return 0; 320505d05c7Sgtb } 321505d05c7Sgtb 322505d05c7Sgtb int krb5int_call_thread_support_init (void) 323505d05c7Sgtb { 324505d05c7Sgtb return CALL_INIT_FUNCTION(krb5int_thread_support_init); 325505d05c7Sgtb } 326505d05c7Sgtb 327505d05c7Sgtb extern int krb5int_init_fac(void); 328505d05c7Sgtb extern void krb5int_fini_fac(void); 329505d05c7Sgtb 330505d05c7Sgtb int krb5int_thread_support_init (void) 331505d05c7Sgtb { 332505d05c7Sgtb int err; 333505d05c7Sgtb 334505d05c7Sgtb #ifndef ENABLE_THREADS 335505d05c7Sgtb 336505d05c7Sgtb /* Nothing to do for TLS initialization. */ 337505d05c7Sgtb 338505d05c7Sgtb #elif defined(_WIN32) 339505d05c7Sgtb 340505d05c7Sgtb tls_idx = TlsAlloc(); 341505d05c7Sgtb /* XXX This can raise an exception if memory is low! */ 342505d05c7Sgtb InitializeCriticalSection(&key_lock); 343505d05c7Sgtb 344505d05c7Sgtb #else /* POSIX */ 345505d05c7Sgtb 346505d05c7Sgtb err = k5_mutex_finish_init(&key_lock); 347505d05c7Sgtb if (err) 348505d05c7Sgtb return err; 349505d05c7Sgtb if (K5_PTHREADS_LOADED) { 350505d05c7Sgtb err = pthread_key_create(&key, thread_termination); 351505d05c7Sgtb if (err) 352505d05c7Sgtb return err; 353505d05c7Sgtb } 354505d05c7Sgtb 355505d05c7Sgtb #endif 356505d05c7Sgtb 357505d05c7Sgtb err = krb5int_init_fac(); 358505d05c7Sgtb if (err) 359505d05c7Sgtb return err; 360505d05c7Sgtb 361505d05c7Sgtb return 0; 362505d05c7Sgtb } 363505d05c7Sgtb 364505d05c7Sgtb void krb5int_thread_support_fini (void) 365505d05c7Sgtb { 366505d05c7Sgtb if (! INITIALIZER_RAN (krb5int_thread_support_init)) 367505d05c7Sgtb return; 368505d05c7Sgtb 369505d05c7Sgtb #ifndef ENABLE_THREADS 370505d05c7Sgtb 371505d05c7Sgtb /* Do nothing. */ 372505d05c7Sgtb 373505d05c7Sgtb #elif defined(_WIN32) 374505d05c7Sgtb 375505d05c7Sgtb /* ... free stuff ... */ 376505d05c7Sgtb TlsFree(tls_idx); 377505d05c7Sgtb DeleteCriticalSection(&key_lock); 378505d05c7Sgtb 379505d05c7Sgtb #else /* POSIX */ 380505d05c7Sgtb 381505d05c7Sgtb if (! INITIALIZER_RAN(krb5int_thread_support_init)) 382505d05c7Sgtb return; 383505d05c7Sgtb if (K5_PTHREADS_LOADED) 384505d05c7Sgtb pthread_key_delete(key); 385505d05c7Sgtb /* ... delete stuff ... */ 386505d05c7Sgtb k5_mutex_destroy(&key_lock); 387505d05c7Sgtb 388505d05c7Sgtb #endif 389505d05c7Sgtb 390505d05c7Sgtb krb5int_fini_fac(); 391505d05c7Sgtb } 392*54925bf6Swillf /* Mutex allocation functions, for use in plugins that may not know 393*54925bf6Swillf what options a given set of libraries was compiled with. */ 394*54925bf6Swillf int KRB5_CALLCONV 395*54925bf6Swillf krb5int_mutex_alloc (k5_mutex_t **m) 396*54925bf6Swillf { 397*54925bf6Swillf k5_mutex_t *ptr; 398*54925bf6Swillf int err; 399505d05c7Sgtb 400*54925bf6Swillf ptr = malloc (sizeof (k5_mutex_t)); 401*54925bf6Swillf if (ptr == NULL) 402*54925bf6Swillf return errno; 403*54925bf6Swillf err = k5_mutex_init (ptr); 404*54925bf6Swillf if (err) { 405*54925bf6Swillf free (ptr); 406*54925bf6Swillf return err; 407*54925bf6Swillf } 408*54925bf6Swillf *m = ptr; 409*54925bf6Swillf return 0; 410*54925bf6Swillf } 411*54925bf6Swillf 412*54925bf6Swillf void KRB5_CALLCONV 413*54925bf6Swillf krb5int_mutex_free (k5_mutex_t *m) 414*54925bf6Swillf { 415*54925bf6Swillf (void) k5_mutex_destroy (m); 416*54925bf6Swillf free (m); 417*54925bf6Swillf } 418*54925bf6Swillf 419*54925bf6Swillf /* Callable versions of the various macros. */ 420*54925bf6Swillf int KRB5_CALLCONV 421*54925bf6Swillf krb5int_mutex_lock (k5_mutex_t *m) 422*54925bf6Swillf { 423*54925bf6Swillf return k5_mutex_lock (m); 424*54925bf6Swillf } 425*54925bf6Swillf int KRB5_CALLCONV 426*54925bf6Swillf krb5int_mutex_unlock (k5_mutex_t *m) 427*54925bf6Swillf { 428*54925bf6Swillf return k5_mutex_unlock (m); 429*54925bf6Swillf } 430