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