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; }