jiuyiUniapp/service/node_modules/react-native/ReactCommon/react/nativemodule/idlecallbacks/NativeIdleCallbacks.cpp

169 lines
5.3 KiB
C++

/*
* 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 "NativeIdleCallbacks.h"
#include <react/renderer/runtimescheduler/RuntimeScheduler.h>
#include <react/renderer/runtimescheduler/RuntimeSchedulerBinding.h>
#include <react/renderer/runtimescheduler/Task.h>
#include <chrono>
#include <utility>
#ifdef RN_DISABLE_OSS_PLUGIN_HEADER
#include "Plugins.h"
#endif
std::shared_ptr<facebook::react::TurboModule> NativeIdleCallbacksModuleProvider(
std::shared_ptr<facebook::react::CallInvoker> jsInvoker) {
return std::make_shared<facebook::react::NativeIdleCallbacks>(
std::move(jsInvoker));
}
namespace facebook::react {
namespace {
class IdleTaskRef : public jsi::NativeState {
public:
IdleTaskRef(std::shared_ptr<Task> task) : task(std::move(task)) {}
std::shared_ptr<Task> task;
};
jsi::Function makeTimeRemainingFunction(
jsi::Runtime& runtime,
std::shared_ptr<RuntimeScheduler> runtimeScheduler,
RuntimeSchedulerTimePoint deadline) {
return jsi::Function::createFromHostFunction(
runtime,
jsi::PropNameID::forAscii(runtime, "timeRemaining"),
0,
[runtimeScheduler, deadline, expired = false](
jsi::Runtime& runtime,
const jsi::Value& /* unused */,
const jsi::Value* /* unused */,
size_t /* unused */) mutable {
double remainingTime = 0;
// No need to access the runtime scheduler if this idle callback expired
// already.
if (!expired) {
if (runtimeScheduler->getShouldYield()) {
expired = true;
} else {
auto now = runtimeScheduler->now();
remainingTime = std::max(
static_cast<double>(
std::chrono::duration_cast<std::chrono::milliseconds>(
deadline - now)
.count()),
0.0);
if (remainingTime == 0) {
expired = true;
}
}
}
return jsi::Value(runtime, remainingTime);
});
}
} // namespace
NativeIdleCallbacks::NativeIdleCallbacks(std::shared_ptr<CallInvoker> jsInvoker)
: NativeIdleCallbacksCxxSpec(std::move(jsInvoker)) {}
CallbackHandle NativeIdleCallbacks::requestIdleCallback(
jsi::Runtime& runtime,
SyncCallback<void(jsi::Object)>&& userCallback,
std::optional<NativeRequestIdleCallbackOptions> options) {
auto binding = RuntimeSchedulerBinding::getBinding(runtime);
auto runtimeScheduler = binding->getRuntimeScheduler();
// handle timeout parameter
std::optional<RuntimeSchedulerTimeout> timeout;
std::optional<RuntimeSchedulerTimePoint> expirationTime;
if (options.has_value() && options.value().timeout.has_value()) {
auto userTimeout = (options.value().timeout.value());
if (userTimeout > 0) {
timeout = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::duration<double, std::milli>(userTimeout));
expirationTime = runtimeScheduler->now() + timeout.value();
}
}
auto userCallbackShared = std::make_shared<SyncCallback<void(jsi::Object)>>(
std::move(userCallback));
auto wrappedCallback = [runtimeScheduler, expirationTime, userCallbackShared](
jsi::Runtime& runtime) -> void {
// This implementation gives each idle callback a 50ms deadline, instead of
// being shared by all idle callbacks. This is ok because we don't really
// have idle periods, and if a higher priority task comes in while we're
// executing an idle callback, we don't execute any more idle callbacks and
// we interrupt the current one. The general outcome should be the same.
auto executionStartTime = runtimeScheduler->now();
auto deadline = executionStartTime + std::chrono::milliseconds(50);
auto didTimeout = expirationTime.has_value()
? executionStartTime > expirationTime
: false;
jsi::Object idleDeadline{runtime};
idleDeadline.setProperty(runtime, "didTimeout", didTimeout);
idleDeadline.setProperty(
runtime,
"timeRemaining",
makeTimeRemainingFunction(runtime, runtimeScheduler, deadline));
userCallbackShared->call(std::move(idleDeadline));
};
std::shared_ptr<Task> task;
if (timeout.has_value()) {
task = runtimeScheduler->scheduleIdleTask(
std::move(wrappedCallback), timeout.value());
} else {
task = runtimeScheduler->scheduleIdleTask(std::move(wrappedCallback));
}
if (task == nullptr) {
throw jsi::JSError(
runtime,
"requestIdleCallback is not supported in legacy runtime scheduler");
}
jsi::Object taskHandle{runtime};
auto taskNativeState = std::make_shared<IdleTaskRef>(task);
taskHandle.setNativeState(runtime, std::move(taskNativeState));
return taskHandle;
}
void NativeIdleCallbacks::cancelIdleCallback(
jsi::Runtime& runtime,
jsi::Object handle) {
auto binding = RuntimeSchedulerBinding::getBinding(runtime);
auto runtimeScheduler = binding->getRuntimeScheduler();
if (!handle.hasNativeState(runtime)) {
return;
}
auto taskHandle =
std::dynamic_pointer_cast<IdleTaskRef>(handle.getNativeState(runtime));
if (!taskHandle) {
return;
}
runtimeScheduler->cancelTask(*taskHandle->task);
}
} // namespace facebook::react