From 78b87638b18c1d97035b7d5003bf488550d128a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Tue, 17 Oct 2023 11:17:55 -0700 Subject: [PATCH] Wire up configuration to use modern version of RuntimeScheduler (#40945) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/40945 This adds some temporary logic to configure the use of the modern version of RuntimeScheduler based on values coming from the app configuration. This logic is centralized in `ReactInstance` so from that point the code is completely cross-platform. This doesn't use `ReactNativeConfig`/`CoreFeatures` because they're initialized after the point where we need to access them for this use case. This way is a bit uglier but this isn't intended to live for long (only until we verify this doesn't have regressions in a complex app). Changelog: [internal] --- Reviewed By: sammy-SC Differential Revision: D50171297 fbshipit-source-id: 82586fa5e5c394d804b7d6e83f8102986be777d5 --- .../facebook/react/config/ReactFeatureFlags.java | 6 ++++++ .../com/facebook/react/runtime/ReactInstance.java | 9 +++++++-- .../main/jni/react/runtime/jni/JReactInstance.cpp | 12 ++++++++---- .../src/main/jni/react/runtime/jni/JReactInstance.h | 6 ++++-- .../ReactCommon/react/runtime/ReactInstance.cpp | 7 ++++--- .../ReactCommon/react/runtime/ReactInstance.h | 3 ++- .../runtime/platform/ios/ReactCommon/RCTHost.h | 12 ++++++++++++ .../runtime/platform/ios/ReactCommon/RCTHost.mm | 13 ++++++++++++- .../runtime/platform/ios/ReactCommon/RCTInstance.h | 12 ++++++++++++ .../runtime/platform/ios/ReactCommon/RCTInstance.mm | 8 +++++++- 10 files changed, 74 insertions(+), 14 deletions(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java index 5608b6fdec9e5c..ad4360afcfcd65 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java @@ -165,4 +165,10 @@ public class ReactFeatureFlags { /** Enables Stable API for TurboModule (removal of ReactModule, ReactModuleInfoProvider). */ public static boolean enableTurboModuleStableAPI = false; + + /** + * When enabled, it uses the modern fork of RuntimeScheduler that allows scheduling tasks with + * priorities from any thread. + */ + public static boolean useModernRuntimeScheduler = false; } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactInstance.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactInstance.java index a9f01fda727a3e..1c5fc2a9db1354 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactInstance.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactInstance.java @@ -170,6 +170,9 @@ public void onHostDestroy() { // Notify JS if profiling is enabled boolean isProfiling = Systrace.isTracing(Systrace.TRACE_TAG_REACT_APPS | Systrace.TRACE_TAG_REACT_JS_VM_CALLS); + // TODO(T166383606): Remove this parameter when we remove the legacy runtime scheduler or we + // have access to ReactNativeConfig before we initialize it. + boolean useModernRuntimeScheduler = ReactFeatureFlags.useModernRuntimeScheduler; mHybridData = initHybrid( jsEngineInstance, @@ -179,7 +182,8 @@ public void onHostDestroy() { jsTimerExecutor, reactExceptionManager, bindingsInstaller, - isProfiling); + isProfiling, + useModernRuntimeScheduler); RuntimeExecutor unbufferedRuntimeExecutor = getUnbufferedRuntimeExecutor(); @@ -435,7 +439,8 @@ private native HybridData initHybrid( JSTimerExecutor jsTimerExecutor, ReactJsExceptionHandler jReactExceptionsManager, @Nullable BindingsInstaller jBindingsInstaller, - boolean isProfiling); + boolean isProfiling, + boolean useModernRuntimeScheduler); @DoNotStrip private static native JSTimerExecutor createJSTimerExecutor(); diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactInstance.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactInstance.cpp index 832b8aa6f2ccf8..325c3c32d0646d 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactInstance.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactInstance.cpp @@ -36,7 +36,8 @@ JReactInstance::JReactInstance( jni::alias_ref jsTimerExecutor, jni::alias_ref jReactExceptionManager, jni::alias_ref jBindingsInstaller, - bool isProfiling) noexcept { + bool isProfiling, + bool useModernRuntimeScheduler) noexcept { // TODO(janzer): Lazily create runtime auto sharedJSMessageQueueThread = std::make_shared(jsMessageQueueThread); @@ -64,7 +65,8 @@ JReactInstance::JReactInstance( jsEngineInstance->cthis()->createJSRuntime(sharedJSMessageQueueThread), sharedJSMessageQueueThread, timerManager, - std::move(jsErrorHandlingFunc)); + std::move(jsErrorHandlingFunc), + useModernRuntimeScheduler); auto bufferedRuntimeExecutor = instance_->getBufferedRuntimeExecutor(); timerManager->setRuntimeExecutor(bufferedRuntimeExecutor); @@ -115,7 +117,8 @@ jni::local_ref JReactInstance::initHybrid( jni::alias_ref jsTimerExecutor, jni::alias_ref jReactExceptionManager, jni::alias_ref jBindingsInstaller, - bool isProfiling) { + bool isProfiling, + bool useModernRuntimeScheduler) { return makeCxxInstance( jsEngineInstance, jsMessageQueueThread, @@ -124,7 +127,8 @@ jni::local_ref JReactInstance::initHybrid( jsTimerExecutor, jReactExceptionManager, jBindingsInstaller, - isProfiling); + isProfiling, + useModernRuntimeScheduler); } void JReactInstance::loadJSBundleFromAssets( diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactInstance.h b/packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactInstance.h index 55ec3e9ffa105a..0fd9a28a438a28 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactInstance.h +++ b/packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactInstance.h @@ -45,7 +45,8 @@ class JReactInstance : public jni::HybridClass { jni::alias_ref jsTimerExecutor, jni::alias_ref jReactExceptionManager, jni::alias_ref jBindingsInstaller, - bool isProfiling); + bool isProfiling, + bool useModernRuntimeScheduler); /* * Instantiates and returns an instance of `JSTimerExecutor`. @@ -90,7 +91,8 @@ class JReactInstance : public jni::HybridClass { jni::alias_ref jsTimerExecutor, jni::alias_ref jReactExceptionManager, jni::alias_ref jBindingsInstaller, - bool isProfiling) noexcept; + bool isProfiling, + bool useModernRuntimeScheduler) noexcept; jni::alias_ref getJSCallInvokerHolder(); jni::alias_ref diff --git a/packages/react-native/ReactCommon/react/runtime/ReactInstance.cpp b/packages/react-native/ReactCommon/react/runtime/ReactInstance.cpp index abfda3b74a41ab..ecb994e9f3c0a2 100644 --- a/packages/react-native/ReactCommon/react/runtime/ReactInstance.cpp +++ b/packages/react-native/ReactCommon/react/runtime/ReactInstance.cpp @@ -28,7 +28,8 @@ ReactInstance::ReactInstance( std::unique_ptr runtime, std::shared_ptr jsMessageQueueThread, std::shared_ptr timerManager, - JsErrorHandler::JsErrorHandlingFunc jsErrorHandlingFunc) + JsErrorHandler::JsErrorHandlingFunc jsErrorHandlingFunc, + bool useModernRuntimeScheduler) : runtime_(std::move(runtime)), jsMessageQueueThread_(jsMessageQueueThread), timerManager_(std::move(timerManager)), @@ -75,8 +76,8 @@ ReactInstance::ReactInstance( } }; - runtimeScheduler_ = - std::make_shared(std::move(runtimeExecutor)); + runtimeScheduler_ = std::make_shared( + std::move(runtimeExecutor), useModernRuntimeScheduler); auto pipedRuntimeExecutor = [runtimeScheduler = runtimeScheduler_.get()]( diff --git a/packages/react-native/ReactCommon/react/runtime/ReactInstance.h b/packages/react-native/ReactCommon/react/runtime/ReactInstance.h index f3126c302f387d..0d1762af01c529 100644 --- a/packages/react-native/ReactCommon/react/runtime/ReactInstance.h +++ b/packages/react-native/ReactCommon/react/runtime/ReactInstance.h @@ -32,7 +32,8 @@ class ReactInstance final { std::unique_ptr runtime, std::shared_ptr jsMessageQueueThread, std::shared_ptr timerManager, - JsErrorHandler::JsErrorHandlingFunc JsErrorHandlingFunc); + JsErrorHandler::JsErrorHandlingFunc JsErrorHandlingFunc, + bool useModernRuntimeScheduler = false); RuntimeExecutor getUnbufferedRuntimeExecutor() noexcept; diff --git a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.h b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.h index 2d57954823b273..3dc528f8a506ec 100644 --- a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.h +++ b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.h @@ -35,6 +35,18 @@ NS_ASSUME_NONNULL_BEGIN @end +/** + * This is a private protocol used to configure internal behavior of the runtime. + * DO NOT USE THIS OUTSIDE OF THE REACT NATIVE CODEBASE. + */ +@protocol RCTHostDelegateInternal + +// TODO(T166383606): Remove this method when we remove the legacy runtime scheduler or we have access to +// ReactNativeConfig before we initialize it. +- (BOOL)useModernRuntimeScheduler:(RCTHost *)host; + +@end + @protocol RCTHostRuntimeDelegate - (void)host:(RCTHost *)host didInitializeRuntime:(facebook::jsi::Runtime &)runtime; diff --git a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.mm b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.mm index f733d5514f6c8d..53ddc5cab4368b 100644 --- a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.mm +++ b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.mm @@ -23,7 +23,7 @@ using namespace facebook::react; -@interface RCTHost () +@interface RCTHost () @end @implementation RCTHost { @@ -247,6 +247,17 @@ - (void)instance:(RCTInstance *)instance didInitializeRuntime:(facebook::jsi::Ru [self.runtimeDelegate host:self didInitializeRuntime:runtime]; } +#pragma mark - RCTInstanceDelegateInternal + +- (BOOL)useModernRuntimeScheduler:(RCTHost *)host +{ + if ([_hostDelegate respondsToSelector:@selector(useModernRuntimeScheduler:)]) { + return [(id)_hostDelegate useModernRuntimeScheduler:self]; + } + + return NO; +} + #pragma mark - RCTContextContainerHandling - (void)didCreateContextContainer:(std::shared_ptr)contextContainer diff --git a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.h b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.h index be1bc2207c89ec..79e9ff50e2e9ee 100644 --- a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.h +++ b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.h @@ -46,6 +46,18 @@ RCT_EXTERN void RCTInstanceSetRuntimeDiagnosticFlags(NSString *_Nullable flags); @end +/** + * This is a private protocol used to configure internal behavior of the runtime. + * DO NOT USE THIS OUTSIDE OF THE REACT NATIVE CODEBASE. + */ +@protocol RCTInstanceDelegateInternal + +// TODO(T166383606): Remove this method when we remove the legacy runtime scheduler or we have access to +// ReactNativeConfig before we initialize it. +- (BOOL)useModernRuntimeScheduler:(RCTInstance *)instance; + +@end + typedef void (^_Null_unspecified RCTInstanceInitialBundleLoadCompletionBlock)(); /** diff --git a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm index f73100ae5fdc53..bf5bd8fe6ba42d 100644 --- a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm +++ b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm @@ -216,12 +216,18 @@ - (void)_start __weak __typeof(self) weakSelf = self; auto jsErrorHandlingFunc = [=](MapBuffer errorMap) { [weakSelf _handleJSErrorMap:std::move(errorMap)]; }; + auto useModernRuntimeScheduler = false; + if ([_delegate respondsToSelector:@selector(useModernRuntimeScheduler:)]) { + useModernRuntimeScheduler = [(id)_delegate useModernRuntimeScheduler:self]; + } + // Create the React Instance _reactInstance = std::make_unique( _jsEngineInstance->createJSRuntime(_jsThreadManager.jsMessageThread), _jsThreadManager.jsMessageThread, timerManager, - jsErrorHandlingFunc); + jsErrorHandlingFunc, + useModernRuntimeScheduler); _valid = true; RuntimeExecutor bufferedRuntimeExecutor = _reactInstance->getBufferedRuntimeExecutor();