From: Apple Date: Fri, 5 Jun 2015 01:14:17 +0000 (+0000) Subject: objc4-647.tar.gz X-Git-Tag: os-x-10102^0 X-Git-Url: https://git.saurik.com/apple/objc4.git/commitdiff_plain/5984afcec0d481527cfed40206070c7feb41f46f objc4-647.tar.gz --- diff --git a/runtime/objc-config.h b/runtime/objc-config.h index 0e33b04..39ada63 100644 --- a/runtime/objc-config.h +++ b/runtime/objc-config.h @@ -142,6 +142,13 @@ # 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 diff --git a/runtime/objc-os.h b/runtime/objc-os.h index 52b2ef5..1765d6e 100644 --- a/runtime/objc-os.h +++ b/runtime/objc-os.h @@ -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 +// do a qos override before taking rw lock in objc + +#include +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; + } } diff --git a/runtime/objc-runtime-new.mm b/runtime/objc-runtime-new.mm index a3cc964..c020399 100644 --- a/runtime/objc-runtime-new.mm +++ b/runtime/objc-runtime-new.mm @@ -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 } diff --git a/runtime/objc-sel-table.s b/runtime/objc-sel-table.s index 0b1cdbb..32bfae7 100644 --- a/runtime/objc-sel-table.s +++ b/runtime/objc-sel-table.s @@ -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 */ diff --git a/test/synchronized-counter.m b/test/synchronized-counter.m index 6356f0d..7f6bd19 100644 --- a/test/synchronized-counter.m +++ b/test/synchronized-counter.m @@ -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