Create proxy for RuntimeScheduler to allow us to use a forked version (#40875)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/40875 This introduces a proxy for RuntimeScheduler so we can select between 2 different implementations at runtime (current implementation vs. new implementation, done in D49316881). Changelog: [internal] Reviewed By: javache, sammy-SC Differential Revision: D49316880 fbshipit-source-id: 4035ed6ba641a2316f2efb7cf4a0a86270d6ae23
This commit is contained in:
Родитель
29bbab5a5a
Коммит
c0e2c68cdc
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
#include "RuntimeScheduler.h"
|
||||
#include "RuntimeScheduler_Legacy.h"
|
||||
#include "SchedulerPriorityUtils.h"
|
||||
|
||||
#include <react/renderer/debug/SystraceSection.h>
|
||||
|
@ -14,177 +15,55 @@
|
|||
|
||||
namespace facebook::react {
|
||||
|
||||
#pragma mark - Public
|
||||
|
||||
RuntimeScheduler::RuntimeScheduler(
|
||||
RuntimeExecutor runtimeExecutor,
|
||||
std::function<RuntimeSchedulerTimePoint()> now)
|
||||
: runtimeExecutor_(std::move(runtimeExecutor)), now_(std::move(now)) {}
|
||||
: runtimeSchedulerImpl_(std::make_unique<RuntimeScheduler_Legacy>(
|
||||
std::move(runtimeExecutor),
|
||||
std::move(now))) {}
|
||||
|
||||
void RuntimeScheduler::scheduleWork(RawCallback&& callback) const noexcept {
|
||||
SystraceSection s("RuntimeScheduler::scheduleWork");
|
||||
|
||||
runtimeAccessRequests_ += 1;
|
||||
|
||||
runtimeExecutor_(
|
||||
[this, callback = std::move(callback)](jsi::Runtime& runtime) {
|
||||
SystraceSection s2("RuntimeScheduler::scheduleWork callback");
|
||||
runtimeAccessRequests_ -= 1;
|
||||
callback(runtime);
|
||||
startWorkLoop(runtime);
|
||||
});
|
||||
return runtimeSchedulerImpl_->scheduleWork(std::move(callback));
|
||||
}
|
||||
|
||||
std::shared_ptr<Task> RuntimeScheduler::scheduleTask(
|
||||
SchedulerPriority priority,
|
||||
jsi::Function&& callback) noexcept {
|
||||
auto expirationTime = now_() + timeoutForSchedulerPriority(priority);
|
||||
auto task =
|
||||
std::make_shared<Task>(priority, std::move(callback), expirationTime);
|
||||
taskQueue_.push(task);
|
||||
|
||||
scheduleWorkLoopIfNecessary();
|
||||
|
||||
return task;
|
||||
return runtimeSchedulerImpl_->scheduleTask(priority, std::move(callback));
|
||||
}
|
||||
|
||||
std::shared_ptr<Task> RuntimeScheduler::scheduleTask(
|
||||
SchedulerPriority priority,
|
||||
RawCallback&& callback) noexcept {
|
||||
auto expirationTime = now_() + timeoutForSchedulerPriority(priority);
|
||||
auto task =
|
||||
std::make_shared<Task>(priority, std::move(callback), expirationTime);
|
||||
taskQueue_.push(task);
|
||||
|
||||
scheduleWorkLoopIfNecessary();
|
||||
|
||||
return task;
|
||||
return runtimeSchedulerImpl_->scheduleTask(priority, std::move(callback));
|
||||
}
|
||||
|
||||
bool RuntimeScheduler::getShouldYield() const noexcept {
|
||||
return runtimeAccessRequests_ > 0;
|
||||
return runtimeSchedulerImpl_->getShouldYield();
|
||||
}
|
||||
|
||||
bool RuntimeScheduler::getIsSynchronous() const noexcept {
|
||||
return isSynchronous_;
|
||||
return runtimeSchedulerImpl_->getIsSynchronous();
|
||||
}
|
||||
|
||||
void RuntimeScheduler::cancelTask(Task& task) noexcept {
|
||||
task.callback.reset();
|
||||
return runtimeSchedulerImpl_->cancelTask(task);
|
||||
}
|
||||
|
||||
SchedulerPriority RuntimeScheduler::getCurrentPriorityLevel() const noexcept {
|
||||
return currentPriority_;
|
||||
return runtimeSchedulerImpl_->getCurrentPriorityLevel();
|
||||
}
|
||||
|
||||
RuntimeSchedulerTimePoint RuntimeScheduler::now() const noexcept {
|
||||
return now_();
|
||||
return runtimeSchedulerImpl_->now();
|
||||
}
|
||||
|
||||
void RuntimeScheduler::executeNowOnTheSameThread(RawCallback&& callback) {
|
||||
SystraceSection s("RuntimeScheduler::executeNowOnTheSameThread");
|
||||
|
||||
runtimeAccessRequests_ += 1;
|
||||
executeSynchronouslyOnSameThread_CAN_DEADLOCK(
|
||||
runtimeExecutor_,
|
||||
[this, callback = std::move(callback)](jsi::Runtime& runtime) {
|
||||
SystraceSection s2(
|
||||
"RuntimeScheduler::executeNowOnTheSameThread callback");
|
||||
|
||||
runtimeAccessRequests_ -= 1;
|
||||
isSynchronous_ = true;
|
||||
callback(runtime);
|
||||
isSynchronous_ = false;
|
||||
});
|
||||
|
||||
// Resume work loop if needed. In synchronous mode
|
||||
// only expired tasks are executed. Tasks with lower priority
|
||||
// might be still in the queue.
|
||||
scheduleWorkLoopIfNecessary();
|
||||
return runtimeSchedulerImpl_->executeNowOnTheSameThread(std::move(callback));
|
||||
}
|
||||
|
||||
void RuntimeScheduler::callExpiredTasks(jsi::Runtime& runtime) {
|
||||
SystraceSection s("RuntimeScheduler::callExpiredTasks");
|
||||
|
||||
auto previousPriority = currentPriority_;
|
||||
try {
|
||||
while (!taskQueue_.empty()) {
|
||||
auto topPriorityTask = taskQueue_.top();
|
||||
auto now = now_();
|
||||
auto didUserCallbackTimeout = topPriorityTask->expirationTime <= now;
|
||||
|
||||
if (!didUserCallbackTimeout) {
|
||||
break;
|
||||
}
|
||||
|
||||
executeTask(runtime, topPriorityTask, didUserCallbackTimeout);
|
||||
}
|
||||
} catch (jsi::JSError& error) {
|
||||
handleFatalError(runtime, error);
|
||||
}
|
||||
|
||||
currentPriority_ = previousPriority;
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
void RuntimeScheduler::scheduleWorkLoopIfNecessary() const {
|
||||
if (!isWorkLoopScheduled_ && !isPerformingWork_) {
|
||||
isWorkLoopScheduled_ = true;
|
||||
runtimeExecutor_([this](jsi::Runtime& runtime) {
|
||||
isWorkLoopScheduled_ = false;
|
||||
startWorkLoop(runtime);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void RuntimeScheduler::startWorkLoop(jsi::Runtime& runtime) const {
|
||||
SystraceSection s("RuntimeScheduler::startWorkLoop");
|
||||
|
||||
auto previousPriority = currentPriority_;
|
||||
isPerformingWork_ = true;
|
||||
try {
|
||||
while (!taskQueue_.empty()) {
|
||||
auto topPriorityTask = taskQueue_.top();
|
||||
auto now = now_();
|
||||
auto didUserCallbackTimeout = topPriorityTask->expirationTime <= now;
|
||||
|
||||
if (!didUserCallbackTimeout && getShouldYield()) {
|
||||
// This currentTask hasn't expired, and we need to yield.
|
||||
break;
|
||||
}
|
||||
|
||||
executeTask(runtime, topPriorityTask, didUserCallbackTimeout);
|
||||
}
|
||||
} catch (jsi::JSError& error) {
|
||||
handleFatalError(runtime, error);
|
||||
}
|
||||
|
||||
currentPriority_ = previousPriority;
|
||||
isPerformingWork_ = false;
|
||||
}
|
||||
|
||||
void RuntimeScheduler::executeTask(
|
||||
jsi::Runtime& runtime,
|
||||
const std::shared_ptr<Task>& task,
|
||||
bool didUserCallbackTimeout) const {
|
||||
SystraceSection s(
|
||||
"RuntimeScheduler::executeTask",
|
||||
"priority",
|
||||
serialize(task->priority),
|
||||
"didUserCallbackTimeout",
|
||||
didUserCallbackTimeout);
|
||||
|
||||
currentPriority_ = task->priority;
|
||||
auto result = task->execute(runtime, didUserCallbackTimeout);
|
||||
|
||||
if (result.isObject() && result.getObject(runtime).isFunction(runtime)) {
|
||||
task->callback = result.getObject(runtime).getFunction(runtime);
|
||||
} else {
|
||||
if (taskQueue_.top() == task) {
|
||||
taskQueue_.pop();
|
||||
}
|
||||
}
|
||||
return runtimeSchedulerImpl_->callExpiredTasks(runtime);
|
||||
}
|
||||
|
||||
} // namespace facebook::react
|
||||
|
|
|
@ -10,18 +10,39 @@
|
|||
#include <ReactCommon/RuntimeExecutor.h>
|
||||
#include <react/renderer/runtimescheduler/RuntimeSchedulerClock.h>
|
||||
#include <react/renderer/runtimescheduler/Task.h>
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
|
||||
namespace facebook::react {
|
||||
|
||||
class RuntimeScheduler final {
|
||||
// This is a temporary abstract class for RuntimeScheduler forks to implement
|
||||
// (and use them interchangeably).
|
||||
class RuntimeSchedulerBase {
|
||||
public:
|
||||
virtual ~RuntimeSchedulerBase() = default;
|
||||
virtual void scheduleWork(RawCallback&& callback) const noexcept = 0;
|
||||
virtual void executeNowOnTheSameThread(RawCallback&& callback) = 0;
|
||||
virtual std::shared_ptr<Task> scheduleTask(
|
||||
SchedulerPriority priority,
|
||||
jsi::Function&& callback) noexcept = 0;
|
||||
virtual std::shared_ptr<Task> scheduleTask(
|
||||
SchedulerPriority priority,
|
||||
RawCallback&& callback) noexcept = 0;
|
||||
virtual void cancelTask(Task& task) noexcept = 0;
|
||||
virtual bool getShouldYield() const noexcept = 0;
|
||||
virtual bool getIsSynchronous() const noexcept = 0;
|
||||
virtual SchedulerPriority getCurrentPriorityLevel() const noexcept = 0;
|
||||
virtual RuntimeSchedulerTimePoint now() const noexcept = 0;
|
||||
virtual void callExpiredTasks(jsi::Runtime& runtime) = 0;
|
||||
};
|
||||
|
||||
// This is a proxy for RuntimeScheduler implementation, which will be selected
|
||||
// at runtime based on a feature flag.
|
||||
class RuntimeScheduler final : RuntimeSchedulerBase {
|
||||
public:
|
||||
RuntimeScheduler(
|
||||
RuntimeExecutor runtimeExecutor,
|
||||
std::function<RuntimeSchedulerTimePoint()> now =
|
||||
RuntimeSchedulerClock::now);
|
||||
|
||||
/*
|
||||
* Not copyable.
|
||||
*/
|
||||
|
@ -34,7 +55,7 @@ class RuntimeScheduler final {
|
|||
RuntimeScheduler(RuntimeScheduler&&) = delete;
|
||||
RuntimeScheduler& operator=(RuntimeScheduler&&) = delete;
|
||||
|
||||
void scheduleWork(RawCallback&& callback) const noexcept;
|
||||
void scheduleWork(RawCallback&& callback) const noexcept override;
|
||||
|
||||
/*
|
||||
* Grants access to the runtime synchronously on the caller's thread.
|
||||
|
@ -43,7 +64,7 @@ class RuntimeScheduler final {
|
|||
* by dispatching a synchronous event via event emitter in your native
|
||||
* component.
|
||||
*/
|
||||
void executeNowOnTheSameThread(RawCallback&& callback);
|
||||
void executeNowOnTheSameThread(RawCallback&& callback) override;
|
||||
|
||||
/*
|
||||
* Adds a JavaScript callback to priority queue with given priority.
|
||||
|
@ -53,11 +74,11 @@ class RuntimeScheduler final {
|
|||
*/
|
||||
std::shared_ptr<Task> scheduleTask(
|
||||
SchedulerPriority priority,
|
||||
jsi::Function&& callback) noexcept;
|
||||
jsi::Function&& callback) noexcept override;
|
||||
|
||||
std::shared_ptr<Task> scheduleTask(
|
||||
SchedulerPriority priority,
|
||||
RawCallback&& callback) noexcept;
|
||||
RawCallback&& callback) noexcept override;
|
||||
|
||||
/*
|
||||
* Cancelled task will never be executed.
|
||||
|
@ -65,7 +86,7 @@ class RuntimeScheduler final {
|
|||
* Operates on JSI object.
|
||||
* Thread synchronization must be enforced externally.
|
||||
*/
|
||||
void cancelTask(Task& task) noexcept;
|
||||
void cancelTask(Task& task) noexcept override;
|
||||
|
||||
/*
|
||||
* Return value indicates if host platform has a pending access to the
|
||||
|
@ -73,7 +94,7 @@ class RuntimeScheduler final {
|
|||
*
|
||||
* Can be called from any thread.
|
||||
*/
|
||||
bool getShouldYield() const noexcept;
|
||||
bool getShouldYield() const noexcept override;
|
||||
|
||||
/*
|
||||
* Return value informs if the current task is executed inside synchronous
|
||||
|
@ -81,14 +102,14 @@ class RuntimeScheduler final {
|
|||
*
|
||||
* Can be called from any thread.
|
||||
*/
|
||||
bool getIsSynchronous() const noexcept;
|
||||
bool getIsSynchronous() const noexcept override;
|
||||
|
||||
/*
|
||||
* Returns value of currently executed task. Designed to be called from React.
|
||||
*
|
||||
* Thread synchronization must be enforced externally.
|
||||
*/
|
||||
SchedulerPriority getCurrentPriorityLevel() const noexcept;
|
||||
SchedulerPriority getCurrentPriorityLevel() const noexcept override;
|
||||
|
||||
/*
|
||||
* Returns current monotonic time. This time is not related to wall clock
|
||||
|
@ -96,7 +117,7 @@ class RuntimeScheduler final {
|
|||
*
|
||||
* Thread synchronization must be enforced externally.
|
||||
*/
|
||||
RuntimeSchedulerTimePoint now() const noexcept;
|
||||
RuntimeSchedulerTimePoint now() const noexcept override;
|
||||
|
||||
/*
|
||||
* Expired task is a task that should have been already executed. Designed to
|
||||
|
@ -106,54 +127,12 @@ class RuntimeScheduler final {
|
|||
*
|
||||
* Thread synchronization must be enforced externally.
|
||||
*/
|
||||
void callExpiredTasks(jsi::Runtime& runtime);
|
||||
void callExpiredTasks(jsi::Runtime& runtime) override;
|
||||
|
||||
private:
|
||||
mutable std::priority_queue<
|
||||
std::shared_ptr<Task>,
|
||||
std::vector<std::shared_ptr<Task>>,
|
||||
TaskPriorityComparer>
|
||||
taskQueue_;
|
||||
|
||||
const RuntimeExecutor runtimeExecutor_;
|
||||
mutable SchedulerPriority currentPriority_{SchedulerPriority::NormalPriority};
|
||||
|
||||
/*
|
||||
* Counter indicating how many access to the runtime have been requested.
|
||||
*/
|
||||
mutable std::atomic<uint_fast8_t> runtimeAccessRequests_{0};
|
||||
|
||||
mutable std::atomic_bool isSynchronous_{false};
|
||||
|
||||
void startWorkLoop(jsi::Runtime& runtime) const;
|
||||
|
||||
/*
|
||||
* Schedules a work loop unless it has been already scheduled
|
||||
* This is to avoid unnecessary calls to `runtimeExecutor`.
|
||||
*/
|
||||
void scheduleWorkLoopIfNecessary() const;
|
||||
|
||||
void executeTask(
|
||||
jsi::Runtime& runtime,
|
||||
const std::shared_ptr<Task>& task,
|
||||
bool didUserCallbackTimeout) const;
|
||||
|
||||
/*
|
||||
* Returns a time point representing the current point in time. May be called
|
||||
* from multiple threads.
|
||||
*/
|
||||
std::function<RuntimeSchedulerTimePoint()> now_;
|
||||
|
||||
/*
|
||||
* Flag indicating if callback on JavaScript queue has been
|
||||
* scheduled.
|
||||
*/
|
||||
mutable std::atomic_bool isWorkLoopScheduled_{false};
|
||||
|
||||
/*
|
||||
* This flag is set while performing work, to prevent re-entrancy.
|
||||
*/
|
||||
mutable std::atomic_bool isPerformingWork_{false};
|
||||
// Actual implementation, stored as a unique pointer to simplify memory
|
||||
// management.
|
||||
std::unique_ptr<RuntimeSchedulerBase> runtimeSchedulerImpl_;
|
||||
};
|
||||
|
||||
} // namespace facebook::react
|
||||
|
|
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "RuntimeScheduler_Legacy.h"
|
||||
#include "SchedulerPriorityUtils.h"
|
||||
|
||||
#include <react/renderer/debug/SystraceSection.h>
|
||||
#include <utility>
|
||||
#include "ErrorUtils.h"
|
||||
|
||||
namespace facebook::react {
|
||||
|
||||
#pragma mark - Public
|
||||
|
||||
RuntimeScheduler_Legacy::RuntimeScheduler_Legacy(
|
||||
RuntimeExecutor runtimeExecutor,
|
||||
std::function<RuntimeSchedulerTimePoint()> now)
|
||||
: runtimeExecutor_(std::move(runtimeExecutor)), now_(std::move(now)) {}
|
||||
|
||||
void RuntimeScheduler_Legacy::scheduleWork(
|
||||
RawCallback&& callback) const noexcept {
|
||||
SystraceSection s("RuntimeScheduler::scheduleWork");
|
||||
|
||||
runtimeAccessRequests_ += 1;
|
||||
|
||||
runtimeExecutor_(
|
||||
[this, callback = std::move(callback)](jsi::Runtime& runtime) {
|
||||
SystraceSection s2("RuntimeScheduler::scheduleWork callback");
|
||||
runtimeAccessRequests_ -= 1;
|
||||
callback(runtime);
|
||||
startWorkLoop(runtime);
|
||||
});
|
||||
}
|
||||
|
||||
std::shared_ptr<Task> RuntimeScheduler_Legacy::scheduleTask(
|
||||
SchedulerPriority priority,
|
||||
jsi::Function&& callback) noexcept {
|
||||
auto expirationTime = now_() + timeoutForSchedulerPriority(priority);
|
||||
auto task =
|
||||
std::make_shared<Task>(priority, std::move(callback), expirationTime);
|
||||
taskQueue_.push(task);
|
||||
|
||||
scheduleWorkLoopIfNecessary();
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
std::shared_ptr<Task> RuntimeScheduler_Legacy::scheduleTask(
|
||||
SchedulerPriority priority,
|
||||
RawCallback&& callback) noexcept {
|
||||
auto expirationTime = now_() + timeoutForSchedulerPriority(priority);
|
||||
auto task =
|
||||
std::make_shared<Task>(priority, std::move(callback), expirationTime);
|
||||
taskQueue_.push(task);
|
||||
|
||||
scheduleWorkLoopIfNecessary();
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
bool RuntimeScheduler_Legacy::getShouldYield() const noexcept {
|
||||
return runtimeAccessRequests_ > 0;
|
||||
}
|
||||
|
||||
bool RuntimeScheduler_Legacy::getIsSynchronous() const noexcept {
|
||||
return isSynchronous_;
|
||||
}
|
||||
|
||||
void RuntimeScheduler_Legacy::cancelTask(Task& task) noexcept {
|
||||
task.callback.reset();
|
||||
}
|
||||
|
||||
SchedulerPriority RuntimeScheduler_Legacy::getCurrentPriorityLevel()
|
||||
const noexcept {
|
||||
return currentPriority_;
|
||||
}
|
||||
|
||||
RuntimeSchedulerTimePoint RuntimeScheduler_Legacy::now() const noexcept {
|
||||
return now_();
|
||||
}
|
||||
|
||||
void RuntimeScheduler_Legacy::executeNowOnTheSameThread(
|
||||
RawCallback&& callback) {
|
||||
SystraceSection s("RuntimeScheduler::executeNowOnTheSameThread");
|
||||
|
||||
runtimeAccessRequests_ += 1;
|
||||
executeSynchronouslyOnSameThread_CAN_DEADLOCK(
|
||||
runtimeExecutor_,
|
||||
[this, callback = std::move(callback)](jsi::Runtime& runtime) {
|
||||
SystraceSection s2(
|
||||
"RuntimeScheduler::executeNowOnTheSameThread callback");
|
||||
|
||||
runtimeAccessRequests_ -= 1;
|
||||
isSynchronous_ = true;
|
||||
callback(runtime);
|
||||
isSynchronous_ = false;
|
||||
});
|
||||
|
||||
// Resume work loop if needed. In synchronous mode
|
||||
// only expired tasks are executed. Tasks with lower priority
|
||||
// might be still in the queue.
|
||||
scheduleWorkLoopIfNecessary();
|
||||
}
|
||||
|
||||
void RuntimeScheduler_Legacy::callExpiredTasks(jsi::Runtime& runtime) {
|
||||
SystraceSection s("RuntimeScheduler::callExpiredTasks");
|
||||
|
||||
auto previousPriority = currentPriority_;
|
||||
try {
|
||||
while (!taskQueue_.empty()) {
|
||||
auto topPriorityTask = taskQueue_.top();
|
||||
auto now = now_();
|
||||
auto didUserCallbackTimeout = topPriorityTask->expirationTime <= now;
|
||||
|
||||
if (!didUserCallbackTimeout) {
|
||||
break;
|
||||
}
|
||||
|
||||
executeTask(runtime, topPriorityTask, didUserCallbackTimeout);
|
||||
}
|
||||
} catch (jsi::JSError& error) {
|
||||
handleFatalError(runtime, error);
|
||||
}
|
||||
|
||||
currentPriority_ = previousPriority;
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
void RuntimeScheduler_Legacy::scheduleWorkLoopIfNecessary() const {
|
||||
if (!isWorkLoopScheduled_ && !isPerformingWork_) {
|
||||
isWorkLoopScheduled_ = true;
|
||||
runtimeExecutor_([this](jsi::Runtime& runtime) {
|
||||
isWorkLoopScheduled_ = false;
|
||||
startWorkLoop(runtime);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void RuntimeScheduler_Legacy::startWorkLoop(jsi::Runtime& runtime) const {
|
||||
SystraceSection s("RuntimeScheduler::startWorkLoop");
|
||||
|
||||
auto previousPriority = currentPriority_;
|
||||
isPerformingWork_ = true;
|
||||
try {
|
||||
while (!taskQueue_.empty()) {
|
||||
auto topPriorityTask = taskQueue_.top();
|
||||
auto now = now_();
|
||||
auto didUserCallbackTimeout = topPriorityTask->expirationTime <= now;
|
||||
|
||||
if (!didUserCallbackTimeout && getShouldYield()) {
|
||||
// This currentTask hasn't expired, and we need to yield.
|
||||
break;
|
||||
}
|
||||
|
||||
executeTask(runtime, topPriorityTask, didUserCallbackTimeout);
|
||||
}
|
||||
} catch (jsi::JSError& error) {
|
||||
handleFatalError(runtime, error);
|
||||
}
|
||||
|
||||
currentPriority_ = previousPriority;
|
||||
isPerformingWork_ = false;
|
||||
}
|
||||
|
||||
void RuntimeScheduler_Legacy::executeTask(
|
||||
jsi::Runtime& runtime,
|
||||
const std::shared_ptr<Task>& task,
|
||||
bool didUserCallbackTimeout) const {
|
||||
SystraceSection s(
|
||||
"RuntimeScheduler::executeTask",
|
||||
"priority",
|
||||
serialize(task->priority),
|
||||
"didUserCallbackTimeout",
|
||||
didUserCallbackTimeout);
|
||||
|
||||
currentPriority_ = task->priority;
|
||||
auto result = task->execute(runtime, didUserCallbackTimeout);
|
||||
|
||||
if (result.isObject() && result.getObject(runtime).isFunction(runtime)) {
|
||||
task->callback = result.getObject(runtime).getFunction(runtime);
|
||||
} else {
|
||||
if (taskQueue_.top() == task) {
|
||||
taskQueue_.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace facebook::react
|
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
* 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 <ReactCommon/RuntimeExecutor.h>
|
||||
#include <react/renderer/runtimescheduler/RuntimeScheduler.h>
|
||||
#include <react/renderer/runtimescheduler/RuntimeSchedulerClock.h>
|
||||
#include <react/renderer/runtimescheduler/Task.h>
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
|
||||
namespace facebook::react {
|
||||
|
||||
class RuntimeScheduler_Legacy final : public RuntimeSchedulerBase {
|
||||
public:
|
||||
RuntimeScheduler_Legacy(
|
||||
RuntimeExecutor runtimeExecutor,
|
||||
std::function<RuntimeSchedulerTimePoint()> now =
|
||||
RuntimeSchedulerClock::now);
|
||||
|
||||
/*
|
||||
* Not copyable.
|
||||
*/
|
||||
RuntimeScheduler_Legacy(const RuntimeScheduler_Legacy&) = delete;
|
||||
RuntimeScheduler_Legacy& operator=(const RuntimeScheduler_Legacy&) = delete;
|
||||
|
||||
/*
|
||||
* Not movable.
|
||||
*/
|
||||
RuntimeScheduler_Legacy(RuntimeScheduler_Legacy&&) = delete;
|
||||
RuntimeScheduler_Legacy& operator=(RuntimeScheduler_Legacy&&) = delete;
|
||||
|
||||
void scheduleWork(RawCallback&& callback) const noexcept override;
|
||||
|
||||
/*
|
||||
* Grants access to the runtime synchronously on the caller's thread.
|
||||
*
|
||||
* Shouldn't be called directly. it is expected to be used
|
||||
* by dispatching a synchronous event via event emitter in your native
|
||||
* component.
|
||||
*/
|
||||
void executeNowOnTheSameThread(RawCallback&& callback) override;
|
||||
|
||||
/*
|
||||
* Adds a JavaScript callback to priority queue with given priority.
|
||||
* Triggers workloop if needed.
|
||||
*
|
||||
* Thread synchronization must be enforced externally.
|
||||
*/
|
||||
std::shared_ptr<Task> scheduleTask(
|
||||
SchedulerPriority priority,
|
||||
jsi::Function&& callback) noexcept override;
|
||||
|
||||
std::shared_ptr<Task> scheduleTask(
|
||||
SchedulerPriority priority,
|
||||
RawCallback&& callback) noexcept override;
|
||||
|
||||
/*
|
||||
* Cancelled task will never be executed.
|
||||
*
|
||||
* Operates on JSI object.
|
||||
* Thread synchronization must be enforced externally.
|
||||
*/
|
||||
void cancelTask(Task& task) noexcept override;
|
||||
|
||||
/*
|
||||
* Return value indicates if host platform has a pending access to the
|
||||
* runtime.
|
||||
*
|
||||
* Can be called from any thread.
|
||||
*/
|
||||
bool getShouldYield() const noexcept override;
|
||||
|
||||
/*
|
||||
* Return value informs if the current task is executed inside synchronous
|
||||
* block.
|
||||
*
|
||||
* Can be called from any thread.
|
||||
*/
|
||||
bool getIsSynchronous() const noexcept override;
|
||||
|
||||
/*
|
||||
* Returns value of currently executed task. Designed to be called from React.
|
||||
*
|
||||
* Thread synchronization must be enforced externally.
|
||||
*/
|
||||
SchedulerPriority getCurrentPriorityLevel() const noexcept override;
|
||||
|
||||
/*
|
||||
* Returns current monotonic time. This time is not related to wall clock
|
||||
* time.
|
||||
*
|
||||
* Thread synchronization must be enforced externally.
|
||||
*/
|
||||
RuntimeSchedulerTimePoint now() const noexcept override;
|
||||
|
||||
/*
|
||||
* Expired task is a task that should have been already executed. Designed to
|
||||
* be called in the event pipeline after an event is dispatched to React.
|
||||
* React may schedule events with immediate priority which need to be handled
|
||||
* before the next event is sent to React.
|
||||
*
|
||||
* Thread synchronization must be enforced externally.
|
||||
*/
|
||||
void callExpiredTasks(jsi::Runtime& runtime) override;
|
||||
|
||||
private:
|
||||
mutable std::priority_queue<
|
||||
std::shared_ptr<Task>,
|
||||
std::vector<std::shared_ptr<Task>>,
|
||||
TaskPriorityComparer>
|
||||
taskQueue_;
|
||||
|
||||
const RuntimeExecutor runtimeExecutor_;
|
||||
mutable SchedulerPriority currentPriority_{SchedulerPriority::NormalPriority};
|
||||
|
||||
/*
|
||||
* Counter indicating how many access to the runtime have been requested.
|
||||
*/
|
||||
mutable std::atomic<uint_fast8_t> runtimeAccessRequests_{0};
|
||||
|
||||
mutable std::atomic_bool isSynchronous_{false};
|
||||
|
||||
void startWorkLoop(jsi::Runtime& runtime) const;
|
||||
|
||||
/*
|
||||
* Schedules a work loop unless it has been already scheduled
|
||||
* This is to avoid unnecessary calls to `runtimeExecutor`.
|
||||
*/
|
||||
void scheduleWorkLoopIfNecessary() const;
|
||||
|
||||
void executeTask(
|
||||
jsi::Runtime& runtime,
|
||||
const std::shared_ptr<Task>& task,
|
||||
bool didUserCallbackTimeout) const;
|
||||
|
||||
/*
|
||||
* Returns a time point representing the current point in time. May be called
|
||||
* from multiple threads.
|
||||
*/
|
||||
std::function<RuntimeSchedulerTimePoint()> now_;
|
||||
|
||||
/*
|
||||
* Flag indicating if callback on JavaScript queue has been
|
||||
* scheduled.
|
||||
*/
|
||||
mutable std::atomic_bool isWorkLoopScheduled_{false};
|
||||
|
||||
/*
|
||||
* This flag is set while performing work, to prevent re-entrancy.
|
||||
*/
|
||||
mutable std::atomic_bool isPerformingWork_{false};
|
||||
};
|
||||
|
||||
} // namespace facebook::react
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
namespace facebook::react {
|
||||
|
||||
class RuntimeScheduler;
|
||||
class RuntimeScheduler_Legacy;
|
||||
class TaskPriorityComparer;
|
||||
|
||||
using RawCallback = std::function<void(jsi::Runtime&)>;
|
||||
|
@ -33,7 +33,7 @@ struct Task final : public jsi::NativeState {
|
|||
std::chrono::steady_clock::time_point expirationTime);
|
||||
|
||||
private:
|
||||
friend RuntimeScheduler;
|
||||
friend RuntimeScheduler_Legacy;
|
||||
friend TaskPriorityComparer;
|
||||
|
||||
SchedulerPriority priority;
|
||||
|
|
Загрузка…
Ссылка в новой задаче