xref: /netbsd-src/crypto/external/bsd/heimdal/dist/lib/base/dll.c (revision 82d56013d7b633d116a93943de88e08335357a7c)
1 /*	$NetBSD: dll.c,v 1.2 2017/01/28 21:31:45 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 #define DEAD_KEY ((void *)8)
89 
90 void
91 heim_w32_service_thread_detach(void *unused)
92 {
93     tls_keys *key_defs;
94     void (*dtor)(void*);
95     size_t i;
96 
97     HEIMDAL_MUTEX_lock(&tls_key_defs_lock);
98     key_defs = tls_key_defs;
99     HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
100 
101     if (key_defs == NULL)
102         return;
103 
104     for (i = 0; i < values.values_num; i++) {
105         assert(i >= key_defs->keys_start_idx);
106         if (i >= key_defs->keys_start_idx + key_defs->keys_num) {
107             HEIMDAL_MUTEX_lock(&tls_key_defs_lock);
108             key_defs = key_defs->keys_next;
109             HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
110 
111             assert(key_defs != NULL);
112             assert(i >= key_defs->keys_start_idx);
113             assert(i < key_defs->keys_start_idx + key_defs->keys_num);
114         }
115         dtor = key_defs->keys_dtors[i - key_defs->keys_start_idx];
116         if (values.values[i] != NULL && dtor != NULL && dtor != DEAD_KEY)
117             dtor(values.values[i]);
118         values.values[i] = NULL;
119     }
120 }
121 
122 #if !defined(WIN32)
123 static pthread_key_t pt_key;
124 pthread_once_t pt_once = PTHREAD_ONCE_INIT;
125 
126 static void
127 atexit_del_tls_for_thread(void)
128 {
129     heim_w32_service_thread_detach(NULL);
130 }
131 
132 static void
133 create_pt_key(void)
134 {
135     int ret;
136 
137     /* The main thread may not execute TLS destructors */
138     atexit(atexit_del_tls_for_thread);
139     ret = pthread_key_create(&pt_key, heim_w32_service_thread_detach);
140     if (ret != 0)
141         err(1, "pthread_key_create() failed");
142 }
143 
144 #endif
145 
146 int
147 heim_w32_key_create(HEIM_PRIV_thread_key *key, void (*dtor)(void *))
148 {
149     tls_keys *key_defs, *new_key_defs;
150     size_t i, k;
151     int ret = ENOMEM;
152 
153 #if !defined(WIN32)
154     (void) pthread_once(&pt_once, create_pt_key);
155     (void) pthread_setspecific(pt_key, DEAD_KEY);
156 #endif
157 
158     HEIMDAL_MUTEX_lock(&tls_key_defs_lock);
159     if (tls_key_defs == NULL) {
160         /* First key */
161         new_key_defs = calloc(1, sizeof(*new_key_defs));
162         if (new_key_defs == NULL) {
163             HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
164             return ENOMEM;
165         }
166         new_key_defs->keys_num = 8;
167         new_key_defs->keys_dtors = calloc(new_key_defs->keys_num,
168                                           sizeof(*new_key_defs->keys_dtors));
169         if (new_key_defs->keys_dtors == NULL) {
170             HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
171             free(new_key_defs);
172             return ENOMEM;
173         }
174         tls_key_defs = new_key_defs;
175         new_key_defs->keys_dtors[0] = dtor;
176         for (i = 1; i < new_key_defs->keys_num; i++)
177             new_key_defs->keys_dtors[i] = NULL;
178         HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
179         return 0;
180     }
181 
182     for (key_defs = tls_key_defs;
183          key_defs != NULL;
184          key_defs = key_defs->keys_next) {
185         k = key_defs->keys_start_idx;
186         for (i = 0; i < key_defs->keys_num; i++, k++) {
187             if (key_defs->keys_dtors[i] == NULL) {
188                 /* Found free slot; use it */
189                 key_defs->keys_dtors[i] = dtor;
190                 *key = k;
191                 HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
192                 return 0;
193             }
194         }
195         if (key_defs->keys_next != NULL)
196             continue;
197 
198         /* Grow the registration array */
199         /* XXX DRY */
200         new_key_defs = calloc(1, sizeof(*new_key_defs));
201         if (new_key_defs == NULL)
202             break;
203 
204         new_key_defs->keys_dtors =
205             calloc(key_defs->keys_num + key_defs->keys_num / 2,
206                    sizeof(*new_key_defs->keys_dtors));
207         if (new_key_defs->keys_dtors == NULL) {
208             free(new_key_defs);
209             break;
210         }
211         new_key_defs->keys_start_idx = key_defs->keys_start_idx +
212             key_defs->keys_num;
213         new_key_defs->keys_num = key_defs->keys_num + key_defs->keys_num / 2;
214         new_key_defs->keys_dtors[i] = dtor;
215         for (i = 1; i < new_key_defs->keys_num; i++)
216             new_key_defs->keys_dtors[i] = NULL;
217         key_defs->keys_next = new_key_defs;
218         ret = 0;
219         break;
220     }
221     HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
222     return ret;
223 }
224 
225 static void
226 key_lookup(HEIM_PRIV_thread_key key, tls_keys **kd,
227            size_t *dtor_idx, void (**dtor)(void *))
228 {
229     tls_keys *key_defs;
230 
231     if (kd != NULL)
232         *kd = NULL;
233     if (dtor_idx != NULL)
234         *dtor_idx = 0;
235     if (dtor != NULL)
236         *dtor = NULL;
237 
238     HEIMDAL_MUTEX_lock(&tls_key_defs_lock);
239     key_defs = tls_key_defs;
240     HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
241 
242     while (key_defs != NULL) {
243         if (key >= key_defs->keys_start_idx &&
244             key < key_defs->keys_start_idx + key_defs->keys_num) {
245             if (kd != NULL)
246                 *kd = key_defs;
247             if (dtor_idx != NULL)
248                 *dtor_idx = key - key_defs->keys_start_idx;
249             if (dtor != NULL)
250                 *dtor = key_defs->keys_dtors[key - key_defs->keys_start_idx];
251             return;
252         }
253 
254         HEIMDAL_MUTEX_lock(&tls_key_defs_lock);
255         key_defs = key_defs->keys_next;
256         HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
257         assert(key_defs != NULL);
258         assert(key >= key_defs->keys_start_idx);
259     }
260 }
261 
262 int
263 heim_w32_delete_key(HEIM_PRIV_thread_key key)
264 {
265     tls_keys *key_defs;
266     size_t dtor_idx;
267 
268     key_lookup(key, &key_defs, &dtor_idx, NULL);
269     if (key_defs == NULL)
270         return EINVAL;
271     key_defs->keys_dtors[dtor_idx] = DEAD_KEY;
272     return 0;
273 }
274 
275 int
276 heim_w32_setspecific(HEIM_PRIV_thread_key key, void *value)
277 {
278     void **new_values;
279     size_t new_num;
280     void (*dtor)(void *);
281     size_t i;
282 
283 #if !defined(WIN32)
284     (void) pthread_setspecific(pt_key, DEAD_KEY);
285 #endif
286 
287     key_lookup(key, NULL, NULL, &dtor);
288     if (dtor == NULL)
289         return EINVAL;
290 
291     if (key >= values.values_num) {
292         if (values.values_num == 0) {
293             values.values = NULL;
294             new_num = 8;
295         } else {
296             new_num = (values.values_num + values.values_num / 2);
297         }
298         new_values = realloc(values.values, sizeof(void *) * new_num);
299         if (new_values == NULL)
300             return ENOMEM;
301         for (i = values.values_num; i < new_num; i++)
302             new_values[i] = NULL;
303         values.values = new_values;
304         values.values_num = new_num;
305     }
306 
307     assert(key < values.values_num);
308 
309     if (values.values[key] != NULL && dtor != NULL && dtor != DEAD_KEY)
310         dtor(values.values[key]);
311 
312     values.values[key] = value;
313     return 0;
314 }
315 
316 void *
317 heim_w32_getspecific(HEIM_PRIV_thread_key key)
318 {
319     if (key >= values.values_num)
320         return NULL;
321     return values.values[key];
322 }
323 
324 #else
325 static char dummy;
326 #endif /* HEIM_WIN32_TLS */
327