144
137
More to be added, perhaps. */
146
#define DEBUG_THREADS
147
#define DEBUG_THREADS_LOC
148
#undef DEBUG_THREADS_SLOW /* debugging stuff that'll slow things down? */
149
#undef DEBUG_THREADS_STATS
151
139
#include <assert.h>
153
/* For tracking locations, of (e.g.) last lock or unlock of mutex. */
154
#ifdef DEBUG_THREADS_LOC
156
const char *filename;
159
#define K5_DEBUG_LOC_INIT { __FILE__, __LINE__ }
161
#define K5_DEBUG_LOC (__extension__ (k5_debug_loc)K5_DEBUG_LOC_INIT)
163
static inline k5_debug_loc k5_debug_make_loc(const char *file, int line)
170
#define K5_DEBUG_LOC (k5_debug_make_loc(__FILE__,__LINE__))
172
#else /* ! DEBUG_THREADS_LOC */
173
typedef char k5_debug_loc;
174
#define K5_DEBUG_LOC_INIT 0
175
#define K5_DEBUG_LOC 0
178
#define k5_debug_update_loc(L) ((L) = K5_DEBUG_LOC)
182
/* Statistics gathering:
184
Currently incomplete, don't try enabling it.
186
Eventually: Report number of times locked, total and standard
187
deviation of the time the lock was held, total and std dev time
188
spent waiting for the lock. "Report" will probably mean "write a
189
line to a file if a magic environment variable is set." */
191
#ifdef DEBUG_THREADS_STATS
193
#if HAVE_TIME_H && (!defined(HAVE_SYS_TIME_H) || defined(TIME_WITH_SYS_TIME))
197
# include <sys/time.h>
205
#include <inttypes.h>
206
typedef uint64_t k5_debug_timediff_t; /* or long double */
207
typedef struct timeval k5_debug_time_t;
208
static inline k5_debug_timediff_t
209
timediff(k5_debug_time_t t2, k5_debug_time_t t1)
211
return (t2.tv_sec - t1.tv_sec) * 1000000 + (t2.tv_usec - t1.tv_usec);
213
static inline k5_debug_time_t get_current_time(void)
216
if (gettimeofday(&tv,0) < 0) { tv.tv_sec = tv.tv_usec = 0; }
219
struct k5_timediff_stats {
220
k5_debug_timediff_t valmin, valmax, valsum, valsqsum;
224
k5_debug_time_t time_acquired, time_created;
225
struct k5_timediff_stats lockwait, lockheld;
226
} k5_debug_mutex_stats;
227
#define k5_mutex_init_stats(S) \
228
(memset((S), 0, sizeof(k5_debug_mutex_stats)), \
229
(S)->time_created = get_current_time(), \
231
#define k5_mutex_finish_init_stats(S) (0)
232
#define K5_MUTEX_STATS_INIT { 0, {0}, {0}, {0}, {0} }
233
typedef k5_debug_time_t k5_mutex_stats_tmp;
234
#define k5_mutex_stats_start() get_current_time()
235
void KRB5_CALLCONV krb5int_mutex_lock_update_stats(k5_debug_mutex_stats *m,
236
k5_mutex_stats_tmp start);
237
void KRB5_CALLCONV krb5int_mutex_unlock_update_stats(k5_debug_mutex_stats *m);
238
#define k5_mutex_lock_update_stats krb5int_mutex_lock_update_stats
239
#define k5_mutex_unlock_update_stats krb5int_mutex_unlock_update_stats
240
void KRB5_CALLCONV krb5int_mutex_report_stats(/* k5_mutex_t *m */);
244
typedef char k5_debug_mutex_stats;
245
#define k5_mutex_init_stats(S) (*(S) = 's', 0)
246
#define k5_mutex_finish_init_stats(S) (0)
247
#define K5_MUTEX_STATS_INIT 's'
248
typedef int k5_mutex_stats_tmp;
249
#define k5_mutex_stats_start() (0)
252
k5_mutex_lock_update_stats(k5_debug_mutex_stats *m, k5_mutex_stats_tmp t)
256
# define k5_mutex_lock_update_stats(M,S) (S)
258
#define k5_mutex_unlock_update_stats(M) (*(M) = 's')
260
/* If statistics tracking isn't enabled, these functions don't actually
261
do anything. Declare anyways so we can do type checking etc. */
262
void KRB5_CALLCONV krb5int_mutex_lock_update_stats(k5_debug_mutex_stats *m,
263
k5_mutex_stats_tmp start);
264
void KRB5_CALLCONV krb5int_mutex_unlock_update_stats(k5_debug_mutex_stats *m);
265
void KRB5_CALLCONV krb5int_mutex_report_stats(/* k5_mutex_t *m */);
267
#define krb5int_mutex_report_stats(M) ((M)->stats = 'd')
142
/* The mutex structure we use, k5_mutex_t, is defined to some
143
OS-specific bits. The use of multiple layers of typedefs are an
144
artifact resulting from debugging code we once used, implemented as
145
wrappers around the OS mutex scheme.
147
The OS specific bits, in k5_os_mutex, break down into three primary
148
implementations, POSIX threads, Windows threads, and no thread
149
support. However, the POSIX thread version is further subdivided:
150
In one case, we can determine at run time whether the thread
151
library is linked into the application, and use it only if it is
152
present; in the other case, we cannot, and the thread library must
153
be linked in always, but can be used unconditionally. In the
154
former case, the k5_os_mutex structure needs to hold both the POSIX
155
and the non-threaded versions.
157
The various k5_os_mutex_* operations are the OS-specific versions,
158
applied to the OS-specific data, and k5_mutex_* uses k5_os_mutex_*
159
to do the OS-specific parts of the work. */
273
161
/* Define the OS mutex bit. */
275
/* First, if we're not actually doing multiple threads, do we
276
want the debug support or not? */
280
enum k5_mutex_init_states {
281
K5_MUTEX_DEBUG_PARTLY_INITIALIZED = 0x12,
282
K5_MUTEX_DEBUG_INITIALIZED,
283
K5_MUTEX_DEBUG_DESTROYED
285
enum k5_mutex_flag_states {
286
K5_MUTEX_DEBUG_UNLOCKED = 0x23,
287
K5_MUTEX_DEBUG_LOCKED
291
enum k5_mutex_init_states initialized;
292
enum k5_mutex_flag_states locked;
293
} k5_os_nothread_mutex;
295
# define K5_OS_NOTHREAD_MUTEX_PARTIAL_INITIALIZER \
296
{ K5_MUTEX_DEBUG_PARTLY_INITIALIZED, K5_MUTEX_DEBUG_UNLOCKED }
298
# define k5_os_nothread_mutex_finish_init(M) \
299
(assert((M)->initialized != K5_MUTEX_DEBUG_INITIALIZED), \
300
assert((M)->initialized == K5_MUTEX_DEBUG_PARTLY_INITIALIZED), \
301
assert((M)->locked == K5_MUTEX_DEBUG_UNLOCKED), \
302
(M)->initialized = K5_MUTEX_DEBUG_INITIALIZED, 0)
303
# define k5_os_nothread_mutex_init(M) \
304
((M)->initialized = K5_MUTEX_DEBUG_INITIALIZED, \
305
(M)->locked = K5_MUTEX_DEBUG_UNLOCKED, 0)
306
# define k5_os_nothread_mutex_destroy(M) \
307
(assert((M)->initialized == K5_MUTEX_DEBUG_INITIALIZED), \
308
(M)->initialized = K5_MUTEX_DEBUG_DESTROYED, 0)
310
# define k5_os_nothread_mutex_lock(M) \
311
(k5_os_nothread_mutex_assert_unlocked(M), \
312
(M)->locked = K5_MUTEX_DEBUG_LOCKED, 0)
313
# define k5_os_nothread_mutex_unlock(M) \
314
(k5_os_nothread_mutex_assert_locked(M), \
315
(M)->locked = K5_MUTEX_DEBUG_UNLOCKED, 0)
317
# define k5_os_nothread_mutex_assert_locked(M) \
318
(assert((M)->initialized == K5_MUTEX_DEBUG_INITIALIZED), \
319
assert((M)->locked != K5_MUTEX_DEBUG_UNLOCKED), \
320
assert((M)->locked == K5_MUTEX_DEBUG_LOCKED))
321
# define k5_os_nothread_mutex_assert_unlocked(M) \
322
(assert((M)->initialized == K5_MUTEX_DEBUG_INITIALIZED), \
323
assert((M)->locked != K5_MUTEX_DEBUG_LOCKED), \
324
assert((M)->locked == K5_MUTEX_DEBUG_UNLOCKED))
326
#else /* threads disabled and not debugging */
328
163
typedef char k5_os_nothread_mutex;
329
164
# define K5_OS_NOTHREAD_MUTEX_PARTIAL_INITIALIZER 0
330
165
/* Empty inline functions avoid the "statement with no effect"
456
271
# define k5_once(O,F) (K5_PTHREADS_LOADED \
457
272
? pthread_once(&(O)->o,F) \
458
273
: k5_os_nothread_once(&(O)->n,F))
277
/* no pragma weak support */
278
# define K5_PTHREADS_LOADED (1)
460
280
typedef pthread_once_t k5_once_t;
461
281
# define K5_ONCE_INIT PTHREAD_ONCE_INIT
462
282
# define k5_once pthread_once
470
#ifdef USE_PTHREAD_LOCK_ONLY_IF_LOADED
471
k5_os_nothread_mutex n;
477
# define k5_pthread_mutex_lock(M) \
479
k5_os_mutex *_m2 = (M); \
480
int _r2 = pthread_mutex_lock(&_m2->p); \
481
if (_r2 == 0) _m2->owner = pthread_self(); \
486
k5_pthread_mutex_lock(k5_os_mutex *m)
488
int r = pthread_mutex_lock(&m->p);
491
m->owner = pthread_self();
495
# define k5_pthread_assert_locked(M) \
496
(K5_PTHREADS_LOADED \
497
? assert(pthread_equal((M)->owner, pthread_self())) \
499
# define k5_pthread_mutex_unlock(M) \
500
(k5_pthread_assert_locked(M), \
501
(M)->owner = (pthread_t) 0, \
502
pthread_mutex_unlock(&(M)->p))
504
# define k5_pthread_mutex_lock(M) pthread_mutex_lock(&(M)->p)
505
static inline void k5_pthread_assert_locked(k5_os_mutex *m) { }
506
# define k5_pthread_mutex_unlock(M) pthread_mutex_unlock(&(M)->p)
509
/* Define as functions to:
510
(1) eliminate "statement with no effect" warnings for "0"
511
(2) encourage type-checking in calling code */
513
static inline void k5_pthread_assert_unlocked(pthread_mutex_t *m) { }
515
#if defined(DEBUG_THREADS_SLOW) && HAVE_SCHED_H && (HAVE_SCHED_YIELD || HAVE_PRAGMA_WEAK_REF)
517
# if !HAVE_SCHED_YIELD
518
# pragma weak sched_yield
519
# define MAYBE_SCHED_YIELD() ((void)((&sched_yield != NULL) ? sched_yield() : 0))
521
# define MAYBE_SCHED_YIELD() ((void)sched_yield())
524
# define MAYBE_SCHED_YIELD() ((void)0)
527
/* It may not be obvious why this function is desirable.
529
I want to call pthread_mutex_lock, then sched_yield, then look at
530
the return code from pthread_mutex_lock. That can't be implemented
531
in a macro without a temporary variable, or GNU C extensions.
533
There used to be an inline function which did it, with both
534
functions called from the inline function. But that messes with
535
the debug information on a lot of configurations, and you can't
536
tell where the inline function was called from. (Typically, gdb
537
gives you the name of the function from which the inline function
538
was called, and a line number within the inline function itself.)
540
With this auxiliary function, pthread_mutex_lock can be called at
541
the invoking site via a macro; once it returns, the inline function
542
is called (with messed-up line-number info for gdb hopefully
543
localized to just that call). */
545
#define return_after_yield(R) \
548
MAYBE_SCHED_YIELD(); \
552
static inline int return_after_yield(int r)
559
#ifdef USE_PTHREAD_LOCK_ONLY_IF_LOADED
561
# if defined(PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP) && defined(DEBUG_THREADS)
562
# define K5_OS_MUTEX_PARTIAL_INITIALIZER \
563
{ PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP, (pthread_t) 0, \
564
K5_OS_NOTHREAD_MUTEX_PARTIAL_INITIALIZER }
565
# elif defined(DEBUG_THREADS)
566
# define K5_OS_MUTEX_PARTIAL_INITIALIZER \
567
{ PTHREAD_MUTEX_INITIALIZER, (pthread_t) 0, \
568
K5_OS_NOTHREAD_MUTEX_PARTIAL_INITIALIZER }
570
# define K5_OS_MUTEX_PARTIAL_INITIALIZER \
571
{ PTHREAD_MUTEX_INITIALIZER, K5_OS_NOTHREAD_MUTEX_PARTIAL_INITIALIZER }
574
# define k5_os_mutex_finish_init(M) \
575
k5_os_nothread_mutex_finish_init(&(M)->n)
286
#if defined(__mips) && defined(__sgi) && (defined(_SYSTYPE_SVR4) || defined(__SYSTYPE_SVR4__))
287
# ifndef HAVE_PRAGMA_WEAK_REF
288
# if defined(__GNUC__) && __GNUC__ < 3
289
# error "Please update to a newer gcc with weak symbol support, or switch to native cc, reconfigure and recompile."
291
# error "Weak reference support is required"
296
typedef pthread_mutex_t k5_os_mutex;
297
# define K5_OS_MUTEX_PARTIAL_INITIALIZER \
298
PTHREAD_MUTEX_INITIALIZER
300
#ifdef USE_PTHREAD_LOCK_ONLY_IF_LOADED
302
# define k5_os_mutex_finish_init(M) (0)
576
303
# define k5_os_mutex_init(M) \
577
(k5_os_nothread_mutex_init(&(M)->n), \
578
(K5_PTHREADS_LOADED \
579
? pthread_mutex_init(&(M)->p, 0) \
304
(K5_PTHREADS_LOADED ? pthread_mutex_init((M), 0) : 0)
581
305
# define k5_os_mutex_destroy(M) \
582
(k5_os_nothread_mutex_destroy(&(M)->n), \
583
(K5_PTHREADS_LOADED \
584
? pthread_mutex_destroy(&(M)->p) \
587
# define k5_os_mutex_lock(M) \
588
return_after_yield(K5_PTHREADS_LOADED \
589
? k5_pthread_mutex_lock(M) \
590
: k5_os_nothread_mutex_lock(&(M)->n))
591
# define k5_os_mutex_unlock(M) \
592
(MAYBE_SCHED_YIELD(), \
593
(K5_PTHREADS_LOADED \
594
? k5_pthread_mutex_unlock(M) \
595
: k5_os_nothread_mutex_unlock(&(M)->n)))
597
# define k5_os_mutex_assert_unlocked(M) \
598
(K5_PTHREADS_LOADED \
599
? k5_pthread_assert_unlocked(&(M)->p) \
600
: k5_os_nothread_mutex_assert_unlocked(&(M)->n))
601
# define k5_os_mutex_assert_locked(M) \
602
(K5_PTHREADS_LOADED \
603
? k5_pthread_assert_locked(M) \
604
: k5_os_nothread_mutex_assert_locked(&(M)->n))
306
(K5_PTHREADS_LOADED ? pthread_mutex_destroy((M)) : 0)
307
# define k5_os_mutex_lock(M) \
308
(K5_PTHREADS_LOADED ? pthread_mutex_lock(M) : 0)
309
# define k5_os_mutex_unlock(M) \
310
(K5_PTHREADS_LOADED ? pthread_mutex_unlock(M) : 0)
608
# ifdef DEBUG_THREADS
609
# ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
610
# define K5_OS_MUTEX_PARTIAL_INITIALIZER \
611
{ PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP, (pthread_t) 0 }
613
# define K5_OS_MUTEX_PARTIAL_INITIALIZER \
614
{ PTHREAD_MUTEX_INITIALIZER, (pthread_t) 0 }
617
# define K5_OS_MUTEX_PARTIAL_INITIALIZER \
618
{ PTHREAD_MUTEX_INITIALIZER }
621
314
static inline int k5_os_mutex_finish_init(k5_os_mutex *m) { return 0; }
622
# define k5_os_mutex_init(M) pthread_mutex_init(&(M)->p, 0)
623
# define k5_os_mutex_destroy(M) pthread_mutex_destroy(&(M)->p)
624
# define k5_os_mutex_lock(M) return_after_yield(k5_pthread_mutex_lock(M))
625
# define k5_os_mutex_unlock(M) (MAYBE_SCHED_YIELD(),k5_pthread_mutex_unlock(M))
627
# define k5_os_mutex_assert_unlocked(M) k5_pthread_assert_unlocked(&(M)->p)
628
# define k5_os_mutex_assert_locked(M) k5_pthread_assert_locked(M)
315
# define k5_os_mutex_init(M) pthread_mutex_init((M), 0)
316
# define k5_os_mutex_destroy(M) pthread_mutex_destroy((M))
317
# define k5_os_mutex_lock(M) pthread_mutex_lock(M)
318
# define k5_os_mutex_unlock(M) pthread_mutex_unlock(M)
630
320
#endif /* is pthreads always available? */
685
k5_debug_loc loc_last, loc_created;
687
k5_debug_mutex_stats stats;
689
#define K5_MUTEX_PARTIAL_INITIALIZER \
690
{ K5_DEBUG_LOC_INIT, K5_DEBUG_LOC_INIT, \
691
K5_OS_MUTEX_PARTIAL_INITIALIZER, K5_MUTEX_STATS_INIT }
692
static inline int k5_mutex_init_1(k5_mutex_t *m, k5_debug_loc l)
694
int err = k5_os_mutex_init(&m->os);
696
m->loc_created = m->loc_last = l;
697
err = k5_mutex_init_stats(&m->stats);
701
#define k5_mutex_init(M) k5_mutex_init_1((M), K5_DEBUG_LOC)
702
static inline int k5_mutex_finish_init_1(k5_mutex_t *m, k5_debug_loc l)
704
int err = k5_os_mutex_finish_init(&m->os);
706
m->loc_created = m->loc_last = l;
707
err = k5_mutex_finish_init_stats(&m->stats);
711
#define k5_mutex_finish_init(M) k5_mutex_finish_init_1((M), K5_DEBUG_LOC)
371
typedef k5_os_mutex k5_mutex_t;
372
#define K5_MUTEX_PARTIAL_INITIALIZER K5_OS_MUTEX_PARTIAL_INITIALIZER
373
static inline int k5_mutex_init(k5_mutex_t *m)
375
return k5_os_mutex_init(m);
377
static inline int k5_mutex_finish_init(k5_mutex_t *m)
379
return k5_os_mutex_finish_init(m);
712
381
#define k5_mutex_destroy(M) \
713
(k5_os_mutex_assert_unlocked(&(M)->os), \
714
krb5int_mutex_report_stats(M), \
715
k5_mutex_lock(M), (M)->loc_last = K5_DEBUG_LOC, k5_mutex_unlock(M), \
716
k5_os_mutex_destroy(&(M)->os))
718
#define k5_mutex_lock(M) \
721
k5_mutex_stats_tmp _stats = k5_mutex_stats_start(); \
722
k5_mutex_t *_m = (M); \
723
_err = k5_os_mutex_lock(&_m->os); \
724
if (_err == 0) _m->loc_last = K5_DEBUG_LOC; \
725
if (_err == 0) k5_mutex_lock_update_stats(&_m->stats, _stats); \
729
static inline int k5_mutex_lock_1(k5_mutex_t *m, k5_debug_loc l)
382
(k5_os_mutex_destroy(M))
385
static int k5_mutex_lock(k5_mutex_t *)
386
__attribute__((warn_unused_result));
388
static inline int k5_mutex_lock(k5_mutex_t *m)
732
k5_mutex_stats_tmp stats = k5_mutex_stats_start();
733
err = k5_os_mutex_lock(&m->os);
737
k5_mutex_lock_update_stats(&m->stats, stats);
390
return k5_os_mutex_lock(m);
740
#define k5_mutex_lock(M) k5_mutex_lock_1(M, K5_DEBUG_LOC)
742
393
#define k5_mutex_unlock(M) \
743
(k5_mutex_assert_locked(M), \
744
k5_mutex_unlock_update_stats(&(M)->stats), \
745
(M)->loc_last = K5_DEBUG_LOC, \
746
k5_os_mutex_unlock(&(M)->os))
748
#define k5_mutex_assert_locked(M) k5_os_mutex_assert_locked(&(M)->os)
749
#define k5_mutex_assert_unlocked(M) k5_os_mutex_assert_unlocked(&(M)->os)
394
(k5_os_mutex_unlock(M))
396
#define k5_mutex_assert_locked(M) ((void)(M))
397
#define k5_mutex_assert_unlocked(M) ((void)(M))
751
398
#define k5_assert_locked k5_mutex_assert_locked
752
399
#define k5_assert_unlocked k5_mutex_assert_unlocked