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