Skip to content

Commit 1ec1b5d

Browse files
RSNarafacebook-github-bot
authored andcommitted
RuntimeExecutor: Fork an ios implementation
Summary: We want the ios implementation of this api, to be implemented differently. Because, we need to build in deadlock mitigation for sync renders and events, for ios only. Changelog: [Internal] Differential Revision: D74901907
1 parent eee143d commit 1ec1b5d

File tree

10 files changed

+227
-74
lines changed

10 files changed

+227
-74
lines changed

packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Legacy.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "RuntimeScheduler_Legacy.h"
99
#include "SchedulerPriorityUtils.h"
1010

11+
#include <ReactCommon/RuntimeExecutorSyncUIThreadUtils.h>
1112
#include <cxxreact/TraceSection.h>
1213
#include <react/renderer/consistency/ScopedShadowTreeRevisionLock.h>
1314
#include <utility>

packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Modern.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "RuntimeScheduler_Modern.h"
99
#include "SchedulerPriorityUtils.h"
1010

11+
#include <ReactCommon/RuntimeExecutorSyncUIThreadUtils.h>
1112
#include <cxxreact/TraceSection.h>
1213
#include <jsinspector-modern/tracing/EventLoopReporter.h>
1314
#include <react/featureflags/ReactNativeFeatureFlags.h>

packages/react-native/ReactCommon/react/runtime/BufferedRuntimeExecutor.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <ReactCommon/RuntimeExecutor.h>
1111
#include <jsi/jsi.h>
1212
#include <atomic>
13+
#include <mutex>
1314
#include <queue>
1415

