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

fix: performance.timeOrigin #26787

Merged
merged 1 commit into from
Nov 8, 2024
Merged
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
4 changes: 2 additions & 2 deletions cli/ops/bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use std::time;

use deno_core::error::generic_error;
use deno_core::error::type_error;
Expand All @@ -13,6 +12,7 @@ use deno_core::ModuleSpecifier;
use deno_core::OpState;
use deno_runtime::deno_permissions::ChildPermissionsArg;
use deno_runtime::deno_permissions::PermissionsContainer;
use deno_runtime::deno_web::StartTime;
use tokio::sync::mpsc::UnboundedSender;
use uuid::Uuid;

Expand Down Expand Up @@ -148,7 +148,7 @@ fn op_dispatch_bench_event(state: &mut OpState, #[serde] event: BenchEvent) {
#[op2(fast)]
#[number]
fn op_bench_now(state: &mut OpState) -> Result<u64, std::num::TryFromIntError> {
let ns = state.borrow::<time::Instant>().elapsed().as_nanos();
let ns = state.borrow::<StartTime>().elapsed().as_nanos();
let ns_u64 = u64::try_from(ns)?;
Ok(ns_u64)
}
13 changes: 1 addition & 12 deletions ext/web/02_timers.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.

import { core, primordials } from "ext:core/mod.js";
import { op_defer, op_now } from "ext:core/ops";
import { op_defer } from "ext:core/ops";
const {
Uint8Array,
Uint32Array,
PromisePrototypeThen,
TypedArrayPrototypeGetBuffer,
TypeError,
indirectEval,
ReflectApply,
Expand All @@ -18,13 +15,6 @@ const {

import * as webidl from "ext:deno_webidl/00_webidl.js";

const hrU8 = new Uint8Array(8);
const hr = new Uint32Array(TypedArrayPrototypeGetBuffer(hrU8));
function opNow() {
op_now(hrU8);
return (hr[0] * 1000 + hr[1] / 1e6);
}

// ---------------------------------------------------------------------------

function checkThis(thisArg) {
Expand Down Expand Up @@ -151,7 +141,6 @@ export {
clearInterval,
clearTimeout,
defer,
opNow,
refTimer,
setImmediate,
setInterval,
Expand Down
24 changes: 17 additions & 7 deletions ext/web/15_performance.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.

import { primordials } from "ext:core/mod.js";
import { op_now, op_time_origin } from "ext:core/ops";
const {
ArrayPrototypeFilter,
ArrayPrototypePush,
Expand All @@ -10,19 +11,34 @@ const {
Symbol,
SymbolFor,
TypeError,
TypedArrayPrototypeGetBuffer,
Uint8Array,
Uint32Array,
} = primordials;

import * as webidl from "ext:deno_webidl/00_webidl.js";
import { structuredClone } from "./02_structured_clone.js";
import { createFilteredInspectProxy } from "ext:deno_console/01_console.js";
import { EventTarget } from "./02_event.js";
import { opNow } from "./02_timers.js";
import { DOMException } from "./01_dom_exception.js";

const illegalConstructorKey = Symbol("illegalConstructorKey");
let performanceEntries = [];
let timeOrigin;

const hrU8 = new Uint8Array(8);
const hr = new Uint32Array(TypedArrayPrototypeGetBuffer(hrU8));

function setTimeOrigin() {
op_time_origin(hrU8);
timeOrigin = hr[0] * 1000 + hr[1] / 1e6;
}

function now() {
op_now(hrU8);
return hr[0] * 1000 + hr[1] / 1e6;
}

webidl.converters["PerformanceMarkOptions"] = webidl
.createDictionaryConverter(
"PerformanceMarkOptions",
Expand Down Expand Up @@ -90,10 +106,6 @@ webidl.converters["DOMString or PerformanceMeasureOptions"] = (
return webidl.converters.DOMString(V, prefix, context, opts);
};

function setTimeOrigin(origin) {
timeOrigin = origin;
}

function findMostRecent(
name,
type,
Expand Down Expand Up @@ -135,8 +147,6 @@ function filterByNameType(
);
}

const now = opNow;

const _name = Symbol("[[name]]");
const _entryType = Symbol("[[entryType]]");
const _startTime = Symbol("[[startTime]]");
Expand Down
6 changes: 4 additions & 2 deletions ext/web/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ pub use crate::message_port::Transferable;

use crate::timers::op_defer;
use crate::timers::op_now;
use crate::timers::StartTime;
use crate::timers::op_time_origin;
pub use crate::timers::StartTime;
pub use crate::timers::TimersPermission;

deno_core::extension!(deno_web,
Expand Down Expand Up @@ -84,6 +85,7 @@ deno_core::extension!(deno_web,
compression::op_compression_write,
compression::op_compression_finish,
op_now<P>,
op_time_origin<P>,
op_defer,
stream_resource::op_readable_stream_resource_allocate,
stream_resource::op_readable_stream_resource_allocate_sized,
Expand Down Expand Up @@ -123,7 +125,7 @@ deno_core::extension!(deno_web,
if let Some(location) = options.maybe_location {
state.put(Location(location));
}
state.put(StartTime::now());
state.put(StartTime::default());
}
);

Expand Down
65 changes: 47 additions & 18 deletions ext/web/timers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@

use deno_core::op2;
use deno_core::OpState;
use std::time::Duration;
use std::time::Instant;
use std::time::SystemTime;
use std::time::UNIX_EPOCH;

pub trait TimersPermission {
fn allow_hrtime(&mut self) -> bool;
Expand All @@ -17,21 +20,28 @@ impl TimersPermission for deno_permissions::PermissionsContainer {
}
}

pub type StartTime = Instant;
pub struct StartTime(Instant);

// Returns a milliseconds and nanoseconds subsec
// since the start time of the deno runtime.
// If the High precision flag is not set, the
// nanoseconds are rounded on 2ms.
#[op2(fast)]
pub fn op_now<TP>(state: &mut OpState, #[buffer] buf: &mut [u8])
impl Default for StartTime {
fn default() -> Self {
Self(Instant::now())
}
}

impl std::ops::Deref for StartTime {
type Target = Instant;

fn deref(&self) -> &Self::Target {
&self.0
}
}

fn expose_time<TP>(state: &mut OpState, duration: Duration, out: &mut [u8])
where
TP: TimersPermission + 'static,
{
let start_time = state.borrow::<StartTime>();
let elapsed = start_time.elapsed();
let seconds = elapsed.as_secs();
let mut subsec_nanos = elapsed.subsec_nanos();
let seconds = duration.as_secs() as u32;
let mut subsec_nanos = duration.subsec_nanos();

// If the permission is not enabled
// Round the nano result on 2 milliseconds
Expand All @@ -40,14 +50,33 @@ where
let reduced_time_precision = 2_000_000; // 2ms in nanoseconds
subsec_nanos -= subsec_nanos % reduced_time_precision;
}
if buf.len() < 8 {
return;

if out.len() >= 8 {
out[0..4].copy_from_slice(&seconds.to_ne_bytes());
out[4..8].copy_from_slice(&subsec_nanos.to_ne_bytes());
}
let buf: &mut [u32] =
// SAFETY: buffer is at least 8 bytes long.
unsafe { std::slice::from_raw_parts_mut(buf.as_mut_ptr() as _, 2) };
buf[0] = seconds as u32;
buf[1] = subsec_nanos;
}

#[op2(fast)]
pub fn op_now<TP>(state: &mut OpState, #[buffer] buf: &mut [u8])
where
TP: TimersPermission + 'static,
{
let start_time = state.borrow::<StartTime>();
let elapsed = start_time.elapsed();
expose_time::<TP>(state, elapsed, buf);
}

#[op2(fast)]
pub fn op_time_origin<TP>(state: &mut OpState, #[buffer] buf: &mut [u8])
where
TP: TimersPermission + 'static,
{
// https://w3c.github.io/hr-time/#dfn-estimated-monotonic-time-of-the-unix-epoch
let wall_time = SystemTime::now();
let monotonic_time = state.borrow::<StartTime>().elapsed();
let epoch = wall_time.duration_since(UNIX_EPOCH).unwrap() - monotonic_time;
expose_time::<TP>(state, epoch, buf);
}

#[allow(clippy::unused_async)]
Expand Down
5 changes: 2 additions & 3 deletions runtime/js/99_main.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ const {
ArrayPrototypeForEach,
ArrayPrototypeIncludes,
ArrayPrototypeMap,
DateNow,
Error,
ErrorPrototype,
FunctionPrototypeBind,
Expand Down Expand Up @@ -642,7 +641,7 @@ function bootstrapMainRuntime(runtimeOptions, warmup = false) {

removeImportedOps();

performance.setTimeOrigin(DateNow());
performance.setTimeOrigin();
globalThis_ = globalThis;

// Remove bootstrapping data from the global scope
Expand Down Expand Up @@ -858,7 +857,7 @@ function bootstrapWorkerRuntime(
7: nodeDebug,
} = runtimeOptions;

performance.setTimeOrigin(DateNow());
performance.setTimeOrigin();
globalThis_ = globalThis;

// Remove bootstrapping data from the global scope
Expand Down