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