X-Git-Url: https://git.saurik.com/apple/libpthread.git/blobdiff_plain/f1a1da6cf65a9d0e6858678f6c259025cf5d27fd..2546420a235d38941a7eed560a8cb61403ecb8e2:/src/pthread_tsd.c diff --git a/src/pthread_tsd.c b/src/pthread_tsd.c index e3fffe7..1aa0285 100644 --- a/src/pthread_tsd.c +++ b/src/pthread_tsd.c @@ -65,6 +65,9 @@ static int __pthread_tsd_max = __pthread_tsd_first; static const int __pthread_tsd_start = _INTERNAL_POSIX_THREAD_KEYS_MAX; static const int __pthread_tsd_end = _INTERNAL_POSIX_THREAD_KEYS_END; +static int __pthread_key_legacy_behaviour = 0; +static int __pthread_key_legacy_behaviour_log = 0; + // Omit support for pthread key destructors in the static archive for dyld. // dyld does not create and destroy threads so these are not necessary. // @@ -77,7 +80,16 @@ static struct { uintptr_t destructor; } _pthread_keys[_INTERNAL_POSIX_THREAD_KEYS_END]; -static pthread_lock_t tsd_lock = LOCK_INITIALIZER; +static _pthread_lock tsd_lock = _PTHREAD_LOCK_INITIALIZER; + +// 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[]) +{ + __pthread_key_legacy_behaviour = _simple_getenv(envp, "PTHREAD_KEY_LEGACY_DESTRUCTOR_ORDER") ? 1 : 0; + __pthread_key_legacy_behaviour_log = _simple_getenv(envp, "PTHREAD_KEY_LEGACY_DESTRUCTOR_ORDER_LOG") ? 1 : 0; +} // Returns true if successful, false if destructor was already set. static bool @@ -121,7 +133,7 @@ pthread_key_create(pthread_key_t *key, void (*destructor)(void *)) int res = EAGAIN; // Returns EAGAIN if key cannot be allocated. pthread_key_t k; - LOCK(tsd_lock); + _PTHREAD_LOCK(tsd_lock); for (k = __pthread_tsd_start; k < __pthread_tsd_end; k++) { if (_pthread_key_set_destructor(k, destructor)) { *key = k; @@ -129,7 +141,7 @@ pthread_key_create(pthread_key_t *key, void (*destructor)(void *)) break; } } - UNLOCK(tsd_lock); + _PTHREAD_UNLOCK(tsd_lock); return res; } @@ -139,20 +151,20 @@ pthread_key_delete(pthread_key_t key) { int res = EINVAL; // Returns EINVAL if key is not allocated. - LOCK(tsd_lock); + _PTHREAD_LOCK(tsd_lock); if (key >= __pthread_tsd_start && key < __pthread_tsd_end) { if (_pthread_key_unset_destructor(key)) { struct _pthread *p; - LOCK(_pthread_list_lock); + _PTHREAD_LOCK(_pthread_list_lock); TAILQ_FOREACH(p, &__pthread_head, plist) { // No lock for word-sized write. p->tsd[key] = 0; } - UNLOCK(_pthread_list_lock); + _PTHREAD_UNLOCK(_pthread_list_lock); res = 0; } } - UNLOCK(tsd_lock); + _PTHREAD_UNLOCK(tsd_lock); return res; } @@ -185,6 +197,21 @@ pthread_setspecific(pthread_key_t key, const void *value) return res; } +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; +} + void* pthread_getspecific(pthread_key_t key) { @@ -209,10 +236,61 @@ _pthread_tsd_cleanup_key(pthread_t self, pthread_key_t key) } #endif // !VARIANT_DYLD -void -_pthread_tsd_cleanup(pthread_t self) -{ +#import <_simple.h> +#import + #if !VARIANT_DYLD +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); + } + + for (k = __pthread_tsd_first; k <= __pthread_tsd_max; k++) { + _pthread_tsd_cleanup_key(self, k); + } + } + + self->max_tsd_key = 0; +} + +static void +_pthread_tsd_behaviour_check(pthread_t self) +{ + // Iterate from dynamic-key start to dynamic-key end, if the key has both + // a desctructor and a value then _pthread_tsd_cleanup_key would cause + // us to re-trigger the destructor. + Dl_info i; + 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 (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); + } + } + } + } + +} + +static void +_pthread_tsd_cleanup_legacy(pthread_t self) +{ int j; // clean up dynamic keys first @@ -231,6 +309,28 @@ _pthread_tsd_cleanup(pthread_t self) for (k = __pthread_tsd_first; k <= __pthread_tsd_max; k++) { _pthread_tsd_cleanup_key(self, k); } + + if (__pthread_key_legacy_behaviour_log != 0 && self->max_tsd_key != 0) { + // max_tsd_key got dirtied, either by static or dynamic keys being + // reset. check for any dirty dynamic keys. + _pthread_tsd_behaviour_check(self); + } + } +} +#endif // !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 } @@ -242,12 +342,12 @@ 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) { - LOCK(tsd_lock); + _PTHREAD_LOCK(tsd_lock); _pthread_key_set_destructor(key, destructor); if (key > __pthread_tsd_max) { __pthread_tsd_max = key; } - UNLOCK(tsd_lock); + _PTHREAD_UNLOCK(tsd_lock); res = 0; } return res;