* k5-thread.h: Restructured mutex code.
authorKen Raeburn <raeburn@mit.edu>
Fri, 2 Jul 2004 22:16:56 +0000 (22:16 +0000)
committerKen Raeburn <raeburn@mit.edu>
Fri, 2 Jul 2004 22:16:56 +0000 (22:16 +0000)
(k5_debug_loc): New type, may contain file/line info if DEBUG_THREADS_LOC is
defined.
(k5_os_nothread_*): Dummy implementation of mutex lock for a single-threded
process.  Uses a flag and assert() if DEBUG_THREADS is defined, does nothing
interesting otherwise.
(k5_os_mutex*, k5_once*): General implementations, with dummy or POSIX or
POSIX-if-loaded-otherwise-dummy variants.
(k5_mutex_*): Combine OS-specific mutex implementation with optional file/line
tracking, and provide a place to instrument for other debugging or performance
data.

git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@16539 dc483132-0cff-0310-8789-dd5450dbe970

src/include/ChangeLog
src/include/k5-thread.h

index 00c437d7970f1e74f3cb40427ea2f982dc7167a1..675e362585df7d28f1254bb42f554a9eacd9b5e3 100644 (file)
@@ -1,3 +1,17 @@
+2004-07-02  Ken Raeburn  <raeburn@mit.edu>
+
+       * k5-thread.h: Restructured mutex code.
+       (k5_debug_loc): New type, may contain file/line info if
+       DEBUG_THREADS_LOC is defined.
+       (k5_os_nothread_*): Dummy implementation of mutex lock for a
+       single-threded process.  Uses a flag and assert() if DEBUG_THREADS
+       is defined, does nothing interesting otherwise.
+       (k5_os_mutex*, k5_once*): General implementations, with dummy or
+       POSIX or POSIX-if-loaded-otherwise-dummy variants.
+       (k5_mutex_*): Combine OS-specific mutex implementation with
+       optional file/line tracking, and provide a place to instrument for
+       other debugging or performance data.
+
 2004-07-01  Ken Raeburn  <raeburn@mit.edu>
 
        * configure.in: Test for 'inline' support.
index a64f862d4e9f52af682c251b422da82270366c15..3619472a98dbbe479f686352f96dc41ce6b47be2 100644 (file)
@@ -31,7 +31,7 @@
 
 #include "autoconf.h"
 
