]> git.saurik.com Git - apple/libpthread.git/blobdiff - src/pthread_tsd.c
libpthread-218.1.3.tar.gz
[apple/libpthread.git] / src / pthread_tsd.c
index e3fffe77d89840bf137986adec02456d98bfb984..1aa0285510a2888615bb51a6d9c87ebe0f7128ae 100644 (file)
@@ -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 <dlfcn.h>
+
 #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;