[ReactNative] Allow bridge modules to run on the JavaScript thread

This commit is contained in:
Tadeu Zagallo
2015-04-25 19:18:39 -07:00
parent 8a3b0fa9e8
commit dd6bce78e1
4 changed files with 41 additions and 15 deletions

View File

@@ -31,6 +31,8 @@
NSString *const RCTReloadNotification = @"RCTReloadNotification";
NSString *const RCTJavaScriptDidLoadNotification = @"RCTJavaScriptDidLoadNotification";
dispatch_queue_t const RCTJSThread = nil;
/**
* Must be kept in sync with `MessageQueue.js`.
*/
@@ -795,6 +797,7 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
_bundleURL = bundleURL;
_moduleProvider = block;
_launchOptions = [launchOptions copy];
[self setUp];
[self bindKeys];
}
@@ -872,6 +875,8 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
dispatch_queue_t queue = [module methodQueue];
if (queue) {
_queuesByID[moduleID] = queue;
} else {
_queuesByID[moduleID] = [NSNull null];
}
}
}];
@@ -1128,6 +1133,16 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
#pragma mark - Payload Generation
- (void)dispatchBlock:(dispatch_block_t)block forModule:(NSNumber *)moduleID
{
id queue = _queuesByID[moduleID];
if (queue == [NSNull null]) {
[_javaScriptExecutor executeBlockOnJavaScriptQueue:block];
} else {
dispatch_async(queue ?: _methodQueue, block);
}
}
- (void)_invokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args context:(NSNumber *)context
{
#if BATCHED_BRIDGE
@@ -1235,10 +1250,9 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
// TODO: batchDidComplete is only used by RCTUIManager - can we eliminate this special case?
[_modulesByID enumerateObjectsUsingBlock:^(id<RCTBridgeModule> module, NSNumber *moduleID, BOOL *stop) {
if ([module respondsToSelector:@selector(batchDidComplete)]) {
dispatch_queue_t queue = _queuesByID[moduleID];
dispatch_async(queue ?: _methodQueue, ^{
[self dispatchBlock:^{
[module batchDidComplete];
});
} forModule:moduleID];
}
}];
}
@@ -1273,8 +1287,7 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
}
__weak RCTBridge *weakSelf = self;
dispatch_queue_t queue = _queuesByID[moduleID];
dispatch_async(queue ?: _methodQueue, ^{
[self dispatchBlock:^{
RCTProfileBeginEvent();
__strong RCTBridge *strongSelf = weakSelf;
@@ -1303,7 +1316,7 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
@"method": method.JSMethodName,
@"selector": NSStringFromSelector(method.selector),
});
});
} forModule:@(moduleID)];
return YES;
}

View File

@@ -17,6 +17,16 @@
*/
typedef void (^RCTResponseSenderBlock)(NSArray *response);
/**
* This constant can be returned from +methodQueue to force module
* methods to be called on the JavaScript thread. This can have serious
* implications for performance, so only use this if you're sure it's what
* you need.
*
* NOTE: RCTJSThread is not a real libdispatch queue
*/
extern const dispatch_queue_t RCTJSThread;
/**
* Provides the interface needed to register a bridge module.
*/

View File

@@ -307,12 +307,17 @@ static NSError *RCTNSErrorFromJSError(JSContextRef context, JSValueRef jsError)
- (void)executeBlockOnJavaScriptQueue:(dispatch_block_t)block
{
if ([NSThread currentThread] != _javaScriptThread) {
[self performSelector:@selector(executeBlockOnJavaScriptQueue:)
onThread:_javaScriptThread withObject:block waitUntilDone:NO];
} else {
block();
}
/**
* Always dispatch async, ensure there are no sync calls on the JS thread
* otherwise timers can cause a deadlock
*/
[self performSelector:@selector(_runBlock:)
onThread:_javaScriptThread withObject:block waitUntilDone:NO];
}
- (void)_runBlock:(dispatch_block_t)block
{
block();
}
- (void)injectJSONText:(NSString *)script

View File

@@ -110,7 +110,7 @@ RCT_IMPORT_METHOD(RCTJSTimers, callTimers)
- (dispatch_queue_t)methodQueue
{
return dispatch_get_main_queue();
return RCTJSThread;
}
- (BOOL)isValid
@@ -131,8 +131,6 @@ RCT_IMPORT_METHOD(RCTJSTimers, callTimers)
- (void)startTimers
{
RCTAssertMainThread();
if (![self isValid] || _timers.count == 0) {
return;
}