-/* Interface (tentative):
+\f/* Interface (tentative):
 
    Mutex support:
 
@@ -55,7 +55,6 @@
    int k5_mutex_lock(k5_mutex_t *);
    int k5_mutex_unlock(k5_mutex_t *);
 
-
    In each library, one new function to finish the static mutex init,
    and any other library-wide initialization that might be desired.
    On POSIX, this function would be called via the second support
@@ -78,7 +77,6 @@
    should also use atexit().  Any static mutexes should be cleaned up
    with k5_mutex_destroy here.
 
-
    How does that second support function invoke the first support
    function only once?  Through something modelled on pthread_once
    that I haven't written up yet.  Probably:
@@ -96,7 +94,7 @@
    so I'm dropping the general scheme.  Eventually the existing uses
    in k5-thread.h and k5-platform.h will be converted to pthread_once
    or static variables.
-
+\f
 
    Thread-specific data:
 
 #endif
 
 #define DEBUG_THREADS
+#define DEBUG_THREADS_LOC
 
 #include <assert.h>
-enum k5_mutex_debug_states {
-    K5_MUTEX_DEBUG_UNLOCKED = 34,
-    K5_MUTEX_DEBUG_LOCKED = 47
-};
+\f
+/* For tracking locations, of (e.g.) last lock or unlock of mutex.  */
+#ifdef DEBUG_THREADS_LOC
 typedef struct {
     const char *filename;
-    /* No source file in this tree gets anywhere near 32K lines.  */
     short lineno;
-    short initialized;
-    enum k5_mutex_debug_states locked;
-} k5_mutex_debug_info;
-#define K5_MUTEX_DEBUG_INITIALIZER     { __FILE__, __LINE__, 2, K5_MUTEX_DEBUG_UNLOCKED }
-#define k5_mutex_debug_finish_init(M)                          \
-       (assert((M)->initialized == 2), (M)->initialized = 1,   \
-        k5_mutex_debug_update_loc(M), 0)
-#define k5_mutex_debug_init(M)                 \
-       ((M)->initialized = 1,                  \
-        (M)->locked = K5_MUTEX_DEBUG_UNLOCKED, \
-        k5_mutex_debug_update_loc(M), 0)
-#define k5_mutex_debug_destroy(M)                              \
-       (assert((M)->initialized == 1                           \
-               && (M)->locked == K5_MUTEX_DEBUG_UNLOCKED),     \
-        k5_mutex_debug_update_loc(M),                          \
-        (M)->initialized = 0)
-#define k5_mutex_debug_check_init(M)           \
-       (assert((M)->initialized != 2),         \
-        assert((M)->initialized != 0),         \
-        assert((M)->initialized == 1), 0)
-#define k5_mutex_debug_update_loc(M)                           \
-       ((M)->lineno = __LINE__, (M)->filename = __FILE__)
-#define k5_mutex_debug_lock(M)                                 \
-       (k5_debug_assert_unlocked(M),                           \
-        k5_mutex_debug_update_loc(M),                          \
+} k5_debug_loc;
+#define K5_DEBUG_LOC_INIT      { __FILE__, __LINE__ }
+#if __GNUC__ >= 2
+#define K5_DEBUG_LOC           (__extension__ (k5_debug_loc)K5_DEBUG_LOC_INIT)
+#else
+static inline k5_debug_loc k5_debug_make_loc(const char *file, short line)
+{
+    k5_debug_loc l;
+    l.filename = file;
+    l.lineno = line;
+    return l;
+}
+#define K5_DEBUG_LOC           (k5_debug_make_loc(__FILE__,__LINE__))
+#endif
+#else /* ! DEBUG_THREADS_LOC */
+typedef char k5_debug_loc;
+#define K5_DEBUG_LOC_INIT      0
+#define K5_DEBUG_LOC           0
+#endif
+
+#define k5_debug_update_loc(L) ((L) = K5_DEBUG_LOC)
+
+\f
+
+/* Define the OS mutex bit.  */
+
+/* First, if we're not actually doing multiple threads, do we
+   want the debug support or not?  */
+
+#ifdef DEBUG_THREADS
+
+enum k5_mutex_init_states {
+    K5_MUTEX_DEBUG_PARTLY_INITIALIZED = 0x12,
+    K5_MUTEX_DEBUG_INITIALIZED,
+    K5_MUTEX_DEBUG_DESTROYED
+};
+enum k5_mutex_flag_states {
+    K5_MUTEX_DEBUG_UNLOCKED = 0x23,
+    K5_MUTEX_DEBUG_LOCKED
+};
+
+typedef struct {
+    enum k5_mutex_init_states initialized;
+    enum k5_mutex_flag_states locked;
+} k5_os_nothread_mutex;
+
+# define K5_OS_NOTHREAD_MUTEX_PARTIAL_INITIALIZER \
+       { K5_MUTEX_DEBUG_PARTLY_INITIALIZED, K5_MUTEX_DEBUG_UNLOCKED }
+
+# define k5_os_nothread_mutex_finish_init(M)                           \
+       (assert((M)->initialized != K5_MUTEX_DEBUG_INITIALIZED),        \
+        assert((M)->initialized == K5_MUTEX_DEBUG_PARTLY_INITIALIZED), \
+        assert((M)->locked == K5_MUTEX_DEBUG_UNLOCKED),                \
+        (M)->initialized = K5_MUTEX_DEBUG_INITIALIZED, 0)
+# define k5_os_nothread_mutex_init(M)                  \
+       ((M)->initialized = K5_MUTEX_DEBUG_INITIALIZED, \
+        (M)->locked = K5_MUTEX_DEBUG_UNLOCKED, 0)
+# define k5_os_nothread_mutex_destroy(M)                               \
+       (assert((M)->initialized == K5_MUTEX_DEBUG_INITIALIZED),        \
+        (M)->initialized = K5_MUTEX_DEBUG_DESTROYED, 0)
+
+# define k5_os_nothread_mutex_lock(M)                  \
+       (k5_os_nothread_mutex_assert_unlocked(M),       \
         (M)->locked = K5_MUTEX_DEBUG_LOCKED, 0)
