Skip to content

Commit c457516

Browse files
rubennortefacebook-github-bot
authored andcommitted
Create new version of RuntimeScheduler (#40944)
Summary: Pull Request resolved: #40944 ## Summary This creates a new version of `RuntimeScheduler` that's intended to be backwards compatible but with a few notable changes: 1. `scheduleTask` is now thread-safe. 2. `scheduleWork` is now just an alias of `scheduleTask` with immediate priority (to preserve the yielding semantics it had over other tasks). 3. Yielding mechanism has changed, to make lower priority tasks to yield to higher priority tasks, instead of just yielding to `scheduleWork` and `executeNowOnTheSameThread`. We don't expect this to have any impact in performance or user perceivable behavior, so we consider it a short-lived refactor. When we validate this assumptions in a complex application we'll delete the old version and only keep the fork. ## Motivation The main motivation for this refactor is to reduce the amount of unnecessary interruptions of running tasks (via `shouldYield`) that are only used to schedule asynchronous tasks from native. The `scheduleWork` method is the only available mechanism exposed to native APIs to schedule work in the JS thread (as the existing version of `scheduleTask` is only meant to be called from JS). This mechanism **always** asks for any running tasks in the scheduler to yield, so these tasks are always considered to have the highest priority. This makes sense for discrete user events, but not for many other use cases coming from native (e.g.: notifying network responses could be UserBlocking, Normal or Low depending on the use case). We need a way to schedule tasks from native with other kinds of priorities, so we don't always have to interrupt what's currently executing if it has a higher priority than what we're scheduling. ## Changes **General APIs:** This centralizes scheduling in only 2 APIs in `RuntimeScheduler` (which already exist in the legacy version): * `scheduleTask`, which is non-blocking for the caller and can be used from any thread. This always uses the task queue in the scheduler and a new yielding mechanism. * `executeNowOnTheSameThread`, which is blocking for the caller and asks any task executing in the scheduler to yield. These tasks don't go through the task queue and instead queue through the existing synchronization mechanism in `RuntimeExecutor`. The yielding mechanism for these tasks is preserved. `scheduleWork` will be deprecated and it's just an alias for `scheduleTask` with an immediate priority (to preserve a similar behavior). **Yielding behavior:** Before, tasks would only yield to tasks scheduled via `scheduleWork` and `executeNowOnTheSameThread` (those tasks didn't go through the task queue). With this implementation, tasks would now yield to any task that has a higher position in the task queue. That means we reuse the existing mechanism to avoid lower priority tasks to never execute because higher priority tasks never stop coming. All tasks would yield to requests for synchronous access (via `executeNowOnTheSameThread`) as did the current implementation. Changelog: [internal] Reviewed By: javache, sammy-SC Differential Revision: D49316881 fbshipit-source-id: 55a72cabbf3f9e110cbf7e878e52e066fd77d8cf
1 parent 51200c1 commit c457516

File tree

8 files changed

+803
-37
lines changed

8 files changed

+803
-37
lines changed

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

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#include "RuntimeScheduler.h"
99
#include "RuntimeScheduler_Legacy.h"
10+
#include "RuntimeScheduler_Modern.h"
1011
#include "SchedulerPriorityUtils.h"
1112

1213
#include <react/renderer/debug/SystraceSection.h>
@@ -15,11 +16,28 @@
1516

1617
namespace facebook::react {
1718

19+
namespace {
20+
std::unique_ptr<RuntimeSchedulerBase> getRuntimeSchedulerImplementation(
21+
RuntimeExecutor runtimeExecutor,
22+
bool useModernRuntimeScheduler,
23+
std::function<RuntimeSchedulerTimePoint()> now) {
24+
if (useModernRuntimeScheduler) {
25+
return std::make_unique<RuntimeScheduler_Modern>(
26+
std::move(runtimeExecutor), std::move(now));
27+
} else {
28+
return std::make_unique<RuntimeScheduler_Legacy>(
29+
std::move(runtimeExecutor), std::move(now));
30+
}
31+
}
32+
} // namespace
33+
1834
RuntimeScheduler::RuntimeScheduler(
1935
RuntimeExecutor runtimeExecutor,
36+
bool useModernRuntimeScheduler,
2037
std::function<RuntimeSchedulerTimePoint()> now)
21-
: runtimeSchedulerImpl_(std::make_unique<RuntimeScheduler_Legacy>(
38+
: runtimeSchedulerImpl_(getRuntimeSchedulerImplementation(
2239
std::move(runtimeExecutor),
40+
useModernRuntimeScheduler,
2341
std::move(now))) {}
2442

2543
void RuntimeScheduler::scheduleWork(RawCallback&& callback) const noexcept {
@@ -28,13 +46,13 @@ void RuntimeScheduler::scheduleWork(RawCallback&& callback) const noexcept {
2846

2947
std::shared_ptr<Task> RuntimeScheduler::scheduleTask(
3048
SchedulerPriority priority,
31-
jsi::Function&& callback) noexcept {
49+
jsi::Function&& callback) const noexcept {
3250
return runtimeSchedulerImpl_->scheduleTask(priority, std::move(callback));
3351
}
3452

3553
std::shared_ptr<Task> RuntimeScheduler::scheduleTask(
3654
SchedulerPriority priority,
37-
RawCallback&& callback) noexcept {
55+
RawCallback&& callback) const noexcept {
3856
return runtimeSchedulerImpl_->scheduleTask(priority, std::move(callback));
3957
}
4058

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

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,20 @@ namespace facebook::react {
1818
class RuntimeSchedulerBase {
1919
public:
2020
virtual ~RuntimeSchedulerBase() = default;
21+
// FIXME(T167271466): remove `const` modified when the RuntimeScheduler
22+
// refactor has been shipped.
2123
virtual void scheduleWork(RawCallback&& callback) const noexcept = 0;
2224
virtual void executeNowOnTheSameThread(RawCallback&& callback) = 0;
25+
// FIXME(T167271466): remove `const` modified when the RuntimeScheduler
26+
// refactor has been shipped.
2327
virtual std::shared_ptr<Task> scheduleTask(
2428
SchedulerPriority priority,
25-
jsi::Function&& callback) noexcept = 0;
29+
jsi::Function&& callback) const noexcept = 0;
30+
// FIXME(T167271466): remove `const` modified when the RuntimeScheduler
31+
// refactor has been shipped.
2632
virtual std::shared_ptr<Task> scheduleTask(
2733
SchedulerPriority priority,
28-
RawCallback&& callback) noexcept = 0;
34+
RawCallback&& callback) const noexcept = 0;
2935
virtual void cancelTask(Task& task) noexcept = 0;
3036
virtual bool getShouldYield() const noexcept = 0;
3137
virtual bool getIsSynchronous() const noexcept = 0;
@@ -38,8 +44,9 @@ class RuntimeSchedulerBase {
3844
// at runtime based on a feature flag.
3945
class RuntimeScheduler final : RuntimeSchedulerBase {
4046
public:
41-
RuntimeScheduler(
47+
explicit RuntimeScheduler(
4248
RuntimeExecutor runtimeExecutor,
49+
bool useModernRuntimeScheduler = false,
4350
std::function<RuntimeSchedulerTimePoint()> now =
4451
RuntimeSchedulerClock::now);
4552

@@ -74,11 +81,11 @@ class RuntimeScheduler final : RuntimeSchedulerBase {
7481
*/
7582
std::shared_ptr<Task> scheduleTask(
7683
SchedulerPriority priority,
77-
jsi::Function&& callback) noexcept override;
84+
jsi::Function&& callback) const noexcept override;
7885

7986
std::shared_ptr<Task> scheduleTask(
8087
SchedulerPriority priority,
81-
RawCallback&& callback) noexcept override;
88+
RawCallback&& callback) const noexcept override;
8289

8390
/*
8491
* Cancelled task will never be executed.

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

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,14 @@ void RuntimeScheduler_Legacy::scheduleWork(
3838

3939
std::shared_ptr<Task> RuntimeScheduler_Legacy::scheduleTask(
4040
SchedulerPriority priority,
41-
jsi::Function&& callback) noexcept {
41+
jsi::Function&& callback) const noexcept {
42+
SystraceSection s(
43+
"RuntimeScheduler::scheduleTask",
44+
"priority",
45+
serialize(priority),
46+
"callbackType",
47+
"jsi::Function");
48+
4249
auto expirationTime = now_() + timeoutForSchedulerPriority(priority);
4350
auto task =
4451
std::make_shared<Task>(priority, std::move(callback), expirationTime);
@@ -51,7 +58,14 @@ std::shared_ptr<Task> RuntimeScheduler_Legacy::scheduleTask(
5158

5259
std::shared_ptr<Task> RuntimeScheduler_Legacy::scheduleTask(
5360
SchedulerPriority priority,
54-
RawCallback&& callback) noexcept {
61+
RawCallback&& callback) const noexcept {
62+
SystraceSection s(
63+
"RuntimeScheduler::scheduleTask",
64+
"priority",
65+
serialize(priority),
66+
"callbackType",
67+
"RawCallback");
68+
5569
auto expirationTime = now_() + timeoutForSchedulerPriority(priority);
5670
auto task =
5771
std::make_shared<Task>(priority, std::move(callback), expirationTime);

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ namespace facebook::react {
1919

2020
class RuntimeScheduler_Legacy final : public RuntimeSchedulerBase {
2121
public:
22-
RuntimeScheduler_Legacy(
22+
explicit RuntimeScheduler_Legacy(
2323
RuntimeExecutor runtimeExecutor,
2424
std::function<RuntimeSchedulerTimePoint()> now =
2525
RuntimeSchedulerClock::now);
@@ -55,11 +55,11 @@ class RuntimeScheduler_Legacy final : public RuntimeSchedulerBase {
5555
*/
5656
std::shared_ptr<Task> scheduleTask(
5757
SchedulerPriority priority,
58-
jsi::Function&& callback) noexcept override;
58+
jsi::Function&& callback) const noexcept override;
5959

6060
std::shared_ptr<Task> scheduleTask(
6161
SchedulerPriority priority,
62-
RawCallback&& callback) noexcept override;
62+
RawCallback&& callback) const noexcept override;
6363

6464
/*
6565
* Cancelled task will never be executed.

0 commit comments

Comments
 (0)