1516
namespace facebook::react {

packages/react-native/ReactCommon/runtimeexecutor/CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ set(CMAKE_VERBOSE_MAKEFILE on)
88

99
include(${REACT_COMMON_DIR}/cmake-utils/react-native-flags.cmake)
1010

11-
file(GLOB_RECURSE runtimeexecutor_SRC CONFIGURE_DEPENDS *.cpp *.h)
11+
file(GLOB_RECURSE runtimeexecutor_SRC CONFIGURE_DEPENDS *.cpp *.h ${CMAKE_CURRENT_SOURCE_DIR}/platform/android/*.cpp)
1212

1313
add_library(runtimeexecutor OBJECT ${runtimeexecutor_SRC})
1414

15-
target_include_directories(runtimeexecutor PUBLIC .)
15+
target_include_directories(runtimeexecutor PUBLIC . ${CMAKE_CURRENT_SOURCE_DIR}/platform/android/)
1616

1717
target_link_libraries(runtimeexecutor jsi)
1818
target_compile_reactnative_options(runtimeexecutor PRIVATE)

packages/react-native/ReactCommon/runtimeexecutor/React-runtimeexecutor.podspec

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ else
1717
end
1818

1919
Pod::Spec.new do |s|
20+
source_files = "*.{m,mm,cpp,h}", "platform/ios/**/*.{m,mm,cpp,h}"
21+
2022
s.name = "React-runtimeexecutor"
2123
s.version = version
2224
s.summary = "-" # TODO
@@ -25,8 +27,14 @@ Pod::Spec.new do |s|
2527
s.author = "Meta Platforms, Inc. and its affiliates"
2628
s.platforms = min_supported_versions
2729
s.source = source
28-
s.source_files = "**/*.{cpp,h}"
30+
s.source_files = source_files
2931
s.header_dir = "ReactCommon"
3032

33+
if ENV['USE_FRAMEWORKS']
34+
s.module_name = "React-runtimeexecutor"
35+
s.header_mappings_dir = "."
36+
header_search_paths = header_search_paths + ["\"$(PODS_TARGET_SRCROOT)/platform/ios\""]
37+
end
38+
3139
s.dependency "React-jsi", version
3240
end

packages/react-native/ReactCommon/runtimeexecutor/ReactCommon/RuntimeExecutor.h

Lines changed: 0 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,6 @@
77

88
#pragma once
99

10-
#include <mutex>
11-
#include <thread>
12-
1310
#include <jsi/jsi.h>
1411

1512
namespace facebook::react {
@@ -25,72 +22,4 @@ namespace facebook::react {
2522
using RuntimeExecutor =
2623
std::function<void(std::function<void(jsi::Runtime& runtime)>&& callback)>;
2724

28-
/**
29-
* Example order of events (when not a sync call in runtimeExecutor
30-
* jsWork):
31-
* - [UI thread] Lock all mutexes at start
32-
* - [UI thread] Schedule "runtime capture block" on js thread
33-
* - [UI thread] Wait for runtime capture: runtimeCaptured.lock()
34-
* - [JS thread] Capture runtime by setting runtimePtr
35-
* - [JS thread] Signal runtime captured: runtimeCaptured.unlock()
36-
* - [UI thread] Call jsWork using runtimePtr
37-
* - [JS thread] Wait until jsWork done: jsWorkDone.lock()
38-
* - [UI thread] Signal jsWork done: jsWorkDone.unlock()
39-
* - [UI thread] Wait until runtime capture block finished:
40-
* runtimeCaptureBlockDone.lock()
41-
* - [JS thread] Signal runtime capture block is finished:
42-
* runtimeCaptureBlockDone.unlock()
43-
*/
44-
inline static void executeSynchronouslyOnSameThread_CAN_DEADLOCK(
45-
const RuntimeExecutor& runtimeExecutor,
46-
std::function<void(jsi::Runtime& runtime)>&& jsWork) noexcept {
47-
// Note: We need the third mutex to get back to the main thread before
48-
// the lambda is finished (because all mutexes are allocated on the stack).
49-
50-
std::mutex runtimeCaptured;
51-
std::mutex jsWorkDone;
52-
std::mutex runtimeCaptureBlockDone;
53-
54-
runtimeCaptured.lock();
55-
jsWorkDone.lock();
56-
runtimeCaptureBlockDone.lock();
57-
58-
jsi::Runtime* runtimePtr;
59-
60-
auto threadId = std::this_thread::get_id();
61-
auto runtimeCaptureBlock = [&](jsi::Runtime& runtime) {
62-
runtimePtr = &runtime;
63-
64-
if (threadId == std::this_thread::get_id()) {
65-
// In case of a synchronous call, we should unlock mutexes and return.
66-
runtimeCaptured.unlock();
67-
runtimeCaptureBlockDone.unlock();
68-
return;
69-
}
70-
71-
runtimeCaptured.unlock();
72-
// `jsWork` is called somewhere here.
73-
jsWorkDone.lock();
74-
runtimeCaptureBlockDone.unlock();
75-
};
76-
runtimeExecutor(std::move(runtimeCaptureBlock));
77-
78-
runtimeCaptured.lock();
79-
jsWork(*runtimePtr);
80-
jsWorkDone.unlock();
81-
runtimeCaptureBlockDone.lock();
82-
}
83-
84-
template <typename DataT>
85-
inline static DataT executeSynchronouslyOnSameThread_CAN_DEADLOCK(
86-
const RuntimeExecutor& runtimeExecutor,
87-
std::function<DataT(jsi::Runtime& runtime)>&& callback) noexcept {
88-
DataT data;
89-
90-
executeSynchronouslyOnSameThread_CAN_DEADLOCK(
91-
runtimeExecutor,
92-
[&](jsi::Runtime& runtime) { data = callback(runtime); });
93-
94-
return data;
95-
}
9625
} // namespace facebook::react
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#include <ReactCommon/RuntimeExecutorSyncUIThreadUtils.h>
9+
#include <mutex>
10+
#include <thread>
11+
12+
namespace facebook::react {
13+
/**
14+
* Example order of events (when not a sync call in runtimeExecutor
15+
* jsWork):
16+
* - [UI thread] Lock all mutexes at start
17+
* - [UI thread] Schedule "runtime capture block" on js thread
18+
* - [UI thread] Wait for runtime capture: runtimeCaptured.lock()
19+
* - [JS thread] Capture runtime by setting runtimePtr
20+
* - [JS thread] Signal runtime captured: runtimeCaptured.unlock()
21+
* - [UI thread] Call jsWork using runtimePtr
22+
* - [JS thread] Wait until jsWork done: jsWorkDone.lock()
23+
* - [UI thread] Signal jsWork done: jsWorkDone.unlock()
24+
* - [UI thread] Wait until runtime capture block finished:
25+
* runtimeCaptureBlockDone.lock()
26+
* - [JS thread] Signal runtime capture block is finished:
27+
* runtimeCaptureBlockDone.unlock()
28+
*/
29+
void executeSynchronouslyOnSameThread_CAN_DEADLOCK(
30+
const RuntimeExecutor& runtimeExecutor,
31+
std::function<void(jsi::Runtime& runtime)>&& jsWork) noexcept {
32+
// Note: We need the third mutex to get back to the main thread before
33+
// the lambda is finished (because all mutexes are allocated on the stack).
34+
35+
std::mutex runtimeCaptured;
36+
std::mutex jsWorkDone;
37+
std::mutex runtimeCaptureBlockDone;
38+
39+
runtimeCaptured.lock();
40+
jsWorkDone.lock();
41+
runtimeCaptureBlockDone.lock();
42+
43+
jsi::Runtime* runtimePtr;
44+
45+
auto threadId = std::this_thread::get_id();
46+
auto runtimeCaptureBlock = [&](jsi::Runtime& runtime) {
47+
runtimePtr = &runtime;
48+
49+
if (threadId == std::this_thread::get_id()) {
50+
// In case of a synchronous call, we should unlock mutexes and return.
51+
runtimeCaptured.unlock();
52+
runtimeCaptureBlockDone.unlock();
53+
return;
54+
}
55+
56+
runtimeCaptured.unlock();
57+
// `jsWork` is called somewhere here.
58+
jsWorkDone.lock();
59+
runtimeCaptureBlockDone.unlock();
60+
};
61+
runtimeExecutor(std::move(runtimeCaptureBlock));
62+
63+
runtimeCaptured.lock();
64+
jsWork(*runtimePtr);
65+
jsWorkDone.unlock();
66+
runtimeCaptureBlockDone.lock();
67+
}
68+
69+
} // namespace facebook::react
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#include <ReactCommon/RuntimeExecutor.h>
9+
10+
#include <jsi/jsi.h>
11+
12+
namespace facebook::react {
13+
14+
/*
15+
* Executes a `callback` in a *synchronous* manner on the same thread using
16+
* given `RuntimeExecutor`.
17+
* Use this method when the caller needs to *be blocked* by executing the
18+
* `callback` and requires that the callback will be executed on the same
19+
* thread.
20+
*/
21+
void executeSynchronouslyOnSameThread_CAN_DEADLOCK(
22+
const RuntimeExecutor& runtimeExecutor,
23+
std::function<void(jsi::Runtime& runtime)>&& callback) noexcept;
24+
25+
template <typename DataT>
26+
DataT executeSynchronouslyOnSameThread_CAN_DEADLOCK(
27+
const RuntimeExecutor& runtimeExecutor,
28+
std::function<DataT(jsi::Runtime& runtime)>&& callback) noexcept {
29+
DataT data;
30+
31+
executeSynchronouslyOnSameThread_CAN_DEADLOCK(
32+
runtimeExecutor,
33+
[&](jsi::Runtime& runtime) { data = callback(runtime); });
34+
35+
return data;
36+
}
37+
} // namespace facebook::react
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#include <ReactCommon/RuntimeExecutor.h>
9+
10+
#include <jsi/jsi.h>
11+
12+
namespace facebook::react {
13+
14+
/*
15+
* Executes a `callback` in a *synchronous* manner on the same thread using
16+
* given `RuntimeExecutor`.
17+
* Use this method when the caller needs to *be blocked* by executing the
18+
* `callback` and requires that the callback will be executed on the same
19+
* thread.
20+
*/
21+
void executeSynchronouslyOnSameThread_CAN_DEADLOCK(
22+
const RuntimeExecutor& runtimeExecutor,
23+
std::function<void(jsi::Runtime& runtime)>&& callback) noexcept;
24+
25+
template <typename DataT>
26+
DataT executeSynchronouslyOnSameThread_CAN_DEADLOCK(
27+
const RuntimeExecutor& runtimeExecutor,
28+
std::function<DataT(jsi::Runtime& runtime)>&& callback) noexcept {
29+
DataT data;
30+
31+
executeSynchronouslyOnSameThread_CAN_DEADLOCK(
32+
runtimeExecutor,
33+
[&](jsi::Runtime& runtime) { data = callback(runtime); });
34+
35+
return data;
36+
}
37+
} // namespace facebook::react
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#import <ReactCommon/RuntimeExecutorSyncUIThreadUtils.h>
9+
#include <mutex>
10+
#include <thread>
11+
12+
namespace facebook::react {
13+
/**
14+
* Example order of events (when not a sync call in runtimeExecutor
15+
* jsWork):
16+
* - [UI thread] Lock all mutexes at start
17+
* - [UI thread] Schedule "runtime capture block" on js thread
18+
* - [UI thread] Wait for runtime capture: runtimeCaptured.lock()
19+
* - [JS thread] Capture runtime by setting runtimePtr
20+
* - [JS thread] Signal runtime captured: runtimeCaptured.unlock()
21+
* - [UI thread] Call jsWork using runtimePtr
22+
* - [JS thread] Wait until jsWork done: jsWorkDone.lock()
23+
* - [UI thread] Signal jsWork done: jsWorkDone.unlock()
24+
* - [UI thread] Wait until runtime capture block finished:
25+
* runtimeCaptureBlockDone.lock()
26+
* - [JS thread] Signal runtime capture block is finished:
27+
* runtimeCaptureBlockDone.unlock()
28+
*/
29+
void executeSynchronouslyOnSameThread_CAN_DEADLOCK(
30+
const RuntimeExecutor &runtimeExecutor,
31+
std::function<void(jsi::Runtime &runtime)> &&jsWork) noexcept
32+
{
33+
// Note: We need the third mutex to get back to the main thread before
34+
// the lambda is finished (because all mutexes are allocated on the stack).
35+
36+
std::mutex runtimeCaptured;
37+
std::mutex jsWorkDone;
38+
std::mutex runtimeCaptureBlockDone;
39+
40+
runtimeCaptured.lock();
41+
jsWorkDone.lock();
42+
runtimeCaptureBlockDone.lock();
43+
44+
jsi::Runtime *runtimePtr;
45+
46+
auto threadId = std::this_thread::get_id();
47+
auto runtimeCaptureBlock = [&](jsi::Runtime &runtime) {
48+
runtimePtr = &runtime;
49+
50+
if (threadId == std::this_thread::get_id()) {
51+
// In case of a synchronous call, we should unlock mutexes and return.
52+
runtimeCaptured.unlock();
53+
runtimeCaptureBlockDone.unlock();
54+
return;
55+
}
56+
57+
runtimeCaptured.unlock();
58+
// `jsWork` is called somewhere here.
59+
jsWorkDone.lock();
60+
runtimeCaptureBlockDone.unlock();
61+
};
62+
runtimeExecutor(std::move(runtimeCaptureBlock));
63+
64+
runtimeCaptured.lock();
65+
jsWork(*runtimePtr);
66+
jsWorkDone.unlock();
67+
runtimeCaptureBlockDone.lock();
68+
}
69+
70+
} // namespace facebook::react

0 commit comments

Comments
 (0)