1 /* 2 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 /* 7 * include/k5-thread.h 8 * 9 * Copyright 2004 by the Massachusetts Institute of Technology. 10 * All Rights Reserved. 11 * 12 * Export of this software from the United States of America may 13 * require a specific license from the United States Government. 14 * It is the responsibility of any person or organization contemplating 15 * export to obtain such a license before exporting. 16 * 17 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 18 * distribute this software and its documentation for any purpose and 19 * without fee is hereby granted, provided that the above copyright 20 * notice appear in all copies and that both that copyright notice and 21 * this permission notice appear in supporting documentation, and that 22 * the name of M.I.T. not be used in advertising or publicity pertaining 23 * to distribution of the software without specific, written prior 24 * permission. Furthermore if you modify this software you must label 25 * your software as modified software and not distribute it in such a 26 * fashion that it might be confused with the original M.I.T. software. 27 * M.I.T. makes no representations about the suitability of 28 * this software for any purpose. It is provided "as is" without express 29 * or implied warranty. 30 * 31 * 32 * Preliminary thread support. 33 */ 34 35 #ifndef K5_THREAD_H 36 #define K5_THREAD_H 37 38 #pragma ident "%Z%%M% %I% %E% SMI" 39 40 #ifndef _KERNEL /* SUNW14resync, mimic k5-int.h ? */ 41 #include "autoconf.h" 42 #endif 43 44 /* Interface (tentative): 45 46 Mutex support: 47 48 // Between these two, we should be able to do pure compile-time 49 // and pure run-time initialization. 50 // POSIX: partial initializer is PTHREAD_MUTEX_INITIALIZER, 51 // finish does nothing 52 // Windows: partial initializer is an invalid handle, 53 // finish does the real initialization work 54 // debug: partial initializer sets one magic value, 55 // finish verifies and sets a new magic value for 56 // lock/unlock to check 57 k5_mutex_t foo_mutex = K5_MUTEX_PARTIAL_INITIALIZER; 58 int k5_mutex_finish_init(k5_mutex_t *); 59 // for dynamic allocation 60 int k5_mutex_init(k5_mutex_t *); 61 // Must work for both kinds of alloc, even if it means adding flags. 62 int k5_mutex_destroy(k5_mutex_t *); 63 64 // As before. 65 int k5_mutex_lock(k5_mutex_t *); 66 int k5_mutex_unlock(k5_mutex_t *); 67 68 In each library, one new function to finish the static mutex init, 69 and any other library-wide initialization that might be desired. 70 On POSIX, this function would be called via the second support 71 function (see below). On Windows, it would be called at library 72 load time. These functions, or functions they calls, should be the 73 only places that k5_mutex_finish_init gets called. 74 75 A second function or macro called at various possible "first" entry 76 points which either calls pthread_once on the first function 77 (POSIX), or checks some flag set by the first function (Windows, 78 debug support), and possibly returns an error. (In the 79 non-threaded case, a simple flag can be used to avoid multiple 80 invocations, and the mutexes don't need run-time initialization 81 anyways.) 82 83 A third function for library termination calls mutex_destroy on 84 each mutex for the library. This function would be called 85 automatically at library unload time. If it turns out to be needed 86 at exit time for libraries that don't get unloaded, perhaps we 87 should also use atexit(). Any static mutexes should be cleaned up 88 with k5_mutex_destroy here. 89 90 How does that second support function invoke the first support 91 function only once? Through something modelled on pthread_once 92 that I haven't written up yet. Probably: 93 94 k5_once_t foo_once = K5_ONCE_INIT; 95 k5_once(k5_once_t *, void (*)(void)); 96 97 For POSIX: Map onto pthread_once facility. 98 For non-threaded case: A simple flag. 99 For Windows: Not needed; library init code takes care of it. 100 101 XXX: A general k5_once mechanism isn't possible for Windows, 102 without faking it through named mutexes or mutexes initialized at 103 startup. I was only using it in one place outside these headers, 104 so I'm dropping the general scheme. Eventually the existing uses 105 in k5-thread.h and k5-platform.h will be converted to pthread_once 106 or static variables. 107 108 109 Thread-specific data: 110 111 // TSD keys are limited in number in gssapi/krb5/com_err; enumerate 112 // them all. This allows support code init to allocate the 113 // necessary storage for pointers all at once, and avoids any 114 // possible error in key creation. 115 enum { ... } k5_key_t; 116 // Register destructor function. Called in library init code. 117 int k5_key_register(k5_key_t, void (*destructor)(void *)); 118 // Returns NULL or data. 119 void *k5_getspecific(k5_key_t); 120 // Returns error if key out of bounds, or the pointer table can't 121 // be allocated. A call to k5_key_register must have happened first. 122 // This may trigger the calling of pthread_setspecific on POSIX. 123 int k5_setspecific(k5_key_t, void *); 124 // Called in library termination code. 125 // Trashes data in all threads, calling the registered destructor 126 // (but calling it from the current thread). 127 int k5_key_delete(k5_key_t); 128 129 For the non-threaded version, the support code will have a static 130 array indexed by k5_key_t values, and get/setspecific simply access 131 the array elements. 132 133 The TSD destructor table is global state, protected by a mutex if 134 threads are enabled. 135 136 Debug support: Not much. Might check if k5_key_register has been 137 called and abort if not. 138 139 140 Any actual external symbols will use the krb5int_ prefix. The k5_ 141 names will be simple macros or inline functions to rename the 142 external symbols, or slightly more complex ones to expand the 143 implementation inline (e.g., map to POSIX versions and/or debug 144 code using __FILE__ and the like). 145 146 147 More to be added, perhaps. */ 148 149 #undef DEBUG_THREADS /* SUNW14resync XXX */ 150 #undef DEBUG_THREADS_LOC /* SUNW14resync XXX */ 151 #undef DEBUG_THREADS_SLOW /* debugging stuff that'll slow things down? */ 152 #undef DEBUG_THREADS_STATS 153 154 #ifndef _KERNEL 155 #include <assert.h> 156 #include <stdarg.h> 157 #define ASSERT assert 158 #endif 159 160 /* For tracking locations, of (e.g.) last lock or unlock of mutex. */ 161 #ifdef DEBUG_THREADS_LOC 162 typedef struct { 163 const char *filename; 164 short lineno; 165 } k5_debug_loc; 166 #define K5_DEBUG_LOC_INIT { __FILE__, __LINE__ } 167 #if __GNUC__ >= 2 168 #define K5_DEBUG_LOC (__extension__ (k5_debug_loc)K5_DEBUG_LOC_INIT) 169 #else 170 static inline k5_debug_loc k5_debug_make_loc(const char *file, short line) 171 { 172 k5_debug_loc l; 173 l.filename = file; 174 l.lineno = line; 175 return l; 176 } 177 #define K5_DEBUG_LOC (k5_debug_make_loc(__FILE__,__LINE__)) 178 #endif 179 #else /* ! DEBUG_THREADS_LOC */ 180 typedef char k5_debug_loc; 181 #define K5_DEBUG_LOC_INIT 0 182 #define K5_DEBUG_LOC 0 183 #endif 184 185 #define k5_debug_update_loc(L) ((L) = K5_DEBUG_LOC) 186 187 188 189 /* Statistics gathering: 190 191 Currently incomplete, don't try enabling it. 192 193 Eventually: Report number of times locked, total and standard 194 deviation of the time the lock was held, total and std dev time 195 spent waiting for the lock. "Report" will probably mean "write a 196 line to a file if a magic environment variable is set." */ 197 198 #ifdef DEBUG_THREADS_STATS 199 200 #if HAVE_TIME_H && (!defined(HAVE_SYS_TIME_H) || defined(TIME_WITH_SYS_TIME)) 201 # include <time.h> 202 #endif 203 #if HAVE_SYS_TIME_H 204 # include <sys/time.h> 205 #endif 206 #ifdef HAVE_STDINT_H 207 # include <stdint.h> 208 #endif 209 #include <inttypes.h> 210 typedef uint64_t k5_debug_timediff_t; 211 typedef struct timeval k5_debug_time_t; 212 static inline k5_debug_timediff_t 213 timediff(k5_debug_time_t t2, k5_debug_time_t t1) 214 { 215 return (t2.tv_sec - t1.tv_sec) * 1000000 + (t2.tv_usec - t1.tv_usec); 216 } 217 struct k5_timediff_stats { 218 k5_debug_timediff_t valmin, valmax, valsum, valsqsum; 219 }; 220 typedef struct { 221 int count; 222 k5_debug_time_t time_acquired, time_created; 223 struct k5_timediff_stats lockwait, lockheld; 224 } k5_debug_mutex_stats; 225 #define k5_mutex_init_stats(S) \ 226 (memset((S), 0, sizeof(struct k5_debug_mutex_stats)), 0) 227 #define k5_mutex_finish_init_stats(S) (0) 228 #define K5_MUTEX_STATS_INIT { 0, {0}, {0}, {0}, {0} } 229 230 #else 231 232 typedef char k5_debug_mutex_stats; 233 #define k5_mutex_init_stats(S) (*(S) = 's', 0) 234 #define k5_mutex_finish_init_stats(S) (0) 235 #define K5_MUTEX_STATS_INIT 's' 236 237 #endif 238 239 240 241 /* Define the OS mutex bit. */ 242 243 /* First, if we're not actually doing multiple threads, do we 244 want the debug support or not? */ 245 246 #ifdef DEBUG_THREADS 247 248 enum k5_mutex_init_states { 249 K5_MUTEX_DEBUG_PARTLY_INITIALIZED = 0x12, 250 K5_MUTEX_DEBUG_INITIALIZED, 251 K5_MUTEX_DEBUG_DESTROYED 252 }; 253 enum k5_mutex_flag_states { 254 K5_MUTEX_DEBUG_UNLOCKED = 0x23, 255 K5_MUTEX_DEBUG_LOCKED 256 }; 257 258 typedef struct { 259 enum k5_mutex_init_states initialized; 260 enum k5_mutex_flag_states locked; 261 } k5_os_nothread_mutex; 262 263 # define K5_OS_NOTHREAD_MUTEX_PARTIAL_INITIALIZER \ 264 { K5_MUTEX_DEBUG_PARTLY_INITIALIZED, K5_MUTEX_DEBUG_UNLOCKED } 265 266 # define k5_os_nothread_mutex_finish_init(M) \ 267 (ASSERT((M)->initialized != K5_MUTEX_DEBUG_INITIALIZED), \ 268 ASSERT((M)->initialized == K5_MUTEX_DEBUG_PARTLY_INITIALIZED), \ 269 ASSERT((M)->locked == K5_MUTEX_DEBUG_UNLOCKED), \ 270 (M)->initialized = K5_MUTEX_DEBUG_INITIALIZED, 0) 271 # define k5_os_nothread_mutex_init(M) \ 272 ((M)->initialized = K5_MUTEX_DEBUG_INITIALIZED, \ 273 (M)->locked = K5_MUTEX_DEBUG_UNLOCKED, 0) 274 # define k5_os_nothread_mutex_destroy(M) \ 275 (ASSERT((M)->initialized == K5_MUTEX_DEBUG_INITIALIZED), \ 276 (M)->initialized = K5_MUTEX_DEBUG_DESTROYED, 0) 277 278 # define k5_os_nothread_mutex_lock(M) \ 279 (k5_os_nothread_mutex_assert_unlocked(M), \ 280 (M)->locked = K5_MUTEX_DEBUG_LOCKED, 0) 281 # define k5_os_nothread_mutex_unlock(M) \ 282 (k5_os_nothread_mutex_assert_locked(M), \ 283 (M)->locked = K5_MUTEX_DEBUG_UNLOCKED, 0) 284 285 # define k5_os_nothread_mutex_assert_locked(M) \ 286 (ASSERT((M)->initialized == K5_MUTEX_DEBUG_INITIALIZED), \ 287 ASSERT((M)->locked != K5_MUTEX_DEBUG_UNLOCKED), \ 288 ASSERT((M)->locked == K5_MUTEX_DEBUG_LOCKED)) 289 # define k5_os_nothread_mutex_assert_unlocked(M) \ 290 (ASSERT((M)->initialized == K5_MUTEX_DEBUG_INITIALIZED), \ 291 ASSERT((M)->locked != K5_MUTEX_DEBUG_LOCKED), \ 292 ASSERT((M)->locked == K5_MUTEX_DEBUG_UNLOCKED)) 293 294 #else /* threads disabled and not debugging */ 295 296 typedef char k5_os_nothread_mutex; 297 # define K5_OS_NOTHREAD_MUTEX_PARTIAL_INITIALIZER 0 298 /* Empty inline functions avoid the "statement with no effect" 299 warnings, and do better type-checking than functions that don't use 300 their arguments. */ 301 /* SUNW 1.4resync, remove "inline" to avoid warning */ 302 /* ARGSUSED */ 303 /* LINTED */ 304 static int k5_os_nothread_mutex_finish_init(k5_os_nothread_mutex *m) { 305 return 0; 306 } 307 /* ARGSUSED */ 308 /* LINTED */ 309 static int k5_os_nothread_mutex_init(k5_os_nothread_mutex *m) { 310 return 0; 311 } 312 /* ARGSUSED */ 313 /* LINTED */ 314 static int k5_os_nothread_mutex_destroy(k5_os_nothread_mutex *m) { 315 return 0; 316 } 317 /* ARGSUSED */ 318 /* LINTED */ 319 static int k5_os_nothread_mutex_lock(k5_os_nothread_mutex *m) { 320 return 0; 321 } 322 /* ARGSUSED */ 323 /* LINTED */ 324 static int k5_os_nothread_mutex_unlock(k5_os_nothread_mutex *m) { 325 return 0; 326 } 327 # define k5_os_nothread_mutex_assert_locked(M) ((void)0) 328 # define k5_os_nothread_mutex_assert_unlocked(M) ((void)0) 329 330 #endif 331 332 /* Values: 333 2 - function has not been run 334 3 - function has been run 335 4 - function is being run -- deadlock detected */ 336 typedef unsigned char k5_os_nothread_once_t; 337 # define K5_OS_NOTHREAD_ONCE_INIT 2 338 # define k5_os_nothread_once(O,F) \ 339 (*(O) == 3 ? 0 \ 340 : *(O) == 2 ? (*(O) = 4, (F)(), *(O) = 3, 0) \ 341 : (ASSERT(*(O) != 4), ASSERT(*(O) == 2 || *(O) == 3), 0)) 342 343 344 345 #ifndef ENABLE_THREADS 346 347 typedef k5_os_nothread_mutex k5_os_mutex; 348 # define K5_OS_MUTEX_PARTIAL_INITIALIZER \ 349 K5_OS_NOTHREAD_MUTEX_PARTIAL_INITIALIZER 350 # define k5_os_mutex_finish_init k5_os_nothread_mutex_finish_init 351 # define k5_os_mutex_init k5_os_nothread_mutex_init 352 # define k5_os_mutex_destroy k5_os_nothread_mutex_destroy 353 # define k5_os_mutex_lock k5_os_nothread_mutex_lock 354 # define k5_os_mutex_unlock k5_os_nothread_mutex_unlock 355 # define k5_os_mutex_assert_locked k5_os_nothread_mutex_assert_locked 356 # define k5_os_mutex_assert_unlocked k5_os_nothread_mutex_assert_unlocked 357 358 # define k5_once_t k5_os_nothread_once_t 359 # define K5_ONCE_INIT K5_OS_NOTHREAD_ONCE_INIT 360 # define k5_once k5_os_nothread_once 361 362 #elif HAVE_PTHREAD 363 364 # include <pthread.h> 365 366 /* Weak reference support, etc. 367 368 Linux: Stub mutex routines exist, but pthread_once does not. 369 370 Solaris: In libc there's a pthread_once that doesn't seem 371 to do anything. Bleah. But pthread_mutexattr_setrobust_np 372 is defined only in libpthread. 373 374 IRIX 6.5 stub pthread support in libc is really annoying. The 375 pthread_mutex_lock function returns ENOSYS for a program not linked 376 against -lpthread. No link-time failure, no weak symbols, etc. 377 The C library doesn't provide pthread_once; we can use weak 378 reference support for that. 379 380 If weak references are not available, then for now, we assume that 381 the pthread support routines will always be available -- either the 382 real thing, or functional stubs that merely prohibit creating 383 threads. 384 385 If we find a platform with non-functional stubs and no weak 386 references, we may have to resort to some hack like dlsym on the 387 symbol tables of the current process. */ 388 #ifdef HAVE_PRAGMA_WEAK_REF 389 # pragma weak pthread_once 390 # pragma weak pthread_mutex_lock 391 # pragma weak pthread_mutex_unlock 392 # pragma weak pthread_mutex_destroy 393 # pragma weak pthread_mutex_init 394 # pragma weak pthread_self 395 # pragma weak pthread_equal 396 # ifdef HAVE_PTHREAD_MUTEXATTR_SETROBUST_NP_IN_THREAD_LIB 397 # pragma weak pthread_mutexattr_setrobust_np 398 # endif 399 # if !defined HAVE_PTHREAD_ONCE 400 # define K5_PTHREADS_LOADED (&pthread_once != 0) 401 # elif !defined HAVE_PTHREAD_MUTEXATTR_SETROBUST_NP \ 402 && defined HAVE_PTHREAD_MUTEXATTR_SETROBUST_NP_IN_THREAD_LIB 403 # define K5_PTHREADS_LOADED (&pthread_mutexattr_setrobust_np != 0) 404 # else 405 # define K5_PTHREADS_LOADED (1) 406 # endif 407 #else 408 /* no pragma weak support */ 409 # define K5_PTHREADS_LOADED (1) 410 #endif 411 412 #if defined(__mips) && defined(__sgi) && (defined(_SYSTYPE_SVR4) || defined(__SYSTYPE_SVR4__)) 413 /* IRIX 6.5 stub pthread support in libc is really annoying. The 414 pthread_mutex_lock function returns ENOSYS for a program not linked 415 against -lpthread. No link-time failure, no weak reference tests, 416 etc. 417 418 The C library doesn't provide pthread_once; we can use weak 419 reference support for that. */ 420 # ifndef HAVE_PRAGMA_WEAK_REF 421 # if defined(__GNUC__) && __GNUC__ < 3 422 # error "Please update to a newer gcc with weak symbol support, or switch to native cc, reconfigure and recompile." 423 # else 424 # error "Weak reference support is required" 425 # endif 426 # endif 427 # define USE_PTHREAD_LOCK_ONLY_IF_LOADED 428 #endif 429 430 #if !defined(HAVE_PTHREAD_MUTEX_LOCK) && !defined(USE_PTHREAD_LOCK_ONLY_IF_LOADED) 431 # define USE_PTHREAD_LOCK_ONLY_IF_LOADED 432 #endif 433 434 #ifdef HAVE_PRAGMA_WEAK_REF 435 /* Can't rely on useful stubs -- see above regarding Solaris. */ 436 typedef struct { 437 pthread_once_t o; 438 k5_os_nothread_once_t n; 439 } k5_once_t; 440 # define K5_ONCE_INIT { PTHREAD_ONCE_INIT, K5_OS_NOTHREAD_ONCE_INIT } 441 # define k5_once(O,F) (K5_PTHREADS_LOADED \ 442 ? pthread_once(&(O)->o,F) \ 443 : k5_os_nothread_once(&(O)->n,F)) 444 #else 445 typedef pthread_once_t k5_once_t; 446 # define K5_ONCE_INIT PTHREAD_ONCE_INIT 447 # define k5_once pthread_once 448 #endif 449 450 typedef struct { 451 pthread_mutex_t p; 452 #ifdef DEBUG_THREADS 453 pthread_t owner; 454 #endif 455 #ifdef USE_PTHREAD_LOCK_ONLY_IF_LOADED 456 k5_os_nothread_mutex n; 457 #endif 458 } k5_os_mutex; 459 460 #ifdef DEBUG_THREADS 461 # ifdef __GNUC__ 462 # define k5_pthread_mutex_lock(M) \ 463 ({ \ 464 k5_os_mutex *_m2 = (M); \ 465 int _r2 = pthread_mutex_lock(&_m2->p); \ 466 if (_r2 == 0) _m2->owner = pthread_self(); \ 467 _r2; \ 468 }) 469 # else 470 static inline int 471 k5_pthread_mutex_lock(k5_os_mutex *m) 472 { 473 int r = pthread_mutex_lock(&m->p); 474 if (r) 475 return r; 476 m->owner = pthread_self(); 477 return 0; 478 } 479 # endif 480 # define k5_pthread_assert_locked(M) \ 481 (K5_PTHREADS_LOADED \ 482 ? ASSERT(pthread_equal((M)->owner, pthread_self())) \ 483 : (void)0) 484 # define k5_pthread_mutex_unlock(M) \ 485 (k5_pthread_assert_locked(M), \ 486 (M)->owner = (pthread_t) 0, \ 487 pthread_mutex_unlock(&(M)->p)) 488 #else 489 # define k5_pthread_mutex_lock(M) pthread_mutex_lock(&(M)->p) 490 /* LINTED */ 491 static void k5_pthread_assert_locked(k5_os_mutex *m) { } 492 # define k5_pthread_mutex_unlock(M) pthread_mutex_unlock(&(M)->p) 493 #endif 494 495 /* Define as functions to: 496 (1) eliminate "statement with no effect" warnings for "0" 497 (2) encourage type-checking in calling code */ 498 499 /* LINTED */ 500 static void k5_pthread_assert_unlocked(pthread_mutex_t *m) { } 501 502 #if defined(DEBUG_THREADS_SLOW) && HAVE_SCHED_H && (HAVE_SCHED_YIELD || HAVE_PRAGMA_WEAK_REF) 503 # include <sched.h> 504 # if !HAVE_SCHED_YIELD 505 # pragma weak sched_yield 506 # define MAYBE_SCHED_YIELD() ((void)((&sched_yield != NULL) ? sched_yield() : 0)) 507 # else 508 # define MAYBE_SCHED_YIELD() ((void)sched_yield()) 509 # endif 510 #else 511 # define MAYBE_SCHED_YIELD() ((void)0) 512 #endif 513 514 /* It may not be obvious why this function is desirable. 515 516 I want to call pthread_mutex_lock, then sched_yield, then look at 517 the return code from pthread_mutex_lock. That can't be implemented 518 in a macro without a temporary variable, or GNU C extensions. 519 520 There used to be an inline function which did it, with both 521 functions called from the inline function. But that messes with 522 the debug information on a lot of configurations, and you can't 523 tell where the inline function was called from. (Typically, gdb 524 gives you the name of the function from which the inline function 525 was called, and a line number within the inline function itself.) 526 527 With this auxiliary function, pthread_mutex_lock can be called at 528 the invoking site via a macro; once it returns, the inline function 529 is called (with messed-up line-number info for gdb hopefully 530 localized to just that call). */ 531 #ifdef __GNUC__ 532 #define return_after_yield(R) \ 533 __extension__ ({ \ 534 int _r = (R); \ 535 MAYBE_SCHED_YIELD(); \ 536 _r; \ 537 }) 538 #else 539 static int return_after_yield(int r) 540 { 541 MAYBE_SCHED_YIELD(); 542 return r; 543 } 544 #endif 545 546 #ifdef USE_PTHREAD_LOCK_ONLY_IF_LOADED 547 548 # if defined(PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP) && defined(DEBUG_THREADS) 549 # define K5_OS_MUTEX_PARTIAL_INITIALIZER \ 550 { PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP, (pthread_t) 0, \ 551 K5_OS_NOTHREAD_MUTEX_PARTIAL_INITIALIZER } 552 # elif defined(DEBUG_THREADS) 553 # define K5_OS_MUTEX_PARTIAL_INITIALIZER \ 554 { PTHREAD_MUTEX_INITIALIZER, (pthread_t) 0, \ 555 K5_OS_NOTHREAD_MUTEX_PARTIAL_INITIALIZER } 556 # else 557 # define K5_OS_MUTEX_PARTIAL_INITIALIZER \ 558 { PTHREAD_MUTEX_INITIALIZER, K5_OS_NOTHREAD_MUTEX_PARTIAL_INITIALIZER } 559 # endif 560 561 # define k5_os_mutex_finish_init(M) \ 562 k5_os_nothread_mutex_finish_init(&(M)->n) 563 # define k5_os_mutex_init(M) \ 564 (k5_os_nothread_mutex_init(&(M)->n), \ 565 (K5_PTHREADS_LOADED \ 566 ? pthread_mutex_init(&(M)->p, 0) \ 567 : 0)) 568 # define k5_os_mutex_destroy(M) \ 569 (k5_os_nothread_mutex_destroy(&(M)->n), \ 570 (K5_PTHREADS_LOADED \ 571 ? pthread_mutex_destroy(&(M)->p) \ 572 : 0)) 573 574 # define k5_os_mutex_lock(M) \ 575 return_after_yield(K5_PTHREADS_LOADED \ 576 ? k5_pthread_mutex_lock(M) \ 577 : k5_os_nothread_mutex_lock(&(M)->n)) 578 # define k5_os_mutex_unlock(M) \ 579 (MAYBE_SCHED_YIELD(), \ 580 (K5_PTHREADS_LOADED \ 581 ? k5_pthread_mutex_unlock(M) \ 582 : k5_os_nothread_mutex_unlock(&(M)->n))) 583 584 # define k5_os_mutex_assert_unlocked(M) \ 585 (K5_PTHREADS_LOADED \ 586 ? k5_pthread_assert_unlocked(&(M)->p) \ 587 : k5_os_nothread_mutex_assert_unlocked(&(M)->n)) 588 # define k5_os_mutex_assert_locked(M) \ 589 (K5_PTHREADS_LOADED \ 590 ? k5_pthread_assert_locked(M) \ 591 : k5_os_nothread_mutex_assert_locked(&(M)->n)) 592 593 #else 594 595 # ifdef DEBUG_THREADS 596 # ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP 597 # define K5_OS_MUTEX_PARTIAL_INITIALIZER \ 598 { PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP, (pthread_t) 0 } 599 # else 600 # define K5_OS_MUTEX_PARTIAL_INITIALIZER \ 601 { PTHREAD_MUTEX_INITIALIZER, (pthread_t) 0 } 602 # endif 603 # else 604 # define K5_OS_MUTEX_PARTIAL_INITIALIZER \ 605 { PTHREAD_MUTEX_INITIALIZER } 606 # endif 607 608 /* LINTED */ 609 static int k5_os_mutex_finish_init(k5_os_mutex *m) { return 0; } 610 # define k5_os_mutex_init(M) pthread_mutex_init(&(M)->p, 0) 611 # define k5_os_mutex_destroy(M) pthread_mutex_destroy(&(M)->p) 612 # define k5_os_mutex_lock(M) return_after_yield(k5_pthread_mutex_lock(M)) 613 # define k5_os_mutex_unlock(M) (MAYBE_SCHED_YIELD(),k5_pthread_mutex_unlock(M)) 614 615 # define k5_os_mutex_assert_unlocked(M) k5_pthread_assert_unlocked(&(M)->p) 616 # define k5_os_mutex_assert_locked(M) k5_pthread_assert_locked(M) 617 618 #endif /* is pthreads always available? */ 619 620 #elif defined _WIN32 621 622 typedef struct { 623 HANDLE h; 624 int is_locked; 625 } k5_os_mutex; 626 627 # define K5_OS_MUTEX_PARTIAL_INITIALIZER { INVALID_HANDLE_VALUE, 0 } 628 629 # define k5_os_mutex_finish_init(M) \ 630 (ASSERT((M)->h == INVALID_HANDLE_VALUE), \ 631 ((M)->h = CreateMutex(NULL, FALSE, NULL)) ? 0 : GetLastError()) 632 # define k5_os_mutex_init(M) \ 633 ((M)->is_locked = 0, \ 634 ((M)->h = CreateMutex(NULL, FALSE, NULL)) ? 0 : GetLastError()) 635 # define k5_os_mutex_destroy(M) \ 636 (CloseHandle((M)->h) ? ((M)->h = 0, 0) : GetLastError()) 637 638 static inline int k5_os_mutex_lock(k5_os_mutex *m) 639 { 640 DWORD res; 641 res = WaitForSingleObject(m->h, INFINITE); 642 if (res == WAIT_FAILED) 643 return GetLastError(); 644 /* Eventually these should be turned into some reasonable error 645 code. */ 646 ASSERT(res != WAIT_TIMEOUT); 647 ASSERT(res != WAIT_ABANDONED); 648 ASSERT(res == WAIT_OBJECT_0); 649 /* Avoid locking twice. */ 650 ASSERT(m->is_locked == 0); 651 m->is_locked = 1; 652 return 0; 653 } 654 655 # define k5_os_mutex_unlock(M) \ 656 (ASSERT((M)->is_locked == 1), \ 657 (M)->is_locked = 0, \ 658 ReleaseMutex((M)->h) ? 0 : GetLastError()) 659 660 # define k5_os_mutex_assert_unlocked(M) ((void)0) 661 # define k5_os_mutex_assert_locked(M) ((void)0) 662 663 #else 664 665 # error "Thread support enabled, but thread system unknown" 666 667 #endif 668 669 670 671 672 typedef struct { 673 k5_debug_loc loc_last, loc_created; 674 k5_os_mutex os; 675 k5_debug_mutex_stats stats; 676 } k5_mutex_t; 677 #define K5_MUTEX_PARTIAL_INITIALIZER \ 678 { K5_DEBUG_LOC_INIT, K5_DEBUG_LOC_INIT, \ 679 K5_OS_MUTEX_PARTIAL_INITIALIZER, K5_MUTEX_STATS_INIT } 680 /* LINTED */ 681 static int k5_mutex_init_1(k5_mutex_t *m, k5_debug_loc l) 682 { 683 int err = k5_os_mutex_init(&m->os); 684 if (err) return err; 685 m->loc_created = m->loc_last = l; 686 err = k5_mutex_init_stats(&m->stats); 687 ASSERT(err == 0); 688 return 0; 689 } 690 #define k5_mutex_init(M) k5_mutex_init_1((M), K5_DEBUG_LOC) 691 /* LINTED */ 692 static int k5_mutex_finish_init_1(k5_mutex_t *m, k5_debug_loc l) 693 { 694 int err = k5_os_mutex_finish_init(&m->os); 695 if (err) return err; 696 m->loc_created = m->loc_last = l; 697 err = k5_mutex_finish_init_stats(&m->stats); 698 ASSERT(err == 0); 699 return 0; 700 } 701 #define k5_mutex_finish_init(M) k5_mutex_finish_init_1((M), K5_DEBUG_LOC) 702 #define k5_mutex_destroy(M) \ 703 (k5_os_mutex_assert_unlocked(&(M)->os), \ 704 k5_mutex_lock(M), (M)->loc_last = K5_DEBUG_LOC, k5_mutex_unlock(M), \ 705 k5_os_mutex_destroy(&(M)->os)) 706 #ifdef __GNUC__ 707 #define k5_mutex_lock(M) \ 708 __extension__ ({ \ 709 int _err = 0; \ 710 k5_mutex_t *_m = (M); \ 711 _err = k5_os_mutex_lock(&_m->os); \ 712 if (_err == 0) _m->loc_last = K5_DEBUG_LOC; \ 713 _err; \ 714 }) 715 #else 716 /* LINTED */ 717 static int k5_mutex_lock_1(k5_mutex_t *m, k5_debug_loc l) 718 { 719 int err = 0; 720 err = k5_os_mutex_lock(&m->os); 721 if (err) 722 return err; 723 m->loc_last = l; 724 return err; 725 } 726 #define k5_mutex_lock(M) k5_mutex_lock_1(M, K5_DEBUG_LOC) 727 #endif 728 #define k5_mutex_unlock(M) \ 729 (k5_mutex_assert_locked(M), \ 730 (M)->loc_last = K5_DEBUG_LOC, \ 731 k5_os_mutex_unlock(&(M)->os)) 732 733 #define k5_mutex_assert_locked(M) k5_os_mutex_assert_locked(&(M)->os) 734 #define k5_mutex_assert_unlocked(M) k5_os_mutex_assert_unlocked(&(M)->os) 735 736 #define k5_assert_locked k5_mutex_assert_locked 737 #define k5_assert_unlocked k5_mutex_assert_unlocked 738 739 740 /* Thread-specific data; implemented in a support file, because we'll 741 need to keep track of some global data for cleanup purposes. 742 743 Note that the callback function type is such that the C library 744 routine free() is a valid callback. */ 745 typedef enum { 746 K5_KEY_COM_ERR, 747 K5_KEY_GSS_KRB5_SET_CCACHE_OLD_NAME, 748 K5_KEY_GSS_KRB5_CCACHE_NAME, 749 K5_KEY_MAX 750 } k5_key_t; 751 /* rename shorthand symbols for export */ 752 #define k5_key_register krb5int_key_register 753 #define k5_getspecific krb5int_getspecific 754 #define k5_setspecific krb5int_setspecific 755 #define k5_key_delete krb5int_key_delete 756 extern int k5_key_register(k5_key_t, void (*)(void *)); 757 extern void *k5_getspecific(k5_key_t); 758 extern int k5_setspecific(k5_key_t, void *); 759 extern int k5_key_delete(k5_key_t); 760 761 #endif /* multiple inclusion? */ 762