Thread-specific data
_pthread_key_global_init source
The pthread_tsd destruction order can be reverted to the old (pre-10.11) order
by setting this environment variable.
void _pthread_key_global_init(const char *envp[]) { #if PTHREAD_KEY_LEGACY_SUPPORT if (_simple_getenv(envp, "PTHREAD_KEY_LEGACY_DESTRUCTOR_ORDER")) { __pthread_key_legacy_behaviour = true; } if (_simple_getenv(envp, "PTHREAD_KEY_LEGACY_DESTRUCTOR_ORDER_LOG")) { __pthread_key_legacy_behaviour_log = true; } #endif // PTHREAD_KEY_LEGACY_SUPPORT }
_pthread_key_set_destructor source
Returns true if successful, false if destructor was already set.
static bool _pthread_key_set_destructor(pthread_key_t key, void (*destructor)(void *)) { uintptr_t *ptr = &_pthread_keys[key].destructor; uintptr_t value = ~(uintptr_t)destructor; if (*ptr == 0) { *ptr = value; return true; } return false; }
_pthread_key_unset_destructor source
Returns true if successful, false if the destructor was not set.
static bool _pthread_key_unset_destructor(pthread_key_t key) { uintptr_t *ptr = &_pthread_keys[key].destructor; if (*ptr != 0) { *ptr = 0; return true; } return false; }
_pthread_key_get_destructor source
Returns true if successful, false if the destructor was not set.
static bool _pthread_key_get_destructor(pthread_key_t key, void (**destructor)(void *)) { uintptr_t value = _pthread_keys[key].destructor; if (value == 0) { return false; } if (destructor) { *destructor = (void (*)(void *))(~value); } return true; }
pthread_key_create source · pthread_key_create reference
int pthread_key_create(pthread_key_t *key, void (*destructor)(void *)) { int res = EAGAIN; // Returns EAGAIN if key cannot be allocated. pthread_key_t k; _pthread_lock_lock(&__pthread_tsd_lock); for (k = __pthread_tsd_start; k < __pthread_tsd_end; k++) { if (_pthread_key_set_destructor(k, destructor)) { *key = k; res = 0; break; } } _pthread_lock_unlock(&__pthread_tsd_lock); return res; }
pthread_key_delete source · pthread_key_delete reference
int pthread_key_delete(pthread_key_t key) { int res = EINVAL; // Returns EINVAL if key is not allocated. _pthread_lock_lock(&__pthread_tsd_lock); if (key >= __pthread_tsd_start && key < __pthread_tsd_end) { if (_pthread_key_unset_destructor(key)) { pthread_t p; _pthread_lock_lock(&_pthread_list_lock); TAILQ_FOREACH(p, &__pthread_head, tl_plist) { // No lock for word-sized write. p->tsd[key] = 0; } _pthread_lock_unlock(&_pthread_list_lock); res = 0; } } _pthread_lock_unlock(&__pthread_tsd_lock); return res; }
_pthread_setspecific source
static inline int _pthread_setspecific(pthread_t thread, pthread_key_t key, const void *value) { int res = EINVAL; if (key >= __pthread_tsd_first && key < __pthread_tsd_end) { bool created = _pthread_key_get_destructor(key, NULL); if (key < __pthread_tsd_start || created) { thread->tsd[key] = (void *)value; res = 0; if (key < __pthread_tsd_start) { // XXX: is this really necessary? _pthread_key_set_destructor(key, NULL); } if (key > thread->max_tsd_key) { thread->max_tsd_key = (uint16_t)key; } } } return res; }
pthread_setspecific source · pthread_setspecific reference
!VARIANT_DYLD
int pthread_setspecific(pthread_key_t key, const void *value) { #if VARIANT_DYLD return ENOTSUP; #else return _pthread_setspecific(pthread_self(), key, value); #endif // !VARIANT_DYLD }
_pthread_setspecific_static source
int _pthread_setspecific_static(pthread_key_t key, void *value) { int res = EINVAL; #if !VARIANT_DYLD if (key < __pthread_tsd_start) { _pthread_setspecific_direct(key, value); res = 0; } #endif // !VARIANT_DYLD return res; }
pthread_getspecific source · pthread_getspecific reference
void* pthread_getspecific(pthread_key_t key) { return _pthread_getspecific_direct(key); }
pthread_introspection_setspecific_np source
int pthread_introspection_setspecific_np(pthread_t thread, pthread_key_t key, const void *value) { pthread_t self = _pthread_self(); if (os_unlikely(self->introspection != PTHREAD_INTROSPECTION_THREAD_CREATE)) { PTHREAD_CLIENT_CRASH(0, "Calling pthread_introspection_setspecific_np " "outside of a CREATE introspection hook"); } return _pthread_setspecific(thread, key, value); }
pthread_introspection_getspecific_np source
void * pthread_introspection_getspecific_np(pthread_t thread, pthread_key_t key) { pthread_t self = _pthread_self(); if (os_unlikely(self->introspection != PTHREAD_INTROSPECTION_THREAD_DESTROY)) { PTHREAD_CLIENT_CRASH(0, "Calling pthread_introspection_getspecific_np " "outside of a DESTROY introspection hook"); } return thread->tsd[key]; }
_pthread_tsd_cleanup_key source
static void _pthread_tsd_cleanup_key(pthread_t self, pthread_key_t key) { void (*destructor)(void *); if (_pthread_key_get_destructor(key, &destructor)) { void **ptr = &self->tsd[key]; void *value = *ptr; if (value) { *ptr = NULL; if (destructor) { destructor(value); } } } }
_pthread_tsd_cleanup_new source
static void _pthread_tsd_cleanup_new(pthread_t self) { int j; // clean up all keys for (j = 0; j < PTHREAD_DESTRUCTOR_ITERATIONS; j++) { pthread_key_t k; for (k = __pthread_tsd_start; k <= self->max_tsd_key; k++) { _pthread_tsd_cleanup_key(self, k); } // Now destroy the shared cache static keys, because they used to be // dynamic before we added the TLV optimization, so avoid changing // the order here. for (k = _pthread_tsd_shared_cache_first ; k <= _pthread_tsd_shared_cache_last ; k++) { _pthread_tsd_cleanup_key(self, k); } for (k = __pthread_tsd_first; k <= __pthread_tsd_max; k++) { _pthread_tsd_cleanup_key(self, k); } } self->max_tsd_key = 0; }
_pthread_tsd_behaviour_check source
static void _pthread_tsd_behaviour_check(pthread_t self) { // Iterate from dynamic-key start to dynamic-key end, if the key has both // a destructor and a value then _pthread_tsd_cleanup_key would cause // us to re-trigger the destructor. pthread_key_t k; for (k = __pthread_tsd_start; k < __pthread_tsd_end; k++) { void (*destructor)(void *); if (_pthread_key_get_destructor(k, &destructor)) { void **ptr = &self->tsd[k]; void *value = *ptr; if (value && destructor) { _simple_asl_log(ASL_LEVEL_ERR, "pthread_tsd", "warning: dynamic tsd keys dirty after static key cleanup loop."); #if 0 // enable this for debugging Dl_info i; if (dladdr(destructor, &i) == 0) { _simple_asl_log(ASL_LEVEL_ERR, "pthread_tsd", i.dli_fname); _simple_asl_log(ASL_LEVEL_ERR, "pthread_tsd", i.dli_saddr); } #endif } } } }
_pthread_tsd_cleanup_legacy source
static void _pthread_tsd_cleanup_legacy(pthread_t self) { int j; // clean up dynamic keys first for (j = 0; j < PTHREAD_DESTRUCTOR_ITERATIONS; j++) { pthread_key_t k; for (k = __pthread_tsd_start; k <= self->max_tsd_key; k++) { _pthread_tsd_cleanup_key(self, k); } // Now destroy the shared cache static keys, because they used to be // dynamic before we added the TLV optimization, so avoid changing // the order here. for (k = _pthread_tsd_shared_cache_first ; k <= _pthread_tsd_shared_cache_last ; k++) { _pthread_tsd_cleanup_key(self, k); } } self->max_tsd_key = 0; // clean up static keys for (j = 0; j < PTHREAD_DESTRUCTOR_ITERATIONS; j++) { pthread_key_t k; for (k = __pthread_tsd_first; k <= __pthread_tsd_max; k++) { _pthread_tsd_cleanup_key(self, k); } … more in source
_pthread_tsd_cleanup source
!VARIANT_DYLD
void _pthread_tsd_cleanup(pthread_t self) { #if !VARIANT_DYLD // unless __pthread_key_legacy_behaviour == 1, use the new pthread key // destructor order: (dynamic -> static) x5 -> (GC x5) if (__pthread_key_legacy_behaviour == 0) { _pthread_tsd_cleanup_new(self); } else { _pthread_tsd_cleanup_legacy(self); } #endif // !VARIANT_DYLD }
pthread_key_init_np source
XXX: key should be pthread_key_t
int pthread_key_init_np(int key, void (*destructor)(void *)) { int res = EINVAL; // Returns EINVAL if key is out of range. if (key >= __pthread_tsd_first && key < __pthread_tsd_start) { _pthread_lock_lock(&__pthread_tsd_lock); _pthread_key_set_destructor(key, destructor); if (key > __pthread_tsd_max) { __pthread_tsd_max = key; } _pthread_lock_unlock(&__pthread_tsd_lock); res = 0; } return res; }
pthread_self source · pthread_self reference
!VARIANT_DYLD
pthread_t pthread_self(void) { pthread_t self = _pthread_self_direct(); _pthread_validate_signature(self); return self; }