-#define k5_mutex_debug_unlock(M)                               \
-       (k5_debug_assert_locked(M),                             \
-        k5_mutex_debug_update_loc(M),                          \
+# define k5_os_nothread_mutex_unlock(M)                        \
+       (k5_os_nothread_mutex_assert_locked(M),         \
         (M)->locked = K5_MUTEX_DEBUG_UNLOCKED, 0)
-#define k5_debug_assert_locked(M)                              \
-       (k5_mutex_debug_check_init(M),                          \
-        assert((M)->locked != K5_MUTEX_DEBUG_UNLOCKED),        \
+
+# define k5_os_nothread_mutex_assert_locked(M)                         \
+       (assert((M)->initialized == K5_MUTEX_DEBUG_INITIALIZED),        \
+        assert((M)->locked != K5_MUTEX_DEBUG_UNLOCKED),                \
         assert((M)->locked == K5_MUTEX_DEBUG_LOCKED), 0)
-#define k5_debug_assert_unlocked(M)                            \
-       (k5_mutex_debug_check_init(M),                          \
+# define k5_os_nothread_mutex_assert_unlocked(M)                       \
+       (assert((M)->initialized == K5_MUTEX_DEBUG_INITIALIZED),        \
+        assert((M)->locked != K5_MUTEX_DEBUG_LOCKED),                  \
         assert((M)->locked == K5_MUTEX_DEBUG_UNLOCKED), 0)
 
-typedef enum {
-    K5_KEY_COM_ERR,
-    K5_KEY_MAX
-} k5_key_t;
-
+#else /* threads disabled and not debugging */
 
-#ifdef ENABLE_THREADS
+typedef char k5_os_nothread_mutex;
+# define K5_OS_NOTHREAD_MUTEX_PARTIAL_INITIALIZER      0
+# define k5_os_nothread_mutex_finish_init(M)           (0)
+# define k5_os_nothread_mutex_init(M)                  (0)
+# define k5_os_nothread_mutex_destroy(M)               (0)
+# define k5_os_nothread_mutex_lock(M)                  (0)
+# define k5_os_nothread_mutex_unlock(M)                        (0)
+# define k5_os_nothread_mutex_assert_locked(M)         (0)
+# define k5_os_nothread_mutex_assert_unlocked(M)       (0)
 
-#ifdef HAVE_PTHREAD_H
-
-#include <pthread.h>
-
-/* To do:  Weak symbol support.  Windows threads.
+#endif
 
-   Mutex initialization may need to be re-thought if we find we want
-   any non-default attributes, like priority inheritance.  */
+/* Values:
+   2 - function has not been run
+   3 - function has been run
+   4 - function is being run -- deadlock detected */
+typedef unsigned char k5_os_nothread_once_t;
+# define K5_OS_NOTHREAD_ONCE_INIT      2
+# define k5_os_nothread_once(O,F)                                      \
+       (*(O) == 3 ? 0                                                  \
+        : *(O) == 2 ? (*(O) = 4, (F)(), *(O) = 3, 0)                   \
+        : (assert(*(O) != 4), assert(*(O) == 2 || *(O) == 3), 0))
 
-#ifndef DEBUG_THREADS
 
-typedef pthread_mutex_t k5_mutex_t;
-#define K5_MUTEX_PARTIAL_INITIALIZER PTHREAD_MUTEX_INITIALIZER
 
-#define k5_mutex_finish_init(M)        ((void)(M),0)
-#define k5_mutex_init(M)       pthread_mutex_init(M, 0)
-#define k5_mutex_destroy(M)    pthread_mutex_destroy(M)
-#define k5_mutex_lock(M)       pthread_mutex_lock(M)
-#define k5_mutex_unlock(M)     pthread_mutex_unlock(M)
-#define k5_assert_locked(M)    (0)
-#define k5_assert_unlocked(M)  (0)
+#ifndef ENABLE_THREADS
 
-#else /* DEBUG_THREADS */
+typedef k5_os_nothread_mutex k5_os_mutex;
+# define K5_OS_MUTEX_PARTIAL_INITIALIZER       \
+               K5_OS_NOTHREAD_MUTEX_PARTIAL_INITIALIZER
+# define k5_os_mutex_finish_init       k5_os_nothread_mutex_finish_init
+# define k5_os_mutex_init              k5_os_nothread_mutex_init
+# define k5_os_mutex_destroy           k5_os_nothread_mutex_destroy
+# define k5_os_mutex_lock              k5_os_nothread_mutex_lock
+# define k5_os_mutex_unlock            k5_os_nothread_mutex_unlock
+# define k5_os_mutex_assert_locked     k5_os_nothread_mutex_assert_locked
+# define k5_os_mutex_assert_unlocked   k5_os_nothread_mutex_assert_unlocked
 
-typedef struct {
-    k5_mutex_debug_info debug;
-    pthread_mutex_t lock;
-} k5_mutex_t;
-#define K5_MUTEX_PARTIAL_INITIALIZER   \
-               { K5_MUTEX_DEBUG_INITIALIZER, PTHREAD_MUTEX_INITIALIZER }
-#define k5_mutex_finish_init(M)        (k5_mutex_debug_finish_init(&(M)->debug))
-#define k5_mutex_init(M)       (k5_mutex_debug_init(&(M)->debug),            \
-                                assert(0==pthread_mutex_init(&(M)->lock,0)), \
-                                0)
-#define k5_mutex_destroy(M)    (k5_mutex_debug_init(&(M)->debug),            \
-                                assert(0==pthread_mutex_destroy(&(M)->lock)))
-#define k5_mutex_lock(M)       (k5_mutex_debug_check_init(&(M)->debug),    \
-                                assert(0==pthread_mutex_lock(&(M)->lock)), \
-                                assert((M)->debug.locked                   \
-                                       == K5_MUTEX_DEBUG_UNLOCKED),        \
-                                k5_mutex_debug_update_loc(&(M)->debug),    \
-                                (M)->debug.locked = K5_MUTEX_DEBUG_LOCKED, \
-                                0)
-#define k5_mutex_unlock(M)     (k5_mutex_debug_check_init(&(M)->debug),      \
-                                assert((M)->debug.locked                     \
-                                       == K5_MUTEX_DEBUG_LOCKED),            \
-                                k5_mutex_debug_update_loc(&(M)->debug),      \
-                                (M)->debug.locked = K5_MUTEX_DEBUG_UNLOCKED, \
-                                assert(0==pthread_mutex_unlock(&(M)->lock)), \
-                                0)
-#define k5_assert_locked(M)    (k5_debug_assert_locked(&(M)->debug))
-#define k5_assert_unlocked(M)  (k5_debug_assert_unlocked(&(M)->debug))
+# define k5_once_t                     k5_os_nothread_once_t
+# define K5_ONCE_INIT                  K5_OS_NOTHREAD_ONCE_INIT
+# define k5_once                       k5_os_nothread_once
 
