mit-krb5 thread support: thread-specific data

Ken Raeburn raeburn at MIT.EDU
Tue Apr 6 09:51:31 EDT 2004


Unlike mutexes, where we'll need to be able to create one per replay
cache or credentials cache object, the number of cases where
per-thread data is needed is limited, and can be enumerated in the
code.  The overhead of making a few tables large enough to include
support for GSSAPI thread-specific data even in an application that
doesn't use GSSAPI shouldn't be significant.

Here's the internal API I propose, though since I haven't started
implementing it yet (probably will today), it may need a bit of
polish:

  // 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);
  // 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, const 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.  Calling k5_key_register a second time on a given
key value, without an intervening k5_key_delete call, is an error.
Note that the C library routine free() has a compatible signature and
could be used directly as a destructor function.

On POSIX, we can use pthread_getspecific and friends to give us a
per-thread array of pointers indexed by key number.  This means the
Kerberos code all together will require only one key at the POSIX
level, which is helpful since POSIX lets a system limit the keys
available to a fairly small number.

These arrays will also be referenced from some global table that can
be walked when a library gets unloaded, to implement k5_key_delete.  I
believe pretty much all UNIX shared library systems these days support
some sort of initialization and finalization operations, though we may
need system-specific mechanisms for setting them up.  (If they don't
have the support, can they support initialization of C++ static
objects in dynamically loadable libraries?  If they support C++, we
may be able to use a C++ source file to build on that support, if no
better approach is available.)

On Windows, library support functions can be invoked on thread
termination and library unloading; we can use this to either walk
through a set of pointers for a thread's specific data, or for each
thread destroy the data it associates with a given key.  That assumes
we've got DLL support, at least for the krb5 thread support code, and
aren't linking everything together statically.  (Is that a reasonable
assumption?)

As with the mutex support, 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).

Comments?

Ken


More information about the krbdev mailing list