From 6bfd4325261b97457fe6aecfeea361a7fd5c17f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Thu, 18 Jul 2024 02:20:03 -0700 Subject: [PATCH 1/2] Move timing definitions to a specific target Differential Revision: D59820241 --- .../ReactAndroid/src/main/jni/CMakeLists.txt | 1 + .../ReactCommon/cxxreact/CMakeLists.txt | 1 + .../ReactCommon/cxxreact/JSExecutor.cpp | 9 +--- .../cxxreact/React-cxxreact.podspec | 1 + .../react/performance/timeline/CMakeLists.txt | 1 + .../timeline/PerformanceEntryReporter.h | 3 +- .../React-performancetimeline.podspec | 1 + .../ReactCommon/react/timing/CMakeLists.txt | 18 +++++++ .../react/timing/React-timing.podspec | 44 +++++++++++++++++ .../ReactCommon/react/timing/primitives.h | 31 ++++++++++++ .../react/timing/tests/PrimitivesTest.cpp | 47 +++++++++++++++++++ .../react-native/scripts/cocoapods/utils.rb | 1 + .../react-native/scripts/react_native_pods.rb | 1 + 13 files changed, 150 insertions(+), 9 deletions(-) create mode 100644 packages/react-native/ReactCommon/react/timing/CMakeLists.txt create mode 100644 packages/react-native/ReactCommon/react/timing/React-timing.podspec create mode 100644 packages/react-native/ReactCommon/react/timing/primitives.h create mode 100644 packages/react-native/ReactCommon/react/timing/tests/PrimitivesTest.cpp diff --git a/packages/react-native/ReactAndroid/src/main/jni/CMakeLists.txt b/packages/react-native/ReactAndroid/src/main/jni/CMakeLists.txt index 488cd4aa29ee08..77ebde6f43cab5 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/CMakeLists.txt +++ b/packages/react-native/ReactAndroid/src/main/jni/CMakeLists.txt @@ -115,6 +115,7 @@ add_react_common_subdir(jserrorhandler) add_react_common_subdir(react/runtime) add_react_common_subdir(react/runtime/hermes) add_react_common_subdir(react/runtime/nativeviewconfig) +add_react_common_subdir(react/timing) # ReactAndroid JNI targets add_react_build_subdir(generated/source/codegen/jni) diff --git a/packages/react-native/ReactCommon/cxxreact/CMakeLists.txt b/packages/react-native/ReactCommon/cxxreact/CMakeLists.txt index 4b14f10da61164..3ebe171f11bdcf 100644 --- a/packages/react-native/ReactCommon/cxxreact/CMakeLists.txt +++ b/packages/react-native/ReactCommon/cxxreact/CMakeLists.txt @@ -28,4 +28,5 @@ target_link_libraries(react_cxxreact logger reactperflogger runtimeexecutor + react_timing react_debug) diff --git a/packages/react-native/ReactCommon/cxxreact/JSExecutor.cpp b/packages/react-native/ReactCommon/cxxreact/JSExecutor.cpp index 263ab90bb5cb31..9a9bb7a1811db6 100644 --- a/packages/react-native/ReactCommon/cxxreact/JSExecutor.cpp +++ b/packages/react-native/ReactCommon/cxxreact/JSExecutor.cpp @@ -12,6 +12,7 @@ #include #include +#include #include namespace facebook::react { @@ -26,13 +27,7 @@ std::string JSExecutor::getSyntheticBundlePath( } double JSExecutor::performanceNow() { - auto time = std::chrono::steady_clock::now(); - auto duration = std::chrono::duration_cast( - time.time_since_epoch()) - .count(); - - constexpr double NANOSECONDS_IN_MILLISECOND = 1000000.0; - return duration / NANOSECONDS_IN_MILLISECOND; + return chronoToDOMHighResTimeStamp(std::chrono::steady_clock::now()); } jsinspector_modern::RuntimeTargetDelegate& diff --git a/packages/react-native/ReactCommon/cxxreact/React-cxxreact.podspec b/packages/react-native/ReactCommon/cxxreact/React-cxxreact.podspec index 9160b03626550d..d30f33a22e163d 100644 --- a/packages/react-native/ReactCommon/cxxreact/React-cxxreact.podspec +++ b/packages/react-native/ReactCommon/cxxreact/React-cxxreact.podspec @@ -52,6 +52,7 @@ Pod::Spec.new do |s| s.dependency "React-jsi", version s.dependency "React-logger", version s.dependency "React-debug", version + s.dependency "React-timing", version s.resource_bundles = {'React-cxxreact_privacy' => 'PrivacyInfo.xcprivacy'} diff --git a/packages/react-native/ReactCommon/react/performance/timeline/CMakeLists.txt b/packages/react-native/ReactCommon/react/performance/timeline/CMakeLists.txt index 720ae9dcdee65e..b58b0924a373ee 100644 --- a/packages/react-native/ReactCommon/react/performance/timeline/CMakeLists.txt +++ b/packages/react-native/ReactCommon/react/performance/timeline/CMakeLists.txt @@ -19,4 +19,5 @@ add_library(react_performance_timeline SHARED ${react_performance_timeline_SRC}) target_include_directories(react_performance_timeline PUBLIC ${REACT_COMMON_DIR}) target_link_libraries(react_performance_timeline + react_timing react_cxxreact) diff --git a/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.h b/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.h index d5d7b772a593d2..7a4d313fb1bd04 100644 --- a/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.h +++ b/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.h @@ -7,6 +7,7 @@ #pragma once +#include #include "BoundedConsumableBuffer.h" #include @@ -20,8 +21,6 @@ namespace facebook::react { -using DOMHighResTimeStamp = double; - using PerformanceEntryInteractionId = uint32_t; enum class PerformanceEntryType { diff --git a/packages/react-native/ReactCommon/react/performance/timeline/React-performancetimeline.podspec b/packages/react-native/ReactCommon/react/performance/timeline/React-performancetimeline.podspec index ab531921b6b434..a8dbcbe7a01407 100644 --- a/packages/react-native/ReactCommon/react/performance/timeline/React-performancetimeline.podspec +++ b/packages/react-native/ReactCommon/react/performance/timeline/React-performancetimeline.podspec @@ -51,6 +51,7 @@ Pod::Spec.new do |s| s.header_mappings_dir = "../../.." end + s.dependency "React-timing" s.dependency "React-cxxreact" s.dependency "RCT-Folly", folly_version end diff --git a/packages/react-native/ReactCommon/react/timing/CMakeLists.txt b/packages/react-native/ReactCommon/react/timing/CMakeLists.txt new file mode 100644 index 00000000000000..7b9f30ca3d883c --- /dev/null +++ b/packages/react-native/ReactCommon/react/timing/CMakeLists.txt @@ -0,0 +1,18 @@ +# 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. + +cmake_minimum_required(VERSION 3.13) +set(CMAKE_VERBOSE_MAKEFILE on) + +add_compile_options( + -fexceptions + -frtti + -std=c++20 + -Wall + -Wpedantic) + +add_library(react_timing INTERFACE) + +target_include_directories(react_timing INTERFACE .) diff --git a/packages/react-native/ReactCommon/react/timing/React-timing.podspec b/packages/react-native/ReactCommon/react/timing/React-timing.podspec new file mode 100644 index 00000000000000..ccd14c99d95d18 --- /dev/null +++ b/packages/react-native/ReactCommon/react/timing/React-timing.podspec @@ -0,0 +1,44 @@ +# 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. + +require "json" + +package = JSON.parse(File.read(File.join(__dir__, "..", "..", "..", "package.json"))) +version = package['version'] + +source = { :git => 'https://github.com/facebook/react-native.git' } +if version == '1000.0.0' + # This is an unpublished version, use the latest commit hash of the react-native repo, which we’re presumably in. + source[:commit] = `git rev-parse HEAD`.strip if system("git rev-parse --git-dir > /dev/null 2>&1") +else + source[:tag] = "v#{version}" +end + +header_search_paths = [] + +if ENV['USE_FRAMEWORKS'] + header_search_paths << "\"$(PODS_TARGET_SRCROOT)/../..\"" # this is needed to allow the target access its own files +end + +Pod::Spec.new do |s| + s.name = "React-timing" + s.version = version + s.summary = "React Native timing primitives" + s.homepage = "https://reactnative.dev/" + s.license = package["license"] + s.author = "Meta Platforms, Inc. and its affiliates" + s.platforms = min_supported_versions + s.source = source + s.source_files = "*.{cpp,h}" + s.header_dir = "react/timing" + s.pod_target_xcconfig = { "CLANG_CXX_LANGUAGE_STANDARD" => rct_cxx_language_standard(), + "HEADER_SEARCH_PATHS" => header_search_paths.join(' '), + "DEFINES_MODULE" => "YES" } + + if ENV['USE_FRAMEWORKS'] + s.module_name = "React_timing" + s.header_mappings_dir = "./" + end +end diff --git a/packages/react-native/ReactCommon/react/timing/primitives.h b/packages/react-native/ReactCommon/react/timing/primitives.h new file mode 100644 index 00000000000000..135be263c9b05e --- /dev/null +++ b/packages/react-native/ReactCommon/react/timing/primitives.h @@ -0,0 +1,31 @@ +/* + * 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. + */ + +#pragma once + +#include + +namespace facebook::react { + +// `DOMHighResTimeStamp` represents a time value in milliseconds (time point or +// duration), with sub-millisecond precision. +// On the Web, the precision can be reduced for security purposes, but that is +// not necessary in React Native. +using DOMHighResTimeStamp = double; + +inline DOMHighResTimeStamp chronoToDOMHighResTimeStamp( + std::chrono::steady_clock::duration duration) { + return static_cast>(duration) + .count(); +} + +inline DOMHighResTimeStamp chronoToDOMHighResTimeStamp( + std::chrono::steady_clock::time_point timePoint) { + return chronoToDOMHighResTimeStamp(timePoint.time_since_epoch()); +} + +} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/timing/tests/PrimitivesTest.cpp b/packages/react-native/ReactCommon/react/timing/tests/PrimitivesTest.cpp new file mode 100644 index 00000000000000..80452a2f713d37 --- /dev/null +++ b/packages/react-native/ReactCommon/react/timing/tests/PrimitivesTest.cpp @@ -0,0 +1,47 @@ +/* + * 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. + */ + +#include + +#include "../primitives.h" + +namespace facebook::react { + +using Clock = std::chrono::steady_clock; +using TimePoint = std::chrono::time_point; + +TEST(chronoToDOMHighResTimeStamp, withDurations) { + EXPECT_EQ(chronoToDOMHighResTimeStamp(std::chrono::nanoseconds(10)), 0.00001); + EXPECT_EQ(chronoToDOMHighResTimeStamp(std::chrono::microseconds(10)), 0.01); + EXPECT_EQ(chronoToDOMHighResTimeStamp(std::chrono::milliseconds(10)), 10.0); + EXPECT_EQ(chronoToDOMHighResTimeStamp(std::chrono::seconds(10)), 10000.0); + EXPECT_EQ( + chronoToDOMHighResTimeStamp( + std::chrono::seconds(1) + std::chrono::nanoseconds(20)), + 1000.000020); +} + +TEST(chronoToDOMHighResTimeStamp, withTimePoints) { + EXPECT_EQ( + chronoToDOMHighResTimeStamp(TimePoint(std::chrono::nanoseconds(10))), + 0.00001); + EXPECT_EQ( + chronoToDOMHighResTimeStamp(TimePoint(std::chrono::microseconds(10))), + 0.01); + EXPECT_EQ( + chronoToDOMHighResTimeStamp(TimePoint(std::chrono::milliseconds(10))), + 10.0); + EXPECT_EQ( + chronoToDOMHighResTimeStamp(TimePoint(std::chrono::seconds(10))), + 10000.0); + EXPECT_EQ( + chronoToDOMHighResTimeStamp( + TimePoint(std::chrono::seconds(1) + std::chrono::nanoseconds(20))), + 1000.000020); +} + +} // namespace facebook::react diff --git a/packages/react-native/scripts/cocoapods/utils.rb b/packages/react-native/scripts/cocoapods/utils.rb index 7db8dacfd3b87f..7dd53126b2a156 100644 --- a/packages/react-native/scripts/cocoapods/utils.rb +++ b/packages/react-native/scripts/cocoapods/utils.rb @@ -619,6 +619,7 @@ def self.react_native_pods "React-perflogger", "React-rncore", "React-runtimeexecutor", + "React-timing", "ReactCommon", "Yoga", "boost", diff --git a/packages/react-native/scripts/react_native_pods.rb b/packages/react-native/scripts/react_native_pods.rb index 1dd1c98544adb5..97e82472ee47ae 100644 --- a/packages/react-native/scripts/react_native_pods.rb +++ b/packages/react-native/scripts/react_native_pods.rb @@ -142,6 +142,7 @@ def use_react_native! ( pod 'React-callinvoker', :path => "#{prefix}/ReactCommon/callinvoker" pod 'React-performancetimeline', :path => "#{prefix}/ReactCommon/react/performance/timeline" + pod 'React-timing', :path => "#{prefix}/ReactCommon/react/timing" pod 'React-runtimeexecutor', :path => "#{prefix}/ReactCommon/runtimeexecutor" pod 'React-runtimescheduler', :path => "#{prefix}/ReactCommon/react/renderer/runtimescheduler" pod 'React-rendererdebug', :path => "#{prefix}/ReactCommon/react/renderer/debug" From de67637aa7ca47adfa7f01f7286f14683e7dd4b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Thu, 18 Jul 2024 03:01:21 -0700 Subject: [PATCH 2/2] Implement Long Tasks API for PerformanceObserver (#45473) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/45473 This is a basic implementation of the Long Tasks API (https://w3c.github.io/longtasks/). It detects and reports long tasks when using the Event Loop (in the modern RuntimeScheduler) when a new feature flag for this purpose is enabled. This doesn't include attribution information at the moment. Changelog: [internal] Reviewed By: sammy-SC Differential Revision: D55491870 --- .../featureflags/ReactNativeFeatureFlags.kt | 8 ++- .../ReactNativeFeatureFlagsCxxAccessor.kt | 12 +++- .../ReactNativeFeatureFlagsCxxInterop.kt | 4 +- .../ReactNativeFeatureFlagsDefaults.kt | 4 +- .../ReactNativeFeatureFlagsLocalAccessor.kt | 13 +++- .../ReactNativeFeatureFlagsProvider.kt | 4 +- .../JReactNativeFeatureFlagsCxxInterop.cpp | 16 ++++- .../JReactNativeFeatureFlagsCxxInterop.h | 5 +- .../featureflags/ReactNativeFeatureFlags.cpp | 6 +- .../featureflags/ReactNativeFeatureFlags.h | 7 +- .../ReactNativeFeatureFlagsAccessor.cpp | 64 +++++++++++------- .../ReactNativeFeatureFlagsAccessor.h | 6 +- .../ReactNativeFeatureFlagsDefaults.h | 6 +- .../ReactNativeFeatureFlagsProvider.h | 3 +- .../NativeReactNativeFeatureFlags.cpp | 7 +- .../NativeReactNativeFeatureFlags.h | 4 +- .../NativePerformanceObserver.cpp | 9 ++- .../timeline/PerformanceEntryReporter.cpp | 10 +++ .../timeline/PerformanceEntryReporter.h | 7 +- .../renderer/runtimescheduler/CMakeLists.txt | 2 + .../React-runtimescheduler.podspec | 2 + .../runtimescheduler/RuntimeScheduler.cpp | 6 ++ .../runtimescheduler/RuntimeScheduler.h | 5 ++ .../RuntimeScheduler_Legacy.cpp | 5 ++ .../RuntimeScheduler_Legacy.h | 3 + .../RuntimeScheduler_Modern.cpp | 31 ++++++++- .../RuntimeScheduler_Modern.h | 10 +++ .../tests/RuntimeSchedulerTest.cpp | 66 +++++++++++++++++++ .../ReactCommon/react/runtime/CMakeLists.txt | 1 + .../react/runtime/React-RuntimeCore.podspec | 1 + .../react/runtime/ReactInstance.cpp | 4 ++ .../ReactNativeFeatureFlags.config.js | 5 ++ .../featureflags/ReactNativeFeatureFlags.js | 7 +- .../specs/NativeReactNativeFeatureFlags.js | 3 +- .../webapis/performance/PerformanceEntry.js | 2 +- .../performance/RawPerformanceEntry.js | 5 ++ .../Performance/PerformanceApiExample.js | 62 ++++++++++++++--- 37 files changed, 360 insertions(+), 55 deletions(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt index 5ea74ae63e1a9e..4d3932fc3dc4f6 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<2dda5cec029bcd7bb62e428f4b4557f5>> + * @generated SignedSource<<3fa1a97be20aa80ef7a1e1595bee0dac>> */ /** @@ -82,6 +82,12 @@ public object ReactNativeFeatureFlags { @JvmStatic public fun enableGranularShadowTreeStateReconciliation(): Boolean = accessor.enableGranularShadowTreeStateReconciliation() + /** + * Enables the reporting of long tasks through `PerformanceObserver`. Only works if the event loop is enabled. + */ + @JvmStatic + public fun enableLongTaskAPI(): Boolean = accessor.enableLongTaskAPI() + /** * Enables the use of microtasks in Hermes (scheduling) and RuntimeScheduler (execution). */ diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt index 55653f44df565b..6c5f44ced848ff 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<125b4c5029a2f7f3d10ecd6f5382c46c>> + * @generated SignedSource<<89b4a679d8121ece9291dd55c46567ad>> */ /** @@ -29,6 +29,7 @@ public class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAccesso private var enableAlignItemsBaselineOnFabricIOSCache: Boolean? = null private var enableCleanTextInputYogaNodeCache: Boolean? = null private var enableGranularShadowTreeStateReconciliationCache: Boolean? = null + private var enableLongTaskAPICache: Boolean? = null private var enableMicrotasksCache: Boolean? = null private var enablePropsUpdateReconciliationAndroidCache: Boolean? = null private var enableSynchronousStateUpdatesCache: Boolean? = null @@ -133,6 +134,15 @@ public class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAccesso return cached } + override fun enableLongTaskAPI(): Boolean { + var cached = enableLongTaskAPICache + if (cached == null) { + cached = ReactNativeFeatureFlagsCxxInterop.enableLongTaskAPI() + enableLongTaskAPICache = cached + } + return cached + } + override fun enableMicrotasks(): Boolean { var cached = enableMicrotasksCache if (cached == null) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt index 548cdbd76c9cb1..c371dd0a3c84b1 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<15d521183f0efa93c87155890d88cf72>> + * @generated SignedSource<<98bd8f4b2b262ac3e75897da515706f5>> */ /** @@ -46,6 +46,8 @@ public object ReactNativeFeatureFlagsCxxInterop { @DoNotStrip @JvmStatic public external fun enableGranularShadowTreeStateReconciliation(): Boolean + @DoNotStrip @JvmStatic public external fun enableLongTaskAPI(): Boolean + @DoNotStrip @JvmStatic public external fun enableMicrotasks(): Boolean @DoNotStrip @JvmStatic public external fun enablePropsUpdateReconciliationAndroid(): Boolean diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt index c251d476e2c818..d5b910ea1d1ab6 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<> */ /** @@ -41,6 +41,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi override fun enableGranularShadowTreeStateReconciliation(): Boolean = false + override fun enableLongTaskAPI(): Boolean = false + override fun enableMicrotasks(): Boolean = false override fun enablePropsUpdateReconciliationAndroid(): Boolean = false diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt index a75971522e7541..2de5f3a7d44b5c 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<0b90fde158e97c4cad711c3ab832f067>> + * @generated SignedSource<<96ed168a0e80e78d1d6f96374f045eca>> */ /** @@ -33,6 +33,7 @@ public class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcces private var enableAlignItemsBaselineOnFabricIOSCache: Boolean? = null private var enableCleanTextInputYogaNodeCache: Boolean? = null private var enableGranularShadowTreeStateReconciliationCache: Boolean? = null + private var enableLongTaskAPICache: Boolean? = null private var enableMicrotasksCache: Boolean? = null private var enablePropsUpdateReconciliationAndroidCache: Boolean? = null private var enableSynchronousStateUpdatesCache: Boolean? = null @@ -146,6 +147,16 @@ public class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcces return cached } + override fun enableLongTaskAPI(): Boolean { + var cached = enableLongTaskAPICache + if (cached == null) { + cached = currentProvider.enableLongTaskAPI() + accessedFeatureFlags.add("enableLongTaskAPI") + enableLongTaskAPICache = cached + } + return cached + } + override fun enableMicrotasks(): Boolean { var cached = enableMicrotasksCache if (cached == null) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt index a9ed3b088ed5d0..9e73803e3fda87 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<55fa16b3333fb8e2820a078c68850f91>> + * @generated SignedSource<<13469640e36f5985531f872b28ff0c30>> */ /** @@ -41,6 +41,8 @@ public interface ReactNativeFeatureFlagsProvider { @DoNotStrip public fun enableGranularShadowTreeStateReconciliation(): Boolean + @DoNotStrip public fun enableLongTaskAPI(): Boolean + @DoNotStrip public fun enableMicrotasks(): Boolean @DoNotStrip public fun enablePropsUpdateReconciliationAndroid(): Boolean diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp index 25dc5d54ae3964..46d1431650d50a 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<0be20439a937a322afa27a9c935a98ad>> + * @generated SignedSource<<8efce8efb9cc312512b8736ed0073b30>> */ /** @@ -93,6 +93,12 @@ class ReactNativeFeatureFlagsProviderHolder return method(javaProvider_); } + bool enableLongTaskAPI() override { + static const auto method = + getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableLongTaskAPI"); + return method(javaProvider_); + } + bool enableMicrotasks() override { static const auto method = getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableMicrotasks"); @@ -274,6 +280,11 @@ bool JReactNativeFeatureFlagsCxxInterop::enableGranularShadowTreeStateReconcilia return ReactNativeFeatureFlags::enableGranularShadowTreeStateReconciliation(); } +bool JReactNativeFeatureFlagsCxxInterop::enableLongTaskAPI( + facebook::jni::alias_ref /*unused*/) { + return ReactNativeFeatureFlags::enableLongTaskAPI(); +} + bool JReactNativeFeatureFlagsCxxInterop::enableMicrotasks( facebook::jni::alias_ref /*unused*/) { return ReactNativeFeatureFlags::enableMicrotasks(); @@ -428,6 +439,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() { makeNativeMethod( "enableGranularShadowTreeStateReconciliation", JReactNativeFeatureFlagsCxxInterop::enableGranularShadowTreeStateReconciliation), + makeNativeMethod( + "enableLongTaskAPI", + JReactNativeFeatureFlagsCxxInterop::enableLongTaskAPI), makeNativeMethod( "enableMicrotasks", JReactNativeFeatureFlagsCxxInterop::enableMicrotasks), diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h index bd890fdbbc739b..7f180c57cd2205 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h +++ b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<5771674cf23f066fb2c7a493441e9967>> + * @generated SignedSource<> */ /** @@ -57,6 +57,9 @@ class JReactNativeFeatureFlagsCxxInterop static bool enableGranularShadowTreeStateReconciliation( facebook::jni::alias_ref); + static bool enableLongTaskAPI( + facebook::jni::alias_ref); + static bool enableMicrotasks( facebook::jni::alias_ref); diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp index 22034069e7ca34..22b75fdcd4eb8e 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<0f673fbed5f3cce13f7a42a8817ed404>> + * @generated SignedSource<> */ /** @@ -57,6 +57,10 @@ bool ReactNativeFeatureFlags::enableGranularShadowTreeStateReconciliation() { return getAccessor().enableGranularShadowTreeStateReconciliation(); } +bool ReactNativeFeatureFlags::enableLongTaskAPI() { + return getAccessor().enableLongTaskAPI(); +} + bool ReactNativeFeatureFlags::enableMicrotasks() { return getAccessor().enableMicrotasks(); } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h index 47350ccbed94c4..a869cf3141311f 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<5b0c518e45a5c98814c8151870aed591>> */ /** @@ -82,6 +82,11 @@ class ReactNativeFeatureFlags { */ RN_EXPORT static bool enableGranularShadowTreeStateReconciliation(); + /** + * Enables the reporting of long tasks through `PerformanceObserver`. Only works if the event loop is enabled. + */ + RN_EXPORT static bool enableLongTaskAPI(); + /** * Enables the use of microtasks in Hermes (scheduling) and RuntimeScheduler (execution). */ diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp index ed6456c75fb958..baddfd65feed44 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<1ee6db4bee3b22c31d5f70785ea9e91a>> + * @generated SignedSource<<18ca9d8ecd307861ff00736d18c517f4>> */ /** @@ -191,6 +191,24 @@ bool ReactNativeFeatureFlagsAccessor::enableGranularShadowTreeStateReconciliatio return flagValue.value(); } +bool ReactNativeFeatureFlagsAccessor::enableLongTaskAPI() { + auto flagValue = enableLongTaskAPI_.load(); + + if (!flagValue.has_value()) { + // This block is not exclusive but it is not necessary. + // If multiple threads try to initialize the feature flag, we would only + // be accessing the provider multiple times but the end state of this + // instance and the returned flag value would be the same. + + markFlagAsAccessed(9, "enableLongTaskAPI"); + + flagValue = currentProvider_->enableLongTaskAPI(); + enableLongTaskAPI_ = flagValue; + } + + return flagValue.value(); +} + bool ReactNativeFeatureFlagsAccessor::enableMicrotasks() { auto flagValue = enableMicrotasks_.load(); @@ -200,7 +218,7 @@ bool ReactNativeFeatureFlagsAccessor::enableMicrotasks() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(9, "enableMicrotasks"); + markFlagAsAccessed(10, "enableMicrotasks"); flagValue = currentProvider_->enableMicrotasks(); enableMicrotasks_ = flagValue; @@ -218,7 +236,7 @@ bool ReactNativeFeatureFlagsAccessor::enablePropsUpdateReconciliationAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(10, "enablePropsUpdateReconciliationAndroid"); + markFlagAsAccessed(11, "enablePropsUpdateReconciliationAndroid"); flagValue = currentProvider_->enablePropsUpdateReconciliationAndroid(); enablePropsUpdateReconciliationAndroid_ = flagValue; @@ -236,7 +254,7 @@ bool ReactNativeFeatureFlagsAccessor::enableSynchronousStateUpdates() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(11, "enableSynchronousStateUpdates"); + markFlagAsAccessed(12, "enableSynchronousStateUpdates"); flagValue = currentProvider_->enableSynchronousStateUpdates(); enableSynchronousStateUpdates_ = flagValue; @@ -254,7 +272,7 @@ bool ReactNativeFeatureFlagsAccessor::enableUIConsistency() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(12, "enableUIConsistency"); + markFlagAsAccessed(13, "enableUIConsistency"); flagValue = currentProvider_->enableUIConsistency(); enableUIConsistency_ = flagValue; @@ -272,7 +290,7 @@ bool ReactNativeFeatureFlagsAccessor::fetchImagesInViewPreallocation() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(13, "fetchImagesInViewPreallocation"); + markFlagAsAccessed(14, "fetchImagesInViewPreallocation"); flagValue = currentProvider_->fetchImagesInViewPreallocation(); fetchImagesInViewPreallocation_ = flagValue; @@ -290,7 +308,7 @@ bool ReactNativeFeatureFlagsAccessor::fixIncorrectScrollViewStateUpdateOnAndroid // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(14, "fixIncorrectScrollViewStateUpdateOnAndroid"); + markFlagAsAccessed(15, "fixIncorrectScrollViewStateUpdateOnAndroid"); flagValue = currentProvider_->fixIncorrectScrollViewStateUpdateOnAndroid(); fixIncorrectScrollViewStateUpdateOnAndroid_ = flagValue; @@ -308,7 +326,7 @@ bool ReactNativeFeatureFlagsAccessor::fixMappingOfEventPrioritiesBetweenFabricAn // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(15, "fixMappingOfEventPrioritiesBetweenFabricAndReact"); + markFlagAsAccessed(16, "fixMappingOfEventPrioritiesBetweenFabricAndReact"); flagValue = currentProvider_->fixMappingOfEventPrioritiesBetweenFabricAndReact(); fixMappingOfEventPrioritiesBetweenFabricAndReact_ = flagValue; @@ -326,7 +344,7 @@ bool ReactNativeFeatureFlagsAccessor::fixMissedFabricStateUpdatesOnAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(16, "fixMissedFabricStateUpdatesOnAndroid"); + markFlagAsAccessed(17, "fixMissedFabricStateUpdatesOnAndroid"); flagValue = currentProvider_->fixMissedFabricStateUpdatesOnAndroid(); fixMissedFabricStateUpdatesOnAndroid_ = flagValue; @@ -344,7 +362,7 @@ bool ReactNativeFeatureFlagsAccessor::forceBatchingMountItemsOnAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(17, "forceBatchingMountItemsOnAndroid"); + markFlagAsAccessed(18, "forceBatchingMountItemsOnAndroid"); flagValue = currentProvider_->forceBatchingMountItemsOnAndroid(); forceBatchingMountItemsOnAndroid_ = flagValue; @@ -362,7 +380,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxEnabledDebug() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(18, "fuseboxEnabledDebug"); + markFlagAsAccessed(19, "fuseboxEnabledDebug"); flagValue = currentProvider_->fuseboxEnabledDebug(); fuseboxEnabledDebug_ = flagValue; @@ -380,7 +398,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxEnabledRelease() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(19, "fuseboxEnabledRelease"); + markFlagAsAccessed(20, "fuseboxEnabledRelease"); flagValue = currentProvider_->fuseboxEnabledRelease(); fuseboxEnabledRelease_ = flagValue; @@ -398,7 +416,7 @@ bool ReactNativeFeatureFlagsAccessor::initEagerTurboModulesOnNativeModulesQueueA // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(20, "initEagerTurboModulesOnNativeModulesQueueAndroid"); + markFlagAsAccessed(21, "initEagerTurboModulesOnNativeModulesQueueAndroid"); flagValue = currentProvider_->initEagerTurboModulesOnNativeModulesQueueAndroid(); initEagerTurboModulesOnNativeModulesQueueAndroid_ = flagValue; @@ -416,7 +434,7 @@ bool ReactNativeFeatureFlagsAccessor::lazyAnimationCallbacks() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(21, "lazyAnimationCallbacks"); + markFlagAsAccessed(22, "lazyAnimationCallbacks"); flagValue = currentProvider_->lazyAnimationCallbacks(); lazyAnimationCallbacks_ = flagValue; @@ -434,7 +452,7 @@ bool ReactNativeFeatureFlagsAccessor::loadVectorDrawablesOnImages() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(22, "loadVectorDrawablesOnImages"); + markFlagAsAccessed(23, "loadVectorDrawablesOnImages"); flagValue = currentProvider_->loadVectorDrawablesOnImages(); loadVectorDrawablesOnImages_ = flagValue; @@ -452,7 +470,7 @@ bool ReactNativeFeatureFlagsAccessor::setAndroidLayoutDirection() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(23, "setAndroidLayoutDirection"); + markFlagAsAccessed(24, "setAndroidLayoutDirection"); flagValue = currentProvider_->setAndroidLayoutDirection(); setAndroidLayoutDirection_ = flagValue; @@ -470,7 +488,7 @@ bool ReactNativeFeatureFlagsAccessor::useImmediateExecutorInAndroidBridgeless() // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(24, "useImmediateExecutorInAndroidBridgeless"); + markFlagAsAccessed(25, "useImmediateExecutorInAndroidBridgeless"); flagValue = currentProvider_->useImmediateExecutorInAndroidBridgeless(); useImmediateExecutorInAndroidBridgeless_ = flagValue; @@ -488,7 +506,7 @@ bool ReactNativeFeatureFlagsAccessor::useModernRuntimeScheduler() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(25, "useModernRuntimeScheduler"); + markFlagAsAccessed(26, "useModernRuntimeScheduler"); flagValue = currentProvider_->useModernRuntimeScheduler(); useModernRuntimeScheduler_ = flagValue; @@ -506,7 +524,7 @@ bool ReactNativeFeatureFlagsAccessor::useNativeViewConfigsInBridgelessMode() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(26, "useNativeViewConfigsInBridgelessMode"); + markFlagAsAccessed(27, "useNativeViewConfigsInBridgelessMode"); flagValue = currentProvider_->useNativeViewConfigsInBridgelessMode(); useNativeViewConfigsInBridgelessMode_ = flagValue; @@ -524,7 +542,7 @@ bool ReactNativeFeatureFlagsAccessor::useNewReactImageViewBackgroundDrawing() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(27, "useNewReactImageViewBackgroundDrawing"); + markFlagAsAccessed(28, "useNewReactImageViewBackgroundDrawing"); flagValue = currentProvider_->useNewReactImageViewBackgroundDrawing(); useNewReactImageViewBackgroundDrawing_ = flagValue; @@ -542,7 +560,7 @@ bool ReactNativeFeatureFlagsAccessor::useRuntimeShadowNodeReferenceUpdate() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(28, "useRuntimeShadowNodeReferenceUpdate"); + markFlagAsAccessed(29, "useRuntimeShadowNodeReferenceUpdate"); flagValue = currentProvider_->useRuntimeShadowNodeReferenceUpdate(); useRuntimeShadowNodeReferenceUpdate_ = flagValue; @@ -560,7 +578,7 @@ bool ReactNativeFeatureFlagsAccessor::useRuntimeShadowNodeReferenceUpdateOnLayou // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(29, "useRuntimeShadowNodeReferenceUpdateOnLayout"); + markFlagAsAccessed(30, "useRuntimeShadowNodeReferenceUpdateOnLayout"); flagValue = currentProvider_->useRuntimeShadowNodeReferenceUpdateOnLayout(); useRuntimeShadowNodeReferenceUpdateOnLayout_ = flagValue; @@ -578,7 +596,7 @@ bool ReactNativeFeatureFlagsAccessor::useStateAlignmentMechanism() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(30, "useStateAlignmentMechanism"); + markFlagAsAccessed(31, "useStateAlignmentMechanism"); flagValue = currentProvider_->useStateAlignmentMechanism(); useStateAlignmentMechanism_ = flagValue; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h index 54fb1b5856af08..8d03c5414541ff 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<6c2bca1240818fc2c13b76943f9954d7>> + * @generated SignedSource<<6f05e241733069ee1bacf671365d8390>> */ /** @@ -40,6 +40,7 @@ class ReactNativeFeatureFlagsAccessor { bool enableAlignItemsBaselineOnFabricIOS(); bool enableCleanTextInputYogaNode(); bool enableGranularShadowTreeStateReconciliation(); + bool enableLongTaskAPI(); bool enableMicrotasks(); bool enablePropsUpdateReconciliationAndroid(); bool enableSynchronousStateUpdates(); @@ -72,7 +73,7 @@ class ReactNativeFeatureFlagsAccessor { std::unique_ptr currentProvider_; bool wasOverridden_; - std::array, 31> accessedFeatureFlags_; + std::array, 32> accessedFeatureFlags_; std::atomic> commonTestFlag_; std::atomic> allowCollapsableChildren_; @@ -83,6 +84,7 @@ class ReactNativeFeatureFlagsAccessor { std::atomic> enableAlignItemsBaselineOnFabricIOS_; std::atomic> enableCleanTextInputYogaNode_; std::atomic> enableGranularShadowTreeStateReconciliation_; + std::atomic> enableLongTaskAPI_; std::atomic> enableMicrotasks_; std::atomic> enablePropsUpdateReconciliationAndroid_; std::atomic> enableSynchronousStateUpdates_; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h index 75a1f0a67fea06..08fd5e08631f6c 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<> */ /** @@ -63,6 +63,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider { return false; } + bool enableLongTaskAPI() override { + return false; + } + bool enableMicrotasks() override { return false; } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h index 313aecdb297b6a..215a3992df4c10 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<21fa85ad4854cc37f3699a3adcb15813>> */ /** @@ -34,6 +34,7 @@ class ReactNativeFeatureFlagsProvider { virtual bool enableAlignItemsBaselineOnFabricIOS() = 0; virtual bool enableCleanTextInputYogaNode() = 0; virtual bool enableGranularShadowTreeStateReconciliation() = 0; + virtual bool enableLongTaskAPI() = 0; virtual bool enableMicrotasks() = 0; virtual bool enablePropsUpdateReconciliationAndroid() = 0; virtual bool enableSynchronousStateUpdates() = 0; diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp index 59d57f081afd1c..16997bb5f425cc 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp +++ b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<8458b6146882bfab11157e94eaaf9967>> + * @generated SignedSource<<8240a36100c3657e90d6388afdcf26e8>> */ /** @@ -82,6 +82,11 @@ bool NativeReactNativeFeatureFlags::enableGranularShadowTreeStateReconciliation( return ReactNativeFeatureFlags::enableGranularShadowTreeStateReconciliation(); } +bool NativeReactNativeFeatureFlags::enableLongTaskAPI( + jsi::Runtime& /*runtime*/) { + return ReactNativeFeatureFlags::enableLongTaskAPI(); +} + bool NativeReactNativeFeatureFlags::enableMicrotasks( jsi::Runtime& /*runtime*/) { return ReactNativeFeatureFlags::enableMicrotasks(); diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h index f22e5be46c2d89..4c5d6ddca1b501 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h +++ b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<36ba8783cdd7ccb5d745dffb978d13ec>> */ /** @@ -53,6 +53,8 @@ class NativeReactNativeFeatureFlags bool enableGranularShadowTreeStateReconciliation(jsi::Runtime& runtime); + bool enableLongTaskAPI(jsi::Runtime& runtime); + bool enableMicrotasks(jsi::Runtime& runtime); bool enablePropsUpdateReconciliationAndroid(jsi::Runtime& runtime); diff --git a/packages/react-native/ReactCommon/react/nativemodule/webperformance/NativePerformanceObserver.cpp b/packages/react-native/ReactCommon/react/nativemodule/webperformance/NativePerformanceObserver.cpp index b84c961c2821b2..6ec448ad426b6e 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/webperformance/NativePerformanceObserver.cpp +++ b/packages/react-native/ReactCommon/react/nativemodule/webperformance/NativePerformanceObserver.cpp @@ -9,6 +9,7 @@ #include "NativePerformanceObserver.h" +#include #include #include #include @@ -113,11 +114,17 @@ std::vector NativePerformanceObserver::getEntries( std::vector NativePerformanceObserver::getSupportedPerformanceEntryTypes( jsi::Runtime& /*rt*/) { - return { + std::vector supportedEntries = { PerformanceEntryType::MARK, PerformanceEntryType::MEASURE, PerformanceEntryType::EVENT, }; + + if (ReactNativeFeatureFlags::enableLongTaskAPI()) { + supportedEntries.push_back(PerformanceEntryType::LONGTASK); + } + + return supportedEntries; } } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.cpp b/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.cpp index 33fa784d72b66d..7559022beab27d 100644 --- a/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.cpp +++ b/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.cpp @@ -287,6 +287,16 @@ void PerformanceEntryReporter::logEventEntry( .interactionId = interactionId}); } +void PerformanceEntryReporter::logLongTaskEntry( + DOMHighResTimeStamp startTime, + DOMHighResTimeStamp duration) { + logEntry( + {.name = std::string{"self"}, + .entryType = PerformanceEntryType::LONGTASK, + .startTime = startTime, + .duration = duration}); +} + void PerformanceEntryReporter::scheduleFlushBuffer() { if (callback_) { callback_(); diff --git a/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.h b/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.h index 7a4d313fb1bd04..1f2615270f916c 100644 --- a/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.h +++ b/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.h @@ -28,7 +28,8 @@ enum class PerformanceEntryType { MARK = 1, MEASURE = 2, EVENT = 3, - _NEXT = 4, + LONGTASK = 4, + _NEXT = 5, }; struct PerformanceEntry { @@ -80,6 +81,8 @@ struct PerformanceEntryBuffer { constexpr size_t NUM_PERFORMANCE_ENTRY_TYPES = (size_t)PerformanceEntryType::_NEXT - 1; // Valid types start from 1. +constexpr DOMHighResTimeStamp LONG_TASK_DURATION_THRESHOLD_MS = 50.0; + class PerformanceEntryReporter { public: PerformanceEntryReporter(); @@ -157,6 +160,8 @@ class PerformanceEntryReporter { double processingEnd, uint32_t interactionId); + void logLongTaskEntry(double startTime, double duration); + const std::unordered_map& getEventCounts() const { return eventCounts_; } diff --git a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/CMakeLists.txt b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/CMakeLists.txt index 9149d39eeabebf..ec62d4df3caaa9 100644 --- a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/CMakeLists.txt +++ b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/CMakeLists.txt @@ -23,8 +23,10 @@ target_link_libraries(react_render_runtimescheduler callinvoker jsi react_debug + react_performance_timeline react_render_consistency react_render_debug + react_timing react_utils react_featureflags runtimeexecutor) diff --git a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/React-runtimescheduler.podspec b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/React-runtimescheduler.podspec index 3fbd814fb03984..fa8143370220cd 100644 --- a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/React-runtimescheduler.podspec +++ b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/React-runtimescheduler.podspec @@ -57,9 +57,11 @@ Pod::Spec.new do |s| s.dependency "React-rendererdebug" s.dependency "React-utils" s.dependency "React-featureflags" + s.dependency "React-timing" s.dependency "glog" s.dependency "RCT-Folly", folly_version s.dependency "React-jsi" + s.dependency "React-performancetimeline" s.dependency "React-rendererconsistency" add_dependency(s, "React-debug") diff --git a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.cpp b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.cpp index b34467b2b476dc..24c9f617f02513 100644 --- a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.cpp +++ b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.cpp @@ -102,4 +102,10 @@ void RuntimeScheduler::setShadowTreeRevisionConsistencyManager( shadowTreeRevisionConsistencyManager); } +void RuntimeScheduler::setPerformanceEntryReporter( + PerformanceEntryReporter* performanceEntryReporter) { + return runtimeSchedulerImpl_->setPerformanceEntryReporter( + performanceEntryReporter); +} + } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.h b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.h index 277f171d48c0e3..6cd772e447ddbe 100644 --- a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.h +++ b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.h @@ -8,6 +8,7 @@ #pragma once #include +#include #include #include #include @@ -48,6 +49,8 @@ class RuntimeSchedulerBase { RuntimeSchedulerRenderingUpdate&& renderingUpdate) = 0; virtual void setShadowTreeRevisionConsistencyManager( ShadowTreeRevisionConsistencyManager* provider) = 0; + virtual void setPerformanceEntryReporter( + PerformanceEntryReporter* reporter) = 0; }; // This is a proxy for RuntimeScheduler implementation, which will be selected @@ -154,6 +157,8 @@ class RuntimeScheduler final : RuntimeSchedulerBase { ShadowTreeRevisionConsistencyManager* shadowTreeRevisionConsistencyManager) override; + void setPerformanceEntryReporter(PerformanceEntryReporter* reporter) override; + private: // Actual implementation, stored as a unique pointer to simplify memory // management. diff --git a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Legacy.cpp b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Legacy.cpp index 4683a2c2945ac2..5129d416a4ff31 100644 --- a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Legacy.cpp +++ b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Legacy.cpp @@ -188,6 +188,11 @@ void RuntimeScheduler_Legacy::setShadowTreeRevisionConsistencyManager( shadowTreeRevisionConsistencyManager_ = shadowTreeRevisionConsistencyManager; } +void RuntimeScheduler_Legacy::setPerformanceEntryReporter( + PerformanceEntryReporter* /*performanceEntryReporter*/) { + // No-op in the legacy scheduler +} + #pragma mark - Private void RuntimeScheduler_Legacy::scheduleWorkLoopIfNecessary() { diff --git a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Legacy.h b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Legacy.h index 7ecc15a47a2ae3..abd7a13a2878bd 100644 --- a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Legacy.h +++ b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Legacy.h @@ -127,6 +127,9 @@ class RuntimeScheduler_Legacy final : public RuntimeSchedulerBase { ShadowTreeRevisionConsistencyManager* shadowTreeRevisionConsistencyManager) override; + void setPerformanceEntryReporter( + PerformanceEntryReporter* performanceEntryReporter) override; + private: std::priority_queue< std::shared_ptr, diff --git a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Modern.cpp b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Modern.cpp index 6684f62c7e25ab..2969fb3de6bfdc 100644 --- a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Modern.cpp +++ b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Modern.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -217,6 +218,11 @@ void RuntimeScheduler_Modern::setShadowTreeRevisionConsistencyManager( shadowTreeRevisionConsistencyManager_ = shadowTreeRevisionConsistencyManager; } +void RuntimeScheduler_Modern::setPerformanceEntryReporter( + PerformanceEntryReporter* performanceEntryReporter) { + performanceEntryReporter_ = performanceEntryReporter; +} + #pragma mark - Private void RuntimeScheduler_Modern::scheduleTask(std::shared_ptr task) { @@ -300,7 +306,7 @@ std::shared_ptr RuntimeScheduler_Modern::selectTask( void RuntimeScheduler_Modern::runEventLoopTick( jsi::Runtime& runtime, Task& task, - RuntimeSchedulerTimePoint currentTime) { + RuntimeSchedulerTimePoint taskStartTime) { SystraceSection s("RuntimeScheduler::runEventLoopTick"); currentTask_ = &task; @@ -310,7 +316,7 @@ void RuntimeScheduler_Modern::runEventLoopTick( ScopedShadowTreeRevisionLock revisionLock( shadowTreeRevisionConsistencyManager_); - auto didUserCallbackTimeout = task.expirationTime <= currentTime; + auto didUserCallbackTimeout = task.expirationTime <= taskStartTime; executeTask(runtime, task, didUserCallbackTimeout); if (ReactNativeFeatureFlags::enableMicrotasks()) { @@ -322,6 +328,11 @@ void RuntimeScheduler_Modern::runEventLoopTick( // "Update the rendering" step. updateRendering(); } + + if (ReactNativeFeatureFlags::enableLongTaskAPI()) { + auto taskEndTime = now_(); + reportLongTasks(task, taskStartTime, taskEndTime); + } } currentTask_ = nullptr; @@ -408,4 +419,20 @@ void RuntimeScheduler_Modern::performMicrotaskCheckpoint( } } +void RuntimeScheduler_Modern::reportLongTasks( + const Task& /*task*/, + RuntimeSchedulerTimePoint startTime, + RuntimeSchedulerTimePoint endTime) { + auto reporter = performanceEntryReporter_; + if (reporter == nullptr) { + return; + } + + auto durationMs = chronoToDOMHighResTimeStamp(endTime - startTime); + if (durationMs >= LONG_TASK_DURATION_THRESHOLD_MS) { + auto startTimeMs = chronoToDOMHighResTimeStamp(startTime); + reporter->logLongTaskEntry(startTimeMs, durationMs); + } +} + } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Modern.h b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Modern.h index 90f5d81a0334eb..67c01a2536519e 100644 --- a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Modern.h +++ b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Modern.h @@ -144,6 +144,9 @@ class RuntimeScheduler_Modern final : public RuntimeSchedulerBase { ShadowTreeRevisionConsistencyManager* shadowTreeRevisionConsistencyManager) override; + void setPerformanceEntryReporter( + PerformanceEntryReporter* performanceEntryReporter) override; + private: std::atomic syncTaskRequests_{0}; @@ -193,6 +196,11 @@ class RuntimeScheduler_Modern final : public RuntimeSchedulerBase { bool performingMicrotaskCheckpoint_{false}; void performMicrotaskCheckpoint(jsi::Runtime& runtime); + void reportLongTasks( + const Task& task, + RuntimeSchedulerTimePoint startTime, + RuntimeSchedulerTimePoint endTime); + /* * Returns a time point representing the current point in time. May be called * from multiple threads. @@ -208,6 +216,8 @@ class RuntimeScheduler_Modern final : public RuntimeSchedulerBase { std::queue pendingRenderingUpdates_; ShadowTreeRevisionConsistencyManager* shadowTreeRevisionConsistencyManager_{ nullptr}; + + PerformanceEntryReporter* performanceEntryReporter_{nullptr}; }; } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/tests/RuntimeSchedulerTest.cpp b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/tests/RuntimeSchedulerTest.cpp index 27bf5852ecda45..9aa37a14283a62 100644 --- a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/tests/RuntimeSchedulerTest.cpp +++ b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/tests/RuntimeSchedulerTest.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -42,6 +43,10 @@ class RuntimeSchedulerTestFeatureFlags return forcedBatchRenderingUpdatesInEventLoop; } + bool enableLongTaskAPI() override { + return true; + } + private: bool useModernRuntimeScheduler_; }; @@ -77,12 +82,19 @@ class RuntimeSchedulerTest : public testing::TestWithParam { return stubClock_->getNow(); }; + performanceEntryReporter_ = PerformanceEntryReporter::getInstance().get(); + + performanceEntryReporter_->startReporting(PerformanceEntryType::LONGTASK); + runtimeScheduler_ = std::make_unique(runtimeExecutor, stubNow); + + runtimeScheduler_->setPerformanceEntryReporter(performanceEntryReporter_); } void TearDown() override { ReactNativeFeatureFlags::dangerouslyReset(); + performanceEntryReporter_->popPendingEntries(); } jsi::Function createHostFunctionFromLambda( @@ -109,6 +121,7 @@ class RuntimeSchedulerTest : public testing::TestWithParam { std::unique_ptr stubQueue_; std::unique_ptr runtimeScheduler_; std::shared_ptr stubErrorUtils_; + PerformanceEntryReporter* performanceEntryReporter_{}; }; TEST_P(RuntimeSchedulerTest, now) { @@ -1166,6 +1179,59 @@ TEST_P(RuntimeSchedulerTest, errorInTaskShouldNotStopMicrotasks) { EXPECT_EQ(stubErrorUtils_->getReportFatalCallCount(), 1); } +TEST_P(RuntimeSchedulerTest, reportsLongTasks) { + // Only for modern runtime scheduler + if (!GetParam()) { + return; + } + + bool didRunTask1 = false; + stubClock_->setTimePoint(10ms); + + auto callback1 = createHostFunctionFromLambda([&](bool /* unused */) { + didRunTask1 = true; + + stubClock_->advanceTimeBy(10ms); + + return jsi::Value::undefined(); + }); + + runtimeScheduler_->scheduleTask( + SchedulerPriority::NormalPriority, std::move(callback1)); + + stubQueue_->tick(); + + EXPECT_EQ(didRunTask1, 1); + EXPECT_EQ(stubQueue_->size(), 0); + auto pendingEntries = performanceEntryReporter_->popPendingEntries(); + EXPECT_EQ(pendingEntries.entries.size(), 0); + + bool didRunTask2 = false; + stubClock_->setTimePoint(100ms); + + auto callback2 = createHostFunctionFromLambda([&](bool /* unused */) { + didRunTask2 = true; + + stubClock_->advanceTimeBy(50ms); + + return jsi::Value::undefined(); + }); + + runtimeScheduler_->scheduleTask( + SchedulerPriority::NormalPriority, std::move(callback2)); + + stubQueue_->tick(); + + EXPECT_EQ(didRunTask2, 1); + EXPECT_EQ(stubQueue_->size(), 0); + pendingEntries = performanceEntryReporter_->popPendingEntries(); + EXPECT_EQ(pendingEntries.entries.size(), 1); + EXPECT_EQ( + pendingEntries.entries[0].entryType, PerformanceEntryType::LONGTASK); + EXPECT_EQ(pendingEntries.entries[0].startTime, 100); + EXPECT_EQ(pendingEntries.entries[0].duration, 50); +} + INSTANTIATE_TEST_SUITE_P( UseModernRuntimeScheduler, RuntimeSchedulerTest, diff --git a/packages/react-native/ReactCommon/react/runtime/CMakeLists.txt b/packages/react-native/ReactCommon/react/runtime/CMakeLists.txt index 3a440e009b0c7a..08a0e7e86efd3e 100644 --- a/packages/react-native/ReactCommon/react/runtime/CMakeLists.txt +++ b/packages/react-native/ReactCommon/react/runtime/CMakeLists.txt @@ -38,5 +38,6 @@ target_link_libraries( react_utils jsinspector react_featureflags + react_performance_timeline react_utils ) diff --git a/packages/react-native/ReactCommon/react/runtime/React-RuntimeCore.podspec b/packages/react-native/ReactCommon/react/runtime/React-RuntimeCore.podspec index 6308397bdefd87..f247f8b834dcad 100644 --- a/packages/react-native/ReactCommon/react/runtime/React-RuntimeCore.podspec +++ b/packages/react-native/ReactCommon/react/runtime/React-RuntimeCore.podspec @@ -53,6 +53,7 @@ Pod::Spec.new do |s| s.dependency "glog" s.dependency "React-jsi" s.dependency "React-jserrorhandler" + s.dependency "React-performancetimeline" s.dependency "React-runtimescheduler" s.dependency "React-utils" s.dependency "React-featureflags" diff --git a/packages/react-native/ReactCommon/react/runtime/ReactInstance.cpp b/packages/react-native/ReactCommon/react/runtime/ReactInstance.cpp index 0b8128c586f4b0..b305ada5813264 100644 --- a/packages/react-native/ReactCommon/react/runtime/ReactInstance.cpp +++ b/packages/react-native/ReactCommon/react/runtime/ReactInstance.cpp @@ -119,6 +119,10 @@ ReactInstance::ReactInstance( } runtimeScheduler_ = std::make_shared(runtimeExecutor); + runtimeScheduler_->setPerformanceEntryReporter( + // FIXME: Move creation of PerformanceEntryReporter to here and guarantee + // that its lifetime is the same as the runtime. + PerformanceEntryReporter::getInstance().get()); bufferedRuntimeExecutor_ = std::make_shared( [runtimeScheduler = runtimeScheduler_.get()]( diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js index 407d2d6b1b1d77..2288e9e215de64 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js @@ -78,6 +78,11 @@ const definitions: FeatureFlagDefinitions = { description: 'When enabled, the renderer would only fail commits when they propagate state and the last commit that updated state changed before committing.', }, + enableLongTaskAPI: { + defaultValue: false, + description: + 'Enables the reporting of long tasks through `PerformanceObserver`. Only works if the event loop is enabled.', + }, enableMicrotasks: { defaultValue: false, description: diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index 72b4dfedcd0910..04d890884d7ebd 100644 --- a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<412289b5c998b3ae03d8b842704523ba>> + * @generated SignedSource<<7421319910b210753a1eddcf079aa151>> * @flow strict-local */ @@ -52,6 +52,7 @@ export type ReactNativeFeatureFlags = { enableAlignItemsBaselineOnFabricIOS: Getter, enableCleanTextInputYogaNode: Getter, enableGranularShadowTreeStateReconciliation: Getter, + enableLongTaskAPI: Getter, enableMicrotasks: Getter, enablePropsUpdateReconciliationAndroid: Getter, enableSynchronousStateUpdates: Getter, @@ -167,6 +168,10 @@ export const enableCleanTextInputYogaNode: Getter = createNativeFlagGet * When enabled, the renderer would only fail commits when they propagate state and the last commit that updated state changed before committing. */ export const enableGranularShadowTreeStateReconciliation: Getter = createNativeFlagGetter('enableGranularShadowTreeStateReconciliation', false); +/** + * Enables the reporting of long tasks through `PerformanceObserver`. Only works if the event loop is enabled. + */ +export const enableLongTaskAPI: Getter = createNativeFlagGetter('enableLongTaskAPI', false); /** * Enables the use of microtasks in Hermes (scheduling) and RuntimeScheduler (execution). */ diff --git a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js index 54c1b38566354a..cf0e63335d396e 100644 --- a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<19718453dfde482545a2313ca74a96ce>> + * @generated SignedSource<<29f4ec3c941708dc99dd38adac52d6dc>> * @flow strict-local */ @@ -32,6 +32,7 @@ export interface Spec extends TurboModule { +enableAlignItemsBaselineOnFabricIOS?: () => boolean; +enableCleanTextInputYogaNode?: () => boolean; +enableGranularShadowTreeStateReconciliation?: () => boolean; + +enableLongTaskAPI?: () => boolean; +enableMicrotasks?: () => boolean; +enablePropsUpdateReconciliationAndroid?: () => boolean; +enableSynchronousStateUpdates?: () => boolean; diff --git a/packages/react-native/src/private/webapis/performance/PerformanceEntry.js b/packages/react-native/src/private/webapis/performance/PerformanceEntry.js index 9c855422af86fc..62c0b0226650d7 100644 --- a/packages/react-native/src/private/webapis/performance/PerformanceEntry.js +++ b/packages/react-native/src/private/webapis/performance/PerformanceEntry.js @@ -9,7 +9,7 @@ */ export type HighResTimeStamp = number; -export type PerformanceEntryType = 'mark' | 'measure' | 'event'; +export type PerformanceEntryType = 'mark' | 'measure' | 'event' | 'longtask'; export type PerformanceEntryJSON = { name: string, diff --git a/packages/react-native/src/private/webapis/performance/RawPerformanceEntry.js b/packages/react-native/src/private/webapis/performance/RawPerformanceEntry.js index c840ec77eeff35..861a0fd6580a18 100644 --- a/packages/react-native/src/private/webapis/performance/RawPerformanceEntry.js +++ b/packages/react-native/src/private/webapis/performance/RawPerformanceEntry.js @@ -21,6 +21,7 @@ export const RawPerformanceEntryTypeValues = { MARK: 1, MEASURE: 2, EVENT: 3, + LONGTASK: 4, }; export function rawToPerformanceEntry( @@ -55,6 +56,8 @@ export function rawToPerformanceEntryType( return 'measure'; case RawPerformanceEntryTypeValues.EVENT: return 'event'; + case RawPerformanceEntryTypeValues.LONGTASK: + return 'longtask'; default: throw new TypeError( `rawToPerformanceEntryType: unexpected performance entry type received: ${type}`, @@ -72,6 +75,8 @@ export function performanceEntryTypeToRaw( return RawPerformanceEntryTypeValues.MEASURE; case 'event': return RawPerformanceEntryTypeValues.EVENT; + case 'longtask': + return RawPerformanceEntryTypeValues.LONGTASK; default: // Verify exhaustive check with Flow (type: empty); diff --git a/packages/rn-tester/js/examples/Performance/PerformanceApiExample.js b/packages/rn-tester/js/examples/Performance/PerformanceApiExample.js index 609d721b2b21d4..da5a0dae16e248 100644 --- a/packages/rn-tester/js/examples/Performance/PerformanceApiExample.js +++ b/packages/rn-tester/js/examples/Performance/PerformanceApiExample.js @@ -9,6 +9,7 @@ * @oncall react_native */ +import type {RNTesterModuleExample} from '../../types/RNTesterTypes'; import type MemoryInfo from 'react-native/src/private/webapis/performance/MemoryInfo'; import type ReactNativeStartupTiming from 'react-native/src/private/webapis/performance/ReactNativeStartupTiming'; @@ -203,6 +204,41 @@ function PerformanceObserverEventTimingExample(): React.Node { ); } +function PerformanceObserverLongtaskExample(): React.Node { + const theme = useContext(RNTesterThemeContext); + + const [entries, setEntries] = useState<$ReadOnlyArray>([]); + + useEffect(() => { + const observer = new PerformanceObserver(list => { + setEntries(list.getEntries()); + }); + + observer.observe({entryTypes: ['longtask']}); + + return () => observer.disconnect(); + }, []); + + const onPress = useCallback(() => { + // Wait 1s to force a long task + busyWait(1000); + }, []); + + return ( + +