Home | History | Annotate | Download | only in include
      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