1 /* $NetBSD: dll.c,v 1.3 2023/06/19 21:41:42 christos Exp $ */
2
3 /***********************************************************************
4 * Copyright (c) 2016 Kungliga Tekniska Högskolan
5 * (Royal Institute of Technology, Stockholm, Sweden).
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
24 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30 * OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 **********************************************************************/
33
34 /*
35 * This is an implementation of thread-specific storage with
36 * destructors. WIN32 doesn't quite have this. Instead it has
37 * DllMain(), an entry point in every DLL that gets called to notify the
38 * DLL of thread/process "attach"/"detach" events.
39 *
40 * We use __thread (or __declspec(thread)) for the thread-local itself
41 * and DllMain() DLL_THREAD_DETACH events to drive destruction of
42 * thread-local values.
43 *
44 * When building in maintainer mode on non-Windows pthread systems this
45 * uses a single pthread key instead to implement multiple keys. This
46 * keeps the code from rotting when modified by non-Windows developers.
47 */
48
49 #include "baselocl.h"
50
51 #ifdef WIN32
52 #include <windows.h>
53 #endif
54
55 #ifdef HEIM_WIN32_TLS
56 #include <assert.h>
57 #include <err.h>
58 #include <heim_threads.h>
59
60 #ifndef WIN32
61 #include <pthread.h>
62 #endif
63
64 /* Logical array of keys that grows lock-lessly */
65 typedef struct tls_keys tls_keys;
66 struct tls_keys {
67 void (**keys_dtors)(void *); /* array of destructors */
68 size_t keys_start_idx; /* index of first destructor */
69 size_t keys_num;
70 tls_keys *keys_next;
71 };
72
73 /*
74 * Well, not quite locklessly. We need synchronization primitives to do
75 * this locklessly. An atomic CAS will do.
76 */
77 static HEIMDAL_MUTEX tls_key_defs_lock = HEIMDAL_MUTEX_INITIALIZER;
78 static tls_keys *tls_key_defs;
79
80 /* Logical array of values (per-thread; no locking needed here) */
81 struct tls_values {
82 void **values; /* realloc()ed */
83 size_t values_num;
84 };
85
86 static HEIMDAL_THREAD_LOCAL struct tls_values values;
87
88 static char dead_key[1];
no_dtor(void * d)89 static void no_dtor(void *d) { (void)d; }
90
91 void
heim_w32_service_thread_detach(void * unused)92 heim_w32_service_thread_detach(void *unused)
93 {
94 tls_keys *key_defs;
95 void (*dtor)(void*);
96 size_t i;
97
98 HEIMDAL_MUTEX_lock(&tls_key_defs_lock);
99 key_defs = tls_key_defs;
100 HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
101
102 if (key_defs == NULL)
103 return;
104
105 for (i = 0; i < values.values_num; i++) {
106 assert(i >= key_defs->keys_start_idx);
107 if (i >= key_defs->keys_start_idx + key_defs->keys_num) {
108 HEIMDAL_MUTEX_lock(&tls_key_defs_lock);
109 key_defs = key_defs->keys_next;
110 HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
111
112 assert(key_defs != NULL);
113 assert(i >= key_defs->keys_start_idx);
114 assert(i < key_defs->keys_start_idx + key_defs->keys_num);
115 }
116 dtor = key_defs->keys_dtors[i - key_defs->keys_start_idx];
117 if (values.values[i] != NULL && dtor != NULL && dtor != no_dtor)
118 dtor(values.values[i]);
119 values.values[i] = NULL;
120 }
121 }
122
123 #if !defined(WIN32)
124 static pthread_key_t pt_key;
125 pthread_once_t pt_once = PTHREAD_ONCE_INIT;
126
127 static void
atexit_del_tls_for_thread(void)128 atexit_del_tls_for_thread(void)
129 {
130 heim_w32_service_thread_detach(NULL);
131 }
132
133 static void
create_pt_key(void)134 create_pt_key(void)
135 {
136 int ret;
137
138 /* The main thread may not execute TLS destructors */
139 atexit(atexit_del_tls_for_thread);
140 ret = pthread_key_create(&pt_key, heim_w32_service_thread_detach);
141 if (ret != 0)
142 err(1, "pthread_key_create() failed");
143 }
144
145 #endif
146
147 int
heim_w32_key_create(HEIM_PRIV_thread_key * key,void (* dtor)(void *))148 heim_w32_key_create(HEIM_PRIV_thread_key *key, void (*dtor)(void *))
149 {
150 tls_keys *key_defs, *new_key_defs;
151 size_t i, k;
152 int ret = ENOMEM;
153
154 #if !defined(WIN32)
155 (void) pthread_once(&pt_once, create_pt_key);
156 (void) pthread_setspecific(pt_key, dead_key);
157 #endif
158
159 HEIMDAL_MUTEX_lock(&tls_key_defs_lock);
160 if (tls_key_defs == NULL) {
161 /* First key */
162 new_key_defs = calloc(1, sizeof(*new_key_defs));
163 if (new_key_defs == NULL) {
164 HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
165 return ENOMEM;
166 }
167 new_key_defs->keys_num = 8;
168 new_key_defs->keys_dtors = calloc(new_key_defs->keys_num,
169 sizeof(*new_key_defs->keys_dtors));
170 if (new_key_defs->keys_dtors == NULL) {
171 HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
172 free(new_key_defs);
173 return ENOMEM;
174 }
175 tls_key_defs = new_key_defs;
176 new_key_defs->keys_dtors[0] = dtor;
177 for (i = 1; i < new_key_defs->keys_num; i++)
178 new_key_defs->keys_dtors[i] = NULL;
179 HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
180 return 0;
181 }
182
183 for (key_defs = tls_key_defs;
184 key_defs != NULL;
185 key_defs = key_defs->keys_next) {
186 k = key_defs->keys_start_idx;
187 for (i = 0; i < key_defs->keys_num; i++, k++) {
188 if (key_defs->keys_dtors[i] == NULL) {
189 /* Found free slot; use it */
190 key_defs->keys_dtors[i] = dtor;
191 *key = k;
192 HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
193 return 0;
194 }
195 }
196 if (key_defs->keys_next != NULL)
197 continue;
198
199 /* Grow the registration array */
200 /* XXX DRY */
201 new_key_defs = calloc(1, sizeof(*new_key_defs));
202 if (new_key_defs == NULL)
203 break;
204
205 new_key_defs->keys_dtors =
206 calloc(key_defs->keys_num + key_defs->keys_num / 2,
207 sizeof(*new_key_defs->keys_dtors));
208 if (new_key_defs->keys_dtors == NULL) {
209 free(new_key_defs);
210 break;
211 }
212 new_key_defs->keys_start_idx = key_defs->keys_start_idx +
213 key_defs->keys_num;
214 new_key_defs->keys_num = key_defs->keys_num + key_defs->keys_num / 2;
215 new_key_defs->keys_dtors[i] = dtor;
216 for (i = 1; i < new_key_defs->keys_num; i++)
217 new_key_defs->keys_dtors[i] = NULL;
218 key_defs->keys_next = new_key_defs;
219 ret = 0;
220 break;
221 }
222 HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
223 return ret;
224 }
225
226 static void
key_lookup(HEIM_PRIV_thread_key key,tls_keys ** kd,size_t * dtor_idx,void (** dtor)(void *))227 key_lookup(HEIM_PRIV_thread_key key, tls_keys **kd,
228 size_t *dtor_idx, void (**dtor)(void *))
229 {
230 tls_keys *key_defs;
231
232 if (kd != NULL)
233 *kd = NULL;
234 if (dtor_idx != NULL)
235 *dtor_idx = 0;
236 if (dtor != NULL)
237 *dtor = NULL;
238
239 HEIMDAL_MUTEX_lock(&tls_key_defs_lock);
240 key_defs = tls_key_defs;
241 HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
242
243 while (key_defs != NULL) {
244 if (key >= key_defs->keys_start_idx &&
245 key < key_defs->keys_start_idx + key_defs->keys_num) {
246 if (kd != NULL)
247 *kd = key_defs;
248 if (dtor_idx != NULL)
249 *dtor_idx = key - key_defs->keys_start_idx;
250 if (dtor != NULL)
251 *dtor = key_defs->keys_dtors[key - key_defs->keys_start_idx];
252 return;
253 }
254
255 HEIMDAL_MUTEX_lock(&tls_key_defs_lock);
256 key_defs = key_defs->keys_next;
257 HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
258 assert(key_defs != NULL);
259 assert(key >= key_defs->keys_start_idx);
260 }
261 }
262
263 int
heim_w32_delete_key(HEIM_PRIV_thread_key key)264 heim_w32_delete_key(HEIM_PRIV_thread_key key)
265 {
266 tls_keys *key_defs;
267 size_t dtor_idx;
268
269 key_lookup(key, &key_defs, &dtor_idx, NULL);
270 if (key_defs == NULL)
271 return EINVAL;
272 key_defs->keys_dtors[dtor_idx] = no_dtor;
273 return 0;
274 }
275
276 int
heim_w32_setspecific(HEIM_PRIV_thread_key key,void * value)277 heim_w32_setspecific(HEIM_PRIV_thread_key key, void *value)
278 {
279 void **new_values;
280 size_t new_num;
281 void (*dtor)(void *);
282 size_t i;
283
284 #if !defined(WIN32)
285 (void) pthread_setspecific(pt_key, dead_key);
286 #endif
287
288 key_lookup(key, NULL, NULL, &dtor);
289 if (dtor == NULL)
290 return EINVAL;
291
292 if (key >= values.values_num) {
293 if (values.values_num == 0) {
294 values.values = NULL;
295 new_num = 8;
296 } else {
297 new_num = (values.values_num + values.values_num / 2);
298 }
299 new_values = realloc(values.values, sizeof(void *) * new_num);
300 if (new_values == NULL)
301 return ENOMEM;
302 for (i = values.values_num; i < new_num; i++)
303 new_values[i] = NULL;
304 values.values = new_values;
305 values.values_num = new_num;
306 }
307
308 assert(key < values.values_num);
309
310 if (values.values[key] != NULL && dtor != NULL && dtor != no_dtor)
311 dtor(values.values[key]);
312
313 values.values[key] = value;
314 return 0;
315 }
316
317 void *
heim_w32_getspecific(HEIM_PRIV_thread_key key)318 heim_w32_getspecific(HEIM_PRIV_thread_key key)
319 {
320 if (key >= values.values_num)
321 return NULL;
322 return values.values[key];
323 }
324
325 #else
326 static char dummy;
327 #endif /* HEIM_WIN32_TLS */
328