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