Skip to content

Commit 64b60c2

Browse files
javachemeta-codesync[bot]
authored andcommitted
Tidy the bridgeless runtime executor plumbing
Summary: Follow-ups to the buffered CallInvoker, kept out of that diff so the behavioural change stays reviewable on its own. No behaviour change intended here. - `BufferedRuntimeExecutor` gains `asRuntimeExecutor()` and `asWeakRuntimeExecutor()`. RuntimeExecutor is a std::function, so callers cannot hand it the object and instead each hand-rolled the same capturing lambda; the ownership question now has one answer per lifetime policy. The weak form is what `getBufferedRuntimeExecutor` needs, which must not keep the instance alive. - `BufferedWork` becomes private. It was only ever an implementation detail, and being public put its fields in the C++ API snapshot. - The buffer holds a `std::vector` sorted once at flush rather than a `std::priority_queue`. Indices are handed out before the lock, so arrival order can differ from submission order and something has to restore it — but a heap is the expensive way. `std::priority_queue::top()` returns a const reference, so every flushed item copied its `std::function` on the way out; that copy is now a move, and nothing sifts on the way in. - `runtimeExecutorThatGoesThroughRuntimeScheduler` was a second copy of `getUnbufferedRuntimeExecutor()`; it now calls it. - `RuntimeSchedulerCallInvoker` is marked deprecated. Its remaining users are migrated: `ReactCxxPlatform` holds a `ReactInstance` and can use `createJSCallInvoker()`, and four other files only `#include`d it without ever constructing one. The single remaining use is the flag-off branch, which is suppressed locally and goes when the flag is cleaned up. - Drops a null check on `bufferedRuntimeExecutor_` in `callFunctionOnModule` that the constructor makes unreachable. Changelog: [Internal] Differential Revision: D118616045
1 parent c746d4c commit 64b60c2

12 files changed

Lines changed: 96 additions & 135 deletions

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

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,16 @@
77

88
#include "BufferedRuntimeExecutor.h"
99

