Skip to content

Commit 04bd850

Browse files
rubennortefacebook-github-bot
authored andcommitted
[skip ci] More spec-compliant execution of microtasks
Summary: Changelog: [internal] This modifies the method to run microtasks in `RuntimeScheduler_Modern` to align a bit better with the spec. In this case, we'll check if we're already running microtasks when we call that method, and skip if that's the case. We're not currently calling this method recursively so this shouldn't really be a change with the current logic. Differential Revision: D54302537
1 parent 7cfd686 commit 04bd850

File tree

3 files changed

+92
-35
lines changed

3 files changed

+92
-35
lines changed

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

Lines changed: 41 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -11,45 +11,12 @@
1111
#include <cxxreact/ErrorUtils.h>
1212
#include <react/featureflags/ReactNativeFeatureFlags.h>
1313
#include <react/renderer/debug/SystraceSection.h>
14+
#include <react/utils/SetForScope.h>
1415
#include <utility>
1516
#include "ErrorUtils.h"
1617

1718
namespace facebook::react {
1819

19-
namespace {
20-
/**
21-
* This is partially equivalent to the "Perform a microtask checkpoint" step in
22-
* the Web event loop. See
23-
* https://html.spec.whatwg.org/multipage/webappapis.html#perform-a-microtask-checkpoint.
24-
*
25-
* Iterates on \c drainMicrotasks until it completes or hits the retries bound.
26-
*/
27-
void executeMicrotasks(jsi::Runtime& runtime) {
28-
SystraceSection s("RuntimeScheduler::executeMicrotasks");
29-
30-
uint8_t retries = 0;
31-
// A heuristic number to guard infinite or absurd numbers of retries.
32-
const static unsigned int kRetriesBound = 255;
33-
34-
while (retries < kRetriesBound) {
35-
try {
36-
// The default behavior of \c drainMicrotasks is unbounded execution.
37-
// We may want to make it bounded in the future.
38-
if (runtime.drainMicrotasks()) {
39-
break;
40-
}
41-
} catch (jsi::JSError& error) {
42-
handleJSError(runtime, error, true);
43-
}
44-
retries++;
45-
}
46-
47-
if (retries == kRetriesBound) {
48-
throw std::runtime_error("Hits microtasks retries bound.");
49-
}
50-
}
51-
} // namespace
52-
5320
#pragma mark - Public
5421

5522
RuntimeScheduler_Modern::RuntimeScheduler_Modern(
@@ -298,7 +265,7 @@ void RuntimeScheduler_Modern::executeTask(
298265

299266
if (ReactNativeFeatureFlags::enableMicrotasks()) {
300267
// "Perform a microtask checkpoint" step.
301-
executeMicrotasks(runtime);
268+
performMicrotaskCheckpoint(runtime);
302269
}
303270

304271
if (ReactNativeFeatureFlags::batchRenderingUpdatesInEventLoop()) {
@@ -339,4 +306,43 @@ void RuntimeScheduler_Modern::executeMacrotask(
339306
}
340307
}
341308

309+
/**
310+
* This is partially equivalent to the "Perform a microtask checkpoint" step in
311+
* the Web event loop. See
312+
* https://html.spec.whatwg.org/multipage/webappapis.html#perform-a-microtask-checkpoint.
313+
*
314+
* Iterates on \c drainMicrotasks until it completes or hits the retries bound.
315+
*/
316+
void RuntimeScheduler_Modern::performMicrotaskCheckpoint(
317+
jsi::Runtime& runtime) {
318+
SystraceSection s("RuntimeScheduler::performMicrotaskCheckpoint");
319+
320+
if (performingMicrotaskCheckpoint_) {
321+
return;
322+
}
323+
324+
SetForScope change(performingMicrotaskCheckpoint_, true);
325+
326+
uint8_t retries = 0;
327+
// A heuristic number to guard infinite or absurd numbers of retries.
328+
const static unsigned int kRetriesBound = 255;
329+
330+
while (retries < kRetriesBound) {
331+
try {
332+
// The default behavior of \c drainMicrotasks is unbounded execution.
333+
// We may want to make it bounded in the future.
334+
if (runtime.drainMicrotasks()) {
335+
break;
336+
}
337+
} catch (jsi::JSError& error) {
338+
handleJSError(runtime, error, true);
339+
}
340+
retries++;
341+
}
342+
343+
if (retries == kRetriesBound) {
344+
throw std::runtime_error("Hits microtasks retries bound.");
345+
}
346+
}
347+
342348
} // namespace facebook::react

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,9 @@ class RuntimeScheduler_Modern final : public RuntimeSchedulerBase {
178178

179179
void updateRendering();
180180

181+
bool performingMicrotaskCheckpoint_{false};
182+
void performMicrotaskCheckpoint(jsi::Runtime& runtime);
183+
181184
/*
182185
* Returns a time point representing the current point in time. May be called
183186
* from multiple threads.
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#pragma once
9+
10+
#include <memory>
11+
12+
namespace facebook::react {
13+
14+
template <typename T>
15+
class SetForScope {
16+
public:
17+
explicit SetForScope(T& variable)
18+
: variable_(variable), valueToRestore_(variable) {}
19+
20+
template <typename U>
21+
SetForScope(T& variable, U&& newValue) : SetForScope(variable) {
22+
variable_ = std::forward<U>(newValue);
23+
}
24+
25+
template <typename U, typename V>
26+
SetForScope(T& variable, U&& newValue, V&& valueToRestore)
27+
: variable_(variable), valueToRestore_(std::forward<V>(valueToRestore)) {
28+
variable_ = std::forward<U>(newValue);
29+
}
30+
31+
// Non-movable
32+
SetForScope(const SetForScope&) = delete;
33+
SetForScope(SetForScope&&) = delete;
34+
35+
// Non-copyable
36+
SetForScope& operator=(const SetForScope&) = delete;
37+
SetForScope& operator=(SetForScope&&) = delete;
38+
39+
~SetForScope() {
40+
variable_ = std::move(valueToRestore_);
41+
}
42+
43+
private:
44+
T& variable_;
45+
T valueToRestore_;
46+
};
47+
48+
} // namespace facebook::react

0 commit comments

Comments
 (0)