From 564b944e56eebf2035861c9fe8b5df1811348a4b Mon Sep 17 00:00:00 2001 From: Ramanpreet Nara Date: Sun, 19 Sep 2021 15:44:23 -0700 Subject: [PATCH] Ensure RCTJSThread is initialized in Bridgeless mode Summary: ## Context When Venice is enabled app-wide, we won't be creating/initializing the bridge. This means that RCTBridge +(void)initialize; won't execute: https://www.internalfb.com/code/fbsource/[d8b25a1907ee55baa21e02a69ecab0f7a9442b8e]/xplat/js/react-native-github/React/Base/RCTBridge.m?lines=167%2C171-180 ## Problem When RCTBridge initialize isn't executed, we won't initialize RCTJSThread to kCFNull. RCTJSThread will be nil. NativeModules like RCTEventDispatcher use RCTJSThread to indicate that their methods must be executed on the JavaScript thread: https://www.internalfb.com/code/fbsource/[44976912ae618619a394f063c4c942ef020b86e8]/xplat/js/react-native-github/React/CoreModules/RCTEventDispatcher.mm?lines=198-201 If RCTJSThread is nil, these NativeModules will fail to initialize w/ the TurboModule system: https://www.internalfb.com/code/fbsource/[f6a04f529ac1354b2973bd3553d12aef28ff24f2][blame]/xplat/js/react-native-github/ReactCommon/react/nativemodule/core/platform/ios/RCTTurboModuleManager.mm?lines=584-589%2C595%2C599%2C610-618 How: 1. The TurboModuleManager will see that each of these NativeModules has a methodQueue getter (line 587 above). 2. It'll invoke that getter (line 588), and get a nil methodQueue out, when **that methodQueue should be kCFNull**. 3. Because the TurboModuleManager gets a nil method queue from the getter, !methodQueue will pass (line 595). 4. So, TurboModuleManager try to create and assign a method queue to these modules (line 611), which'll raise an error (line 613), because none of these modules synthesize the methodQueue (b/c they expose a getter to methodQueue instead). ## Changes We need to initialize RCTJSThread to kCFNull in all cases, to prevent this breakage. So, I moved RCTJSThread into its own header: RCTJSThread.h. RCTJSThread.h exports a function that initializes the RCTJSThread constant: _RCTInitializeJSThreadConstantInternal. This function gets invoked inside RCTHost initialize, and RCTBridge initialize. Created from CodeHub with https://fburl.com/edit-in-codehub Changelog: [Internal] Reviewed By: p-sun, mdvacca Differential Revision: D30910515 fbshipit-source-id: 2dd9b0cfcda92c497bb497f12f9fb847da563f47 --- BUCK | 1 + React/Base/RCTBridge.m | 9 ++------- React/Base/RCTBridgeModule.h | 11 +---------- React/Base/RCTJSThread.h | 29 +++++++++++++++++++++++++++++ React/Base/RCTJSThread.m | 19 +++++++++++++++++++ 5 files changed, 52 insertions(+), 17 deletions(-) create mode 100644 React/Base/RCTJSThread.h create mode 100644 React/Base/RCTJSThread.m diff --git a/BUCK b/BUCK index a2eccd5adfd..0b338cccef6 100644 --- a/BUCK +++ b/BUCK @@ -247,6 +247,7 @@ REACT_PUBLIC_HEADERS = { "React/RCTInvalidating.h": RCTBASE_PATH + "RCTInvalidating.h", "React/RCTJSScriptLoaderModule.h": RCTBASE_PATH + "RCTJSScriptLoaderModule.h", "React/RCTJSStackFrame.h": RCTBASE_PATH + "RCTJSStackFrame.h", + "React/RCTJSThread.h": RCTBASE_PATH + "RCTJSThread.h", "React/RCTJavaScriptExecutor.h": RCTBASE_PATH + "RCTJavaScriptExecutor.h", "React/RCTJavaScriptLoader.h": RCTBASE_PATH + "RCTJavaScriptLoader.h", "React/RCTKeyCommands.h": RCTBASE_PATH + "RCTKeyCommands.h", diff --git a/React/Base/RCTBridge.m b/React/Base/RCTBridge.m index 3cc61733034..38b9cdd4695 100644 --- a/React/Base/RCTBridge.m +++ b/React/Base/RCTBridge.m @@ -15,6 +15,7 @@ #import "RCTInspectorDevServerHelper.h" #endif #import "RCTDevLoadingViewProtocol.h" +#import "RCTJSThread.h" #import "RCTLog.h" #import "RCTModuleData.h" #import "RCTPerformanceLogger.h" @@ -168,15 +169,9 @@ void RCTDisableTurboModuleManagerDelegateLocking(BOOL disabled) NSURL *_delegateBundleURL; } -dispatch_queue_t RCTJSThread; - + (void)initialize { - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - // Set up JS thread - RCTJSThread = (id)kCFNull; - }); + _RCTInitializeJSThreadConstantInternal(); } static RCTBridge *RCTCurrentBridgeInstance = nil; diff --git a/React/Base/RCTBridgeModule.h b/React/Base/RCTBridgeModule.h index c823dac3dc5..fff361a58d4 100644 --- a/React/Base/RCTBridgeModule.h +++ b/React/Base/RCTBridgeModule.h @@ -9,6 +9,7 @@ #import #import +#import @class RCTBridge; @protocol RCTBridgeMethod; @@ -42,16 +43,6 @@ typedef void (^RCTPromiseResolveBlock)(id result); */ typedef void (^RCTPromiseRejectBlock)(NSString *code, NSString *message, NSError *error); -/** - * 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 - */ -RCT_EXTERN dispatch_queue_t RCTJSThread; - RCT_EXTERN_C_BEGIN typedef struct RCTMethodInfo { diff --git a/React/Base/RCTJSThread.h b/React/Base/RCTJSThread.h new file mode 100644 index 00000000000..8f1bc76d2c2 --- /dev/null +++ b/React/Base/RCTJSThread.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import +#import + +#import + +/** + * 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 + */ +RCT_EXTERN dispatch_queue_t RCTJSThread; + +/** + * Initializes the RCTJSThread constant. + * Exported because the bridgeless initialization layer needs to initialize + * RCTJSThread. In bridgeless mode, RCTBridge isn't accessed, and RCTJSThread + * therefore isn't initialized. + */ +RCT_EXTERN void _RCTInitializeJSThreadConstantInternal(void); diff --git a/React/Base/RCTJSThread.m b/React/Base/RCTJSThread.m new file mode 100644 index 00000000000..192822a8606 --- /dev/null +++ b/React/Base/RCTJSThread.m @@ -0,0 +1,19 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import "RCTJSThread.h" + +dispatch_queue_t RCTJSThread; + +void _RCTInitializeJSThreadConstantInternal(void) +{ + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + // Set up JS thread + RCTJSThread = (id)kCFNull; + }); +}