10+
#include <algorithm>
11+
#include <utility>
12+
1013
namespace facebook::react {
1114

1215
BufferedRuntimeExecutor::BufferedRuntimeExecutor(Executor executor)
1316
: executor_(std::move(executor)),
1417
isBufferingEnabled_(true),
1518
lastIndex_(0) {}
1619

17-
void BufferedRuntimeExecutor::execute(Work&& callback) {
18-
execute(SchedulerPriority::ImmediatePriority, std::move(callback));
19-
}
20-
2120
void BufferedRuntimeExecutor::execute(
2221
SchedulerPriority priority,
2322
Work&& callback) {
@@ -35,7 +34,7 @@ void BufferedRuntimeExecutor::execute(
3534
uint64_t newIndex = lastIndex_++;
3635
std::scoped_lock guard(lock_);
3736
if (isBufferingEnabled_) {
38-
queue_.push(
37+
queue_.push_back(
3938
{.index_ = newIndex,
4039
.work_ = std::move(callback),
4140
.priority_ = priority});
@@ -48,18 +47,41 @@ void BufferedRuntimeExecutor::execute(
4847
executor_(priority, std::move(callback));
4948
}
5049

50+
RuntimeExecutor BufferedRuntimeExecutor::asRuntimeExecutor() {
51+
return [self = shared_from_this()](Work&& callback) {
52+
self->execute(SchedulerPriority::ImmediatePriority, std::move(callback));
53+
};
54+
}
55+
56+
RuntimeExecutor BufferedRuntimeExecutor::asWeakRuntimeExecutor() {
57+
return [weakSelf = weak_from_this()](Work&& callback) {
58+
if (auto self = weakSelf.lock()) {
59+
self->execute(SchedulerPriority::ImmediatePriority, std::move(callback));
60+
}
61+
};
62+
}
63+
5164
void BufferedRuntimeExecutor::flush() {
5265
std::scoped_lock guard(lock_);
5366
unsafeFlush();
5467
isBufferingEnabled_ = false;
5568
}
5669

5770
void BufferedRuntimeExecutor::unsafeFlush() {
58-
while (!queue_.empty()) {
59-
const BufferedWork& bufferedWork = queue_.top();
60-
Work work = bufferedWork.work_;
61-
executor_(bufferedWork.priority_, std::move(work));
62-
queue_.pop();
71+
// Indices are handed out before the lock is taken, so arrival order can
72+
// differ from submission order. Sorting once here restores it, and costs less
73+
// than a heap did: nothing sifts on the way in, and each callback is moved
74+
// out rather than copied.
75+
auto batch = std::move(queue_);
76+
queue_.clear();
77+
std::sort(
78+
batch.begin(),
79+
batch.end(),
80+
[](const BufferedWork& lhs, const BufferedWork& rhs) {
81+
return lhs.index_ < rhs.index_;
82+
});
83+
for (auto& bufferedWork : batch) {
84+
executor_(bufferedWork.priority_, std::move(bufferedWork.work_));
6385
}
6486
}
6587

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

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -11,39 +11,25 @@
1111
#include <ReactCommon/SchedulerPriority.h>
1212
#include <jsi/jsi.h>
1313
#include <atomic>
14+
#include <memory>
1415
#include <mutex>
15-
#include <queue>
16+
#include <vector>
1617

1718
namespace facebook::react {
1819

19-
class BufferedRuntimeExecutor {
20+
class BufferedRuntimeExecutor : public std::enable_shared_from_this<BufferedRuntimeExecutor> {
2021
public:
2122
using Work = std::function<void(jsi::Runtime &runtime)>;
2223

23-
/**
24-
* Drains one piece of buffered work. Always given a priority; an executor
25-
* that sits below the RuntimeScheduler, and so has no notion of one, ignores
26-
* it.
27-
*/
24+
// Drains one piece of buffered work, priority may be ignored
2825
using Executor = std::function<void(SchedulerPriority, Work &&)>;
2926

30-
// A utility structure to track pending work in the order of when they arrive.
31-
struct BufferedWork {
32-
uint64_t index_;
33-
Work work_;
34-
SchedulerPriority priority_;
35-
bool operator<(const BufferedWork &rhs) const
36-
{
37-
// Higher index has lower priority, so this inverted comparison puts
38-
// the smaller index on top of the queue.
39-
return index_ > rhs.index_;
40-
}
41-
};
42-
4327
BufferedRuntimeExecutor(Executor executor);
4428

45-
/** Equivalent to `execute(SchedulerPriority::ImmediatePriority, ...)`. */
46-
void execute(Work &&callback);
29+
void execute(Work &&callback)
30+
{
31+
execute(SchedulerPriority::ImmediatePriority, std::move(callback));
32+
}
4733

4834
/**
4935
* Buffers [callback] alongside work submitted through the other overload,
@@ -52,18 +38,37 @@ class BufferedRuntimeExecutor {
5238
*/
5339
void execute(SchedulerPriority priority, Work &&callback);
5440

41+
/**
42+
* RuntimeExecutor, keeping this class alive for as long as the result is
43+
* held.
44+
*/
45+
RuntimeExecutor asRuntimeExecutor();
46+
47+
/**
48+
* RuntimeExecutor, keeping a weak reference to this class, so it does not
49+
* keep the runtime alive unnecessarily.
50+
*/
51+
RuntimeExecutor asWeakRuntimeExecutor();
52+
5553
// Flush buffered JS calls and then diable JS buffering
5654
void flush();
5755

5856
private:
5957
// Perform flushing without locking mechanism
6058
void unsafeFlush();
6159

60+
// A utility structure to track pending work in the order of when they arrive.
61+
struct BufferedWork {
62+
uint64_t index_;
63+
Work work_;
64+
SchedulerPriority priority_;
65+
};
66+
6267
Executor executor_;
6368
std::atomic<bool> isBufferingEnabled_;
6469
std::mutex lock_;
6570
std::atomic<uint64_t> lastIndex_;
66-
std::priority_queue<BufferedWork> queue_;
71+
std::vector<BufferedWork> queue_;
6772
};
6873

6974
} // namespace facebook::react

packages/react-native/ReactCommon/react/runtime/ReactInstance.cpp

Lines changed: 9 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -124,32 +124,18 @@ ReactInstance::ReactInstance(
124124
std::function<void(jsi::Runtime & runtime)>&& callback) {
125125
runtimeExecutor(std::move(callback));
126126
});
127-
auto runtimeExecutorThatExecutesAfterInspectorSetup =
128-
[bufferedRuntimeExecutorThatWaitsForInspectorSetup](
129-
std::function<void(jsi::Runtime & runtime)>&& callback) {
130-
bufferedRuntimeExecutorThatWaitsForInspectorSetup->execute(
131-
std::move(callback));
132-
};
133127

134128
runtimeScheduler_ = createRuntimeScheduler(
135-
runtimeExecutorThatExecutesAfterInspectorSetup,
129+
bufferedRuntimeExecutorThatWaitsForInspectorSetup->asRuntimeExecutor(),
136130
[jsErrorHandler = jsErrorHandler_](
137131
jsi::Runtime& runtime, jsi::JSError& error) {
138132
jsErrorHandler->handleError(runtime, error, true);
139133
});
140134

141-
auto runtimeExecutorThatGoesThroughRuntimeScheduler =
142-
[runtimeScheduler = runtimeScheduler_.get()](
143-
std::function<void(jsi::Runtime & runtime)>&& callback) {
144-
runtimeScheduler->scheduleWork(std::move(callback));
145-
};
146-
147135
// This code can execute from any thread, so we need to make sure we set up
148136
// the inspector logic in the right one. The callback executes immediately
149137
// if we are already in the right thread.
150-
executor([this,
151-
runtimeExecutorThatGoesThroughRuntimeScheduler,
152-
bufferedRuntimeExecutorThatWaitsForInspectorSetup](
138+
executor([this, bufferedRuntimeExecutorThatWaitsForInspectorSetup](
153139
jsinspector_modern::HostTarget& hostTarget) {
154140
// Callbacks scheduled through the page target executor are generally
155141
// not guaranteed to run (e.g.: if the page target is destroyed)
@@ -160,8 +146,7 @@ ReactInstance::ReactInstance(
160146
// creation task to finish before starting the destruction.
161147
inspectorTarget_ = &hostTarget.registerInstance(*this);
162148
runtimeInspectorTarget_ = &inspectorTarget_->registerRuntime(
163-
runtime_->getRuntimeTargetDelegate(),
164-
runtimeExecutorThatGoesThroughRuntimeScheduler);
149+
runtime_->getRuntimeTargetDelegate(), getUnbufferedRuntimeExecutor());
165150
bufferedRuntimeExecutorThatWaitsForInspectorSetup->flush();
166151
});
167152
} else {
@@ -178,9 +163,9 @@ ReactInstance::ReactInstance(
178163
setHermesEventLoopControl(runtime, runtimeScheduler);
179164
});
180165

181-
// `scheduleWork` is `scheduleTask(ImmediatePriority)` on the modern
182-
// scheduler, which is the only one bridgeless uses, so routing everything
183-
// through `scheduleTask` leaves unprioritised callers where they were.
166+
// Note that bufferedRuntimeExecutor_ only has a raw pointer to
167+
// RuntimeScheduler It should always be retained weakly, as it should be
168+
// destroyed when the runtime is.
184169
bufferedRuntimeExecutor_ = std::make_shared<BufferedRuntimeExecutor>(
185170
[runtimeScheduler = runtimeScheduler_.get()](
186171
SchedulerPriority priority,
@@ -214,7 +199,8 @@ void ReactInstance::unregisterFromInspector() {
214199
RuntimeExecutor ReactInstance::getUnbufferedRuntimeExecutor() noexcept {
215200
return [runtimeScheduler = runtimeScheduler_.get()](
216201
std::function<void(jsi::Runtime & runtime)>&& callback) {
217-
runtimeScheduler->scheduleWork(std::move(callback));
202+
runtimeScheduler->scheduleTask(
203+
SchedulerPriority::ImmediatePriority, std::move(callback));
218204
};
219205
}
220206

@@ -223,14 +209,7 @@ RuntimeExecutor ReactInstance::getUnbufferedRuntimeExecutor() noexcept {
223209
// getUnbufferedRuntimeExecutor() instead if you do not need the main JS
224210
// bundle to have finished. e.g. setting global variables into JS runtime.
225211
RuntimeExecutor ReactInstance::getBufferedRuntimeExecutor() noexcept {
226-
return [weakBufferedRuntimeExecutor_ =
227-
std::weak_ptr<BufferedRuntimeExecutor>(bufferedRuntimeExecutor_)](
228-
std::function<void(jsi::Runtime & runtime)>&& callback) {
229-
if (auto strongBufferedRuntimeExecutor_ =
230-
weakBufferedRuntimeExecutor_.lock()) {
231-
strongBufferedRuntimeExecutor_->execute(std::move(callback));
232-
}
233-
};
212+
return bufferedRuntimeExecutor_->asWeakRuntimeExecutor();
234213
}
235214

236215
// TODO(T184010230): Should the RuntimeScheduler returned from this method be

scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1901,22 +1901,17 @@ class facebook::react::BridgelessNativeMethodCallInvoker : public facebook::reac
19011901
public virtual void invokeSync(const std::string& methodName, facebook::react::NativeMethodCallFunc&& func) override;
19021902
}
19031903

1904-
class facebook::react::BufferedRuntimeExecutor {
1904+
class facebook::react::BufferedRuntimeExecutor : public std::enable_shared_from_this<facebook::react::BufferedRuntimeExecutor> {
19051905
public BufferedRuntimeExecutor(facebook::react::BufferedRuntimeExecutor::Executor executor);
1906+
public facebook::react::RuntimeExecutor asRuntimeExecutor();
1907+
public facebook::react::RuntimeExecutor asWeakRuntimeExecutor();
19061908
public using Executor = std::function<void(facebook::react::SchedulerPriority, facebook::react::BufferedRuntimeExecutor::Work&&)>;
19071909
public using Work = std::function<void(facebook::jsi::Runtime& runtime)>;
19081910
public void execute(facebook::react::BufferedRuntimeExecutor::Work&& callback);
19091911
public void execute(facebook::react::SchedulerPriority priority, facebook::react::BufferedRuntimeExecutor::Work&& callback);
19101912
public void flush();
19111913
}
19121914

1913-
struct facebook::react::BufferedRuntimeExecutor::BufferedWork {
1914-
public bool operator<(const facebook::react::BufferedRuntimeExecutor::BufferedWork& rhs) const;
1915-
public facebook::react::BufferedRuntimeExecutor::Work work_;
1916-
public facebook::react::SchedulerPriority priority_;
1917-
public uint64_t index_;
1918-
}
1919-
19201915
class facebook::react::CSSSyntaxParser {
19211916
public constexpr CSSSyntaxParser(const facebook::react::CSSSyntaxParser&) = default;
19221917
public constexpr CSSSyntaxParser(facebook::react::CSSSyntaxParser&&) = default;

scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1895,22 +1895,17 @@ class facebook::react::BridgelessNativeMethodCallInvoker : public facebook::reac
18951895
public virtual void invokeSync(const std::string& methodName, facebook::react::NativeMethodCallFunc&& func) override;
18961896
}
18971897

1898-
class facebook::react::BufferedRuntimeExecutor {
1898+
class facebook::react::BufferedRuntimeExecutor : public std::enable_shared_from_this<facebook::react::BufferedRuntimeExecutor> {
18991899
public BufferedRuntimeExecutor(facebook::react::BufferedRuntimeExecutor::Executor executor);
1900+
public facebook::react::RuntimeExecutor asRuntimeExecutor();
1901+
public facebook::react::RuntimeExecutor asWeakRuntimeExecutor();
19001902
public using Executor = std::function<void(facebook::react::SchedulerPriority, facebook::react::BufferedRuntimeExecutor::Work&&)>;
19011903
public using Work = std::function<void(facebook::jsi::Runtime& runtime)>;
19021904
public void execute(facebook::react::BufferedRuntimeExecutor::Work&& callback);
19031905
public void execute(facebook::react::SchedulerPriority priority, facebook::react::BufferedRuntimeExecutor::Work&& callback);
19041906
public void flush();
19051907
}
19061908

1907-
struct facebook::react::BufferedRuntimeExecutor::BufferedWork {
1908-
public bool operator<(const facebook::react::BufferedRuntimeExecutor::BufferedWork& rhs) const;
1909-
public facebook::react::BufferedRuntimeExecutor::Work work_;
1910-
public facebook::react::SchedulerPriority priority_;
1911-
public uint64_t index_;
1912-
}
1913-
19141909
class facebook::react::CSSSyntaxParser {
19151910
public constexpr CSSSyntaxParser(const facebook::react::CSSSyntaxParser&) = default;
19161911
public constexpr CSSSyntaxParser(facebook::react::CSSSyntaxParser&&) = default;

scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1899,22 +1899,17 @@ class facebook::react::BridgelessNativeMethodCallInvoker : public facebook::reac
18991899
public virtual void invokeSync(const std::string& methodName, facebook::react::NativeMethodCallFunc&& func) override;
19001900
}
19011901

1902-
class facebook::react::BufferedRuntimeExecutor {
1902+
class facebook::react::BufferedRuntimeExecutor : public std::enable_shared_from_this<facebook::react::BufferedRuntimeExecutor> {
19031903
public BufferedRuntimeExecutor(facebook::react::BufferedRuntimeExecutor::Executor executor);
1904+
public facebook::react::RuntimeExecutor asRuntimeExecutor();
1905+
public facebook::react::RuntimeExecutor asWeakRuntimeExecutor();
19041906
public using Executor = std::function<void(facebook::react::SchedulerPriority, facebook::react::BufferedRuntimeExecutor::Work&&)>;
19051907
public using Work = std::function<void(facebook::jsi::Runtime& runtime)>;
19061908
public void execute(facebook::react::BufferedRuntimeExecutor::Work&& callback);
19071909
public void execute(facebook::react::SchedulerPriority priority, facebook::react::BufferedRuntimeExecutor::Work&& callback);
19081910
public void flush();
19091911
}
19101912

1911-
struct facebook::react::BufferedRuntimeExecutor::BufferedWork {
1912-
public bool operator<(const facebook::react::BufferedRuntimeExecutor::BufferedWork& rhs) const;
1913-
public facebook::react::BufferedRuntimeExecutor::Work work_;
1914-
public facebook::react::SchedulerPriority priority_;
1915-
public uint64_t index_;
1916-
}
1917-
19181913
class facebook::react::CSSSyntaxParser {
19191914
public constexpr CSSSyntaxParser(const facebook::react::CSSSyntaxParser&) = default;
19201915
public constexpr CSSSyntaxParser(facebook::react::CSSSyntaxParser&&) = default;

scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4484,22 +4484,17 @@ class facebook::react::BridgelessNativeMethodCallInvoker : public facebook::reac
44844484
public virtual void invokeSync(const std::string& methodName, facebook::react::NativeMethodCallFunc&& func) override;
44854485
}
44864486

4487-
class facebook::react::BufferedRuntimeExecutor {
4487+
class facebook::react::BufferedRuntimeExecutor : public std::enable_shared_from_this<facebook::react::BufferedRuntimeExecutor> {
44884488
public BufferedRuntimeExecutor(facebook::react::BufferedRuntimeExecutor::Executor executor);
4489+
public facebook::react::RuntimeExecutor asRuntimeExecutor();
4490+
public facebook::react::RuntimeExecutor asWeakRuntimeExecutor();
44894491
public using Executor = std::function<void(facebook::react::SchedulerPriority, facebook::react::BufferedRuntimeExecutor::Work&&)>;
44904492
public using Work = std::function<void(facebook::jsi::Runtime& runtime)>;
44914493
public void execute(facebook::react::BufferedRuntimeExecutor::Work&& callback);
44924494
public void execute(facebook::react::SchedulerPriority priority, facebook::react::BufferedRuntimeExecutor::Work&& callback);
44934495
public void flush();
44944496
}
44954497

4496-
struct facebook::react::BufferedRuntimeExecutor::BufferedWork {
4497-
public bool operator<(const facebook::react::BufferedRuntimeExecutor::BufferedWork& rhs) const;
4498-
public facebook::react::BufferedRuntimeExecutor::Work work_;
4499-
public facebook::react::SchedulerPriority priority_;
4500-
public uint64_t index_;
4501-
}
4502-
45034498
class facebook::react::CSSSyntaxParser {
45044499
public constexpr CSSSyntaxParser(const facebook::react::CSSSyntaxParser&) = default;
45054500
public constexpr CSSSyntaxParser(facebook::react::CSSSyntaxParser&&) = default;

scripts/cxx-api/api-snapshots/ReactAppleNewarchCxx.api

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4471,22 +4471,17 @@ class facebook::react::BridgelessNativeMethodCallInvoker : public facebook::reac
44714471
public virtual void invokeSync(const std::string& methodName, facebook::react::NativeMethodCallFunc&& func) override;
44724472
}
44734473

4474-
class facebook::react::BufferedRuntimeExecutor {
4474+
class facebook::react::BufferedRuntimeExecutor : public std::enable_shared_from_this<facebook::react::BufferedRuntimeExecutor> {
44754475
public BufferedRuntimeExecutor(facebook::react::BufferedRuntimeExecutor::Executor executor);
4476+
public facebook::react::RuntimeExecutor asRuntimeExecutor();
4477+
public facebook::react::RuntimeExecutor asWeakRuntimeExecutor();
44764478
public using Executor = std::function<void(facebook::react::SchedulerPriority, facebook::react::BufferedRuntimeExecutor::Work&&)>;
44774479
public using Work = std::function<void(facebook::jsi::Runtime& runtime)>;
44784480
public void execute(facebook::react::BufferedRuntimeExecutor::Work&& callback);
44794481
public void execute(facebook::react::SchedulerPriority priority, facebook::react::BufferedRuntimeExecutor::Work&& callback);
44804482
public void flush();
44814483
}
44824484

4483-
struct facebook::react::BufferedRuntimeExecutor::BufferedWork {
4484-
public bool operator<(const facebook::react::BufferedRuntimeExecutor::BufferedWork& rhs) const;
4485-
public facebook::react::BufferedRuntimeExecutor::Work work_;
4486-
public facebook::react::SchedulerPriority priority_;
4487-
public uint64_t index_;
4488-
}
4489-
44904485
class facebook::react::CSSSyntaxParser {
44914486
public constexpr CSSSyntaxParser(const facebook::react::CSSSyntaxParser&) = default;
44924487
public constexpr CSSSyntaxParser(facebook::react::CSSSyntaxParser&&) = default;

0 commit comments

Comments
 (0)