-#if defined(__mips) && defined(__sgi) && (defined(_SYSTYPE_SVR4) || defined(__SYSTYPE_SVR4__))
-/* IRIX 6.5 stub pthread support in libc is really annoying.
-   The pthread_mutex_lock function returns ENOSYS for a program
-   not linked against -lpthread.  No link-time failure, no weak
-   symbols, etc.
+#elif HAVE_PTHREAD_H
 
-   The C library doesn't provide pthread_once; we can use weak
-   reference support for that.  */
-#if defined(__GNUC__) && __GNUC__ < 3
-# error "Please update to a newer gcc with weak symbol support, or switch to native cc; reconfigure and recompile."
-#endif
-#undef k5_mutex_lock
-#undef k5_mutex_unlock
-#define k5_mutex_lock(M)                       \
-       (k5_mutex_debug_lock(&(M)->debug),      \
-        (K5_PTHREADS_LOADED                    \
-         ? pthread_mutex_lock(&(M)->lock)      \
-         : 0))
-#define k5_mutex_unlock(M)                     \
-       (k5_mutex_debug_unlock(&(M)->debug),    \
-        (K5_PTHREADS_LOADED                    \
-         ? pthread_mutex_unlock(&(M)->lock)    \
-         : 0))
-#endif
+# include <pthread.h>
 
