Skip to content

Commit 0e9020f

Browse files
rubennortefacebook-github-bot
authored andcommitted
Create proxy for RuntimeScheduler to allow us to use a forked version
Differential Revision: D49316880 fbshipit-source-id: e987a274358387659ba686d91c7d98735061e3fe
1 parent 968fb18 commit 0e9020f

File tree

5 files changed

+408
-196
lines changed

5 files changed

+408
-196
lines changed

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

Lines changed: 14 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*/
77

88
#include "RuntimeScheduler.h"
9+
#include "RuntimeScheduler_Legacy.h"
910
#include "SchedulerPriorityUtils.h"
1011

1112
#include <react/renderer/debug/SystraceSection.h>
@@ -14,177 +15,55 @@
1415

1516
namespace facebook::react {
1617

17-
#pragma mark - Public
18-
1918
RuntimeScheduler::RuntimeScheduler(
2019
RuntimeExecutor runtimeExecutor,
2120
std::function<RuntimeSchedulerTimePoint()> now)
22-
: runtimeExecutor_(std::move(runtimeExecutor)), now_(std::move(now)) {}
21+
: runtimeSchedulerImpl_(std::make_unique<RuntimeScheduler_Legacy>(
22+
std::move(runtimeExecutor),
23+
std::move(now))) {}
2324

2425
void RuntimeScheduler::scheduleWork(RawCallback&& callback) const noexcept {
25-
SystraceSection s("RuntimeScheduler::scheduleWork");
26-
27-
runtimeAccessRequests_ += 1;
28-
29-
runtimeExecutor_(
30-
[this, callback = std::move(callback)](jsi::Runtime& runtime) {
31-
SystraceSection s2("RuntimeScheduler::scheduleWork callback");
32-
runtimeAccessRequests_ -= 1;
33-
callback(runtime);
34-
startWorkLoop(runtime);
35-
});
26+
return runtimeSchedulerImpl_->scheduleWork(std::move(callback));
3627
}
3728

3829
std::shared_ptr<Task> RuntimeScheduler::scheduleTask(
3930
SchedulerPriority priority,
4031
jsi::Function&& callback) noexcept {
41-
auto expirationTime = now_() + timeoutForSchedulerPriority(priority);
42-
auto task =
43-
std::make_shared<Task>(priority, std::move(callback), expirationTime);
44-
taskQueue_.push(task);
45-
46-
scheduleWorkLoopIfNecessary();
47-
48-
return task;
32+
return runtimeSchedulerImpl_->scheduleTask(priority, std::move(callback));
4933
}
5034

5135
std::shared_ptr<Task> RuntimeScheduler::scheduleTask(
5236
SchedulerPriority priority,
5337
RawCallback&& callback) noexcept {
54-
auto expirationTime = now_() + timeoutForSchedulerPriority(priority);
55-
auto task =
56-
std::make_shared<Task>(priority, std::move(callback), expirationTime);
57-
taskQueue_.push(task);
58-
59-
scheduleWorkLoopIfNecessary();
60-
61-
return task;
38+
return runtimeSchedulerImpl_->scheduleTask(priority, std::move(callback));
6239
}
6340

6441
bool RuntimeScheduler::getShouldYield() const noexcept {
65-
return runtimeAccessRequests_ > 0;
42+
return runtimeSchedulerImpl_->getShouldYield();
6643
}
6744

6845
bool RuntimeScheduler::getIsSynchronous() const noexcept {
69-
return isSynchronous_;
46+
return runtimeSchedulerImpl_->getIsSynchronous();
7047
}
7148

7249
void RuntimeScheduler::cancelTask(Task& task) noexcept {
73-
task.callback.reset();
50+
return runtimeSchedulerImpl_->cancelTask(task);
7451
}
7552

7653
SchedulerPriority RuntimeScheduler::getCurrentPriorityLevel() const noexcept {
77-
return currentPriority_;
54+
return runtimeSchedulerImpl_->getCurrentPriorityLevel();
7855
}
7956

8057
RuntimeSchedulerTimePoint RuntimeScheduler::now() const noexcept {
81-
return now_();
58+
return runtimeSchedulerImpl_->now();
8259
}
8360

8461
void RuntimeScheduler::executeNowOnTheSameThread(RawCallback&& callback) {
85-
SystraceSection s("RuntimeScheduler::executeNowOnTheSameThread");
86-
87-
runtimeAccessRequests_ += 1;
88-
executeSynchronouslyOnSameThread_CAN_DEADLOCK(
89-
runtimeExecutor_,
90-
[this, callback = std::move(callback)](jsi::Runtime& runtime) {
91-
SystraceSection s2(
92-
"RuntimeScheduler::executeNowOnTheSameThread callback");
93-
94-
runtimeAccessRequests_ -= 1;
95-
isSynchronous_ = true;
96-
callback(runtime);
97-
isSynchronous_ = false;
98-
});
99-
100-
// Resume work loop if needed. In synchronous mode
101-
// only expired tasks are executed. Tasks with lower priority
102-
// might be still in the queue.
103-
scheduleWorkLoopIfNecessary();
62+
return runtimeSchedulerImpl_->executeNowOnTheSameThread(std::move(callback));
10463
}
10564

10665
void RuntimeScheduler::callExpiredTasks(jsi::Runtime& runtime) {
107-
SystraceSection s("RuntimeScheduler::callExpiredTasks");
108-
109-
auto previousPriority = currentPriority_;
110-
try {
111-
while (!taskQueue_.empty()) {
112-
auto topPriorityTask = taskQueue_.top();
113-
auto now = now_();
114-
auto didUserCallbackTimeout = topPriorityTask->expirationTime <= now;
115-
116-
if (!didUserCallbackTimeout) {
117-
break;
118-
}
119-
120-
executeTask(runtime, topPriorityTask, didUserCallbackTimeout);
121-
}
122-
} catch (jsi::JSError& error) {
123-
handleFatalError(runtime, error);
124-
}
125-
126-
currentPriority_ = previousPriority;
127-
}
128-
129-
#pragma mark - Private
130-
131-
void RuntimeScheduler::scheduleWorkLoopIfNecessary() const {
132-
if (!isWorkLoopScheduled_ && !isPerformingWork_) {
133-
isWorkLoopScheduled_ = true;
134-
runtimeExecutor_([this](jsi::Runtime& runtime) {
135-
isWorkLoopScheduled_ = false;
136-
startWorkLoop(runtime);
137-
});
138-
}
139-
}
140-
141-
void RuntimeScheduler::startWorkLoop(jsi::Runtime& runtime) const {
142-
SystraceSection s("RuntimeScheduler::startWorkLoop");
143-
144-
auto previousPriority = currentPriority_;
145-
isPerformingWork_ = true;
146-
try {
147-
while (!taskQueue_.empty()) {
148-
auto topPriorityTask = taskQueue_.top();
149-
auto now = now_();
150-
auto didUserCallbackTimeout = topPriorityTask->expirationTime <= now;
151-
152-
if (!didUserCallbackTimeout && getShouldYield()) {
153-
// This currentTask hasn't expired, and we need to yield.
154-
break;
155-
}
156-
157-
executeTask(runtime, topPriorityTask, didUserCallbackTimeout);
158-
}
159-
} catch (jsi::JSError& error) {
160-
handleFatalError(runtime, error);
161-
}
162-
163-
currentPriority_ = previousPriority;
164-
isPerformingWork_ = false;
165-
}
166-
167-
void RuntimeScheduler::executeTask(
168-
jsi::Runtime& runtime,
169-
const std::shared_ptr<Task>& task,
170-
bool didUserCallbackTimeout) const {
171-
SystraceSection s(
172-
"RuntimeScheduler::executeTask",
173-
"priority",
174-
serialize(task->priority),
175-
"didUserCallbackTimeout",
176-
didUserCallbackTimeout);
177-
178-
currentPriority_ = task->priority;
179-
auto result = task->execute(runtime, didUserCallbackTimeout);
180-
181-
if (result.isObject() && result.getObject(runtime).isFunction(runtime)) {
182-
task->callback = result.getObject(runtime).getFunction(runtime);
183-
} else {
184-
if (taskQueue_.top() == task) {
185-
taskQueue_.pop();
186-
}
187-
}
66+
return runtimeSchedulerImpl_->callExpiredTasks(runtime);
18867
}
18968

19069
} // namespace facebook::react

packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.h

Lines changed: 38 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,39 @@
1010
#include <ReactCommon/RuntimeExecutor.h>
1111
#include <react/renderer/runtimescheduler/RuntimeSchedulerClock.h>
1212
#include <react/renderer/runtimescheduler/Task.h>
13-
#include <atomic>
14-
#include <memory>
15-
#include <queue>
1613

1714
namespace facebook::react {
1815

19-
class RuntimeScheduler final {
16+
// This is a temporary abstract class for RuntimeScheduler forks to implement
17+
// (and use them interchangeably).
18+
class RuntimeSchedulerBase {
19+
public:
20+
virtual ~RuntimeSchedulerBase() = default;
21+
virtual void scheduleWork(RawCallback&& callback) const noexcept = 0;
22+
virtual void executeNowOnTheSameThread(RawCallback&& callback) = 0;
23+
virtual std::shared_ptr<Task> scheduleTask(
24+
SchedulerPriority priority,
25+
jsi::Function&& callback) noexcept = 0;
26+
virtual std::shared_ptr<Task> scheduleTask(
27+
SchedulerPriority priority,
28+
RawCallback&& callback) noexcept = 0;
29+
virtual void cancelTask(Task& task) noexcept = 0;
30+
virtual bool getShouldYield() const noexcept = 0;
31+
virtual bool getIsSynchronous() const noexcept = 0;
32+
virtual SchedulerPriority getCurrentPriorityLevel() const noexcept = 0;
33+
virtual RuntimeSchedulerTimePoint now() const noexcept = 0;
34+
virtual void callExpiredTasks(jsi::Runtime& runtime) = 0;
35+
};
36+
37+
// This is a proxy for RuntimeScheduler implementation, which will be selected
38+
// at runtime based on a feature flag.
39+
class RuntimeScheduler final : RuntimeSchedulerBase {
2040
public:
2141
RuntimeScheduler(
2242
RuntimeExecutor runtimeExecutor,
2343
std::function<RuntimeSchedulerTimePoint()> now =
2444
RuntimeSchedulerClock::now);
45+
2546
/*
2647
* Not copyable.
2748
*/
@@ -34,7 +55,7 @@ class RuntimeScheduler final {
3455
RuntimeScheduler(RuntimeScheduler&&) = delete;
3556
RuntimeScheduler& operator=(RuntimeScheduler&&) = delete;
3657

37-
void scheduleWork(RawCallback&& callback) const noexcept;
58+
void scheduleWork(RawCallback&& callback) const noexcept override;
3859

3960
/*
4061
* Grants access to the runtime synchronously on the caller's thread.
@@ -43,7 +64,7 @@ class RuntimeScheduler final {
4364
* by dispatching a synchronous event via event emitter in your native
4465
* component.
4566
*/
46-
void executeNowOnTheSameThread(RawCallback&& callback);
67+
void executeNowOnTheSameThread(RawCallback&& callback) override;
4768

4869
/*
4970
* Adds a JavaScript callback to priority queue with given priority.
@@ -53,50 +74,50 @@ class RuntimeScheduler final {
5374
*/
5475
std::shared_ptr<Task> scheduleTask(
5576
SchedulerPriority priority,
56-
jsi::Function&& callback) noexcept;
77+
jsi::Function&& callback) noexcept override;
5778

5879
std::shared_ptr<Task> scheduleTask(
5980
SchedulerPriority priority,
60-
RawCallback&& callback) noexcept;
81+
RawCallback&& callback) noexcept override;
6182

6283
/*
6384
* Cancelled task will never be executed.
6485
*
6586
* Operates on JSI object.
6687
* Thread synchronization must be enforced externally.
6788
*/
68-
void cancelTask(Task& task) noexcept;
89+
void cancelTask(Task& task) noexcept override;
6990

7091
/*
7192
* Return value indicates if host platform has a pending access to the
7293
* runtime.
7394
*
7495
* Can be called from any thread.
7596
*/
76-
bool getShouldYield() const noexcept;
97+
bool getShouldYield() const noexcept override;
7798

7899
/*
79100
* Return value informs if the current task is executed inside synchronous
80101
* block.
81102
*
82103
* Can be called from any thread.
83104
*/
84-
bool getIsSynchronous() const noexcept;
105+
bool getIsSynchronous() const noexcept override;
85106

86107
/*
87108
* Returns value of currently executed task. Designed to be called from React.
88109
*
89110
* Thread synchronization must be enforced externally.
90111
*/
91-
SchedulerPriority getCurrentPriorityLevel() const noexcept;
112+
SchedulerPriority getCurrentPriorityLevel() const noexcept override;
92113

93114
/*
94115
* Returns current monotonic time. This time is not related to wall clock
95116
* time.
96117
*
97118
* Thread synchronization must be enforced externally.
98119
*/
99-
RuntimeSchedulerTimePoint now() const noexcept;
120+
RuntimeSchedulerTimePoint now() const noexcept override;
100121

101122
/*
102123
* Expired task is a task that should have been already executed. Designed to
@@ -106,54 +127,12 @@ class RuntimeScheduler final {
106127
*
107128
* Thread synchronization must be enforced externally.
108129
*/
109-
void callExpiredTasks(jsi::Runtime& runtime);
130+
void callExpiredTasks(jsi::Runtime& runtime) override;
110131

111132
private:
112-
mutable std::priority_queue<
113-
std::shared_ptr<Task>,
114-
std::vector<std::shared_ptr<Task>>,
115-
TaskPriorityComparer>
116-
taskQueue_;
117-
118-
const RuntimeExecutor runtimeExecutor_;
119-
mutable SchedulerPriority currentPriority_{SchedulerPriority::NormalPriority};
120-
121-
/*
122-
* Counter indicating how many access to the runtime have been requested.
123-
*/
124-
mutable std::atomic<uint_fast8_t> runtimeAccessRequests_{0};
125-
126-
mutable std::atomic_bool isSynchronous_{false};
127-
128-
void startWorkLoop(jsi::Runtime& runtime) const;
129-
130-
/*
131-
* Schedules a work loop unless it has been already scheduled
132-
* This is to avoid unnecessary calls to `runtimeExecutor`.
133-
*/
134-
void scheduleWorkLoopIfNecessary() const;
135-
136-
void executeTask(
137-
jsi::Runtime& runtime,
138-
const std::shared_ptr<Task>& task,
139-
bool didUserCallbackTimeout) const;
140-
141-
/*
142-
* Returns a time point representing the current point in time. May be called
143-
* from multiple threads.
144-
*/
145-
std::function<RuntimeSchedulerTimePoint()> now_;
146-
147-
/*
148-
* Flag indicating if callback on JavaScript queue has been
149-
* scheduled.
150-
*/
151-
mutable std::atomic_bool isWorkLoopScheduled_{false};
152-
153-
/*
154-
* This flag is set while performing work, to prevent re-entrancy.
155-
*/
156-
mutable std::atomic_bool isPerformingWork_{false};
133+
// Actual implementation, stored as a unique pointer to simplify memory
134+
// management.
135+
std::unique_ptr<RuntimeSchedulerBase> runtimeSchedulerImpl_;
157136
};
158137

159138
} // namespace facebook::react

0 commit comments

Comments
 (0)