Skip to content

Commit 103c863

Browse files
RSNarafacebook-github-bot
authored andcommitted
Eagerly initialize TurboModules before executing JS bundle
Summary: ## Context 1. In FBReactModule jsExecutorForBridge, we asynchronously initialize a list of TurboModules on the main queue: https://fburl.com/diffusion/i56wi3px 2. After initializing the bridge, we start executing the JS bundle, here: https://github.com/facebook/react-native/blob/e23e9328aa164d0a70fe4f16042c982e7801d924/React/CxxBridge/RCTCxxBridge.mm#L414-L417. Since bridge initialization knows nothing about TurboModule eager initialization, this happens concurrently with 1, and starts requiring NativeModules/TurboModules on the JS thread. ## The Race 1. Both the main thread and the JS thread race to create a TurboModule that requires main queue setup. 2. The JS thread wins, and starts creating the TurboModule. Meanwhile, the main thread blocks, waiting on a signal here, in RCTTurboModuleManager: https://github.com/facebook/react-native/blob/e23e9328aa164d0a70fe4f16042c982e7801d924/ReactCommon/turbomodule/core/platform/ios/RCTTurboModuleManager.mm#L430 3. The JS thread tries to dispatch_sync to the main queue to setup the TurboModule because the TurboModule requires main queue setup, here: https://github.com/facebook/react-native/blob/e23e9328aa164d0a70fe4f16042c982e7801d924/ReactCommon/turbomodule/core/platform/ios/RCTTurboModuleManager.mm#L402 4. We deadlock. ## The fix Succinctly, NativeModule eager initialization finishes before execute the JS bundle, but TurboModule initialization doesn't. This diff corrects that mistake. The changes in this diff: 1. The RN application via the TurboModuleManager delegate can now optionally provide the names of all eagerly initialized TurboModules by implementing two methods `getEagerInitModuleNames`, `getEagerInitMainQueueModuleNames`. 2. The TurboModuleManager grabs these two lists from the delegate, and exposes them to its owner via the `RCTTurboModuleRegistry` protocol. 3. The RCTCxxBridge, which already owns a `id<RCTTurboModuleRegistry>` object, uses it to eagerly initialize the TurboModules in these two lists with the correct timing requirements. This is exactly how we implement eager initialization in Android. **Note:** Right now, phase one and two of TurboModule eager initialization happen after phase one and two of NativeModule eager initialization. We could make the timing even more correct by initializing the TurboModules at the exact same time we initialize the NativeModules. However, that would require a bit more surgery on the bridge, and the bridge delegate. I think this is good enough for now. Changelog: [iOS][Fixed] - Fix TurboModule eager init race Reviewed By: PeteTheHeat Differential Revision: D22406171 fbshipit-source-id: 4715be0bceb478a8e4aa206180c0316eaaf287e8
1 parent 5c24746 commit 103c863

File tree

4 files changed

+45
-0
lines changed

4 files changed

+45
-0
lines changed

React/Base/RCTBridgeModule.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,9 @@ RCT_EXTERN_C_END
360360
*/
361361
- (id)moduleForName:(const char *)moduleName warnOnLookupFailure:(BOOL)warnOnLookupFailure;
362362
- (BOOL)moduleIsInitialized:(const char *)moduleName;
363+
364+
- (NSArray<NSString *> *)eagerInitModuleNames;
365+
- (NSArray<NSString *> *)eagerInitMainQueueModuleNames;
363366
@end
364367

365368
/**

React/CxxBridge/RCTCxxBridge.mm

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,27 @@ - (void)start
381381
}));
382382
}
383383

384+
/**
385+
* id<RCTCxxBridgeDelegate> jsExecutorFactory may create and assign an id<RCTTurboModuleLookupDelegate> object to
386+
* RCTCxxBridge If id<RCTTurboModuleLookupDelegate> is assigned by this time, eagerly initialize all TurboModules
387+
*/
388+
if (_turboModuleLookupDelegate) {
389+
for (NSString *moduleName in [_turboModuleLookupDelegate eagerInitModuleNames]) {
390+
[_turboModuleLookupDelegate moduleForName:[moduleName UTF8String]];
391+
}
392+
393+
for (NSString *moduleName in [_turboModuleLookupDelegate eagerInitMainQueueModuleNames]) {
394+
if (RCTIsMainQueue()) {
395+
[_turboModuleLookupDelegate moduleForName:[moduleName UTF8String]];
396+
} else {
397+
id<RCTTurboModuleLookupDelegate> turboModuleLookupDelegate = _turboModuleLookupDelegate;
398+
dispatch_group_async(prepareBridge, dispatch_get_main_queue(), ^{
399+
[turboModuleLookupDelegate moduleForName:[moduleName UTF8String]];
400+
});
401+
}
402+
}
403+
}
404+
384405
// Dispatch the instance initialization as soon as the initial module metadata has
385406
// been collected (see initModules)
386407
dispatch_group_enter(prepareBridge);

ReactCommon/turbomodule/core/platform/ios/RCTTurboModuleManager.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
2020
initParams:
2121
(const facebook::react::ObjCTurboModule::InitParams &)params;
22+
@optional
23+
- (NSArray<NSString *> *)getEagerInitModuleNames;
24+
- (NSArray<NSString *> *)getEagerInitMainQueueModuleNames;
2225

2326
@optional
2427

ReactCommon/turbomodule/core/platform/ios/RCTTurboModuleManager.mm

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -714,6 +714,24 @@ - (BOOL)moduleIsInitialized:(const char *)moduleName
714714
return _turboModuleHolders.find(moduleName) != _turboModuleHolders.end();
715715
}
716716

717+
- (NSArray<NSString *> *)eagerInitModuleNames
718+
{
719+
if ([_delegate respondsToSelector:@selector(getEagerInitModuleNames)]) {
720+
return [_delegate getEagerInitModuleNames];
721+
}
722+
723+
return @[];
724+
}
725+
726+
- (NSArray<NSString *> *)eagerInitMainQueueModuleNames
727+
{
728+
if ([_delegate respondsToSelector:@selector(getEagerInitMainQueueModuleNames)]) {
729+
return [_delegate getEagerInitMainQueueModuleNames];
730+
}
731+
732+
return @[];
733+
}
734+
717735
#pragma mark Invalidation logic
718736

719737
- (void)bridgeWillInvalidateModules:(NSNotification *)notification

0 commit comments

Comments
 (0)