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