Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/krb5/src/util/support/threads.c
34889 views
1
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
/* util/support/threads.c - Portable thread support */
3
/*
4
* Copyright 2004,2005,2006,2007,2008 by the Massachusetts Institute of
5
* Technology. All Rights Reserved.
6
*
7
* Export of this software from the United States of America may
8
* require a specific license from the United States Government.
9
* It is the responsibility of any person or organization contemplating
10
* export to obtain such a license before exporting.
11
*
12
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13
* distribute this software and its documentation for any purpose and
14
* without fee is hereby granted, provided that the above copyright
15
* notice appear in all copies and that both that copyright notice and
16
* this permission notice appear in supporting documentation, and that
17
* the name of M.I.T. not be used in advertising or publicity pertaining
18
* to distribution of the software without specific, written prior
19
* permission. Furthermore if you modify this software you must label
20
* your software as modified software and not distribute it in such a
21
* fashion that it might be confused with the original M.I.T. software.
22
* M.I.T. makes no representations about the suitability of
23
* this software for any purpose. It is provided "as is" without express
24
* or implied warranty.
25
*/
26
27
#define THREAD_SUPPORT_IMPL
28
#include "k5-platform.h"
29
#include "k5-thread.h"
30
#include "supp-int.h"
31
32
MAKE_INIT_FUNCTION(krb5int_thread_support_init);
33
MAKE_FINI_FUNCTION(krb5int_thread_support_fini);
34
35
/* This function used to be referenced from elsewhere in the tree, but is now
36
* only used internally. Keep it linker-visible for now. */
37
int krb5int_pthread_loaded(void);
38
39
#ifndef ENABLE_THREADS /* no thread support */
40
41
static void (*destructors[K5_KEY_MAX])(void *);
42
struct tsd_block { void *values[K5_KEY_MAX]; };
43
static struct tsd_block tsd_no_threads;
44
static unsigned char destructors_set[K5_KEY_MAX];
45
46
int krb5int_pthread_loaded (void)
47
{
48
return 0;
49
}
50
51
#elif defined(_WIN32)
52
53
static DWORD tls_idx;
54
static CRITICAL_SECTION key_lock;
55
struct tsd_block {
56
void *values[K5_KEY_MAX];
57
};
58
static void (*destructors[K5_KEY_MAX])(void *);
59
static unsigned char destructors_set[K5_KEY_MAX];
60
61
void krb5int_thread_detach_hook (void)
62
{
63
/* XXX Memory leak here!
64
Need to destroy all TLS objects we know about for this thread. */
65
struct tsd_block *t;
66
int i, err;
67
68
err = CALL_INIT_FUNCTION(krb5int_thread_support_init);
69
if (err)
70
return;
71
72
t = TlsGetValue(tls_idx);
73
if (t == NULL)
74
return;
75
for (i = 0; i < K5_KEY_MAX; i++) {
76
if (destructors_set[i] && destructors[i] && t->values[i]) {
77
void *v = t->values[i];
78
t->values[i] = 0;
79
(*destructors[i])(v);
80
}
81
}
82
}
83
84
/* Stub function not used on Windows. */
85
int krb5int_pthread_loaded (void)
86
{
87
return 0;
88
}
89
#else /* POSIX threads */
90
91
/* Must support register/delete/register sequence, e.g., if krb5 is
92
loaded so this support code stays in the process, and gssapi is
93
loaded, unloaded, and loaded again. */
94
95
static k5_mutex_t key_lock = K5_MUTEX_PARTIAL_INITIALIZER;
96
static void (*destructors[K5_KEY_MAX])(void *);
97
static unsigned char destructors_set[K5_KEY_MAX];
98
99
/* This is not safe yet!
100
101
Thread termination concurrent with key deletion can cause two
102
threads to interfere. It's a bit tricky, since one of the threads
103
will want to remove this structure from the list being walked by
104
the other.
105
106
Other cases, like looking up data while the library owning the key
107
is in the process of being unloaded, we don't worry about. */
108
109
struct tsd_block {
110
struct tsd_block *next;
111
void *values[K5_KEY_MAX];
112
};
113
114
#ifdef HAVE_PRAGMA_WEAK_REF
115
# pragma weak pthread_once
116
# pragma weak pthread_mutex_lock
117
# pragma weak pthread_mutex_unlock
118
# pragma weak pthread_mutex_destroy
119
# pragma weak pthread_mutex_init
120
# pragma weak pthread_self
121
# pragma weak pthread_getspecific
122
# pragma weak pthread_setspecific
123
# pragma weak pthread_key_create
124
# pragma weak pthread_key_delete
125
# pragma weak pthread_create
126
# pragma weak pthread_join
127
# define K5_PTHREADS_LOADED (krb5int_pthread_loaded())
128
static volatile int flag_pthread_loaded = -1;
129
static void loaded_test_aux(void)
130
{
131
if (flag_pthread_loaded == -1)
132
flag_pthread_loaded = 1;
133
else
134
/* Could we have been called twice? */
135
flag_pthread_loaded = 0;
136
}
137
static pthread_once_t loaded_test_once = PTHREAD_ONCE_INIT;
138
int krb5int_pthread_loaded (void)
139
{
140
int x = flag_pthread_loaded;
141
if (x != -1)
142
return x;
143
if (&pthread_getspecific == 0
144
|| &pthread_setspecific == 0
145
|| &pthread_key_create == 0
146
|| &pthread_key_delete == 0
147
|| &pthread_once == 0
148
|| &pthread_mutex_lock == 0
149
|| &pthread_mutex_unlock == 0
150
|| &pthread_mutex_destroy == 0
151
|| &pthread_mutex_init == 0
152
|| &pthread_self == 0
153
/* Any program that's really multithreaded will have to be
154
able to create threads. */
155
|| &pthread_create == 0
156
|| &pthread_join == 0
157
/* Okay, all the interesting functions -- or stubs for them --
158
seem to be present. If we call pthread_once, does it
159
actually seem to cause the indicated function to get called
160
exactly one time? */
161
|| pthread_once(&loaded_test_once, loaded_test_aux) != 0
162
|| pthread_once(&loaded_test_once, loaded_test_aux) != 0
163
/* This catches cases where pthread_once does nothing, and
164
never causes the function to get called. That's a pretty
165
clear violation of the POSIX spec, but hey, it happens. */
166
|| flag_pthread_loaded < 0) {
167
flag_pthread_loaded = 0;
168
return 0;
169
}
170
/* If we wanted to be super-paranoid, we could try testing whether
171
pthread_get/setspecific work, too. I don't know -- so far --
172
of any system with non-functional stubs for those. */
173
return flag_pthread_loaded;
174
}
175
176
static struct tsd_block tsd_if_single;
177
# define GET_NO_PTHREAD_TSD() (&tsd_if_single)
178
#else
179
# define K5_PTHREADS_LOADED (1)
180
int krb5int_pthread_loaded (void)
181
{
182
return 1;
183
}
184
185
# define GET_NO_PTHREAD_TSD() (abort(),(struct tsd_block *)0)
186
#endif
187
188
static pthread_key_t key;
189
static void thread_termination(void *);
190
191
static void thread_termination (void *tptr)
192
{
193
int i, pass, none_found;
194
struct tsd_block *t = tptr;
195
196
k5_mutex_lock(&key_lock);
197
198
/*
199
* Make multiple passes in case, for example, a libkrb5 cleanup
200
* function wants to print out an error message, which causes
201
* com_err to allocate a thread-specific buffer, after we just
202
* freed up the old one.
203
*
204
* Shouldn't actually happen, if we're careful, but check just in
205
* case.
206
*/
207
208
pass = 0;
209
none_found = 0;
210
while (pass < 4 && !none_found) {
211
none_found = 1;
212
for (i = 0; i < K5_KEY_MAX; i++) {
213
if (destructors_set[i] && destructors[i] && t->values[i]) {
214
void *v = t->values[i];
215
t->values[i] = 0;
216
(*destructors[i])(v);
217
none_found = 0;
218
}
219
}
220
}
221
free (t);
222
k5_mutex_unlock(&key_lock);
223
224
/* remove thread from global linked list */
225
}
226
227
#endif /* no threads vs Win32 vs POSIX */
228
229
void *k5_getspecific (k5_key_t keynum)
230
{
231
struct tsd_block *t;
232
int err;
233
234
err = CALL_INIT_FUNCTION(krb5int_thread_support_init);
235
if (err)
236
return NULL;
237
238
assert(destructors_set[keynum] == 1);
239
240
#ifndef ENABLE_THREADS
241
242
t = &tsd_no_threads;
243
244
#elif defined(_WIN32)
245
246
t = TlsGetValue(tls_idx);
247
248
#else /* POSIX */
249
250
if (K5_PTHREADS_LOADED)
251
t = pthread_getspecific(key);
252
else
253
t = GET_NO_PTHREAD_TSD();
254
255
#endif
256
257
if (t == NULL)
258
return NULL;
259
return t->values[keynum];
260
}
261
262
int k5_setspecific (k5_key_t keynum, void *value)
263
{
264
struct tsd_block *t;
265
int err;
266
267
err = CALL_INIT_FUNCTION(krb5int_thread_support_init);
268
if (err)
269
return err;
270
271
assert(destructors_set[keynum] == 1);
272
273
#ifndef ENABLE_THREADS
274
275
t = &tsd_no_threads;
276
277
#elif defined(_WIN32)
278
279
t = TlsGetValue(tls_idx);
280
if (t == NULL) {
281
int i;
282
t = malloc(sizeof(*t));
283
if (t == NULL)
284
return ENOMEM;
285
for (i = 0; i < K5_KEY_MAX; i++)
286
t->values[i] = 0;
287
/* add to global linked list */
288
/* t->next = 0; */
289
err = TlsSetValue(tls_idx, t);
290
if (!err) {
291
free(t);
292
return GetLastError();
293
}
294
}
295
296
#else /* POSIX */
297
298
if (K5_PTHREADS_LOADED) {
299
t = pthread_getspecific(key);
300
if (t == NULL) {
301
int i;
302
t = malloc(sizeof(*t));
303
if (t == NULL)
304
return ENOMEM;
305
for (i = 0; i < K5_KEY_MAX; i++)
306
t->values[i] = 0;
307
/* add to global linked list */
308
t->next = 0;
309
err = pthread_setspecific(key, t);
310
if (err) {
311
free(t);
312
return err;
313
}
314
}
315
} else {
316
t = GET_NO_PTHREAD_TSD();
317
}
318
319
#endif
320
321
t->values[keynum] = value;
322
return 0;
323
}
324
325
int k5_key_register (k5_key_t keynum, void (*destructor)(void *))
326
{
327
int err;
328
329
err = CALL_INIT_FUNCTION(krb5int_thread_support_init);
330
if (err)
331
return err;
332
333
#ifndef ENABLE_THREADS
334
335
assert(destructors_set[keynum] == 0);
336
destructors[keynum] = destructor;
337
destructors_set[keynum] = 1;
338
339
#elif defined(_WIN32)
340
341
/* XXX: This can raise EXCEPTION_POSSIBLE_DEADLOCK. */
342
EnterCriticalSection(&key_lock);
343
assert(destructors_set[keynum] == 0);
344
destructors_set[keynum] = 1;
345
destructors[keynum] = destructor;
346
LeaveCriticalSection(&key_lock);
347
348
#else /* POSIX */
349
350
k5_mutex_lock(&key_lock);
351
assert(destructors_set[keynum] == 0);
352
destructors_set[keynum] = 1;
353
destructors[keynum] = destructor;
354
k5_mutex_unlock(&key_lock);
355
356
#endif
357
return 0;
358
}
359
360
int k5_key_delete (k5_key_t keynum)
361
{
362
#ifndef ENABLE_THREADS
363
364
assert(destructors_set[keynum] == 1);
365
if (destructors[keynum] && tsd_no_threads.values[keynum])
366
(*destructors[keynum])(tsd_no_threads.values[keynum]);
367
destructors[keynum] = 0;
368
tsd_no_threads.values[keynum] = 0;
369
destructors_set[keynum] = 0;
370
371
#elif defined(_WIN32)
372
373
/* XXX: This can raise EXCEPTION_POSSIBLE_DEADLOCK. */
374
EnterCriticalSection(&key_lock);
375
/* XXX Memory leak here!
376
Need to destroy the associated data for all threads.
377
But watch for race conditions in case threads are going away too. */
378
assert(destructors_set[keynum] == 1);
379
destructors_set[keynum] = 0;
380
destructors[keynum] = 0;
381
LeaveCriticalSection(&key_lock);
382
383
#else /* POSIX */
384
385
/* XXX RESOURCE LEAK: Need to destroy the allocated objects first! */
386
k5_mutex_lock(&key_lock);
387
assert(destructors_set[keynum] == 1);
388
destructors_set[keynum] = 0;
389
destructors[keynum] = NULL;
390
k5_mutex_unlock(&key_lock);
391
392
#endif
393
394
return 0;
395
}
396
397
int krb5int_call_thread_support_init (void)
398
{
399
return CALL_INIT_FUNCTION(krb5int_thread_support_init);
400
}
401
402
#include "cache-addrinfo.h"
403
404
int krb5int_thread_support_init (void)
405
{
406
int err;
407
408
#ifdef SHOW_INITFINI_FUNCS
409
printf("krb5int_thread_support_init\n");
410
#endif
411
412
#ifndef ENABLE_THREADS
413
414
/* Nothing to do for TLS initialization. */
415
416
#elif defined(_WIN32)
417
418
tls_idx = TlsAlloc();
419
/* XXX This can raise an exception if memory is low! */
420
InitializeCriticalSection(&key_lock);
421
422
#else /* POSIX */
423
424
err = k5_mutex_finish_init(&key_lock);
425
if (err)
426
return err;
427
if (K5_PTHREADS_LOADED) {
428
err = pthread_key_create(&key, thread_termination);
429
if (err)
430
return err;
431
}
432
433
#endif
434
435
err = krb5int_init_fac();
436
if (err)
437
return err;
438
439
err = krb5int_err_init();
440
if (err)
441
return err;
442
443
return 0;
444
}
445
446
void krb5int_thread_support_fini (void)
447
{
448
if (! INITIALIZER_RAN (krb5int_thread_support_init))
449
return;
450
451
#ifdef SHOW_INITFINI_FUNCS
452
printf("krb5int_thread_support_fini\n");
453
#endif
454
455
#ifndef ENABLE_THREADS
456
457
/* Do nothing. */
458
459
#elif defined(_WIN32)
460
461
/* ... free stuff ... */
462
TlsFree(tls_idx);
463
DeleteCriticalSection(&key_lock);
464
465
#else /* POSIX */
466
467
if (! INITIALIZER_RAN(krb5int_thread_support_init))
468
return;
469
if (K5_PTHREADS_LOADED)
470
pthread_key_delete(key);
471
/* ... delete stuff ... */
472
k5_mutex_destroy(&key_lock);
473
474
#endif
475
476
krb5int_fini_fac();
477
}
478
479
/* Mutex allocation functions, for use in plugins that may not know
480
what options a given set of libraries was compiled with. */
481
int KRB5_CALLCONV
482
krb5int_mutex_alloc (k5_mutex_t **m)
483
{
484
k5_mutex_t *ptr;
485
int err;
486
487
ptr = malloc (sizeof (k5_mutex_t));
488
if (ptr == NULL)
489
return ENOMEM;
490
err = k5_mutex_init (ptr);
491
if (err) {
492
free (ptr);
493
return err;
494
}
495
*m = ptr;
496
return 0;
497
}
498
499
void KRB5_CALLCONV
500
krb5int_mutex_free (k5_mutex_t *m)
501
{
502
(void) k5_mutex_destroy (m);
503
free (m);
504
}
505
506
/* Callable versions of the various macros. */
507
void KRB5_CALLCONV
508
krb5int_mutex_lock (k5_mutex_t *m)
509
{
510
k5_mutex_lock (m);
511
}
512
void KRB5_CALLCONV
513
krb5int_mutex_unlock (k5_mutex_t *m)
514
{
515
k5_mutex_unlock (m);
516
}
517
518
#ifdef USE_CONDITIONAL_PTHREADS
519
520
int
521
k5_os_mutex_init(k5_os_mutex *m)
522
{
523
if (krb5int_pthread_loaded())
524
return pthread_mutex_init(m, 0);
525
else
526
return 0;
527
}
528
529
int
530
k5_os_mutex_destroy(k5_os_mutex *m)
531
{
532
if (krb5int_pthread_loaded())
533
return pthread_mutex_destroy(m);
534
else
535
return 0;
536
}
537
538
int
539
k5_os_mutex_lock(k5_os_mutex *m)
540
{
541
if (krb5int_pthread_loaded())
542
return pthread_mutex_lock(m);
543
else
544
return 0;
545
}
546
547
int
548
k5_os_mutex_unlock(k5_os_mutex *m)
549
{
550
if (krb5int_pthread_loaded())
551
return pthread_mutex_unlock(m);
552
else
553
return 0;
554
}
555
556
int
557
k5_once(k5_once_t *once, void (*fn)(void))
558
{
559
if (krb5int_pthread_loaded())
560
return pthread_once(&once->o, fn);
561
else
562
return k5_os_nothread_once(&once->n, fn);
563
}
564
565
#else /* USE_CONDITIONAL_PTHREADS */
566
567
#undef k5_os_mutex_init
568
#undef k5_os_mutex_destroy
569
#undef k5_os_mutex_lock
570
#undef k5_os_mutex_unlock
571
#undef k5_once
572
573
int k5_os_mutex_init(k5_os_mutex *m);
574
int k5_os_mutex_destroy(k5_os_mutex *m);
575
int k5_os_mutex_lock(k5_os_mutex *m);
576
int k5_os_mutex_unlock(k5_os_mutex *m);
577
int k5_once(k5_once_t *once, void (*fn)(void));
578
579
/* Stub functions */
580
int
581
k5_os_mutex_init(k5_os_mutex *m)
582
{
583
return 0;
584
}
585
int
586
k5_os_mutex_destroy(k5_os_mutex *m)
587
{
588
return 0;
589
}
590
int
591
k5_os_mutex_lock(k5_os_mutex *m)
592
{
593
return 0;
594
}
595
int
596
k5_os_mutex_unlock(k5_os_mutex *m)
597
{
598
return 0;
599
}
600
int
601
k5_once(k5_once_t *once, void (*fn)(void))
602
{
603
return 0;
604
}
605
606
#endif /* not USE_CONDITIONAL_PTHREADS */
607
608