Skip to content

Commit 5e933fd

Browse files
sammy-SCfacebook-github-bot
authored andcommitted
Yield for each access to the runtime
Summary: changelog: [internal] With multiple requests to the runtime, we need to make sure they are all granted before React continues with rendering. A boolean is not enough to track this. The first lambda that has VM will set it to false and subsequent requests will have to wait for React to finish rendering. To prevent this, we can count how many lambdas are pending access to the runtime. Reviewed By: ShikaSD Differential Revision: D33792734 fbshipit-source-id: f785fae3575470179dd69acc6a466211b79b633b
1 parent 0a79888 commit 5e933fd

File tree

4 files changed

+60
-13
lines changed

4 files changed

+60
-13
lines changed

ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@ RuntimeScheduler::RuntimeScheduler(
2323
void RuntimeScheduler::scheduleWork(
2424
std::function<void(jsi::Runtime &)> callback) const {
2525
if (enableYielding_) {
26-
shouldYield_ = true;
26+
runtimeAccessRequests_ += 1;
2727
runtimeExecutor_(
2828
[this, callback = std::move(callback)](jsi::Runtime &runtime) {
29-
shouldYield_ = false;
29+
runtimeAccessRequests_ -= 1;
3030
callback(runtime);
3131
startWorkLoop(runtime);
3232
});
@@ -51,7 +51,7 @@ std::shared_ptr<Task> RuntimeScheduler::scheduleTask(
5151
}
5252

5353
bool RuntimeScheduler::getShouldYield() const noexcept {
54-
return shouldYield_;
54+
return runtimeAccessRequests_ > 0;
5555
}
5656

5757
bool RuntimeScheduler::getIsSynchronous() const noexcept {
@@ -76,11 +76,11 @@ void RuntimeScheduler::setEnableYielding(bool enableYielding) {
7676

7777
void RuntimeScheduler::executeNowOnTheSameThread(
7878
std::function<void(jsi::Runtime &runtime)> callback) {
79-
shouldYield_ = true;
79+
runtimeAccessRequests_ += 1;
8080
executeSynchronouslyOnSameThread_CAN_DEADLOCK(
8181
runtimeExecutor_,
8282
[this, callback = std::move(callback)](jsi::Runtime &runtime) {
83-
shouldYield_ = false;
83+
runtimeAccessRequests_ -= 1;
8484
isSynchronous_ = true;
8585
callback(runtime);
8686
isSynchronous_ = false;

ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,12 @@ class RuntimeScheduler final {
117117

118118
RuntimeExecutor const runtimeExecutor_;
119119
mutable SchedulerPriority currentPriority_{SchedulerPriority::NormalPriority};
120-
mutable std::atomic_bool shouldYield_{false};
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+
121126
mutable std::atomic_bool isSynchronous_{false};
122127

123128
void startWorkLoop(jsi::Runtime &runtime) const;

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

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

4444
runtimeScheduler_ =
4545
std::make_unique<RuntimeScheduler>(runtimeExecutor, stubNow);
46+
runtimeScheduler_->setEnableYielding(true);
4647
}
4748

4849
jsi::Function createHostFunctionFromLambda(
@@ -317,6 +318,7 @@ TEST_F(RuntimeSchedulerTest, getCurrentPriorityLevel) {
317318
}
318319

319320
TEST_F(RuntimeSchedulerTest, scheduleWork) {
321+
runtimeScheduler_->setEnableYielding(false);
320322
bool wasCalled = false;
321323
runtimeScheduler_->scheduleWork(
322324
[&](jsi::Runtime const &) { wasCalled = true; });
@@ -334,7 +336,6 @@ TEST_F(RuntimeSchedulerTest, scheduleWork) {
334336
}
335337

336338
TEST_F(RuntimeSchedulerTest, scheduleWorkWithYielding) {
337-
runtimeScheduler_->setEnableYielding(true);
338339
bool wasCalled = false;
339340
runtimeScheduler_->scheduleWork(
340341
[&](jsi::Runtime const &) { wasCalled = true; });
@@ -353,8 +354,6 @@ TEST_F(RuntimeSchedulerTest, scheduleWorkWithYielding) {
353354
}
354355

355356
TEST_F(RuntimeSchedulerTest, normalTaskYieldsToPlatformEvent) {
356-
runtimeScheduler_->setEnableYielding(true);
357-
358357
bool didRunJavaScriptTask = false;
359358
bool didRunPlatformWork = false;
360359

@@ -382,8 +381,6 @@ TEST_F(RuntimeSchedulerTest, normalTaskYieldsToPlatformEvent) {
382381
}
383382

384383
TEST_F(RuntimeSchedulerTest, expiredTaskDoesntYieldToPlatformEvent) {
385-
runtimeScheduler_->setEnableYielding(true);
386-
387384
bool didRunJavaScriptTask = false;
388385
bool didRunPlatformWork = false;
389386

@@ -412,8 +409,6 @@ TEST_F(RuntimeSchedulerTest, expiredTaskDoesntYieldToPlatformEvent) {
412409
}
413410

414411
TEST_F(RuntimeSchedulerTest, immediateTaskDoesntYieldToPlatformEvent) {
415-
runtimeScheduler_->setEnableYielding(true);
416-
417412
bool didRunJavaScriptTask = false;
418413
bool didRunPlatformWork = false;
419414

@@ -603,4 +598,42 @@ TEST_F(RuntimeSchedulerTest, sameThreadTaskCreatesLowPriorityTask) {
603598
EXPECT_EQ(stubQueue_->size(), 0);
604599
}
605600

601+
TEST_F(RuntimeSchedulerTest, twoThreadsRequestAccessToTheRuntime) {
602+
bool didRunSynchronousTask = false;
603+
bool didRunWork = false;
604+
605+
runtimeScheduler_->scheduleWork(
606+
[&didRunWork](jsi::Runtime &) { didRunWork = true; });
607+
608+
std::thread t1([this, &didRunSynchronousTask]() {
609+
runtimeScheduler_->executeNowOnTheSameThread(
610+
[&didRunSynchronousTask](jsi::Runtime &runtime) {
611+
didRunSynchronousTask = true;
612+
});
613+
});
614+
615+
auto hasTask = stubQueue_->waitForTasks(2, 1ms);
616+
617+
EXPECT_TRUE(hasTask);
618+
EXPECT_FALSE(didRunWork);
619+
EXPECT_FALSE(didRunSynchronousTask);
620+
EXPECT_TRUE(runtimeScheduler_->getShouldYield());
621+
EXPECT_EQ(stubQueue_->size(), 2);
622+
623+
stubQueue_->tick();
624+
625+
EXPECT_TRUE(didRunWork);
626+
EXPECT_FALSE(didRunSynchronousTask);
627+
EXPECT_TRUE(runtimeScheduler_->getShouldYield());
628+
EXPECT_EQ(stubQueue_->size(), 1);
629+
630+
stubQueue_->tick();
631+
632+
t1.join();
633+
634+
EXPECT_TRUE(didRunWork);
635+
EXPECT_TRUE(didRunSynchronousTask);
636+
EXPECT_FALSE(runtimeScheduler_->getShouldYield());
637+
}
638+
606639
} // namespace facebook::react

ReactCommon/react/renderer/runtimescheduler/tests/StubQueue.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,15 @@ class StubQueue {
5353
lock, timeout, [this]() { return !callbackQueue_.empty(); });
5454
}
5555

56+
bool waitForTasks(
57+
std::size_t numberOfTasks,
58+
std::chrono::duration<double> timeout) const {
59+
std::unique_lock<std::mutex> lock(mutex_);
60+
return signal_.wait_for(lock, timeout, [this, numberOfTasks]() {
61+
return numberOfTasks == callbackQueue_.size();
62+
});
63+
}
64+
5665
private:
5766
mutable std::condition_variable signal_;
5867
mutable std::mutex mutex_;

0 commit comments

Comments
 (0)