1*505d05c7Sgtb #pragma ident	"%Z%%M%	%I%	%E% SMI"
2*505d05c7Sgtb 
3*505d05c7Sgtb /*
4*505d05c7Sgtb  * util/support/threads.c
5*505d05c7Sgtb  *
6*505d05c7Sgtb  * Copyright 2004 by the Massachusetts Institute of Technology.
7*505d05c7Sgtb  * All Rights Reserved.
8*505d05c7Sgtb  *
9*505d05c7Sgtb  * Export of this software from the United States of America may
10*505d05c7Sgtb  *   require a specific license from the United States Government.
11*505d05c7Sgtb  *   It is the responsibility of any person or organization contemplating
12*505d05c7Sgtb  *   export to obtain such a license before exporting.
13*505d05c7Sgtb  *
14*505d05c7Sgtb  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
15*505d05c7Sgtb  * distribute this software and its documentation for any purpose and
16*505d05c7Sgtb  * without fee is hereby granted, provided that the above copyright
17*505d05c7Sgtb  * notice appear in all copies and that both that copyright notice and
18*505d05c7Sgtb  * this permission notice appear in supporting documentation, and that
19*505d05c7Sgtb  * the name of M.I.T. not be used in advertising or publicity pertaining
20*505d05c7Sgtb  * to distribution of the software without specific, written prior
21*505d05c7Sgtb  * permission.  Furthermore if you modify this software you must label
22*505d05c7Sgtb  * your software as modified software and not distribute it in such a
23*505d05c7Sgtb  * fashion that it might be confused with the original M.I.T. software.
24*505d05c7Sgtb  * M.I.T. makes no representations about the suitability of
25*505d05c7Sgtb  * this software for any purpose.  It is provided "as is" without express
26*505d05c7Sgtb  * or implied warranty.
27*505d05c7Sgtb  *
28*505d05c7Sgtb  *
29*505d05c7Sgtb  * Preliminary thread support.
30*505d05c7Sgtb  */
31*505d05c7Sgtb 
32*505d05c7Sgtb #include <assert.h>
33*505d05c7Sgtb #include <stdlib.h>
34*505d05c7Sgtb #include <errno.h>
35*505d05c7Sgtb #include <k5-thread.h>
36*505d05c7Sgtb #include <k5-platform.h>
37*505d05c7Sgtb 
38*505d05c7Sgtb MAKE_INIT_FUNCTION(krb5int_thread_support_init);
39*505d05c7Sgtb MAKE_FINI_FUNCTION(krb5int_thread_support_fini);
40*505d05c7Sgtb 
41*505d05c7Sgtb #ifndef ENABLE_THREADS /* no thread support */
42*505d05c7Sgtb 
43*505d05c7Sgtb static void (*destructors[K5_KEY_MAX])(void *);
44*505d05c7Sgtb struct tsd_block { void *values[K5_KEY_MAX]; };
45*505d05c7Sgtb static struct tsd_block tsd_no_threads;
46*505d05c7Sgtb static unsigned char destructors_set[K5_KEY_MAX];
47*505d05c7Sgtb 
48*505d05c7Sgtb #elif defined(_WIN32)
49*505d05c7Sgtb 
50*505d05c7Sgtb static DWORD tls_idx;
51*505d05c7Sgtb static CRITICAL_SECTION key_lock;
52*505d05c7Sgtb struct tsd_block {
53*505d05c7Sgtb   void *values[K5_KEY_MAX];
54*505d05c7Sgtb };
55*505d05c7Sgtb static void (*destructors[K5_KEY_MAX])(void *);
56*505d05c7Sgtb static unsigned char destructors_set[K5_KEY_MAX];
57*505d05c7Sgtb 
58*505d05c7Sgtb void krb5int_thread_detach_hook (void)
59*505d05c7Sgtb {
60*505d05c7Sgtb     /* XXX Memory leak here!
61*505d05c7Sgtb        Need to destroy all TLS objects we know about for this thread.  */
62*505d05c7Sgtb     struct tsd_block *t;
63*505d05c7Sgtb     int i, err;
64*505d05c7Sgtb 
65*505d05c7Sgtb     err = CALL_INIT_FUNCTION(krb5int_thread_support_init);
66*505d05c7Sgtb     if (err)
67*505d05c7Sgtb 	return;
68*505d05c7Sgtb 
69*505d05c7Sgtb     t = TlsGetValue(tls_idx);
70*505d05c7Sgtb     if (t == NULL)
71*505d05c7Sgtb 	return;
72*505d05c7Sgtb     for (i = 0; i < K5_KEY_MAX; i++) {
73*505d05c7Sgtb 	if (destructors_set[i] && destructors[i] && t->values[i]) {
74*505d05c7Sgtb 	    void *v = t->values[i];
75*505d05c7Sgtb 	    t->values[i] = 0;
76*505d05c7Sgtb 	    (*destructors[i])(v);
77*505d05c7Sgtb 	}
78*505d05c7Sgtb     }
79*505d05c7Sgtb }
80*505d05c7Sgtb 
81*505d05c7Sgtb #else /* POSIX threads */
82*505d05c7Sgtb 
83*505d05c7Sgtb /* Must support register/delete/register sequence, e.g., if krb5 is
84*505d05c7Sgtb    loaded so this support code stays in the process, and gssapi is
85*505d05c7Sgtb    loaded, unloaded, and loaded again.  */
86*505d05c7Sgtb 
87*505d05c7Sgtb static k5_mutex_t key_lock = K5_MUTEX_PARTIAL_INITIALIZER;
88*505d05c7Sgtb static void (*destructors[K5_KEY_MAX])(void *);
89*505d05c7Sgtb static unsigned char destructors_set[K5_KEY_MAX];
90*505d05c7Sgtb 
91*505d05c7Sgtb /* This is not safe yet!
92*505d05c7Sgtb 
93*505d05c7Sgtb    Thread termination concurrent with key deletion can cause two
94*505d05c7Sgtb    threads to interfere.  It's a bit tricky, since one of the threads
95*505d05c7Sgtb    will want to remove this structure from the list being walked by
96*505d05c7Sgtb    the other.
97*505d05c7Sgtb 
98*505d05c7Sgtb    Other cases, like looking up data while the library owning the key
99*505d05c7Sgtb    is in the process of being unloaded, we don't worry about.  */
100*505d05c7Sgtb 
101*505d05c7Sgtb struct tsd_block {
102*505d05c7Sgtb     struct tsd_block *next;
103*505d05c7Sgtb     void *values[K5_KEY_MAX];
104*505d05c7Sgtb };
105*505d05c7Sgtb 
106*505d05c7Sgtb #ifdef HAVE_PRAGMA_WEAK_REF
107*505d05c7Sgtb # pragma weak pthread_getspecific
108*505d05c7Sgtb # pragma weak pthread_setspecific
109*505d05c7Sgtb # pragma weak pthread_key_create
110*505d05c7Sgtb # pragma weak pthread_key_delete
111*505d05c7Sgtb static struct tsd_block tsd_if_single;
112*505d05c7Sgtb # define GET_NO_PTHREAD_TSD()	(&tsd_if_single)
113*505d05c7Sgtb #else
114*505d05c7Sgtb # define GET_NO_PTHREAD_TSD()	(abort(),(struct tsd_block *)0)
115*505d05c7Sgtb #endif
116*505d05c7Sgtb 
117*505d05c7Sgtb static pthread_key_t key;
118*505d05c7Sgtb static void thread_termination(void *);
119*505d05c7Sgtb 
120*505d05c7Sgtb static void thread_termination (void *tptr)
121*505d05c7Sgtb {
122*505d05c7Sgtb     int i, pass, none_found;
123*505d05c7Sgtb     struct tsd_block *t = tptr;
124*505d05c7Sgtb 
125*505d05c7Sgtb     /* Make multiple passes in case, for example, a libkrb5 cleanup
126*505d05c7Sgtb        function wants to print out an error message, which causes
127*505d05c7Sgtb        com_err to allocate a thread-specific buffer, after we just
128*505d05c7Sgtb        freed up the old one.
129*505d05c7Sgtb 
130*505d05c7Sgtb        Shouldn't actually happen, if we're careful, but check just in
131*505d05c7Sgtb        case.  */
132*505d05c7Sgtb 
133*505d05c7Sgtb     pass = 0;
134*505d05c7Sgtb     none_found = 0;
135*505d05c7Sgtb     while (pass < 4 && !none_found) {
136*505d05c7Sgtb 	none_found = 1;
137*505d05c7Sgtb 	for (i = 0; i < K5_KEY_MAX; i++) {
138*505d05c7Sgtb 	    if (destructors_set[i] && destructors[i] && t->values[i]) {
139*505d05c7Sgtb 		void *v = t->values[i];
140*505d05c7Sgtb 		t->values[i] = 0;
141*505d05c7Sgtb 		(*destructors[i])(v);
142*505d05c7Sgtb 		none_found = 0;
143*505d05c7Sgtb 	    }
144*505d05c7Sgtb 	}
145*505d05c7Sgtb     }
146*505d05c7Sgtb     /* remove thread from global linked list */
147*505d05c7Sgtb }
148*505d05c7Sgtb 
149*505d05c7Sgtb #endif /* no threads vs Win32 vs POSIX */
150*505d05c7Sgtb 
151*505d05c7Sgtb void *k5_getspecific (k5_key_t keynum)
152*505d05c7Sgtb {
153*505d05c7Sgtb     struct tsd_block *t;
154*505d05c7Sgtb     int err;
155*505d05c7Sgtb 
156*505d05c7Sgtb     err = CALL_INIT_FUNCTION(krb5int_thread_support_init);
157*505d05c7Sgtb     if (err)
158*505d05c7Sgtb 	return NULL;
159*505d05c7Sgtb 
160*505d05c7Sgtb     assert(keynum >= 0 && keynum < K5_KEY_MAX);
161*505d05c7Sgtb     assert(destructors_set[keynum] == 1);
162*505d05c7Sgtb 
163*505d05c7Sgtb #ifndef ENABLE_THREADS
164*505d05c7Sgtb 
165*505d05c7Sgtb     t = &tsd_no_threads;
166*505d05c7Sgtb 
167*505d05c7Sgtb #elif defined(_WIN32)
168*505d05c7Sgtb 
169*505d05c7Sgtb     t = TlsGetValue(tls_idx);
170*505d05c7Sgtb 
171*505d05c7Sgtb #else /* POSIX */
172*505d05c7Sgtb 
173*505d05c7Sgtb     if (K5_PTHREADS_LOADED)
174*505d05c7Sgtb 	t = pthread_getspecific(key);
175*505d05c7Sgtb     else
176*505d05c7Sgtb 	t = GET_NO_PTHREAD_TSD();
177*505d05c7Sgtb 
178*505d05c7Sgtb #endif
179*505d05c7Sgtb 
180*505d05c7Sgtb     if (t == NULL)
181*505d05c7Sgtb 	return NULL;
182*505d05c7Sgtb     return t->values[keynum];
183*505d05c7Sgtb }
184*505d05c7Sgtb 
185*505d05c7Sgtb int k5_setspecific (k5_key_t keynum, void *value)
186*505d05c7Sgtb {
187*505d05c7Sgtb     struct tsd_block *t;
188*505d05c7Sgtb     int err;
189*505d05c7Sgtb 
190*505d05c7Sgtb     err = CALL_INIT_FUNCTION(krb5int_thread_support_init);
191*505d05c7Sgtb     if (err)
192*505d05c7Sgtb 	return err;
193*505d05c7Sgtb 
194*505d05c7Sgtb     assert(keynum >= 0 && keynum < K5_KEY_MAX);
195*505d05c7Sgtb     assert(destructors_set[keynum] == 1);
196*505d05c7Sgtb 
197*505d05c7Sgtb #ifndef ENABLE_THREADS
198*505d05c7Sgtb 
199*505d05c7Sgtb     t = &tsd_no_threads;
200*505d05c7Sgtb 
201*505d05c7Sgtb #elif defined(_WIN32)
202*505d05c7Sgtb 
203*505d05c7Sgtb     t = TlsGetValue(tls_idx);
204*505d05c7Sgtb     if (t == NULL) {
205*505d05c7Sgtb 	int i;
206*505d05c7Sgtb 	t = malloc(sizeof(*t));
207*505d05c7Sgtb 	if (t == NULL)
208*505d05c7Sgtb 	    return errno;
209*505d05c7Sgtb 	for (i = 0; i < K5_KEY_MAX; i++)
210*505d05c7Sgtb 	    t->values[i] = 0;
211*505d05c7Sgtb 	/* add to global linked list */
212*505d05c7Sgtb 	/*	t->next = 0; */
213*505d05c7Sgtb 	err = TlsSetValue(tls_idx, t);
214*505d05c7Sgtb 	if (err) {
215*505d05c7Sgtb 	    free(t);
216*505d05c7Sgtb 	    return err;
217*505d05c7Sgtb 	}
218*505d05c7Sgtb     }
219*505d05c7Sgtb 
220*505d05c7Sgtb #else /* POSIX */
221*505d05c7Sgtb 
222*505d05c7Sgtb     if (K5_PTHREADS_LOADED) {
223*505d05c7Sgtb 	t = pthread_getspecific(key);
224*505d05c7Sgtb 	if (t == NULL) {
225*505d05c7Sgtb 	    int i;
226*505d05c7Sgtb 	    t = malloc(sizeof(*t));
227*505d05c7Sgtb 	    if (t == NULL)
228*505d05c7Sgtb 		return errno;
229*505d05c7Sgtb 	    for (i = 0; i < K5_KEY_MAX; i++)
230*505d05c7Sgtb 		t->values[i] = 0;
231*505d05c7Sgtb 	    /* add to global linked list */
232*505d05c7Sgtb 	    t->next = 0;
233*505d05c7Sgtb 	    err = pthread_setspecific(key, t);
234*505d05c7Sgtb 	    if (err) {
235*505d05c7Sgtb 		free(t);
236*505d05c7Sgtb 		return err;
237*505d05c7Sgtb 	    }
238*505d05c7Sgtb 	}
239*505d05c7Sgtb     } else {
240*505d05c7Sgtb 	t = GET_NO_PTHREAD_TSD();
241*505d05c7Sgtb     }
242*505d05c7Sgtb 
243*505d05c7Sgtb #endif
244*505d05c7Sgtb 
245*505d05c7Sgtb     t->values[keynum] = value;
246*505d05c7Sgtb     return 0;
247*505d05c7Sgtb }
248*505d05c7Sgtb 
249*505d05c7Sgtb int k5_key_register (k5_key_t keynum, void (*destructor)(void *))
250*505d05c7Sgtb {
251*505d05c7Sgtb     int err;
252*505d05c7Sgtb 
253*505d05c7Sgtb     err = CALL_INIT_FUNCTION(krb5int_thread_support_init);
254*505d05c7Sgtb     if (err)
255*505d05c7Sgtb 	return err;
256*505d05c7Sgtb 
257*505d05c7Sgtb     assert(keynum >= 0 && keynum < K5_KEY_MAX);
258*505d05c7Sgtb 
259*505d05c7Sgtb #ifndef ENABLE_THREADS
260*505d05c7Sgtb 
261*505d05c7Sgtb     assert(destructors_set[keynum] == 0);
262*505d05c7Sgtb     destructors[keynum] = destructor;
263*505d05c7Sgtb     destructors_set[keynum] = 1;
264*505d05c7Sgtb     err = 0;
265*505d05c7Sgtb 
266*505d05c7Sgtb #elif defined(_WIN32)
267*505d05c7Sgtb 
268*505d05c7Sgtb     /* XXX: This can raise EXCEPTION_POSSIBLE_DEADLOCK.  */
269*505d05c7Sgtb     EnterCriticalSection(&key_lock);
270*505d05c7Sgtb     assert(destructors_set[keynum] == 0);
271*505d05c7Sgtb     destructors_set[keynum] = 1;
272*505d05c7Sgtb     destructors[keynum] = destructor;
273*505d05c7Sgtb     LeaveCriticalSection(&key_lock);
274*505d05c7Sgtb     err = 0;
275*505d05c7Sgtb 
276*505d05c7Sgtb #else /* POSIX */
277*505d05c7Sgtb 
278*505d05c7Sgtb     err = k5_mutex_lock(&key_lock);
279*505d05c7Sgtb     if (err == 0) {
280*505d05c7Sgtb 	assert(destructors_set[keynum] == 0);
281*505d05c7Sgtb 	destructors_set[keynum] = 1;
282*505d05c7Sgtb 	destructors[keynum] = destructor;
283*505d05c7Sgtb 	err = k5_mutex_unlock(&key_lock);
284*505d05c7Sgtb     }
285*505d05c7Sgtb 
286*505d05c7Sgtb #endif
287*505d05c7Sgtb     return 0;
288*505d05c7Sgtb }
289*505d05c7Sgtb 
290*505d05c7Sgtb int k5_key_delete (k5_key_t keynum)
291*505d05c7Sgtb {
292*505d05c7Sgtb     assert(keynum >= 0 && keynum < K5_KEY_MAX);
293*505d05c7Sgtb 
294*505d05c7Sgtb #ifndef ENABLE_THREADS
295*505d05c7Sgtb 
296*505d05c7Sgtb     assert(destructors_set[keynum] == 1);
297*505d05c7Sgtb     if (destructors[keynum] && tsd_no_threads.values[keynum])
298*505d05c7Sgtb 	(*destructors[keynum])(tsd_no_threads.values[keynum]);
299*505d05c7Sgtb     destructors[keynum] = 0;
300*505d05c7Sgtb     tsd_no_threads.values[keynum] = 0;
301*505d05c7Sgtb     destructors_set[keynum] = 0;
302*505d05c7Sgtb 
303*505d05c7Sgtb #elif defined(_WIN32)
304*505d05c7Sgtb 
305*505d05c7Sgtb     /* XXX: This can raise EXCEPTION_POSSIBLE_DEADLOCK.  */
306*505d05c7Sgtb     EnterCriticalSection(&key_lock);
307*505d05c7Sgtb     /* XXX Memory leak here!
308*505d05c7Sgtb        Need to destroy the associated data for all threads.
309*505d05c7Sgtb        But watch for race conditions in case threads are going away too.  */
310*505d05c7Sgtb     LeaveCriticalSection(&key_lock);
311*505d05c7Sgtb 
312*505d05c7Sgtb #else /* POSIX */
313*505d05c7Sgtb 
314*505d05c7Sgtb     /* Not written yet.  */
315*505d05c7Sgtb     abort();
316*505d05c7Sgtb 
317*505d05c7Sgtb #endif
318*505d05c7Sgtb 
319*505d05c7Sgtb     return 0;
320*505d05c7Sgtb }
321*505d05c7Sgtb 
322*505d05c7Sgtb int krb5int_call_thread_support_init (void)
323*505d05c7Sgtb {
324*505d05c7Sgtb     return CALL_INIT_FUNCTION(krb5int_thread_support_init);
325*505d05c7Sgtb }
326*505d05c7Sgtb 
327*505d05c7Sgtb extern int krb5int_init_fac(void);
328*505d05c7Sgtb extern void krb5int_fini_fac(void);
329*505d05c7Sgtb 
330*505d05c7Sgtb int krb5int_thread_support_init (void)
331*505d05c7Sgtb {
332*505d05c7Sgtb     int err;
333*505d05c7Sgtb 
334*505d05c7Sgtb #ifndef ENABLE_THREADS
335*505d05c7Sgtb 
336*505d05c7Sgtb     /* Nothing to do for TLS initialization.  */
337*505d05c7Sgtb 
338*505d05c7Sgtb #elif defined(_WIN32)
339*505d05c7Sgtb 
340*505d05c7Sgtb     tls_idx = TlsAlloc();
341*505d05c7Sgtb     /* XXX This can raise an exception if memory is low!  */
342*505d05c7Sgtb     InitializeCriticalSection(&key_lock);
343*505d05c7Sgtb 
344*505d05c7Sgtb #else /* POSIX */
345*505d05c7Sgtb 
346*505d05c7Sgtb     err = k5_mutex_finish_init(&key_lock);
347*505d05c7Sgtb     if (err)
348*505d05c7Sgtb 	return err;
349*505d05c7Sgtb     if (K5_PTHREADS_LOADED) {
350*505d05c7Sgtb 	err = pthread_key_create(&key, thread_termination);
351*505d05c7Sgtb 	if (err)
352*505d05c7Sgtb 	    return err;
353*505d05c7Sgtb     }
354*505d05c7Sgtb 
355*505d05c7Sgtb #endif
356*505d05c7Sgtb 
357*505d05c7Sgtb     err = krb5int_init_fac();
358*505d05c7Sgtb     if (err)
359*505d05c7Sgtb 	return err;
360*505d05c7Sgtb 
361*505d05c7Sgtb     return 0;
362*505d05c7Sgtb }
363*505d05c7Sgtb 
364*505d05c7Sgtb void krb5int_thread_support_fini (void)
365*505d05c7Sgtb {
366*505d05c7Sgtb     if (! INITIALIZER_RAN (krb5int_thread_support_init))
367*505d05c7Sgtb 	return;
368*505d05c7Sgtb 
369*505d05c7Sgtb #ifndef ENABLE_THREADS
370*505d05c7Sgtb 
371*505d05c7Sgtb     /* Do nothing.  */
372*505d05c7Sgtb 
373*505d05c7Sgtb #elif defined(_WIN32)
374*505d05c7Sgtb 
375*505d05c7Sgtb     /* ... free stuff ... */
376*505d05c7Sgtb     TlsFree(tls_idx);
377*505d05c7Sgtb     DeleteCriticalSection(&key_lock);
378*505d05c7Sgtb 
379*505d05c7Sgtb #else /* POSIX */
380*505d05c7Sgtb 
381*505d05c7Sgtb     if (! INITIALIZER_RAN(krb5int_thread_support_init))
382*505d05c7Sgtb 	return;
383*505d05c7Sgtb     if (K5_PTHREADS_LOADED)
384*505d05c7Sgtb 	pthread_key_delete(key);
385*505d05c7Sgtb     /* ... delete stuff ... */
386*505d05c7Sgtb     k5_mutex_destroy(&key_lock);
387*505d05c7Sgtb 
388*505d05c7Sgtb #endif
389*505d05c7Sgtb 
390*505d05c7Sgtb     krb5int_fini_fac();
391*505d05c7Sgtb }
392*505d05c7Sgtb 
393