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