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