]> git.saurik.com Git - apple/objc4.git/commitdiff
objc4-647.tar.gz os-x-10102 os-x-10103 os-x-10104 os-x-10105 v647
authorApple <opensource@apple.com>
Fri, 5 Jun 2015 01:14:17 +0000 (01:14 +0000)
committerApple <opensource@apple.com>
Fri, 5 Jun 2015 01:14:17 +0000 (01:14 +0000)
runtime/objc-config.h
runtime/objc-os.h
runtime/objc-runtime-new.mm
runtime/objc-sel-table.s
test/synchronized-counter.m

index 0e33b04f7ae4bed92e375692bb40f626054e97e5..39ada63dce06816e37618448efd6a2b279c4eda5 100644 (file)
 #   define SUPPORT_MESSAGE_LOGGING 1
 #endif
 
+// Define SUPPORT_QOS_HACK to work around deadlocks due to QoS bugs.
+#if !__OBJC2__  ||  TARGET_OS_WIN32
+#   define SUPPORT_QOS_HACK 0
+#else
+#   define SUPPORT_QOS_HACK 1
+#endif
+
 // OBJC_INSTRUMENTED controls whether message dispatching is dynamically
 // monitored.  Monitoring introduces substantial overhead.
 // NOTE: To define this condition, do so in the build command, NOT by
index 52b2ef5ff663d1029ab1e28cf49a3efa303e0f87..1765d6e24a75bc90ba01cf69475b8963ae5e72a4 100644 (file)
@@ -331,15 +331,17 @@ extern void _objc_fatal(const char *fmt, ...) __attribute__((noreturn, format (p
 
 
 // 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
@@ -660,6 +662,9 @@ static bool is_valid_direct_key(tls_key_t k) {
             || k == AUTORELEASE_POOL_KEY
 #   if SUPPORT_RETURN_AUTORELEASE
             || k == AUTORELEASE_POOL_RECLAIM_KEY
+#   endif
+#   if SUPPORT_QOS_HACK
+            || k == QOS_KEY
 #   endif
                );
 }
@@ -737,6 +742,28 @@ static inline void tls_set_direct(tls_key_t k, void *value)
 #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;
 
@@ -817,6 +844,59 @@ static inline semaphore_t create_semaphore(void)
 }
 
 
+#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
@@ -840,6 +920,7 @@ static inline void rwlock_init(rwlock_t *l)
 
 static inline void _rwlock_read_nodebug(rwlock_t *l)
 {
+    qosStartOverride();
     int err __unused = pthread_rwlock_rdlock(&l->rwl);
     assert(err == 0);
 }
@@ -848,19 +929,27 @@ static inline void _rwlock_unlock_read_nodebug(rwlock_t *l)
 {
     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);
 }
@@ -869,13 +958,20 @@ static inline void _rwlock_unlock_write_nodebug(rwlock_t *l)
 {
     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;
+    }
 }
 
 
index a3cc96477664d376d1429140096c2d0e94cce9fd..c0203991adda4650faca47e18d3ee69d235b025f 100644 (file)
@@ -75,11 +75,30 @@ rwlock_t selLock;
 mutex_t cacheUpdateLock = MUTEX_INITIALIZER;
 recursive_mutex_t loadMethodLock = RECURSIVE_MUTEX_INITIALIZER;
 
+#if SUPPORT_QOS_HACK
+pthread_priority_t BackgroundPriority = 0;
+pthread_priority_t MainPriority = 0;
+# if !NDEBUG
+static __unused void destroyQOSKey(void *arg) {
+    _objc_fatal("QoS override level at thread exit is %zu instead of zero", 
+                (size_t)(uintptr_t)arg);
+}
+# endif
+#endif
+
 void lock_init(void)
 {
     rwlock_init(&selLock);
     rwlock_init(&runtimeLock);
     recursive_mutex_init(&loadMethodLock);
+
+#if SUPPORT_QOS_HACK
+    BackgroundPriority = _pthread_qos_class_encode(QOS_CLASS_BACKGROUND, 0, 0);
+    MainPriority = _pthread_qos_class_encode(qos_class_main(), 0, 0);
+# if !NDEBUG
+    pthread_key_init_np(QOS_KEY, &destroyQOSKey);
+# endif
+#endif
 }
 
 
index 0b1cdbb2b1da5d4a84169900ddc4d091f2464ef2..32bfae777e3d280baf5a403a189e41c29bf38462 100644 (file)
@@ -9,14 +9,13 @@ __objc_opt_data:
 .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 */
index 6356f0d38079140e2bed7b176878146f9ff4a506..7f6bd1919fa9de2b81ee3cfd9a5f2a83fd9d6b54 100644 (file)
@@ -49,8 +49,8 @@ static void *threadfn(void *arg)
     }
 
     // Verify lack of objc pthread data (should have used sync fast cache)
-#ifdef __PTK_FRAMEWORK_OBJC_KEY5
-    testassert(! pthread_getspecific(__PTK_FRAMEWORK_OBJC_KEY5));
+#ifdef __PTK_FRAMEWORK_OBJC_KEY0
+    testassert(! pthread_getspecific(__PTK_FRAMEWORK_OBJC_KEY0));
 #endif
 
     return NULL;
@@ -66,8 +66,8 @@ int main()
 
     // Verify objc pthread data on this thread (from +initialize)
     // Worker threads shouldn't have any because of sync fast cache.
-#ifdef __PTK_FRAMEWORK_OBJC_KEY5
-    testassert(pthread_getspecific(__PTK_FRAMEWORK_OBJC_KEY5));
+#ifdef __PTK_FRAMEWORK_OBJC_KEY0
+    testassert(pthread_getspecific(__PTK_FRAMEWORK_OBJC_KEY0));
 #endif
 
     // Start the threads