diff --git a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Modern.cpp b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Modern.cpp index 4a30a2ff5c4..aa737c68310 100644 --- a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Modern.cpp +++ b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Modern.cpp @@ -11,45 +11,12 @@ #include #include #include +#include #include #include "ErrorUtils.h" namespace facebook::react { -namespace { -/** - * This is partially equivalent to the "Perform a microtask checkpoint" step in - * the Web event loop. See - * https://html.spec.whatwg.org/multipage/webappapis.html#perform-a-microtask-checkpoint. - * - * Iterates on \c drainMicrotasks until it completes or hits the retries bound. - */ -void executeMicrotasks(jsi::Runtime& runtime) { - SystraceSection s("RuntimeScheduler::executeMicrotasks"); - - uint8_t retries = 0; - // A heuristic number to guard infinite or absurd numbers of retries. - const static unsigned int kRetriesBound = 255; - - while (retries < kRetriesBound) { - try { - // The default behavior of \c drainMicrotasks is unbounded execution. - // We may want to make it bounded in the future. - if (runtime.drainMicrotasks()) { - break; - } - } catch (jsi::JSError& error) { - handleJSError(runtime, error, true); - } - retries++; - } - - if (retries == kRetriesBound) { - throw std::runtime_error("Hits microtasks retries bound."); - } -} -} // namespace - #pragma mark - Public RuntimeScheduler_Modern::RuntimeScheduler_Modern( @@ -298,7 +265,7 @@ void RuntimeScheduler_Modern::executeTask( if (ReactNativeFeatureFlags::enableMicrotasks()) { // "Perform a microtask checkpoint" step. - executeMicrotasks(runtime); + performMicrotaskCheckpoint(runtime); } if (ReactNativeFeatureFlags::batchRenderingUpdatesInEventLoop()) { @@ -339,4 +306,44 @@ void RuntimeScheduler_Modern::executeMacrotask( } } +/** + * This is partially equivalent to the "Perform a microtask checkpoint" step in + * the Web event loop. See + * https://html.spec.whatwg.org/multipage/webappapis.html#perform-a-microtask-checkpoint. + * + * Iterates on \c drainMicrotasks until it completes or hits the retries bound. + */ +void RuntimeScheduler_Modern::performMicrotaskCheckpoint( + jsi::Runtime& runtime) { + SystraceSection s("RuntimeScheduler::performMicrotaskCheckpoint"); + + if (performingMicrotaskCheckpoint_) { + return; + } + + performingMicrotaskCheckpoint_ = true; + OnScopeExit restoreFlag([&]() { performingMicrotaskCheckpoint_ = false; }); + + uint8_t retries = 0; + // A heuristic number to guard infinite or absurd numbers of retries. + const static unsigned int kRetriesBound = 255; + + while (retries < kRetriesBound) { + try { + // The default behavior of \c drainMicrotasks is unbounded execution. + // We may want to make it bounded in the future. + if (runtime.drainMicrotasks()) { + break; + } + } catch (jsi::JSError& error) { + handleJSError(runtime, error, true); + } + retries++; + } + + if (retries == kRetriesBound) { + throw std::runtime_error("Hits microtasks retries bound."); + } +} + } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Modern.h b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Modern.h index 34c8eb4cf16..0759d8470bd 100644 --- a/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Modern.h +++ b/packages/react-native/ReactCommon/react/renderer/runtimescheduler/RuntimeScheduler_Modern.h @@ -178,6 +178,9 @@ class RuntimeScheduler_Modern final : public RuntimeSchedulerBase { void updateRendering(); + bool performingMicrotaskCheckpoint_{false}; + void performMicrotaskCheckpoint(jsi::Runtime& runtime); + /* * Returns a time point representing the current point in time. May be called * from multiple threads. diff --git a/packages/react-native/ReactCommon/react/utils/OnScopeExit.h b/packages/react-native/ReactCommon/react/utils/OnScopeExit.h new file mode 100644 index 00000000000..ce8e907da3b --- /dev/null +++ b/packages/react-native/ReactCommon/react/utils/OnScopeExit.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +namespace facebook::react { + +template +class OnScopeExit { + public: + explicit OnScopeExit(const Lambda&& lambda) noexcept + : lambda_(std::move(lambda)) {} + + // Non-movable + OnScopeExit(const OnScopeExit&) = delete; + OnScopeExit(OnScopeExit&&) = delete; + + // Non-copyable + OnScopeExit& operator=(const OnScopeExit&) = delete; + OnScopeExit& operator=(OnScopeExit&&) = delete; + + ~OnScopeExit() noexcept { + lambda_(); + } + + private: + Lambda lambda_; +}; + +} // namespace facebook::react