HEIMDAL: move code from source4/heimdal* to third_party/heimdal*
[samba.git] / third_party / heimdal / lib / base / dll.c
1 /***********************************************************************
2  * Copyright (c) 2016 Kungliga Tekniska Högskolan
3  * (Royal Institute of Technology, Stockholm, Sweden).
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
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.
16  *
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.
29  *
30  **********************************************************************/
31
32 /*
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.
37  *
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.
41  *
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.
45  */
46
47 #include "baselocl.h"
48
49 #ifdef WIN32
50 #include <windows.h>
51 #endif
52
53 #ifdef HEIM_WIN32_TLS
54 #include <assert.h>
55 #include <err.h>
56 #include <heim_threads.h>
57
58 #ifndef WIN32
59 #include <pthread.h>
60 #endif
61
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;
69 };
70
71 /*
72  * Well, not quite locklessly.  We need synchronization primitives to do
73  * this locklessly.  An atomic CAS will do.
74  */
75 static HEIMDAL_MUTEX tls_key_defs_lock = HEIMDAL_MUTEX_INITIALIZER;
76 static tls_keys *tls_key_defs;
77
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;
82 };
83
84 static HEIMDAL_THREAD_LOCAL struct tls_values values;
85
86 #define DEAD_KEY ((void *)8)
87
88 void
89 heim_w32_service_thread_detach(void *unused)
90 {
91     tls_keys *key_defs;
92     void (*dtor)(void*);
93     size_t i;
94
95     HEIMDAL_MUTEX_lock(&tls_key_defs_lock);
96     key_defs = tls_key_defs;
97     HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
98
99     if (key_defs == NULL)
100         return;
101
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);
108
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);
112         }
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;
117     }
118 }
119
120 #if !defined(WIN32)
121 static pthread_key_t pt_key;
122 pthread_once_t pt_once = PTHREAD_ONCE_INIT;
123
124 static void
125 atexit_del_tls_for_thread(void)
126 {
127     heim_w32_service_thread_detach(NULL);
128 }
129
130 static void
131 create_pt_key(void)
132 {
133     int ret;
134
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");
140 }
141
142 #endif
143
144 int
145 heim_w32_key_create(HEIM_PRIV_thread_key *key, void (*dtor)(void *))
146 {
147     tls_keys *key_defs, *new_key_defs;
148     size_t i, k;
149     int ret = ENOMEM;
150
151 #if !defined(WIN32)
152     (void) pthread_once(&pt_once, create_pt_key);
153     (void) pthread_setspecific(pt_key, DEAD_KEY);
154 #endif
155
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;
163         }
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;
171         }
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;
178     }
179
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;
191             }
192         }
193         if (key_defs->keys_next != NULL)
194             continue;
195
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;
201
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;
208         }
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;
218     }
219     HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
220     return ret;
221 }
222
223 static void
224 key_lookup(HEIM_PRIV_thread_key key, tls_keys **kd,
225            size_t *dtor_idx, void (**dtor)(void *))
226 {
227     tls_keys *key_defs;
228
229     if (kd != NULL)
230         *kd = NULL;
231     if (dtor_idx != NULL)
232         *dtor_idx = 0;
233     if (dtor != NULL)
234         *dtor = NULL;
235
236     HEIMDAL_MUTEX_lock(&tls_key_defs_lock);
237     key_defs = tls_key_defs;
238     HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
239
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;
250         }
251
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);
257     }
258 }
259
260 int
261 heim_w32_delete_key(HEIM_PRIV_thread_key key)
262 {
263     tls_keys *key_defs;
264     size_t dtor_idx;
265
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;
271 }
272
273 int
274 heim_w32_setspecific(HEIM_PRIV_thread_key key, void *value)
275 {
276     void **new_values;
277     size_t new_num;
278     void (*dtor)(void *);
279     size_t i;
280
281 #if !defined(WIN32)
282     (void) pthread_setspecific(pt_key, DEAD_KEY);
283 #endif
284
285     key_lookup(key, NULL, NULL, &dtor);
286     if (dtor == NULL)
287         return EINVAL;
288
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);
295         }
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;
303     }
304
305     assert(key < values.values_num);
306
307     if (values.values[key] != NULL && dtor != NULL && dtor != DEAD_KEY)
308         dtor(values.values[key]);
309
310     values.values[key] = value;
311     return 0;
312 }
313
314 void *
315 heim_w32_getspecific(HEIM_PRIV_thread_key key)
316 {
317     if (key >= values.values_num)
318         return NULL;
319     return values.values[key];
320 }
321
322 #else
323 static char dummy;
324 #endif /* HEIM_WIN32_TLS */