8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / lib / gss_mechs / mech_krb5 / support / threads.c
blobf81af831e406b24ff302c2173f405d0da0a36263
1 /*
2 * util/support/threads.c
4 * Copyright 2004,2005,2006 by the Massachusetts Institute of Technology.
5 * All Rights Reserved.
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.
27 * Preliminary thread support.
30 #include <assert.h>
31 #include <stdlib.h>
32 #include <errno.h>
33 #include "k5-thread.h"
34 #include "k5-platform.h"
35 #include "supp-int.h"
37 MAKE_INIT_FUNCTION(krb5int_thread_support_init);
38 MAKE_FINI_FUNCTION(krb5int_thread_support_fini);
40 #ifndef ENABLE_THREADS /* no thread support */
42 static void (*destructors[K5_KEY_MAX])(void *);
43 struct tsd_block { void *values[K5_KEY_MAX]; };
44 static struct tsd_block tsd_no_threads;
45 static unsigned char destructors_set[K5_KEY_MAX];
47 int krb5int_pthread_loaded (void)
49 return 0;
52 #elif defined(_WIN32)
54 static DWORD tls_idx;
55 static CRITICAL_SECTION key_lock;
56 struct tsd_block {
57 void *values[K5_KEY_MAX];
59 static void (*destructors[K5_KEY_MAX])(void *);
60 static unsigned char destructors_set[K5_KEY_MAX];
62 void krb5int_thread_detach_hook (void)
64 /* XXX Memory leak here!
65 Need to destroy all TLS objects we know about for this thread. */
66 struct tsd_block *t;
67 int i, err;
69 err = CALL_INIT_FUNCTION(krb5int_thread_support_init);
70 if (err)
71 return;
73 t = TlsGetValue(tls_idx);
74 if (t == NULL)
75 return;
76 for (i = 0; i < K5_KEY_MAX; i++) {
77 if (destructors_set[i] && destructors[i] && t->values[i]) {
78 void *v = t->values[i];
79 t->values[i] = 0;
80 (*destructors[i])(v);
85 /* Stub function not used on Windows. */
86 int krb5int_pthread_loaded (void)
88 return 0;
90 #else /* POSIX threads */
92 /* Must support register/delete/register sequence, e.g., if krb5 is
93 loaded so this support code stays in the process, and gssapi is
94 loaded, unloaded, and loaded again. */
96 static k5_mutex_t key_lock = K5_MUTEX_PARTIAL_INITIALIZER;
97 static void (*destructors[K5_KEY_MAX])(void *);
98 static unsigned char destructors_set[K5_KEY_MAX];
100 /* This is not safe yet!
102 Thread termination concurrent with key deletion can cause two
103 threads to interfere. It's a bit tricky, since one of the threads
104 will want to remove this structure from the list being walked by
105 the other.
107 Other cases, like looking up data while the library owning the key
108 is in the process of being unloaded, we don't worry about. */
110 struct tsd_block {
111 struct tsd_block *next;
112 void *values[K5_KEY_MAX];
115 #ifdef HAVE_PRAGMA_WEAK_REF
116 # pragma weak pthread_getspecific
117 # pragma weak pthread_setspecific
118 # pragma weak pthread_key_create
119 # pragma weak pthread_key_delete
120 # pragma weak pthread_create
121 # pragma weak pthread_join
122 static volatile int flag_pthread_loaded = -1;
123 static void loaded_test_aux(void)
125 if (flag_pthread_loaded == -1)
126 flag_pthread_loaded = 1;
127 else
128 /* Could we have been called twice? */
129 flag_pthread_loaded = 0;
131 static pthread_once_t loaded_test_once = PTHREAD_ONCE_INIT;
132 int krb5int_pthread_loaded (void)
134 int x = flag_pthread_loaded;
135 if (x != -1)
136 return x;
137 if (&pthread_getspecific == 0
138 || &pthread_setspecific == 0
139 || &pthread_key_create == 0
140 || &pthread_key_delete == 0
141 || &pthread_once == 0
142 || &pthread_mutex_lock == 0
143 || &pthread_mutex_unlock == 0
144 || &pthread_mutex_destroy == 0
145 || &pthread_mutex_init == 0
146 || &pthread_self == 0
147 || &pthread_equal == 0
148 /* Any program that's really multithreaded will have to be
149 able to create threads. */
150 || &pthread_create == 0
151 || &pthread_join == 0
152 /* Okay, all the interesting functions -- or stubs for them --
153 seem to be present. If we call pthread_once, does it
154 actually seem to cause the indicated function to get called
155 exactly one time? */
156 || pthread_once(&loaded_test_once, loaded_test_aux) != 0
157 || pthread_once(&loaded_test_once, loaded_test_aux) != 0
158 /* This catches cases where pthread_once does nothing, and
159 never causes the function to get called. That's a pretty
160 clear violation of the POSIX spec, but hey, it happens. */
161 || flag_pthread_loaded < 0) {
162 flag_pthread_loaded = 0;
163 return 0;
165 /* If we wanted to be super-paranoid, we could try testing whether
166 pthread_get/setspecific work, too. I don't know -- so far --
167 of any system with non-functional stubs for those. */
168 return flag_pthread_loaded;
170 static struct tsd_block tsd_if_single;
171 # define GET_NO_PTHREAD_TSD() (&tsd_if_single)
172 #else
173 # define GET_NO_PTHREAD_TSD() (abort(),(struct tsd_block *)0)
174 #endif
176 static pthread_key_t key;
177 static void thread_termination(void *);
179 static void thread_termination (void *tptr)
181 int err = k5_mutex_lock(&key_lock);
182 if (err == 0) {
183 int i, pass, none_found;
184 struct tsd_block *t = tptr;
186 /* Make multiple passes in case, for example, a libkrb5 cleanup
187 function wants to print out an error message, which causes
188 com_err to allocate a thread-specific buffer, after we just
189 freed up the old one.
191 Shouldn't actually happen, if we're careful, but check just in
192 case. */
194 pass = 0;
195 none_found = 0;
196 while (pass < 4 && !none_found) {
197 none_found = 1;
198 for (i = 0; i < K5_KEY_MAX; i++) {
199 if (destructors_set[i] && destructors[i] && t->values[i]) {
200 void *v = t->values[i];
201 t->values[i] = 0;
202 (*destructors[i])(v);
203 none_found = 0;
207 free (t);
208 err = k5_mutex_unlock(&key_lock);
211 /* remove thread from global linked list */
214 #endif /* no threads vs Win32 vs POSIX */
216 void *k5_getspecific (k5_key_t keynum)
218 struct tsd_block *t;
219 int err;
221 err = CALL_INIT_FUNCTION(krb5int_thread_support_init);
222 if (err)
223 return NULL;
225 assert(keynum >= 0 && keynum < K5_KEY_MAX);
226 assert(destructors_set[keynum] == 1);
228 #ifndef ENABLE_THREADS
230 t = &tsd_no_threads;
232 #elif defined(_WIN32)
234 t = TlsGetValue(tls_idx);
236 #else /* POSIX */
238 if (K5_PTHREADS_LOADED)
239 t = pthread_getspecific(key);
240 else
241 t = GET_NO_PTHREAD_TSD();
243 #endif
245 if (t == NULL)
246 return NULL;
247 return t->values[keynum];
250 int k5_setspecific (k5_key_t keynum, void *value)
252 struct tsd_block *t;
253 int err;
255 err = CALL_INIT_FUNCTION(krb5int_thread_support_init);
256 if (err)
257 return err;
259 assert(keynum >= 0 && keynum < K5_KEY_MAX);
260 assert(destructors_set[keynum] == 1);
262 #ifndef ENABLE_THREADS
264 t = &tsd_no_threads;
266 #elif defined(_WIN32)
268 t = TlsGetValue(tls_idx);
269 if (t == NULL) {
270 int i;
271 t = malloc(sizeof(*t));
272 if (t == NULL)
273 return errno;
274 for (i = 0; i < K5_KEY_MAX; i++)
275 t->values[i] = 0;
276 /* add to global linked list */
277 /* t->next = 0; */
278 err = TlsSetValue(tls_idx, t);
279 if (!err) {
280 free(t);
281 return GetLastError();
285 #else /* POSIX */
287 if (K5_PTHREADS_LOADED) {
288 t = pthread_getspecific(key);
289 if (t == NULL) {
290 int i;
291 t = malloc(sizeof(*t));
292 if (t == NULL)
293 return errno;
294 for (i = 0; i < K5_KEY_MAX; i++)
295 t->values[i] = 0;
296 /* add to global linked list */
297 t->next = 0;
298 err = pthread_setspecific(key, t);
299 if (err) {
300 free(t);
301 return err;
304 } else {
305 t = GET_NO_PTHREAD_TSD();
308 #endif
310 t->values[keynum] = value;
311 return 0;
314 int k5_key_register (k5_key_t keynum, void (*destructor)(void *))
316 int err;
318 err = CALL_INIT_FUNCTION(krb5int_thread_support_init);
319 if (err)
320 return err;
322 assert(keynum >= 0 && keynum < K5_KEY_MAX);
324 #ifndef ENABLE_THREADS
326 assert(destructors_set[keynum] == 0);
327 destructors[keynum] = destructor;
328 destructors_set[keynum] = 1;
329 err = 0;
331 #elif defined(_WIN32)
333 /* XXX: This can raise EXCEPTION_POSSIBLE_DEADLOCK. */
334 EnterCriticalSection(&key_lock);
335 assert(destructors_set[keynum] == 0);
336 destructors_set[keynum] = 1;
337 destructors[keynum] = destructor;
338 LeaveCriticalSection(&key_lock);
339 err = 0;
341 #else /* POSIX */
343 err = k5_mutex_lock(&key_lock);
344 if (err == 0) {
345 assert(destructors_set[keynum] == 0);
346 destructors_set[keynum] = 1;
347 destructors[keynum] = destructor;
348 err = k5_mutex_unlock(&key_lock);
351 #endif
352 return 0;
355 int k5_key_delete (k5_key_t keynum)
357 assert(keynum >= 0 && keynum < K5_KEY_MAX);
359 #ifndef ENABLE_THREADS
361 assert(destructors_set[keynum] == 1);
362 if (destructors[keynum] && tsd_no_threads.values[keynum])
363 (*destructors[keynum])(tsd_no_threads.values[keynum]);
364 destructors[keynum] = 0;
365 tsd_no_threads.values[keynum] = 0;
366 destructors_set[keynum] = 0;
368 #elif defined(_WIN32)
370 /* XXX: This can raise EXCEPTION_POSSIBLE_DEADLOCK. */
371 EnterCriticalSection(&key_lock);
372 /* XXX Memory leak here!
373 Need to destroy the associated data for all threads.
374 But watch for race conditions in case threads are going away too. */
375 assert(destructors_set[keynum] == 1);
376 destructors_set[keynum] = 0;
377 destructors[keynum] = 0;
378 LeaveCriticalSection(&key_lock);
380 #else /* POSIX */
383 int err;
385 /* XXX RESOURCE LEAK:
387 Need to destroy the allocated objects first! */
389 err = k5_mutex_lock(&key_lock);
390 if (err == 0) {
391 assert(destructors_set[keynum] == 1);
392 destructors_set[keynum] = 0;
393 destructors[keynum] = NULL;
394 k5_mutex_unlock(&key_lock);
398 #endif
400 return 0;
403 int krb5int_call_thread_support_init (void)
405 return CALL_INIT_FUNCTION(krb5int_thread_support_init);
408 #include "cache-addrinfo.h"
410 #ifdef DEBUG_THREADS_STATS
411 #include <stdio.h>
412 static FILE *stats_logfile;
413 #endif
415 int krb5int_thread_support_init (void)
417 int err;
419 #ifdef SHOW_INITFINI_FUNCS
420 printf("krb5int_thread_support_init\n");
421 #endif
423 #ifdef DEBUG_THREADS_STATS
424 /* stats_logfile = stderr; */
425 stats_logfile = fopen("/dev/tty", "w+");
426 if (stats_logfile == NULL)
427 stats_logfile = stderr;
428 #endif
430 #ifndef ENABLE_THREADS
432 /* Nothing to do for TLS initialization. */
434 #elif defined(_WIN32)
436 tls_idx = TlsAlloc();
437 /* XXX This can raise an exception if memory is low! */
438 InitializeCriticalSection(&key_lock);
440 #else /* POSIX */
442 err = k5_mutex_finish_init(&key_lock);
443 if (err)
444 return err;
445 if (K5_PTHREADS_LOADED) {
446 err = pthread_key_create(&key, thread_termination);
447 if (err)
448 return err;
451 #endif
453 err = krb5int_init_fac();
454 if (err)
455 return err;
457 err = krb5int_err_init();
458 if (err)
459 return err;
461 return 0;
464 void krb5int_thread_support_fini (void)
466 if (! INITIALIZER_RAN (krb5int_thread_support_init))
467 return;
469 #ifdef SHOW_INITFINI_FUNCS
470 printf("krb5int_thread_support_fini\n");
471 #endif
473 #ifndef ENABLE_THREADS
475 /* Do nothing. */
477 #elif defined(_WIN32)
479 /* ... free stuff ... */
480 TlsFree(tls_idx);
481 DeleteCriticalSection(&key_lock);
483 #else /* POSIX */
485 if (! INITIALIZER_RAN(krb5int_thread_support_init))
486 return;
487 if (K5_PTHREADS_LOADED)
488 pthread_key_delete(key);
489 /* ... delete stuff ... */
490 k5_mutex_destroy(&key_lock);
492 #endif
494 #ifdef DEBUG_THREADS_STATS
495 fflush(stats_logfile);
496 /* XXX Should close if not stderr, in case unloading library but
497 not exiting. */
498 #endif
500 krb5int_fini_fac();
503 #ifdef DEBUG_THREADS_STATS
504 void KRB5_CALLCONV
505 k5_mutex_lock_update_stats(k5_debug_mutex_stats *m,
506 k5_mutex_stats_tmp startwait)
508 k5_debug_time_t now;
509 k5_debug_timediff_t tdiff, tdiff2;
511 now = get_current_time();
512 (void) krb5int_call_thread_support_init();
513 m->count++;
514 m->time_acquired = now;
515 tdiff = timediff(now, startwait);
516 tdiff2 = tdiff * tdiff;
517 if (m->count == 1 || m->lockwait.valmin > tdiff)
518 m->lockwait.valmin = tdiff;
519 if (m->count == 1 || m->lockwait.valmax < tdiff)
520 m->lockwait.valmax = tdiff;
521 m->lockwait.valsum += tdiff;
522 m->lockwait.valsqsum += tdiff2;
525 void KRB5_CALLCONV
526 krb5int_mutex_unlock_update_stats(k5_debug_mutex_stats *m)
528 k5_debug_time_t now = get_current_time();
529 k5_debug_timediff_t tdiff, tdiff2;
530 tdiff = timediff(now, m->time_acquired);
531 tdiff2 = tdiff * tdiff;
532 if (m->count == 1 || m->lockheld.valmin > tdiff)
533 m->lockheld.valmin = tdiff;
534 if (m->count == 1 || m->lockheld.valmax < tdiff)
535 m->lockheld.valmax = tdiff;
536 m->lockheld.valsum += tdiff;
537 m->lockheld.valsqsum += tdiff2;
540 #include <math.h>
541 static double
542 get_stddev(struct k5_timediff_stats sp, int count)
544 long double mu, mu_squared, rho_squared;
545 mu = (long double) sp.valsum / count;
546 mu_squared = mu * mu;
547 /* SUM((x_i - mu)^2)
548 = SUM(x_i^2 - 2*mu*x_i + mu^2)
549 = SUM(x_i^2) - 2*mu*SUM(x_i) + N*mu^2
551 Standard deviation rho^2 = SUM(...) / N. */
552 rho_squared = (sp.valsqsum - 2 * mu * sp.valsum + count * mu_squared) / count;
553 return sqrt(rho_squared);
556 void KRB5_CALLCONV
557 krb5int_mutex_report_stats(k5_mutex_t *m)
559 char *p;
561 /* Tweak this to only record data on "interesting" locks. */
562 if (m->stats.count < 10)
563 return;
564 if (m->stats.lockwait.valsum < 10 * m->stats.count)
565 return;
567 p = strrchr(m->loc_created.filename, '/');
568 if (p == NULL)
569 p = m->loc_created.filename;
570 else
571 p++;
572 fprintf(stats_logfile, "mutex @%p: created at line %d of %s\n",
573 (void *) m, m->loc_created.lineno, p);
574 if (m->stats.count == 0)
575 fprintf(stats_logfile, "\tnever locked\n");
576 else {
577 double sd_wait, sd_hold;
578 sd_wait = get_stddev(m->stats.lockwait, m->stats.count);
579 sd_hold = get_stddev(m->stats.lockheld, m->stats.count);
580 fprintf(stats_logfile,
581 "\tlocked %d time%s; wait %lu/%f/%lu/%fus, hold %lu/%f/%lu/%fus\n",
582 m->stats.count, m->stats.count == 1 ? "" : "s",
583 (unsigned long) m->stats.lockwait.valmin,
584 (double) m->stats.lockwait.valsum / m->stats.count,
585 (unsigned long) m->stats.lockwait.valmax,
586 sd_wait,
587 (unsigned long) m->stats.lockheld.valmin,
588 (double) m->stats.lockheld.valsum / m->stats.count,
589 (unsigned long) m->stats.lockheld.valmax,
590 sd_hold);
593 #else
594 /* On Windows, everything defined in the export list must be defined.
595 The UNIX systems where we're using the export list don't seem to
596 care. */
597 #undef krb5int_mutex_lock_update_stats
598 void KRB5_CALLCONV
599 krb5int_mutex_lock_update_stats(k5_debug_mutex_stats *m,
600 k5_mutex_stats_tmp startwait)
603 #undef krb5int_mutex_unlock_update_stats
604 void KRB5_CALLCONV
605 krb5int_mutex_unlock_update_stats(k5_debug_mutex_stats *m)
608 #undef krb5int_mutex_report_stats
609 void KRB5_CALLCONV
610 krb5int_mutex_report_stats(k5_mutex_t *m)
613 #endif
615 /* Mutex allocation functions, for use in plugins that may not know
616 what options a given set of libraries was compiled with. */
617 int KRB5_CALLCONV
618 krb5int_mutex_alloc (k5_mutex_t **m)
620 k5_mutex_t *ptr;
621 int err;
623 ptr = malloc (sizeof (k5_mutex_t));
624 if (ptr == NULL)
625 return errno;
626 err = k5_mutex_init (ptr);
627 if (err) {
628 free (ptr);
629 return err;
631 *m = ptr;
632 return 0;
635 void KRB5_CALLCONV
636 krb5int_mutex_free (k5_mutex_t *m)
638 (void) k5_mutex_destroy (m);
639 free (m);
642 /* Callable versions of the various macros. */
643 int KRB5_CALLCONV
644 krb5int_mutex_lock (k5_mutex_t *m)
646 return k5_mutex_lock (m);
648 int KRB5_CALLCONV
649 krb5int_mutex_unlock (k5_mutex_t *m)
651 return k5_mutex_unlock (m);