diff --git a/packages/react-native/ReactCommon/react/runtime/TimerManager.cpp b/packages/react-native/ReactCommon/react/runtime/TimerManager.cpp index 3ebf425447eeaf..1f4cade5f85653 100644 --- a/packages/react-native/ReactCommon/react/runtime/TimerManager.cpp +++ b/packages/react-native/ReactCommon/react/runtime/TimerManager.cpp @@ -8,10 +8,26 @@ #include "TimerManager.h" #include +#include #include namespace facebook::react { +namespace { +inline const char* getTimerSourceName(TimerSource source) { + switch (source) { + case TimerSource::Unknown: + return "unknown"; + case TimerSource::SetTimeout: + return "setTimeout"; + case TimerSource::SetInterval: + return "setInterval"; + case TimerSource::RequestAnimationFrame: + return "requestAnimationFrame"; + } +} +} // namespace + TimerManager::TimerManager( std::unique_ptr platformTimerRegistry) noexcept : platformTimerRegistry_(std::move(platformTimerRegistry)) {} @@ -59,14 +75,28 @@ void TimerManager::callReactNativeMicrotasks(jsi::Runtime& runtime) { TimerHandle TimerManager::createTimer( jsi::Function&& callback, std::vector&& args, - double delay) { + double delay, + TimerSource source) { // Get the id for the callback. TimerHandle timerID = timerIndex_++; + + SystraceSection s( + "TimerManager::createTimer", + "id", + timerID, + "type", + getTimerSourceName(source), + "delay", + delay); + timers_.emplace( std::piecewise_construct, std::forward_as_tuple(timerID), std::forward_as_tuple( - std::move(callback), std::move(args), /* repeat */ false)); + std::move(callback), + std::move(args), + /* repeat */ false, + source)); platformTimerRegistry_->createTimer(timerID, delay); @@ -76,14 +106,25 @@ TimerHandle TimerManager::createTimer( TimerHandle TimerManager::createRecurringTimer( jsi::Function&& callback, std::vector&& args, - double delay) { + double delay, + TimerSource source) { // Get the id for the callback. TimerHandle timerID = timerIndex_++; + + SystraceSection s( + "TimerManager::createRecurringTimer", + "id", + timerID, + "type", + getTimerSourceName(source), + "delay", + delay); + timers_.emplace( std::piecewise_construct, std::forward_as_tuple(timerID), std::forward_as_tuple( - std::move(callback), std::move(args), /* repeat */ true)); + std::move(callback), std::move(args), /* repeat */ true, source)); platformTimerRegistry_->createRecurringTimer(timerID, delay); @@ -130,11 +171,20 @@ void TimerManager::deleteRecurringTimer( void TimerManager::callTimer(TimerHandle timerHandle) { runtimeExecutor_([this, timerHandle](jsi::Runtime& runtime) { - SystraceSection s("TimerManager::callTimer"); auto it = timers_.find(timerHandle); if (it != timers_.end()) { - bool repeats = it->second.repeat; - it->second.invoke(runtime); + auto& timerCallback = it->second; + bool repeats = timerCallback.repeat; + + { + SystraceSection s( + "TimerManager::callTimer", + "id", + timerHandle, + "type", + getTimerSourceName(timerCallback.source)); + timerCallback.invoke(runtime); + } if (!repeats) { // Invoking a timer has the potential to delete it. Do not re-use the @@ -148,60 +198,64 @@ void TimerManager::callTimer(TimerHandle timerHandle) { void TimerManager::attachGlobals(jsi::Runtime& runtime) { // Install host functions for timers. // TODO (T45786383): Add missing timer functions from JSTimers - // TODO (T96212789): Remove when JSVM microtask queue is used everywhere in - // bridgeless mode. This is being overwritten in JS in that case. - runtime.global().setProperty( - runtime, - "setImmediate", - jsi::Function::createFromHostFunction( - runtime, - jsi::PropNameID::forAscii(runtime, "setImmediate"), - 2, // Function, ...args - [this]( - jsi::Runtime& rt, - const jsi::Value& thisVal, - const jsi::Value* args, - size_t count) { - if (count == 0) { - throw jsi::JSError( - rt, - "setImmediate must be called with at least one argument (a function to call)"); - } - - if (!args[0].isObject() || !args[0].asObject(rt).isFunction(rt)) { - throw jsi::JSError( - rt, "The first argument to setImmediate must be a function."); - } - auto callback = args[0].getObject(rt).getFunction(rt); - // Package up the remaining argument values into one place. - std::vector moreArgs; - for (size_t extraArgNum = 1; extraArgNum < count; extraArgNum++) { - moreArgs.emplace_back(rt, args[extraArgNum]); - } - - return createReactNativeMicrotask( - std::move(callback), std::move(moreArgs)); - })); - - runtime.global().setProperty( - runtime, - "clearImmediate", - jsi::Function::createFromHostFunction( - runtime, - jsi::PropNameID::forAscii(runtime, "clearImmediate"), - 1, // handle - [this]( - jsi::Runtime& rt, - const jsi::Value& thisVal, - const jsi::Value* args, - size_t count) { - if (count > 0 && args[0].isNumber()) { - auto handle = (TimerHandle)args[0].asNumber(); - deleteReactNativeMicrotask(rt, handle); - } - return jsi::Value::undefined(); - })); + // Ensure that we don't define `setImmediate` and `clearImmediate` if + // microtasks are enabled (as we polyfill them using `queueMicrotask` then). + if (!ReactNativeFeatureFlags::enableMicrotasks()) { + runtime.global().setProperty( + runtime, + "setImmediate", + jsi::Function::createFromHostFunction( + runtime, + jsi::PropNameID::forAscii(runtime, "setImmediate"), + 2, // Function, ...args + [this]( + jsi::Runtime& rt, + const jsi::Value& thisVal, + const jsi::Value* args, + size_t count) { + if (count == 0) { + throw jsi::JSError( + rt, + "setImmediate must be called with at least one argument (a function to call)"); + } + + if (!args[0].isObject() || !args[0].asObject(rt).isFunction(rt)) { + throw jsi::JSError( + rt, + "The first argument to setImmediate must be a function."); + } + auto callback = args[0].getObject(rt).getFunction(rt); + + // Package up the remaining argument values into one place. + std::vector moreArgs; + for (size_t extraArgNum = 1; extraArgNum < count; extraArgNum++) { + moreArgs.emplace_back(rt, args[extraArgNum]); + } + + return createReactNativeMicrotask( + std::move(callback), std::move(moreArgs)); + })); + + runtime.global().setProperty( + runtime, + "clearImmediate", + jsi::Function::createFromHostFunction( + runtime, + jsi::PropNameID::forAscii(runtime, "clearImmediate"), + 1, // handle + [this]( + jsi::Runtime& rt, + const jsi::Value& thisVal, + const jsi::Value* args, + size_t count) { + if (count > 0 && args[0].isNumber()) { + auto handle = (TimerHandle)args[0].asNumber(); + deleteReactNativeMicrotask(rt, handle); + } + return jsi::Value::undefined(); + })); + } runtime.global().setProperty( runtime, @@ -241,7 +295,11 @@ void TimerManager::attachGlobals(jsi::Runtime& runtime) { moreArgs.emplace_back(rt, args[extraArgNum]); } - return createTimer(std::move(callback), std::move(moreArgs), delay); + return createTimer( + std::move(callback), + std::move(moreArgs), + delay, + TimerSource::SetTimeout); })); runtime.global().setProperty( @@ -296,7 +354,10 @@ void TimerManager::attachGlobals(jsi::Runtime& runtime) { } return createRecurringTimer( - std::move(callback), std::move(moreArgs), delay); + std::move(callback), + std::move(moreArgs), + delay, + TimerSource::SetInterval); })); runtime.global().setProperty( @@ -365,7 +426,8 @@ void TimerManager::attachGlobals(jsi::Runtime& runtime) { return createTimer( std::move(callback), std::vector(), - /* delay */ 0); + /* delay */ 0, + TimerSource::RequestAnimationFrame); })); runtime.global().setProperty( diff --git a/packages/react-native/ReactCommon/react/runtime/TimerManager.h b/packages/react-native/ReactCommon/react/runtime/TimerManager.h index 9743087e80c897..879ba30430a18f 100644 --- a/packages/react-native/ReactCommon/react/runtime/TimerManager.h +++ b/packages/react-native/ReactCommon/react/runtime/TimerManager.h @@ -18,6 +18,13 @@ namespace facebook::react { using TimerHandle = int; +enum class TimerSource { + Unknown, + SetTimeout, + SetInterval, + RequestAnimationFrame +}; + /* * Wraps a jsi::Function to make it copyable so we can pass it into a lambda. */ @@ -25,10 +32,12 @@ struct TimerCallback { TimerCallback( jsi::Function callback, std::vector args, - bool repeat) + bool repeat, + TimerSource source = TimerSource::Unknown) : callback_(std::move(callback)), args_(std::move(args)), - repeat(repeat) {} + repeat(repeat), + source(source) {} void invoke(jsi::Runtime& runtime) { callback_.call(runtime, args_.data(), args_.size()); @@ -37,6 +46,7 @@ struct TimerCallback { jsi::Function callback_; const std::vector args_; bool repeat; + TimerSource source; }; class TimerManager { @@ -62,14 +72,16 @@ class TimerManager { TimerHandle createTimer( jsi::Function&& callback, std::vector&& args, - double delay); + double delay, + TimerSource source = TimerSource::Unknown); void deleteTimer(jsi::Runtime& runtime, TimerHandle handle); TimerHandle createRecurringTimer( jsi::Function&& callback, std::vector&& args, - double delay); + double delay, + TimerSource source = TimerSource::Unknown); void deleteRecurringTimer(jsi::Runtime& runtime, TimerHandle handle);