fix missing synchronization of pthread TSD keys with MT-fork
[musl] / src / thread / pthread_key_create.c
1 #include "pthread_impl.h"
2 #include "fork_impl.h"
3
4 volatile size_t __pthread_tsd_size = sizeof(void *) * PTHREAD_KEYS_MAX;
5 void *__pthread_tsd_main[PTHREAD_KEYS_MAX] = { 0 };
6
7 static void (*keys[PTHREAD_KEYS_MAX])(void *);
8
9 static pthread_rwlock_t key_lock = PTHREAD_RWLOCK_INITIALIZER;
10
11 static pthread_key_t next_key;
12
13 static void nodtor(void *dummy)
14 {
15 }
16
17 static void dummy_0(void)
18 {
19 }
20
21 weak_alias(dummy_0, __tl_lock);
22 weak_alias(dummy_0, __tl_unlock);
23
24 void __pthread_key_atfork(int who)
25 {
26         if (who<0) __pthread_rwlock_rdlock(&key_lock);
27         else if (!who) __pthread_rwlock_unlock(&key_lock);
28         else key_lock = (pthread_rwlock_t)PTHREAD_RWLOCK_INITIALIZER;
29 }
30
31 int __pthread_key_create(pthread_key_t *k, void (*dtor)(void *))
32 {
33         pthread_t self = __pthread_self();
34
35         /* This can only happen in the main thread before
36          * pthread_create has been called. */
37         if (!self->tsd) self->tsd = __pthread_tsd_main;
38
39         /* Purely a sentinel value since null means slot is free. */
40         if (!dtor) dtor = nodtor;
41
42         __pthread_rwlock_wrlock(&key_lock);
43         pthread_key_t j = next_key;
44         do {
45                 if (!keys[j]) {
46                         keys[next_key = *k = j] = dtor;
47                         __pthread_rwlock_unlock(&key_lock);
48                         return 0;
49                 }
50         } while ((j=(j+1)%PTHREAD_KEYS_MAX) != next_key);
51
52         __pthread_rwlock_unlock(&key_lock);
53         return EAGAIN;
54 }
55
56 int __pthread_key_delete(pthread_key_t k)
57 {
58         sigset_t set;
59         pthread_t self = __pthread_self(), td=self;
60
61         __block_app_sigs(&set);
62         __pthread_rwlock_wrlock(&key_lock);
63
64         __tl_lock();
65         do td->tsd[k] = 0;
66         while ((td=td->next)!=self);
67         __tl_unlock();
68
69         keys[k] = 0;
70
71         __pthread_rwlock_unlock(&key_lock);
72         __restore_sigs(&set);
73
74         return 0;
75 }
76
77 void __pthread_tsd_run_dtors()
78 {
79         pthread_t self = __pthread_self();
80         int i, j;
81         for (j=0; self->tsd_used && j<PTHREAD_DESTRUCTOR_ITERATIONS; j++) {
82                 __pthread_rwlock_rdlock(&key_lock);
83                 self->tsd_used = 0;
84                 for (i=0; i<PTHREAD_KEYS_MAX; i++) {
85                         void *val = self->tsd[i];
86                         void (*dtor)(void *) = keys[i];
87                         self->tsd[i] = 0;
88                         if (val && dtor && dtor != nodtor) {
89                                 __pthread_rwlock_unlock(&key_lock);
90                                 dtor(val);
91                                 __pthread_rwlock_rdlock(&key_lock);
92                         }
93                 }
94                 __pthread_rwlock_unlock(&key_lock);
95         }
96 }
97
98 weak_alias(__pthread_key_create, pthread_key_create);
99 weak_alias(__pthread_key_delete, pthread_key_delete);