// Thread keys reserved by libc for our use.
-// Keys [0..4] are used by autozone.
-#if defined(__PTK_FRAMEWORK_OBJC_KEY5)
+#if defined(__PTK_FRAMEWORK_OBJC_KEY0)
 #   define SUPPORT_DIRECT_THREAD_KEYS 1
-#   define TLS_DIRECT_KEY        ((tls_key_t)__PTK_FRAMEWORK_OBJC_KEY5)
-#   define SYNC_DATA_DIRECT_KEY  ((tls_key_t)__PTK_FRAMEWORK_OBJC_KEY6)
-#   define SYNC_COUNT_DIRECT_KEY ((tls_key_t)__PTK_FRAMEWORK_OBJC_KEY7)
-#   define AUTORELEASE_POOL_KEY  ((tls_key_t)__PTK_FRAMEWORK_OBJC_KEY8)
+#   define TLS_DIRECT_KEY        ((tls_key_t)__PTK_FRAMEWORK_OBJC_KEY0)
+#   define SYNC_DATA_DIRECT_KEY  ((tls_key_t)__PTK_FRAMEWORK_OBJC_KEY1)
+#   define SYNC_COUNT_DIRECT_KEY ((tls_key_t)__PTK_FRAMEWORK_OBJC_KEY2)
+#   define AUTORELEASE_POOL_KEY  ((tls_key_t)__PTK_FRAMEWORK_OBJC_KEY3)
 # if SUPPORT_RETURN_AUTORELEASE
-#   define AUTORELEASE_POOL_RECLAIM_KEY ((tls_key_t)__PTK_FRAMEWORK_OBJC_KEY9)
+#   define AUTORELEASE_POOL_RECLAIM_KEY ((tls_key_t)__PTK_FRAMEWORK_OBJC_KEY4)
+# endif
+# if SUPPORT_QOS_HACK
+#   define QOS_KEY               ((tls_key_t)__PTK_FRAMEWORK_OBJC_KEY5)
 # endif
 #else
 #   define SUPPORT_DIRECT_THREAD_KEYS 0
             || k == AUTORELEASE_POOL_KEY
 #   if SUPPORT_RETURN_AUTORELEASE
             || k == AUTORELEASE_POOL_RECLAIM_KEY
+#   endif
+#   if SUPPORT_QOS_HACK
+            || k == QOS_KEY
 #   endif
                );
 }
 #endif
 
 
+static inline pthread_t pthread_self_direct()
+{
+    return (pthread_t)
+        _pthread_getspecific_direct(_PTHREAD_TSD_SLOT_PTHREAD_SELF);
+}
+
+static inline mach_port_t mach_thread_self_direct() 
+{
+    return (mach_port_t)(uintptr_t)
+        _pthread_getspecific_direct(_PTHREAD_TSD_SLOT_MACH_THREAD_SELF);
+}
+
+#if SUPPORT_QOS_HACK
+static inline pthread_priority_t pthread_self_priority_direct() 
+{
+    pthread_priority_t pri = (pthread_priority_t)
+        _pthread_getspecific_direct(_PTHREAD_TSD_SLOT_PTHREAD_QOS_CLASS);
+    return pri & ~_PTHREAD_PRIORITY_FLAGS_MASK;
+}
+#endif
+
+
 typedef pthread_mutex_t mutex_t;
 #define MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER;
 
 }
 
 
