Skip to content

Commit

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

[IOS] [FIXED] - unexpected useEffects flushing semantics

Pull Request resolved: #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 facebook-github-bot committed May 25, 2023
1 parent b98de92 commit 7211ef1
Show file tree
Hide file tree
Showing 30 changed files with 787 additions and 261 deletions.
24 changes: 16 additions & 8 deletions packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm
Original file line number Diff line number Diff line change
Expand Up @@ -6,38 +6,41 @@
*/

#import "RCTAppDelegate.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/RCTComponentViewFactory.h>
#import <React/RCTComponentViewProtocol.h>
#import <React/RCTCxxBridgeDelegate.h>
#import <React/RCTFabricSurfaceHostingProxyRootView.h>
#import <React/RCTLegacyViewManagerInteropComponentView.h>
#import <React/RCTSurfacePresenter.h>
#import <React/RCTSurfacePresenterBridgeAdapter.h>
#import <ReactCommon/RCTTurboModuleManager.h>
#import <react/config/ReactNativeConfig.h>
#import <react/renderer/runtimescheduler/RuntimeScheduler.h>
#import <react/renderer/runtimescheduler/RuntimeSchedulerCallInvoker.h>
#import "RCTLegacyInteropComponents.h"

static NSString *const kRNConcurrentRoot = @"concurrentRoot";

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

#endif

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

@implementation RCTAppDelegate

#if RCT_NEW_ARCH_ENABLED
Expand Down Expand Up @@ -129,11 +132,11 @@ - (UIViewController *)createRootViewController
return [UIViewController new];
}

#if RCT_NEW_ARCH_ENABLED
#pragma mark - RCTCxxBridgeDelegate
- (std::unique_ptr<facebook::react::JSExecutorFactory>)jsExecutorFactoryForBridge:(RCTBridge *)bridge
{
_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
Expand All @@ -142,8 +145,13 @@ - (UIViewController *)createRootViewController
_contextContainer->erase("RuntimeScheduler");
_contextContainer->insert("RuntimeScheduler", _runtimeScheduler);
return RCTAppSetupDefaultJsExecutorFactory(bridge, turboModuleManager, _runtimeScheduler);
#else
return RCTAppSetupJsExecutorFactoryForOldArch(bridge, _runtimeScheduler);
#endif
}

#if RCT_NEW_ARCH_ENABLED

#pragma mark - RCTTurboModuleManagerDelegate

- (Class)getModuleClassFromName:(const char *)name
Expand Down
10 changes: 8 additions & 2 deletions packages/react-native/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,21 +27,27 @@
#import <React/JSCExecutorFactory.h>
#endif

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

#if RCT_NEW_ARCH_ENABLED
// 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,
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 packages/react-native/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 @@ -135,4 +138,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
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ header_search_paths = [
"$(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 @@ -80,6 +83,7 @@ Pod::Spec.new do |s|
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"
Expand All @@ -91,6 +95,8 @@ Pod::Spec.new do |s|
s.dependency "React-Fabric"
s.dependency "React-RCTFabric"
s.dependency "React-graphics"
s.dependency "React-utils"
s.dependency "React-debug"

s.script_phases = {
:name => "Generate Legacy Components Interop",
Expand Down
10 changes: 6 additions & 4 deletions packages/react-native/React-Core.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -126,11 +126,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 packages/react-native/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 packages/react-native/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;
}
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 <UIKit/UIKit.h>
#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
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
6 changes: 5 additions & 1 deletion packages/react-native/React/React-RCTFabric.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ header_search_paths = [
"\"$(PODS_ROOT)/Headers/Private/Yoga\"",
"\"$(PODS_ROOT)/Headers/Public/React-Codegen\"",
"\"${PODS_CONFIGURATION_BUILD_DIR}/React-Codegen/React_Codegen.framework/Headers\"",

]

if ENV['USE_FRAMEWORKS']
Expand All @@ -43,6 +42,9 @@ if ENV['USE_FRAMEWORKS']
header_search_paths << "\"${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers/react/renderer/graphics/platform/ios\""
header_search_paths << "\"${PODS_CONFIGURATION_BUILD_DIR}/React-ImageManager/React_ImageManager.framework/Headers\""
header_search_paths << "\"${PODS_CONFIGURATION_BUILD_DIR}/React-RCTFabric/RCTFabric.framework/Headers\""
header_search_paths << "\"${PODS_CONFIGURATION_BUILD_DIR}/React-debug/React_debug.framework/Headers\""
header_search_paths << "\"${PODS_CONFIGURATION_BUILD_DIR}/React-utils/React_utils.framework/Headers\""
header_search_paths << "\"${PODS_CONFIGURATION_BUILD_DIR}/React-runtimescheduler/React_runtimescheduler.framework/Headers\""
end

Pod::Spec.new do |s|
Expand Down Expand Up @@ -79,6 +81,8 @@ Pod::Spec.new do |s|
s.dependency "Yoga"
s.dependency "React-RCTText"
s.dependency "React-FabricImage"
s.dependency "React-utils"
s.dependency "React-runtimescheduler"

if ENV["USE_HERMES"] == nil || ENV["USE_HERMES"] == "1"
s.dependency "hermes-engine"
Expand Down
Loading

0 comments on commit 7211ef1

Please sign in to comment.