Thread safety in the MIT Kerberos libraries

The return value from krb5_cc_default_name is a handle on internal
storage from the krb5_context.  It is valid only until
krb5_cc_set_default_name or krb5_free_context is called.  If
krb5_cc_set_default_name may be called, the calling code must ensure
that the storage returned by krb5_cc_default_name is no longer in use
by that time.

Any use of krb5_context must be confined to one thread at a time by
the application code.

Uses of credentials caches, replay caches, and keytabs may happen in
multiple threads simultaneously as long as none of them destroys the
object while other threads may still be using it.  (Any internal data
modification in those objects will be protected by mutexes or other
means, within the krb5 library.)

The simple, exposed data structures in krb5.h like krb5_principal are
not protected; they should not be used in one thread while another
thread might be modifying them.  (TO DO: Build a list of which calls
keep references to supplied data or return references to
otherwise-referenced data, as opposed to everything making copies.)



[ This part is a little outdated already. ]

   // 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 zero/empty,
   //            finish does the actual work and runs at load time
   //   debug: partial initializer sets one magic value,
   //          finish verifies, sets a new magic value
   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 allocation, even if it means adding
   // a flag.
   int k5_mutex_destroy(k5_mutex_t *);
   //
   // Per library, one function to finish the static mutex
   // initialization.
   //
   // A second function 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.
   //
   // A third function for library termination calls mutex_destroy on
   // each mutex for the library.
   //
   // 
   int k5_mutex_lock(k5_mutex_t *);
   int k5_mutex_unlock(k5_mutex_t *);
   // Optional (always defined, but need not do anything):
   void k5_mutex_assert_locked(k5_mutex_t *);
   void k5_mutex_assert_unlocked(k5_mutex_t *);


   k5_key_t key;
   int k5_key_create(k5_key_t *, void (*destructor)(void *));
   void *k5_getspecific(k5_key_t);
   int k5_setspecific(k5_key_t, const void *);
   ... stuff to signal library termination ...

This is **NOT** an exported interface, and is subject to change.

On many platforms with weak reference support, we can declare certain
symbols to be weak, and test the addresses before calling them.  The
references generally will be non-null if the application pulls in the
pthread support.  Sometimes stubs are present in the C library for
some of these routines, and sometimes they're not functional; if so,
we need to figure out which ones, and check for the presence of some
*other* routines.

AIX 4.3.3 doesn't support weak references.  However, it looks like
calling dlsym(NULL) causes the pthread library to get loaded, so we're
going to just go ahead and link against it anyways.

On Tru64 we also link against the thread library always.


For now, the basic model is:

  If weak references supported, use them.
  Else, assume support is present; if that means explicitly pulling in
  the thread library, so be it.



The locking described above may not be sufficient, at least for good
performance.  At some point we may want to switch to read/write locks,
so multiple threads can grovel over a data structure at once as long
as they don't change it.


See also notes in src/include/k5-thread.h.
