1505d05c7Sgtb /*
2505d05c7Sgtb  * util/support/threads.c
3505d05c7Sgtb  *
4*159d09a2SMark Phalan  * Copyright 2004,2005,2006 by the Massachusetts Institute of Technology.
5505d05c7Sgtb  * All Rights Reserved.
6505d05c7Sgtb  *
7505d05c7Sgtb  * Export of this software from the United States of America may
8505d05c7Sgtb  *   require a specific license from the United States Government.
9505d05c7Sgtb  *   It is the responsibility of any person or organization contemplating
10505d05c7Sgtb  *   export to obtain such a license before exporting.
11505d05c7Sgtb  *
12505d05c7Sgtb  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13505d05c7Sgtb  * distribute this software and its documentation for any purpose and
14505d05c7Sgtb  * without fee is hereby granted, provided that the above copyright
15505d05c7Sgtb  * notice appear in all copies and that both that copyright notice and
16505d05c7Sgtb  * this permission notice appear in supporting documentation, and that
17505d05c7Sgtb  * the name of M.I.T. not be used in advertising or publicity pertaining
18505d05c7Sgtb  * to distribution of the software without specific, written prior
19505d05c7Sgtb  * permission.  Furthermore if you modify this software you must label
20505d05c7Sgtb  * your software as modified software and not distribute it in such a
21505d05c7Sgtb  * fashion that it might be confused with the original M.I.T. software.
22505d05c7Sgtb  * M.I.T. makes no representations about the suitability of
23505d05c7Sgtb  * this software for any purpose.  It is provided "as is" without express
24505d05c7Sgtb  * or implied warranty.
25505d05c7Sgtb  *
26505d05c7Sgtb  *
27505d05c7Sgtb  * Preliminary thread support.
28505d05c7Sgtb  */
29505d05c7Sgtb 
30505d05c7Sgtb #include <assert.h>
31505d05c7Sgtb #include <stdlib.h>
32505d05c7Sgtb #include <errno.h>
33*159d09a2SMark Phalan #include "k5-thread.h"
34*159d09a2SMark Phalan #include "k5-platform.h"
35*159d09a2SMark Phalan #include "supp-int.h"
36505d05c7Sgtb 
37505d05c7Sgtb MAKE_INIT_FUNCTION(krb5int_thread_support_init);
38505d05c7Sgtb MAKE_FINI_FUNCTION(krb5int_thread_support_fini);
39505d05c7Sgtb 
40505d05c7Sgtb #ifndef ENABLE_THREADS /* no thread support */
41505d05c7Sgtb 
42505d05c7Sgtb static void (*destructors[K5_KEY_MAX])(void *);
43505d05c7Sgtb struct tsd_block { void *values[K5_KEY_MAX]; };
44505d05c7Sgtb static struct tsd_block tsd_no_threads;
45505d05c7Sgtb static unsigned char destructors_set[K5_KEY_MAX];
46505d05c7Sgtb 
krb5int_pthread_loaded(void)47*159d09a2SMark Phalan int krb5int_pthread_loaded (void)
48*159d09a2SMark Phalan {
49*159d09a2SMark Phalan     return 0;
50*159d09a2SMark Phalan }
51*159d09a2SMark Phalan 
52505d05c7Sgtb #elif defined(_WIN32)
53505d05c7Sgtb 
54505d05c7Sgtb static DWORD tls_idx;
55505d05c7Sgtb static CRITICAL_SECTION key_lock;
56505d05c7Sgtb struct tsd_block {
57505d05c7Sgtb   void *values[K5_KEY_MAX];
58505d05c7Sgtb };
59505d05c7Sgtb static void (*destructors[K5_KEY_MAX])(void *);
60505d05c7Sgtb static unsigned char destructors_set[K5_KEY_MAX];
61505d05c7Sgtb 
krb5int_thread_detach_hook(void)62505d05c7Sgtb void krb5int_thread_detach_hook (void)
63505d05c7Sgtb {
64505d05c7Sgtb     /* XXX Memory leak here!
65505d05c7Sgtb        Need to destroy all TLS objects we know about for this thread.  */
66505d05c7Sgtb     struct tsd_block *t;
67505d05c7Sgtb     int i, err;
68505d05c7Sgtb 
69505d05c7Sgtb     err = CALL_INIT_FUNCTION(krb5int_thread_support_init);
70505d05c7Sgtb     if (err)
71505d05c7Sgtb 	return;
72505d05c7Sgtb 
73505d05c7Sgtb     t = TlsGetValue(tls_idx);
74505d05c7Sgtb     if (t == NULL)
75505d05c7Sgtb 	return;
76505d05c7Sgtb     for (i = 0; i < K5_KEY_MAX; i++) {
77505d05c7Sgtb 	if (destructors_set[i] && destructors[i] && t->values[i]) {
78505d05c7Sgtb 	    void *v = t->values[i];
79505d05c7Sgtb 	    t->values[i] = 0;
80505d05c7Sgtb 	    (*destructors[i])(v);
81505d05c7Sgtb 	}
82505d05c7Sgtb     }
83505d05c7Sgtb }
84505d05c7Sgtb 
85*159d09a2SMark Phalan /* Stub function not used on Windows. */
krb5int_pthread_loaded(void)86*159d09a2SMark Phalan int krb5int_pthread_loaded (void)
87*159d09a2SMark Phalan {
88*159d09a2SMark Phalan     return 0;
89*159d09a2SMark Phalan }
90505d05c7Sgtb #else /* POSIX threads */
91505d05c7Sgtb 
92505d05c7Sgtb /* Must support register/delete/register sequence, e.g., if krb5 is
93505d05c7Sgtb    loaded so this support code stays in the process, and gssapi is
94505d05c7Sgtb    loaded, unloaded, and loaded again.  */
95505d05c7Sgtb 
96505d05c7Sgtb static k5_mutex_t key_lock = K5_MUTEX_PARTIAL_INITIALIZER;
97505d05c7Sgtb static void (*destructors[K5_KEY_MAX])(void *);
98505d05c7Sgtb static unsigned char destructors_set[K5_KEY_MAX];
99505d05c7Sgtb 
100505d05c7Sgtb /* This is not safe yet!
101505d05c7Sgtb 
102505d05c7Sgtb    Thread termination concurrent with key deletion can cause two
103505d05c7Sgtb    threads to interfere.  It's a bit tricky, since one of the threads
104505d05c7Sgtb    will want to remove this structure from the list being walked by
105505d05c7Sgtb    the other.
106505d05c7Sgtb 
107505d05c7Sgtb    Other cases, like looking up data while the library owning the key
108505d05c7Sgtb    is in the process of being unloaded, we don't worry about.  */
109505d05c7Sgtb 
110505d05c7Sgtb struct tsd_block {
111505d05c7Sgtb     struct tsd_block *next;
112505d05c7Sgtb     void *values[K5_KEY_MAX];
113505d05c7Sgtb };
114505d05c7Sgtb 
115505d05c7Sgtb #ifdef HAVE_PRAGMA_WEAK_REF
116505d05c7Sgtb # pragma weak pthread_getspecific
117505d05c7Sgtb # pragma weak pthread_setspecific
118505d05c7Sgtb # pragma weak pthread_key_create
119505d05c7Sgtb # pragma weak pthread_key_delete
120*159d09a2SMark Phalan # pragma weak pthread_create
121*159d09a2SMark Phalan # pragma weak pthread_join
122*159d09a2SMark Phalan static volatile int flag_pthread_loaded = -1;
loaded_test_aux(void)123*159d09a2SMark Phalan static void loaded_test_aux(void)
124*159d09a2SMark Phalan {
125*159d09a2SMark Phalan     if (flag_pthread_loaded == -1)
126*159d09a2SMark Phalan 	flag_pthread_loaded = 1;
127*159d09a2SMark Phalan     else
128*159d09a2SMark Phalan 	/* Could we have been called twice?  */
129*159d09a2SMark Phalan 	flag_pthread_loaded = 0;
130*159d09a2SMark Phalan }
131*159d09a2SMark Phalan static pthread_once_t loaded_test_once = PTHREAD_ONCE_INIT;
krb5int_pthread_loaded(void)132*159d09a2SMark Phalan int krb5int_pthread_loaded (void)
133*159d09a2SMark Phalan {
134*159d09a2SMark Phalan     int x = flag_pthread_loaded;
135*159d09a2SMark Phalan     if (x != -1)
136*159d09a2SMark Phalan 	return x;
137*159d09a2SMark Phalan     if (&pthread_getspecific == 0
138*159d09a2SMark Phalan 	|| &pthread_setspecific == 0
139*159d09a2SMark Phalan 	|| &pthread_key_create == 0
140*159d09a2SMark Phalan 	|| &pthread_key_delete == 0
141*159d09a2SMark Phalan 	|| &pthread_once == 0
142*159d09a2SMark Phalan 	|| &pthread_mutex_lock == 0
143*159d09a2SMark Phalan 	|| &pthread_mutex_unlock == 0
144*159d09a2SMark Phalan 	|| &pthread_mutex_destroy == 0
145*159d09a2SMark Phalan 	|| &pthread_mutex_init == 0
146*159d09a2SMark Phalan 	|| &pthread_self == 0
147*159d09a2SMark Phalan 	|| &pthread_equal == 0
148*159d09a2SMark Phalan 	/* Any program that's really multithreaded will have to be
149*159d09a2SMark Phalan 	   able to create threads.  */
150*159d09a2SMark Phalan 	|| &pthread_create == 0
151*159d09a2SMark Phalan 	|| &pthread_join == 0
152*159d09a2SMark Phalan 	/* Okay, all the interesting functions -- or stubs for them --
153*159d09a2SMark Phalan 	   seem to be present.  If we call pthread_once, does it
154*159d09a2SMark Phalan 	   actually seem to cause the indicated function to get called
155*159d09a2SMark Phalan 	   exactly one time?  */
156*159d09a2SMark Phalan 	|| pthread_once(&loaded_test_once, loaded_test_aux) != 0
157*159d09a2SMark Phalan 	|| pthread_once(&loaded_test_once, loaded_test_aux) != 0
158*159d09a2SMark Phalan 	/* This catches cases where pthread_once does nothing, and
159*159d09a2SMark Phalan 	   never causes the function to get called.  That's a pretty
160*159d09a2SMark Phalan 	   clear violation of the POSIX spec, but hey, it happens.  */
161*159d09a2SMark Phalan 	|| flag_pthread_loaded < 0) {
162*159d09a2SMark Phalan 	flag_pthread_loaded = 0;
163*159d09a2SMark Phalan 	return 0;
164*159d09a2SMark Phalan     }
165*159d09a2SMark Phalan     /* If we wanted to be super-paranoid, we could try testing whether
166*159d09a2SMark Phalan        pthread_get/setspecific work, too.  I don't know -- so far --
167*159d09a2SMark Phalan        of any system with non-functional stubs for those.  */
168*159d09a2SMark Phalan     return flag_pthread_loaded;
169*159d09a2SMark Phalan }
170505d05c7Sgtb static struct tsd_block tsd_if_single;
171505d05c7Sgtb # define GET_NO_PTHREAD_TSD()	(&tsd_if_single)
172505d05c7Sgtb #else
173505d05c7Sgtb # define GET_NO_PTHREAD_TSD()	(abort(),(struct tsd_block *)0)
174505d05c7Sgtb #endif
175505d05c7Sgtb 
176505d05c7Sgtb static pthread_key_t key;
177505d05c7Sgtb static void thread_termination(void *);
178505d05c7Sgtb 
thread_termination(void * tptr)179505d05c7Sgtb static void thread_termination (void *tptr)
180505d05c7Sgtb {
181*159d09a2SMark Phalan     int err = k5_mutex_lock(&key_lock);
182*159d09a2SMark Phalan     if (err == 0) {
183505d05c7Sgtb         int i, pass, none_found;
184505d05c7Sgtb         struct tsd_block *t = tptr;
185505d05c7Sgtb 
186505d05c7Sgtb         /* Make multiple passes in case, for example, a libkrb5 cleanup
187505d05c7Sgtb             function wants to print out an error message, which causes
188505d05c7Sgtb             com_err to allocate a thread-specific buffer, after we just
189505d05c7Sgtb             freed up the old one.
190505d05c7Sgtb 
191505d05c7Sgtb             Shouldn't actually happen, if we're careful, but check just in
192505d05c7Sgtb             case.  */
193505d05c7Sgtb 
194505d05c7Sgtb         pass = 0;
195505d05c7Sgtb         none_found = 0;
196505d05c7Sgtb         while (pass < 4 && !none_found) {
197505d05c7Sgtb             none_found = 1;
198505d05c7Sgtb             for (i = 0; i < K5_KEY_MAX; i++) {
199505d05c7Sgtb                 if (destructors_set[i] && destructors[i] && t->values[i]) {
200505d05c7Sgtb                     void *v = t->values[i];
201505d05c7Sgtb                     t->values[i] = 0;
202505d05c7Sgtb                     (*destructors[i])(v);
203505d05c7Sgtb                     none_found = 0;
204505d05c7Sgtb                 }
205505d05c7Sgtb             }
206505d05c7Sgtb         }
207*159d09a2SMark Phalan         free (t);
208*159d09a2SMark Phalan         err = k5_mutex_unlock(&key_lock);
209*159d09a2SMark Phalan    }
210*159d09a2SMark Phalan 
211505d05c7Sgtb     /* remove thread from global linked list */
212505d05c7Sgtb }
213505d05c7Sgtb 
214505d05c7Sgtb #endif /* no threads vs Win32 vs POSIX */
215505d05c7Sgtb 
k5_getspecific(k5_key_t keynum)216505d05c7Sgtb void *k5_getspecific (k5_key_t keynum)
217505d05c7Sgtb {
218505d05c7Sgtb     struct tsd_block *t;
219505d05c7Sgtb     int err;
220505d05c7Sgtb 
221505d05c7Sgtb     err = CALL_INIT_FUNCTION(krb5int_thread_support_init);
222505d05c7Sgtb     if (err)
223505d05c7Sgtb 	return NULL;
224505d05c7Sgtb 
225505d05c7Sgtb     assert(keynum >= 0 && keynum < K5_KEY_MAX);
226505d05c7Sgtb     assert(destructors_set[keynum] == 1);
227505d05c7Sgtb 
228505d05c7Sgtb #ifndef ENABLE_THREADS
229505d05c7Sgtb 
230505d05c7Sgtb     t = &tsd_no_threads;
231505d05c7Sgtb 
232505d05c7Sgtb #elif defined(_WIN32)
233505d05c7Sgtb 
234505d05c7Sgtb     t = TlsGetValue(tls_idx);
235505d05c7Sgtb 
236505d05c7Sgtb #else /* POSIX */
237505d05c7Sgtb 
238505d05c7Sgtb     if (K5_PTHREADS_LOADED)
239505d05c7Sgtb 	t = pthread_getspecific(key);
240505d05c7Sgtb     else
241505d05c7Sgtb 	t = GET_NO_PTHREAD_TSD();
242505d05c7Sgtb 
243505d05c7Sgtb #endif
244505d05c7Sgtb 
245505d05c7Sgtb     if (t == NULL)
246505d05c7Sgtb 	return NULL;
247505d05c7Sgtb     return t->values[keynum];
248505d05c7Sgtb }
249505d05c7Sgtb 
k5_setspecific(k5_key_t keynum,void * value)250505d05c7Sgtb int k5_setspecific (k5_key_t keynum, void *value)
251505d05c7Sgtb {
252505d05c7Sgtb     struct tsd_block *t;
253505d05c7Sgtb     int err;
254505d05c7Sgtb 
255505d05c7Sgtb     err = CALL_INIT_FUNCTION(krb5int_thread_support_init);
256505d05c7Sgtb     if (err)
257505d05c7Sgtb 	return err;
258505d05c7Sgtb 
259505d05c7Sgtb     assert(keynum >= 0 && keynum < K5_KEY_MAX);
260505d05c7Sgtb     assert(destructors_set[keynum] == 1);
261505d05c7Sgtb 
262505d05c7Sgtb #ifndef ENABLE_THREADS
263505d05c7Sgtb 
264505d05c7Sgtb     t = &tsd_no_threads;
265505d05c7Sgtb 
266505d05c7Sgtb #elif defined(_WIN32)
267505d05c7Sgtb 
268505d05c7Sgtb     t = TlsGetValue(tls_idx);
269505d05c7Sgtb     if (t == NULL) {
270505d05c7Sgtb 	int i;
271505d05c7Sgtb 	t = malloc(sizeof(*t));
272505d05c7Sgtb 	if (t == NULL)
273505d05c7Sgtb 	    return errno;
274505d05c7Sgtb 	for (i = 0; i < K5_KEY_MAX; i++)
275505d05c7Sgtb 	    t->values[i] = 0;
276505d05c7Sgtb 	/* add to global linked list */
277505d05c7Sgtb 	/*	t->next = 0; */
278505d05c7Sgtb 	err = TlsSetValue(tls_idx, t);
279*159d09a2SMark Phalan 	if (!err) {
280505d05c7Sgtb 	    free(t);
281*159d09a2SMark Phalan 	    return GetLastError();
282505d05c7Sgtb 	}
283505d05c7Sgtb     }
284505d05c7Sgtb 
285505d05c7Sgtb #else /* POSIX */
286505d05c7Sgtb 
287505d05c7Sgtb     if (K5_PTHREADS_LOADED) {
288505d05c7Sgtb 	t = pthread_getspecific(key);
289505d05c7Sgtb 	if (t == NULL) {
290505d05c7Sgtb 	    int i;
291505d05c7Sgtb 	    t = malloc(sizeof(*t));
292505d05c7Sgtb 	    if (t == NULL)
293505d05c7Sgtb 		return errno;
294505d05c7Sgtb 	    for (i = 0; i < K5_KEY_MAX; i++)
295505d05c7Sgtb 		t->values[i] = 0;
296505d05c7Sgtb 	    /* add to global linked list */
297505d05c7Sgtb 	    t->next = 0;
298505d05c7Sgtb 	    err = pthread_setspecific(key, t);
299505d05c7Sgtb 	    if (err) {
300505d05c7Sgtb 		free(t);
301505d05c7Sgtb 		return err;
302505d05c7Sgtb 	    }
303505d05c7Sgtb 	}
304505d05c7Sgtb     } else {
305505d05c7Sgtb 	t = GET_NO_PTHREAD_TSD();
306505d05c7Sgtb     }
307505d05c7Sgtb 
308505d05c7Sgtb #endif
309505d05c7Sgtb 
310505d05c7Sgtb     t->values[keynum] = value;
311505d05c7Sgtb     return 0;
312505d05c7Sgtb }
313505d05c7Sgtb 
k5_key_register(k5_key_t keynum,void (* destructor)(void *))314505d05c7Sgtb int k5_key_register (k5_key_t keynum, void (*destructor)(void *))
315505d05c7Sgtb {
316505d05c7Sgtb     int err;
317505d05c7Sgtb 
318505d05c7Sgtb     err = CALL_INIT_FUNCTION(krb5int_thread_support_init);
319505d05c7Sgtb     if (err)
320505d05c7Sgtb 	return err;
321505d05c7Sgtb 
322505d05c7Sgtb     assert(keynum >= 0 && keynum < K5_KEY_MAX);
323505d05c7Sgtb 
324505d05c7Sgtb #ifndef ENABLE_THREADS
325505d05c7Sgtb 
326505d05c7Sgtb     assert(destructors_set[keynum] == 0);
327505d05c7Sgtb     destructors[keynum] = destructor;
328505d05c7Sgtb     destructors_set[keynum] = 1;
329505d05c7Sgtb     err = 0;
330505d05c7Sgtb 
331505d05c7Sgtb #elif defined(_WIN32)
332505d05c7Sgtb 
333505d05c7Sgtb     /* XXX: This can raise EXCEPTION_POSSIBLE_DEADLOCK.  */
334505d05c7Sgtb     EnterCriticalSection(&key_lock);
335505d05c7Sgtb     assert(destructors_set[keynum] == 0);
336505d05c7Sgtb     destructors_set[keynum] = 1;
337505d05c7Sgtb     destructors[keynum] = destructor;
338505d05c7Sgtb     LeaveCriticalSection(&key_lock);
339505d05c7Sgtb     err = 0;
340505d05c7Sgtb 
341505d05c7Sgtb #else /* POSIX */
342505d05c7Sgtb 
343505d05c7Sgtb     err = k5_mutex_lock(&key_lock);
344505d05c7Sgtb     if (err == 0) {
345505d05c7Sgtb 	assert(destructors_set[keynum] == 0);
346505d05c7Sgtb 	destructors_set[keynum] = 1;
347505d05c7Sgtb 	destructors[keynum] = destructor;
348505d05c7Sgtb 	err = k5_mutex_unlock(&key_lock);
349505d05c7Sgtb     }
350505d05c7Sgtb 
351505d05c7Sgtb #endif
352505d05c7Sgtb     return 0;
353505d05c7Sgtb }
354505d05c7Sgtb 
k5_key_delete(k5_key_t keynum)355505d05c7Sgtb int k5_key_delete (k5_key_t keynum)
356505d05c7Sgtb {
357505d05c7Sgtb     assert(keynum >= 0 && keynum < K5_KEY_MAX);
358505d05c7Sgtb 
359505d05c7Sgtb #ifndef ENABLE_THREADS
360505d05c7Sgtb 
361505d05c7Sgtb     assert(destructors_set[keynum] == 1);
362505d05c7Sgtb     if (destructors[keynum] && tsd_no_threads.values[keynum])
363505d05c7Sgtb 	(*destructors[keynum])(tsd_no_threads.values[keynum]);
364505d05c7Sgtb     destructors[keynum] = 0;
365505d05c7Sgtb     tsd_no_threads.values[keynum] = 0;
366505d05c7Sgtb     destructors_set[keynum] = 0;
367505d05c7Sgtb 
368505d05c7Sgtb #elif defined(_WIN32)
369505d05c7Sgtb 
370505d05c7Sgtb     /* XXX: This can raise EXCEPTION_POSSIBLE_DEADLOCK.  */
371505d05c7Sgtb     EnterCriticalSection(&key_lock);
372505d05c7Sgtb     /* XXX Memory leak here!
373505d05c7Sgtb        Need to destroy the associated data for all threads.
374505d05c7Sgtb        But watch for race conditions in case threads are going away too.  */
375*159d09a2SMark Phalan     assert(destructors_set[keynum] == 1);
376*159d09a2SMark Phalan     destructors_set[keynum] = 0;
377*159d09a2SMark Phalan     destructors[keynum] = 0;
378505d05c7Sgtb     LeaveCriticalSection(&key_lock);
379505d05c7Sgtb 
380505d05c7Sgtb #else /* POSIX */
381505d05c7Sgtb 
382*159d09a2SMark Phalan     {
383*159d09a2SMark Phalan 	int err;
384*159d09a2SMark Phalan 
385*159d09a2SMark Phalan 	/* XXX RESOURCE LEAK:
386*159d09a2SMark Phalan 
387*159d09a2SMark Phalan 	   Need to destroy the allocated objects first!  */
388*159d09a2SMark Phalan 
389*159d09a2SMark Phalan 	err = k5_mutex_lock(&key_lock);
390*159d09a2SMark Phalan 	if (err == 0) {
391*159d09a2SMark Phalan 	    assert(destructors_set[keynum] == 1);
392*159d09a2SMark Phalan 	    destructors_set[keynum] = 0;
393*159d09a2SMark Phalan 	    destructors[keynum] = NULL;
394*159d09a2SMark Phalan 	    k5_mutex_unlock(&key_lock);
395*159d09a2SMark Phalan 	}
396*159d09a2SMark Phalan     }
397505d05c7Sgtb 
398505d05c7Sgtb #endif
399505d05c7Sgtb 
400505d05c7Sgtb     return 0;
401505d05c7Sgtb }
402505d05c7Sgtb 
krb5int_call_thread_support_init(void)403505d05c7Sgtb int krb5int_call_thread_support_init (void)
404505d05c7Sgtb {
405505d05c7Sgtb     return CALL_INIT_FUNCTION(krb5int_thread_support_init);
406505d05c7Sgtb }
407505d05c7Sgtb 
408*159d09a2SMark Phalan #include "cache-addrinfo.h"
409*159d09a2SMark Phalan 
410*159d09a2SMark Phalan #ifdef DEBUG_THREADS_STATS
411*159d09a2SMark Phalan #include <stdio.h>
412*159d09a2SMark Phalan static FILE *stats_logfile;
413*159d09a2SMark Phalan #endif
414505d05c7Sgtb 
krb5int_thread_support_init(void)415505d05c7Sgtb int krb5int_thread_support_init (void)
416505d05c7Sgtb {
417505d05c7Sgtb     int err;
418505d05c7Sgtb 
419*159d09a2SMark Phalan #ifdef SHOW_INITFINI_FUNCS
420*159d09a2SMark Phalan     printf("krb5int_thread_support_init\n");
421*159d09a2SMark Phalan #endif
422*159d09a2SMark Phalan 
423*159d09a2SMark Phalan #ifdef DEBUG_THREADS_STATS
424*159d09a2SMark Phalan     /*    stats_logfile = stderr; */
425*159d09a2SMark Phalan     stats_logfile = fopen("/dev/tty", "w+");
426*159d09a2SMark Phalan     if (stats_logfile == NULL)
427*159d09a2SMark Phalan       stats_logfile = stderr;
428*159d09a2SMark Phalan #endif
429*159d09a2SMark Phalan 
430505d05c7Sgtb #ifndef ENABLE_THREADS
431505d05c7Sgtb 
432505d05c7Sgtb     /* Nothing to do for TLS initialization.  */
433505d05c7Sgtb 
434505d05c7Sgtb #elif defined(_WIN32)
435505d05c7Sgtb 
436505d05c7Sgtb     tls_idx = TlsAlloc();
437505d05c7Sgtb     /* XXX This can raise an exception if memory is low!  */
438505d05c7Sgtb     InitializeCriticalSection(&key_lock);
439505d05c7Sgtb 
440505d05c7Sgtb #else /* POSIX */
441505d05c7Sgtb 
442505d05c7Sgtb     err = k5_mutex_finish_init(&key_lock);
443505d05c7Sgtb     if (err)
444505d05c7Sgtb 	return err;
445505d05c7Sgtb     if (K5_PTHREADS_LOADED) {
446505d05c7Sgtb 	err = pthread_key_create(&key, thread_termination);
447505d05c7Sgtb 	if (err)
448505d05c7Sgtb 	    return err;
449505d05c7Sgtb     }
450505d05c7Sgtb 
451505d05c7Sgtb #endif
452505d05c7Sgtb 
453505d05c7Sgtb     err = krb5int_init_fac();
454505d05c7Sgtb     if (err)
455505d05c7Sgtb 	return err;
456505d05c7Sgtb 
457*159d09a2SMark Phalan     err = krb5int_err_init();
458*159d09a2SMark Phalan     if (err)
459*159d09a2SMark Phalan 	return err;
460*159d09a2SMark Phalan 
461505d05c7Sgtb     return 0;
462505d05c7Sgtb }
463505d05c7Sgtb 
krb5int_thread_support_fini(void)464505d05c7Sgtb void krb5int_thread_support_fini (void)
465505d05c7Sgtb {
466505d05c7Sgtb     if (! INITIALIZER_RAN (krb5int_thread_support_init))
467505d05c7Sgtb 	return;
468505d05c7Sgtb 
469*159d09a2SMark Phalan #ifdef SHOW_INITFINI_FUNCS
470*159d09a2SMark Phalan     printf("krb5int_thread_support_fini\n");
471*159d09a2SMark Phalan #endif
472*159d09a2SMark Phalan 
473505d05c7Sgtb #ifndef ENABLE_THREADS
474505d05c7Sgtb 
475505d05c7Sgtb     /* Do nothing.  */
476505d05c7Sgtb 
477505d05c7Sgtb #elif defined(_WIN32)
478505d05c7Sgtb 
479505d05c7Sgtb     /* ... free stuff ... */
480505d05c7Sgtb     TlsFree(tls_idx);
481505d05c7Sgtb     DeleteCriticalSection(&key_lock);
482505d05c7Sgtb 
483505d05c7Sgtb #else /* POSIX */
484505d05c7Sgtb 
485505d05c7Sgtb     if (! INITIALIZER_RAN(krb5int_thread_support_init))
486505d05c7Sgtb 	return;
487505d05c7Sgtb     if (K5_PTHREADS_LOADED)
488505d05c7Sgtb 	pthread_key_delete(key);
489505d05c7Sgtb     /* ... delete stuff ... */
490505d05c7Sgtb     k5_mutex_destroy(&key_lock);
491505d05c7Sgtb 
492505d05c7Sgtb #endif
493505d05c7Sgtb 
494*159d09a2SMark Phalan #ifdef DEBUG_THREADS_STATS
495*159d09a2SMark Phalan     fflush(stats_logfile);
496*159d09a2SMark Phalan     /* XXX Should close if not stderr, in case unloading library but
497*159d09a2SMark Phalan        not exiting.  */
498*159d09a2SMark Phalan #endif
499*159d09a2SMark Phalan 
500505d05c7Sgtb     krb5int_fini_fac();
501505d05c7Sgtb }
502*159d09a2SMark Phalan 
503*159d09a2SMark Phalan #ifdef DEBUG_THREADS_STATS
504*159d09a2SMark Phalan void KRB5_CALLCONV
k5_mutex_lock_update_stats(k5_debug_mutex_stats * m,k5_mutex_stats_tmp startwait)505*159d09a2SMark Phalan k5_mutex_lock_update_stats(k5_debug_mutex_stats *m,
506*159d09a2SMark Phalan 			   k5_mutex_stats_tmp startwait)
507*159d09a2SMark Phalan {
508*159d09a2SMark Phalan   k5_debug_time_t now;
509*159d09a2SMark Phalan   k5_debug_timediff_t tdiff, tdiff2;
510*159d09a2SMark Phalan 
511*159d09a2SMark Phalan   now = get_current_time();
512*159d09a2SMark Phalan   (void) krb5int_call_thread_support_init();
513*159d09a2SMark Phalan   m->count++;
514*159d09a2SMark Phalan   m->time_acquired = now;
515*159d09a2SMark Phalan   tdiff = timediff(now, startwait);
516*159d09a2SMark Phalan   tdiff2 = tdiff * tdiff;
517*159d09a2SMark Phalan   if (m->count == 1 || m->lockwait.valmin > tdiff)
518*159d09a2SMark Phalan     m->lockwait.valmin = tdiff;
519*159d09a2SMark Phalan   if (m->count == 1 || m->lockwait.valmax < tdiff)
520*159d09a2SMark Phalan     m->lockwait.valmax = tdiff;
521*159d09a2SMark Phalan   m->lockwait.valsum += tdiff;
522*159d09a2SMark Phalan   m->lockwait.valsqsum += tdiff2;
523*159d09a2SMark Phalan }
524*159d09a2SMark Phalan 
525*159d09a2SMark Phalan void KRB5_CALLCONV
krb5int_mutex_unlock_update_stats(k5_debug_mutex_stats * m)526*159d09a2SMark Phalan krb5int_mutex_unlock_update_stats(k5_debug_mutex_stats *m)
527*159d09a2SMark Phalan {
528*159d09a2SMark Phalan   k5_debug_time_t now = get_current_time();
529*159d09a2SMark Phalan   k5_debug_timediff_t tdiff, tdiff2;
530*159d09a2SMark Phalan   tdiff = timediff(now, m->time_acquired);
531*159d09a2SMark Phalan   tdiff2 = tdiff * tdiff;
532*159d09a2SMark Phalan   if (m->count == 1 || m->lockheld.valmin > tdiff)
533*159d09a2SMark Phalan     m->lockheld.valmin = tdiff;
534*159d09a2SMark Phalan   if (m->count == 1 || m->lockheld.valmax < tdiff)
535*159d09a2SMark Phalan     m->lockheld.valmax = tdiff;
536*159d09a2SMark Phalan   m->lockheld.valsum += tdiff;
537*159d09a2SMark Phalan   m->lockheld.valsqsum += tdiff2;
538*159d09a2SMark Phalan }
539*159d09a2SMark Phalan 
540*159d09a2SMark Phalan #include <math.h>
541*159d09a2SMark Phalan static double
get_stddev(struct k5_timediff_stats sp,int count)542*159d09a2SMark Phalan get_stddev(struct k5_timediff_stats sp, int count)
543*159d09a2SMark Phalan {
544*159d09a2SMark Phalan   long double mu, mu_squared, rho_squared;
545*159d09a2SMark Phalan   mu = (long double) sp.valsum / count;
546*159d09a2SMark Phalan   mu_squared = mu * mu;
547*159d09a2SMark Phalan   /* SUM((x_i - mu)^2)
548*159d09a2SMark Phalan      = SUM(x_i^2 - 2*mu*x_i + mu^2)
549*159d09a2SMark Phalan      = SUM(x_i^2) - 2*mu*SUM(x_i) + N*mu^2
550*159d09a2SMark Phalan 
551*159d09a2SMark Phalan      Standard deviation rho^2 = SUM(...) / N.  */
552*159d09a2SMark Phalan   rho_squared = (sp.valsqsum - 2 * mu * sp.valsum + count * mu_squared) / count;
553*159d09a2SMark Phalan   return sqrt(rho_squared);
554*159d09a2SMark Phalan }
555*159d09a2SMark Phalan 
556*159d09a2SMark Phalan void KRB5_CALLCONV
krb5int_mutex_report_stats(k5_mutex_t * m)557*159d09a2SMark Phalan krb5int_mutex_report_stats(k5_mutex_t *m)
558*159d09a2SMark Phalan {
559*159d09a2SMark Phalan   char *p;
560*159d09a2SMark Phalan 
561*159d09a2SMark Phalan   /* Tweak this to only record data on "interesting" locks.  */
562*159d09a2SMark Phalan   if (m->stats.count < 10)
563*159d09a2SMark Phalan     return;
564*159d09a2SMark Phalan   if (m->stats.lockwait.valsum < 10 * m->stats.count)
565*159d09a2SMark Phalan     return;
566*159d09a2SMark Phalan 
567*159d09a2SMark Phalan   p = strrchr(m->loc_created.filename, '/');
568*159d09a2SMark Phalan   if (p == NULL)
569*159d09a2SMark Phalan     p = m->loc_created.filename;
570*159d09a2SMark Phalan   else
571*159d09a2SMark Phalan     p++;
572*159d09a2SMark Phalan   fprintf(stats_logfile, "mutex @%p: created at line %d of %s\n",
573*159d09a2SMark Phalan 	  (void *) m, m->loc_created.lineno, p);
574*159d09a2SMark Phalan   if (m->stats.count == 0)
575*159d09a2SMark Phalan     fprintf(stats_logfile, "\tnever locked\n");
576*159d09a2SMark Phalan   else {
577*159d09a2SMark Phalan     double sd_wait, sd_hold;
578*159d09a2SMark Phalan     sd_wait = get_stddev(m->stats.lockwait, m->stats.count);
579*159d09a2SMark Phalan     sd_hold = get_stddev(m->stats.lockheld, m->stats.count);
580*159d09a2SMark Phalan     fprintf(stats_logfile,
581*159d09a2SMark Phalan 	    "\tlocked %d time%s; wait %lu/%f/%lu/%fus, hold %lu/%f/%lu/%fus\n",
582*159d09a2SMark Phalan 	    m->stats.count, m->stats.count == 1 ? "" : "s",
583*159d09a2SMark Phalan 	    (unsigned long) m->stats.lockwait.valmin,
584*159d09a2SMark Phalan 	    (double) m->stats.lockwait.valsum / m->stats.count,
585*159d09a2SMark Phalan 	    (unsigned long) m->stats.lockwait.valmax,
586*159d09a2SMark Phalan 	    sd_wait,
587*159d09a2SMark Phalan 	    (unsigned long) m->stats.lockheld.valmin,
588*159d09a2SMark Phalan 	    (double) m->stats.lockheld.valsum / m->stats.count,
589*159d09a2SMark Phalan 	    (unsigned long) m->stats.lockheld.valmax,
590*159d09a2SMark Phalan 	    sd_hold);
591*159d09a2SMark Phalan   }
592*159d09a2SMark Phalan }
593*159d09a2SMark Phalan #else
594*159d09a2SMark Phalan /* On Windows, everything defined in the export list must be defined.
595*159d09a2SMark Phalan    The UNIX systems where we're using the export list don't seem to
596*159d09a2SMark Phalan    care.  */
597*159d09a2SMark Phalan #undef krb5int_mutex_lock_update_stats
598*159d09a2SMark Phalan void KRB5_CALLCONV
krb5int_mutex_lock_update_stats(k5_debug_mutex_stats * m,k5_mutex_stats_tmp startwait)599*159d09a2SMark Phalan krb5int_mutex_lock_update_stats(k5_debug_mutex_stats *m,
600*159d09a2SMark Phalan 				k5_mutex_stats_tmp startwait)
601*159d09a2SMark Phalan {
602*159d09a2SMark Phalan }
603*159d09a2SMark Phalan #undef krb5int_mutex_unlock_update_stats
604*159d09a2SMark Phalan void KRB5_CALLCONV
krb5int_mutex_unlock_update_stats(k5_debug_mutex_stats * m)605*159d09a2SMark Phalan krb5int_mutex_unlock_update_stats(k5_debug_mutex_stats *m)
606*159d09a2SMark Phalan {
607*159d09a2SMark Phalan }
608*159d09a2SMark Phalan #undef krb5int_mutex_report_stats
609*159d09a2SMark Phalan void KRB5_CALLCONV
krb5int_mutex_report_stats(k5_mutex_t * m)610*159d09a2SMark Phalan krb5int_mutex_report_stats(k5_mutex_t *m)
611*159d09a2SMark Phalan {
612*159d09a2SMark Phalan }
613*159d09a2SMark Phalan #endif
614*159d09a2SMark Phalan 
61554925bf6Swillf /* Mutex allocation functions, for use in plugins that may not know
61654925bf6Swillf    what options a given set of libraries was compiled with.  */
61754925bf6Swillf int KRB5_CALLCONV
krb5int_mutex_alloc(k5_mutex_t ** m)61854925bf6Swillf krb5int_mutex_alloc (k5_mutex_t **m)
61954925bf6Swillf {
62054925bf6Swillf     k5_mutex_t *ptr;
62154925bf6Swillf     int err;
622505d05c7Sgtb 
62354925bf6Swillf     ptr = malloc (sizeof (k5_mutex_t));
62454925bf6Swillf     if (ptr == NULL)
62554925bf6Swillf 	return errno;
62654925bf6Swillf     err = k5_mutex_init (ptr);
62754925bf6Swillf     if (err) {
62854925bf6Swillf 	free (ptr);
62954925bf6Swillf 	return err;
63054925bf6Swillf     }
63154925bf6Swillf     *m = ptr;
63254925bf6Swillf     return 0;
63354925bf6Swillf }
63454925bf6Swillf 
63554925bf6Swillf void KRB5_CALLCONV
krb5int_mutex_free(k5_mutex_t * m)63654925bf6Swillf krb5int_mutex_free (k5_mutex_t *m)
63754925bf6Swillf {
63854925bf6Swillf     (void) k5_mutex_destroy (m);
63954925bf6Swillf     free (m);
64054925bf6Swillf }
64154925bf6Swillf 
64254925bf6Swillf /* Callable versions of the various macros.  */
64354925bf6Swillf int KRB5_CALLCONV
krb5int_mutex_lock(k5_mutex_t * m)64454925bf6Swillf krb5int_mutex_lock (k5_mutex_t *m)
64554925bf6Swillf {
64654925bf6Swillf     return k5_mutex_lock (m);
64754925bf6Swillf }
64854925bf6Swillf int KRB5_CALLCONV
krb5int_mutex_unlock(k5_mutex_t * m)64954925bf6Swillf krb5int_mutex_unlock (k5_mutex_t *m)
65054925bf6Swillf {
65154925bf6Swillf     return k5_mutex_unlock (m);
65254925bf6Swillf }
653