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