Skip to content

Commit

Permalink
Enable RuntimeScheduler in old architecture (facebook#37523)
Browse files Browse the repository at this point in the history
Summary:

[IOS] [FIXED] - unexpected useEffects flushing semantics

Pull Request resolved: facebook#37523

Test Plan: Run RNTester with old architecture and make sure RuntimeScheduler is used.

Reviewed By: cipolleschi

Differential Revision: D46078324

Pulled By: sammy-SC

fbshipit-source-id: 5f18cbe60e6c9c753c373f175ba413b79288a928
  • Loading branch information
sammy-SC authored and Saadnajmi committed May 30, 2023
1 parent 6daa435 commit 2c256b1
Show file tree
Hide file tree
Showing 28 changed files with 558 additions and 116 deletions.
40 changes: 31 additions & 9 deletions Libraries/AppDelegate/RCTAppDelegate.mm
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,40 @@
*/

#import "RCTAppDelegate.h"
#import "RCTAppSetupUtils.h"
#import <React/RCTCxxBridgeDelegate.h>
#import <React/RCTRootView.h>
#import <React/RCTRuntimeExecutorFromBridge.h>
#import <react/renderer/runtimescheduler/RuntimeScheduler.h>

#import "RCTAppSetupUtils.h"

#if RCT_NEW_ARCH_ENABLED
#import <React/CoreModulesPlugins.h>
#import <React/RCTCxxBridgeDelegate.h>
#import <React/RCTComponentViewFactory.h>
#import <React/RCTComponentViewProtocol.h>
#import <React/RCTFabricSurfaceHostingProxyRootView.h>
#import <React/RCTSurfacePresenter.h>
#import <React/RCTSurfacePresenterBridgeAdapter.h>
#import <ReactCommon/RCTTurboModuleManager.h>
#import <react/config/ReactNativeConfig.h>
#import <react/renderer/runtimescheduler/RuntimeSchedulerCallInvoker.h>
#import "RCTLegacyInteropComponents.h"

static NSString *const kRNConcurrentRoot = @"concurrentRoot";

@interface RCTAppDelegate () <RCTTurboModuleManagerDelegate, RCTCxxBridgeDelegate> {
@interface RCTAppDelegate () <RCTTurboModuleManagerDelegate, RCTComponentViewFactoryComponentProvider> {
std::shared_ptr<const facebook::react::ReactNativeConfig> _reactNativeConfig;
facebook::react::ContextContainer::Shared _contextContainer;
}
@end

#endif

@interface RCTAppDelegate () <RCTCxxBridgeDelegate> {
std::shared_ptr<facebook::react::RuntimeScheduler> _runtimeScheduler;
}
@end

@implementation RCTAppDelegate

#if !TARGET_OS_OSX // [macOS]
Expand Down Expand Up @@ -140,18 +152,28 @@ - (NSViewController *)createRootViewController
}
#endif // macOS]

#if RCT_NEW_ARCH_ENABLED
#pragma mark - RCTCxxBridgeDelegate

- (std::unique_ptr<facebook::react::JSExecutorFactory>)jsExecutorFactoryForBridge:(RCTBridge *)bridge
{
self.turboModuleManager = [[RCTTurboModuleManager alloc] initWithBridge:bridge
delegate:self
jsInvoker:bridge.jsCallInvoker];
return RCTAppSetupDefaultJsExecutorFactory(bridge, _turboModuleManager);
_runtimeScheduler = std::make_shared<facebook::react::RuntimeScheduler>(RCTRuntimeExecutorFromBridge(bridge));
#if RCT_NEW_ARCH_ENABLED
std::shared_ptr<facebook::react::CallInvoker> callInvoker =
std::make_shared<facebook::react::RuntimeSchedulerCallInvoker>(_runtimeScheduler);
RCTTurboModuleManager *turboModuleManager = [[RCTTurboModuleManager alloc] initWithBridge:bridge
delegate:self
jsInvoker:callInvoker];
_contextContainer->erase("RuntimeScheduler");
_contextContainer->insert("RuntimeScheduler", _runtimeScheduler);
return RCTAppSetupDefaultJsExecutorFactory(bridge, turboModuleManager, _runtimeScheduler);
#else
return RCTAppSetupJsExecutorFactoryForOldArch(bridge, _runtimeScheduler);
#endif
}

#pragma mark RCTTurboModuleManagerDelegate
#if RCT_NEW_ARCH_ENABLED

#pragma mark - RCTTurboModuleManagerDelegate

- (Class)getModuleClassFromName:(const char *)name
{
Expand Down
16 changes: 14 additions & 2 deletions Libraries/AppDelegate/RCTAppSetupUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

#ifdef __cplusplus

#if RCT_NEW_ARCH_ENABLED
#import <memory>

#ifndef RCT_USE_HERMES
#if __has_include(<reacthermes/HermesExecutorFactory.h>)
Expand All @@ -27,15 +27,27 @@
#import <React/JSCExecutorFactory.h>
#endif

#if RCT_NEW_ARCH_ENABLED
#import <ReactCommon/RCTTurboModuleManager.h>
#endif

// Forward declaration to decrease compilation coupling
namespace facebook::react {
class RuntimeScheduler;
}

#if RCT_NEW_ARCH_ENABLED
RCT_EXTERN id<RCTTurboModule> RCTAppSetupDefaultModuleFromClass(Class moduleClass);

std::unique_ptr<facebook::react::JSExecutorFactory> RCTAppSetupDefaultJsExecutorFactory(
RCTBridge *bridge,
RCTTurboModuleManager *turboModuleManager);
RCTTurboModuleManager *turboModuleManager,
std::shared_ptr<facebook::react::RuntimeScheduler> const &runtimeScheduler);

#else
std::unique_ptr<facebook::react::JSExecutorFactory> RCTAppSetupJsExecutorFactoryForOldArch(
RCTBridge *bridge,
std::shared_ptr<facebook::react::RuntimeScheduler> const &runtimeScheduler);
#endif

#endif // __cplusplus
Expand Down
26 changes: 25 additions & 1 deletion Libraries/AppDelegate/RCTAppSetupUtils.mm
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@

#import "RCTAppSetupUtils.h"

#import <React/RCTJSIExecutorRuntimeInstaller.h>
#import <react/renderer/runtimescheduler/RuntimeScheduler.h>
#import <react/renderer/runtimescheduler/RuntimeSchedulerBinding.h>

#if RCT_NEW_ARCH_ENABLED
// Turbo Module
#import <React/CoreModulesPlugins.h>
Expand All @@ -15,7 +19,6 @@
#import <React/RCTGIFImageDecoder.h>
#import <React/RCTHTTPRequestHandler.h>
#import <React/RCTImageLoader.h>
#import <React/RCTJSIExecutorRuntimeInstaller.h>
#import <React/RCTLocalAssetImageLoader.h>
#import <React/RCTNetworking.h>

Expand Down Expand Up @@ -128,4 +131,25 @@ void RCTAppSetupPrepareApp(UIApplication *application, BOOL turboModuleEnabled)
}));
}