+#if SUPPORT_QOS_HACK
+// Override QOS class to avoid priority inversion in rwlocks
+// <rdar://17697862> do a qos override before taking rw lock in objc
+
+#include <pthread/workqueue_private.h>
+extern pthread_priority_t BackgroundPriority;
+extern pthread_priority_t MainPriority;
+
+static inline void qosStartOverride()
+{
+    uintptr_t overrideRefCount = (uintptr_t)tls_get_direct(QOS_KEY);
+    if (overrideRefCount > 0) {
+        // If there is a qos override, increment the refcount and continue
+        tls_set_direct(QOS_KEY, (void *)(overrideRefCount + 1));
+    }
+    else {
+        pthread_priority_t currentPriority = pthread_self_priority_direct();
+        // Check if override is needed. Only override if we are background qos
+        if (currentPriority <= BackgroundPriority) {
+            int res __unused = _pthread_override_qos_class_start_direct(mach_thread_self_direct(), MainPriority);
+            assert(res == 0);
+            // Once we override, we set the reference count in the tsd 
+            // to know when to end the override
+            tls_set_direct(QOS_KEY, (void *)1);
+        }
+    }
+}
+
+static inline void qosEndOverride()
+{
+    uintptr_t overrideRefCount = (uintptr_t)tls_get_direct(QOS_KEY);
+    if (overrideRefCount == 0) return;
+
+    if (overrideRefCount == 1) {
+        // end the override
+        int res __unused = _pthread_override_qos_class_end_direct(mach_thread_self_direct());
+        assert(res == 0);
+    }
+
+    // decrement refcount
+    tls_set_direct(QOS_KEY, (void *)(overrideRefCount - 1));
+}
+
+// SUPPORT_QOS_HACK
+#else
+// not SUPPORT_QOS_HACK
+
+static inline void qosStartOverride() { }
+static inline void qosEndOverride() { }
+
+// not SUPPORT_QOS_HACK
+#endif
+
 /* Custom read-write lock
    - reader is atomic add/subtract 
    - writer is pthread mutex plus atomic add/subtract
 
 static inline void _rwlock_read_nodebug(rwlock_t *l)
 {
+    qosStartOverride();
     int err __unused = pthread_rwlock_rdlock(&l->rwl);
     assert(err == 0);
 }
 {
     int err __unused = pthread_rwlock_unlock(&l->rwl);
     assert(err == 0);
+    qosEndOverride();
 }
 
 
 static inline bool _rwlock_try_read_nodebug(rwlock_t *l)
 {
+    qosStartOverride();
     int err = pthread_rwlock_tryrdlock(&l->rwl);
-    assert(err == 0  ||  err == EBUSY);
-    return (err == 0);
+    assert(err == 0  ||  err == EBUSY  ||  err == EAGAIN);
+    if (err == 0) {
+        return true;
+    } else {
+        qosEndOverride();
+        return false;
+    }
 }
 
 
 static inline void _rwlock_write_nodebug(rwlock_t *l)
 {
+    qosStartOverride();
     int err __unused = pthread_rwlock_wrlock(&l->rwl);
     assert(err == 0);
 }
 {
     int err __unused = pthread_rwlock_unlock(&l->rwl);
     assert(err == 0);
+    qosEndOverride();
 }
 
 static inline bool _rwlock_try_write_nodebug(rwlock_t *l)
 {
+    qosStartOverride();
     int err = pthread_rwlock_trywrlock(&l->rwl);
     assert(err == 0  ||  err == EBUSY);
-    return (err == 0);
+    if (err == 0) {
+        return true;
+    } else {
+        qosEndOverride();
+        return false;
+    }
 }
 
 
 
 .long 0 /* table.clsopt_offset */
 .space PAGE_MAX_SIZE-16
 
-/* space for selopt, smax/capacity=262144, blen/mask=65535+1 */
-.space 65536
+/* space for selopt, smax/capacity=262144, blen/mask=131071+1 */
+.space 131072    /* mask tab */
 .space 262144    /* checkbytes */
 .space 262144*4  /* offsets */
 
-       
-/* space for clsopt, smax/capacity=32768, blen/mask=4095+1 */
-.space PAGE_MAX_SIZE
+/* space for clsopt, smax/capacity=32768, blen/mask=16383+1 */
+.space 16384            /* mask tab */
 .space 32768           /* checkbytes */
 .space 32768*12        /* offsets to name and class and header_info */
 .space PAGE_MAX_SIZE   /* some duplicate classes */