Path: blob/main/crypto/krb5/src/include/k5-thread.h
34879 views
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */1/* include/k5-thread.h - Preliminary portable thread support */2/*3* Copyright 2004,2005,2006,2007,2008 by the Massachusetts Institute of Technology.4* All Rights Reserved.5*6* Export of this software from the United States of America may7* require a specific license from the United States Government.8* It is the responsibility of any person or organization contemplating9* export to obtain such a license before exporting.10*11* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and12* distribute this software and its documentation for any purpose and13* without fee is hereby granted, provided that the above copyright14* notice appear in all copies and that both that copyright notice and15* this permission notice appear in supporting documentation, and that16* the name of M.I.T. not be used in advertising or publicity pertaining17* to distribution of the software without specific, written prior18* permission. Furthermore if you modify this software you must label19* your software as modified software and not distribute it in such a20* fashion that it might be confused with the original M.I.T. software.21* M.I.T. makes no representations about the suitability of22* this software for any purpose. It is provided "as is" without express23* or implied warranty.24*/2526#ifndef K5_THREAD_H27#define K5_THREAD_H2829#include "autoconf.h"30#ifndef KRB5_CALLCONV31# define KRB5_CALLCONV32#endif33#ifndef KRB5_CALLCONV_C34# define KRB5_CALLCONV_C35#endif3637/* Interface (tentative):3839Mutex support:4041// Between these two, we should be able to do pure compile-time42// and pure run-time initialization.43// POSIX: partial initializer is PTHREAD_MUTEX_INITIALIZER,44// finish does nothing45// Windows: partial initializer is an invalid handle,46// finish does the real initialization work47k5_mutex_t foo_mutex = K5_MUTEX_PARTIAL_INITIALIZER;48int k5_mutex_finish_init(k5_mutex_t *);49// for dynamic allocation50int k5_mutex_init(k5_mutex_t *);51// Must work for both kinds of alloc, even if it means adding flags.52int k5_mutex_destroy(k5_mutex_t *);5354// As before.55int k5_mutex_lock(k5_mutex_t *);56int k5_mutex_unlock(k5_mutex_t *);5758In each library, one new function to finish the static mutex init,59and any other library-wide initialization that might be desired.60On POSIX, this function would be called via the second support61function (see below). On Windows, it would be called at library62load time. These functions, or functions they calls, should be the63only places that k5_mutex_finish_init gets called.6465A second function or macro called at various possible "first" entry66points which either calls pthread_once on the first function67(POSIX), or checks some flag set by the first function (Windows),68and possibly returns an error. (In the non-threaded case, a simple69flag can be used to avoid multiple invocations, and the mutexes70don't need run-time initialization anyways.)7172A third function for library termination calls mutex_destroy on73each mutex for the library. This function would be called74automatically at library unload time. If it turns out to be needed75at exit time for libraries that don't get unloaded, perhaps we76should also use atexit(). Any static mutexes should be cleaned up77with k5_mutex_destroy here.7879How does that second support function invoke the first support80function only once? Through something modelled on pthread_once81that I haven't written up yet. Probably:8283k5_once_t foo_once = K5_ONCE_INIT;84k5_once(k5_once_t *, void (*)(void));8586For POSIX: Map onto pthread_once facility.87For non-threaded case: A simple flag.88For Windows: Not needed; library init code takes care of it.8990XXX: A general k5_once mechanism isn't possible for Windows,91without faking it through named mutexes or mutexes initialized at92startup. I was only using it in one place outside these headers,93so I'm dropping the general scheme. Eventually the existing uses94in k5-thread.h and k5-platform.h will be converted to pthread_once95or static variables.969798Thread-specific data:99100// TSD keys are limited in number in gssapi/krb5/com_err; enumerate101// them all. This allows support code init to allocate the102// necessary storage for pointers all at once, and avoids any103// possible error in key creation.104enum { ... } k5_key_t;105// Register destructor function. Called in library init code.106int k5_key_register(k5_key_t, void (*destructor)(void *));107// Returns NULL or data.108void *k5_getspecific(k5_key_t);109// Returns error if key out of bounds, or the pointer table can't110// be allocated. A call to k5_key_register must have happened first.111// This may trigger the calling of pthread_setspecific on POSIX.112int k5_setspecific(k5_key_t, void *);113// Called in library termination code.114// Trashes data in all threads, calling the registered destructor115// (but calling it from the current thread).116int k5_key_delete(k5_key_t);117118For the non-threaded version, the support code will have a static119array indexed by k5_key_t values, and get/setspecific simply access120the array elements.121122The TSD destructor table is global state, protected by a mutex if123threads are enabled.124125126Any actual external symbols will use the krb5int_ prefix. The k5_127names will be simple macros or inline functions to rename the128external symbols, or slightly more complex ones to expand the129implementation inline (e.g., map to POSIX versions and/or debug130code using __FILE__ and the like).131132133More to be added, perhaps. */134135#include <assert.h>136#ifndef NDEBUG137#include <stdio.h>138#include <string.h>139#endif140141/* The mutex structure we use, k5_mutex_t, is defined to some142OS-specific bits. The use of multiple layers of typedefs are an143artifact resulting from debugging code we once used, implemented as144wrappers around the OS mutex scheme.145146The OS specific bits, in k5_os_mutex, break down into three primary147implementations, POSIX threads, Windows threads, and no thread148support. However, the POSIX thread version is further subdivided:149In one case, we can determine at run time whether the thread150library is linked into the application, and use it only if it is151present; in the other case, we cannot, and the thread library must152be linked in always, but can be used unconditionally. In the153former case, the k5_os_mutex structure needs to hold both the POSIX154and the non-threaded versions.155156The various k5_os_mutex_* operations are the OS-specific versions,157applied to the OS-specific data, and k5_mutex_* uses k5_os_mutex_*158to do the OS-specific parts of the work. */159160/* Define the OS mutex bit. */161162typedef char k5_os_nothread_mutex;163# define K5_OS_NOTHREAD_MUTEX_PARTIAL_INITIALIZER 0164/* Empty inline functions avoid the "statement with no effect"165warnings, and do better type-checking than functions that don't use166their arguments. */167static inline int k5_os_nothread_mutex_finish_init(k5_os_nothread_mutex *m) {168return 0;169}170static inline int k5_os_nothread_mutex_init(k5_os_nothread_mutex *m) {171return 0;172}173static inline int k5_os_nothread_mutex_destroy(k5_os_nothread_mutex *m) {174return 0;175}176static inline int k5_os_nothread_mutex_lock(k5_os_nothread_mutex *m) {177return 0;178}179static inline int k5_os_nothread_mutex_unlock(k5_os_nothread_mutex *m) {180return 0;181}182183/* Values:1842 - function has not been run1853 - function has been run1864 - function is being run -- deadlock detected */187typedef unsigned char k5_os_nothread_once_t;188# define K5_OS_NOTHREAD_ONCE_INIT 2189# define k5_os_nothread_once(O,F) \190(*(O) == 3 ? 0 \191: *(O) == 2 ? (*(O) = 4, (F)(), *(O) = 3, 0) \192: (assert(*(O) != 4), assert(*(O) == 2 || *(O) == 3), 0))193194195196#ifndef ENABLE_THREADS197198typedef k5_os_nothread_mutex k5_os_mutex;199# define K5_OS_MUTEX_PARTIAL_INITIALIZER \200K5_OS_NOTHREAD_MUTEX_PARTIAL_INITIALIZER201# define k5_os_mutex_finish_init k5_os_nothread_mutex_finish_init202# define k5_os_mutex_init k5_os_nothread_mutex_init203# define k5_os_mutex_destroy k5_os_nothread_mutex_destroy204# define k5_os_mutex_lock k5_os_nothread_mutex_lock205# define k5_os_mutex_unlock k5_os_nothread_mutex_unlock206207# define k5_once_t k5_os_nothread_once_t208# define K5_ONCE_INIT K5_OS_NOTHREAD_ONCE_INIT209# define k5_once k5_os_nothread_once210211#elif HAVE_PTHREAD212213# include <pthread.h>214215/* Weak reference support, etc.216217Linux: Stub mutex routines exist, but pthread_once does not.218219Solaris <10: In libc there's a pthread_once that doesn't seem to do220anything. Bleah. But pthread_mutexattr_setrobust_np is defined221only in libpthread. However, some version of GNU libc (Red Hat's222Fedora Core 5, reportedly) seems to have that function, but no223declaration, so we'd have to declare it in order to test for its224address. We now have tests to see if pthread_once actually works,225so stick with that for now.226227Solaris 10: The real thread support now lives in libc, and228libpthread is just a filter object. So we might as well use the229real functions unconditionally. Since we haven't got a test for230this property yet, we use NO_WEAK_PTHREADS defined in aclocal.m4231depending on the OS type.232233IRIX 6.5 stub pthread support in libc is really annoying. The234pthread_mutex_lock function returns ENOSYS for a program not linked235against -lpthread. No link-time failure, no weak symbols, etc.236The C library doesn't provide pthread_once; we can use weak237reference support for that.238239If weak references are not available, then for now, we assume that240the pthread support routines will always be available -- either the241real thing, or functional stubs that merely prohibit creating242threads.243244If we find a platform with non-functional stubs and no weak245references, we may have to resort to some hack like dlsym on the246symbol tables of the current process. */247248#if defined(HAVE_PRAGMA_WEAK_REF) && !defined(NO_WEAK_PTHREADS)249# define USE_CONDITIONAL_PTHREADS250#endif251252#ifdef USE_CONDITIONAL_PTHREADS253254/* Can't rely on useful stubs -- see above regarding Solaris. */255typedef struct {256pthread_once_t o;257k5_os_nothread_once_t n;258} k5_once_t;259# define K5_ONCE_INIT { PTHREAD_ONCE_INIT, K5_OS_NOTHREAD_ONCE_INIT }260261int k5_once(k5_once_t *once, void (*fn)(void));262#else263264/* no pragma weak support */265266typedef pthread_once_t k5_once_t;267# define K5_ONCE_INIT PTHREAD_ONCE_INIT268# define k5_once pthread_once269270#endif271272#if defined(__mips) && defined(__sgi) && (defined(_SYSTYPE_SVR4) || defined(__SYSTYPE_SVR4__))273# ifndef HAVE_PRAGMA_WEAK_REF274# if defined(__GNUC__) && __GNUC__ < 3275# error "Please update to a newer gcc with weak symbol support, or switch to native cc, reconfigure and recompile."276# else277# error "Weak reference support is required"278# endif279# endif280#endif281282typedef pthread_mutex_t k5_os_mutex;283# define K5_OS_MUTEX_PARTIAL_INITIALIZER \284PTHREAD_MUTEX_INITIALIZER285286#ifdef USE_CONDITIONAL_PTHREADS287288# define k5_os_mutex_finish_init(M) (0)289int k5_os_mutex_init(k5_os_mutex *m);290int k5_os_mutex_destroy(k5_os_mutex *m);291int k5_os_mutex_lock(k5_os_mutex *m);292int k5_os_mutex_unlock(k5_os_mutex *m);293294#else295296static inline int k5_os_mutex_finish_init(k5_os_mutex *m) { return 0; }297# define k5_os_mutex_init(M) pthread_mutex_init((M), 0)298# define k5_os_mutex_destroy(M) pthread_mutex_destroy((M))299# define k5_os_mutex_lock(M) pthread_mutex_lock(M)300# define k5_os_mutex_unlock(M) pthread_mutex_unlock(M)301302#endif /* is pthreads always available? */303304#elif defined _WIN32305306# define k5_once_t k5_os_nothread_once_t307308typedef struct {309HANDLE h;310int is_locked;311} k5_os_mutex;312313# define K5_OS_MUTEX_PARTIAL_INITIALIZER { INVALID_HANDLE_VALUE, 0 }314315# define k5_os_mutex_finish_init(M) \316(assert((M)->h == INVALID_HANDLE_VALUE), \317((M)->h = CreateMutex(NULL, FALSE, NULL)) ? 0 : GetLastError())318# define k5_os_mutex_init(M) \319((M)->is_locked = 0, \320((M)->h = CreateMutex(NULL, FALSE, NULL)) ? 0 : GetLastError())321# define k5_os_mutex_destroy(M) \322(CloseHandle((M)->h) ? ((M)->h = 0, 0) : GetLastError())323# define k5_os_mutex_lock k5_win_mutex_lock324325static inline int k5_win_mutex_lock(k5_os_mutex *m)326{327DWORD res;328res = WaitForSingleObject(m->h, INFINITE);329if (res == WAIT_FAILED)330return GetLastError();331/* Eventually these should be turned into some reasonable error332code. */333assert(res != WAIT_TIMEOUT);334assert(res != WAIT_ABANDONED);335assert(res == WAIT_OBJECT_0);336/* Avoid locking twice. */337assert(m->is_locked == 0);338m->is_locked = 1;339return 0;340}341342# define k5_os_mutex_unlock(M) \343(assert((M)->is_locked == 1), \344(M)->is_locked = 0, \345ReleaseMutex((M)->h) ? 0 : GetLastError())346347#else348349# error "Thread support enabled, but thread system unknown"350351#endif352353typedef k5_os_mutex k5_mutex_t;354#define K5_MUTEX_PARTIAL_INITIALIZER K5_OS_MUTEX_PARTIAL_INITIALIZER355static inline int k5_mutex_init(k5_mutex_t *m)356{357return k5_os_mutex_init(m);358}359static inline int k5_mutex_finish_init(k5_mutex_t *m)360{361return k5_os_mutex_finish_init(m);362}363#define k5_mutex_destroy(M) \364(k5_os_mutex_destroy(M))365366static inline void k5_mutex_lock(k5_mutex_t *m)367{368int r = k5_os_mutex_lock(m);369#ifndef NDEBUG370if (r != 0) {371fprintf(stderr, "k5_mutex_lock: Received error %d (%s)\n",372r, strerror(r));373}374#endif375assert(r == 0);376}377378static inline void k5_mutex_unlock(k5_mutex_t *m)379{380int r = k5_os_mutex_unlock(m);381#ifndef NDEBUG382if (r != 0) {383fprintf(stderr, "k5_mutex_unlock: Received error %d (%s)\n",384r, strerror(r));385}386#endif387assert(r == 0);388}389390#define k5_mutex_assert_locked(M) ((void)(M))391#define k5_mutex_assert_unlocked(M) ((void)(M))392#define k5_assert_locked k5_mutex_assert_locked393#define k5_assert_unlocked k5_mutex_assert_unlocked394395/* Thread-specific data; implemented in a support file, because we'll396need to keep track of some global data for cleanup purposes.397398Note that the callback function type is such that the C library399routine free() is a valid callback. */400typedef enum {401K5_KEY_COM_ERR,402K5_KEY_GSS_KRB5_SET_CCACHE_OLD_NAME,403K5_KEY_GSS_KRB5_CCACHE_NAME,404K5_KEY_GSS_KRB5_ERROR_MESSAGE,405K5_KEY_GSS_SPNEGO_STATUS,406#if defined(__MACH__) && defined(__APPLE__)407K5_KEY_IPC_CONNECTION_INFO,408#endif409K5_KEY_MAX410} k5_key_t;411/* rename shorthand symbols for export */412#define k5_key_register krb5int_key_register413#define k5_getspecific krb5int_getspecific414#define k5_setspecific krb5int_setspecific415#define k5_key_delete krb5int_key_delete416extern int k5_key_register(k5_key_t, void (*)(void *));417extern void *k5_getspecific(k5_key_t);418extern int k5_setspecific(k5_key_t, void *);419extern int k5_key_delete(k5_key_t);420421extern int KRB5_CALLCONV krb5int_mutex_alloc (k5_mutex_t **);422extern void KRB5_CALLCONV krb5int_mutex_free (k5_mutex_t *);423extern void KRB5_CALLCONV krb5int_mutex_lock (k5_mutex_t *);424extern void KRB5_CALLCONV krb5int_mutex_unlock (k5_mutex_t *);425426/* In time, many of the definitions above should move into the support427library, and this file should be greatly simplified. For type428definitions, that'll take some work, since other data structures429incorporate mutexes directly, and our mutex type is dependent on430configuration options and system attributes. For most functions,431though, it should be relatively easy.432433For now, plugins should use the exported functions, and not the434above macros, and use krb5int_mutex_alloc for allocations. */435#if defined(PLUGIN) || (defined(CONFIG_SMALL) && !defined(THREAD_SUPPORT_IMPL))436#undef k5_mutex_lock437#define k5_mutex_lock krb5int_mutex_lock438#undef k5_mutex_unlock439#define k5_mutex_unlock krb5int_mutex_unlock440#endif441442#endif /* multiple inclusion? */443444445