summaryrefslogtreecommitdiffstats
path: root/src/include
diff options
context:
space:
mode:
authorKen Raeburn <raeburn@mit.edu>2004-04-24 21:09:44 +0000
committerKen Raeburn <raeburn@mit.edu>2004-04-24 21:09:44 +0000
commitfcf02bef88a17724aa230547459a9eaf1159d6c1 (patch)
tree7f9dd998b8e7baf61111f6bf0428da47de1291eb /src/include
parent3b4d753fc5169469da270c831654a8fae407cfdf (diff)
downloadkrb5-fcf02bef88a17724aa230547459a9eaf1159d6c1.tar.gz
krb5-fcf02bef88a17724aa230547459a9eaf1159d6c1.tar.xz
krb5-fcf02bef88a17724aa230547459a9eaf1159d6c1.zip
Added support for library initialization and finalization, and verification
that the initializer completed successfully. Delay initialization on POSIX until the first "verification" call. Currently specific to a few platforms, but should still build on others without thread support enabled. Use it to finish creating (if necessary) and destroy mutexes, and free some other storage "permanently" allocated by libraries (currently, libkrb5 cache/keytab type registries only). Change initialization of static mutexes to a two-step operation, a static "partial" initializer and a "finish_init" routine called from a thread-safe environment like library initialization is assumed to be. POSIX will use the former, Windows will use the latter, and the debug support will check that *both* have been used. Added init/fini functions to com_err, profile, krb5, and gssapi libraries. (The profile library one may need to be removed later.) The existing ones, not thread-safe, are still around. Use weak symbol support if available to figure out if the pthread library has been linked in, and avoid calling certain routines if the C library stubs are known not to exist or work. Stub declarations for thread-specific data. Minor bugfixes, whitespace changes. git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@16268 dc483132-0cff-0310-8789-dd5450dbe970
Diffstat (limited to 'src/include')
-rw-r--r--src/include/ChangeLog43
-rw-r--r--src/include/Makefile.in2
-rw-r--r--src/include/k5-platform.h251
-rw-r--r--src/include/k5-thread.h227
4 files changed, 493 insertions, 30 deletions
diff --git a/src/include/ChangeLog b/src/include/ChangeLog
index cb87feb7a..279990cb3 100644
--- a/src/include/ChangeLog
+++ b/src/include/ChangeLog
@@ -1,3 +1,46 @@
+2004-04-24 Ken Raeburn <raeburn@mit.edu>
+
+ * k5-platform.h (DELAY_INITIALIZER): New macro, defined always.
+ (CONSTRUCTOR_ATTR_WORKS, DESTRUCTOR_ATTR_WORKS): New macro,
+ defined for Linux and NetBSD, and Solaris with gcc.
+ (USE_LINKER_FINI_OPTION): New macro, defined on IRIX, and on
+ Solaris with native compiler.
+ (JOIN2, JOIN2_2, JOIN3, JOIN3_2, JOIN4, JOIN4_2): New utility
+ macros.
+ (k5_init_t): New typedef, for some platforms.
+ (MAKE_INIT_FUNCTION, MAKE_FINI_FUNCTION, CALL_INIT_FUNCTION,
+ INITIALIZER_RAN, PROGRAM_EXITING): New macros for library
+ initialization and finalization support.
+
+ * k5-thread.h: Use k5_mutex_init instead of K5_MUTEX_INITIALIZER
+ for multiple-inclusion protection.
+ (K5_MUTEX_DEBUG_INITIALIZER): Change initial initialized flag to
+ 2.
+ (k5_mutex_debug_finish_init): New macro, verifies value 2 and
+ changes it to 1.
+ (k5_mutex_debug_lock): Test various values of initialized and
+ locked flags separately, so assertion failure message is more
+ immediately informative.
+ (K5_MUTEX_PARTIAL_INITIALIZER): Rename from K5_MUTEX_INITIALIZER.
+ (k5_mutex_finish_init): New macro.
+ (pthread_once, pthread_mutexattr_setrobust_np): Consider
+ declaring weak based on availability.
+ (K5_PTHREADS_LOADED): New macro, tests to see if pthread
+ functions are available, if weak references are supported.
+ (k5_mutex_lock, k5_mutex_unlock): On IRIX, redefine to bypass
+ pthread code if pthread library isn't loaded.
+ (k5_once_t): New typedef.
+ (K5_ONCE_INIT, k5_once): New macros.
+
+ * Makefile.in (autoconf.stmp): Depend on aclocal.m4.
+
+ * k5-platform.h: Include limits.h before testing for SIZE_MAX.
+
+ * k5-thread.h (k5_key_t): New enumerator typedef.
+ (k5_key_register, k5_getspecific, k5_setspecific): New macros.
+ (krb5int_key_register, krb5int_getspecific, krb5int_setspecific,
+ krb5int_key_delete): Declare.
+
2004-04-16 Sam Hartman <hartmans@mit.edu>
* k5-int.h: krb5int_populate_gic_opt now takes credentials so it
diff --git a/src/include/Makefile.in b/src/include/Makefile.in
index a54505982..346b4a155 100644
--- a/src/include/Makefile.in
+++ b/src/include/Makefile.in
@@ -27,7 +27,7 @@ maybe-make-db.h-redirect:
krb5/autoconf.h: $(srcdir)/krb5/autoconf.h.in
(cd krb5; $(MAKE) autoconf.h)
$(srcdir)/krb5/autoconf.h.in: @MAINT@ $(srcdir)/krb5/autoconf.stmp
-$(srcdir)/krb5/autoconf.stmp: $(srcdir)/configure.in
+$(srcdir)/krb5/autoconf.stmp: $(srcdir)/configure.in $(SRCTOP)/aclocal.m4
cd $(srcdir) && ($(AUTOHEADER) --include=$(CONFIG_RELTOPDIR) $(AUTOHEADERFLAGS) || $(AUTOHEADER) --localdir=$(CONFIG_RELTOPDIR) $(AUTOHEADERFLAGS))
touch $(srcdir)/krb5/autoconf.stmp
diff --git a/src/include/k5-platform.h b/src/include/k5-platform.h
index c4cc7bb75..22b38f498 100644
--- a/src/include/k5-platform.h
+++ b/src/include/k5-platform.h
@@ -27,8 +27,11 @@
* Some platform-dependent definitions to sync up the C support level.
* Some to a C99-ish level, some related utility code.
*
- * Currently: make "static inline" work; 64-bit types and load/store
- * code; SIZE_MAX.
+ * Currently:
+ * + make "static inline" work
+ * + 64-bit types and load/store code
+ * + SIZE_MAX
+ * + shared library init/fini hooks
*/
#ifndef K5_PLATFORM_H
@@ -52,6 +55,249 @@
#include "autoconf.h"
+
+/* Initialization and finalization function support for libraries.
+
+ At top level, before the functions are defined or even declared:
+ MAKE_INIT_FUNCTION(init_fn);
+ MAKE_FINI_FUNCTION(fini_fn);
+ int init_fn(void) { ... }
+ void fini_fn(void) { if (INITIALIZER_RAN(init_fn)) ... }
+
+ In code, in the same file:
+ err = CALL_INIT_FUNCTION(init_fn);
+
+ To trigger or verify the initializer invocation from another file,
+ an additional function must be created.
+
+ The init_fn and fini_fn names should be chosen such that any
+ exported names staring with those names, and optionally followed by
+ additional characters, fits in with any namespace constraints on
+ the library in question.
+
+
+ Implementation outline:
+
+ Windows: MAKE_FINI_FUNCTION creates a symbol with a magic name that
+ is sought at library build time, and code is added to invoke the
+ function when the library is unloaded. MAKE_INIT_FUNCTION does
+ likewise, but the function is invoked when the library is loaded,
+ and an extra variable is declared to hold an error code and a "yes
+ the initializer ran" flag. CALL_INIT_FUNCTION blows up if the flag
+ isn't set, otherwise returns the error code.
+
+ UNIX: MAKE_INIT_FUNCTION creates and initializes a variable with a
+ name derived from the function name, containing a k5_once_t
+ (pthread_once_t or int), an error code, and a pointer to the
+ function. The function itself is declared static, but the
+ associated variable has external linkage. CALL_INIT_FUNCTION
+ ensures thath the function is called exactly once (pthread_once or
+ just check the flag) and returns the stored error code (or the
+ pthread_once error).
+
+ UNIX, with compiler support: MAKE_FINI_FUNCTION declares the
+ function as a destructor, and the run time linker support or
+ whatever will cause it to be invoked when the library is unloaded,
+ the program ends, etc.
+
+ UNIX, with linker support: MAKE_FINI_FUNCTION creates a symbol with
+ a magic name that is sought at library build time, and linker
+ options are used to mark it as a finalization function for the
+ library. The symbol must be exported.
+
+ UNIX, no library finalization support: The finalization function
+ never runs, and we leak memory. Tough.
+
+
+
+ For maximum flexibility in defining the macros, the function name
+ parameter should be a simple name, not even a macro defined as
+ another name. The function should have a unique name, and should
+ conform to whatever namespace is used by the library in question.
+
+ If the macro expansion needs the function to have been declared, it
+ must include a declaration. If it is not necessary for the symbol
+ name to be exported from the object file, the macro should declare
+ it as "static". Hence the signature must exactly match "void
+ foo(void)". (ANSI C allows a static declaration followed by a
+ non-static one; the result is internal linkage.) The macro
+ expansion has to come before the function, because gcc apparently
+ won't act on "__attribute__((constructor))" if it comes after the
+ function definition.
+
+ This is going to be compiler- and environment-specific, and may
+ require some support at library build time, and/or "asm"
+ statements.
+
+ It's okay for this code to require that the library be built
+ with the same compiler and compiler options throughout, but
+ we shouldn't require that the library and application use the
+ same compiler.
+
+ For static libraries, we don't really care about cleanup too much,
+ since it's all memory handling and mutex allocation which will all
+ be cleaned up when the program exits. Thus, it's okay if gcc-built
+ static libraries don't play nicely with cc-built executables when
+ it comes to static constructors, just as long as it doesn't cause
+ linking to fail.
+
+ For dynamic libraries on UNIX, we'll use pthread_once-type support
+ to do delayed initialization, so if finalization can't be made to
+ work, we'll only have memory leaks in a load/use/unload cycle. If
+ anyone (like, say, the OS vendor) complains about this, they can
+ tell us how to get a shared library finalization function invoked
+ automatically. */
+
+#if !defined(_WIN32xxx)
+# define DELAY_INITIALIZER
+#endif
+
+/* These should be turned into feature tests or otherwise driven from
+ the configure script. */
+#if defined(__linux__) || defined(__NetBSD__) \
+ || (defined(__sun__) && defined(__svr4__) && defined(__GNUC__))
+# define CONSTRUCTOR_ATTR_WORKS
+# define DESTRUCTOR_ATTR_WORKS
+#endif
+#if defined(__sgi) && defined(__mips) && defined(_SYSTYPE_SVR4) /* IRIX? */
+# define USE_LINKER_FINI_OPTION
+#endif
+#if !defined(__GNUC__) && defined(__sun) && defined(__SVR4) && defined(__SUNPRO_C) /* Solaris ld -z finiarray */
+# define USE_LINKER_FINI_OPTION
+#endif
+
+# define JOIN4_2(A,B,C,D) A ## B ## C ## D
+# define JOIN4(A,B,C,D) JOIN4_2(A,B,C,D)
+# define JOIN3_2(A,B,C) A ## B ## C
+# define JOIN3(A,B,C) JOIN3_2(A,B,C)
+# define JOIN2_2(A,B) A ## B
+# define JOIN2(A,B) JOIN2_2(A,B)
+
+/* XXX Should test USE_LINKER_INIT_OPTION early, and if it's set,
+ always provide a function by the expected name, even if we're
+ delaying initialization. */
+
+#if defined(DELAY_INITIALIZER)
+
+/* Run the initialization code during program execution, at the latest
+ possible moment. This means multiple threads may be active. */
+# include "k5-thread.h"
+typedef struct { k5_once_t once; int error, did_run; void (*fn)(void); } k5_init_t;
+# define MAKE_INIT_FUNCTION(NAME) \
+ static int NAME(void); \
+ /* forward declaration for use in initializer */ \
+ static void JOIN2(NAME, __aux) (void); \
+ static k5_init_t JOIN2(NAME, __once) = \
+ { K5_ONCE_INIT, 0, 0, JOIN2(NAME, __aux) }; \
+ static void JOIN2(NAME, __aux) (void) \
+ { \
+ JOIN2(NAME, __once).did_run = 1; \
+ JOIN2(NAME, __once).error = NAME(); \
+ } \
+ /* so ';' following macro use won't get error */ \
+ static int NAME(void)
+# define CALL_INIT_FUNCTION(NAME) \
+ k5_call_init_function(& JOIN2(NAME, __once))
+static inline int k5_call_init_function(k5_init_t *i)
+{
+ int err;
+ err = k5_once(&i->once, i->fn);
+ if (err)
+ return err;
+ assert (i->did_run != 0);
+ return i->error;
+}
+/* This should be called in finalization only, so we shouldn't have
+ multiple active threads mucking around in our library at this
+ point. So ignore the once_t object and just look at the flag.
+
+ XXX Could we have problems with memory coherence between
+ processors if we don't invoke mutex/once routines? */
+# define INITIALIZER_RAN(NAME) \
+ (JOIN2(NAME, __once).did_run && JOIN2(NAME, __once).error == 0)
+
+# define PROGRAM_EXITING() (0)
+
+#elif defined(__GNUC__) && !defined(_WIN32) && defined(CONSTRUCTOR_ATTR_WORKS)
+
+/* Run initializer at load time, via GCC/C++ hook magic. */
+typedef struct { int error; unsigned char did_run; } k5_init_t;
+# define MAKE_INIT_FUNCTION(NAME) \
+ static k5_init_t JOIN2(NAME, __ran) \
+ = { 0, 2 }; \
+ static void JOIN2(NAME, __aux)(void) \
+ __attribute__((constructor)); \
+ static int NAME(void); \
+ static void JOIN2(NAME, __aux)(void) \
+ { \
+ JOIN2(NAME, __ran).error = NAME(); \
+ JOIN2(NAME, __ran).did_run = 3; \
+ } \
+ static int NAME(void)
+# define CALL_INIT_FUNCTION(NAME) \
+ (JOIN2(NAME, __ran).did_run == 3 \
+ ? JOIN2(NAME, __ran).error \
+ : (abort(),0))
+# define INITIALIZER_RAN(NAME) (JOIN2(NAME, __ran).error == 0)
+
+#elif defined(LINKER_HAS_INIT_OPTION)
+
+/* Run initializer at load time, via linker magic. */
+typedef struct { int error; unsigned char did_run; } k5_init_t;
+# define MAKE_INIT_FUNCTION(NAME) \
+ static k5_init_t JOIN2(NAME, __ran) \
+ = { 0, 2 }; \
+ static int NAME(void); \
+ void JOIN2(NAME, __aux) \
+ { \
+ JOIN2(NAME, __ran).error = NAME(); \
+ JOIN2(NAME, __ran).did_run = 3; \
+ } \
+ static int NAME(void)
+# define CALL_INIT_FUNCTION(NAME) \
+ (JOIN2(NAME, __ran).did_run == 3 \
+ ? JOIN2(NAME, __ran).error \
+ : (abort(),0))
+# define INITIALIZER_RAN(NAME) \
+ (JOIN2(NAME, __ran).error == 0)
+
+# define PROGRAM_EXITING() (0)
+
+#else
+
+# error "Don't know how to do load-time initializers for this configuration."
+
+# define PROGRAM_EXITING() (0)
+
+#endif
+
+
+
+#ifdef USE_LINKER_FINI_OPTION
+/* If we're told the linker option will be used, it doesn't really
+ matter what compiler we're using. Do it the same way
+ regardless. */
+
+# define MAKE_FINI_FUNCTION(NAME) \
+ void NAME(void)
+
+#elif defined(__GNUC__) && !defined(_WIN32) && defined(DESTRUCTOR_ATTR_WORKS)
+/* If we're using gcc, if the C++ support works, the compiler should
+ build executables and shared libraries that support the use of
+ static constructors and destructors. The C compiler supports a
+ function attribute that makes use of the same facility as C++.
+
+ XXX How do we know if the C++ support actually works? */
+# define MAKE_FINI_FUNCTION(NAME) \
+ static void NAME(void) __attribute__((destructor))
+
+#else
+
+# error "Don't know how to do unload-time finalization for this configuration."
+
+#endif
+
+
/* 64-bit support: krb5_ui_8 and krb5_int64.
This should move to krb5.h eventually, but without the namespace
@@ -73,6 +319,7 @@
# define UINT64_TYPE unsigned long long
#endif
+#include <limits.h>
#ifndef SIZE_MAX
# define SIZE_MAX ((size_t)((size_t)0 - 1))
#endif
diff --git a/src/include/k5-thread.h b/src/include/k5-thread.h
index 27d705f79..d596e4827 100644
--- a/src/include/k5-thread.h
+++ b/src/include/k5-thread.h
@@ -27,25 +27,109 @@
* Preliminary thread support.
*/
-#ifndef K5_MUTEX_INITIALIZER /* handle multiple inclusion */
+#ifndef k5_mutex_init /* handle multiple inclusion */
#include "autoconf.h"
/* Interface (tentative):
- k5_mutex_t foo_mutex = K5_MUTEX_INITIALIZER;
+ Mutex support:
+
+ // Between these two, we should be able to do pure compile-time
+ // and pure run-time initialization.
+ // POSIX: partial initializer is PTHREAD_MUTEX_INITIALIZER,
+ // finish does nothing
+ // Windows: partial initializer is an invalid handle,
+ // finish does the real initialization work
+ // debug: partial initializer sets one magic value,
+ // finish verifies and sets a new magic value for
+ // lock/unlock to check
+ k5_mutex_t foo_mutex = K5_MUTEX_PARTIAL_INITIALIZER;
+ int k5_mutex_finish_init(k5_mutex_t *);
+ // for dynamic allocation
int k5_mutex_init(k5_mutex_t *);
+ // Must work for both kinds of alloc, even if it means adding flags.
int k5_mutex_destroy(k5_mutex_t *);
+
+ // As before.
int k5_mutex_lock(k5_mutex_t *);
int k5_mutex_unlock(k5_mutex_t *);
- k5_key_t key;
- int k5_key_create(k5_key_t *, void (*destructor)(void *));
+
+ 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
+ function (see below). On Windows, it would be called at library
+ load time. These functions, or functions they calls, should be the
+ only places that k5_mutex_finish_init gets called.
+
+ A second function or macro called at various possible "first" entry
+ points which either calls pthread_once on the first function
+ (POSIX), or checks some flag set by the first function (Windows,
+ debug support), and possibly returns an error. (In the
+ non-threaded case, a simple flag can be used to avoid multiple
+ invocations, and the mutexes don't need run-time initialization
+ anyways.)
+
+ A third function for library termination calls mutex_destroy on
+ each mutex for the library. This function would be called
+ automatically at library unload time. If it turns out to be needed
+ at exit time for libraries that don't get unloaded, perhaps we
+ 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:
+
+ k5_once_t foo_once = K5_ONCE_INIT;
+ k5_once(k5_once_t *, void (*)(void));
+
+ For POSIX: Map onto pthread_once facility.
+ For non-threaded case: A simple flag.
+ For Windows: Not needed; library init code takes care of it.
+
+
+ Thread-specific data:
+
+ // TSD keys are limited in number in gssapi/krb5/com_err; enumerate
+ // them all. This allows support code init to allocate the
+ // necessary storage for pointers all at once, and avoids any
+ // possible error in key creation.
+ enum { ... } k5_key_t;
+ // Register destructor function. Called in library init code.
+ int k5_key_register(k5_key_t, void (*destructor)(void *));
+ // Returns NULL or data.
void *k5_getspecific(k5_key_t);
- int k5_setspecific(k5_key_t, const void *);
- ... stuff to signal library termination ...
+ // Returns error if key out of bounds, or the pointer table can't
+ // be allocated. A call to k5_key_register must have happened first.
+ // This may trigger the calling of pthread_setspecific on POSIX.
+ int k5_setspecific(k5_key_t, void *);
+ // Called in library termination code.
+ // Trashes data in all threads, calling the registered destructor
+ // (but calling it from the current thread).
+ int k5_key_delete(k5_key_t);
+
+ For the non-threaded version, the support code will have a static
+ array indexed by k5_key_t values, and get/setspecific simply access
+ the array elements.
+
+ The TSD destructor table is global state, protected by a mutex if
+ threads are enabled.
+
+ Debug support: Not much. Might check if k5_key_register has been
+ called and abort if not.
+
+
+ Any actual external symbols will use the krb5int_ prefix. The k5_
+ names will be simple macros or inline functions to rename the
+ external symbols, or slightly more complex ones to expand the
+ implementation inline (e.g., map to POSIX versions and/or debug
+ code using __FILE__ and the like).
- More to be added, probably. */
+
+ More to be added, perhaps. */
#ifndef HAVE_PTHREAD_H
# undef ENABLE_THREADS
@@ -64,9 +148,11 @@ typedef struct {
short lineno;
const char *filename;
} k5_mutex_debug_info;
-#define K5_MUTEX_DEBUG_INITIALIZER { 1, K5_MUTEX_DEBUG_UNLOCKED, 0, 0 }
+#define K5_MUTEX_DEBUG_INITIALIZER { 2, K5_MUTEX_DEBUG_UNLOCKED, 0, 0 }
#define K5_MUTEX_DEBUG_LOCKED 4
#define K5_MUTEX_DEBUG_UNLOCKED 3
+#define k5_mutex_debug_finish_init(M) \
+ (assert((M)->initialized == 2), (M)->initialized = 1, 0)
#define k5_mutex_debug_init(M) \
((M)->initialized = 1, \
(M)->locked = K5_MUTEX_DEBUG_UNLOCKED, \
@@ -76,8 +162,12 @@ typedef struct {
&& (M)->locked == K5_MUTEX_DEBUG_UNLOCKED), \
(M)->initialized = 0)
#define k5_mutex_debug_lock(M) \
- (assert((M)->initialized == 1 \
- && (M)->locked == K5_MUTEX_DEBUG_UNLOCKED), \
+ (assert((M)->initialized != 2), \
+ assert((M)->initialized != 0), \
+ assert((M)->initialized == 1), \
+ assert((M)->locked != 0), \
+ assert((M)->locked != K5_MUTEX_DEBUG_LOCKED), \
+ assert((M)->locked == K5_MUTEX_DEBUG_UNLOCKED), \
(M)->locked = K5_MUTEX_DEBUG_LOCKED, \
(M)->lineno = __LINE__, (M)->filename = __FILE__, 0)
#define k5_mutex_debug_unlock(M) \
@@ -86,6 +176,13 @@ typedef struct {
(M)->locked = K5_MUTEX_DEBUG_UNLOCKED, \
(M)->lineno = __LINE__, (M)->filename = __FILE__, 0)
+
+typedef enum {
+ K5_KEY_COM_ERR,
+ K5_KEY_MAX
+} k5_key_t;
+
+
#ifdef ENABLE_THREADS
#include <pthread.h>
@@ -98,8 +195,9 @@ typedef struct {
#ifndef DEBUG_THREADS
typedef pthread_mutex_t k5_mutex_t;
-#define K5_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
+#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)
@@ -111,12 +209,14 @@ typedef struct {
k5_mutex_debug_info debug;
pthread_mutex_t lock;
} k5_mutex_t;
-#define K5_MUTEX_INITIALIZER { K5_MUTEX_DEBUG_INITIALIZER, PTHREAD_MUTEX_INITIALIZER }
+#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))
+ assert(0==pthread_mutex_destroy(&(M)->lock)))
#define k5_mutex_lock(M) (k5_mutex_debug_lock(&(M)->debug), \
assert(0==pthread_mutex_lock(&(M)->lock)), \
0)
@@ -124,19 +224,69 @@ typedef struct {
assert(0==pthread_mutex_unlock(&(M)->lock)), \
0)
+#if defined(__mips) && defined(__sgi) && 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.
+
+ The C library doesn't provide pthread_once; we can use weak
+ reference support for that. */
+#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
+
#endif /* DEBUG_THREADS ? */
-#if 0
-/* *** This will need to change.
- We'd prefer to use only one POSIX data key.
-
- And we need to do some additional bookkeeping for dealing with
- unloading libraries (free storage, destroy the key), such that we
- can't just map the functions to POSIX in the long term. */
-typedef pthread_key_t k5_key_t;
-#define k5_key_create(K,D) pthread_key_create(K,D)
-#define k5_getspecific(K) pthread_getspecific(K)
-#define k5_setspecific(K,P) pthread_setspecific(K,P)
+/* Linux with weak reference support:
+ 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.
+ */
+
+#ifdef HAVE_PRAGMA_WEAK_REF
+# pragma weak pthread_once
+# ifdef HAVE_PTHREAD_MUTEXATTR_SETROBUST_NP_IN_THREAD_LIB
+# pragma weak pthread_mutexattr_setrobust_np
+# endif
+# if !defined HAVE_PTHREAD_ONCE
+# define K5_PTHREADS_LOADED (&pthread_once != 0)
+# elif !defined HAVE_PTHREAD_MUTEXATTR_SETROBUST_NP \
+ && defined HAVE_PTHREAD_MUTEXATTR_SETROBUST_NP_IN_THREAD_LIB
+# define K5_PTHREADS_LOADED (&pthread_mutexattr_setrobust_np != 0)
+# else
+# define K5_PTHREADS_LOADED (1)
+# endif
+#else
+/* no pragma weak support */
+# define K5_PTHREADS_LOADED (1)
+#endif
+
+/* Would be nice to use a union, but we need to initialize both. */
+#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)
+#else
+typedef pthread_once_t k5_once_t;
+#define K5_ONCE_INIT PTHREAD_ONCE_INIT
+#define k5_once pthread_once
#endif
#else /* ! ENABLE_THREADS */
@@ -149,25 +299,48 @@ typedef pthread_key_t k5_key_t;
we're pairing up lock and unlock calls properly. */
#define k5_mutex_t k5_mutex_debug_info
-#define K5_MUTEX_INITIALIZER K5_MUTEX_DEBUG_INITIALIZER
+#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_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 */
/* no-op versions */
typedef char k5_mutex_t;
-#define K5_MUTEX_INITIALIZER 0
+#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_once_t unsigned char
+#define K5_ONCE_INIT 2
+#define k5_once(F,O) \
+ (*(O) == 3 ? 0 : ((F)(), *(O) = 3, 0))
+
#endif /* DEBUG_THREADS ? */
#endif /* ENABLE_THREADS ? */
-#endif /* K5_MUTEX_INITIALIZER for multiple inclusion */
+/* rename shorthand symbols for export */
+#define k5_key_register krb5int_key_register
+#define k5_getspecific krb5int_getspecific
+#define k5_setspecific krb5int_setspecific
+#define k5_key_delete krb5int_key_delete
+extern int k5_key_register(k5_key_t, void (*)(void *));
+extern void *k5_getspecific(k5_key_t);
+extern int k5_setspecific(k5_key_t, void *);
+extern int k5_key_delete(k5_key_t);
+
+#endif /* multiple inclusion? */