-#endif /* DEBUG_THREADS ? */
+/* Weak reference support, etc.
 
-/* Linux with weak reference support:
-   Stub mutex routines exist, but pthread_once does not.
+   Linux: Stub mutex routines exist, but pthread_once does not.
 
    Solaris: In libc there's a pthread_once that doesn't seem
    to do anything.  Bleah.  But pthread_mutexattr_setrobust_np
    is defined only in libpthread.
- */
 
+   IRIX 6.5 stub pthread support in libc is really annoying.  The
+   pthread_mutex_lock function returns ENOSYS for a program not linked
+   against -lpthread.  No link-time failure, no weak symbols, etc.
+   The C library doesn't provide pthread_once; we can use weak
+   reference support for that.
+
+   If weak references are not available, then for now, we assume that
+   the pthread support routines will always be available -- either the
+   real thing, or functional stubs that merely prohibit creating
+   threads.
+
+   If we find a platform with non-functional stubs and no weak
+   references, we may have to resort to some hack like dlsym on the
+   symbol tables of the current process.  */
 #ifdef HAVE_PRAGMA_WEAK_REF
 # pragma weak pthread_once
 # ifdef HAVE_PTHREAD_MUTEXATTR_SETROBUST_NP_IN_THREAD_LIB
@@ -305,80 +315,173 @@ typedef struct {
 # define K5_PTHREADS_LOADED    (1)
 #endif
 
-/* Would be nice to use a union, but we need to initialize both.  */
+#if defined(__mips) && defined(__sgi) && (defined(_SYSTYPE_SVR4) || defined(__SYSTYPE_SVR4__))
+/* IRIX 6.5 stub pthread support in libc is really annoying.  The
+   pthread_mutex_lock function returns ENOSYS for a program not linked
+   against -lpthread.  No link-time failure, no weak reference tests,
+   etc.
+
+   The C library doesn't provide pthread_once; we can use weak
+   reference support for that.  */
+# ifndef HAVE_PRAGMA_WEAK_REF
+#  if defined(__GNUC__) && __GNUC__ < 3
+#   error "Please update to a newer gcc with weak symbol support, or switch to native cc, reconfigure and recompile."
+#  else
+#   error "Weak reference support is required"
+#  endif
+# endif
+# define USE_PTHREAD_LOCK_ONLY_IF_LOADED
+#endif
+
 #ifdef HAVE_PRAGMA_WEAK_REF
-typedef struct { pthread_once_t o; int i; } k5_once_t;
-#define K5_ONCE_INIT   { PTHREAD_ONCE_INIT, 2 }
-#define k5_once(O,F)   (K5_PTHREADS_LOADED             \
-                        ? pthread_once(&(O)->o,F)      \
-                        : (O)->i == 2                  \
-                        ? ((O)->i = 3, (*F)(), 0)      \
-                        : 0)
+/* Can't rely on useful stubs -- see above regarding Solaris.  */
+typedef struct {
+    pthread_once_t o;
+    k5_os_nothread_once_t n;
+} k5_once_t;
+# define K5_ONCE_INIT  { PTHREAD_ONCE_INIT, K5_OS_NOTHREAD_ONCE_INIT }
+# define k5_once(O,F)  (K5_PTHREADS_LOADED                     \
+                        ? pthread_once(&(O)->o,F)              \
+                        : k5_os_nothread_once(&(O)->n,F))
 #else
 typedef pthread_once_t k5_once_t;
-#define K5_ONCE_INIT   PTHREAD_ONCE_INIT
-#define k5_once                pthread_once
+# define K5_ONCE_INIT  PTHREAD_ONCE_INIT
+# define k5_once       pthread_once
 #endif
 
