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