Replaced isMainThread checks with a proper test for main queue

Summary:
As per https://twitter.com/olebegemann/status/738656134731599872, our use of "main thread" to mean "main queue" seems to be unsafe.

This diff replaces the `NSThread.isMainQueue` checks with dispatch_get_specific(), which is the recommended approach.

I've also replaced all use of "MainThread" terminology with "MainQueue", and taken the opportunity to deprecate the "sync" param of `RCTExecuteOnMainThread()`, which, while we do still use it in a few places, is incredibly unsafe and shouldn't be encouraged.

Reviewed By: javache

Differential Revision: D3384910

fbshipit-source-id: ea7c216013372267b82eb25a38db5eb4cd46a089
This commit is contained in:
Nick Lockwood 2016-06-06 07:57:55 -07:00 committed by Facebook Github Bot 6
parent 1048e5d344
commit 72b363d7fc
19 changed files with 121 additions and 83 deletions

View File

@ -48,7 +48,7 @@ RCT_EXPORT_MODULE()
@interface RCTTestCustomInitModule : NSObject <RCTBridgeModule>
@property (nonatomic, assign) BOOL initializedOnMainThread;
@property (nonatomic, assign) BOOL initializedOnMainQueue;
@end
@ -62,7 +62,7 @@ RCT_EXPORT_MODULE()
- (id)init
{
if ((self = [super init])) {
_initializedOnMainThread = [NSThread isMainThread];
_initializedOnMainQueue = RCTIsMainQueue();
}
return self;
}
@ -72,7 +72,7 @@ RCT_EXPORT_MODULE()
@interface RCTTestCustomSetBridgeModule : NSObject <RCTBridgeModule>
@property (nonatomic, assign) BOOL setBridgeOnMainThread;
@property (nonatomic, assign) BOOL setBridgeOnMainQueue;
@end
@ -86,7 +86,7 @@ RCT_EXPORT_MODULE()
- (void)setBridge:(RCTBridge *)bridge
{
_bridge = bridge;
_setBridgeOnMainThread = [NSThread isMainThread];
_setBridgeOnMainQueue = RCTIsMainQueue();
}
@end
@ -95,7 +95,7 @@ RCT_EXPORT_MODULE()
@interface RCTTestExportConstantsModule : NSObject <RCTBridgeModule>
@property (nonatomic, assign) BOOL exportedConstants;
@property (nonatomic, assign) BOOL exportedConstantsOnMainThread;
@property (nonatomic, assign) BOOL exportedConstantsOnMainQueue;
@end
@ -109,7 +109,7 @@ RCT_EXPORT_MODULE()
- (NSDictionary<NSString *, id> *)constantsToExport
{
_exportedConstants = YES;
_exportedConstantsOnMainThread = [NSThread isMainThread];
_exportedConstantsOnMainQueue = RCTIsMainQueue();
return @{ @"foo": @"bar" };
}
@ -137,7 +137,7 @@ RCT_EXPORT_MODULE()
BOOL _customSetBridgeModuleNotificationSent;
BOOL _exportConstantsModuleNotificationSent;
BOOL _lazyInitModuleNotificationSent;
BOOL _lazyInitModuleNotificationSentOnMainThread;
BOOL _lazyInitModuleNotificationSentOnMainQueue;
BOOL _viewManagerModuleNotificationSent;
RCTTestInjectedModule *_injectedModule;
}
@ -196,7 +196,7 @@ RCT_EXPORT_MODULE()
_exportConstantsModuleNotificationSent = YES;
} else if ([module isKindOfClass:[RCTLazyInitModule class]]) {
_lazyInitModuleNotificationSent = YES;
_lazyInitModuleNotificationSentOnMainThread = [NSThread isMainThread];
_lazyInitModuleNotificationSentOnMainQueue = RCTIsMainQueue();
}
}
@ -214,7 +214,7 @@ RCT_EXPORT_MODULE()
RUN_RUNLOOP_WHILE(!_customInitModuleNotificationSent);
XCTAssertTrue(_customInitModuleNotificationSent);
RCTTestCustomInitModule *module = [_bridge moduleForClass:[RCTTestCustomInitModule class]];
XCTAssertTrue(module.initializedOnMainThread);
XCTAssertTrue(module.initializedOnMainQueue);
XCTAssertEqual(module.bridge, _bridge.batchedBridge);
XCTAssertNotNil(module.methodQueue);
}
@ -230,7 +230,7 @@ RCT_EXPORT_MODULE()
RUN_RUNLOOP_WHILE(!module);
XCTAssertTrue(_customSetBridgeModuleNotificationSent);
XCTAssertFalse(module.setBridgeOnMainThread);
XCTAssertFalse(module.setBridgeOnMainQueue);
XCTAssertEqual(module.bridge, _bridge.batchedBridge);
XCTAssertNotNil(module.methodQueue);
}
@ -242,7 +242,7 @@ RCT_EXPORT_MODULE()
RCTTestExportConstantsModule *module = [_bridge moduleForClass:[RCTTestExportConstantsModule class]];
RUN_RUNLOOP_WHILE(!module.exportedConstants);
XCTAssertTrue(module.exportedConstants);
XCTAssertTrue(module.exportedConstantsOnMainThread);
XCTAssertTrue(module.exportedConstantsOnMainQueue);
XCTAssertEqual(module.bridge, _bridge.batchedBridge);
XCTAssertNotNil(module.methodQueue);
}
@ -258,7 +258,7 @@ RCT_EXPORT_MODULE()
RUN_RUNLOOP_WHILE(!module);
XCTAssertTrue(_lazyInitModuleNotificationSent);
XCTAssertFalse(_lazyInitModuleNotificationSentOnMainThread);
XCTAssertFalse(_lazyInitModuleNotificationSentOnMainQueue);
XCTAssertNotNil(module);
XCTAssertEqual(module.bridge, _bridge.batchedBridge);
XCTAssertNotNil(module.methodQueue);

