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