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