1505d05c7Sgtb /* 2505d05c7Sgtb * util/support/threads.c 3505d05c7Sgtb * 4*159d09a2SMark Phalan * Copyright 2004,2005,2006 by the Massachusetts Institute of Technology. 5505d05c7Sgtb * All Rights Reserved. 6505d05c7Sgtb * 7505d05c7Sgtb * Export of this software from the United States of America may 8505d05c7Sgtb * require a specific license from the United States Government. 9505d05c7Sgtb * It is the responsibility of any person or organization contemplating 10505d05c7Sgtb * export to obtain such a license before exporting. 11505d05c7Sgtb * 12505d05c7Sgtb * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 13505d05c7Sgtb * distribute this software and its documentation for any purpose and 14505d05c7Sgtb * without fee is hereby granted, provided that the above copyright 15505d05c7Sgtb * notice appear in all copies and that both that copyright notice and 16505d05c7Sgtb * this permission notice appear in supporting documentation, and that 17505d05c7Sgtb * the name of M.I.T. not be used in advertising or publicity pertaining 18505d05c7Sgtb * to distribution of the software without specific, written prior 19505d05c7Sgtb * permission. Furthermore if you modify this software you must label 20505d05c7Sgtb * your software as modified software and not distribute it in such a 21505d05c7Sgtb * fashion that it might be confused with the original M.I.T. software. 22505d05c7Sgtb * M.I.T. makes no representations about the suitability of 23505d05c7Sgtb * this software for any purpose. It is provided "as is" without express 24505d05c7Sgtb * or implied warranty. 25505d05c7Sgtb * 26505d05c7Sgtb * 27505d05c7Sgtb * Preliminary thread support. 28505d05c7Sgtb */ 29505d05c7Sgtb 30505d05c7Sgtb #include <assert.h> 31505d05c7Sgtb #include <stdlib.h> 32505d05c7Sgtb #include <errno.h> 33*159d09a2SMark Phalan #include "k5-thread.h" 34*159d09a2SMark Phalan #include "k5-platform.h" 35*159d09a2SMark Phalan #include "supp-int.h" 36505d05c7Sgtb 37505d05c7Sgtb MAKE_INIT_FUNCTION(krb5int_thread_support_init); 38505d05c7Sgtb MAKE_FINI_FUNCTION(krb5int_thread_support_fini); 39505d05c7Sgtb 40505d05c7Sgtb #ifndef ENABLE_THREADS /* no thread support */ 41505d05c7Sgtb 42505d05c7Sgtb static void (*destructors[K5_KEY_MAX])(void *); 43505d05c7Sgtb struct tsd_block { void *values[K5_KEY_MAX]; }; 44505d05c7Sgtb static struct tsd_block tsd_no_threads; 45505d05c7Sgtb static unsigned char destructors_set[K5_KEY_MAX]; 46505d05c7Sgtb 47*159d09a2SMark Phalan int krb5int_pthread_loaded (void) 48*159d09a2SMark Phalan { 49*159d09a2SMark Phalan return 0; 50*159d09a2SMark Phalan } 51*159d09a2SMark Phalan 52505d05c7Sgtb #elif defined(_WIN32) 53505d05c7Sgtb 54505d05c7Sgtb static DWORD tls_idx; 55505d05c7Sgtb static CRITICAL_SECTION key_lock; 56505d05c7Sgtb struct tsd_block { 57505d05c7Sgtb void *values[K5_KEY_MAX]; 58505d05c7Sgtb }; 59505d05c7Sgtb static void (*destructors[K5_KEY_MAX])(void *); 60505d05c7Sgtb static unsigned char destructors_set[K5_KEY_MAX]; 61505d05c7Sgtb 62505d05c7Sgtb void krb5int_thread_detach_hook (void) 63505d05c7Sgtb { 64505d05c7Sgtb /* XXX Memory leak here! 65505d05c7Sgtb Need to destroy all TLS objects we know about for this thread. */ 66505d05c7Sgtb struct tsd_block *t; 67505d05c7Sgtb int i, err; 68505d05c7Sgtb 69505d05c7Sgtb err = CALL_INIT_FUNCTION(krb5int_thread_support_init); 70505d05c7Sgtb if (err) 71505d05c7Sgtb return; 72505d05c7Sgtb 73505d05c7Sgtb t = TlsGetValue(tls_idx); 74505d05c7Sgtb if (t == NULL) 75505d05c7Sgtb return; 76505d05c7Sgtb for (i = 0; i < K5_KEY_MAX; i++) { 77505d05c7Sgtb if (destructors_set[i] && destructors[i] && t->values[i]) { 78505d05c7Sgtb void *v = t->values[i]; 79505d05c7Sgtb t->values[i] = 0; 80505d05c7Sgtb (*destructors[i])(v); 81505d05c7Sgtb } 82505d05c7Sgtb } 83505d05c7Sgtb } 84505d05c7Sgtb 85*159d09a2SMark Phalan /* Stub function not used on Windows. */ 86*159d09a2SMark Phalan int krb5int_pthread_loaded (void) 87*159d09a2SMark Phalan { 88*159d09a2SMark Phalan return 0; 89*159d09a2SMark Phalan } 90505d05c7Sgtb #else /* POSIX threads */ 91505d05c7Sgtb 92505d05c7Sgtb /* Must support register/delete/register sequence, e.g., if krb5 is 93505d05c7Sgtb loaded so this support code stays in the process, and gssapi is 94505d05c7Sgtb loaded, unloaded, and loaded again. */ 95505d05c7Sgtb 96505d05c7Sgtb static k5_mutex_t key_lock = K5_MUTEX_PARTIAL_INITIALIZER; 97505d05c7Sgtb static void (*destructors[K5_KEY_MAX])(void *); 98505d05c7Sgtb static unsigned char destructors_set[K5_KEY_MAX]; 99505d05c7Sgtb 100505d05c7Sgtb /* This is not safe yet! 101505d05c7Sgtb 102505d05c7Sgtb Thread termination concurrent with key deletion can cause two 103505d05c7Sgtb threads to interfere. It's a bit tricky, since one of the threads 104505d05c7Sgtb will want to remove this structure from the list being walked by 105505d05c7Sgtb the other. 106505d05c7Sgtb 107505d05c7Sgtb Other cases, like looking up data while the library owning the key 108505d05c7Sgtb is in the process of being unloaded, we don't worry about. */ 109505d05c7Sgtb 110505d05c7Sgtb struct tsd_block { 111505d05c7Sgtb struct tsd_block *next; 112505d05c7Sgtb void *values[K5_KEY_MAX]; 113505d05c7Sgtb }; 114505d05c7Sgtb 115505d05c7Sgtb #ifdef HAVE_PRAGMA_WEAK_REF 116505d05c7Sgtb # pragma weak pthread_getspecific 117505d05c7Sgtb # pragma weak pthread_setspecific 118505d05c7Sgtb # pragma weak pthread_key_create 119505d05c7Sgtb # pragma weak pthread_key_delete 120*159d09a2SMark Phalan # pragma weak pthread_create 121*159d09a2SMark Phalan # pragma weak pthread_join 122*159d09a2SMark Phalan static volatile int flag_pthread_loaded = -1; 123*159d09a2SMark Phalan static void loaded_test_aux(void) 124*159d09a2SMark Phalan { 125*159d09a2SMark Phalan if (flag_pthread_loaded == -1) 126*159d09a2SMark Phalan flag_pthread_loaded = 1; 127*159d09a2SMark Phalan else 128*159d09a2SMark Phalan /* Could we have been called twice? */ 129*159d09a2SMark Phalan flag_pthread_loaded = 0; 130*159d09a2SMark Phalan } 131*159d09a2SMark Phalan static pthread_once_t loaded_test_once = PTHREAD_ONCE_INIT; 132*159d09a2SMark Phalan int krb5int_pthread_loaded (void) 133*159d09a2SMark Phalan { 134*159d09a2SMark Phalan int x = flag_pthread_loaded; 135*159d09a2SMark Phalan if (x != -1) 136*159d09a2SMark Phalan return x; 137*159d09a2SMark Phalan if (&pthread_getspecific == 0 138*159d09a2SMark Phalan || &pthread_setspecific == 0 139*159d09a2SMark Phalan || &pthread_key_create == 0 140*159d09a2SMark Phalan || &pthread_key_delete == 0 141*159d09a2SMark Phalan || &pthread_once == 0 142*159d09a2SMark Phalan || &pthread_mutex_lock == 0 143*159d09a2SMark Phalan || &pthread_mutex_unlock == 0 144*159d09a2SMark Phalan || &pthread_mutex_destroy == 0 145*159d09a2SMark Phalan || &pthread_mutex_init == 0 146*159d09a2SMark Phalan || &pthread_self == 0 147*159d09a2SMark Phalan || &pthread_equal == 0 148*159d09a2SMark Phalan /* Any program that's really multithreaded will have to be 149*159d09a2SMark Phalan able to create threads. */ 150*159d09a2SMark Phalan || &pthread_create == 0 151*159d09a2SMark Phalan || &pthread_join == 0 152*159d09a2SMark Phalan /* Okay, all the interesting functions -- or stubs for them -- 153*159d09a2SMark Phalan seem to be present. If we call pthread_once, does it 154*159d09a2SMark Phalan actually seem to cause the indicated function to get called 155*159d09a2SMark Phalan exactly one time? */ 156*159d09a2SMark Phalan || pthread_once(&loaded_test_once, loaded_test_aux) != 0 157*159d09a2SMark Phalan || pthread_once(&loaded_test_once, loaded_test_aux) != 0 158*159d09a2SMark Phalan /* This catches cases where pthread_once does nothing, and 159*159d09a2SMark Phalan never causes the function to get called. That's a pretty 160*159d09a2SMark Phalan clear violation of the POSIX spec, but hey, it happens. */ 161*159d09a2SMark Phalan || flag_pthread_loaded < 0) { 162*159d09a2SMark Phalan flag_pthread_loaded = 0; 163*159d09a2SMark Phalan return 0; 164*159d09a2SMark Phalan } 165*159d09a2SMark Phalan /* If we wanted to be super-paranoid, we could try testing whether 166*159d09a2SMark Phalan pthread_get/setspecific work, too. I don't know -- so far -- 167*159d09a2SMark Phalan of any system with non-functional stubs for those. */ 168*159d09a2SMark Phalan return flag_pthread_loaded; 169*159d09a2SMark Phalan } 170505d05c7Sgtb static struct tsd_block tsd_if_single; 171505d05c7Sgtb # define GET_NO_PTHREAD_TSD() (&tsd_if_single) 172505d05c7Sgtb #else 173505d05c7Sgtb # define GET_NO_PTHREAD_TSD() (abort(),(struct tsd_block *)0) 174505d05c7Sgtb #endif 175505d05c7Sgtb 176505d05c7Sgtb static pthread_key_t key; 177505d05c7Sgtb static void thread_termination(void *); 178505d05c7Sgtb 179505d05c7Sgtb static void thread_termination (void *tptr) 180505d05c7Sgtb { 181*159d09a2SMark Phalan int err = k5_mutex_lock(&key_lock); 182*159d09a2SMark Phalan if (err == 0) { 183505d05c7Sgtb int i, pass, none_found; 184505d05c7Sgtb struct tsd_block *t = tptr; 185505d05c7Sgtb 186505d05c7Sgtb /* Make multiple passes in case, for example, a libkrb5 cleanup 187505d05c7Sgtb function wants to print out an error message, which causes 188505d05c7Sgtb com_err to allocate a thread-specific buffer, after we just 189505d05c7Sgtb freed up the old one. 190505d05c7Sgtb 191505d05c7Sgtb Shouldn't actually happen, if we're careful, but check just in 192505d05c7Sgtb case. */ 193505d05c7Sgtb 194505d05c7Sgtb pass = 0; 195505d05c7Sgtb none_found = 0; 196505d05c7Sgtb while (pass < 4 && !none_found) { 197505d05c7Sgtb none_found = 1; 198505d05c7Sgtb for (i = 0; i < K5_KEY_MAX; i++) { 199505d05c7Sgtb if (destructors_set[i] && destructors[i] && t->values[i]) { 200505d05c7Sgtb void *v = t->values[i]; 201505d05c7Sgtb t->values[i] = 0; 202505d05c7Sgtb (*destructors[i])(v); 203505d05c7Sgtb none_found = 0; 204505d05c7Sgtb } 205505d05c7Sgtb } 206505d05c7Sgtb } 207*159d09a2SMark Phalan free (t); 208*159d09a2SMark Phalan err = k5_mutex_unlock(&key_lock); 209*159d09a2SMark Phalan } 210*159d09a2SMark Phalan 211505d05c7Sgtb /* remove thread from global linked list */ 212505d05c7Sgtb } 213505d05c7Sgtb 214505d05c7Sgtb #endif /* no threads vs Win32 vs POSIX */ 215505d05c7Sgtb 216505d05c7Sgtb void *k5_getspecific (k5_key_t keynum) 217505d05c7Sgtb { 218505d05c7Sgtb struct tsd_block *t; 219505d05c7Sgtb int err; 220505d05c7Sgtb 221505d05c7Sgtb err = CALL_INIT_FUNCTION(krb5int_thread_support_init); 222505d05c7Sgtb if (err) 223505d05c7Sgtb return NULL; 224505d05c7Sgtb 225505d05c7Sgtb assert(keynum >= 0 && keynum < K5_KEY_MAX); 226505d05c7Sgtb assert(destructors_set[keynum] == 1); 227505d05c7Sgtb 228505d05c7Sgtb #ifndef ENABLE_THREADS 229505d05c7Sgtb 230505d05c7Sgtb t = &tsd_no_threads; 231505d05c7Sgtb 232505d05c7Sgtb #elif defined(_WIN32) 233505d05c7Sgtb 234505d05c7Sgtb t = TlsGetValue(tls_idx); 235505d05c7Sgtb 236505d05c7Sgtb #else /* POSIX */ 237505d05c7Sgtb 238505d05c7Sgtb if (K5_PTHREADS_LOADED) 239505d05c7Sgtb t = pthread_getspecific(key); 240505d05c7Sgtb else 241505d05c7Sgtb t = GET_NO_PTHREAD_TSD(); 242505d05c7Sgtb 243505d05c7Sgtb #endif 244505d05c7Sgtb 245505d05c7Sgtb if (t == NULL) 246505d05c7Sgtb return NULL; 247505d05c7Sgtb return t->values[keynum]; 248505d05c7Sgtb } 249505d05c7Sgtb 250505d05c7Sgtb int k5_setspecific (k5_key_t keynum, void *value) 251505d05c7Sgtb { 252505d05c7Sgtb struct tsd_block *t; 253505d05c7Sgtb int err; 254505d05c7Sgtb 255505d05c7Sgtb err = CALL_INIT_FUNCTION(krb5int_thread_support_init); 256505d05c7Sgtb if (err) 257505d05c7Sgtb return err; 258505d05c7Sgtb 259505d05c7Sgtb assert(keynum >= 0 && keynum < K5_KEY_MAX); 260505d05c7Sgtb assert(destructors_set[keynum] == 1); 261505d05c7Sgtb 262505d05c7Sgtb #ifndef ENABLE_THREADS 263505d05c7Sgtb 264505d05c7Sgtb t = &tsd_no_threads; 265505d05c7Sgtb 266505d05c7Sgtb #elif defined(_WIN32) 267505d05c7Sgtb 268505d05c7Sgtb t = TlsGetValue(tls_idx); 269505d05c7Sgtb if (t == NULL) { 270505d05c7Sgtb int i; 271505d05c7Sgtb t = malloc(sizeof(*t)); 272505d05c7Sgtb if (t == NULL) 273505d05c7Sgtb return errno; 274505d05c7Sgtb for (i = 0; i < K5_KEY_MAX; i++) 275505d05c7Sgtb t->values[i] = 0; 276505d05c7Sgtb /* add to global linked list */ 277505d05c7Sgtb /* t->next = 0; */ 278505d05c7Sgtb err = TlsSetValue(tls_idx, t); 279*159d09a2SMark Phalan if (!err) { 280505d05c7Sgtb free(t); 281*159d09a2SMark Phalan return GetLastError(); 282505d05c7Sgtb } 283505d05c7Sgtb } 284505d05c7Sgtb 285505d05c7Sgtb #else /* POSIX */ 286505d05c7Sgtb 287505d05c7Sgtb if (K5_PTHREADS_LOADED) { 288505d05c7Sgtb t = pthread_getspecific(key); 289505d05c7Sgtb if (t == NULL) { 290505d05c7Sgtb int i; 291505d05c7Sgtb t = malloc(sizeof(*t)); 292505d05c7Sgtb if (t == NULL) 293505d05c7Sgtb return errno; 294505d05c7Sgtb for (i = 0; i < K5_KEY_MAX; i++) 295505d05c7Sgtb t->values[i] = 0; 296505d05c7Sgtb /* add to global linked list */ 297505d05c7Sgtb t->next = 0; 298505d05c7Sgtb err = pthread_setspecific(key, t); 299505d05c7Sgtb if (err) { 300505d05c7Sgtb free(t); 301505d05c7Sgtb return err; 302505d05c7Sgtb } 303505d05c7Sgtb } 304505d05c7Sgtb } else { 305505d05c7Sgtb t = GET_NO_PTHREAD_TSD(); 306505d05c7Sgtb } 307505d05c7Sgtb 308505d05c7Sgtb #endif 309505d05c7Sgtb 310505d05c7Sgtb t->values[keynum] = value; 311505d05c7Sgtb return 0; 312505d05c7Sgtb } 313505d05c7Sgtb 314505d05c7Sgtb int k5_key_register (k5_key_t keynum, void (*destructor)(void *)) 315505d05c7Sgtb { 316505d05c7Sgtb int err; 317505d05c7Sgtb 318505d05c7Sgtb err = CALL_INIT_FUNCTION(krb5int_thread_support_init); 319505d05c7Sgtb if (err) 320505d05c7Sgtb return err; 321505d05c7Sgtb 322505d05c7Sgtb assert(keynum >= 0 && keynum < K5_KEY_MAX); 323505d05c7Sgtb 324505d05c7Sgtb #ifndef ENABLE_THREADS 325505d05c7Sgtb 326505d05c7Sgtb assert(destructors_set[keynum] == 0); 327505d05c7Sgtb destructors[keynum] = destructor; 328505d05c7Sgtb destructors_set[keynum] = 1; 329505d05c7Sgtb err = 0; 330505d05c7Sgtb 331505d05c7Sgtb #elif defined(_WIN32) 332505d05c7Sgtb 333505d05c7Sgtb /* XXX: This can raise EXCEPTION_POSSIBLE_DEADLOCK. */ 334505d05c7Sgtb EnterCriticalSection(&key_lock); 335505d05c7Sgtb assert(destructors_set[keynum] == 0); 336505d05c7Sgtb destructors_set[keynum] = 1; 337505d05c7Sgtb destructors[keynum] = destructor; 338505d05c7Sgtb LeaveCriticalSection(&key_lock); 339505d05c7Sgtb err = 0; 340505d05c7Sgtb 341505d05c7Sgtb #else /* POSIX */ 342505d05c7Sgtb 343505d05c7Sgtb err = k5_mutex_lock(&key_lock); 344505d05c7Sgtb if (err == 0) { 345505d05c7Sgtb assert(destructors_set[keynum] == 0); 346505d05c7Sgtb destructors_set[keynum] = 1; 347505d05c7Sgtb destructors[keynum] = destructor; 348505d05c7Sgtb err = k5_mutex_unlock(&key_lock); 349505d05c7Sgtb } 350505d05c7Sgtb 351505d05c7Sgtb #endif 352505d05c7Sgtb return 0; 353505d05c7Sgtb } 354505d05c7Sgtb 355505d05c7Sgtb int k5_key_delete (k5_key_t keynum) 356505d05c7Sgtb { 357505d05c7Sgtb assert(keynum >= 0 && keynum < K5_KEY_MAX); 358505d05c7Sgtb 359505d05c7Sgtb #ifndef ENABLE_THREADS 360505d05c7Sgtb 361505d05c7Sgtb assert(destructors_set[keynum] == 1); 362505d05c7Sgtb if (destructors[keynum] && tsd_no_threads.values[keynum]) 363505d05c7Sgtb (*destructors[keynum])(tsd_no_threads.values[keynum]); 364505d05c7Sgtb destructors[keynum] = 0; 365505d05c7Sgtb tsd_no_threads.values[keynum] = 0; 366505d05c7Sgtb destructors_set[keynum] = 0; 367505d05c7Sgtb 368505d05c7Sgtb #elif defined(_WIN32) 369505d05c7Sgtb 370505d05c7Sgtb /* XXX: This can raise EXCEPTION_POSSIBLE_DEADLOCK. */ 371505d05c7Sgtb EnterCriticalSection(&key_lock); 372505d05c7Sgtb /* XXX Memory leak here! 373505d05c7Sgtb Need to destroy the associated data for all threads. 374505d05c7Sgtb But watch for race conditions in case threads are going away too. */ 375*159d09a2SMark Phalan assert(destructors_set[keynum] == 1); 376*159d09a2SMark Phalan destructors_set[keynum] = 0; 377*159d09a2SMark Phalan destructors[keynum] = 0; 378505d05c7Sgtb LeaveCriticalSection(&key_lock); 379505d05c7Sgtb 380505d05c7Sgtb #else /* POSIX */ 381505d05c7Sgtb 382*159d09a2SMark Phalan { 383*159d09a2SMark Phalan int err; 384*159d09a2SMark Phalan 385*159d09a2SMark Phalan /* XXX RESOURCE LEAK: 386*159d09a2SMark Phalan 387*159d09a2SMark Phalan Need to destroy the allocated objects first! */ 388*159d09a2SMark Phalan 389*159d09a2SMark Phalan err = k5_mutex_lock(&key_lock); 390*159d09a2SMark Phalan if (err == 0) { 391*159d09a2SMark Phalan assert(destructors_set[keynum] == 1); 392*159d09a2SMark Phalan destructors_set[keynum] = 0; 393*159d09a2SMark Phalan destructors[keynum] = NULL; 394*159d09a2SMark Phalan k5_mutex_unlock(&key_lock); 395*159d09a2SMark Phalan } 396*159d09a2SMark Phalan } 397505d05c7Sgtb 398505d05c7Sgtb #endif 399505d05c7Sgtb 400505d05c7Sgtb return 0; 401505d05c7Sgtb } 402505d05c7Sgtb 403505d05c7Sgtb int krb5int_call_thread_support_init (void) 404505d05c7Sgtb { 405505d05c7Sgtb return CALL_INIT_FUNCTION(krb5int_thread_support_init); 406505d05c7Sgtb } 407505d05c7Sgtb 408*159d09a2SMark Phalan #include "cache-addrinfo.h" 409*159d09a2SMark Phalan 410*159d09a2SMark Phalan #ifdef DEBUG_THREADS_STATS 411*159d09a2SMark Phalan #include <stdio.h> 412*159d09a2SMark Phalan static FILE *stats_logfile; 413*159d09a2SMark Phalan #endif 414505d05c7Sgtb 415505d05c7Sgtb int krb5int_thread_support_init (void) 416505d05c7Sgtb { 417505d05c7Sgtb int err; 418505d05c7Sgtb 419*159d09a2SMark Phalan #ifdef SHOW_INITFINI_FUNCS 420*159d09a2SMark Phalan printf("krb5int_thread_support_init\n"); 421*159d09a2SMark Phalan #endif 422*159d09a2SMark Phalan 423*159d09a2SMark Phalan #ifdef DEBUG_THREADS_STATS 424*159d09a2SMark Phalan /* stats_logfile = stderr; */ 425*159d09a2SMark Phalan stats_logfile = fopen("/dev/tty", "w+"); 426*159d09a2SMark Phalan if (stats_logfile == NULL) 427*159d09a2SMark Phalan stats_logfile = stderr; 428*159d09a2SMark Phalan #endif 429*159d09a2SMark Phalan 430505d05c7Sgtb #ifndef ENABLE_THREADS 431505d05c7Sgtb 432505d05c7Sgtb /* Nothing to do for TLS initialization. */ 433505d05c7Sgtb 434505d05c7Sgtb #elif defined(_WIN32) 435505d05c7Sgtb 436505d05c7Sgtb tls_idx = TlsAlloc(); 437505d05c7Sgtb /* XXX This can raise an exception if memory is low! */ 438505d05c7Sgtb InitializeCriticalSection(&key_lock); 439505d05c7Sgtb 440505d05c7Sgtb #else /* POSIX */ 441505d05c7Sgtb 442505d05c7Sgtb err = k5_mutex_finish_init(&key_lock); 443505d05c7Sgtb if (err) 444505d05c7Sgtb return err; 445505d05c7Sgtb if (K5_PTHREADS_LOADED) { 446505d05c7Sgtb err = pthread_key_create(&key, thread_termination); 447505d05c7Sgtb if (err) 448505d05c7Sgtb return err; 449505d05c7Sgtb } 450505d05c7Sgtb 451505d05c7Sgtb #endif 452505d05c7Sgtb 453505d05c7Sgtb err = krb5int_init_fac(); 454505d05c7Sgtb if (err) 455505d05c7Sgtb return err; 456505d05c7Sgtb 457*159d09a2SMark Phalan err = krb5int_err_init(); 458*159d09a2SMark Phalan if (err) 459*159d09a2SMark Phalan return err; 460*159d09a2SMark Phalan 461505d05c7Sgtb return 0; 462505d05c7Sgtb } 463505d05c7Sgtb 464505d05c7Sgtb void krb5int_thread_support_fini (void) 465505d05c7Sgtb { 466505d05c7Sgtb if (! INITIALIZER_RAN (krb5int_thread_support_init)) 467505d05c7Sgtb return; 468505d05c7Sgtb 469*159d09a2SMark Phalan #ifdef SHOW_INITFINI_FUNCS 470*159d09a2SMark Phalan printf("krb5int_thread_support_fini\n"); 471*159d09a2SMark Phalan #endif 472*159d09a2SMark Phalan 473505d05c7Sgtb #ifndef ENABLE_THREADS 474505d05c7Sgtb 475505d05c7Sgtb /* Do nothing. */ 476505d05c7Sgtb 477505d05c7Sgtb #elif defined(_WIN32) 478505d05c7Sgtb 479505d05c7Sgtb /* ... free stuff ... */ 480505d05c7Sgtb TlsFree(tls_idx); 481505d05c7Sgtb DeleteCriticalSection(&key_lock); 482505d05c7Sgtb 483505d05c7Sgtb #else /* POSIX */ 484505d05c7Sgtb 485505d05c7Sgtb if (! INITIALIZER_RAN(krb5int_thread_support_init)) 486505d05c7Sgtb return; 487505d05c7Sgtb if (K5_PTHREADS_LOADED) 488505d05c7Sgtb pthread_key_delete(key); 489505d05c7Sgtb /* ... delete stuff ... */ 490505d05c7Sgtb k5_mutex_destroy(&key_lock); 491505d05c7Sgtb 492505d05c7Sgtb #endif 493505d05c7Sgtb 494*159d09a2SMark Phalan #ifdef DEBUG_THREADS_STATS 495*159d09a2SMark Phalan fflush(stats_logfile); 496*159d09a2SMark Phalan /* XXX Should close if not stderr, in case unloading library but 497*159d09a2SMark Phalan not exiting. */ 498*159d09a2SMark Phalan #endif 499*159d09a2SMark Phalan 500505d05c7Sgtb krb5int_fini_fac(); 501505d05c7Sgtb } 502*159d09a2SMark Phalan 503*159d09a2SMark Phalan #ifdef DEBUG_THREADS_STATS 504*159d09a2SMark Phalan void KRB5_CALLCONV 505*159d09a2SMark Phalan k5_mutex_lock_update_stats(k5_debug_mutex_stats *m, 506*159d09a2SMark Phalan k5_mutex_stats_tmp startwait) 507*159d09a2SMark Phalan { 508*159d09a2SMark Phalan k5_debug_time_t now; 509*159d09a2SMark Phalan k5_debug_timediff_t tdiff, tdiff2; 510*159d09a2SMark Phalan 511*159d09a2SMark Phalan now = get_current_time(); 512*159d09a2SMark Phalan (void) krb5int_call_thread_support_init(); 513*159d09a2SMark Phalan m->count++; 514*159d09a2SMark Phalan m->time_acquired = now; 515*159d09a2SMark Phalan tdiff = timediff(now, startwait); 516*159d09a2SMark Phalan tdiff2 = tdiff * tdiff; 517*159d09a2SMark Phalan if (m->count == 1 || m->lockwait.valmin > tdiff) 518*159d09a2SMark Phalan m->lockwait.valmin = tdiff; 519*159d09a2SMark Phalan if (m->count == 1 || m->lockwait.valmax < tdiff) 520*159d09a2SMark Phalan m->lockwait.valmax = tdiff; 521*159d09a2SMark Phalan m->lockwait.valsum += tdiff; 522*159d09a2SMark Phalan m->lockwait.valsqsum += tdiff2; 523*159d09a2SMark Phalan } 524*159d09a2SMark Phalan 525*159d09a2SMark Phalan void KRB5_CALLCONV 526*159d09a2SMark Phalan krb5int_mutex_unlock_update_stats(k5_debug_mutex_stats *m) 527*159d09a2SMark Phalan { 528*159d09a2SMark Phalan k5_debug_time_t now = get_current_time(); 529*159d09a2SMark Phalan k5_debug_timediff_t tdiff, tdiff2; 530*159d09a2SMark Phalan tdiff = timediff(now, m->time_acquired); 531*159d09a2SMark Phalan tdiff2 = tdiff * tdiff; 532*159d09a2SMark Phalan if (m->count == 1 || m->lockheld.valmin > tdiff) 533*159d09a2SMark Phalan m->lockheld.valmin = tdiff; 534*159d09a2SMark Phalan if (m->count == 1 || m->lockheld.valmax < tdiff) 535*159d09a2SMark Phalan m->lockheld.valmax = tdiff; 536*159d09a2SMark Phalan m->lockheld.valsum += tdiff; 537*159d09a2SMark Phalan m->lockheld.valsqsum += tdiff2; 538*159d09a2SMark Phalan } 539*159d09a2SMark Phalan 540*159d09a2SMark Phalan #include <math.h> 541*159d09a2SMark Phalan static double 542*159d09a2SMark Phalan get_stddev(struct k5_timediff_stats sp, int count) 543*159d09a2SMark Phalan { 544*159d09a2SMark Phalan long double mu, mu_squared, rho_squared; 545*159d09a2SMark Phalan mu = (long double) sp.valsum / count; 546*159d09a2SMark Phalan mu_squared = mu * mu; 547*159d09a2SMark Phalan /* SUM((x_i - mu)^2) 548*159d09a2SMark Phalan = SUM(x_i^2 - 2*mu*x_i + mu^2) 549*159d09a2SMark Phalan = SUM(x_i^2) - 2*mu*SUM(x_i) + N*mu^2 550*159d09a2SMark Phalan 551*159d09a2SMark Phalan Standard deviation rho^2 = SUM(...) / N. */ 552*159d09a2SMark Phalan rho_squared = (sp.valsqsum - 2 * mu * sp.valsum + count * mu_squared) / count; 553*159d09a2SMark Phalan return sqrt(rho_squared); 554*159d09a2SMark Phalan } 555*159d09a2SMark Phalan 556*159d09a2SMark Phalan void KRB5_CALLCONV 557*159d09a2SMark Phalan krb5int_mutex_report_stats(k5_mutex_t *m) 558*159d09a2SMark Phalan { 559*159d09a2SMark Phalan char *p; 560*159d09a2SMark Phalan 561*159d09a2SMark Phalan /* Tweak this to only record data on "interesting" locks. */ 562*159d09a2SMark Phalan if (m->stats.count < 10) 563*159d09a2SMark Phalan return; 564*159d09a2SMark Phalan if (m->stats.lockwait.valsum < 10 * m->stats.count) 565*159d09a2SMark Phalan return; 566*159d09a2SMark Phalan 567*159d09a2SMark Phalan p = strrchr(m->loc_created.filename, '/'); 568*159d09a2SMark Phalan if (p == NULL) 569*159d09a2SMark Phalan p = m->loc_created.filename; 570*159d09a2SMark Phalan else 571*159d09a2SMark Phalan p++; 572*159d09a2SMark Phalan fprintf(stats_logfile, "mutex @%p: created at line %d of %s\n", 573*159d09a2SMark Phalan (void *) m, m->loc_created.lineno, p); 574*159d09a2SMark Phalan if (m->stats.count == 0) 575*159d09a2SMark Phalan fprintf(stats_logfile, "\tnever locked\n"); 576*159d09a2SMark Phalan else { 577*159d09a2SMark Phalan double sd_wait, sd_hold; 578*159d09a2SMark Phalan sd_wait = get_stddev(m->stats.lockwait, m->stats.count); 579*159d09a2SMark Phalan sd_hold = get_stddev(m->stats.lockheld, m->stats.count); 580*159d09a2SMark Phalan fprintf(stats_logfile, 581*159d09a2SMark Phalan "\tlocked %d time%s; wait %lu/%f/%lu/%fus, hold %lu/%f/%lu/%fus\n", 582*159d09a2SMark Phalan m->stats.count, m->stats.count == 1 ? "" : "s", 583*159d09a2SMark Phalan (unsigned long) m->stats.lockwait.valmin, 584*159d09a2SMark Phalan (double) m->stats.lockwait.valsum / m->stats.count, 585*159d09a2SMark Phalan (unsigned long) m->stats.lockwait.valmax, 586*159d09a2SMark Phalan sd_wait, 587*159d09a2SMark Phalan (unsigned long) m->stats.lockheld.valmin, 588*159d09a2SMark Phalan (double) m->stats.lockheld.valsum / m->stats.count, 589*159d09a2SMark Phalan (unsigned long) m->stats.lockheld.valmax, 590*159d09a2SMark Phalan sd_hold); 591*159d09a2SMark Phalan } 592*159d09a2SMark Phalan } 593*159d09a2SMark Phalan #else 594*159d09a2SMark Phalan /* On Windows, everything defined in the export list must be defined. 595*159d09a2SMark Phalan The UNIX systems where we're using the export list don't seem to 596*159d09a2SMark Phalan care. */ 597*159d09a2SMark Phalan #undef krb5int_mutex_lock_update_stats 598*159d09a2SMark Phalan void KRB5_CALLCONV 599*159d09a2SMark Phalan krb5int_mutex_lock_update_stats(k5_debug_mutex_stats *m, 600*159d09a2SMark Phalan k5_mutex_stats_tmp startwait) 601*159d09a2SMark Phalan { 602*159d09a2SMark Phalan } 603*159d09a2SMark Phalan #undef krb5int_mutex_unlock_update_stats 604*159d09a2SMark Phalan void KRB5_CALLCONV 605*159d09a2SMark Phalan krb5int_mutex_unlock_update_stats(k5_debug_mutex_stats *m) 606*159d09a2SMark Phalan { 607*159d09a2SMark Phalan } 608*159d09a2SMark Phalan #undef krb5int_mutex_report_stats 609*159d09a2SMark Phalan void KRB5_CALLCONV 610*159d09a2SMark Phalan krb5int_mutex_report_stats(k5_mutex_t *m) 611*159d09a2SMark Phalan { 612*159d09a2SMark Phalan } 613*159d09a2SMark Phalan #endif 614*159d09a2SMark Phalan 61554925bf6Swillf /* Mutex allocation functions, for use in plugins that may not know 61654925bf6Swillf what options a given set of libraries was compiled with. */ 61754925bf6Swillf int KRB5_CALLCONV 61854925bf6Swillf krb5int_mutex_alloc (k5_mutex_t **m) 61954925bf6Swillf { 62054925bf6Swillf k5_mutex_t *ptr; 62154925bf6Swillf int err; 622505d05c7Sgtb 62354925bf6Swillf ptr = malloc (sizeof (k5_mutex_t)); 62454925bf6Swillf if (ptr == NULL) 62554925bf6Swillf return errno; 62654925bf6Swillf err = k5_mutex_init (ptr); 62754925bf6Swillf if (err) { 62854925bf6Swillf free (ptr); 62954925bf6Swillf return err; 63054925bf6Swillf } 63154925bf6Swillf *m = ptr; 63254925bf6Swillf return 0; 63354925bf6Swillf } 63454925bf6Swillf 63554925bf6Swillf void KRB5_CALLCONV 63654925bf6Swillf krb5int_mutex_free (k5_mutex_t *m) 63754925bf6Swillf { 63854925bf6Swillf (void) k5_mutex_destroy (m); 63954925bf6Swillf free (m); 64054925bf6Swillf } 64154925bf6Swillf 64254925bf6Swillf /* Callable versions of the various macros. */ 64354925bf6Swillf int KRB5_CALLCONV 64454925bf6Swillf krb5int_mutex_lock (k5_mutex_t *m) 64554925bf6Swillf { 64654925bf6Swillf return k5_mutex_lock (m); 64754925bf6Swillf } 64854925bf6Swillf int KRB5_CALLCONV 64954925bf6Swillf krb5int_mutex_unlock (k5_mutex_t *m) 65054925bf6Swillf { 65154925bf6Swillf return k5_mutex_unlock (m); 65254925bf6Swillf } 653