View File

@ -293,7 +293,7 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
__weak RCTImageLoader *weakSelf = self;
void (^completionHandler)(NSError *error, id imageOrData) = ^(NSError *error, id imageOrData) {
if ([NSThread isMainThread]) {
if (RCTIsMainQueue()) {
// Most loaders do not return on the main thread, so caller is probably not
// expecting it, and may do expensive post-processing in the callback
@ -545,7 +545,7 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
__block volatile uint32_t cancelled = 0;
void (^completionHandler)(NSError *, UIImage *) = ^(NSError *error, UIImage *image) {
if ([NSThread isMainThread]) {
if (RCTIsMainQueue()) {
// Most loaders do not return on the main thread, so caller is probably not
// expecting it, and may do expensive post-processing in the callback
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

View File

@ -191,7 +191,7 @@ RCT_EXPORT_METHOD(addImageFromBase64:(NSString *)base64String
- (NSString *)storeImage:(UIImage *)image
{
RCTAssertMainThread();
RCTAssertMainQueue();
RCTLogWarn(@"RCTImageStoreManager.storeImage() is deprecated and has poor performance. Use an alternative method instead.");
__block NSString *imageTag;
dispatch_sync(_methodQueue, ^{
@ -202,7 +202,7 @@ RCT_EXPORT_METHOD(addImageFromBase64:(NSString *)base64String
- (UIImage *)imageForTag:(NSString *)imageTag
{
RCTAssertMainThread();
RCTAssertMainQueue();
RCTLogWarn(@"RCTImageStoreManager.imageForTag() is deprecated and has poor performance. Use an alternative method instead.");
__block NSData *imageData;
dispatch_sync(_methodQueue, ^{

View File

@ -263,22 +263,18 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
// Blur on a background thread to avoid blocking interaction
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
UIImage *image = RCTBlurredImageWithRadius(loadedImage, blurRadius);
RCTExecuteOnMainThread(^{
RCTExecuteOnMainQueue(^{
setImageBlock(image);
}, NO);
});
});
} else {
// No blur, so try to set the image on the main thread synchronously to minimize image
// flashing. (For instance, if this view gets attached to a window, then -didMoveToWindow
// calls -reloadImage, and we want to set the image synchronously if possible so that the
// image property is set in the same CATransaction that attaches this view to the window.)
if ([NSThread isMainThread]) {
RCTExecuteOnMainQueue(^{
setImageBlock(loadedImage);
} else {
RCTExecuteOnMainThread(^{
setImageBlock(loadedImage);
}, NO);
}
});
}
}];
} else {

View File

@ -215,7 +215,7 @@ RCT_EXPORT_MODULE()
- (void)executeBlockOnJavaScriptQueue:(dispatch_block_t)block
{
RCTExecuteOnMainThread(block, NO);
RCTExecuteOnMainQueue(block);
}
- (void)executeAsyncBlockOnJavaScriptQueue:(dispatch_block_t)block

View File

@ -11,6 +11,11 @@
#import "RCTDefines.h"
/*
* Defined in RCTUtils.m
*/
RCT_EXTERN BOOL RCTIsMainQueue(void);
/**
* This is the main assert macro that you should use. Asserts should be compiled out
* in production builds. You can customize the assert behaviour by setting a custom
@ -58,13 +63,11 @@ RCT_EXTERN NSString *const RCTFatalExceptionName;
/**
* A block signature to be used for custom assertion handling.
*/
typedef void (^RCTAssertFunction)(
NSString *condition,
typedef void (^RCTAssertFunction)(NSString *condition,
NSString *fileName,
NSNumber *lineNumber,
NSString *function,
NSString *message
);
NSString *message);
typedef void (^RCTFatalHandler)(NSError *error);
@ -74,17 +77,23 @@ typedef void (^RCTFatalHandler)(NSError *error);
#define RCTAssertParam(name) RCTAssert(name, @"'%s' is a required parameter", #name)
/**
* Convenience macro for asserting that we're running on main thread.
* Convenience macro for asserting that we're running on main queue.
*/
#define RCTAssertMainThread() RCTAssert([NSThread isMainThread], \
#define RCTAssertMainQueue() RCTAssert(RCTIsMainQueue(), \
@"This function must be called on the main thread")
/**
* Convenience macro for asserting that we're running off the main thread.
* Convenience macro for asserting that we're running off the main queue.
*/
#define RCTAssertNotMainThread() RCTAssert(![NSThread isMainThread], \
#define RCTAssertNotMainQueue() RCTAssert(!RCTIsMainQueue(), \
@"This function must not be called on the main thread")
/**
* Deprecated, do not use
*/
#define RCTAssertMainThread() RCTAssertMainQueue()
#define RCTAssertNotMainThread() RCTAssertNotMainQueue()
/**
* These methods get and set the current assert function called by the RCTAssert
* macros. You can use these to replace the standard behavior with custom assert

View File

@ -87,7 +87,7 @@ void RCTPerformBlockWithAssertFunction(void (^block)(void), RCTAssertFunction as
NSString *RCTCurrentThreadName(void)
{
NSThread *thread = [NSThread currentThread];
NSString *threadName = thread.isMainThread ? @"main" : thread.name;
NSString *threadName = RCTIsMainQueue() || thread.isMainThread ? @"main" : thread.name;
if (threadName.length == 0) {
const char *label = dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL);
if (label && strlen(label) > 0) {

View File

@ -366,7 +366,7 @@ RCT_EXTERN NSArray<Class> *RCTGetModuleClasses(void);
// Synchronously set up the pre-initialized modules
for (RCTModuleData *moduleData in _moduleDataByID) {
if (moduleData.hasInstance &&
(!moduleData.requiresMainThreadSetup || [NSThread isMainThread])) {
(!moduleData.requiresMainQueueSetup || RCTIsMainQueue())) {
// Modules that were pre-initialized should ideally be set up before
// bridge init has finished, otherwise the caller may try to access the
// module directly rather than via `[bridge moduleForClass:]`, which won't
@ -383,10 +383,10 @@ RCT_EXTERN NSArray<Class> *RCTGetModuleClasses(void);
// Set up modules that require main thread init or constants export
RCTPerformanceLoggerSet(RCTPLNativeModuleMainThread, 0);
NSUInteger modulesOnMainThreadCount = 0;
NSUInteger modulesOnMainQueueCount = 0;
for (RCTModuleData *moduleData in _moduleDataByID) {
__weak RCTBatchedBridge *weakSelf = self;
if (moduleData.requiresMainThreadSetup || moduleData.hasConstantsToExport) {
if (moduleData.requiresMainQueueSetup || moduleData.hasConstantsToExport) {
// Modules that need to be set up on the main thread cannot be initialized
// lazily when required without doing a dispatch_sync to the main thread,
// which can result in deadlock. To avoid this, we initialize all of these
@ -400,12 +400,12 @@ RCT_EXTERN NSArray<Class> *RCTGetModuleClasses(void);
RCTPerformanceLoggerAppendEnd(RCTPLNativeModuleMainThread);
}
});
modulesOnMainThreadCount++;
modulesOnMainQueueCount++;
}
}
RCTPerformanceLoggerEnd(RCTPLNativeModuleInit);
RCTPerformanceLoggerSet(RCTPLNativeModuleMainThreadUsesCount, modulesOnMainThreadCount);
RCTPerformanceLoggerSet(RCTPLNativeModuleMainThreadUsesCount, modulesOnMainQueueCount);
}
- (void)setUpExecutor
@ -509,7 +509,7 @@ RCT_EXTERN NSArray<Class> *RCTGetModuleClasses(void);
- (void)stopLoadingWithError:(NSError *)error
{
RCTAssertMainThread();
RCTAssertMainQueue();
if (!_valid || !_loading) {
return;
@ -551,7 +551,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR
- (void)setExecutorClass:(Class)executorClass
{
RCTAssertMainThread();
RCTAssertMainQueue();
_parentBridge.executorClass = executorClass;
}
@ -599,7 +599,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR
return;
}
RCTAssertMainThread();
RCTAssertMainQueue();
RCTAssert(_javaScriptExecutor != nil, @"Can't complete invalidation without a JS executor");
_loading = NO;
@ -1004,7 +1004,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR
- (void)startProfiling
{
RCTAssertMainThread();
RCTAssertMainQueue();
[_javaScriptExecutor executeBlockOnJavaScriptQueue:^{
RCTProfileInit(self);
@ -1013,7 +1013,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR
- (void)stopProfiling:(void (^)(NSData *))callback
{
RCTAssertMainThread();
RCTAssertMainQueue();
[_javaScriptExecutor executeBlockOnJavaScriptQueue:^{
RCTProfileEnd(self, ^(NSString *log) {

View File

@ -117,7 +117,7 @@ static RCTBridge *RCTCurrentBridgeInstance = nil;
_delegate = delegate;
_launchOptions = [launchOptions copy];
[self setUp];
RCTExecuteOnMainThread(^{ [self bindKeys]; }, NO);
RCTExecuteOnMainQueue(^{ [self bindKeys]; });
}
return self;
}
@ -134,7 +134,7 @@ static RCTBridge *RCTCurrentBridgeInstance = nil;
_moduleProvider = block;
_launchOptions = [launchOptions copy];
[self setUp];
RCTExecuteOnMainThread(^{ [self bindKeys]; }, NO);
RCTExecuteOnMainQueue(^{ [self bindKeys]; });
}
return self;
}
@ -145,7 +145,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
{
/**
* This runs only on the main thread, but crashes the subclass
* RCTAssertMainThread();
* RCTAssertMainQueue();
*/
[[NSNotificationCenter defaultCenter] removeObserver:self];
[self invalidate];
@ -153,7 +153,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
- (void)bindKeys
{
RCTAssertMainThread();
RCTAssertMainQueue();
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(reload)
@ -270,9 +270,9 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
self.batchedBridge = nil;
if (batchedBridge) {
RCTExecuteOnMainThread(^{
RCTExecuteOnMainQueue(^{
[batchedBridge invalidate];
}, NO);
});
}
}

View File

@ -884,7 +884,7 @@ RCT_ENUM_CONVERTER(RCTAnimationType, (@{
}
__block UIImage *image;
if (![NSThread isMainThread]) {
if (!RCTIsMainQueue()) {
// It seems that none of the UIImage loading methods can be guaranteed
// thread safe, so we'll pick the lesser of two evils here and block rather
// than run the risk of crashing

View File

@ -271,7 +271,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
modifierFlags:(UIKeyModifierFlags)flags
action:(void (^)(UIKeyCommand *))block
{
RCTAssertMainThread();
RCTAssertMainQueue();
if (input.length && flags && RCTIsIOS8OrEarlier()) {
@ -296,7 +296,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
- (void)unregisterKeyCommandWithInput:(NSString *)input
modifierFlags:(UIKeyModifierFlags)flags
{
RCTAssertMainThread();
RCTAssertMainQueue();
for (RCTKeyCommand *command in _commands.allObjects) {
if ([command matchesInput:input flags:flags]) {
@ -309,7 +309,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
- (BOOL)isKeyCommandRegisteredForInput:(NSString *)input
modifierFlags:(UIKeyModifierFlags)flags
{
RCTAssertMainThread();
RCTAssertMainQueue();
for (RCTKeyCommand *command in _commands) {
if ([command matchesInput:input flags:flags]) {
@ -323,7 +323,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
modifierFlags:(UIKeyModifierFlags)flags
action:(void (^)(UIKeyCommand *))block
{
RCTAssertMainThread();
RCTAssertMainQueue();
if (input.length && flags && RCTIsIOS8OrEarlier()) {
@ -348,7 +348,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
- (void)unregisterDoublePressKeyCommandWithInput:(NSString *)input
modifierFlags:(UIKeyModifierFlags)flags
{
RCTAssertMainThread();
RCTAssertMainQueue();
for (RCTKeyCommand *command in _commands.allObjects) {
if ([command matchesInput:input flags:flags]) {
@ -361,7 +361,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
- (BOOL)isDoublePressKeyCommandRegisteredForInput:(NSString *)input
modifierFlags:(UIKeyModifierFlags)flags
{
RCTAssertMainThread();
RCTAssertMainQueue();
for (RCTKeyCommand *command in _commands) {
if ([command matchesInput:input flags:flags]) {

View File

@ -48,7 +48,7 @@
/**
* Returns YES if module instance must be created on the main thread.
*/
@property (nonatomic, assign, readonly) BOOL requiresMainThreadSetup;
@property (nonatomic, assign, readonly) BOOL requiresMainQueueSetup;
/**
* Returns YES if module has constants to export.

View File

@ -44,7 +44,7 @@
// If a module overrides `init` then we must assume that it expects to be
// initialized on the main thread, because it may need to access UIKit.
_requiresMainThreadSetup = !_instance &&
_requiresMainQueueSetup = !_instance &&
[_moduleClass instanceMethodForSelector:@selector(init)] != objectInitMethod;
// If a module overrides `constantsToExport` then we must assume that it
@ -85,8 +85,8 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init);
[_instanceLock lock];
if (!_setupComplete && _bridge.valid) {
if (!_instance) {
if (RCT_DEBUG && _requiresMainThreadSetup) {
RCTAssertMainThread();
if (RCT_DEBUG && _requiresMainQueueSetup) {
RCTAssertMainQueue();
}
RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"[RCTModuleData setUpInstanceAndBridge] [_moduleClass new]", nil);
_instance = [_moduleClass new];
@ -128,13 +128,13 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init);
// If we're here, then the module is completely initialized,
// except for what finishSetupForInstance does. When the instance
// method is called after moduleSetupComplete,
// finishSetupForInstance will run. If _requiresMainThreadSetup
// finishSetupForInstance will run. If _requiresMainQueueSetup
// is true, getting the instance will block waiting for the main
// thread, which could take a while if the main thread is busy
// (I've seen 50ms in testing). So we clear that flag, since
// nothing in finishSetupForInstance needs to be run on the main
// thread.
_requiresMainThreadSetup = NO;
_requiresMainQueueSetup = NO;
}
}
@ -208,7 +208,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init);
{
if (!_setupComplete) {
RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, [NSString stringWithFormat:@"[RCTModuleData instanceForClass:%@]", _moduleClass], nil);
if (_requiresMainThreadSetup) {
if (_requiresMainQueueSetup) {
// The chances of deadlock here are low, because module init very rarely
// calls out to other threads, however we can't control when a module might
// get accessed by client code during bridge setup, and a very low risk of

View File

@ -58,7 +58,7 @@ NSString *const RCTContentDidAppearNotification = @"RCTContentDidAppearNotificat
moduleName:(NSString *)moduleName
initialProperties:(NSDictionary *)initialProperties
{
RCTAssertMainThread();
RCTAssertMainQueue();
RCTAssert(bridge, @"A bridge instance is required to create an RCTRootView");
RCTAssert(moduleName, @"A moduleName is required to create an RCTRootView");
@ -175,7 +175,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
- (NSNumber *)reactTag
{
RCTAssertMainThread();
RCTAssertMainQueue();
if (!super.reactTag) {
/**
* Every root view that is created must have a unique react tag.
@ -191,14 +191,14 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
- (void)bridgeDidReload
{
RCTAssertMainThread();
RCTAssertMainQueue();
// Clear the reactTag so it can be re-assigned
self.reactTag = nil;
}
- (void)javaScriptDidLoad:(NSNotification *)notification
{
RCTAssertMainThread();
RCTAssertMainQueue();
RCTBridge *bridge = notification.userInfo[@"bridge"];
[self bundleFinishedLoading:bridge];
}
@ -250,7 +250,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
- (void)setAppProperties:(NSDictionary *)appProperties
{
RCTAssertMainThread();
RCTAssertMainQueue();
if ([_appProperties isEqualToDictionary:appProperties]) {
return;

View File

@ -29,9 +29,19 @@ RCT_EXTERN id RCTJSONClean(id object);
// Get MD5 hash of a string
RCT_EXTERN NSString *RCTMD5Hash(NSString *string);
// Execute the specified block on the main thread. Unlike dispatch_sync/async
// this will not context-switch if we're already running on the main thread.
RCT_EXTERN void RCTExecuteOnMainThread(dispatch_block_t block, BOOL sync);
// Check is we are currently on the main queue (not to be confused with
// the main thread, which is not neccesarily the same thing)
// https://twitter.com/olebegemann/status/738656134731599872
RCT_EXTERN BOOL RCTIsMainQueue(void);
// Execute the specified block on the main queue. Unlike dispatch_async()
// this will execute immediately if we're already on the main queue.
RCT_EXTERN void RCTExecuteOnMainQueue(dispatch_block_t block);
// Deprecated - do not use.
RCT_EXTERN void RCTExecuteOnMainThread(dispatch_block_t block, BOOL sync)
__deprecated_msg("Use RCTExecuteOnMainQueue instead. RCTExecuteOnMainQueue is "
"async. If you need to use the `sync` option... please don't.");
// Get screen metrics in a thread-safe way
RCT_EXTERN CGFloat RCTScreenScale(void);

View File

@ -19,6 +19,7 @@
#import <zlib.h>
#import <dlfcn.h>
#import "RCTAssert.h"
#import "RCTLog.h"
NSString *const RCTErrorUnspecified = @"EUNSPECIFIED";
@ -228,9 +229,31 @@ NSString *RCTMD5Hash(NSString *string)
];
}
BOOL RCTIsMainQueue()
{
static void *mainQueueKey = &mainQueueKey;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
dispatch_queue_set_specific(dispatch_get_main_queue(),
mainQueueKey, mainQueueKey, NULL);
});
return dispatch_get_specific(mainQueueKey) == mainQueueKey;
}
void RCTExecuteOnMainQueue(dispatch_block_t block)
{
if (RCTIsMainQueue()) {
block();
} else {
dispatch_async(dispatch_get_main_queue(), ^{
block();
});
}
}
void RCTExecuteOnMainThread(dispatch_block_t block, BOOL sync)
{
if ([NSThread isMainThread]) {
if (RCTIsMainQueue()) {
block();
} else if (sync) {
dispatch_sync(dispatch_get_main_queue(), ^{

View File

@ -16,7 +16,7 @@
static NSString *RCTCurrentAppBackgroundState()
{
RCTAssertMainThread();
RCTAssertMainQueue();
static NSDictionary *states;
static dispatch_once_t onceToken;

View File

@ -366,7 +366,7 @@ dispatch_queue_t RCTGetUIManagerQueue(void)
- (void)registerRootView:(UIView *)rootView withSizeFlexibility:(RCTRootViewSizeFlexibility)sizeFlexibility
{
RCTAssertMainThread();
RCTAssertMainQueue();
NSNumber *reactTag = rootView.reactTag;
RCTAssert(RCTIsReactRootView(reactTag),
@ -404,13 +404,13 @@ dispatch_queue_t RCTGetUIManagerQueue(void)
- (UIView *)viewForReactTag:(NSNumber *)reactTag
{
RCTAssertMainThread();
RCTAssertMainQueue();
return _viewRegistry[reactTag];
}
- (void)setFrame:(CGRect)frame forView:(UIView *)view
{
RCTAssertMainThread();
RCTAssertMainQueue();
// The following variable has no meaning if the view is not a react root view
RCTRootViewSizeFlexibility sizeFlexibility = RCTRootViewSizeFlexibilityNone;
@ -453,7 +453,7 @@ dispatch_queue_t RCTGetUIManagerQueue(void)
- (void)setIntrinsicContentSize:(CGSize)size forView:(UIView *)view
{
RCTAssertMainThread();
RCTAssertMainQueue();
NSNumber *reactTag = view.reactTag;
dispatch_async(RCTGetUIManagerQueue(), ^{
@ -468,7 +468,7 @@ dispatch_queue_t RCTGetUIManagerQueue(void)
- (void)setBackgroundColor:(UIColor *)color forView:(UIView *)view
{
RCTAssertMainThread();
RCTAssertMainQueue();
NSNumber *reactTag = view.reactTag;
@ -534,7 +534,7 @@ dispatch_queue_t RCTGetUIManagerQueue(void)
- (RCTViewManagerUIBlock)uiBlockWithLayoutUpdateForRootView:(RCTRootShadowView *)rootShadowView
{
RCTAssert(![NSThread isMainThread], @"Should be called on shadow thread");
RCTAssert(!RCTIsMainQueue(), @"Should be called on shadow queue");
// This is nuanced. In the JS thread, we create a new update buffer
// `frameTags`/`frames` that is created/mutated in the JS thread. We access
@ -835,7 +835,7 @@ RCT_EXPORT_METHOD(removeRootView:(nonnull NSNumber *)rootReactTag)
[_rootViewTags removeObject:rootReactTag];
[self addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry){
RCTAssertMainThread();
RCTAssertMainQueue();
UIView *rootView = viewRegistry[rootReactTag];
[uiManager _purgeChildren:(NSArray<id<RCTComponent>> *)rootView.reactSubviews
fromRegistry:(NSMutableDictionary<NSNumber *, id<RCTComponent>> *)viewRegistry];
@ -1488,7 +1488,7 @@ RCT_EXPORT_METHOD(clearJSResponder)
static NSDictionary *RCTExportedDimensions(BOOL rotateBounds)
{
RCTAssertMainThread();
RCTAssertMainQueue();
// Don't use RCTScreenSize since it the interface orientation doesn't apply to it
CGRect screenSize = [[UIScreen mainScreen] bounds];

View File

@ -87,7 +87,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
- (UIView *)createViewWithTag:(NSNumber *)tag
{
RCTAssertMainThread();
RCTAssertMainQueue();
UIView *view = [self.manager view];
view.reactTag = tag;