-#elif defined(_WIN32)
-
-# error "Windows thread support not implemented yet"
-/* N.B.: No k5_once support for this platform.  */
+typedef struct {
+    pthread_mutex_t p;
+#ifdef USE_PTHREAD_LOCK_ONLY_IF_LOADED
+    k5_os_nothread_mutex n;
+#endif
+} k5_os_mutex;
+
+#define k5_pthread_assert_unlocked(M)  (0)
+#define k5_pthread_assert_locked(M)    (0)
+
+#ifdef USE_PTHREAD_LOCK_ONLY_IF_LOADED
+
+# define K5_OS_MUTEX_PARTIAL_INITIALIZER \
+       { PTHREAD_MUTEX_INITIALIZER, K5_OS_NOTHREAD_MUTEX_PARTIAL_INITIALIZER }
+
+# define k5_os_mutex_finish_init(M)            \
+       k5_os_nothread_mutex_finish_init(&(M)->n)
+# define k5_os_mutex_init(M)                   \
+       (k5_os_nothread_mutex_init(&(M)->n),    \
+        pthread_mutex_init(&(M)->p, 0))
+# define k5_os_mutex_destroy(M)                        \
+       (k5_os_nothread_mutex_destroy(&(M)->n), \
+        pthread_mutex_destroy(&(M)->p))
+
+# define k5_os_mutex_lock(M)                   \
+       (K5_PTHREADS_LOADED                     \
+        ? pthread_mutex_lock(&(M)->p)          \
+        : k5_os_nothread_lock(&(M)->n))
+# define k5_os_mutex_unlock(M)                 \
+       (K5_PTHREADS_LOADED                     \
+        ? pthread_mutex_unlock(&(M)->p)        \
+        : k5_os_nothread_unlock(&(M)->n))
+
+# define k5_os_mutex_assert_unlocked(M)                        \
+       (K5_PTHREADS_LOADED                             \
+        ? k5_pthread_assert_unlocked(&(M)->p)          \
+        : k5_os_nothread_assert_unlocked(&(M)->n))
+# define k5_os_mutex_assert_locked(M)                  \
+       (K5_PTHREADS_LOADED                             \
+        ? k5_pthread_assert_locked(&(M)->p)            \
+        : k5_os_nothread_assert_locked(&(M)->n))
 
 #else
 
-# error "Thread support enabled, but thread system unknown"
+# define K5_OS_MUTEX_PARTIAL_INITIALIZER \
+       { PTHREAD_MUTEX_INITIALIZER }
 
-#endif
+# define k5_os_mutex_finish_init(M)    (0)
+# define k5_os_mutex_init(M)           pthread_mutex_init(&(M)->p, 0)
+# define k5_os_mutex_destroy(M)                pthread_mutex_destroy(&(M)->p)
 
-#else /* ! ENABLE_THREADS */
+# define k5_os_mutex_lock(M)           pthread_mutex_lock(&(M)->p)
+# define k5_os_mutex_unlock(M)         pthread_mutex_unlock(&(M)->p)
 
-#ifdef DEBUG_THREADS
+# define k5_os_mutex_assert_unlocked(M)        k5_pthread_assert_unlocked(&(M)->p)
+# define k5_os_mutex_assert_locked(M)  k5_pthread_assert_locked(&(M)->p)
 
-#include <assert.h>
+#endif /* is pthreads always available? */
 
-/* Even if not using threads, use some mutex-like locks to see if
-   we're pairing up lock and unlock calls properly.  */
+#elif defined _WIN32
 
-#define k5_mutex_t             k5_mutex_debug_info
-#define K5_MUTEX_PARTIAL_INITIALIZER   K5_MUTEX_DEBUG_INITIALIZER
-#define k5_mutex_finish_init   k5_mutex_debug_finish_init
-#define k5_mutex_init          k5_mutex_debug_init
-#define k5_mutex_destroy       k5_mutex_debug_destroy
-#define k5_mutex_lock          k5_mutex_debug_lock
-#define k5_mutex_unlock                k5_mutex_debug_unlock
-#define k5_assert_locked       k5_debug_assert_locked
-#define k5_assert_unlocked     k5_debug_assert_unlocked
-
-#define k5_once_t      unsigned char
-#define K5_ONCE_INIT   2
-#define k5_once(O,F)                                   \
-       (assert(*(O) == 2 || *(O) == 3),                \
-        (*(O) == 3 ? 0 : ((F)(), *(O) = 3, 0)))
-
-#else /* ! DEBUG_THREADS */
+# error "Windows thread support not implemented yet"
 
