Skip to content

Make processingStartTime and processingEndTime optional #51390

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

Open
wants to merge 5 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <react/bridging/Error.h>
#include <react/bridging/EventEmitter.h>
#include <react/bridging/Function.h>
#include <react/bridging/HighResTimeStamp.h>
#include <react/bridging/Number.h>
#include <react/bridging/Object.h>
#include <react/bridging/Promise.h>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@ add_library(react_bridging OBJECT ${react_bridging_SRC})

target_include_directories(react_bridging PUBLIC ${REACT_COMMON_DIR})

target_link_libraries(react_bridging jsi callinvoker)
target_link_libraries(react_bridging jsi callinvoker react_timing)
target_compile_reactnative_options(react_bridging PRIVATE "ReactNative")
target_compile_options(react_bridging PRIVATE -Wpedantic)
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* 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 <react/bridging/Base.h>
#include <react/timing/primitives.h>

namespace facebook::react {

template <>
struct Bridging<HighResTimeStamp> {
static HighResTimeStamp fromJs(
jsi::Runtime& /*rt*/,
const jsi::Value& jsiValue) {
return HighResTimeStamp::fromDOMHighResTimeStamp(jsiValue.asNumber());
}

static double toJs(jsi::Runtime& /*rt*/, const HighResTimeStamp& value) {
return value.toDOMHighResTimeStamp();
}
};

template <>
struct Bridging<HighResDuration> {
static HighResDuration fromJs(
jsi::Runtime& /*rt*/,
const jsi::Value& jsiValue) {
return HighResDuration::fromDOMHighResTimeStamp(jsiValue.asNumber());
}

static double toJs(jsi::Runtime& /*rt*/, const HighResDuration& value) {
return value.toDOMHighResTimeStamp();
}
};

} // namespace facebook::react
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,13 @@ TEST_F(BridgingTest, supportTest) {
EXPECT_FALSE((bridging::supportsFromJs<jsi::Function, jsi::Array>));
EXPECT_FALSE((bridging::supportsFromJs<jsi::Function, jsi::Array&>));

// Ensure we can create HighResTimeStamp and HighResDuration from JSI
// values.
EXPECT_TRUE((bridging::supportsFromJs<HighResTimeStamp, jsi::Value>));
EXPECT_TRUE((bridging::supportsFromJs<HighResTimeStamp, jsi::Value&>));
EXPECT_TRUE((bridging::supportsFromJs<HighResDuration, jsi::Value>));
EXPECT_TRUE((bridging::supportsFromJs<HighResDuration, jsi::Value&>));

// Ensure we can convert some basic types to JSI values.
EXPECT_TRUE((bridging::supportsToJs<bool>));
EXPECT_TRUE((bridging::supportsToJs<int>));
Expand All @@ -677,6 +684,11 @@ TEST_F(BridgingTest, supportTest) {
EXPECT_FALSE((bridging::supportsToJs<double, jsi::Object>));
EXPECT_FALSE((bridging::supportsToJs<std::string, jsi::Object>));
EXPECT_FALSE((bridging::supportsToJs<std::vector<int>, jsi::Function>));

// Ensure we can convert HighResTimeStamp and HighResDuration to
// DOMHighResTimeStamp (double).
EXPECT_TRUE((bridging::supportsToJs<HighResTimeStamp, double>));
EXPECT_TRUE((bridging::supportsToJs<HighResDuration, double>));
}

TEST_F(BridgingTest, dynamicTest) {
Expand Down Expand Up @@ -765,4 +777,22 @@ TEST_F(BridgingTest, dynamicTest) {
EXPECT_TRUE(undefinedFromJsResult.isNull());
}

TEST_F(BridgingTest, highResTimeStampTest) {
HighResTimeStamp timestamp = HighResTimeStamp::now();
EXPECT_EQ(
timestamp,
bridging::fromJs<HighResTimeStamp>(
rt, bridging::toJs(rt, timestamp), invoker));

HighResDuration duration = HighResDuration::fromNanoseconds(1);
EXPECT_EQ(
duration,
bridging::fromJs<HighResDuration>(
rt, bridging::toJs(rt, duration), invoker));

EXPECT_EQ(1.0, bridging::toJs(rt, HighResDuration::fromNanoseconds(1e6)));
EXPECT_EQ(
1.000001, bridging::toJs(rt, HighResDuration::fromNanoseconds(1e6 + 1)));
}

} // namespace facebook::react
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
#include <react/renderer/runtimescheduler/RuntimeScheduler.h>
#include <react/renderer/runtimescheduler/RuntimeSchedulerBinding.h>
#include <react/renderer/runtimescheduler/Task.h>
#include <chrono>
#include <react/timing/primitives.h>
#include <utility>

#ifdef RN_DISABLE_OSS_PLUGIN_HEADER
Expand All @@ -36,7 +36,7 @@ class IdleTaskRef : public jsi::NativeState {
jsi::Function makeTimeRemainingFunction(
jsi::Runtime& runtime,
std::shared_ptr<RuntimeScheduler> runtimeScheduler,
RuntimeSchedulerTimePoint deadline) {
HighResTimeStamp deadline) {
return jsi::Function::createFromHostFunction(
runtime,
jsi::PropNameID::forAscii(runtime, "timeRemaining"),
Expand All @@ -55,13 +55,8 @@ jsi::Function makeTimeRemainingFunction(
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);
auto diff = deadline - now;
remainingTime = std::max(diff.toDOMHighResTimeStamp(), 0.0);

if (remainingTime == 0) {
expired = true;
Expand All @@ -86,15 +81,13 @@ CallbackHandle NativeIdleCallbacks::requestIdleCallback(
auto runtimeScheduler = binding->getRuntimeScheduler();

// handle timeout parameter
std::optional<RuntimeSchedulerTimeout> timeout;
std::optional<RuntimeSchedulerTimePoint> expirationTime;
std::optional<HighResDuration> timeout;
std::optional<HighResTimeStamp> 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();
HighResDuration userTimeout = options.value().timeout.value();
if (userTimeout > HighResDuration::zero()) {
expirationTime = runtimeScheduler->now() + userTimeout;
}
}

Expand All @@ -110,7 +103,7 @@ CallbackHandle NativeIdleCallbacks::requestIdleCallback(
// we interrupt the current one. The general outcome should be the same.

auto executionStartTime = runtimeScheduler->now();
auto deadline = executionStartTime + std::chrono::milliseconds(50);
auto deadline = executionStartTime + HighResDuration::fromMilliseconds(50);
auto didTimeout = expirationTime.has_value()
? executionStartTime > expirationTime
: false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ namespace facebook::react {
using CallbackHandle = jsi::Object;

using NativeRequestIdleCallbackOptions =
NativeIdleCallbacksRequestIdleCallbackOptions<std::optional<double>>;
NativeIdleCallbacksRequestIdleCallbackOptions<
std::optional<HighResDuration>>;

template <>
struct Bridging<NativeRequestIdleCallbackOptions>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

#include <react/renderer/core/EventTarget.h>
#include <react/timing/primitives.h>

#include <optional>
#include <string_view>

namespace facebook::react {
Expand All @@ -32,8 +34,8 @@ class EventLogger {
virtual EventTag onEventStart(
std::string_view name,
SharedEventTarget target,
DOMHighResTimeStamp eventStartTimeStamp =
DOM_HIGH_RES_TIME_STAMP_UNSET) = 0;
std::optional<DOMHighResTimeStamp> eventStartTimeStamp =
std::nullopt) = 0;

/*
* Called when event starts getting dispatched (processed by the handlers, if
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#pragma once

#include <memory>
#include <optional>
#include <string>

#include <react/renderer/core/EventLogger.h>
Expand Down Expand Up @@ -86,7 +87,7 @@ struct RawEvent {
// The client may specify a platform-specific timestamp for the event start
// time, for example when MotionEvent was triggered on the Android native
// side.
DOMHighResTimeStamp eventStartTimeStamp{DOM_HIGH_RES_TIME_STAMP_UNSET};
std::optional<DOMHighResTimeStamp> eventStartTimeStamp = std::nullopt;
};

} // namespace facebook::react
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class MockEventLogger : public EventLogger {
EventTag onEventStart(
std::string_view /*name*/,
SharedEventTarget /*target*/,
DOMHighResTimeStamp /*eventStartTimeStamp*/) override {
std::optional<DOMHighResTimeStamp> /*eventStartTimeStamp*/) override {
return EMPTY_EVENT_TAG;
}
void onEventProcessingStart(EventTag /*tag*/) override {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ add_library(react_renderer_observers_events OBJECT ${react_renderer_observers_ev

target_include_directories(react_renderer_observers_events PUBLIC ${REACT_COMMON_DIR})
target_link_libraries(react_renderer_observers_events
react_debug
react_performance_timeline
react_timing
react_renderer_core
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@

#include "EventPerformanceLogger.h"

#include <react/debug/react_native_assert.h>
#include <react/featureflags/ReactNativeFeatureFlags.h>
#include <react/timing/primitives.h>

#include <unordered_map>

namespace facebook::react {
Expand Down Expand Up @@ -103,7 +105,7 @@ EventPerformanceLogger::EventPerformanceLogger(
EventTag EventPerformanceLogger::onEventStart(
std::string_view name,
SharedEventTarget target,
DOMHighResTimeStamp eventStartTimeStamp) {
std::optional<DOMHighResTimeStamp> eventStartTimeStamp) {
auto performanceEntryReporter = performanceEntryReporter_.lock();
if (performanceEntryReporter == nullptr) {
return EMPTY_EVENT_TAG;
Expand All @@ -121,13 +123,13 @@ EventTag EventPerformanceLogger::onEventStart(

// The event start timestamp may be provided by the caller in order to
// specify the platform specific event start time.
auto timeStamp = eventStartTimeStamp == DOM_HIGH_RES_TIME_STAMP_UNSET
? performanceEntryReporter->getCurrentTimeStamp()
: eventStartTimeStamp;
DOMHighResTimeStamp timeStamp = eventStartTimeStamp
? *eventStartTimeStamp
: performanceEntryReporter->getCurrentTimeStamp();
{
std::lock_guard lock(eventsInFlightMutex_);
eventsInFlight_.emplace(
eventTag, EventEntry{reportedName, target, timeStamp, 0.0});
eventTag, EventEntry{reportedName, target, timeStamp});
}
return eventTag;
}
Expand Down Expand Up @@ -163,6 +165,9 @@ void EventPerformanceLogger::onEventProcessingEnd(EventTag tag) {
}

auto& entry = it->second;
react_native_assert(
entry.processingStartTime.has_value() &&
"Attempting to set processingEndTime while processingStartTime is not set.");
entry.processingEndTime = timeStamp;
}
}
Expand All @@ -188,12 +193,18 @@ void EventPerformanceLogger::dispatchPendingEventTimingEntries(
entry.isWaitingForMount = true;
++it;
} else {
react_native_assert(
entry.processingStartTime.has_value() &&
"Attempted to report PerformanceEventTiming, which did not have processingStartTime defined.");
react_native_assert(
entry.processingEndTime.has_value() &&
"Attempted to report PerformanceEventTiming, which did not have processingEndTime defined.");
performanceEntryReporter->reportEvent(
std::string(entry.name),
entry.startTime,
performanceEntryReporter->getCurrentTimeStamp() - entry.startTime,
entry.processingStartTime,
entry.processingEndTime,
entry.processingStartTime.value(),
entry.processingEndTime.value(),
entry.interactionId);
it = eventsInFlight_.erase(it);
}
Expand All @@ -214,12 +225,18 @@ void EventPerformanceLogger::shadowTreeDidMount(
const auto& entry = it->second;
if (entry.isWaitingForMount &&
isTargetInRootShadowNode(entry.target, rootShadowNode)) {
react_native_assert(
entry.processingStartTime.has_value() &&
"Attempted to report PerformanceEventTiming, which did not have processingStartTime defined.");
react_native_assert(
entry.processingEndTime.has_value() &&
"Attempted to report PerformanceEventTiming, which did not have processingEndTime defined.");
performanceEntryReporter->reportEvent(
std::string(entry.name),
entry.startTime,
mountTime - entry.startTime,
entry.processingStartTime,
entry.processingEndTime,
entry.processingStartTime.value(),
entry.processingEndTime.value(),
entry.interactionId);
it = eventsInFlight_.erase(it);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@
#include <react/renderer/core/EventLogger.h>
#include <react/renderer/runtimescheduler/RuntimeSchedulerEventTimingDelegate.h>
#include <react/renderer/uimanager/UIManagerMountHook.h>

#include <memory>
#include <mutex>
#include <optional>
#include <string_view>
#include <unordered_map>

Expand All @@ -30,8 +32,8 @@ class EventPerformanceLogger : public EventLogger,
EventTag onEventStart(
std::string_view name,
SharedEventTarget target,
DOMHighResTimeStamp eventStartTimeStamp =
DOM_HIGH_RES_TIME_STAMP_UNSET) override;
std::optional<DOMHighResTimeStamp> eventStartTimeStamp =
std::nullopt) override;
void onEventProcessingStart(EventTag tag) override;
void onEventProcessingEnd(EventTag tag) override;

Expand All @@ -51,9 +53,9 @@ class EventPerformanceLogger : public EventLogger,
struct EventEntry {
std::string_view name;
SharedEventTarget target{nullptr};
DOMHighResTimeStamp startTime{0.0};
DOMHighResTimeStamp processingStartTime{0.0};
DOMHighResTimeStamp processingEndTime{0.0};
DOMHighResTimeStamp startTime;
std::optional<DOMHighResTimeStamp> processingStartTime;
std::optional<DOMHighResTimeStamp> processingEndTime;

bool isWaitingForMount{false};

Expand All @@ -62,7 +64,7 @@ class EventPerformanceLogger : public EventLogger,
PerformanceEntryInteractionId interactionId{0};

bool isWaitingForDispatch() {
return processingEndTime == 0.0;
return !processingEndTime.has_value();
}
};

Expand Down
Loading
Loading