From eab390aecba4a6df9a3b233cb8933c24351c8680 Mon Sep 17 00:00:00 2001 From: Pieter De Baets Date: Fri, 21 Aug 2015 11:33:04 -0700 Subject: [PATCH] Cleanup bridge init, measure native module init time --- React/Base/RCTBatchedBridge.m | 136 +++++++++++++-------------- React/Base/RCTBridge.m | 12 ++- React/Base/RCTPerformanceLogger.h | 4 +- React/Base/RCTPerformanceLogger.m | 15 +-- React/Base/RCTRootView.m | 5 +- React/Executors/RCTContextExecutor.m | 5 +- 6 files changed, 90 insertions(+), 87 deletions(-) diff --git a/React/Base/RCTBatchedBridge.m b/React/Base/RCTBatchedBridge.m index fb3182f605..7e447a5e7a 100644 --- a/React/Base/RCTBatchedBridge.m +++ b/React/Base/RCTBatchedBridge.m @@ -113,27 +113,26 @@ RCT_EXTERN NSArray *RCTGetModuleClasses(void); - (void)start { - __weak RCTBatchedBridge *weakSelf = self; - - __block NSString *sourceCode; - __block NSString *config; - dispatch_queue_t bridgeQueue = dispatch_queue_create("com.facebook.react.RCTBridgeQueue", DISPATCH_QUEUE_CONCURRENT); + dispatch_group_t initModulesAndLoadSource = dispatch_group_create(); - dispatch_group_enter(initModulesAndLoadSource); - [weakSelf loadSource:^(NSError *error, NSString *source) { - if (error) { - RCTLogError(@"%@", error); - } else { - sourceCode = source; - } - + __block NSString *sourceCode; + [self loadSource:^(NSError *error, NSString *source) { + sourceCode = source; dispatch_group_leave(initModulesAndLoadSource); }]; + // Synchronously initialize all native modules [self initModules]; + if (RCTProfileIsProfiling()) { + // Depends on moduleDataByID being loaded + RCTProfileHookModules(self); + } + + __weak RCTBatchedBridge *weakSelf = self; + __block NSString *config; dispatch_group_enter(initModulesAndLoadSource); dispatch_async(bridgeQueue, ^{ dispatch_group_t setupJSExecutorAndModuleConfig = dispatch_group_create(); @@ -144,16 +143,16 @@ RCT_EXTERN NSArray *RCTGetModuleClasses(void); dispatch_group_async(setupJSExecutorAndModuleConfig, bridgeQueue, ^{ if (weakSelf.isValid) { config = [weakSelf moduleConfig]; - - if (RCTProfileIsProfiling()) { - RCTProfileHookModules(weakSelf); - } } }); dispatch_group_notify(setupJSExecutorAndModuleConfig, bridgeQueue, ^{ - [weakSelf injectJSONConfiguration:config onComplete:^(__unused NSError *error) {}]; - + // We're not waiting for this complete to leave the dispatch group, since + // injectJSONConfiguration and executeSourceCode will schedule operations on the + // same queue anyway. + [weakSelf injectJSONConfiguration:config onComplete:^(__unused NSError *error) { + RCTPerformanceLoggerEnd(RCTPLNativeModuleInit); + }]; dispatch_group_leave(initModulesAndLoadSource); }); }); @@ -167,51 +166,52 @@ RCT_EXTERN NSArray *RCTGetModuleClasses(void); - (void)loadSource:(RCTSourceLoadBlock)_onSourceLoad { - RCTPerformanceLoggerStart(RCTPLScriptDownload); - int cookie = RCTProfileBeginAsyncEvent(0, @"JavaScript download", nil); + RCTPerformanceLoggerStart(RCTPLScriptDownload); + int cookie = RCTProfileBeginAsyncEvent(0, @"JavaScript download", nil); - RCTSourceLoadBlock onSourceLoad = ^(NSError *error, NSString *source) { - RCTPerformanceLoggerEnd(RCTPLScriptDownload); - RCTProfileEndAsyncEvent(0, @"init,download", cookie, @"JavaScript download", nil); + RCTSourceLoadBlock onSourceLoad = ^(NSError *error, NSString *source) { + RCTProfileEndAsyncEvent(0, @"init,download", cookie, @"JavaScript download", nil); + RCTPerformanceLoggerEnd(RCTPLScriptDownload); - if (error) { - NSArray *stack = [error userInfo][@"stack"]; - if (stack) { - [self.redBox showErrorMessage:error.localizedDescription - withStack:stack]; - } else { - [self.redBox showErrorMessage:error.localizedDescription - withDetails:error.localizedFailureReason]; - } - - NSDictionary *userInfo = @{@"bridge": self, @"error": error}; - [[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidFailToLoadNotification - object:_parentBridge - userInfo:userInfo]; + if (error) { + NSArray *stack = [error userInfo][@"stack"]; + if (stack) { + [self.redBox showErrorMessage:error.localizedDescription + withStack:stack]; + } else { + [self.redBox showErrorMessage:error.localizedDescription + withDetails:error.localizedFailureReason]; } - _onSourceLoad(error, source); - }; - - if ([self.delegate respondsToSelector:@selector(loadSourceForBridge:withBlock:)]) { - [self.delegate loadSourceForBridge:_parentBridge withBlock:onSourceLoad]; - } else if (self.bundleURL) { - [RCTJavaScriptLoader loadBundleAtURL:self.bundleURL onComplete:onSourceLoad]; - } else { - // Allow testing without a script - dispatch_async(dispatch_get_main_queue(), ^{ - _loading = NO; - [[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidLoadNotification - object:_parentBridge - userInfo:@{ @"bridge": self }]; - }); - onSourceLoad(nil, nil); + NSDictionary *userInfo = @{@"bridge": self, @"error": error}; + [[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidFailToLoadNotification + object:_parentBridge + userInfo:userInfo]; } + + _onSourceLoad(error, source); + }; + + if ([self.delegate respondsToSelector:@selector(loadSourceForBridge:withBlock:)]) { + [self.delegate loadSourceForBridge:_parentBridge withBlock:onSourceLoad]; + } else if (self.bundleURL) { + [RCTJavaScriptLoader loadBundleAtURL:self.bundleURL onComplete:onSourceLoad]; + } else { + // Allow testing without a script + dispatch_async(dispatch_get_main_queue(), ^{ + _loading = NO; + [[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidLoadNotification + object:_parentBridge + userInfo:@{ @"bridge": self }]; + }); + onSourceLoad(nil, nil); + } } - (void)initModules { RCTAssertMainThread(); + RCTPerformanceLoggerStart(RCTPLNativeModuleInit); // Register passed-in module instances NSMutableDictionary *preregisteredModules = [NSMutableDictionary new]; @@ -266,7 +266,6 @@ RCT_EXTERN NSArray *RCTGetModuleClasses(void); _javaScriptExecutor = _modulesByName[RCTBridgeModuleNameForClass(self.executorClass)]; for (id module in _modulesByName.allValues) { - // Bridge must be set before moduleData is set up, as methodQueue // initialization requires it (View Managers get their queue by calling // self.bridge.uiManager.methodQueue) @@ -288,7 +287,6 @@ RCT_EXTERN NSArray *RCTGetModuleClasses(void); - (void)setupExecutor { [_javaScriptExecutor setUp]; - } - (NSString *)moduleConfig @@ -309,7 +307,7 @@ RCT_EXTERN NSArray *RCTGetModuleClasses(void); - (void)injectJSONConfiguration:(NSString *)configJSON onComplete:(void (^)(NSError *))onComplete { - if (!self.isValid) { + if (!self.valid) { return; } @@ -325,9 +323,7 @@ RCT_EXTERN NSArray *RCTGetModuleClasses(void); - (void)executeSourceCode:(NSString *)sourceCode { - _loading = NO; - - if (!self.isValid || !_javaScriptExecutor) { + if (!self.valid || !_javaScriptExecutor) { return; } @@ -336,22 +332,23 @@ RCT_EXTERN NSArray *RCTGetModuleClasses(void); sourceCodeModule.scriptText = sourceCode; [self enqueueApplicationScript:sourceCode url:self.bundleURL onComplete:^(NSError *loadError) { - if (loadError) { [self.redBox showError:loadError]; return; } - /** - * Register the display link to start sending js calls after everything - * is setup - */ + // Register the display link to start sending js calls after everything is setup NSRunLoop *targetRunLoop = [_javaScriptExecutor isKindOfClass:[RCTContextExecutor class]] ? [NSRunLoop currentRunLoop] : [NSRunLoop mainRunLoop]; [_jsDisplayLink addToRunLoop:targetRunLoop forMode:NSRunLoopCommonModes]; - [[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidLoadNotification - object:_parentBridge - userInfo:@{ @"bridge": self }]; + // Perform the state update and notification on the main thread, so we can't run into + // timing issues with RCTRootView + dispatch_async(dispatch_get_main_queue(), ^{ + _loading = NO; + [[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidLoadNotification + object:_parentBridge + userInfo:@{ @"bridge": self }]; + }); }]; } @@ -420,12 +417,13 @@ RCT_NOT_IMPLEMENTED(-initWithBundleURL:(__unused NSURL *)bundleURL - (void)invalidate { - if (!self.isValid) { + if (!self.valid) { return; } RCTAssertMainThread(); + _loading = NO; _valid = NO; if ([RCTBridge currentBridge] == self) { [RCTBridge setCurrentBridge:nil]; diff --git a/React/Base/RCTBridge.m b/React/Base/RCTBridge.m index 5c9b676d32..486b9de73c 100644 --- a/React/Base/RCTBridge.m +++ b/React/Base/RCTBridge.m @@ -186,8 +186,8 @@ static RCTBridge *RCTCurrentBridgeInstance = nil; _bundleURL = bundleURL; _moduleProvider = block; _launchOptions = [launchOptions copy]; - [self bindKeys]; [self setUp]; + [self bindKeys]; } return self; } @@ -214,21 +214,18 @@ RCT_NOT_IMPLEMENTED(-init) object:nil]; #if TARGET_IPHONE_SIMULATOR - RCTKeyCommands *commands = [RCTKeyCommands sharedInstance]; // reload in current mode [commands registerKeyCommandWithInput:@"r" modifierFlags:UIKeyModifierCommand - action:^(__unused UIKeyCommand *command) - { + action:^(__unused UIKeyCommand *command) { [[NSNotificationCenter defaultCenter] postNotificationName:RCTReloadNotification object:nil userInfo:nil]; }]; #endif - } - (RCTEventDispatcher *)eventDispatcher @@ -260,6 +257,11 @@ RCT_NOT_IMPLEMENTED(-init) return _batchedBridge.loading; } +- (BOOL)isValid +{ + return _batchedBridge.valid; +} + - (void)invalidate { RCTAssertMainThread(); diff --git a/React/Base/RCTPerformanceLogger.h b/React/Base/RCTPerformanceLogger.h index 4c220d170f..66d3dd8639 100644 --- a/React/Base/RCTPerformanceLogger.h +++ b/React/Base/RCTPerformanceLogger.h @@ -10,11 +10,11 @@ #import #import "RCTDefines.h" -#import "RCTBridgeModule.h" typedef NS_ENUM(NSUInteger, RCTPLTag) { RCTPLScriptDownload = 0, - RCTPLAppScriptExecution, + RCTPLScriptExecution, + RCTPLNativeModuleInit, RCTPLTTI, RCTPLSize }; diff --git a/React/Base/RCTPerformanceLogger.m b/React/Base/RCTPerformanceLogger.m index c19f4938af..87e643355f 100644 --- a/React/Base/RCTPerformanceLogger.m +++ b/React/Base/RCTPerformanceLogger.m @@ -27,12 +27,14 @@ void RCTPerformanceLoggerEnd(RCTPLTag tag) NSArray *RCTPerformanceLoggerOutput(void) { return @[ - @(RCTPLData[0][0]), - @(RCTPLData[0][1]), - @(RCTPLData[1][0]), - @(RCTPLData[1][1]), - @(RCTPLData[2][0]), - @(RCTPLData[2][1]), + @(RCTPLData[RCTPLScriptDownload][0]), + @(RCTPLData[RCTPLScriptDownload][1]), + @(RCTPLData[RCTPLScriptExecution][0]), + @(RCTPLData[RCTPLScriptExecution][1]), + @(RCTPLData[RCTPLNativeModuleInit][0]), + @(RCTPLData[RCTPLNativeModuleInit][1]), + @(RCTPLData[RCTPLTTI][0]), + @(RCTPLData[RCTPLTTI][1]), ]; } @@ -71,6 +73,7 @@ RCT_EXPORT_MODULE() @[ @"ScriptDownload", @"ScriptExecution", + @"NativeModuleInit", @"TTI", ], ]]; diff --git a/React/Base/RCTRootView.m b/React/Base/RCTRootView.m index e2ae3e60d3..1e8551e888 100644 --- a/React/Base/RCTRootView.m +++ b/React/Base/RCTRootView.m @@ -160,10 +160,9 @@ RCT_NOT_IMPLEMENTED(-initWithCoder:(NSCoder *)aDecoder) - (void)javaScriptDidLoad:(NSNotification *)notification { + RCTAssertMainThread(); RCTBridge *bridge = notification.userInfo[@"bridge"]; - dispatch_async(dispatch_get_main_queue(), ^{ - [self bundleFinishedLoading:bridge]; - }); + [self bundleFinishedLoading:bridge]; } - (void)bundleFinishedLoading:(RCTBridge *)bridge diff --git a/React/Executors/RCTContextExecutor.m b/React/Executors/RCTContextExecutor.m index 95f22fe92e..df3c232d5d 100644 --- a/React/Executors/RCTContextExecutor.m +++ b/React/Executors/RCTContextExecutor.m @@ -458,14 +458,15 @@ static NSError *RCTNSErrorFromJSError(JSContextRef context, JSValueRef jsError) if (!strongSelf || !strongSelf.isValid) { return; } - RCTPerformanceLoggerStart(RCTPLAppScriptExecution); + + RCTPerformanceLoggerStart(RCTPLScriptExecution); JSValueRef jsError = NULL; JSStringRef execJSString = JSStringCreateWithCFString((__bridge CFStringRef)script); JSStringRef jsURL = JSStringCreateWithCFString((__bridge CFStringRef)sourceURL.absoluteString); JSValueRef result = JSEvaluateScript(strongSelf->_context.ctx, execJSString, NULL, jsURL, 0, &jsError); JSStringRelease(jsURL); JSStringRelease(execJSString); - RCTPerformanceLoggerEnd(RCTPLAppScriptExecution); + RCTPerformanceLoggerEnd(RCTPLScriptExecution); if (onComplete) { NSError *error;