From bcd0bb6120a4e33d3e49fb136f123d6b97042488 Mon Sep 17 00:00:00 2001 From: Ramanpreet Nara Date: Tue, 27 May 2025 22:25:50 -0700 Subject: [PATCH] RuntimeExecutor: Create an ios fork of sync ui thread utils (#51432) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/51432 RuntimeExecutor.h has sync ui thread utils: * executeSynchronouslyOnSameThread_CAN_DEADLOCK The ios platform has js -> ui sync calls. This util, when it executes concurrently with those sync calls, deadlocks react native. On ios, we're going to resolve these deadlocks, which'll involve customizing this util: D74769326. Therefore, this diff forks an implementation of these sync ui thread utils for ios. Changelog: [Internal] Reviewed By: mdvacca Differential Revision: D74901907 --- .../AppDelegate/React-RCTAppDelegate.podspec | 1 + packages/react-native/React-Core.podspec | 1 + .../CoreModules/React-CoreModules.podspec | 2 + .../React/React-RCTFabric.podspec | 1 + .../React/Runtime/React-RCTRuntime.podspec | 1 + .../ReactCommon/React-Fabric.podspec | 1 + .../cxxreact/React-cxxreact.podspec | 2 +- .../ReactCommon/hermes/React-hermes.podspec | 2 +- .../jsiexecutor/React-jsiexecutor.podspec | 1 + .../React-jsinspector.podspec | 2 +- .../jsitooling/React-jsitooling.podspec | 1 + .../ios/React-NativeModulesApple.podspec | 2 +- .../dom/React-domnativemodule.podspec | 1 + .../React-idlecallbacksnativemodule.podspec | 1 + .../React-runtimescheduler.podspec | 2 +- .../RuntimeScheduler_Legacy.cpp | 1 + .../RuntimeScheduler_Modern.cpp | 1 + .../tests/RuntimeSchedulerTest.cpp | 1 + .../react/runtime/BufferedRuntimeExecutor.h | 1 + .../react/runtime/React-RuntimeCore.podspec | 2 +- .../react/runtime/React-RuntimeHermes.podspec | 1 + .../platform/ios/React-RuntimeApple.podspec | 2 +- .../runtimeexecutor/CMakeLists.txt | 4 +- .../React-runtimeexecutor.podspec | 17 ++++- .../ReactCommon/RuntimeExecutor.h | 62 ------------------- .../RuntimeExecutorSyncUIThreadUtils.cpp | 61 ++++++++++++++++++ .../RuntimeExecutorSyncUIThreadUtils.h | 36 +++++++++++ .../RuntimeExecutorSyncUIThreadUtils.h | 36 +++++++++++ .../RuntimeExecutorSyncUIThreadUtils.mm | 61 ++++++++++++++++++ 29 files changed, 235 insertions(+), 72 deletions(-) create mode 100644 packages/react-native/ReactCommon/runtimeexecutor/platform/cxx/ReactCommon/RuntimeExecutorSyncUIThreadUtils.cpp create mode 100644 packages/react-native/ReactCommon/runtimeexecutor/platform/cxx/ReactCommon/RuntimeExecutorSyncUIThreadUtils.h create mode 100644 packages/react-native/ReactCommon/runtimeexecutor/platform/ios/ReactCommon/RuntimeExecutorSyncUIThreadUtils.h create mode 100644 packages/react-native/ReactCommon/runtimeexecutor/platform/ios/ReactCommon/RuntimeExecutorSyncUIThreadUtils.mm diff --git a/packages/react-native/Libraries/AppDelegate/React-RCTAppDelegate.podspec b/packages/react-native/Libraries/AppDelegate/React-RCTAppDelegate.podspec index 1eadb664ccd0..09823fba146f 100644 --- a/packages/react-native/Libraries/AppDelegate/React-RCTAppDelegate.podspec +++ b/packages/react-native/Libraries/AppDelegate/React-RCTAppDelegate.podspec @@ -68,6 +68,7 @@ Pod::Spec.new do |s| s.dependency "React-defaultsnativemodule" s.dependency 'React-hermes' + add_dependency(s, "React-runtimeexecutor", :additional_framework_paths => ["platform/ios"]) add_dependency(s, "ReactCommon", :subspec => "turbomodule/core", :additional_framework_paths => ["react/nativemodule/core"]) add_dependency(s, "React-NativeModulesApple") add_dependency(s, "React-runtimescheduler") diff --git a/packages/react-native/React-Core.podspec b/packages/react-native/React-Core.podspec index b6eb3b761020..c57a3361ad7e 100644 --- a/packages/react-native/React-Core.podspec +++ b/packages/react-native/React-Core.podspec @@ -126,6 +126,7 @@ Pod::Spec.new do |s| s.resource_bundles = {'React-Core_privacy' => 'React/Resources/PrivacyInfo.xcprivacy'} + add_dependency(s, "React-runtimeexecutor", :additional_framework_paths => ["platform/ios"]) add_dependency(s, "React-jsinspector", :framework_name => 'jsinspector_modern') add_dependency(s, "React-jsinspectorcdp", :framework_name => 'jsinspector_moderncdp') add_dependency(s, "React-jsitooling", :framework_name => "JSITooling") diff --git a/packages/react-native/React/CoreModules/React-CoreModules.podspec b/packages/react-native/React/CoreModules/React-CoreModules.podspec index 07eb23ce91b6..f0d0fcd4ba29 100644 --- a/packages/react-native/React/CoreModules/React-CoreModules.podspec +++ b/packages/react-native/React/CoreModules/React-CoreModules.podspec @@ -52,6 +52,8 @@ Pod::Spec.new do |s| s.dependency "React-RCTImage", version s.dependency "React-jsi", version s.dependency 'React-RCTBlob' + + add_dependency(s, "React-runtimeexecutor", :additional_framework_paths => ["platform/ios"]) add_dependency(s, "React-jsinspector", :framework_name => 'jsinspector_modern') add_dependency(s, "React-jsinspectorcdp", :framework_name => 'jsinspector_moderncdp') add_dependency(s, "React-jsinspectortracing", :framework_name => 'jsinspector_moderntracing') diff --git a/packages/react-native/React/React-RCTFabric.podspec b/packages/react-native/React/React-RCTFabric.podspec index 819f83eb5177..989460cd8067 100644 --- a/packages/react-native/React/React-RCTFabric.podspec +++ b/packages/react-native/React/React-RCTFabric.podspec @@ -85,6 +85,7 @@ Pod::Spec.new do |s| add_dependency(s, "React-performancetimeline") add_dependency(s, "React-rendererdebug") add_dependency(s, "React-rendererconsistency") + add_dependency(s, "React-runtimeexecutor", :additional_framework_paths => ["platform/ios"]) add_dependency(s, "React-runtimescheduler") add_dependency(s, "React-RCTAnimation", :framework_name => 'RCTAnimation') add_dependency(s, "React-jsinspector", :framework_name => 'jsinspector_modern') diff --git a/packages/react-native/React/Runtime/React-RCTRuntime.podspec b/packages/react-native/React/Runtime/React-RCTRuntime.podspec index eba281d1a640..8a496d5beb39 100644 --- a/packages/react-native/React/Runtime/React-RCTRuntime.podspec +++ b/packages/react-native/React/Runtime/React-RCTRuntime.podspec @@ -49,6 +49,7 @@ Pod::Spec.new do |s| s.dependency "React-Core" s.dependency "React-jsi" + add_dependency(s, "React-runtimeexecutor", :additional_framework_paths => ["platform/ios"]) add_dependency(s, "React-jsitooling", :framework_name => "JSITooling") add_dependency(s, "React-jsinspector", :framework_name => 'jsinspector_modern') add_dependency(s, "React-jsinspectorcdp", :framework_name => 'jsinspector_moderncdp') diff --git a/packages/react-native/ReactCommon/React-Fabric.podspec b/packages/react-native/ReactCommon/React-Fabric.podspec index 3b78049e2596..8af4c7e3f4ff 100644 --- a/packages/react-native/ReactCommon/React-Fabric.podspec +++ b/packages/react-native/ReactCommon/React-Fabric.podspec @@ -49,6 +49,7 @@ Pod::Spec.new do |s| s.dependency "React-runtimescheduler" s.dependency "React-cxxreact" + add_dependency(s, "React-runtimeexecutor", :additional_framework_paths => ["platform/ios"]) add_dependency(s, "React-rendererdebug") add_dependency(s, "React-graphics", :additional_framework_paths => ["react/renderer/graphics/platform/ios"]) add_dependency(s, "React-utils", :additional_framework_paths => ["react/utils/platform/ios"]) diff --git a/packages/react-native/ReactCommon/cxxreact/React-cxxreact.podspec b/packages/react-native/ReactCommon/cxxreact/React-cxxreact.podspec index e34e6e0d948f..d47eeada0504 100644 --- a/packages/react-native/ReactCommon/cxxreact/React-cxxreact.podspec +++ b/packages/react-native/ReactCommon/cxxreact/React-cxxreact.podspec @@ -39,7 +39,7 @@ Pod::Spec.new do |s| add_dependency(s, "React-jsinspectorcdp", :framework_name => 'jsinspector_moderncdp') add_dependency(s, "React-jsinspectortracing", :framework_name => 'jsinspector_moderntracing') s.dependency "React-callinvoker", version - s.dependency "React-runtimeexecutor", version + add_dependency(s, "React-runtimeexecutor", :additional_framework_paths => ["platform/ios"]) s.dependency "React-perflogger", version s.dependency "React-jsi", version s.dependency "React-logger", version diff --git a/packages/react-native/ReactCommon/hermes/React-hermes.podspec b/packages/react-native/ReactCommon/hermes/React-hermes.podspec index 1592899c190d..186177f6ebb6 100644 --- a/packages/react-native/ReactCommon/hermes/React-hermes.podspec +++ b/packages/react-native/ReactCommon/hermes/React-hermes.podspec @@ -43,7 +43,7 @@ Pod::Spec.new do |s| s.dependency "React-perflogger", version s.dependency "hermes-engine" s.dependency "React-jsi" - s.dependency "React-runtimeexecutor" + add_dependency(s, "React-runtimeexecutor", :additional_framework_paths => ["platform/ios"]) add_rn_third_party_dependencies(s) end diff --git a/packages/react-native/ReactCommon/jsiexecutor/React-jsiexecutor.podspec b/packages/react-native/ReactCommon/jsiexecutor/React-jsiexecutor.podspec index 7b70f4e2188a..ebeddf24a236 100644 --- a/packages/react-native/ReactCommon/jsiexecutor/React-jsiexecutor.podspec +++ b/packages/react-native/ReactCommon/jsiexecutor/React-jsiexecutor.podspec @@ -32,6 +32,7 @@ Pod::Spec.new do |s| s.dependency "React-cxxreact", version s.dependency "React-jsi", version s.dependency "React-perflogger", version + add_dependency(s, "React-runtimeexecutor", :additional_framework_paths => ["platform/ios"]) add_dependency(s, "React-jsinspector", :framework_name => 'jsinspector_modern') add_dependency(s, "React-jsinspectorcdp", :framework_name => 'jsinspector_moderncdp') add_dependency(s, "React-jsinspectortracing", :framework_name => 'jsinspector_moderntracing') diff --git a/packages/react-native/ReactCommon/jsinspector-modern/React-jsinspector.podspec b/packages/react-native/ReactCommon/jsinspector-modern/React-jsinspector.podspec index 6a666a7afa8b..a1f1766cc836 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/React-jsinspector.podspec +++ b/packages/react-native/ReactCommon/jsinspector-modern/React-jsinspector.podspec @@ -49,7 +49,7 @@ Pod::Spec.new do |s| end s.dependency "React-featureflags" - s.dependency "React-runtimeexecutor", version + add_dependency(s, "React-runtimeexecutor", :additional_framework_paths => ["platform/ios"]) s.dependency "React-jsi" add_dependency(s, "React-jsinspectorcdp", :framework_name => 'jsinspector_moderncdp') add_dependency(s, "React-jsinspectornetwork", :framework_name => 'jsinspector_modernnetwork') diff --git a/packages/react-native/ReactCommon/jsitooling/React-jsitooling.podspec b/packages/react-native/ReactCommon/jsitooling/React-jsitooling.podspec index ae1a79c38d0c..35b278483257 100644 --- a/packages/react-native/ReactCommon/jsitooling/React-jsitooling.podspec +++ b/packages/react-native/ReactCommon/jsitooling/React-jsitooling.podspec @@ -40,6 +40,7 @@ Pod::Spec.new do |s| s.dependency "React-cxxreact", version s.dependency "React-jsi", version + add_dependency(s, "React-runtimeexecutor", :additional_framework_paths => ["platform/ios"]) add_dependency(s, "React-jsinspector", :framework_name => 'jsinspector_modern') add_dependency(s, "React-jsinspectorcdp", :framework_name => 'jsinspector_moderncdp') add_dependency(s, "React-jsinspectortracing", :framework_name => 'jsinspector_moderntracing') diff --git a/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/React-NativeModulesApple.podspec b/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/React-NativeModulesApple.podspec index 530383c5a735..caab2aa4508f 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/React-NativeModulesApple.podspec +++ b/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/React-NativeModulesApple.podspec @@ -44,7 +44,7 @@ Pod::Spec.new do |s| s.dependency "React-cxxreact" s.dependency "React-jsi" s.dependency "React-featureflags" - s.dependency "React-runtimeexecutor" + add_dependency(s, "React-runtimeexecutor", :additional_framework_paths => ["platform/ios"]) add_dependency(s, "React-featureflags") add_dependency(s, "React-jsinspector", :framework_name => 'jsinspector_modern') add_dependency(s, "React-jsinspectorcdp", :framework_name => 'jsinspector_moderncdp') diff --git a/packages/react-native/ReactCommon/react/nativemodule/dom/React-domnativemodule.podspec b/packages/react-native/ReactCommon/react/nativemodule/dom/React-domnativemodule.podspec index 01492676e1e1..07fa49320f1e 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/dom/React-domnativemodule.podspec +++ b/packages/react-native/ReactCommon/react/nativemodule/dom/React-domnativemodule.podspec @@ -55,6 +55,7 @@ Pod::Spec.new do |s| s.dependency "ReactCommon/turbomodule/core" s.dependency "React-Fabric" s.dependency "React-FabricComponents" + add_dependency(s, "React-runtimeexecutor", :additional_framework_paths => ["platform/ios"]) add_dependency(s, "React-graphics", :additional_framework_paths => ["react/renderer/graphics/platform/ios"]) add_dependency(s, "React-RCTFBReactNativeSpec") end diff --git a/packages/react-native/ReactCommon/react/nativemodule/idlecallbacks/React-idlecallbacksnativemodule.podspec b/packages/react-native/ReactCommon/react/nativemodule/idlecallbacks/React-idlecallbacksnativemodule.podspec index 7038acefb5ae..d1f48e7dd550 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/idlecallbacks/React-idlecallbacksnativemodule.podspec +++ b/packages/react-native/ReactCommon/react/nativemodule/idlecallbacks/React-idlecallbacksnativemodule.podspec @@ -52,5 +52,6 @@ Pod::Spec.new do |s| s.dependency "ReactCommon/turbomodule/core" s.dependency "React-runtimescheduler" add_dependency(s, "React-RCTFBReactNativeSpec") + add_dependency(s, "React-runtimeexecutor", :additional_framework_paths => ["platform/ios"]) end 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 0ec33c274799..e2ba39977360 100644 --- a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/React-runtimescheduler.podspec +++ b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/React-runtimescheduler.podspec @@ -43,7 +43,7 @@ Pod::Spec.new do |s| s.header_mappings_dir = "../../.." end - s.dependency "React-runtimeexecutor" + add_dependency(s, "React-runtimeexecutor", :additional_framework_paths => ["platform/ios"]) s.dependency "React-callinvoker" s.dependency "React-cxxreact" s.dependency "React-rendererdebug" 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 f9f2f2232363..e8d9b4359cea 100644 --- a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Legacy.cpp +++ b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Legacy.cpp @@ -8,6 +8,7 @@ #include "RuntimeScheduler_Legacy.h" #include "SchedulerPriorityUtils.h" +#include #include #include #include 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 fcdb9565edb6..aa29da73fd8b 100644 --- a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Modern.cpp +++ b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Modern.cpp @@ -8,6 +8,7 @@ #include "RuntimeScheduler_Modern.h" #include "SchedulerPriorityUtils.h" +#include #include #include #include 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 6251b5a552b6..aa2340f98fb5 100644 --- a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/tests/RuntimeSchedulerTest.cpp +++ b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/tests/RuntimeSchedulerTest.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include "StubClock.h" diff --git a/packages/react-native/ReactCommon/react/runtime/BufferedRuntimeExecutor.h b/packages/react-native/ReactCommon/react/runtime/BufferedRuntimeExecutor.h index c13e7aa459ea..6606e492ec5f 100644 --- a/packages/react-native/ReactCommon/react/runtime/BufferedRuntimeExecutor.h +++ b/packages/react-native/ReactCommon/react/runtime/BufferedRuntimeExecutor.h @@ -10,6 +10,7 @@ #include #include #include +#include #include namespace facebook::react { diff --git a/packages/react-native/ReactCommon/react/runtime/React-RuntimeCore.podspec b/packages/react-native/ReactCommon/react/runtime/React-RuntimeCore.podspec index ed2e7a1d1bb1..53cc77932892 100644 --- a/packages/react-native/ReactCommon/react/runtime/React-RuntimeCore.podspec +++ b/packages/react-native/ReactCommon/react/runtime/React-RuntimeCore.podspec @@ -40,7 +40,7 @@ Pod::Spec.new do |s| s.dependency "React-jsiexecutor" s.dependency "React-cxxreact" - s.dependency "React-runtimeexecutor" + add_dependency(s, "React-runtimeexecutor", :additional_framework_paths => ["platform/ios"]) s.dependency "React-jsi" s.dependency "React-jserrorhandler" s.dependency "React-performancetimeline" diff --git a/packages/react-native/ReactCommon/react/runtime/React-RuntimeHermes.podspec b/packages/react-native/ReactCommon/react/runtime/React-RuntimeHermes.podspec index 7f08453b71bb..4f32e5c979e0 100644 --- a/packages/react-native/ReactCommon/react/runtime/React-RuntimeHermes.podspec +++ b/packages/react-native/ReactCommon/react/runtime/React-RuntimeHermes.podspec @@ -48,6 +48,7 @@ Pod::Spec.new do |s| s.dependency "React-hermes" s.dependency "hermes-engine" + add_dependency(s, "React-runtimeexecutor", :additional_framework_paths => ["platform/ios"]) add_dependency(s, "React-jsitooling", :framework_name => "JSITooling") add_rn_third_party_dependencies(s) diff --git a/packages/react-native/ReactCommon/react/runtime/platform/ios/React-RuntimeApple.podspec b/packages/react-native/ReactCommon/react/runtime/platform/ios/React-RuntimeApple.podspec index 2d0daa00f635..ff17cec5b9e3 100644 --- a/packages/react-native/ReactCommon/react/runtime/platform/ios/React-RuntimeApple.podspec +++ b/packages/react-native/ReactCommon/react/runtime/platform/ios/React-RuntimeApple.podspec @@ -46,7 +46,7 @@ Pod::Spec.new do |s| s.dependency "React-jsiexecutor" s.dependency "React-cxxreact" s.dependency "React-callinvoker" - s.dependency "React-runtimeexecutor" + add_dependency(s, "React-runtimeexecutor", :additional_framework_paths => ["platform/ios"]) s.dependency "React-runtimescheduler" s.dependency "React-jsi" s.dependency "React-Core/Default" diff --git a/packages/react-native/ReactCommon/runtimeexecutor/CMakeLists.txt b/packages/react-native/ReactCommon/runtimeexecutor/CMakeLists.txt index ca883784338d..6a1a14d29924 100644 --- a/packages/react-native/ReactCommon/runtimeexecutor/CMakeLists.txt +++ b/packages/react-native/ReactCommon/runtimeexecutor/CMakeLists.txt @@ -8,11 +8,11 @@ set(CMAKE_VERBOSE_MAKEFILE on) include(${REACT_COMMON_DIR}/cmake-utils/react-native-flags.cmake) -file(GLOB_RECURSE runtimeexecutor_SRC CONFIGURE_DEPENDS *.cpp *.h) +file(GLOB_RECURSE runtimeexecutor_SRC CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/ReactCommon/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/platform/cxx/*.cpp) add_library(runtimeexecutor OBJECT ${runtimeexecutor_SRC}) -target_include_directories(runtimeexecutor PUBLIC .) +target_include_directories(runtimeexecutor PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/platform/cxx) target_link_libraries(runtimeexecutor jsi) target_compile_reactnative_options(runtimeexecutor PRIVATE) diff --git a/packages/react-native/ReactCommon/runtimeexecutor/React-runtimeexecutor.podspec b/packages/react-native/ReactCommon/runtimeexecutor/React-runtimeexecutor.podspec index c6fb40df8de7..2cf65a5db3a1 100644 --- a/packages/react-native/ReactCommon/runtimeexecutor/React-runtimeexecutor.podspec +++ b/packages/react-native/ReactCommon/runtimeexecutor/React-runtimeexecutor.podspec @@ -17,7 +17,12 @@ else end Pod::Spec.new do |s| + header_search_paths = [ + "\"$(PODS_TARGET_SRCROOT)\"", + ] + s.name = "React-runtimeexecutor" + s.module_name = "React_runtimeexecutor" s.version = version s.summary = "-" # TODO s.homepage = "https://reactnative.dev/" @@ -25,8 +30,18 @@ Pod::Spec.new do |s| s.author = "Meta Platforms, Inc. and its affiliates" s.platforms = min_supported_versions s.source = source - s.source_files = "**/*.{cpp,h}" + s.source_files = "ReactCommon/*.{m,mm,cpp,h}", "platform/ios/**/*.{m,mm,cpp,h}" s.header_dir = "ReactCommon" + if ENV['USE_FRAMEWORKS'] + s.header_mappings_dir = '.' + header_search_paths = header_search_paths + ["\"$(PODS_TARGET_SRCROOT)/platform/ios\""] + end + + s.pod_target_xcconfig = { "USE_HEADERMAP" => "NO", + "CLANG_CXX_LANGUAGE_STANDARD" => rct_cxx_language_standard(), + "HEADER_SEARCH_PATHS" => header_search_paths.join(' '), + "DEFINES_MODULE" => "YES" } + s.dependency "React-jsi", version end diff --git a/packages/react-native/ReactCommon/runtimeexecutor/ReactCommon/RuntimeExecutor.h b/packages/react-native/ReactCommon/runtimeexecutor/ReactCommon/RuntimeExecutor.h index 41ec520599f8..f8f69b54343e 100644 --- a/packages/react-native/ReactCommon/runtimeexecutor/ReactCommon/RuntimeExecutor.h +++ b/packages/react-native/ReactCommon/runtimeexecutor/ReactCommon/RuntimeExecutor.h @@ -7,9 +7,6 @@ #pragma once -#include -#include - #include namespace facebook::react { @@ -25,63 +22,4 @@ namespace facebook::react { using RuntimeExecutor = std::function&& callback)>; -/* - * Schedules `runtimeWork` to be executed on the same thread using the - * `RuntimeExecutor`, and blocks on its completion. - * - * Example: - * - [UI thread] Schedule `runtimeCaptureBlock` on js thread - * - [UI thread] Wait for runtime capture: await(runtime) - * - [JS thread] Capture runtime for ui thread: resolve(runtime, &rt); - * - [JS thread] Wait until runtimeWork done: await(runtimeWorkDone) - * - [UI thread] Call runtimeWork: runtimeWork(*runtimePrt); - * - [UI thread] Signal runtimeWork done: resolve(runtimeWorkDone) - * - [UI thread] Wait until runtime capture block finished: - * await(runtimeCaptureBlockDone); - * - [JS thread] Signal runtime capture block is finished: - * resolve(runtimeCaptureBlockDone); - */ -inline static void executeSynchronouslyOnSameThread_CAN_DEADLOCK( - const RuntimeExecutor& runtimeExecutor, - std::function&& runtimeWork) { - std::promise runtime; - std::promise runtimeCaptureBlockDone; - std::promise runtimeWorkDone; - - auto callingThread = std::this_thread::get_id(); - - auto runtimeCaptureBlock = [&](jsi::Runtime& rt) { - runtime.set_value(&rt); - - auto runtimeThread = std::this_thread::get_id(); - if (callingThread != runtimeThread) { - // Block `runtimeThread` on execution of `runtimeWork` on `callingThread`. - runtimeWorkDone.get_future().wait(); - } - - // TODO(T225331233): This is likely unnecessary. Remove it. - runtimeCaptureBlockDone.set_value(); - }; - runtimeExecutor(std::move(runtimeCaptureBlock)); - - jsi::Runtime* runtimePtr = runtime.get_future().get(); - runtimeWork(*runtimePtr); - runtimeWorkDone.set_value(); - - // TODO(T225331233): This is likely unnecessary. Remove it. - runtimeCaptureBlockDone.get_future().wait(); -} - -template -inline static DataT executeSynchronouslyOnSameThread_CAN_DEADLOCK( - const RuntimeExecutor& runtimeExecutor, - std::function&& runtimeWork) { - DataT data; - - executeSynchronouslyOnSameThread_CAN_DEADLOCK( - runtimeExecutor, - [&](jsi::Runtime& runtime) { data = runtimeWork(runtime); }); - - return data; -} } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/runtimeexecutor/platform/cxx/ReactCommon/RuntimeExecutorSyncUIThreadUtils.cpp b/packages/react-native/ReactCommon/runtimeexecutor/platform/cxx/ReactCommon/RuntimeExecutorSyncUIThreadUtils.cpp new file mode 100644 index 000000000000..d7a83751972f --- /dev/null +++ b/packages/react-native/ReactCommon/runtimeexecutor/platform/cxx/ReactCommon/RuntimeExecutorSyncUIThreadUtils.cpp @@ -0,0 +1,61 @@ +/* + * 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 +#include + +namespace facebook::react { + +/* + * Schedules `runtimeWork` to be executed on the same thread using the + * `RuntimeExecutor`, and blocks on its completion. + * + * Example: + * - [UI thread] Schedule `runtimeCaptureBlock` on js thread + * - [UI thread] Wait for runtime capture: await(runtime) + * - [JS thread] Capture runtime for ui thread: resolve(runtime, &rt); + * - [JS thread] Wait until runtimeWork done: await(runtimeWorkDone) + * - [UI thread] Call runtimeWork: runtimeWork(*runtimePrt); + * - [UI thread] Signal runtimeWork done: resolve(runtimeWorkDone) + * - [UI thread] Wait until runtime capture block finished: + * await(runtimeCaptureBlockDone); + * - [JS thread] Signal runtime capture block is finished: + * resolve(runtimeCaptureBlockDone); + */ +void executeSynchronouslyOnSameThread_CAN_DEADLOCK( + const RuntimeExecutor& runtimeExecutor, + std::function&& runtimeWork) { + std::promise runtime; + std::promise runtimeCaptureBlockDone; + std::promise runtimeWorkDone; + + auto callingThread = std::this_thread::get_id(); + + auto runtimeCaptureBlock = [&](jsi::Runtime& rt) { + runtime.set_value(&rt); + + auto runtimeThread = std::this_thread::get_id(); + if (callingThread != runtimeThread) { + // Block `runtimeThread` on execution of `runtimeWork` on `callingThread`. + runtimeWorkDone.get_future().wait(); + } + + // TODO(T225331233): This is likely unnecessary. Remove it. + runtimeCaptureBlockDone.set_value(); + }; + runtimeExecutor(std::move(runtimeCaptureBlock)); + + jsi::Runtime* runtimePtr = runtime.get_future().get(); + runtimeWork(*runtimePtr); + runtimeWorkDone.set_value(); + + // TODO(T225331233): This is likely unnecessary. Remove it. + runtimeCaptureBlockDone.get_future().wait(); +} + +} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/runtimeexecutor/platform/cxx/ReactCommon/RuntimeExecutorSyncUIThreadUtils.h b/packages/react-native/ReactCommon/runtimeexecutor/platform/cxx/ReactCommon/RuntimeExecutorSyncUIThreadUtils.h new file mode 100644 index 000000000000..fff9664f59fa --- /dev/null +++ b/packages/react-native/ReactCommon/runtimeexecutor/platform/cxx/ReactCommon/RuntimeExecutorSyncUIThreadUtils.h @@ -0,0 +1,36 @@ +/* + * 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 + +#include + +namespace facebook::react { + +/* + * Schedules `runtimeWork` to be executed on the same thread using the + * `RuntimeExecutor`, and blocks on its completion. + */ +void executeSynchronouslyOnSameThread_CAN_DEADLOCK( + const RuntimeExecutor& runtimeExecutor, + std::function&& runtimeWork); + +template +inline static DataT executeSynchronouslyOnSameThread_CAN_DEADLOCK( + const RuntimeExecutor& runtimeExecutor, + std::function&& runtimeWork) { + DataT data; + + executeSynchronouslyOnSameThread_CAN_DEADLOCK( + runtimeExecutor, + [&](jsi::Runtime& runtime) { data = runtimeWork(runtime); }); + + return data; +} +} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/runtimeexecutor/platform/ios/ReactCommon/RuntimeExecutorSyncUIThreadUtils.h b/packages/react-native/ReactCommon/runtimeexecutor/platform/ios/ReactCommon/RuntimeExecutorSyncUIThreadUtils.h new file mode 100644 index 000000000000..fff9664f59fa --- /dev/null +++ b/packages/react-native/ReactCommon/runtimeexecutor/platform/ios/ReactCommon/RuntimeExecutorSyncUIThreadUtils.h @@ -0,0 +1,36 @@ +/* + * 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 + +#include + +namespace facebook::react { + +/* + * Schedules `runtimeWork` to be executed on the same thread using the + * `RuntimeExecutor`, and blocks on its completion. + */ +void executeSynchronouslyOnSameThread_CAN_DEADLOCK( + const RuntimeExecutor& runtimeExecutor, + std::function&& runtimeWork); + +template +inline static DataT executeSynchronouslyOnSameThread_CAN_DEADLOCK( + const RuntimeExecutor& runtimeExecutor, + std::function&& runtimeWork) { + DataT data; + + executeSynchronouslyOnSameThread_CAN_DEADLOCK( + runtimeExecutor, + [&](jsi::Runtime& runtime) { data = runtimeWork(runtime); }); + + return data; +} +} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/runtimeexecutor/platform/ios/ReactCommon/RuntimeExecutorSyncUIThreadUtils.mm b/packages/react-native/ReactCommon/runtimeexecutor/platform/ios/ReactCommon/RuntimeExecutorSyncUIThreadUtils.mm new file mode 100644 index 000000000000..c2484f255952 --- /dev/null +++ b/packages/react-native/ReactCommon/runtimeexecutor/platform/ios/ReactCommon/RuntimeExecutorSyncUIThreadUtils.mm @@ -0,0 +1,61 @@ +/* + * 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 +#include +#include + +namespace facebook::react { +/* + * Schedules `runtimeWork` to be executed on the same thread using the + * `RuntimeExecutor`, and blocks on its completion. + * + * Example: + * - [UI thread] Schedule `runtimeCaptureBlock` on js thread + * - [UI thread] Wait for runtime capture: await(runtime) + * - [JS thread] Capture runtime for ui thread: resolve(runtime, &rt); + * - [JS thread] Wait until runtimeWork done: await(runtimeWorkDone) + * - [UI thread] Call runtimeWork: runtimeWork(*runtimePrt); + * - [UI thread] Signal runtimeWork done: resolve(runtimeWorkDone) + * - [UI thread] Wait until runtime capture block finished: + * await(runtimeCaptureBlockDone); + * - [JS thread] Signal runtime capture block is finished: + * resolve(runtimeCaptureBlockDone); + */ +void executeSynchronouslyOnSameThread_CAN_DEADLOCK( + const RuntimeExecutor &runtimeExecutor, + std::function &&runtimeWork) +{ + std::promise runtime; + std::promise runtimeCaptureBlockDone; + std::promise runtimeWorkDone; + + auto callingThread = std::this_thread::get_id(); + + auto runtimeCaptureBlock = [&](jsi::Runtime &rt) { + runtime.set_value(&rt); + + auto runtimeThread = std::this_thread::get_id(); + if (callingThread != runtimeThread) { + // Block `runtimeThread` on execution of `runtimeWork` on `callingThread`. + runtimeWorkDone.get_future().wait(); + } + + // TODO(T225331233): This is likely unnecessary. Remove it. + runtimeCaptureBlockDone.set_value(); + }; + runtimeExecutor(std::move(runtimeCaptureBlock)); + + jsi::Runtime *runtimePtr = runtime.get_future().get(); + runtimeWork(*runtimePtr); + runtimeWorkDone.set_value(); + + // TODO(T225331233): This is likely unnecessary. Remove it. + runtimeCaptureBlockDone.get_future().wait(); +} + +} // namespace facebook::react