From 9062bda79b4a7b381da5611c94de090ad33dcb4d Mon Sep 17 00:00:00 2001 From: Tadeu Zagallo Date: Mon, 25 May 2015 05:19:53 -0700 Subject: [PATCH] [ReactNative] Add RCTAssertThread and restrict -[UIManager addUIBlock:] to _shadowQueue Summary: @public Add `RCTAssertThread` to `RCTAssert.h` for convenience when checking the current/queue, it accepts either a `NSString *`, `NSThread *` or `dispatch_queue_t` as the object to be checked Also add a check to `-[RCTUIManager addUIBlock:]` - There was a discussion on github (https://github.com/facebook/react-native/issues/1365) due to the weird behavior caused by calling it from a different thread/queue (it might be added after `batchDidComplete` has been called and will just be dispatched on the next call from JS to objc) Test Plan: Change `-[RCTAnimationExperimentalManager methodQueue]` to return `dispatch_get_main_queue()` and run the 2048 example, it should dispatch with a helpful message (screenshot on the comments) --- React/Base/RCTAssert.h | 28 ++++++++++++++++++++++++++++ React/Base/RCTAssert.m | 17 +++++++++++++++++ React/Base/RCTLog.h | 6 ------ React/Base/RCTLog.m | 25 ++++--------------------- React/Base/RCTProfile.m | 4 ++-- React/Modules/RCTUIManager.m | 4 ++++ 6 files changed, 55 insertions(+), 29 deletions(-) diff --git a/React/Base/RCTAssert.h b/React/Base/RCTAssert.h index b0a3c5c52..1fc5b9d32 100644 --- a/React/Base/RCTAssert.h +++ b/React/Base/RCTAssert.h @@ -62,3 +62,31 @@ RCT_EXTERN RCTAssertFunction RCTGetAssertFunction(void); * assert info to an extra service without changing the default behavior. */ RCT_EXTERN void RCTAddAssertFunction(RCTAssertFunction assertFunction); + +/** + * Get the current thread's name (or the current queue, if in debug mode) + */ +RCT_EXTERN NSString *RCTCurrentThreadName(void); + +/** + * Convenience macro to assert which thread is currently running (DEBUG mode only) + */ +#if DEBUG + +#define RCTAssertThread(thread, format...) \ +_Pragma("clang diagnostic push") \ +_Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") \ +RCTAssert( \ + [(id)thread isKindOfClass:[NSString class]] ? \ + [RCTCurrentThreadName() isEqualToString:(NSString *)thread] : \ + [(id)thread isKindOfClass:[NSThread class]] ? \ + [NSThread currentThread] == (NSThread *)thread : \ + dispatch_get_current_queue() == (dispatch_queue_t)thread, \ + format); \ +_Pragma("clang diagnostic pop") + +#else + +#define RCTAssertThread(thread, format...) + +#endif diff --git a/React/Base/RCTAssert.m b/React/Base/RCTAssert.m index 86d71cd80..41369406c 100644 --- a/React/Base/RCTAssert.m +++ b/React/Base/RCTAssert.m @@ -60,3 +60,20 @@ void RCTAddAssertFunction(RCTAssertFunction assertFunction) RCTCurrentAssertFunction = assertFunction; } } + +NSString *RCTCurrentThreadName(void) +{ + NSThread *thread = [NSThread currentThread]; + NSString *threadName = [thread isMainThread] ? @"main" : thread.name; + if (threadName.length == 0) { +#if DEBUG // This is DEBUG not RCT_DEBUG because it *really* must not ship in RC +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + threadName = @(dispatch_queue_get_label(dispatch_get_current_queue())); +#pragma clang diagnostic pop +#else + threadName = [NSString stringWithFormat:@"%p", thread]; +#endif + } + return threadName; +} diff --git a/React/Base/RCTLog.h b/React/Base/RCTLog.h index 75cbe722e..5dead10be 100644 --- a/React/Base/RCTLog.h +++ b/React/Base/RCTLog.h @@ -42,18 +42,12 @@ typedef void (^RCTLogFunction)( NSString *message ); -/** - * Get a given thread's name (or the current queue, if in debug mode) - */ -RCT_EXTERN NSString *RCTThreadName(NSThread *); - /** * A method to generate a string from a collection of log data. To omit any * particular data from the log, just pass nil or zero for the argument. */ RCT_EXTERN NSString *RCTFormatLog( NSDate *timestamp, - NSThread *thread, RCTLogLevel level, NSString *fileName, NSNumber *lineNumber, diff --git a/React/Base/RCTLog.m b/React/Base/RCTLog.m index fb70fe6d3..d99bb4fe1 100644 --- a/React/Base/RCTLog.m +++ b/React/Base/RCTLog.m @@ -53,7 +53,7 @@ RCTLogFunction RCTDefaultLogFunction = ^( ) { NSString *log = RCTFormatLog( - [NSDate date], [NSThread currentThread], level, fileName, lineNumber, message + [NSDate date], level, fileName, lineNumber, message ); fprintf(stderr, "%s\n", log.UTF8String); fflush(stderr); @@ -99,25 +99,8 @@ void RCTPerformBlockWithLogPrefix(void (^block)(void), NSString *prefix) [prefixStack removeLastObject]; } -NSString *RCTThreadName(NSThread *thread) -{ - NSString *threadName = [thread isMainThread] ? @"main" : thread.name; - if (threadName.length == 0) { -#if DEBUG // This is DEBUG not RCT_DEBUG because it *really* must not ship in RC -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - threadName = @(dispatch_queue_get_label(dispatch_get_current_queue())); -#pragma clang diagnostic pop -#else - threadName = [NSString stringWithFormat:@"%p", thread]; -#endif - } - return threadName; -} - NSString *RCTFormatLog( NSDate *timestamp, - NSThread *thread, RCTLogLevel level, NSString *fileName, NSNumber *lineNumber, @@ -137,9 +120,9 @@ NSString *RCTFormatLog( if (level) { [log appendFormat:@"[%s]", RCTLogLevels[level - 1]]; } - if (thread) { - [log appendFormat:@"[tid:%@]", RCTThreadName(thread)]; - } + + [log appendFormat:@"[tid:%@]", RCTCurrentThreadName()]; + if (fileName) { fileName = [fileName lastPathComponent]; if (lineNumber) { diff --git a/React/Base/RCTProfile.m b/React/Base/RCTProfile.m index 19c6900c7..29e606e86 100644 --- a/React/Base/RCTProfile.m +++ b/React/Base/RCTProfile.m @@ -13,8 +13,8 @@ #import +#import "RCTAssert.h" #import "RCTDefines.h" -#import "RCTLog.h" #import "RCTUtils.h" #if RCT_DEV @@ -43,7 +43,7 @@ NSLock *_RCTProfileLock; #define RCTProfileAddEvent(type, props...) \ [RCTProfileInfo[type] addObject:@{ \ @"pid": @([[NSProcessInfo processInfo] processIdentifier]), \ - @"tid": RCTThreadName([NSThread currentThread]), \ + @"tid": RCTCurrentThreadName(), \ props \ }]; diff --git a/React/Modules/RCTUIManager.m b/React/Modules/RCTUIManager.m index e9734c204..ec12aa6fb 100644 --- a/React/Modules/RCTUIManager.m +++ b/React/Modules/RCTUIManager.m @@ -390,6 +390,10 @@ static NSDictionary *RCTViewConfigForModule(Class managerClass, NSString *viewNa - (void)addUIBlock:(RCTViewManagerUIBlock)block { + RCTAssertThread(_shadowQueue, + @"-[RCTUIManager addUIBlock:] should only be called from the " + "UIManager's _shadowQueue (it may be accessed via `bridge.uiManager.methodQueue`)"); + if (!self.isValid) { return; }