Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Register globals for idleCallback #44216

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -125,6 +125,9 @@ ReactInstance::ReactInstance(
std::function<void(jsi::Runtime & runtime)>&& callback) {
runtimeScheduler->scheduleWork(std::move(callback));
});


bufferedRuntimeScheduler_ = std::make_shared<RuntimeScheduler>(this->getBufferedRuntimeExecutor());
}

void ReactInstance::unregisterFromInspector() {
Expand Down Expand Up @@ -161,13 +164,16 @@ RuntimeExecutor ReactInstance::getBufferedRuntimeExecutor() noexcept {
};
}

// TODO(T184010230): Should the RuntimeScheduler returned from this method be
// buffered?
std::shared_ptr<RuntimeScheduler>
ReactInstance::getRuntimeScheduler() noexcept {
return runtimeScheduler_;
}

std::shared_ptr<RuntimeScheduler>
ReactInstance::getBufferedRuntimeScheduler() noexcept {
return bufferedRuntimeScheduler_;
}

namespace {

// Copied from JSIExecutor.cpp
Expand Down
Expand Up @@ -42,6 +42,8 @@ class ReactInstance final : private jsinspector_modern::InstanceTargetDelegate {
RuntimeExecutor getBufferedRuntimeExecutor() noexcept;

std::shared_ptr<RuntimeScheduler> getRuntimeScheduler() noexcept;

std::shared_ptr<RuntimeScheduler> getBufferedRuntimeScheduler() noexcept;

struct JSRuntimeFlags {
bool isProfiling = false;
Expand Down Expand Up @@ -80,6 +82,7 @@ class ReactInstance final : private jsinspector_modern::InstanceTargetDelegate {
std::shared_ptr<TimerManager> timerManager_;
std::unordered_map<std::string, std::shared_ptr<CallableModule>> modules_;
std::shared_ptr<RuntimeScheduler> runtimeScheduler_;
std::shared_ptr<RuntimeScheduler> bufferedRuntimeScheduler_;
std::shared_ptr<JsErrorHandler> jsErrorHandler_;

jsinspector_modern::InstanceTarget* inspectorTarget_{nullptr};
Expand Down
93 changes: 93 additions & 0 deletions packages/react-native/ReactCommon/react/runtime/TimerManager.cpp
Expand Up @@ -8,6 +8,8 @@
#include "TimerManager.h"

#include <cxxreact/SystraceSection.h>
#include <react/renderer/runtimescheduler/RuntimeScheduler.h>
#include <chrono>
#include <utility>

namespace facebook::react {
Expand All @@ -21,6 +23,11 @@ void TimerManager::setRuntimeExecutor(
runtimeExecutor_ = runtimeExecutor;
}

void TimerManager::setRuntimeScheduler(
std::weak_ptr<RuntimeScheduler> runtimeScheduler) noexcept {
runtimeScheduler_ = runtimeScheduler;
}

std::shared_ptr<TimerHandle> TimerManager::createReactNativeMicrotask(
jsi::Function&& callback,
std::vector<jsi::Value>&& args) {
Expand Down Expand Up @@ -145,6 +152,21 @@ void TimerManager::callTimer(uint32_t timerID) {
});
}

std::shared_ptr<TimerHandle> TimerManager::createIdleCallback(
jsi::Function&& callback) {
return nullptr;
}

std::shared_ptr<TimerHandle> TimerManager::createIdleCallbackWithTimeout(
jsi::Function&& callback,
int32_t timeout) {
return nullptr;
}

void TimerManager::clearIdleCallback(
jsi::Runtime& runtime,
std::shared_ptr<TimerHandle> idleCallbackHandle) {}

void TimerManager::attachGlobals(jsi::Runtime& runtime) {
// Install host functions for timers.
// TODO (T45786383): Add missing timer functions from JSTimers
Expand Down Expand Up @@ -410,6 +432,77 @@ void TimerManager::attachGlobals(jsi::Runtime& runtime) {
deleteTimer(rt, host);
return jsi::Value::undefined();
}));

runtime.global().setProperty(
runtime,
"requestIdleCallback",
jsi::Function::createFromHostFunction(
runtime,
jsi::PropNameID::forAscii(runtime, "requestIdleCallback"),
2, // callback, options
[this](
jsi::Runtime& rt,
const jsi::Value& /*thisVal*/,
const jsi::Value* args,
size_t count) {
if (count < 0) {
throw jsi::JSError(
rt,
"requestIdleCallback must be called with at least a callback)");
}

if (!args[0].isObject() || !args[0].asObject(rt).isFunction(rt)) {
throw jsi::JSError(
rt,
"The first argument to requestIdleCallback must be a function.");
}

auto callback = args[0].getObject(rt).getFunction(rt);

if (count >= 2) {
if (args[1].isNull() ||
(!args[1].isNull() && !args[1].isObject())) {
throw jsi::JSError(
rt,
"The second argument of requestIdleCallback, if provided, must be an object");
}
auto options = args[1].asObject(rt);
if (!options.hasProperty(rt, "timeout")) {
throw jsi::JSError(
rt,
"The second argument of requestIdleCallback must have a timeout property");
}
auto timeout = options.getProperty(rt, "timeout").asNumber();
auto handle =
createIdleCallbackWithTimeout(std::move(callback), timeout);
return jsi::Object::createFromHostObject(rt, handle);
}

auto handle = createIdleCallback(std::move(callback));
return jsi::Object::createFromHostObject(rt, handle);
}));

runtime.global().setProperty(
runtime,
"cancelIdleCallback",
jsi::Function::createFromHostFunction(
runtime,
jsi::PropNameID::forAscii(runtime, "cancelIdleCallback"),
1, // idleCallbackID
[this](
jsi::Runtime& rt,
const jsi::Value& /*thisVal*/,
const jsi::Value* args,
size_t count) {
if (count > 0 && args[0].isObject() &&
args[0].asObject(rt).isHostObject<TimerHandle>(rt)) {
std::shared_ptr<TimerHandle> host =
args[0].asObject(rt).asHostObject<TimerHandle>(rt);
clearIdleCallback(rt, host);
}

return jsi::Value::undefined();
}));
}

} // namespace facebook::react
16 changes: 16 additions & 0 deletions packages/react-native/ReactCommon/react/runtime/TimerManager.h
Expand Up @@ -16,6 +16,8 @@

namespace facebook::react {

class RuntimeScheduler;

/*
* A HostObject subclass representing the result of a setTimeout call.
* Can be used as an argument to clearTimeout.
Expand Down Expand Up @@ -65,6 +67,9 @@ class TimerManager {

void setRuntimeExecutor(RuntimeExecutor runtimeExecutor) noexcept;

void setRuntimeScheduler(
std::weak_ptr<RuntimeScheduler> runtimeScheduler) noexcept;

void callReactNativeMicrotasks(jsi::Runtime& runtime);

void callTimer(uint32_t);
Expand Down Expand Up @@ -96,7 +101,18 @@ class TimerManager {
jsi::Runtime& runtime,
std::shared_ptr<TimerHandle> handle);

std::shared_ptr<TimerHandle> createIdleCallback(jsi::Function&& callback);

std::shared_ptr<TimerHandle> createIdleCallbackWithTimeout(
jsi::Function&& callback,
int32_t timeout);

void clearIdleCallback(
jsi::Runtime& runtime,
std::shared_ptr<TimerHandle> idleCallbackHandle);

RuntimeExecutor runtimeExecutor_;
std::weak_ptr<RuntimeScheduler> runtimeScheduler_;
std::unique_ptr<PlatformTimerRegistry> platformTimerRegistry_;

// A map (id => callback func) of the currently active JS timers
Expand Down
Expand Up @@ -144,7 +144,7 @@ - (void)callFunctionOnJSModule:(NSString *)moduleName method:(NSString *)method
{
if (_valid) {
_reactInstance->callFunctionOnModule(
[moduleName UTF8String], [method UTF8String], convertIdToFollyDynamic(args ?: @[]));
[moduleName UTF8String], [method UTF8String], convertIdToFollyDynamic(args ? args : @[]));
}
}

Expand Down Expand Up @@ -256,6 +256,8 @@ - (void)_start
RuntimeExecutor bufferedRuntimeExecutor = _reactInstance->getBufferedRuntimeExecutor();
timerManager->setRuntimeExecutor(bufferedRuntimeExecutor);

timerManager->setRuntimeScheduler(std::weak_ptr<RuntimeScheduler>(_reactInstance->getBufferedRuntimeScheduler()));

auto jsCallInvoker = make_shared<BridgelessJSCallInvoker>(bufferedRuntimeExecutor);
RCTBridgeProxy *bridgeProxy =
[[RCTBridgeProxy alloc] initWithViewRegistry:_bridgeModuleDecorator.viewRegistry_DEPRECATED
Expand Down