-/* no-op versions */
+#else
 
-typedef char k5_mutex_t;
-#define K5_MUTEX_PARTIAL_INITIALIZER   0
-#define k5_mutex_finish_init(M)        (0)
-#define k5_mutex_init(M)       (*(M) = 0, *(M) = *(M))
-#define k5_mutex_destroy(M)    (0)
-#define k5_mutex_lock(M)       (0)
-#define k5_mutex_unlock(M)     (0)
-#define k5_assert_locked(M)    (0)
-#define k5_assert_unlocked(M)  (0)
+# error "Thread support enabled, but thread system unknown"
 
-#define k5_once_t      unsigned char
-#define K5_ONCE_INIT   2
-#define k5_once(F,O)   \
-       (*(O) == 3 ? 0 : ((F)(), *(O) = 3, 0))
+#endif
 
-#endif /* DEBUG_THREADS ? */
 
-#endif /* ENABLE_THREADS ? */
+\f
 
+typedef struct {
+    k5_debug_loc loc_last, loc_created;
+    k5_os_mutex os;
+} k5_mutex_t;
+#define K5_MUTEX_PARTIAL_INITIALIZER           \
+       { K5_DEBUG_LOC_INIT, K5_DEBUG_LOC_INIT, \
+         K5_OS_MUTEX_PARTIAL_INITIALIZER }
+static inline int k5_mutex_init_1(k5_mutex_t *m, k5_debug_loc l)
+{
+    int err = k5_os_mutex_init(&m->os);
+    if (err) return err;
+    m->loc_created = m->loc_last = l;
+    return 0;
+}
+#define k5_mutex_init(M)       k5_mutex_init_1((M), K5_DEBUG_LOC)
+static inline int k5_mutex_finish_init_1(k5_mutex_t *m, k5_debug_loc l)
+{
+    int err = k5_os_mutex_finish_init(&m->os);
+    if (err) return err;
+    m->loc_created = m->loc_last = l;
+    return 0;
+}
+#define k5_mutex_finish_init(M)        k5_mutex_finish_init_1((M), K5_DEBUG_LOC)
+#define k5_mutex_destroy(M)                    \
+       (k5_os_mutex_assert_unlocked(&(M)->os), \
+        (M)->loc_last = K5_DEBUG_LOC,          \
+        k5_os_mutex_destroy(&(M)->os))
+static inline int k5_mutex_lock_1(k5_mutex_t *m, k5_debug_loc l)
+{
+    int err = 0;
+    err = k5_os_mutex_lock(&m->os);
+    if (err)
+       return err;
+    m->loc_last = l;
+    return err;
+}
+#define k5_mutex_lock(M)       k5_mutex_lock_1(M, K5_DEBUG_LOC)
+static inline int k5_mutex_unlock_1(k5_mutex_t *m, k5_debug_loc l)
+{
+    int err = 0;
+    err = k5_os_mutex_unlock(&m->os);
+    if (err)
+       return err;
+    m->loc_last = l;
+    return err;
+}
+#define k5_mutex_unlock(M)     k5_mutex_unlock_1(M, K5_DEBUG_LOC)
+
+#define k5_mutex_assert_locked(M)      k5_os_mutex_assert_locked(&(M)->os)
+#define k5_mutex_assert_unlocked(M)    k5_os_mutex_assert_unlocked(&(M)->os)
+
+#define k5_assert_locked       k5_mutex_assert_locked
+#define k5_assert_unlocked     k5_mutex_assert_unlocked
+
+\f
+/* Thread-specific data; implemented in a support file, because we'll
+   need to keep track of some global data for cleanup purposes.  */
+typedef enum {
+    K5_KEY_COM_ERR,
+    K5_KEY_MAX
+} k5_key_t;
 /* rename shorthand symbols for export */
 #define k5_key_register        krb5int_key_register
 #define k5_getspecific krb5int_getspecific