1 /***********************************************************************
2 * Copyright (c) 2016 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
56 #include <heim_threads.h>
62 /* Logical array of keys that grows lock-lessly */
63 typedef struct tls_keys tls_keys
;
65 void (**keys_dtors
)(void *); /* array of destructors */
66 size_t keys_start_idx
; /* index of first destructor */
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) */
80 void **values
; /* realloc()ed */
84 static HEIMDAL_THREAD_LOCAL
struct tls_values values
;
86 #define DEAD_KEY ((void *)8)
89 heim_w32_service_thread_detach(void *unused
)
95 HEIMDAL_MUTEX_lock(&tls_key_defs_lock
);
96 key_defs
= tls_key_defs
;
97 HEIMDAL_MUTEX_unlock(&tls_key_defs_lock
);
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
;
121 static pthread_key_t pt_key
;
122 pthread_once_t pt_once
= PTHREAD_ONCE_INIT
;
125 atexit_del_tls_for_thread(void)
127 heim_w32_service_thread_detach(NULL
);
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
);
139 err(1, "pthread_key_create() failed");
145 heim_w32_key_create(HEIM_PRIV_thread_key
*key
, void (*dtor
)(void *))
147 tls_keys
*key_defs
, *new_key_defs
;
152 (void) pthread_once(&pt_once
, create_pt_key
);
153 (void) pthread_setspecific(pt_key
, DEAD_KEY
);
156 HEIMDAL_MUTEX_lock(&tls_key_defs_lock
);
157 if (tls_key_defs
== NULL
) {
159 new_key_defs
= calloc(1, sizeof(*new_key_defs
));
160 if (new_key_defs
== NULL
) {
161 HEIMDAL_MUTEX_unlock(&tls_key_defs_lock
);
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
);
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
);
180 for (key_defs
= tls_key_defs
;
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
;
189 HEIMDAL_MUTEX_unlock(&tls_key_defs_lock
);
193 if (key_defs
->keys_next
!= NULL
)
196 /* Grow the registration array */
198 new_key_defs
= calloc(1, sizeof(*new_key_defs
));
199 if (new_key_defs
== NULL
)
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
) {
209 new_key_defs
->keys_start_idx
= key_defs
->keys_start_idx
+
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
;
219 HEIMDAL_MUTEX_unlock(&tls_key_defs_lock
);
224 key_lookup(HEIM_PRIV_thread_key key
, tls_keys
**kd
,
225 size_t *dtor_idx
, void (**dtor
)(void *))
231 if (dtor_idx
!= 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
) {
245 if (dtor_idx
!= NULL
)
246 *dtor_idx
= key
- key_defs
->keys_start_idx
;
248 *dtor
= key_defs
->keys_dtors
[key
- key_defs
->keys_start_idx
];
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
)
266 key_lookup(key
, &key_defs
, &dtor_idx
, NULL
);
267 if (key_defs
== NULL
)
269 key_defs
->keys_dtors
[dtor_idx
] = DEAD_KEY
;
274 heim_w32_setspecific(HEIM_PRIV_thread_key key
, void *value
)
278 void (*dtor
)(void *);
282 (void) pthread_setspecific(pt_key
, DEAD_KEY
);
285 key_lookup(key
, NULL
, NULL
, &dtor
);
289 if (key
>= values
.values_num
) {
290 if (values
.values_num
== 0) {
291 values
.values
= NULL
;
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
)
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
;
315 heim_w32_getspecific(HEIM_PRIV_thread_key key
)
317 if (key
>= values
.values_num
)
319 return values
.values
[key
];
324 #endif /* HEIM_WIN32_TLS */