#include "autoconf.h"
-/* Interface (tentative):
+\f/* Interface (tentative):
Mutex support:
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
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:
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
# 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