1*505d05c7Sgtb #pragma ident "%Z%%M% %I% %E% SMI" 2*505d05c7Sgtb 3*505d05c7Sgtb /* 4*505d05c7Sgtb * util/support/threads.c 5*505d05c7Sgtb * 6*505d05c7Sgtb * Copyright 2004 by the Massachusetts Institute of Technology. 7*505d05c7Sgtb * All Rights Reserved. 8*505d05c7Sgtb * 9*505d05c7Sgtb * Export of this software from the United States of America may 10*505d05c7Sgtb * require a specific license from the United States Government. 11*505d05c7Sgtb * It is the responsibility of any person or organization contemplating 12*505d05c7Sgtb * export to obtain such a license before exporting. 13*505d05c7Sgtb * 14*505d05c7Sgtb * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 15*505d05c7Sgtb * distribute this software and its documentation for any purpose and 16*505d05c7Sgtb * without fee is hereby granted, provided that the above copyright 17*505d05c7Sgtb * notice appear in all copies and that both that copyright notice and 18*505d05c7Sgtb * this permission notice appear in supporting documentation, and that 19*505d05c7Sgtb * the name of M.I.T. not be used in advertising or publicity pertaining 20*505d05c7Sgtb * to distribution of the software without specific, written prior 21*505d05c7Sgtb * permission. Furthermore if you modify this software you must label 22*505d05c7Sgtb * your software as modified software and not distribute it in such a 23*505d05c7Sgtb * fashion that it might be confused with the original M.I.T. software. 24*505d05c7Sgtb * M.I.T. makes no representations about the suitability of 25*505d05c7Sgtb * this software for any purpose. It is provided "as is" without express 26*505d05c7Sgtb * or implied warranty. 27*505d05c7Sgtb * 28*505d05c7Sgtb * 29*505d05c7Sgtb * Preliminary thread support. 30*505d05c7Sgtb */ 31*505d05c7Sgtb 32*505d05c7Sgtb #include <assert.h> 33*505d05c7Sgtb #include <stdlib.h> 34*505d05c7Sgtb #include <errno.h> 35*505d05c7Sgtb #include <k5-thread.h> 36*505d05c7Sgtb #include <k5-platform.h> 37*505d05c7Sgtb 38*505d05c7Sgtb MAKE_INIT_FUNCTION(krb5int_thread_support_init); 39*505d05c7Sgtb MAKE_FINI_FUNCTION(krb5int_thread_support_fini); 40*505d05c7Sgtb 41*505d05c7Sgtb #ifndef ENABLE_THREADS /* no thread support */ 42*505d05c7Sgtb 43*505d05c7Sgtb static void (*destructors[K5_KEY_MAX])(void *); 44*505d05c7Sgtb struct tsd_block { void *values[K5_KEY_MAX]; }; 45*505d05c7Sgtb static struct tsd_block tsd_no_threads; 46*505d05c7Sgtb static unsigned char destructors_set[K5_KEY_MAX]; 47*505d05c7Sgtb 48*505d05c7Sgtb #elif defined(_WIN32) 49*505d05c7Sgtb 50*505d05c7Sgtb static DWORD tls_idx; 51*505d05c7Sgtb static CRITICAL_SECTION key_lock; 52*505d05c7Sgtb struct tsd_block { 53*505d05c7Sgtb void *values[K5_KEY_MAX]; 54*505d05c7Sgtb }; 55*505d05c7Sgtb static void (*destructors[K5_KEY_MAX])(void *); 56*505d05c7Sgtb static unsigned char destructors_set[K5_KEY_MAX]; 57*505d05c7Sgtb 58*505d05c7Sgtb void krb5int_thread_detach_hook (void) 59*505d05c7Sgtb { 60*505d05c7Sgtb /* XXX Memory leak here! 61*505d05c7Sgtb Need to destroy all TLS objects we know about for this thread. */ 62*505d05c7Sgtb struct tsd_block *t; 63*505d05c7Sgtb int i, err; 64*505d05c7Sgtb 65*505d05c7Sgtb err = CALL_INIT_FUNCTION(krb5int_thread_support_init); 66*505d05c7Sgtb if (err) 67*505d05c7Sgtb return; 68*505d05c7Sgtb 69*505d05c7Sgtb t = TlsGetValue(tls_idx); 70*505d05c7Sgtb if (t == NULL) 71*505d05c7Sgtb return; 72*505d05c7Sgtb for (i = 0; i < K5_KEY_MAX; i++) { 73*505d05c7Sgtb if (destructors_set[i] && destructors[i] && t->values[i]) { 74*505d05c7Sgtb void *v = t->values[i]; 75*505d05c7Sgtb t->values[i] = 0; 76*505d05c7Sgtb (*destructors[i])(v); 77*505d05c7Sgtb } 78*505d05c7Sgtb } 79*505d05c7Sgtb } 80*505d05c7Sgtb 81*505d05c7Sgtb #else /* POSIX threads */ 82*505d05c7Sgtb 83*505d05c7Sgtb /* Must support register/delete/register sequence, e.g., if krb5 is 84*505d05c7Sgtb loaded so this support code stays in the process, and gssapi is 85*505d05c7Sgtb loaded, unloaded, and loaded again. */ 86*505d05c7Sgtb 87*505d05c7Sgtb static k5_mutex_t key_lock = K5_MUTEX_PARTIAL_INITIALIZER; 88*505d05c7Sgtb static void (*destructors[K5_KEY_MAX])(void *); 89*505d05c7Sgtb static unsigned char destructors_set[K5_KEY_MAX]; 90*505d05c7Sgtb 91*505d05c7Sgtb /* This is not safe yet! 92*505d05c7Sgtb 93*505d05c7Sgtb Thread termination concurrent with key deletion can cause two 94*505d05c7Sgtb threads to interfere. It's a bit tricky, since one of the threads 95*505d05c7Sgtb will want to remove this structure from the list being walked by 96*505d05c7Sgtb the other. 97*505d05c7Sgtb 98*505d05c7Sgtb Other cases, like looking up data while the library owning the key 99*505d05c7Sgtb is in the process of being unloaded, we don't worry about. */ 100*505d05c7Sgtb 101*505d05c7Sgtb struct tsd_block { 102*505d05c7Sgtb struct tsd_block *next; 103*505d05c7Sgtb void *values[K5_KEY_MAX]; 104*505d05c7Sgtb }; 105*505d05c7Sgtb 106*505d05c7Sgtb #ifdef HAVE_PRAGMA_WEAK_REF 107*505d05c7Sgtb # pragma weak pthread_getspecific 108*505d05c7Sgtb # pragma weak pthread_setspecific 109*505d05c7Sgtb # pragma weak pthread_key_create 110*505d05c7Sgtb # pragma weak pthread_key_delete 111*505d05c7Sgtb static struct tsd_block tsd_if_single; 112*505d05c7Sgtb # define GET_NO_PTHREAD_TSD() (&tsd_if_single) 113*505d05c7Sgtb #else 114*505d05c7Sgtb # define GET_NO_PTHREAD_TSD() (abort(),(struct tsd_block *)0) 115*505d05c7Sgtb #endif 116*505d05c7Sgtb 117*505d05c7Sgtb static pthread_key_t key; 118*505d05c7Sgtb static void thread_termination(void *); 119*505d05c7Sgtb 120*505d05c7Sgtb static void thread_termination (void *tptr) 121*505d05c7Sgtb { 122*505d05c7Sgtb int i, pass, none_found; 123*505d05c7Sgtb struct tsd_block *t = tptr; 124*505d05c7Sgtb 125*505d05c7Sgtb /* Make multiple passes in case, for example, a libkrb5 cleanup 126*505d05c7Sgtb function wants to print out an error message, which causes 127*505d05c7Sgtb com_err to allocate a thread-specific buffer, after we just 128*505d05c7Sgtb freed up the old one. 129*505d05c7Sgtb 130*505d05c7Sgtb Shouldn't actually happen, if we're careful, but check just in 131*505d05c7Sgtb case. */ 132*505d05c7Sgtb 133*505d05c7Sgtb pass = 0; 134*505d05c7Sgtb none_found = 0; 135*505d05c7Sgtb while (pass < 4 && !none_found) { 136*505d05c7Sgtb none_found = 1; 137*505d05c7Sgtb for (i = 0; i < K5_KEY_MAX; i++) { 138*505d05c7Sgtb if (destructors_set[i] && destructors[i] && t->values[i]) { 139*505d05c7Sgtb void *v = t->values[i]; 140*505d05c7Sgtb t->values[i] = 0; 141*505d05c7Sgtb (*destructors[i])(v); 142*505d05c7Sgtb none_found = 0; 143*505d05c7Sgtb } 144*505d05c7Sgtb } 145*505d05c7Sgtb } 146*505d05c7Sgtb /* remove thread from global linked list */ 147*505d05c7Sgtb } 148*505d05c7Sgtb 149*505d05c7Sgtb #endif /* no threads vs Win32 vs POSIX */ 150*505d05c7Sgtb 151*505d05c7Sgtb void *k5_getspecific (k5_key_t keynum) 152*505d05c7Sgtb { 153*505d05c7Sgtb struct tsd_block *t; 154*505d05c7Sgtb int err; 155*505d05c7Sgtb 156*505d05c7Sgtb err = CALL_INIT_FUNCTION(krb5int_thread_support_init); 157*505d05c7Sgtb if (err) 158*505d05c7Sgtb return NULL; 159*505d05c7Sgtb 160*505d05c7Sgtb assert(keynum >= 0 && keynum < K5_KEY_MAX); 161*505d05c7Sgtb assert(destructors_set[keynum] == 1); 162*505d05c7Sgtb 163*505d05c7Sgtb #ifndef ENABLE_THREADS 164*505d05c7Sgtb 165*505d05c7Sgtb t = &tsd_no_threads; 166*505d05c7Sgtb 167*505d05c7Sgtb #elif defined(_WIN32) 168*505d05c7Sgtb 169*505d05c7Sgtb t = TlsGetValue(tls_idx); 170*505d05c7Sgtb 171*505d05c7Sgtb #else /* POSIX */ 172*505d05c7Sgtb 173*505d05c7Sgtb if (K5_PTHREADS_LOADED) 174*505d05c7Sgtb t = pthread_getspecific(key); 175*505d05c7Sgtb else 176*505d05c7Sgtb t = GET_NO_PTHREAD_TSD(); 177*505d05c7Sgtb 178*505d05c7Sgtb #endif 179*505d05c7Sgtb 180*505d05c7Sgtb if (t == NULL) 181*505d05c7Sgtb return NULL; 182*505d05c7Sgtb return t->values[keynum]; 183*505d05c7Sgtb } 184*505d05c7Sgtb 185*505d05c7Sgtb int k5_setspecific (k5_key_t keynum, void *value) 186*505d05c7Sgtb { 187*505d05c7Sgtb struct tsd_block *t; 188*505d05c7Sgtb int err; 189*505d05c7Sgtb 190*505d05c7Sgtb err = CALL_INIT_FUNCTION(krb5int_thread_support_init); 191*505d05c7Sgtb if (err) 192*505d05c7Sgtb return err; 193*505d05c7Sgtb 194*505d05c7Sgtb assert(keynum >= 0 && keynum < K5_KEY_MAX); 195*505d05c7Sgtb assert(destructors_set[keynum] == 1); 196*505d05c7Sgtb 197*505d05c7Sgtb #ifndef ENABLE_THREADS 198*505d05c7Sgtb 199*505d05c7Sgtb t = &tsd_no_threads; 200*505d05c7Sgtb 201*505d05c7Sgtb #elif defined(_WIN32) 202*505d05c7Sgtb 203*505d05c7Sgtb t = TlsGetValue(tls_idx); 204*505d05c7Sgtb if (t == NULL) { 205*505d05c7Sgtb int i; 206*505d05c7Sgtb t = malloc(sizeof(*t)); 207*505d05c7Sgtb if (t == NULL) 208*505d05c7Sgtb return errno; 209*505d05c7Sgtb for (i = 0; i < K5_KEY_MAX; i++) 210*505d05c7Sgtb t->values[i] = 0; 211*505d05c7Sgtb /* add to global linked list */ 212*505d05c7Sgtb /* t->next = 0; */ 213*505d05c7Sgtb err = TlsSetValue(tls_idx, t); 214*505d05c7Sgtb if (err) { 215*505d05c7Sgtb free(t); 216*505d05c7Sgtb return err; 217*505d05c7Sgtb } 218*505d05c7Sgtb } 219*505d05c7Sgtb 220*505d05c7Sgtb #else /* POSIX */ 221*505d05c7Sgtb 222*505d05c7Sgtb if (K5_PTHREADS_LOADED) { 223*505d05c7Sgtb t = pthread_getspecific(key); 224*505d05c7Sgtb if (t == NULL) { 225*505d05c7Sgtb int i; 226*505d05c7Sgtb t = malloc(sizeof(*t)); 227*505d05c7Sgtb if (t == NULL) 228*505d05c7Sgtb return errno; 229*505d05c7Sgtb for (i = 0; i < K5_KEY_MAX; i++) 230*505d05c7Sgtb t->values[i] = 0; 231*505d05c7Sgtb /* add to global linked list */ 232*505d05c7Sgtb t->next = 0; 233*505d05c7Sgtb err = pthread_setspecific(key, t); 234*505d05c7Sgtb if (err) { 235*505d05c7Sgtb free(t); 236*505d05c7Sgtb return err; 237*505d05c7Sgtb } 238*505d05c7Sgtb } 239*505d05c7Sgtb } else { 240*505d05c7Sgtb t = GET_NO_PTHREAD_TSD(); 241*505d05c7Sgtb } 242*505d05c7Sgtb 243*505d05c7Sgtb #endif 244*505d05c7Sgtb 245*505d05c7Sgtb t->values[keynum] = value; 246*505d05c7Sgtb return 0; 247*505d05c7Sgtb } 248*505d05c7Sgtb 249*505d05c7Sgtb int k5_key_register (k5_key_t keynum, void (*destructor)(void *)) 250*505d05c7Sgtb { 251*505d05c7Sgtb int err; 252*505d05c7Sgtb 253*505d05c7Sgtb err = CALL_INIT_FUNCTION(krb5int_thread_support_init); 254*505d05c7Sgtb if (err) 255*505d05c7Sgtb return err; 256*505d05c7Sgtb 257*505d05c7Sgtb assert(keynum >= 0 && keynum < K5_KEY_MAX); 258*505d05c7Sgtb 259*505d05c7Sgtb #ifndef ENABLE_THREADS 260*505d05c7Sgtb 261*505d05c7Sgtb assert(destructors_set[keynum] == 0); 262*505d05c7Sgtb destructors[keynum] = destructor; 263*505d05c7Sgtb destructors_set[keynum] = 1; 264*505d05c7Sgtb err = 0; 265*505d05c7Sgtb 266*505d05c7Sgtb #elif defined(_WIN32) 267*505d05c7Sgtb 268*505d05c7Sgtb /* XXX: This can raise EXCEPTION_POSSIBLE_DEADLOCK. */ 269*505d05c7Sgtb EnterCriticalSection(&key_lock); 270*505d05c7Sgtb assert(destructors_set[keynum] == 0); 271*505d05c7Sgtb destructors_set[keynum] = 1; 272*505d05c7Sgtb destructors[keynum] = destructor; 273*505d05c7Sgtb LeaveCriticalSection(&key_lock); 274*505d05c7Sgtb err = 0; 275*505d05c7Sgtb 276*505d05c7Sgtb #else /* POSIX */ 277*505d05c7Sgtb 278*505d05c7Sgtb err = k5_mutex_lock(&key_lock); 279*505d05c7Sgtb if (err == 0) { 280*505d05c7Sgtb assert(destructors_set[keynum] == 0); 281*505d05c7Sgtb destructors_set[keynum] = 1; 282*505d05c7Sgtb destructors[keynum] = destructor; 283*505d05c7Sgtb err = k5_mutex_unlock(&key_lock); 284*505d05c7Sgtb } 285*505d05c7Sgtb 286*505d05c7Sgtb #endif 287*505d05c7Sgtb return 0; 288*505d05c7Sgtb } 289*505d05c7Sgtb 290*505d05c7Sgtb int k5_key_delete (k5_key_t keynum) 291*505d05c7Sgtb { 292*505d05c7Sgtb assert(keynum >= 0 && keynum < K5_KEY_MAX); 293*505d05c7Sgtb 294*505d05c7Sgtb #ifndef ENABLE_THREADS 295*505d05c7Sgtb 296*505d05c7Sgtb assert(destructors_set[keynum] == 1); 297*505d05c7Sgtb if (destructors[keynum] && tsd_no_threads.values[keynum]) 298*505d05c7Sgtb (*destructors[keynum])(tsd_no_threads.values[keynum]); 299*505d05c7Sgtb destructors[keynum] = 0; 300*505d05c7Sgtb tsd_no_threads.values[keynum] = 0; 301*505d05c7Sgtb destructors_set[keynum] = 0; 302*505d05c7Sgtb 303*505d05c7Sgtb #elif defined(_WIN32) 304*505d05c7Sgtb 305*505d05c7Sgtb /* XXX: This can raise EXCEPTION_POSSIBLE_DEADLOCK. */ 306*505d05c7Sgtb EnterCriticalSection(&key_lock); 307*505d05c7Sgtb /* XXX Memory leak here! 308*505d05c7Sgtb Need to destroy the associated data for all threads. 309*505d05c7Sgtb But watch for race conditions in case threads are going away too. */ 310*505d05c7Sgtb LeaveCriticalSection(&key_lock); 311*505d05c7Sgtb 312*505d05c7Sgtb #else /* POSIX */ 313*505d05c7Sgtb 314*505d05c7Sgtb /* Not written yet. */ 315*505d05c7Sgtb abort(); 316*505d05c7Sgtb 317*505d05c7Sgtb #endif 318*505d05c7Sgtb 319*505d05c7Sgtb return 0; 320*505d05c7Sgtb } 321*505d05c7Sgtb 322*505d05c7Sgtb int krb5int_call_thread_support_init (void) 323*505d05c7Sgtb { 324*505d05c7Sgtb return CALL_INIT_FUNCTION(krb5int_thread_support_init); 325*505d05c7Sgtb } 326*505d05c7Sgtb 327*505d05c7Sgtb extern int krb5int_init_fac(void); 328*505d05c7Sgtb extern void krb5int_fini_fac(void); 329*505d05c7Sgtb 330*505d05c7Sgtb int krb5int_thread_support_init (void) 331*505d05c7Sgtb { 332*505d05c7Sgtb int err; 333*505d05c7Sgtb 334*505d05c7Sgtb #ifndef ENABLE_THREADS 335*505d05c7Sgtb 336*505d05c7Sgtb /* Nothing to do for TLS initialization. */ 337*505d05c7Sgtb 338*505d05c7Sgtb #elif defined(_WIN32) 339*505d05c7Sgtb 340*505d05c7Sgtb tls_idx = TlsAlloc(); 341*505d05c7Sgtb /* XXX This can raise an exception if memory is low! */ 342*505d05c7Sgtb InitializeCriticalSection(&key_lock); 343*505d05c7Sgtb 344*505d05c7Sgtb #else /* POSIX */ 345*505d05c7Sgtb 346*505d05c7Sgtb err = k5_mutex_finish_init(&key_lock); 347*505d05c7Sgtb if (err) 348*505d05c7Sgtb return err; 349*505d05c7Sgtb if (K5_PTHREADS_LOADED) { 350*505d05c7Sgtb err = pthread_key_create(&key, thread_termination); 351*505d05c7Sgtb if (err) 352*505d05c7Sgtb return err; 353*505d05c7Sgtb } 354*505d05c7Sgtb 355*505d05c7Sgtb #endif 356*505d05c7Sgtb 357*505d05c7Sgtb err = krb5int_init_fac(); 358*505d05c7Sgtb if (err) 359*505d05c7Sgtb return err; 360*505d05c7Sgtb 361*505d05c7Sgtb return 0; 362*505d05c7Sgtb } 363*505d05c7Sgtb 364*505d05c7Sgtb void krb5int_thread_support_fini (void) 365*505d05c7Sgtb { 366*505d05c7Sgtb if (! INITIALIZER_RAN (krb5int_thread_support_init)) 367*505d05c7Sgtb return; 368*505d05c7Sgtb 369*505d05c7Sgtb #ifndef ENABLE_THREADS 370*505d05c7Sgtb 371*505d05c7Sgtb /* Do nothing. */ 372*505d05c7Sgtb 373*505d05c7Sgtb #elif defined(_WIN32) 374*505d05c7Sgtb 375*505d05c7Sgtb /* ... free stuff ... */ 376*505d05c7Sgtb TlsFree(tls_idx); 377*505d05c7Sgtb DeleteCriticalSection(&key_lock); 378*505d05c7Sgtb 379*505d05c7Sgtb #else /* POSIX */ 380*505d05c7Sgtb 381*505d05c7Sgtb if (! INITIALIZER_RAN(krb5int_thread_support_init)) 382*505d05c7Sgtb return; 383*505d05c7Sgtb if (K5_PTHREADS_LOADED) 384*505d05c7Sgtb pthread_key_delete(key); 385*505d05c7Sgtb /* ... delete stuff ... */ 386*505d05c7Sgtb k5_mutex_destroy(&key_lock); 387*505d05c7Sgtb 388*505d05c7Sgtb #endif 389*505d05c7Sgtb 390*505d05c7Sgtb krb5int_fini_fac(); 391*505d05c7Sgtb } 392*505d05c7Sgtb 393