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:
parent
1048e5d344
commit
72b363d7fc
|
@ -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);
|
||||
|
|
|
@ -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), ^{
|
||||
|
|
|
@ -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, ^{
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -215,7 +215,7 @@ RCT_EXPORT_MODULE()
|
|||
|
||||
- (void)executeBlockOnJavaScriptQueue:(dispatch_block_t)block
|
||||
{
|
||||
RCTExecuteOnMainThread(block, NO);
|
||||
RCTExecuteOnMainQueue(block);
|
||||
}
|
||||
|
||||
- (void)executeAsyncBlockOnJavaScriptQueue:(dispatch_block_t)block
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]) {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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(), ^{
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
static NSString *RCTCurrentAppBackgroundState()
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
RCTAssertMainQueue();
|
||||
|
||||
static NSDictionary *states;
|
||||
static dispatch_once_t onceToken;
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -87,7 +87,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||
|
||||
- (UIView *)createViewWithTag:(NSNumber *)tag
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
RCTAssertMainQueue();
|
||||
|
||||
UIView *view = [self.manager view];
|
||||
view.reactTag = tag;
|
||||
|
|
Loading…
Reference in New Issue