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