xref: /onnv-gate/usr/src/lib/gss_mechs/mech_krb5/support/threads.c (revision 7934:6aeeafc994de)
1781Sgtb /*
2781Sgtb  * util/support/threads.c
3781Sgtb  *
4*7934SMark.Phalan@Sun.COM  * Copyright 2004,2005,2006 by the Massachusetts Institute of Technology.
5781Sgtb  * All Rights Reserved.
6781Sgtb  *
7781Sgtb  * Export of this software from the United States of America may
8781Sgtb  *   require a specific license from the United States Government.
9781Sgtb  *   It is the responsibility of any person or organization contemplating
10781Sgtb  *   export to obtain such a license before exporting.
11781Sgtb  *
12781Sgtb  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13781Sgtb  * distribute this software and its documentation for any purpose and
14781Sgtb  * without fee is hereby granted, provided that the above copyright
15781Sgtb  * notice appear in all copies and that both that copyright notice and
16781Sgtb  * this permission notice appear in supporting documentation, and that
17781Sgtb  * the name of M.I.T. not be used in advertising or publicity pertaining
18781Sgtb  * to distribution of the software without specific, written prior
19781Sgtb  * permission.  Furthermore if you modify this software you must label
20781Sgtb  * your software as modified software and not distribute it in such a
21781Sgtb  * fashion that it might be confused with the original M.I.T. software.
22781Sgtb  * M.I.T. makes no representations about the suitability of
23781Sgtb  * this software for any purpose.  It is provided "as is" without express
24781Sgtb  * or implied warranty.
25781Sgtb  *
26781Sgtb  *
27781Sgtb  * Preliminary thread support.
28781Sgtb  */
29781Sgtb 
30781Sgtb #include <assert.h>
31781Sgtb #include <stdlib.h>
32781Sgtb #include <errno.h>
33*7934SMark.Phalan@Sun.COM #include "k5-thread.h"
34*7934SMark.Phalan@Sun.COM #include "k5-platform.h"
35*7934SMark.Phalan@Sun.COM #include "supp-int.h"
36781Sgtb 
37781Sgtb MAKE_INIT_FUNCTION(krb5int_thread_support_init);
38781Sgtb MAKE_FINI_FUNCTION(krb5int_thread_support_fini);
39781Sgtb 
40781Sgtb #ifndef ENABLE_THREADS /* no thread support */
41781Sgtb 
42781Sgtb static void (*destructors[K5_KEY_MAX])(void *);
43781Sgtb struct tsd_block { void *values[K5_KEY_MAX]; };
44781Sgtb static struct tsd_block tsd_no_threads;
45781Sgtb static unsigned char destructors_set[K5_KEY_MAX];
46781Sgtb 
krb5int_pthread_loaded(void)47*7934SMark.Phalan@Sun.COM int krb5int_pthread_loaded (void)
48*7934SMark.Phalan@Sun.COM {
49*7934SMark.Phalan@Sun.COM     return 0;
50*7934SMark.Phalan@Sun.COM }
51*7934SMark.Phalan@Sun.COM 
52781Sgtb #elif defined(_WIN32)
53781Sgtb 
54781Sgtb static DWORD tls_idx;
55781Sgtb static CRITICAL_SECTION key_lock;
56781Sgtb struct tsd_block {
57781Sgtb   void *values[K5_KEY_MAX];
58781Sgtb };
59781Sgtb static void (*destructors[K5_KEY_MAX])(void *);
60781Sgtb static unsigned char destructors_set[K5_KEY_MAX];
61781Sgtb 
krb5int_thread_detach_hook(void)62781Sgtb void krb5int_thread_detach_hook (void)
63781Sgtb {
64781Sgtb     /* XXX Memory leak here!
65781Sgtb        Need to destroy all TLS objects we know about for this thread.  */
66781Sgtb     struct tsd_block *t;
67781Sgtb     int i, err;
68781Sgtb 
69781Sgtb     err = CALL_INIT_FUNCTION(krb5int_thread_support_init);
70781Sgtb     if (err)
71781Sgtb 	return;
72781Sgtb 
73781Sgtb     t = TlsGetValue(tls_idx);
74781Sgtb     if (t == NULL)
75781Sgtb 	return;
76781Sgtb     for (i = 0; i < K5_KEY_MAX; i++) {
77781Sgtb 	if (destructors_set[i] && destructors[i] && t->values[i]) {
78781Sgtb 	    void *v = t->values[i];
79781Sgtb 	    t->values[i] = 0;
80781Sgtb 	    (*destructors[i])(v);
81781Sgtb 	}
82781Sgtb     }
83781Sgtb }
84781Sgtb 
85*7934SMark.Phalan@Sun.COM /* Stub function not used on Windows. */
krb5int_pthread_loaded(void)86*7934SMark.Phalan@Sun.COM int krb5int_pthread_loaded (void)
87*7934SMark.Phalan@Sun.COM {
88*7934SMark.Phalan@Sun.COM     return 0;
89*7934SMark.Phalan@Sun.COM }
90781Sgtb #else /* POSIX threads */
91781Sgtb 
92781Sgtb /* Must support register/delete/register sequence, e.g., if krb5 is
93781Sgtb    loaded so this support code stays in the process, and gssapi is
94781Sgtb    loaded, unloaded, and loaded again.  */
95781Sgtb 
96781Sgtb static k5_mutex_t key_lock = K5_MUTEX_PARTIAL_INITIALIZER;
97781Sgtb static void (*destructors[K5_KEY_MAX])(void *);
98781Sgtb static unsigned char destructors_set[K5_KEY_MAX];
99781Sgtb 
100781Sgtb /* This is not safe yet!
101781Sgtb 
102781Sgtb    Thread termination concurrent with key deletion can cause two
103781Sgtb    threads to interfere.  It's a bit tricky, since one of the threads
104781Sgtb    will want to remove this structure from the list being walked by
105781Sgtb    the other.
106781Sgtb 
107781Sgtb    Other cases, like looking up data while the library owning the key
108781Sgtb    is in the process of being unloaded, we don't worry about.  */
109781Sgtb 
110781Sgtb struct tsd_block {
111781Sgtb     struct tsd_block *next;
112781Sgtb     void *values[K5_KEY_MAX];
113781Sgtb };
114781Sgtb 
115781Sgtb #ifdef HAVE_PRAGMA_WEAK_REF
116781Sgtb # pragma weak pthread_getspecific
117781Sgtb # pragma weak pthread_setspecific
118781Sgtb # pragma weak pthread_key_create
119781Sgtb # pragma weak pthread_key_delete
120*7934SMark.Phalan@Sun.COM # pragma weak pthread_create
121*7934SMark.Phalan@Sun.COM # pragma weak pthread_join
122*7934SMark.Phalan@Sun.COM static volatile int flag_pthread_loaded = -1;
loaded_test_aux(void)123*7934SMark.Phalan@Sun.COM static void loaded_test_aux(void)
124*7934SMark.Phalan@Sun.COM {
125*7934SMark.Phalan@Sun.COM     if (flag_pthread_loaded == -1)
126*7934SMark.Phalan@Sun.COM 	flag_pthread_loaded = 1;
127*7934SMark.Phalan@Sun.COM     else
128*7934SMark.Phalan@Sun.COM 	/* Could we have been called twice?  */
129*7934SMark.Phalan@Sun.COM 	flag_pthread_loaded = 0;
130*7934SMark.Phalan@Sun.COM }
131*7934SMark.Phalan@Sun.COM static pthread_once_t loaded_test_once = PTHREAD_ONCE_INIT;
krb5int_pthread_loaded(void)132*7934SMark.Phalan@Sun.COM int krb5int_pthread_loaded (void)
133*7934SMark.Phalan@Sun.COM {
134*7934SMark.Phalan@Sun.COM     int x = flag_pthread_loaded;
135*7934SMark.Phalan@Sun.COM     if (x != -1)
136*7934SMark.Phalan@Sun.COM 	return x;
137*7934SMark.Phalan@Sun.COM     if (&pthread_getspecific == 0
138*7934SMark.Phalan@Sun.COM 	|| &pthread_setspecific == 0
139*7934SMark.Phalan@Sun.COM 	|| &pthread_key_create == 0
140*7934SMark.Phalan@Sun.COM 	|| &pthread_key_delete == 0
141*7934SMark.Phalan@Sun.COM 	|| &pthread_once == 0
142*7934SMark.Phalan@Sun.COM 	|| &pthread_mutex_lock == 0
143*7934SMark.Phalan@Sun.COM 	|| &pthread_mutex_unlock == 0
144*7934SMark.Phalan@Sun.COM 	|| &pthread_mutex_destroy == 0
145*7934SMark.Phalan@Sun.COM 	|| &pthread_mutex_init == 0
146*7934SMark.Phalan@Sun.COM 	|| &pthread_self == 0
147*7934SMark.Phalan@Sun.COM 	|| &pthread_equal == 0
148*7934SMark.Phalan@Sun.COM 	/* Any program that's really multithreaded will have to be
149*7934SMark.Phalan@Sun.COM 	   able to create threads.  */
150*7934SMark.Phalan@Sun.COM 	|| &pthread_create == 0
151*7934SMark.Phalan@Sun.COM 	|| &pthread_join == 0
152*7934SMark.Phalan@Sun.COM 	/* Okay, all the interesting functions -- or stubs for them --
153*7934SMark.Phalan@Sun.COM 	   seem to be present.  If we call pthread_once, does it
154*7934SMark.Phalan@Sun.COM 	   actually seem to cause the indicated function to get called
155*7934SMark.Phalan@Sun.COM 	   exactly one time?  */
156*7934SMark.Phalan@Sun.COM 	|| pthread_once(&loaded_test_once, loaded_test_aux) != 0
157*7934SMark.Phalan@Sun.COM 	|| pthread_once(&loaded_test_once, loaded_test_aux) != 0
158*7934SMark.Phalan@Sun.COM 	/* This catches cases where pthread_once does nothing, and
159*7934SMark.Phalan@Sun.COM 	   never causes the function to get called.  That's a pretty
160*7934SMark.Phalan@Sun.COM 	   clear violation of the POSIX spec, but hey, it happens.  */
161*7934SMark.Phalan@Sun.COM 	|| flag_pthread_loaded < 0) {
162*7934SMark.Phalan@Sun.COM 	flag_pthread_loaded = 0;
163*7934SMark.Phalan@Sun.COM 	return 0;
164*7934SMark.Phalan@Sun.COM     }
165*7934SMark.Phalan@Sun.COM     /* If we wanted to be super-paranoid, we could try testing whether
166*7934SMark.Phalan@Sun.COM        pthread_get/setspecific work, too.  I don't know -- so far --
167*7934SMark.Phalan@Sun.COM        of any system with non-functional stubs for those.  */
168*7934SMark.Phalan@Sun.COM     return flag_pthread_loaded;
169*7934SMark.Phalan@Sun.COM }
170781Sgtb static struct tsd_block tsd_if_single;
171781Sgtb # define GET_NO_PTHREAD_TSD()	(&tsd_if_single)
172781Sgtb #else
173781Sgtb # define GET_NO_PTHREAD_TSD()	(abort(),(struct tsd_block *)0)
174781Sgtb #endif
175781Sgtb 
176781Sgtb static pthread_key_t key;
177781Sgtb static void thread_termination(void *);
178781Sgtb 
thread_termination(void * tptr)179781Sgtb static void thread_termination (void *tptr)
180781Sgtb {
181*7934SMark.Phalan@Sun.COM     int err = k5_mutex_lock(&key_lock);
182*7934SMark.Phalan@Sun.COM     if (err == 0) {
183*7934SMark.Phalan@Sun.COM         int i, pass, none_found;
184*7934SMark.Phalan@Sun.COM         struct tsd_block *t = tptr;
185*7934SMark.Phalan@Sun.COM 
186*7934SMark.Phalan@Sun.COM         /* Make multiple passes in case, for example, a libkrb5 cleanup
187*7934SMark.Phalan@Sun.COM             function wants to print out an error message, which causes
188*7934SMark.Phalan@Sun.COM             com_err to allocate a thread-specific buffer, after we just
189*7934SMark.Phalan@Sun.COM             freed up the old one.
190*7934SMark.Phalan@Sun.COM 
191*7934SMark.Phalan@Sun.COM             Shouldn't actually happen, if we're careful, but check just in
192*7934SMark.Phalan@Sun.COM             case.  */
193*7934SMark.Phalan@Sun.COM 
194*7934SMark.Phalan@Sun.COM         pass = 0;
195*7934SMark.Phalan@Sun.COM         none_found = 0;
196*7934SMark.Phalan@Sun.COM         while (pass < 4 && !none_found) {
197*7934SMark.Phalan@Sun.COM             none_found = 1;
198*7934SMark.Phalan@Sun.COM             for (i = 0; i < K5_KEY_MAX; i++) {
199*7934SMark.Phalan@Sun.COM                 if (destructors_set[i] && destructors[i] && t->values[i]) {
200*7934SMark.Phalan@Sun.COM                     void *v = t->values[i];
201*7934SMark.Phalan@Sun.COM                     t->values[i] = 0;
202*7934SMark.Phalan@Sun.COM                     (*destructors[i])(v);
203*7934SMark.Phalan@Sun.COM                     none_found = 0;
204*7934SMark.Phalan@Sun.COM                 }
205*7934SMark.Phalan@Sun.COM             }
206*7934SMark.Phalan@Sun.COM         }
207*7934SMark.Phalan@Sun.COM         free (t);
208*7934SMark.Phalan@Sun.COM         err = k5_mutex_unlock(&key_lock);
209*7934SMark.Phalan@Sun.COM    }
210*7934SMark.Phalan@Sun.COM 
211781Sgtb     /* remove thread from global linked list */
212781Sgtb }
213781Sgtb 
214781Sgtb #endif /* no threads vs Win32 vs POSIX */
215781Sgtb 
k5_getspecific(k5_key_t keynum)216781Sgtb void *k5_getspecific (k5_key_t keynum)
217781Sgtb {
218781Sgtb     struct tsd_block *t;
219781Sgtb     int err;
220781Sgtb 
221781Sgtb     err = CALL_INIT_FUNCTION(krb5int_thread_support_init);
222781Sgtb     if (err)
223781Sgtb 	return NULL;
224781Sgtb 
225781Sgtb     assert(keynum >= 0 && keynum < K5_KEY_MAX);
226781Sgtb     assert(destructors_set[keynum] == 1);
227781Sgtb 
228781Sgtb #ifndef ENABLE_THREADS
229781Sgtb 
230781Sgtb     t = &tsd_no_threads;
231781Sgtb 
232781Sgtb #elif defined(_WIN32)
233781Sgtb 
234781Sgtb     t = TlsGetValue(tls_idx);
235781Sgtb 
236781Sgtb #else /* POSIX */
237781Sgtb 
238781Sgtb     if (K5_PTHREADS_LOADED)
239781Sgtb 	t = pthread_getspecific(key);
240781Sgtb     else
241781Sgtb 	t = GET_NO_PTHREAD_TSD();
242781Sgtb 
243781Sgtb #endif
244781Sgtb 
245781Sgtb     if (t == NULL)
246781Sgtb 	return NULL;
247781Sgtb     return t->values[keynum];
248781Sgtb }
249781Sgtb 
k5_setspecific(k5_key_t keynum,void * value)250781Sgtb int k5_setspecific (k5_key_t keynum, void *value)
251781Sgtb {
252781Sgtb     struct tsd_block *t;
253781Sgtb     int err;
254781Sgtb 
255781Sgtb     err = CALL_INIT_FUNCTION(krb5int_thread_support_init);
256781Sgtb     if (err)
257781Sgtb 	return err;
258781Sgtb 
259781Sgtb     assert(keynum >= 0 && keynum < K5_KEY_MAX);
260781Sgtb     assert(destructors_set[keynum] == 1);
261781Sgtb 
262781Sgtb #ifndef ENABLE_THREADS
263781Sgtb 
264781Sgtb     t = &tsd_no_threads;
265781Sgtb 
266781Sgtb #elif defined(_WIN32)
267781Sgtb 
268781Sgtb     t = TlsGetValue(tls_idx);
269781Sgtb     if (t == NULL) {
270781Sgtb 	int i;
271781Sgtb 	t = malloc(sizeof(*t));
272781Sgtb 	if (t == NULL)
273781Sgtb 	    return errno;
274781Sgtb 	for (i = 0; i < K5_KEY_MAX; i++)
275781Sgtb 	    t->values[i] = 0;
276781Sgtb 	/* add to global linked list */
277781Sgtb 	/*	t->next = 0; */
278781Sgtb 	err = TlsSetValue(tls_idx, t);
279*7934SMark.Phalan@Sun.COM 	if (!err) {
280781Sgtb 	    free(t);
281*7934SMark.Phalan@Sun.COM 	    return GetLastError();
282781Sgtb 	}
283781Sgtb     }
284781Sgtb 
285781Sgtb #else /* POSIX */
286781Sgtb 
287781Sgtb     if (K5_PTHREADS_LOADED) {
288781Sgtb 	t = pthread_getspecific(key);
289781Sgtb 	if (t == NULL) {
290781Sgtb 	    int i;
291781Sgtb 	    t = malloc(sizeof(*t));
292781Sgtb 	    if (t == NULL)
293781Sgtb 		return errno;
294781Sgtb 	    for (i = 0; i < K5_KEY_MAX; i++)
295781Sgtb 		t->values[i] = 0;
296781Sgtb 	    /* add to global linked list */
297781Sgtb 	    t->next = 0;
298781Sgtb 	    err = pthread_setspecific(key, t);
299781Sgtb 	    if (err) {
300781Sgtb 		free(t);
301781Sgtb 		return err;
302781Sgtb 	    }
303781Sgtb 	}
304781Sgtb     } else {
305781Sgtb 	t = GET_NO_PTHREAD_TSD();
306781Sgtb     }
307781Sgtb 
308781Sgtb #endif
309781Sgtb 
310781Sgtb     t->values[keynum] = value;
311781Sgtb     return 0;
312781Sgtb }
313781Sgtb 
k5_key_register(k5_key_t keynum,void (* destructor)(void *))314781Sgtb int k5_key_register (k5_key_t keynum, void (*destructor)(void *))
315781Sgtb {
316781Sgtb     int err;
317781Sgtb 
318781Sgtb     err = CALL_INIT_FUNCTION(krb5int_thread_support_init);
319781Sgtb     if (err)
320781Sgtb 	return err;
321781Sgtb 
322781Sgtb     assert(keynum >= 0 && keynum < K5_KEY_MAX);
323781Sgtb 
324781Sgtb #ifndef ENABLE_THREADS
325781Sgtb 
326781Sgtb     assert(destructors_set[keynum] == 0);
327781Sgtb     destructors[keynum] = destructor;
328781Sgtb     destructors_set[keynum] = 1;
329781Sgtb     err = 0;
330781Sgtb 
331781Sgtb #elif defined(_WIN32)
332781Sgtb 
333781Sgtb     /* XXX: This can raise EXCEPTION_POSSIBLE_DEADLOCK.  */
334781Sgtb     EnterCriticalSection(&key_lock);
335781Sgtb     assert(destructors_set[keynum] == 0);
336781Sgtb     destructors_set[keynum] = 1;
337781Sgtb     destructors[keynum] = destructor;
338781Sgtb     LeaveCriticalSection(&key_lock);
339781Sgtb     err = 0;
340781Sgtb 
341781Sgtb #else /* POSIX */
342781Sgtb 
343781Sgtb     err = k5_mutex_lock(&key_lock);
344781Sgtb     if (err == 0) {
345781Sgtb 	assert(destructors_set[keynum] == 0);
346781Sgtb 	destructors_set[keynum] = 1;
347781Sgtb 	destructors[keynum] = destructor;
348781Sgtb 	err = k5_mutex_unlock(&key_lock);
349781Sgtb     }
350781Sgtb 
351781Sgtb #endif
352781Sgtb     return 0;
353781Sgtb }
354781Sgtb 
k5_key_delete(k5_key_t keynum)355781Sgtb int k5_key_delete (k5_key_t keynum)
356781Sgtb {
357781Sgtb     assert(keynum >= 0 && keynum < K5_KEY_MAX);
358781Sgtb 
359781Sgtb #ifndef ENABLE_THREADS
360781Sgtb 
361781Sgtb     assert(destructors_set[keynum] == 1);
362781Sgtb     if (destructors[keynum] && tsd_no_threads.values[keynum])
363781Sgtb 	(*destructors[keynum])(tsd_no_threads.values[keynum]);
364781Sgtb     destructors[keynum] = 0;
365781Sgtb     tsd_no_threads.values[keynum] = 0;
366781Sgtb     destructors_set[keynum] = 0;
367781Sgtb 
368781Sgtb #elif defined(_WIN32)
369781Sgtb 
370781Sgtb     /* XXX: This can raise EXCEPTION_POSSIBLE_DEADLOCK.  */
371781Sgtb     EnterCriticalSection(&key_lock);
372781Sgtb     /* XXX Memory leak here!
373781Sgtb        Need to destroy the associated data for all threads.
374781Sgtb        But watch for race conditions in case threads are going away too.  */
375*7934SMark.Phalan@Sun.COM     assert(destructors_set[keynum] == 1);
376*7934SMark.Phalan@Sun.COM     destructors_set[keynum] = 0;
377*7934SMark.Phalan@Sun.COM     destructors[keynum] = 0;
378781Sgtb     LeaveCriticalSection(&key_lock);
379781Sgtb 
380781Sgtb #else /* POSIX */
381781Sgtb 
382*7934SMark.Phalan@Sun.COM     {
383*7934SMark.Phalan@Sun.COM 	int err;
384*7934SMark.Phalan@Sun.COM 
385*7934SMark.Phalan@Sun.COM 	/* XXX RESOURCE LEAK:
386*7934SMark.Phalan@Sun.COM 
387*7934SMark.Phalan@Sun.COM 	   Need to destroy the allocated objects first!  */
388*7934SMark.Phalan@Sun.COM 
389*7934SMark.Phalan@Sun.COM 	err = k5_mutex_lock(&key_lock);
390*7934SMark.Phalan@Sun.COM 	if (err == 0) {
391*7934SMark.Phalan@Sun.COM 	    assert(destructors_set[keynum] == 1);
392*7934SMark.Phalan@Sun.COM 	    destructors_set[keynum] = 0;
393*7934SMark.Phalan@Sun.COM 	    destructors[keynum] = NULL;
394*7934SMark.Phalan@Sun.COM 	    k5_mutex_unlock(&key_lock);
395*7934SMark.Phalan@Sun.COM 	}
396*7934SMark.Phalan@Sun.COM     }
397781Sgtb 
398781Sgtb #endif
399781Sgtb 
400781Sgtb     return 0;
401781Sgtb }
402781Sgtb 
krb5int_call_thread_support_init(void)403781Sgtb int krb5int_call_thread_support_init (void)
404781Sgtb {
405781Sgtb     return CALL_INIT_FUNCTION(krb5int_thread_support_init);
406781Sgtb }
407781Sgtb 
408*7934SMark.Phalan@Sun.COM #include "cache-addrinfo.h"
409*7934SMark.Phalan@Sun.COM 
410*7934SMark.Phalan@Sun.COM #ifdef DEBUG_THREADS_STATS
411*7934SMark.Phalan@Sun.COM #include <stdio.h>
412*7934SMark.Phalan@Sun.COM static FILE *stats_logfile;
413*7934SMark.Phalan@Sun.COM #endif
414781Sgtb 
krb5int_thread_support_init(void)415781Sgtb int krb5int_thread_support_init (void)
416781Sgtb {
417781Sgtb     int err;
418781Sgtb 
419*7934SMark.Phalan@Sun.COM #ifdef SHOW_INITFINI_FUNCS
420*7934SMark.Phalan@Sun.COM     printf("krb5int_thread_support_init\n");
421*7934SMark.Phalan@Sun.COM #endif
422*7934SMark.Phalan@Sun.COM 
423*7934SMark.Phalan@Sun.COM #ifdef DEBUG_THREADS_STATS
424*7934SMark.Phalan@Sun.COM     /*    stats_logfile = stderr; */
425*7934SMark.Phalan@Sun.COM     stats_logfile = fopen("/dev/tty", "w+");
426*7934SMark.Phalan@Sun.COM     if (stats_logfile == NULL)
427*7934SMark.Phalan@Sun.COM       stats_logfile = stderr;
428*7934SMark.Phalan@Sun.COM #endif
429*7934SMark.Phalan@Sun.COM 
430781Sgtb #ifndef ENABLE_THREADS
431781Sgtb 
432781Sgtb     /* Nothing to do for TLS initialization.  */
433781Sgtb 
434781Sgtb #elif defined(_WIN32)
435781Sgtb 
436781Sgtb     tls_idx = TlsAlloc();
437781Sgtb     /* XXX This can raise an exception if memory is low!  */
438781Sgtb     InitializeCriticalSection(&key_lock);
439781Sgtb 
440781Sgtb #else /* POSIX */
441781Sgtb 
442781Sgtb     err = k5_mutex_finish_init(&key_lock);
443781Sgtb     if (err)
444781Sgtb 	return err;
445781Sgtb     if (K5_PTHREADS_LOADED) {
446781Sgtb 	err = pthread_key_create(&key, thread_termination);
447781Sgtb 	if (err)
448781Sgtb 	    return err;
449781Sgtb     }
450781Sgtb 
451781Sgtb #endif
452781Sgtb 
453781Sgtb     err = krb5int_init_fac();
454781Sgtb     if (err)
455781Sgtb 	return err;
456781Sgtb 
457*7934SMark.Phalan@Sun.COM     err = krb5int_err_init();
458*7934SMark.Phalan@Sun.COM     if (err)
459*7934SMark.Phalan@Sun.COM 	return err;
460*7934SMark.Phalan@Sun.COM 
461781Sgtb     return 0;
462781Sgtb }
463781Sgtb 
krb5int_thread_support_fini(void)464781Sgtb void krb5int_thread_support_fini (void)
465781Sgtb {
466781Sgtb     if (! INITIALIZER_RAN (krb5int_thread_support_init))
467781Sgtb 	return;
468781Sgtb 
469*7934SMark.Phalan@Sun.COM #ifdef SHOW_INITFINI_FUNCS
470*7934SMark.Phalan@Sun.COM     printf("krb5int_thread_support_fini\n");
471*7934SMark.Phalan@Sun.COM #endif
472*7934SMark.Phalan@Sun.COM 
473781Sgtb #ifndef ENABLE_THREADS
474781Sgtb 
475781Sgtb     /* Do nothing.  */
476781Sgtb 
477781Sgtb #elif defined(_WIN32)
478781Sgtb 
479781Sgtb     /* ... free stuff ... */
480781Sgtb     TlsFree(tls_idx);
481781Sgtb     DeleteCriticalSection(&key_lock);
482781Sgtb 
483781Sgtb #else /* POSIX */
484781Sgtb 
485781Sgtb     if (! INITIALIZER_RAN(krb5int_thread_support_init))
486781Sgtb 	return;
487781Sgtb     if (K5_PTHREADS_LOADED)
488781Sgtb 	pthread_key_delete(key);
489781Sgtb     /* ... delete stuff ... */
490781Sgtb     k5_mutex_destroy(&key_lock);
491781Sgtb 
492781Sgtb #endif
493781Sgtb 
494*7934SMark.Phalan@Sun.COM #ifdef DEBUG_THREADS_STATS
495*7934SMark.Phalan@Sun.COM     fflush(stats_logfile);
496*7934SMark.Phalan@Sun.COM     /* XXX Should close if not stderr, in case unloading library but
497*7934SMark.Phalan@Sun.COM        not exiting.  */
498*7934SMark.Phalan@Sun.COM #endif
499*7934SMark.Phalan@Sun.COM 
500781Sgtb     krb5int_fini_fac();
501781Sgtb }
502*7934SMark.Phalan@Sun.COM 
503*7934SMark.Phalan@Sun.COM #ifdef DEBUG_THREADS_STATS
504*7934SMark.Phalan@Sun.COM void KRB5_CALLCONV
k5_mutex_lock_update_stats(k5_debug_mutex_stats * m,k5_mutex_stats_tmp startwait)505*7934SMark.Phalan@Sun.COM k5_mutex_lock_update_stats(k5_debug_mutex_stats *m,
506*7934SMark.Phalan@Sun.COM 			   k5_mutex_stats_tmp startwait)
507*7934SMark.Phalan@Sun.COM {
508*7934SMark.Phalan@Sun.COM   k5_debug_time_t now;
509*7934SMark.Phalan@Sun.COM   k5_debug_timediff_t tdiff, tdiff2;
510*7934SMark.Phalan@Sun.COM 
511*7934SMark.Phalan@Sun.COM   now = get_current_time();
512*7934SMark.Phalan@Sun.COM   (void) krb5int_call_thread_support_init();
513*7934SMark.Phalan@Sun.COM   m->count++;
514*7934SMark.Phalan@Sun.COM   m->time_acquired = now;
515*7934SMark.Phalan@Sun.COM   tdiff = timediff(now, startwait);
516*7934SMark.Phalan@Sun.COM   tdiff2 = tdiff * tdiff;
517*7934SMark.Phalan@Sun.COM   if (m->count == 1 || m->lockwait.valmin > tdiff)
518*7934SMark.Phalan@Sun.COM     m->lockwait.valmin = tdiff;
519*7934SMark.Phalan@Sun.COM   if (m->count == 1 || m->lockwait.valmax < tdiff)
520*7934SMark.Phalan@Sun.COM     m->lockwait.valmax = tdiff;
521*7934SMark.Phalan@Sun.COM   m->lockwait.valsum += tdiff;
522*7934SMark.Phalan@Sun.COM   m->lockwait.valsqsum += tdiff2;
523*7934SMark.Phalan@Sun.COM }
524*7934SMark.Phalan@Sun.COM 
525*7934SMark.Phalan@Sun.COM void KRB5_CALLCONV
krb5int_mutex_unlock_update_stats(k5_debug_mutex_stats * m)526*7934SMark.Phalan@Sun.COM krb5int_mutex_unlock_update_stats(k5_debug_mutex_stats *m)
527*7934SMark.Phalan@Sun.COM {
528*7934SMark.Phalan@Sun.COM   k5_debug_time_t now = get_current_time();
529*7934SMark.Phalan@Sun.COM   k5_debug_timediff_t tdiff, tdiff2;
530*7934SMark.Phalan@Sun.COM   tdiff = timediff(now, m->time_acquired);
531*7934SMark.Phalan@Sun.COM   tdiff2 = tdiff * tdiff;
532*7934SMark.Phalan@Sun.COM   if (m->count == 1 || m->lockheld.valmin > tdiff)
533*7934SMark.Phalan@Sun.COM     m->lockheld.valmin = tdiff;
534*7934SMark.Phalan@Sun.COM   if (m->count == 1 || m->lockheld.valmax < tdiff)
535*7934SMark.Phalan@Sun.COM     m->lockheld.valmax = tdiff;
536*7934SMark.Phalan@Sun.COM   m->lockheld.valsum += tdiff;
537*7934SMark.Phalan@Sun.COM   m->lockheld.valsqsum += tdiff2;
538*7934SMark.Phalan@Sun.COM }
539*7934SMark.Phalan@Sun.COM 
540*7934SMark.Phalan@Sun.COM #include <math.h>
541*7934SMark.Phalan@Sun.COM static double
get_stddev(struct k5_timediff_stats sp,int count)542*7934SMark.Phalan@Sun.COM get_stddev(struct k5_timediff_stats sp, int count)
543*7934SMark.Phalan@Sun.COM {
544*7934SMark.Phalan@Sun.COM   long double mu, mu_squared, rho_squared;
545*7934SMark.Phalan@Sun.COM   mu = (long double) sp.valsum / count;
546*7934SMark.Phalan@Sun.COM   mu_squared = mu * mu;
547*7934SMark.Phalan@Sun.COM   /* SUM((x_i - mu)^2)
548*7934SMark.Phalan@Sun.COM      = SUM(x_i^2 - 2*mu*x_i + mu^2)
549*7934SMark.Phalan@Sun.COM      = SUM(x_i^2) - 2*mu*SUM(x_i) + N*mu^2
550*7934SMark.Phalan@Sun.COM 
551*7934SMark.Phalan@Sun.COM      Standard deviation rho^2 = SUM(...) / N.  */
552*7934SMark.Phalan@Sun.COM   rho_squared = (sp.valsqsum - 2 * mu * sp.valsum + count * mu_squared) / count;
553*7934SMark.Phalan@Sun.COM   return sqrt(rho_squared);
554*7934SMark.Phalan@Sun.COM }
555*7934SMark.Phalan@Sun.COM 
556*7934SMark.Phalan@Sun.COM void KRB5_CALLCONV
krb5int_mutex_report_stats(k5_mutex_t * m)557*7934SMark.Phalan@Sun.COM krb5int_mutex_report_stats(k5_mutex_t *m)
558*7934SMark.Phalan@Sun.COM {
559*7934SMark.Phalan@Sun.COM   char *p;
560*7934SMark.Phalan@Sun.COM 
561*7934SMark.Phalan@Sun.COM   /* Tweak this to only record data on "interesting" locks.  */
562*7934SMark.Phalan@Sun.COM   if (m->stats.count < 10)
563*7934SMark.Phalan@Sun.COM     return;
564*7934SMark.Phalan@Sun.COM   if (m->stats.lockwait.valsum < 10 * m->stats.count)
565*7934SMark.Phalan@Sun.COM     return;
566*7934SMark.Phalan@Sun.COM 
567*7934SMark.Phalan@Sun.COM   p = strrchr(m->loc_created.filename, '/');
568*7934SMark.Phalan@Sun.COM   if (p == NULL)
569*7934SMark.Phalan@Sun.COM     p = m->loc_created.filename;
570*7934SMark.Phalan@Sun.COM   else
571*7934SMark.Phalan@Sun.COM     p++;
572*7934SMark.Phalan@Sun.COM   fprintf(stats_logfile, "mutex @%p: created at line %d of %s\n",
573*7934SMark.Phalan@Sun.COM 	  (void *) m, m->loc_created.lineno, p);
574*7934SMark.Phalan@Sun.COM   if (m->stats.count == 0)
575*7934SMark.Phalan@Sun.COM     fprintf(stats_logfile, "\tnever locked\n");
576*7934SMark.Phalan@Sun.COM   else {
577*7934SMark.Phalan@Sun.COM     double sd_wait, sd_hold;
578*7934SMark.Phalan@Sun.COM     sd_wait = get_stddev(m->stats.lockwait, m->stats.count);
579*7934SMark.Phalan@Sun.COM     sd_hold = get_stddev(m->stats.lockheld, m->stats.count);
580*7934SMark.Phalan@Sun.COM     fprintf(stats_logfile,
581*7934SMark.Phalan@Sun.COM 	    "\tlocked %d time%s; wait %lu/%f/%lu/%fus, hold %lu/%f/%lu/%fus\n",
582*7934SMark.Phalan@Sun.COM 	    m->stats.count, m->stats.count == 1 ? "" : "s",
583*7934SMark.Phalan@Sun.COM 	    (unsigned long) m->stats.lockwait.valmin,
584*7934SMark.Phalan@Sun.COM 	    (double) m->stats.lockwait.valsum / m->stats.count,
585*7934SMark.Phalan@Sun.COM 	    (unsigned long) m->stats.lockwait.valmax,
586*7934SMark.Phalan@Sun.COM 	    sd_wait,
587*7934SMark.Phalan@Sun.COM 	    (unsigned long) m->stats.lockheld.valmin,
588*7934SMark.Phalan@Sun.COM 	    (double) m->stats.lockheld.valsum / m->stats.count,
589*7934SMark.Phalan@Sun.COM 	    (unsigned long) m->stats.lockheld.valmax,
590*7934SMark.Phalan@Sun.COM 	    sd_hold);
591*7934SMark.Phalan@Sun.COM   }
592*7934SMark.Phalan@Sun.COM }
593*7934SMark.Phalan@Sun.COM #else
594*7934SMark.Phalan@Sun.COM /* On Windows, everything defined in the export list must be defined.
595*7934SMark.Phalan@Sun.COM    The UNIX systems where we're using the export list don't seem to
596*7934SMark.Phalan@Sun.COM    care.  */
597*7934SMark.Phalan@Sun.COM #undef krb5int_mutex_lock_update_stats
598*7934SMark.Phalan@Sun.COM void KRB5_CALLCONV
krb5int_mutex_lock_update_stats(k5_debug_mutex_stats * m,k5_mutex_stats_tmp startwait)599*7934SMark.Phalan@Sun.COM krb5int_mutex_lock_update_stats(k5_debug_mutex_stats *m,
600*7934SMark.Phalan@Sun.COM 				k5_mutex_stats_tmp startwait)
601*7934SMark.Phalan@Sun.COM {
602*7934SMark.Phalan@Sun.COM }
603*7934SMark.Phalan@Sun.COM #undef krb5int_mutex_unlock_update_stats
604*7934SMark.Phalan@Sun.COM void KRB5_CALLCONV
krb5int_mutex_unlock_update_stats(k5_debug_mutex_stats * m)605*7934SMark.Phalan@Sun.COM krb5int_mutex_unlock_update_stats(k5_debug_mutex_stats *m)
606*7934SMark.Phalan@Sun.COM {
607*7934SMark.Phalan@Sun.COM }
608*7934SMark.Phalan@Sun.COM #undef krb5int_mutex_report_stats
609*7934SMark.Phalan@Sun.COM void KRB5_CALLCONV
krb5int_mutex_report_stats(k5_mutex_t * m)610*7934SMark.Phalan@Sun.COM krb5int_mutex_report_stats(k5_mutex_t *m)
611*7934SMark.Phalan@Sun.COM {
612*7934SMark.Phalan@Sun.COM }
613*7934SMark.Phalan@Sun.COM #endif
614*7934SMark.Phalan@Sun.COM 
6154960Swillf /* Mutex allocation functions, for use in plugins that may not know
6164960Swillf    what options a given set of libraries was compiled with.  */
6174960Swillf int KRB5_CALLCONV
krb5int_mutex_alloc(k5_mutex_t ** m)6184960Swillf krb5int_mutex_alloc (k5_mutex_t **m)
6194960Swillf {
6204960Swillf     k5_mutex_t *ptr;
6214960Swillf     int err;
622781Sgtb 
6234960Swillf     ptr = malloc (sizeof (k5_mutex_t));
6244960Swillf     if (ptr == NULL)
6254960Swillf 	return errno;
6264960Swillf     err = k5_mutex_init (ptr);
6274960Swillf     if (err) {
6284960Swillf 	free (ptr);
6294960Swillf 	return err;
6304960Swillf     }
6314960Swillf     *m = ptr;
6324960Swillf     return 0;
6334960Swillf }
6344960Swillf 
6354960Swillf void KRB5_CALLCONV
krb5int_mutex_free(k5_mutex_t * m)6364960Swillf krb5int_mutex_free (k5_mutex_t *m)
6374960Swillf {
6384960Swillf     (void) k5_mutex_destroy (m);
6394960Swillf     free (m);
6404960Swillf }
6414960Swillf 
6424960Swillf /* Callable versions of the various macros.  */
6434960Swillf int KRB5_CALLCONV
krb5int_mutex_lock(k5_mutex_t * m)6444960Swillf krb5int_mutex_lock (k5_mutex_t *m)
6454960Swillf {
6464960Swillf     return k5_mutex_lock (m);
6474960Swillf }
6484960Swillf int KRB5_CALLCONV
krb5int_mutex_unlock(k5_mutex_t * m)6494960Swillf krb5int_mutex_unlock (k5_mutex_t *m)
6504960Swillf {
6514960Swillf     return k5_mutex_unlock (m);
6524960Swillf }
653