0903d48f3f5c36cdf3f10cdc25593703195390e9
[musl] / src / thread / pthread_key_create.c
1 #include "pthread_impl.h"
2
3 volatile size_t __pthread_tsd_size = sizeof(void *) * PTHREAD_KEYS_MAX;
4 void *__pthread_tsd_main[PTHREAD_KEYS_MAX] = { 0 };
5
6 static void (*keys[PTHREAD_KEYS_MAX])(void *);
7
8 static pthread_rwlock_t key_lock = PTHREAD_RWLOCK_INITIALIZER;
9
10 static pthread_key_t next_key;
11
12 static void nodtor(void *dummy)
13 {
14 }
15
16 static void dirty(void *dummy)
17 {
18 }
19
20 struct cleanup_args {
21         pthread_t caller;
22         int ret;
23 };
24
25 static void clean_dirty_tsd_callback(void *p)
26 {
27         struct cleanup_args *args = p;
28         pthread_t self = __pthread_self();
29         pthread_key_t i;
30         for (i=0; i<PTHREAD_KEYS_MAX; i++) {
31                 if (keys[i] == dirty && self->tsd[i])
32                         self->tsd[i] = 0;
33         }
34         /* Arbitrary choice to avoid data race. */
35         if (args->caller == self) args->ret = 0;
36 }
37
38 static void dummy2(void (*f)(void *), void *p)
39 {
40 }
41
42 weak_alias(dummy2, __pthread_key_delete_synccall);
43
44 static int clean_dirty_tsd(void)
45 {
46         struct cleanup_args args = {
47                 .caller = __pthread_self(),
48                 .ret = EAGAIN
49         };
50         __pthread_key_delete_synccall(clean_dirty_tsd_callback, &args);
51         return args.ret;
52 }
53
54 int __pthread_key_create(pthread_key_t *k, void (*dtor)(void *))
55 {
56         pthread_key_t j = next_key;
57         pthread_t self = __pthread_self();
58         int found_dirty = 0;
59
60         /* This can only happen in the main thread before
61          * pthread_create has been called. */
62         if (!self->tsd) self->tsd = __pthread_tsd_main;
63
64         /* Purely a sentinel value since null means slot is free. */
65         if (!dtor) dtor = nodtor;
66
67         pthread_rwlock_wrlock(&key_lock);
68         do {
69                 if (!keys[j]) {
70                         keys[next_key = *k = j] = dtor;
71                         pthread_rwlock_unlock(&key_lock);
72                         return 0;
73                 } else if (keys[j] == dirty) {
74                         found_dirty = 1;
75                 }
76         } while ((j=(j+1)%PTHREAD_KEYS_MAX) != next_key);
77
78         /* It's possible that all slots are in use or __synccall fails. */
79         if (!found_dirty || clean_dirty_tsd()) {
80                 pthread_rwlock_unlock(&key_lock);
81                 return EAGAIN;
82         }
83
84         /* If this point is reached there is necessarily a newly-cleaned
85          * slot to allocate to satisfy the caller's request. Find it and
86          * mark any additional previously-dirty slots clean. */
87         for (j=0; j<PTHREAD_KEYS_MAX; j++) {
88                 if (keys[j] == dirty) {
89                         if (dtor) {
90                                 keys[next_key = *k = j] = dtor;
91                                 dtor = 0;
92                         } else {
93                                 keys[j] = 0;
94                         }
95                 }
96         }
97
98         pthread_rwlock_unlock(&key_lock);
99         return 0;
100 }
101
102 int __pthread_key_delete_impl(pthread_key_t k)
103 {
104         pthread_rwlock_wrlock(&key_lock);
105         keys[k] = dirty;
106         pthread_rwlock_unlock(&key_lock);
107         return 0;
108 }
109
110 void __pthread_tsd_run_dtors()
111 {
112         pthread_t self = __pthread_self();
113         int i, j;
114         for (j=0; self->tsd_used && j<PTHREAD_DESTRUCTOR_ITERATIONS; j++) {
115                 pthread_rwlock_rdlock(&key_lock);
116                 self->tsd_used = 0;
117                 for (i=0; i<PTHREAD_KEYS_MAX; i++) {
118                         void *val = self->tsd[i];
119                         void (*dtor)(void *) = keys[i];
120                         self->tsd[i] = 0;
121                         if (val && dtor && dtor != nodtor && dtor != dirty) {
122                                 pthread_rwlock_unlock(&key_lock);
123                                 dtor(val);
124                                 pthread_rwlock_rdlock(&key_lock);
125                         }
126                 }
127                 pthread_rwlock_unlock(&key_lock);
128         }
129 }
130
131 weak_alias(__pthread_key_create, pthread_key_create);