xref: /freebsd/crypto/krb5/src/include/k5-thread.h (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
1*7f2fe78bSCy Schubert /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2*7f2fe78bSCy Schubert /* include/k5-thread.h - Preliminary portable thread support */
3*7f2fe78bSCy Schubert /*
4*7f2fe78bSCy Schubert  * Copyright 2004,2005,2006,2007,2008 by the Massachusetts Institute of Technology.
5*7f2fe78bSCy Schubert  * All Rights Reserved.
6*7f2fe78bSCy Schubert  *
7*7f2fe78bSCy Schubert  * Export of this software from the United States of America may
8*7f2fe78bSCy Schubert  *   require a specific license from the United States Government.
9*7f2fe78bSCy Schubert  *   It is the responsibility of any person or organization contemplating
10*7f2fe78bSCy Schubert  *   export to obtain such a license before exporting.
11*7f2fe78bSCy Schubert  *
12*7f2fe78bSCy Schubert  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13*7f2fe78bSCy Schubert  * distribute this software and its documentation for any purpose and
14*7f2fe78bSCy Schubert  * without fee is hereby granted, provided that the above copyright
15*7f2fe78bSCy Schubert  * notice appear in all copies and that both that copyright notice and
16*7f2fe78bSCy Schubert  * this permission notice appear in supporting documentation, and that
17*7f2fe78bSCy Schubert  * the name of M.I.T. not be used in advertising or publicity pertaining
18*7f2fe78bSCy Schubert  * to distribution of the software without specific, written prior
19*7f2fe78bSCy Schubert  * permission.  Furthermore if you modify this software you must label
20*7f2fe78bSCy Schubert  * your software as modified software and not distribute it in such a
21*7f2fe78bSCy Schubert  * fashion that it might be confused with the original M.I.T. software.
22*7f2fe78bSCy Schubert  * M.I.T. makes no representations about the suitability of
23*7f2fe78bSCy Schubert  * this software for any purpose.  It is provided "as is" without express
24*7f2fe78bSCy Schubert  * or implied warranty.
25*7f2fe78bSCy Schubert  */
26*7f2fe78bSCy Schubert 
27*7f2fe78bSCy Schubert #ifndef K5_THREAD_H
28*7f2fe78bSCy Schubert #define K5_THREAD_H
29*7f2fe78bSCy Schubert 
30*7f2fe78bSCy Schubert #include "autoconf.h"
31*7f2fe78bSCy Schubert #ifndef KRB5_CALLCONV
32*7f2fe78bSCy Schubert # define KRB5_CALLCONV
33*7f2fe78bSCy Schubert #endif
34*7f2fe78bSCy Schubert #ifndef KRB5_CALLCONV_C
35*7f2fe78bSCy Schubert # define KRB5_CALLCONV_C
36*7f2fe78bSCy Schubert #endif
37*7f2fe78bSCy Schubert 
38*7f2fe78bSCy Schubert /* Interface (tentative):
39*7f2fe78bSCy Schubert 
40*7f2fe78bSCy Schubert      Mutex support:
41*7f2fe78bSCy Schubert 
42*7f2fe78bSCy Schubert      // Between these two, we should be able to do pure compile-time
43*7f2fe78bSCy Schubert      // and pure run-time initialization.
44*7f2fe78bSCy Schubert      //   POSIX:   partial initializer is PTHREAD_MUTEX_INITIALIZER,
45*7f2fe78bSCy Schubert      //            finish does nothing
46*7f2fe78bSCy Schubert      //   Windows: partial initializer is an invalid handle,
47*7f2fe78bSCy Schubert      //            finish does the real initialization work
48*7f2fe78bSCy Schubert      k5_mutex_t foo_mutex = K5_MUTEX_PARTIAL_INITIALIZER;
49*7f2fe78bSCy Schubert      int k5_mutex_finish_init(k5_mutex_t *);
50*7f2fe78bSCy Schubert      // for dynamic allocation
51*7f2fe78bSCy Schubert      int k5_mutex_init(k5_mutex_t *);
52*7f2fe78bSCy Schubert      // Must work for both kinds of alloc, even if it means adding flags.
53*7f2fe78bSCy Schubert      int k5_mutex_destroy(k5_mutex_t *);
54*7f2fe78bSCy Schubert 
55*7f2fe78bSCy Schubert      // As before.
56*7f2fe78bSCy Schubert      int k5_mutex_lock(k5_mutex_t *);
57*7f2fe78bSCy Schubert      int k5_mutex_unlock(k5_mutex_t *);
58*7f2fe78bSCy Schubert 
59*7f2fe78bSCy Schubert      In each library, one new function to finish the static mutex init,
60*7f2fe78bSCy Schubert      and any other library-wide initialization that might be desired.
61*7f2fe78bSCy Schubert      On POSIX, this function would be called via the second support
62*7f2fe78bSCy Schubert      function (see below).  On Windows, it would be called at library
63*7f2fe78bSCy Schubert      load time.  These functions, or functions they calls, should be the
64*7f2fe78bSCy Schubert      only places that k5_mutex_finish_init gets called.
65*7f2fe78bSCy Schubert 
66*7f2fe78bSCy Schubert      A second function or macro called at various possible "first" entry
67*7f2fe78bSCy Schubert      points which either calls pthread_once on the first function
68*7f2fe78bSCy Schubert      (POSIX), or checks some flag set by the first function (Windows),
69*7f2fe78bSCy Schubert      and possibly returns an error.  (In the non-threaded case, a simple
70*7f2fe78bSCy Schubert      flag can be used to avoid multiple invocations, and the mutexes
71*7f2fe78bSCy Schubert      don't need run-time initialization anyways.)
72*7f2fe78bSCy Schubert 
73*7f2fe78bSCy Schubert      A third function for library termination calls mutex_destroy on
74*7f2fe78bSCy Schubert      each mutex for the library.  This function would be called
75*7f2fe78bSCy Schubert      automatically at library unload time.  If it turns out to be needed
76*7f2fe78bSCy Schubert      at exit time for libraries that don't get unloaded, perhaps we
77*7f2fe78bSCy Schubert      should also use atexit().  Any static mutexes should be cleaned up
78*7f2fe78bSCy Schubert      with k5_mutex_destroy here.
79*7f2fe78bSCy Schubert 
80*7f2fe78bSCy Schubert      How does that second support function invoke the first support
81*7f2fe78bSCy Schubert      function only once?  Through something modelled on pthread_once
82*7f2fe78bSCy Schubert      that I haven't written up yet.  Probably:
83*7f2fe78bSCy Schubert 
84*7f2fe78bSCy Schubert      k5_once_t foo_once = K5_ONCE_INIT;
85*7f2fe78bSCy Schubert      k5_once(k5_once_t *, void (*)(void));
86*7f2fe78bSCy Schubert 
87*7f2fe78bSCy Schubert      For POSIX: Map onto pthread_once facility.
88*7f2fe78bSCy Schubert      For non-threaded case: A simple flag.
89*7f2fe78bSCy Schubert      For Windows: Not needed; library init code takes care of it.
90*7f2fe78bSCy Schubert 
91*7f2fe78bSCy Schubert      XXX: A general k5_once mechanism isn't possible for Windows,
92*7f2fe78bSCy Schubert      without faking it through named mutexes or mutexes initialized at
93*7f2fe78bSCy Schubert      startup.  I was only using it in one place outside these headers,
94*7f2fe78bSCy Schubert      so I'm dropping the general scheme.  Eventually the existing uses
95*7f2fe78bSCy Schubert      in k5-thread.h and k5-platform.h will be converted to pthread_once
96*7f2fe78bSCy Schubert      or static variables.
97*7f2fe78bSCy Schubert 
98*7f2fe78bSCy Schubert 
99*7f2fe78bSCy Schubert      Thread-specific data:
100*7f2fe78bSCy Schubert 
101*7f2fe78bSCy Schubert      // TSD keys are limited in number in gssapi/krb5/com_err; enumerate
102*7f2fe78bSCy Schubert      // them all.  This allows support code init to allocate the
103*7f2fe78bSCy Schubert      // necessary storage for pointers all at once, and avoids any
104*7f2fe78bSCy Schubert      // possible error in key creation.
105*7f2fe78bSCy Schubert      enum { ... } k5_key_t;
106*7f2fe78bSCy Schubert      // Register destructor function.  Called in library init code.
107*7f2fe78bSCy Schubert      int k5_key_register(k5_key_t, void (*destructor)(void *));
108*7f2fe78bSCy Schubert      // Returns NULL or data.
109*7f2fe78bSCy Schubert      void *k5_getspecific(k5_key_t);
110*7f2fe78bSCy Schubert      // Returns error if key out of bounds, or the pointer table can't
111*7f2fe78bSCy Schubert      // be allocated.  A call to k5_key_register must have happened first.
112*7f2fe78bSCy Schubert      // This may trigger the calling of pthread_setspecific on POSIX.
113*7f2fe78bSCy Schubert      int k5_setspecific(k5_key_t, void *);
114*7f2fe78bSCy Schubert      // Called in library termination code.
115*7f2fe78bSCy Schubert      // Trashes data in all threads, calling the registered destructor
116*7f2fe78bSCy Schubert      // (but calling it from the current thread).
117*7f2fe78bSCy Schubert      int k5_key_delete(k5_key_t);
118*7f2fe78bSCy Schubert 
119*7f2fe78bSCy Schubert      For the non-threaded version, the support code will have a static
120*7f2fe78bSCy Schubert      array indexed by k5_key_t values, and get/setspecific simply access
121*7f2fe78bSCy Schubert      the array elements.
122*7f2fe78bSCy Schubert 
123*7f2fe78bSCy Schubert      The TSD destructor table is global state, protected by a mutex if
124*7f2fe78bSCy Schubert      threads are enabled.
125*7f2fe78bSCy Schubert 
126*7f2fe78bSCy Schubert 
127*7f2fe78bSCy Schubert      Any actual external symbols will use the krb5int_ prefix.  The k5_
128*7f2fe78bSCy Schubert      names will be simple macros or inline functions to rename the
129*7f2fe78bSCy Schubert      external symbols, or slightly more complex ones to expand the
130*7f2fe78bSCy Schubert      implementation inline (e.g., map to POSIX versions and/or debug
131*7f2fe78bSCy Schubert      code using __FILE__ and the like).
132*7f2fe78bSCy Schubert 
133*7f2fe78bSCy Schubert 
134*7f2fe78bSCy Schubert      More to be added, perhaps.  */
135*7f2fe78bSCy Schubert 
136*7f2fe78bSCy Schubert #include <assert.h>
137*7f2fe78bSCy Schubert #ifndef NDEBUG
138*7f2fe78bSCy Schubert #include <stdio.h>
139*7f2fe78bSCy Schubert #include <string.h>
140*7f2fe78bSCy Schubert #endif
141*7f2fe78bSCy Schubert 
142*7f2fe78bSCy Schubert /* The mutex structure we use, k5_mutex_t, is defined to some
143*7f2fe78bSCy Schubert    OS-specific bits.  The use of multiple layers of typedefs are an
144*7f2fe78bSCy Schubert    artifact resulting from debugging code we once used, implemented as
145*7f2fe78bSCy Schubert    wrappers around the OS mutex scheme.
146*7f2fe78bSCy Schubert 
147*7f2fe78bSCy Schubert    The OS specific bits, in k5_os_mutex, break down into three primary
148*7f2fe78bSCy Schubert    implementations, POSIX threads, Windows threads, and no thread
149*7f2fe78bSCy Schubert    support.  However, the POSIX thread version is further subdivided:
150*7f2fe78bSCy Schubert    In one case, we can determine at run time whether the thread
151*7f2fe78bSCy Schubert    library is linked into the application, and use it only if it is
152*7f2fe78bSCy Schubert    present; in the other case, we cannot, and the thread library must
153*7f2fe78bSCy Schubert    be linked in always, but can be used unconditionally.  In the
154*7f2fe78bSCy Schubert    former case, the k5_os_mutex structure needs to hold both the POSIX
155*7f2fe78bSCy Schubert    and the non-threaded versions.
156*7f2fe78bSCy Schubert 
157*7f2fe78bSCy Schubert    The various k5_os_mutex_* operations are the OS-specific versions,
158*7f2fe78bSCy Schubert    applied to the OS-specific data, and k5_mutex_* uses k5_os_mutex_*
159*7f2fe78bSCy Schubert    to do the OS-specific parts of the work.  */
160*7f2fe78bSCy Schubert 
161*7f2fe78bSCy Schubert /* Define the OS mutex bit.  */
162*7f2fe78bSCy Schubert 
163*7f2fe78bSCy Schubert typedef char k5_os_nothread_mutex;
164*7f2fe78bSCy Schubert # define K5_OS_NOTHREAD_MUTEX_PARTIAL_INITIALIZER       0
165*7f2fe78bSCy Schubert /* Empty inline functions avoid the "statement with no effect"
166*7f2fe78bSCy Schubert    warnings, and do better type-checking than functions that don't use
167*7f2fe78bSCy Schubert    their arguments.  */
k5_os_nothread_mutex_finish_init(k5_os_nothread_mutex * m)168*7f2fe78bSCy Schubert static inline int k5_os_nothread_mutex_finish_init(k5_os_nothread_mutex *m) {
169*7f2fe78bSCy Schubert     return 0;
170*7f2fe78bSCy Schubert }
k5_os_nothread_mutex_init(k5_os_nothread_mutex * m)171*7f2fe78bSCy Schubert static inline int k5_os_nothread_mutex_init(k5_os_nothread_mutex *m) {
172*7f2fe78bSCy Schubert     return 0;
173*7f2fe78bSCy Schubert }
k5_os_nothread_mutex_destroy(k5_os_nothread_mutex * m)174*7f2fe78bSCy Schubert static inline int k5_os_nothread_mutex_destroy(k5_os_nothread_mutex *m) {
175*7f2fe78bSCy Schubert     return 0;
176*7f2fe78bSCy Schubert }
k5_os_nothread_mutex_lock(k5_os_nothread_mutex * m)177*7f2fe78bSCy Schubert static inline int k5_os_nothread_mutex_lock(k5_os_nothread_mutex *m) {
178*7f2fe78bSCy Schubert     return 0;
179*7f2fe78bSCy Schubert }
k5_os_nothread_mutex_unlock(k5_os_nothread_mutex * m)180*7f2fe78bSCy Schubert static inline int k5_os_nothread_mutex_unlock(k5_os_nothread_mutex *m) {
181*7f2fe78bSCy Schubert     return 0;
182*7f2fe78bSCy Schubert }
183*7f2fe78bSCy Schubert 
184*7f2fe78bSCy Schubert /* Values:
185*7f2fe78bSCy Schubert    2 - function has not been run
186*7f2fe78bSCy Schubert    3 - function has been run
187*7f2fe78bSCy Schubert    4 - function is being run -- deadlock detected */
188*7f2fe78bSCy Schubert typedef unsigned char k5_os_nothread_once_t;
189*7f2fe78bSCy Schubert # define K5_OS_NOTHREAD_ONCE_INIT       2
190*7f2fe78bSCy Schubert # define k5_os_nothread_once(O,F)                               \
191*7f2fe78bSCy Schubert     (*(O) == 3 ? 0                                              \
192*7f2fe78bSCy Schubert      : *(O) == 2 ? (*(O) = 4, (F)(), *(O) = 3, 0)               \
193*7f2fe78bSCy Schubert      : (assert(*(O) != 4), assert(*(O) == 2 || *(O) == 3), 0))
194*7f2fe78bSCy Schubert 
195*7f2fe78bSCy Schubert 
196*7f2fe78bSCy Schubert 
197*7f2fe78bSCy Schubert #ifndef ENABLE_THREADS
198*7f2fe78bSCy Schubert 
199*7f2fe78bSCy Schubert typedef k5_os_nothread_mutex k5_os_mutex;
200*7f2fe78bSCy Schubert # define K5_OS_MUTEX_PARTIAL_INITIALIZER        \
201*7f2fe78bSCy Schubert     K5_OS_NOTHREAD_MUTEX_PARTIAL_INITIALIZER
202*7f2fe78bSCy Schubert # define k5_os_mutex_finish_init        k5_os_nothread_mutex_finish_init
203*7f2fe78bSCy Schubert # define k5_os_mutex_init               k5_os_nothread_mutex_init
204*7f2fe78bSCy Schubert # define k5_os_mutex_destroy            k5_os_nothread_mutex_destroy
205*7f2fe78bSCy Schubert # define k5_os_mutex_lock               k5_os_nothread_mutex_lock
206*7f2fe78bSCy Schubert # define k5_os_mutex_unlock             k5_os_nothread_mutex_unlock
207*7f2fe78bSCy Schubert 
208*7f2fe78bSCy Schubert # define k5_once_t                      k5_os_nothread_once_t
209*7f2fe78bSCy Schubert # define K5_ONCE_INIT                   K5_OS_NOTHREAD_ONCE_INIT
210*7f2fe78bSCy Schubert # define k5_once                        k5_os_nothread_once
211*7f2fe78bSCy Schubert 
212*7f2fe78bSCy Schubert #elif HAVE_PTHREAD
213*7f2fe78bSCy Schubert 
214*7f2fe78bSCy Schubert # include <pthread.h>
215*7f2fe78bSCy Schubert 
216*7f2fe78bSCy Schubert /* Weak reference support, etc.
217*7f2fe78bSCy Schubert 
218*7f2fe78bSCy Schubert    Linux: Stub mutex routines exist, but pthread_once does not.
219*7f2fe78bSCy Schubert 
220*7f2fe78bSCy Schubert    Solaris <10: In libc there's a pthread_once that doesn't seem to do
221*7f2fe78bSCy Schubert    anything.  Bleah.  But pthread_mutexattr_setrobust_np is defined
222*7f2fe78bSCy Schubert    only in libpthread.  However, some version of GNU libc (Red Hat's
223*7f2fe78bSCy Schubert    Fedora Core 5, reportedly) seems to have that function, but no
224*7f2fe78bSCy Schubert    declaration, so we'd have to declare it in order to test for its
225*7f2fe78bSCy Schubert    address.  We now have tests to see if pthread_once actually works,
226*7f2fe78bSCy Schubert    so stick with that for now.
227*7f2fe78bSCy Schubert 
228*7f2fe78bSCy Schubert    Solaris 10: The real thread support now lives in libc, and
229*7f2fe78bSCy Schubert    libpthread is just a filter object.  So we might as well use the
230*7f2fe78bSCy Schubert    real functions unconditionally.  Since we haven't got a test for
231*7f2fe78bSCy Schubert    this property yet, we use NO_WEAK_PTHREADS defined in aclocal.m4
232*7f2fe78bSCy Schubert    depending on the OS type.
233*7f2fe78bSCy Schubert 
234*7f2fe78bSCy Schubert    IRIX 6.5 stub pthread support in libc is really annoying.  The
235*7f2fe78bSCy Schubert    pthread_mutex_lock function returns ENOSYS for a program not linked
236*7f2fe78bSCy Schubert    against -lpthread.  No link-time failure, no weak symbols, etc.
237*7f2fe78bSCy Schubert    The C library doesn't provide pthread_once; we can use weak
238*7f2fe78bSCy Schubert    reference support for that.
239*7f2fe78bSCy Schubert 
240*7f2fe78bSCy Schubert    If weak references are not available, then for now, we assume that
241*7f2fe78bSCy Schubert    the pthread support routines will always be available -- either the
242*7f2fe78bSCy Schubert    real thing, or functional stubs that merely prohibit creating
243*7f2fe78bSCy Schubert    threads.
244*7f2fe78bSCy Schubert 
245*7f2fe78bSCy Schubert    If we find a platform with non-functional stubs and no weak
246*7f2fe78bSCy Schubert    references, we may have to resort to some hack like dlsym on the
247*7f2fe78bSCy Schubert    symbol tables of the current process.  */
248*7f2fe78bSCy Schubert 
249*7f2fe78bSCy Schubert #if defined(HAVE_PRAGMA_WEAK_REF) && !defined(NO_WEAK_PTHREADS)
250*7f2fe78bSCy Schubert # define USE_CONDITIONAL_PTHREADS
251*7f2fe78bSCy Schubert #endif
252*7f2fe78bSCy Schubert 
253*7f2fe78bSCy Schubert #ifdef USE_CONDITIONAL_PTHREADS
254*7f2fe78bSCy Schubert 
255*7f2fe78bSCy Schubert /* Can't rely on useful stubs -- see above regarding Solaris.  */
256*7f2fe78bSCy Schubert typedef struct {
257*7f2fe78bSCy Schubert     pthread_once_t o;
258*7f2fe78bSCy Schubert     k5_os_nothread_once_t n;
259*7f2fe78bSCy Schubert } k5_once_t;
260*7f2fe78bSCy Schubert # define K5_ONCE_INIT   { PTHREAD_ONCE_INIT, K5_OS_NOTHREAD_ONCE_INIT }
261*7f2fe78bSCy Schubert 
262*7f2fe78bSCy Schubert int k5_once(k5_once_t *once, void (*fn)(void));
263*7f2fe78bSCy Schubert #else
264*7f2fe78bSCy Schubert 
265*7f2fe78bSCy Schubert /* no pragma weak support */
266*7f2fe78bSCy Schubert 
267*7f2fe78bSCy Schubert typedef pthread_once_t k5_once_t;
268*7f2fe78bSCy Schubert # define K5_ONCE_INIT   PTHREAD_ONCE_INIT
269*7f2fe78bSCy Schubert # define k5_once        pthread_once
270*7f2fe78bSCy Schubert 
271*7f2fe78bSCy Schubert #endif
272*7f2fe78bSCy Schubert 
273*7f2fe78bSCy Schubert #if defined(__mips) && defined(__sgi) && (defined(_SYSTYPE_SVR4) || defined(__SYSTYPE_SVR4__))
274*7f2fe78bSCy Schubert # ifndef HAVE_PRAGMA_WEAK_REF
275*7f2fe78bSCy Schubert #  if defined(__GNUC__) && __GNUC__ < 3
276*7f2fe78bSCy Schubert #   error "Please update to a newer gcc with weak symbol support, or switch to native cc, reconfigure and recompile."
277*7f2fe78bSCy Schubert #  else
278*7f2fe78bSCy Schubert #   error "Weak reference support is required"
279*7f2fe78bSCy Schubert #  endif
280*7f2fe78bSCy Schubert # endif
281*7f2fe78bSCy Schubert #endif
282*7f2fe78bSCy Schubert 
283*7f2fe78bSCy Schubert typedef pthread_mutex_t k5_os_mutex;
284*7f2fe78bSCy Schubert # define K5_OS_MUTEX_PARTIAL_INITIALIZER        \
285*7f2fe78bSCy Schubert     PTHREAD_MUTEX_INITIALIZER
286*7f2fe78bSCy Schubert 
287*7f2fe78bSCy Schubert #ifdef USE_CONDITIONAL_PTHREADS
288*7f2fe78bSCy Schubert 
289*7f2fe78bSCy Schubert # define k5_os_mutex_finish_init(M)             (0)
290*7f2fe78bSCy Schubert int k5_os_mutex_init(k5_os_mutex *m);
291*7f2fe78bSCy Schubert int k5_os_mutex_destroy(k5_os_mutex *m);
292*7f2fe78bSCy Schubert int k5_os_mutex_lock(k5_os_mutex *m);
293*7f2fe78bSCy Schubert int k5_os_mutex_unlock(k5_os_mutex *m);
294*7f2fe78bSCy Schubert 
295*7f2fe78bSCy Schubert #else
296*7f2fe78bSCy Schubert 
k5_os_mutex_finish_init(k5_os_mutex * m)297*7f2fe78bSCy Schubert static inline int k5_os_mutex_finish_init(k5_os_mutex *m) { return 0; }
298*7f2fe78bSCy Schubert # define k5_os_mutex_init(M)            pthread_mutex_init((M), 0)
299*7f2fe78bSCy Schubert # define k5_os_mutex_destroy(M)         pthread_mutex_destroy((M))
300*7f2fe78bSCy Schubert # define k5_os_mutex_lock(M)            pthread_mutex_lock(M)
301*7f2fe78bSCy Schubert # define k5_os_mutex_unlock(M)          pthread_mutex_unlock(M)
302*7f2fe78bSCy Schubert 
303*7f2fe78bSCy Schubert #endif /* is pthreads always available? */
304*7f2fe78bSCy Schubert 
305*7f2fe78bSCy Schubert #elif defined _WIN32
306*7f2fe78bSCy Schubert 
307*7f2fe78bSCy Schubert # define k5_once_t k5_os_nothread_once_t
308*7f2fe78bSCy Schubert 
309*7f2fe78bSCy Schubert typedef struct {
310*7f2fe78bSCy Schubert     HANDLE h;
311*7f2fe78bSCy Schubert     int is_locked;
312*7f2fe78bSCy Schubert } k5_os_mutex;
313*7f2fe78bSCy Schubert 
314*7f2fe78bSCy Schubert # define K5_OS_MUTEX_PARTIAL_INITIALIZER { INVALID_HANDLE_VALUE, 0 }
315*7f2fe78bSCy Schubert 
316*7f2fe78bSCy Schubert # define k5_os_mutex_finish_init(M)                                     \
317*7f2fe78bSCy Schubert     (assert((M)->h == INVALID_HANDLE_VALUE),                            \
318*7f2fe78bSCy Schubert      ((M)->h = CreateMutex(NULL, FALSE, NULL)) ? 0 : GetLastError())
319*7f2fe78bSCy Schubert # define k5_os_mutex_init(M)                                            \
320*7f2fe78bSCy Schubert     ((M)->is_locked = 0,                                                \
321*7f2fe78bSCy Schubert      ((M)->h = CreateMutex(NULL, FALSE, NULL)) ? 0 : GetLastError())
322*7f2fe78bSCy Schubert # define k5_os_mutex_destroy(M)                                 \
323*7f2fe78bSCy Schubert     (CloseHandle((M)->h) ? ((M)->h = 0, 0) : GetLastError())
324*7f2fe78bSCy Schubert # define k5_os_mutex_lock k5_win_mutex_lock
325*7f2fe78bSCy Schubert 
k5_win_mutex_lock(k5_os_mutex * m)326*7f2fe78bSCy Schubert static inline int k5_win_mutex_lock(k5_os_mutex *m)
327*7f2fe78bSCy Schubert {
328*7f2fe78bSCy Schubert     DWORD res;
329*7f2fe78bSCy Schubert     res = WaitForSingleObject(m->h, INFINITE);
330*7f2fe78bSCy Schubert     if (res == WAIT_FAILED)
331*7f2fe78bSCy Schubert         return GetLastError();
332*7f2fe78bSCy Schubert     /* Eventually these should be turned into some reasonable error
333*7f2fe78bSCy Schubert        code.  */
334*7f2fe78bSCy Schubert     assert(res != WAIT_TIMEOUT);
335*7f2fe78bSCy Schubert     assert(res != WAIT_ABANDONED);
336*7f2fe78bSCy Schubert     assert(res == WAIT_OBJECT_0);
337*7f2fe78bSCy Schubert     /* Avoid locking twice.  */
338*7f2fe78bSCy Schubert     assert(m->is_locked == 0);
339*7f2fe78bSCy Schubert     m->is_locked = 1;
340*7f2fe78bSCy Schubert     return 0;
341*7f2fe78bSCy Schubert }
342*7f2fe78bSCy Schubert 
343*7f2fe78bSCy Schubert # define k5_os_mutex_unlock(M)                  \
344*7f2fe78bSCy Schubert     (assert((M)->is_locked == 1),               \
345*7f2fe78bSCy Schubert      (M)->is_locked = 0,                        \
346*7f2fe78bSCy Schubert      ReleaseMutex((M)->h) ? 0 : GetLastError())
347*7f2fe78bSCy Schubert 
348*7f2fe78bSCy Schubert #else
349*7f2fe78bSCy Schubert 
350*7f2fe78bSCy Schubert # error "Thread support enabled, but thread system unknown"
351*7f2fe78bSCy Schubert 
352*7f2fe78bSCy Schubert #endif
353*7f2fe78bSCy Schubert 
354*7f2fe78bSCy Schubert typedef k5_os_mutex k5_mutex_t;
355*7f2fe78bSCy Schubert #define K5_MUTEX_PARTIAL_INITIALIZER    K5_OS_MUTEX_PARTIAL_INITIALIZER
k5_mutex_init(k5_mutex_t * m)356*7f2fe78bSCy Schubert static inline int k5_mutex_init(k5_mutex_t *m)
357*7f2fe78bSCy Schubert {
358*7f2fe78bSCy Schubert     return k5_os_mutex_init(m);
359*7f2fe78bSCy Schubert }
k5_mutex_finish_init(k5_mutex_t * m)360*7f2fe78bSCy Schubert static inline int k5_mutex_finish_init(k5_mutex_t *m)
361*7f2fe78bSCy Schubert {
362*7f2fe78bSCy Schubert     return k5_os_mutex_finish_init(m);
363*7f2fe78bSCy Schubert }
364*7f2fe78bSCy Schubert #define k5_mutex_destroy(M)                     \
365*7f2fe78bSCy Schubert     (k5_os_mutex_destroy(M))
366*7f2fe78bSCy Schubert 
k5_mutex_lock(k5_mutex_t * m)367*7f2fe78bSCy Schubert static inline void k5_mutex_lock(k5_mutex_t *m)
368*7f2fe78bSCy Schubert {
369*7f2fe78bSCy Schubert     int r = k5_os_mutex_lock(m);
370*7f2fe78bSCy Schubert #ifndef NDEBUG
371*7f2fe78bSCy Schubert     if (r != 0) {
372*7f2fe78bSCy Schubert         fprintf(stderr, "k5_mutex_lock: Received error %d (%s)\n",
373*7f2fe78bSCy Schubert                 r, strerror(r));
374*7f2fe78bSCy Schubert     }
375*7f2fe78bSCy Schubert #endif
376*7f2fe78bSCy Schubert     assert(r == 0);
377*7f2fe78bSCy Schubert }
378*7f2fe78bSCy Schubert 
k5_mutex_unlock(k5_mutex_t * m)379*7f2fe78bSCy Schubert static inline void k5_mutex_unlock(k5_mutex_t *m)
380*7f2fe78bSCy Schubert {
381*7f2fe78bSCy Schubert     int r = k5_os_mutex_unlock(m);
382*7f2fe78bSCy Schubert #ifndef NDEBUG
383*7f2fe78bSCy Schubert     if (r != 0) {
384*7f2fe78bSCy Schubert         fprintf(stderr, "k5_mutex_unlock: Received error %d (%s)\n",
385*7f2fe78bSCy Schubert                 r, strerror(r));
386*7f2fe78bSCy Schubert     }
387*7f2fe78bSCy Schubert #endif
388*7f2fe78bSCy Schubert     assert(r == 0);
389*7f2fe78bSCy Schubert }
390*7f2fe78bSCy Schubert 
391*7f2fe78bSCy Schubert #define k5_mutex_assert_locked(M)       ((void)(M))
392*7f2fe78bSCy Schubert #define k5_mutex_assert_unlocked(M)     ((void)(M))
393*7f2fe78bSCy Schubert #define k5_assert_locked        k5_mutex_assert_locked
394*7f2fe78bSCy Schubert #define k5_assert_unlocked      k5_mutex_assert_unlocked
395*7f2fe78bSCy Schubert 
396*7f2fe78bSCy Schubert /* Thread-specific data; implemented in a support file, because we'll
397*7f2fe78bSCy Schubert    need to keep track of some global data for cleanup purposes.
398*7f2fe78bSCy Schubert 
399*7f2fe78bSCy Schubert    Note that the callback function type is such that the C library
400*7f2fe78bSCy Schubert    routine free() is a valid callback.  */
401*7f2fe78bSCy Schubert typedef enum {
402*7f2fe78bSCy Schubert     K5_KEY_COM_ERR,
403*7f2fe78bSCy Schubert     K5_KEY_GSS_KRB5_SET_CCACHE_OLD_NAME,
404*7f2fe78bSCy Schubert     K5_KEY_GSS_KRB5_CCACHE_NAME,
405*7f2fe78bSCy Schubert     K5_KEY_GSS_KRB5_ERROR_MESSAGE,
406*7f2fe78bSCy Schubert     K5_KEY_GSS_SPNEGO_STATUS,
407*7f2fe78bSCy Schubert #if defined(__MACH__) && defined(__APPLE__)
408*7f2fe78bSCy Schubert     K5_KEY_IPC_CONNECTION_INFO,
409*7f2fe78bSCy Schubert #endif
410*7f2fe78bSCy Schubert     K5_KEY_MAX
411*7f2fe78bSCy Schubert } k5_key_t;
412*7f2fe78bSCy Schubert /* rename shorthand symbols for export */
413*7f2fe78bSCy Schubert #define k5_key_register krb5int_key_register
414*7f2fe78bSCy Schubert #define k5_getspecific  krb5int_getspecific
415*7f2fe78bSCy Schubert #define k5_setspecific  krb5int_setspecific
416*7f2fe78bSCy Schubert #define k5_key_delete   krb5int_key_delete
417*7f2fe78bSCy Schubert extern int k5_key_register(k5_key_t, void (*)(void *));
418*7f2fe78bSCy Schubert extern void *k5_getspecific(k5_key_t);
419*7f2fe78bSCy Schubert extern int k5_setspecific(k5_key_t, void *);
420*7f2fe78bSCy Schubert extern int k5_key_delete(k5_key_t);
421*7f2fe78bSCy Schubert 
422*7f2fe78bSCy Schubert extern int  KRB5_CALLCONV krb5int_mutex_alloc  (k5_mutex_t **);
423*7f2fe78bSCy Schubert extern void KRB5_CALLCONV krb5int_mutex_free   (k5_mutex_t *);
424*7f2fe78bSCy Schubert extern void KRB5_CALLCONV krb5int_mutex_lock   (k5_mutex_t *);
425*7f2fe78bSCy Schubert extern void KRB5_CALLCONV krb5int_mutex_unlock (k5_mutex_t *);
426*7f2fe78bSCy Schubert 
427*7f2fe78bSCy Schubert /* In time, many of the definitions above should move into the support
428*7f2fe78bSCy Schubert    library, and this file should be greatly simplified.  For type
429*7f2fe78bSCy Schubert    definitions, that'll take some work, since other data structures
430*7f2fe78bSCy Schubert    incorporate mutexes directly, and our mutex type is dependent on
431*7f2fe78bSCy Schubert    configuration options and system attributes.  For most functions,
432*7f2fe78bSCy Schubert    though, it should be relatively easy.
433*7f2fe78bSCy Schubert 
434*7f2fe78bSCy Schubert    For now, plugins should use the exported functions, and not the
435*7f2fe78bSCy Schubert    above macros, and use krb5int_mutex_alloc for allocations.  */
436*7f2fe78bSCy Schubert #if defined(PLUGIN) || (defined(CONFIG_SMALL) && !defined(THREAD_SUPPORT_IMPL))
437*7f2fe78bSCy Schubert #undef k5_mutex_lock
438*7f2fe78bSCy Schubert #define k5_mutex_lock krb5int_mutex_lock
439*7f2fe78bSCy Schubert #undef k5_mutex_unlock
440*7f2fe78bSCy Schubert #define k5_mutex_unlock krb5int_mutex_unlock
441*7f2fe78bSCy Schubert #endif
442*7f2fe78bSCy Schubert 
443*7f2fe78bSCy Schubert #endif /* multiple inclusion? */
444