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