Skip to content

Commit a8d0dd6

Browse files
sammy-SCfacebook-github-bot
authored andcommitted
Implement task continuation
Summary: Changelog: [internal] Scheduler's callback have option to add more work inside callback. This work stays on top of the priority queue and gives React ability to flush all work synchronously if need. This diff adds use of `shouldYield_` to the workLoop. For now, it always evaluates to false. In the future when we allow access to the scheduler to native, it will allow yielding. Relevant code in JavaScript: https://github.com/facebook/react/blob/master/packages/scheduler/src/forks/SchedulerNoDOM.js#L190 Reviewed By: mdvacca Differential Revision: D27823528 fbshipit-source-id: 016101e41eb7c41c2ac5abb55f803814867b8517
1 parent ed76719 commit a8d0dd6

File tree

4 files changed

+82
-42
lines changed

4 files changed

+82
-42
lines changed

ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.cpp

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,23 @@ std::shared_ptr<Task> RuntimeScheduler::scheduleTask(
3232

3333
while (!taskQueue_.empty()) {
3434
auto topPriority = taskQueue_.top();
35-
taskQueue_.pop();
36-
(*topPriority)(runtime);
35+
auto now = now_();
36+
auto didUserCallbackTimeout = topPriority->expirationTime <= now;
37+
38+
if (!didUserCallbackTimeout && shouldYield_) {
39+
// This task hasn't expired and we need to yield.
40+
break;
41+
}
42+
43+
auto result = topPriority->execute(runtime);
44+
45+
if (result.isObject() &&
46+
result.getObject(runtime).isFunction(runtime)) {
47+
topPriority->callback =
48+
result.getObject(runtime).getFunction(runtime);
49+
} else {
50+
taskQueue_.pop();
51+
}
3752
}
3853
});
3954
}
@@ -42,7 +57,7 @@ std::shared_ptr<Task> RuntimeScheduler::scheduleTask(
4257
}
4358

4459
void RuntimeScheduler::cancelTask(const std::shared_ptr<Task> &task) {
45-
task->cancel();
60+
task->callback.reset();
4661
}
4762

