libpthread/src/pthread_tsd.c

1/* 2 * Copyright (c) 2000-2003, 2007, 2012 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23/* 24 * Copyright 1996 1995 by Open Software Foundation, Inc. 1997 1996 1995 1994 1993 1992 1991 25 * All Rights Reserved 26 * 27 * Permission to use, copy, modify, and distribute this software and 28 * its documentation for any purpose and without fee is hereby granted, 29 * provided that the above copyright notice appears in all copies and 30 * that both the copyright notice and this permission notice appear in 31 * supporting documentation. 32 * 33 * OSF DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE 34 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 35 * FOR A PARTICULAR PURPOSE. 36 * 37 * IN NO EVENT SHALL OSF BE LIABLE FOR ANY SPECIAL, INDIRECT, OR 38 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 39 * LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, 40 * NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION 41 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 42 * 43 */ 44/* 45 * MkLinux 46 */ 47 48/* 49 * POSIX Pthread Library 50 * Thread Specific Data support 51 * NB: pthread_getspecific() is in a separate assembly file 52 */ 53 54#include "internal.h" 55 56#ifndef PTHREAD_KEY_LEGACY_SUPPORT 57#if TARGET_OS_DRIVERKIT 58#define PTHREAD_KEY_LEGACY_SUPPORT 0 59#else 60#define PTHREAD_KEY_LEGACY_SUPPORT 1 61#endif // TARGET_OS_DRIVERKIT 62#endif // PTHREAD_KEY_LEGACY_SUPPORT 63 64#if !VARIANT_DYLD 65// __pthread_tsd_first is first static key managed by libpthread. 66// __pthread_tsd_max is the (observed) end of static key destructors. 67// Inside [__pthread_tsd_first, __pthread_tsd_max] lies 68// [_pthread_tsd_shared_cache_first, _pthread_tsd_shared_cache_last] (closed bounds), 69// where keys are assigned by the shared cache builder 70// for __thread variables. 71// __pthread_tsd_start is the start of dynamic keys. 72// __pthread_tsd_end is the end of dynamic keys. 73 74static const int __pthread_tsd_first = __TSD_RESERVED_MAX + 1; 75 76// These two symbol names and types are hardcoded in the shared cache builder, 77// we need to coordinate with dyld if we want to change them. 78// The values can be modified freely, however, and would not need a world 79// build to change. 80const uint32_t _pthread_tsd_shared_cache_first = 125; 81const uint32_t _pthread_tsd_shared_cache_last = 209; 82 83static const int __pthread_tsd_start = _INTERNAL_POSIX_THREAD_KEYS_MAX; 84static const int __pthread_tsd_end = _INTERNAL_POSIX_THREAD_KEYS_END; 85 86static int __pthread_tsd_max = __pthread_tsd_first; 87static _pthread_lock __pthread_tsd_lock = _PTHREAD_LOCK_INITIALIZER; 88#if PTHREAD_KEY_LEGACY_SUPPORT 89static bool __pthread_key_legacy_behaviour = 0; 90static bool __pthread_key_legacy_behaviour_log = 0; 91#else 92#define __pthread_key_legacy_behaviour 0 93#define _pthread_tsd_cleanup_legacy(...) 94#endif // PTHREAD_KEY_LEGACY_SUPPORT 95 96// Omit support for pthread key destructors in the static archive for dyld. 97// dyld does not create and destroy threads so these are not necessary. 98// 99// We store the bit-wise negation of the destructor so that a quick non-zero 100// test can be used to determine if the destructor has been set, even if it is 101// NULL. This means that a destructor of value ~0x0ull cannot be used. That 102// shouldn't be a problem in practice since it isn't a valid function address. 103 104static struct { 105 uintptr_t destructor; 106} _pthread_keys[_INTERNAL_POSIX_THREAD_KEYS_END]; 107 108// The pthread_tsd destruction order can be reverted to the old (pre-10.11) order 109// by setting this environment variable. 110void 111_pthread_key_global_init(const char *envp[]) 112{ 113#if PTHREAD_KEY_LEGACY_SUPPORT 114 if (_simple_getenv(envp, "PTHREAD_KEY_LEGACY_DESTRUCTOR_ORDER")) { 115 __pthread_key_legacy_behaviour = true; 116 } 117 if (_simple_getenv(envp, "PTHREAD_KEY_LEGACY_DESTRUCTOR_ORDER_LOG")) { 118 __pthread_key_legacy_behaviour_log = true; 119 } 120#endif // PTHREAD_KEY_LEGACY_SUPPORT 121} 122 123// Returns true if successful, false if destructor was already set. 124static bool 125_pthread_key_set_destructor(pthread_key_t key, void (*destructor)(void *)) 126{ 127 uintptr_t *ptr = &_pthread_keys[key].destructor; 128 uintptr_t value = ~(uintptr_t)destructor; 129 if (*ptr == 0) { 130 *ptr = value; 131 return true; 132 } 133 return false; 134} 135 136// Returns true if successful, false if the destructor was not set. 137static bool 138_pthread_key_unset_destructor(pthread_key_t key) 139{ 140 uintptr_t *ptr = &_pthread_keys[key].destructor; 141 if (*ptr != 0) { 142 *ptr = 0; 143 return true; 144 } 145 return false; 146} 147 148// Returns true if successful, false if the destructor was not set. 149static bool 150_pthread_key_get_destructor(pthread_key_t key, void (**destructor)(void *)) 151{ 152 uintptr_t value = _pthread_keys[key].destructor; 153 if (value == 0) { 154 return false; 155 } 156 if (destructor) { 157 *destructor = (void (*)(void *))(~value); 158 } 159 return true; 160} 161 162int 163pthread_key_create(pthread_key_t *key, void (*destructor)(void *)) 164{ 165 int res = EAGAIN; // Returns EAGAIN if key cannot be allocated. 166 pthread_key_t k; 167 168 _pthread_lock_lock(&__pthread_tsd_lock); 169 for (k = __pthread_tsd_start; k < __pthread_tsd_end; k++) { 170 if (_pthread_key_set_destructor(k, destructor)) { 171 *key = k; 172 res = 0; 173 break; 174 } 175 } 176 _pthread_lock_unlock(&__pthread_tsd_lock); 177 178 return res; 179} 180 181int 182pthread_key_delete(pthread_key_t key) 183{ 184 int res = EINVAL; // Returns EINVAL if key is not allocated. 185 186 _pthread_lock_lock(&__pthread_tsd_lock); 187 if (key >= __pthread_tsd_start && key < __pthread_tsd_end) { 188 if (_pthread_key_unset_destructor(key)) { 189 pthread_t p; 190 _pthread_lock_lock(&_pthread_list_lock); 191 TAILQ_FOREACH(p, &__pthread_head, tl_plist) { 192 // No lock for word-sized write. 193 p->tsd[key] = 0; 194 } 195 _pthread_lock_unlock(&_pthread_list_lock); 196 res = 0; 197 } 198 } 199 _pthread_lock_unlock(&__pthread_tsd_lock); 200 201 return res; 202} 203 204static inline int 205_pthread_setspecific(pthread_t thread, pthread_key_t key, const void *value) 206{ 207 int res = EINVAL; 208 209 if (key >= __pthread_tsd_first && key < __pthread_tsd_end) { 210 bool created = _pthread_key_get_destructor(key, NULL); 211 if (key < __pthread_tsd_start || created) { 212 thread->tsd[key] = (void *)value; 213 res = 0; 214 215 if (key < __pthread_tsd_start) { 216 // XXX: is this really necessary? 217 _pthread_key_set_destructor(key, NULL); 218 } 219 if (key > thread->max_tsd_key) { 220 thread->max_tsd_key = (uint16_t)key; 221 } 222 } 223 } 224 225 return res; 226} 227#endif // !VARIANT_DYLD 228 229int 230pthread_setspecific(pthread_key_t key, const void *value) 231{ 232#if VARIANT_DYLD 233 return ENOTSUP; 234#else 235 return _pthread_setspecific(pthread_self(), key, value); 236#endif // !VARIANT_DYLD 237} 238 239int 240_pthread_setspecific_static(pthread_key_t key, void *value) 241{ 242 int res = EINVAL; 243 244#if !VARIANT_DYLD 245 if (key < __pthread_tsd_start) { 246 _pthread_setspecific_direct(key, value); 247 res = 0; 248 } 249#endif // !VARIANT_DYLD 250 251 return res; 252} 253 254void* 255pthread_getspecific(pthread_key_t key) 256{ 257 return _pthread_getspecific_direct(key); 258} 259 260#if !VARIANT_DYLD 261int 262pthread_introspection_setspecific_np(pthread_t thread, 263 pthread_key_t key, const void *value) 264{ 265 pthread_t self = _pthread_self(); 266 if (os_unlikely(self->introspection != PTHREAD_INTROSPECTION_THREAD_CREATE)) { 267 PTHREAD_CLIENT_CRASH(0, "Calling pthread_introspection_setspecific_np " 268 "outside of a CREATE introspection hook"); 269 } 270 return _pthread_setspecific(thread, key, value); 271 272} 273 274void * 275pthread_introspection_getspecific_np(pthread_t thread, pthread_key_t key) 276{ 277 pthread_t self = _pthread_self(); 278 if (os_unlikely(self->introspection != PTHREAD_INTROSPECTION_THREAD_DESTROY)) { 279 PTHREAD_CLIENT_CRASH(0, "Calling pthread_introspection_getspecific_np " 280 "outside of a DESTROY introspection hook"); 281 } 282 return thread->tsd[key]; 283} 284 285static void 286_pthread_tsd_cleanup_key(pthread_t self, pthread_key_t key) 287{ 288 void (*destructor)(void *); 289 if (_pthread_key_get_destructor(key, &destructor)) { 290 void **ptr = &self->tsd[key]; 291 void *value = *ptr; 292 if (value) { 293 *ptr = NULL; 294 if (destructor) { 295 destructor(value); 296 } 297 } 298 } 299} 300 301static void 302_pthread_tsd_cleanup_new(pthread_t self) 303{ 304 int j; 305 306 // clean up all keys 307 for (j = 0; j < PTHREAD_DESTRUCTOR_ITERATIONS; j++) { 308 pthread_key_t k; 309 for (k = __pthread_tsd_start; k <= self->max_tsd_key; k++) { 310 _pthread_tsd_cleanup_key(self, k); 311 } 312 313 // Now destroy the shared cache static keys, because they used to be 314 // dynamic before we added the TLV optimization, so avoid changing 315 // the order here. 316 317 for (k = _pthread_tsd_shared_cache_first ; k <= _pthread_tsd_shared_cache_last ; k++) { 318 _pthread_tsd_cleanup_key(self, k); 319 } 320 321 for (k = __pthread_tsd_first; k <= __pthread_tsd_max; k++) { 322 _pthread_tsd_cleanup_key(self, k); 323 } 324 } 325 326 self->max_tsd_key = 0; 327} 328 329#if PTHREAD_KEY_LEGACY_SUPPORT 330#import <_simple.h> 331#import <dlfcn.h> 332static void 333_pthread_tsd_behaviour_check(pthread_t self) 334{ 335 // Iterate from dynamic-key start to dynamic-key end, if the key has both 336 // a destructor and a value then _pthread_tsd_cleanup_key would cause 337 // us to re-trigger the destructor. 338 pthread_key_t k; 339 340 for (k = __pthread_tsd_start; k < __pthread_tsd_end; k++) { 341 void (*destructor)(void *); 342 if (_pthread_key_get_destructor(k, &destructor)) { 343 void **ptr = &self->tsd[k]; 344 void *value = *ptr; 345 if (value && destructor) { 346 _simple_asl_log(ASL_LEVEL_ERR, "pthread_tsd", 347 "warning: dynamic tsd keys dirty after static key cleanup loop."); 348#if 0 349 // enable this for debugging 350 Dl_info i; 351 if (dladdr(destructor, &i) == 0) { 352 _simple_asl_log(ASL_LEVEL_ERR, "pthread_tsd", i.dli_fname); 353 _simple_asl_log(ASL_LEVEL_ERR, "pthread_tsd", i.dli_saddr); 354 } 355#endif 356 } 357 } 358 } 359 360} 361 362static void 363_pthread_tsd_cleanup_legacy(pthread_t self) 364{ 365 int j; 366 367 // clean up dynamic keys first 368 for (j = 0; j < PTHREAD_DESTRUCTOR_ITERATIONS; j++) { 369 pthread_key_t k; 370 for (k = __pthread_tsd_start; k <= self->max_tsd_key; k++) { 371 _pthread_tsd_cleanup_key(self, k); 372 } 373 374 // Now destroy the shared cache static keys, because they used to be 375 // dynamic before we added the TLV optimization, so avoid changing 376 // the order here. 377 378 for (k = _pthread_tsd_shared_cache_first ; k <= _pthread_tsd_shared_cache_last ; k++) { 379 _pthread_tsd_cleanup_key(self, k); 380 } 381 } 382 383 self->max_tsd_key = 0; 384 385 // clean up static keys 386 for (j = 0; j < PTHREAD_DESTRUCTOR_ITERATIONS; j++) { 387 pthread_key_t k; 388 for (k = __pthread_tsd_first; k <= __pthread_tsd_max; k++) { 389 _pthread_tsd_cleanup_key(self, k); 390 } 391 392 if (__pthread_key_legacy_behaviour_log != 0 && self->max_tsd_key != 0) { 393 // max_tsd_key got dirtied, either by static or dynamic keys being 394 // reset. check for any dirty dynamic keys. 395 _pthread_tsd_behaviour_check(self); 396 } 397 } 398} 399#endif // PTHREAD_KEY_LEGACY_SUPPORT 400#endif // !VARIANT_DYLD 401 402void 403_pthread_tsd_cleanup(pthread_t self) 404{ 405#if !VARIANT_DYLD 406 407 // unless __pthread_key_legacy_behaviour == 1, use the new pthread key 408 // destructor order: (dynamic -> static) x5 -> (GC x5) 409 410 if (__pthread_key_legacy_behaviour == 0) { 411 _pthread_tsd_cleanup_new(self); 412 } else { 413 _pthread_tsd_cleanup_legacy(self); 414 } 415#endif // !VARIANT_DYLD 416} 417 418#if !VARIANT_DYLD 419// XXX: key should be pthread_key_t 420int 421pthread_key_init_np(int key, void (*destructor)(void *)) 422{ 423 int res = EINVAL; // Returns EINVAL if key is out of range. 424 if (key >= __pthread_tsd_first && key < __pthread_tsd_start) { 425 _pthread_lock_lock(&__pthread_tsd_lock); 426 _pthread_key_set_destructor(key, destructor); 427 if (key > __pthread_tsd_max) { 428 __pthread_tsd_max = key; 429 } 430 _pthread_lock_unlock(&__pthread_tsd_lock); 431 res = 0; 432 } 433 return res; 434} 435#endif // !VARIANT_DYLD 436 437#undef pthread_self 438pthread_t 439pthread_self(void) 440{ 441 pthread_t self = _pthread_self_direct(); 442 _pthread_validate_signature(self); 443 return self; 444} 445 446// rdar://57406917 447pthread_t 448_pthread_self(void) 449{ 450 return pthread_self(); 451}