#else

std::unique_ptr<facebook::react::JSExecutorFactory> RCTAppSetupJsExecutorFactoryForOldArch(
RCTBridge *bridge,
std::shared_ptr<facebook::react::RuntimeScheduler> const &runtimeScheduler)
{
#if RCT_USE_HERMES
return std::make_unique<facebook::react::HermesExecutorFactory>(
#else
return std::make_unique<facebook::react::JSCExecutorFactory>(
#endif
facebook::react::RCTJSIExecutorRuntimeInstaller([bridge, runtimeScheduler](facebook::jsi::Runtime &runtime) {
if (!bridge) {
return;
}
if (runtimeScheduler) {
facebook::react::RuntimeSchedulerBinding::createAndInstallIfNeeded(runtime, runtimeScheduler);
}
}));
}

#endif
34 changes: 34 additions & 0 deletions Libraries/AppDelegate/React-RCTAppDelegate.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ header_search_paths = [
].concat(use_hermes ? [
"$(PODS_ROOT)/Headers/Public/React-hermes",
"$(PODS_ROOT)/Headers/Public/hermes-engine"
] : []).concat(use_frameworks ? [
"$(PODS_CONFIGURATION_BUILD_DIR)/React-Fabric/React_Fabric.framework/Headers/",
"$(PODS_CONFIGURATION_BUILD_DIR)/React-graphics/React_graphics.framework/Headers/",
"$(PODS_CONFIGURATION_BUILD_DIR)/React-graphics/React_graphics.framework/Headers/react/renderer/graphics/platform/ios",
"$(PODS_CONFIGURATION_BUILD_DIR)/ReactCommon/ReactCommon.framework/Headers/react/nativemodule/core",
"$(PODS_CONFIGURATION_BUILD_DIR)/React-NativeModulesApple/React_NativeModulesApple.framework/Headers",
"$(PODS_CONFIGURATION_BUILD_DIR)/React-RCTFabric/RCTFabric.framework/Headers/",
"$(PODS_CONFIGURATION_BUILD_DIR)/React-utils/React_utils.framework/Headers/",
"$(PODS_CONFIGURATION_BUILD_DIR)/React-debug/React_debug.framework/Headers/",
"$(PODS_CONFIGURATION_BUILD_DIR)/React-runtimescheduler/React_runtimescheduler.framework/Headers/",
] : []).map{|p| "\"#{p}\""}.join(" ")

Pod::Spec.new do |s|
Expand Down Expand Up @@ -65,9 +75,33 @@ Pod::Spec.new do |s|
s.dependency "RCTRequired"
s.dependency "RCTTypeSafety"
s.dependency "ReactCommon/turbomodule/core"
s.dependency "React-RCTNetwork"
s.dependency "React-RCTImage"
s.dependency "React-NativeModulesApple"
s.dependency "React-CoreModules"
s.dependency "React-runtimescheduler"

if ENV['USE_HERMES'] == nil || ENV['USE_HERMES'] == "1"
s.dependency "React-hermes"
else
s.dependency "React-jsc"
end

if is_new_arch_enabled
s.dependency "React-RCTFabric"
s.dependency "React-graphics"
s.dependency "React-utils"
s.dependency "React-debug"

s.script_phases = {
:name => "Generate Legacy Components Interop",
:script => "
. ${PODS_ROOT}/../.xcode.env
${NODE_BINARY} ${REACT_NATIVE_PATH}/scripts/codegen/generate-legacy-interop-components.js -p #{ENV['APP_PATH']} -o ${REACT_NATIVE_PATH}/Libraries/AppDelegate
",
:execution_position => :before_compile,
:input_files => ["#{ENV['APP_PATH']}/react-native.config.js"],
:output_files => ["${REACT_NATIVE_PATH}/Libraries/AppDelegate/RCTLegacyInteropComponents.mm"],
}
end
end
10 changes: 6 additions & 4 deletions React-Core.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -117,11 +117,13 @@ Pod::Spec.new do |s|
end

s.dependency "RCT-Folly", folly_version
s.dependency "React-cxxreact", version
s.dependency "React-perflogger", version
s.dependency "React-jsi", version
s.dependency "React-jsiexecutor", version
s.dependency "React-cxxreact"
s.dependency "React-perflogger"
s.dependency "React-jsi"
s.dependency "React-jsiexecutor"
s.dependency "React-utils"
s.dependency "SocketRocket", socket_rocket_version
s.dependency "React-runtimeexecutor"
s.dependency "Yoga"
s.dependency "glog"

Expand Down
19 changes: 19 additions & 0 deletions React/Base/RCTRuntimeExecutorFromBridge.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

#import <Foundation/Foundation.h>
#ifdef __cplusplus
#import <ReactCommon/RuntimeExecutor.h>

NS_ASSUME_NONNULL_BEGIN

@class RCTBridge;

facebook::react::RuntimeExecutor RCTRuntimeExecutorFromBridge(RCTBridge *bridge);

NS_ASSUME_NONNULL_END
#endif
56 changes: 56 additions & 0 deletions React/Base/RCTRuntimeExecutorFromBridge.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

#import "RCTRuntimeExecutorFromBridge.h"
#import <React/RCTAssert.h>
#import <React/RCTBridge+Private.h>
#import <cxxreact/MessageQueueThread.h>
#import <react/utils/ManagedObjectWrapper.h>

using namespace facebook::react;

@interface RCTBridge ()
- (std::shared_ptr<MessageQueueThread>)jsMessageThread;
- (void)invokeAsync:(std::function<void()> &&)func;
@end

RuntimeExecutor RCTRuntimeExecutorFromBridge(RCTBridge *bridge)
{
RCTAssert(bridge, @"RCTRuntimeExecutorFromBridge: Bridge must not be nil.");

auto bridgeWeakWrapper = wrapManagedObjectWeakly([bridge batchedBridge] ?: bridge);

RuntimeExecutor runtimeExecutor = [bridgeWeakWrapper](
std::function<void(facebook::jsi::Runtime & runtime)> &&callback) {
RCTBridge *bridge = unwrapManagedObjectWeakly(bridgeWeakWrapper);

RCTAssert(bridge, @"RCTRuntimeExecutorFromBridge: Bridge must not be nil at the moment of scheduling a call.");

[bridge invokeAsync:[bridgeWeakWrapper, callback = std::move(callback)]() {
RCTCxxBridge *batchedBridge = (RCTCxxBridge *)unwrapManagedObjectWeakly(bridgeWeakWrapper);

RCTAssert(batchedBridge, @"RCTRuntimeExecutorFromBridge: Bridge must not be nil at the moment of invocation.");

if (!batchedBridge) {
return;
}

auto runtime = (facebook::jsi::Runtime *)(batchedBridge.runtime);

RCTAssert(
runtime, @"RCTRuntimeExecutorFromBridge: Bridge must have a valid jsi::Runtime at the moment of invocation.");

if (!runtime) {
return;
}

callback(*runtime);
}];
};

return runtimeExecutor;
}
3 changes: 0 additions & 3 deletions React/Fabric/RCTSurfacePresenterBridgeAdapter.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
*/

#import <Foundation/Foundation.h>
#import <ReactCommon/RuntimeExecutor.h>
#import <React/RCTUIKit.h> // [macOS]
#import <react/utils/ContextContainer.h>

Expand All @@ -15,8 +14,6 @@ NS_ASSUME_NONNULL_BEGIN
@class RCTSurfacePresenter;
@class RCTBridge;

facebook::react::RuntimeExecutor RCTRuntimeExecutorFromBridge(RCTBridge *bridge);

/*
* Controls a life-cycle of a Surface Presenter based on Bridge's life-cycle.
* We are moving away from using Bridge.
Expand Down
38 changes: 1 addition & 37 deletions React/Fabric/RCTSurfacePresenterBridgeAdapter.mm
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#import <React/RCTConstants.h>
#import <React/RCTImageLoader.h>
#import <React/RCTImageLoaderWithAttributionProtocol.h>
#import <React/RCTRuntimeExecutorFromBridge.h>
#import <React/RCTSurfacePresenter.h>
#import <React/RCTSurfacePresenterStub.h>

Expand Down Expand Up @@ -42,43 +43,6 @@ - (void)invokeAsync:(std::function<void()> &&)func;
return contextContainer;
}

RuntimeExecutor RCTRuntimeExecutorFromBridge(RCTBridge *bridge)
{
RCTAssert(bridge, @"RCTRuntimeExecutorFromBridge: Bridge must not be nil.");

auto bridgeWeakWrapper = wrapManagedObjectWeakly([bridge batchedBridge] ?: bridge);

RuntimeExecutor runtimeExecutor = [bridgeWeakWrapper](
std::function<void(facebook::jsi::Runtime & runtime)> &&callback) {
RCTBridge *bridge = unwrapManagedObjectWeakly(bridgeWeakWrapper);

RCTAssert(bridge, @"RCTRuntimeExecutorFromBridge: Bridge must not be nil at the moment of scheduling a call.");

[bridge invokeAsync:[bridgeWeakWrapper, callback = std::move(callback)]() {
RCTCxxBridge *batchedBridge = (RCTCxxBridge *)unwrapManagedObjectWeakly(bridgeWeakWrapper);

RCTAssert(batchedBridge, @"RCTRuntimeExecutorFromBridge: Bridge must not be nil at the moment of invocation.");

if (!batchedBridge) {
return;
}

auto runtime = (facebook::jsi::Runtime *)(batchedBridge.runtime);

RCTAssert(
runtime, @"RCTRuntimeExecutorFromBridge: Bridge must have a valid jsi::Runtime at the moment of invocation.");

if (!runtime) {
return;
}

callback(*runtime);
}];
};

return runtimeExecutor;
}

@implementation RCTSurfacePresenterBridgeAdapter {
RCTSurfacePresenter *_Nullable _surfacePresenter;
__weak RCTBridge *_bridge;
Expand Down
Loading

0 comments on commit 2c256b1

Please sign in to comment.