4863
bool RuntimeScheduler::getShouldYield() const {

ReactCommon/react/renderer/runtimescheduler/Task.cpp

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,33 +13,19 @@ Task::Task(
1313
SchedulerPriority priority,
1414
jsi::Function callback,
1515
std::chrono::steady_clock::time_point expirationTime)
16-
: priority_(priority),
17-
callback_(std::move(callback)),
18-
expirationTime_(expirationTime) {}
16+
: priority(priority),
17+
callback(std::move(callback)),
18+
expirationTime(expirationTime) {}
1919

20-
SchedulerPriority Task::getPriority() const {
21-
return priority_;
22-
}
23-
24-
RuntimeSchedulerClock::time_point Task::getExpirationTime() const {
25-
return expirationTime_;
26-
}
27-
28-
void Task::cancel() {
29-
// Null out the callback to indicate the task has been canceled. (Can't
30-
// remove from the priority_queue because you can't remove arbitrary nodes
31-
// from an array based heap, only the first one.)
32-
callback_.reset();
33-
}
34-
35-
void Task::operator()(jsi::Runtime &runtime) const {
20+
jsi::Value Task::execute(jsi::Runtime &runtime) const {
3621
// Cancelled task doesn't have a callback.
37-
if (callback_) {
22+
if (callback) {
3823
// Callback in JavaScript is expecting a single bool parameter.
3924
// React team plans to remove it and it is safe to pass in
4025
// hardcoded false value.
41-
callback_.value().call(runtime, {false});
26+
return callback.value().call(runtime, {false});
4227
}
28+
return jsi::Value::undefined();
4329
}
4430

4531
} // namespace facebook::react

ReactCommon/react/renderer/runtimescheduler/Task.h

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,33 +14,32 @@
1414

1515
namespace facebook::react {
1616

17-
class Task final {
18-
public:
17+
class RuntimeScheduler;
18+
class TaskPriorityComparer;
19+
20+
struct Task final {
1921
Task(
2022
SchedulerPriority priority,
2123
jsi::Function callback,
2224
std::chrono::steady_clock::time_point expirationTime);
2325

24-
SchedulerPriority getPriority() const;
25-
26-
RuntimeSchedulerClock::time_point getExpirationTime() const;
27-
28-
void cancel();
26+
private:
27+
friend RuntimeScheduler;
28+
friend TaskPriorityComparer;
2929

30-
void operator()(jsi::Runtime &runtime) const;
30+
SchedulerPriority priority;
31+
better::optional<jsi::Function> callback;
32+
RuntimeSchedulerClock::time_point expirationTime;
3133

32-
private:
33-
SchedulerPriority priority_;
34-
better::optional<jsi::Function> callback_;
35-
RuntimeSchedulerClock::time_point expirationTime_;
34+
jsi::Value execute(jsi::Runtime &runtime) const;
3635
};
3736

3837
class TaskPriorityComparer {
3938
public:
4039
inline bool operator()(
4140
std::shared_ptr<Task> const &lhs,
4241
std::shared_ptr<Task> const &rhs) {
43-
return lhs->getExpirationTime() > rhs->getExpirationTime();
42+
return lhs->expirationTime > rhs->expirationTime;
4443
}
4544
};
4645

ReactCommon/react/renderer/runtimescheduler/tests/RuntimeSchedulerTest.cpp

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ class RuntimeSchedulerTest : public testing::Test {
4343
}
4444

4545
jsi::Function createHostFunctionFromLambda(
46-
std::function<void(bool)> callback) {
46+
std::function<jsi::Value(bool)> callback) {
4747
return jsi::Function::createFromHostFunction(
4848
*runtime_,
4949
jsi::PropNameID::forUtf8(*runtime_, ""),
@@ -55,8 +55,7 @@ class RuntimeSchedulerTest : public testing::Test {
5555
size_t) noexcept -> jsi::Value {
5656
++hostFunctionCallCount_;
5757
auto didUserCallbackTimeout = arguments[0].getBool();
58-
callback(didUserCallbackTimeout);
59-
return jsi::Value::undefined();
58+
return callback(didUserCallbackTimeout);
6059
});
6160
}
6261

@@ -93,6 +92,7 @@ TEST_F(RuntimeSchedulerTest, scheduleSingleTask) {
9392
createHostFunctionFromLambda([&didRunTask](bool didUserCallbackTimeout) {
9493
didRunTask = true;
9594
EXPECT_FALSE(didUserCallbackTimeout);
95+
return jsi::Value::undefined();
9696
});
9797

9898
runtimeScheduler_->scheduleTask(
@@ -113,6 +113,7 @@ TEST_F(RuntimeSchedulerTest, scheduleImmediatePriorityTask) {
113113
createHostFunctionFromLambda([&didRunTask](bool didUserCallbackTimeout) {
114114
didRunTask = true;
115115
EXPECT_FALSE(didUserCallbackTimeout);
116+
return jsi::Value::undefined();
116117
});
117118

118119
runtimeScheduler_->scheduleTask(
@@ -135,6 +136,7 @@ TEST_F(RuntimeSchedulerTest, taskExpiration) {
135136
// Task has timed out but the parameter is deprecated and `false` is
136137
// hardcoded.
137138
EXPECT_FALSE(didUserCallbackTimeout);
139+
return jsi::Value::undefined();
138140
});
139141

140142
runtimeScheduler_->scheduleTask(
@@ -157,6 +159,7 @@ TEST_F(RuntimeSchedulerTest, scheduleTwoTasksWithSamePriority) {
157159
auto callbackOne =
158160
createHostFunctionFromLambda([this, &firstTaskCallOrder](bool) {
159161
firstTaskCallOrder = hostFunctionCallCount_;
162+
return jsi::Value::undefined();
160163
});
161164

162165
runtimeScheduler_->scheduleTask(
@@ -166,6 +169,7 @@ TEST_F(RuntimeSchedulerTest, scheduleTwoTasksWithSamePriority) {
166169
auto callbackTwo =
167170
createHostFunctionFromLambda([this, &secondTaskCallOrder](bool) {
168171
secondTaskCallOrder = hostFunctionCallCount_;
172+
return jsi::Value::undefined();
169173
});
170174

171175
runtimeScheduler_->scheduleTask(
@@ -188,6 +192,7 @@ TEST_F(RuntimeSchedulerTest, scheduleTwoTasksWithDifferentPriorities) {
188192
auto callbackOne =
189193
createHostFunctionFromLambda([this, &lowPriorityTaskCallOrder](bool) {
190194
lowPriorityTaskCallOrder = hostFunctionCallCount_;
195+
return jsi::Value::undefined();
191196
});
192197

193198
runtimeScheduler_->scheduleTask(
@@ -197,6 +202,7 @@ TEST_F(RuntimeSchedulerTest, scheduleTwoTasksWithDifferentPriorities) {
197202
auto callbackTwo = createHostFunctionFromLambda(
198203
[this, &userBlockingPriorityTaskCallOrder](bool) {
199204
userBlockingPriorityTaskCallOrder = hostFunctionCallCount_;
205+
return jsi::Value::undefined();
200206
});
201207

202208
runtimeScheduler_->scheduleTask(
@@ -216,8 +222,10 @@ TEST_F(RuntimeSchedulerTest, scheduleTwoTasksWithDifferentPriorities) {
216222

217223
TEST_F(RuntimeSchedulerTest, cancelTask) {
218224
bool didRunTask = false;
219-
auto callback =
220-
createHostFunctionFromLambda([&didRunTask](bool) { didRunTask = true; });
225+
auto callback = createHostFunctionFromLambda([&didRunTask](bool) {
226+
didRunTask = true;
227+
return jsi::Value::undefined();
228+
});
221229

222230
auto task = runtimeScheduler_->scheduleTask(
223231
SchedulerPriority::NormalPriority, std::move(callback));
@@ -233,4 +241,36 @@ TEST_F(RuntimeSchedulerTest, cancelTask) {
233241
EXPECT_EQ(stubQueue_->size(), 0);
234242
}
235243

244+
TEST_F(RuntimeSchedulerTest, continuationTask) {
245+
bool didRunTask = false;
246+
bool didContinuationTask = false;
247+
248+
auto callback = createHostFunctionFromLambda([&](bool) {
249+
didRunTask = true;
250+
return jsi::Function::createFromHostFunction(
251+
*runtime_,
252+
jsi::PropNameID::forUtf8(*runtime_, ""),
253+
1,
254+
[&](jsi::Runtime &runtime,
255+
jsi::Value const &,
256+
jsi::Value const *arguments,
257+
size_t) noexcept -> jsi::Value {
258+
didContinuationTask = true;
259+
return jsi::Value::undefined();
260+
});
261+
});
262+
263+
auto task = runtimeScheduler_->scheduleTask(
264+
SchedulerPriority::NormalPriority, std::move(callback));
265+
266+
EXPECT_FALSE(didRunTask);
267+
EXPECT_EQ(stubQueue_->size(), 1);
268+
269+
stubQueue_->tick();
270+
271+
EXPECT_TRUE(didRunTask);
272+
EXPECT_TRUE(didContinuationTask);
273+
EXPECT_EQ(stubQueue_->size(), 0);
274+
}
275+
236276
} // namespace facebook::react

0 commit comments

Comments
 (0)