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