Skip to content

Commit

Permalink
compute pressure: Fix timestamp to be relative to the time origin
Browse files Browse the repository at this point in the history
The Compute Pressure API specification stipulates that the timestamp
must be relative to time origin of the global associated object with
the PressureObserver instance that generates the notification.[1]

The implementation was reporting the current time, relative to
epoch in Unix-like system.

This patch fixes the issue, by using base::TimeTicks instead of
base::Time, which is always incrementing, and also by subtracting the
time origin from the current time to provide a timespace relative
to the time origin of the associated object.

[1]: https://w3c.github.io/compute-pressure/#the-pressurerecord-interface

Bug: 336140984
Change-Id: I5e3cfb5ad966f62ce13ade3d81e55b5b4273025a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5472087
Reviewed-by: Reilly Grant <reillyg@chromium.org>
Reviewed-by: Raphael Kubo Da Costa <raphael.kubo.da.costa@intel.com>
Reviewed-by: Michael Cui <mlcui@google.com>
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Commit-Queue: Arnaud Mandy <arnaud.mandy@intel.com>
Cr-Commit-Position: refs/heads/main@{#1296013}
  • Loading branch information
arskama authored and chromium-wpt-export-bot committed May 3, 2024
1 parent 10faea3 commit c5ced76
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 38 deletions.
86 changes: 64 additions & 22 deletions compute-pressure/compute_pressure_timestamp.https.any.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,74 @@

'use strict';

pressure_test(async (t, mockPressureService) => {
const [change, timeOrigin] = await new Promise(resolve => {
const observer = new PressureObserver(change => {
resolve([change, performance.timeOrigin]);
});
t.add_cleanup(() => observer.disconnect());
observer.observe('cpu');
mockPressureService.setPressureUpdate('cpu', 'critical');
mockPressureService.startPlatformCollector(/*sampleInterval=*/ 200);
});
assert_greater_than(change[0].time, timeOrigin);
}, 'Timestamp from update should be greater than timeOrigin');

pressure_test(async (t, mockPressureService) => {
const readings = ['nominal', 'fair', 'serious', 'critical'];

const sampleInterval = 250;
const pressureChanges = await new Promise(async resolve => {
const observerChanges = [];
const observer = new PressureObserver(changes => {
observerChanges.push(changes);
});
observer.observe('cpu', {sampleInterval});
const pressureChanges = [];
const observer = new PressureObserver(changes => {
pressureChanges.push(changes);
});
observer.observe('cpu', {sampleInterval});

mockPressureService.startPlatformCollector(sampleInterval / 2);
let i = 0;
// mockPressureService.updatesDelivered() does not necessarily match
// pressureChanges.length, as system load and browser optimizations can
// cause the actual timer used by mockPressureService to deliver readings
// to be a bit slower or faster than requested.
while (observerChanges.length < 4) {
mockPressureService.setPressureUpdate(
'cpu', readings[i++ % readings.length]);
await t.step_wait(
() => mockPressureService.updatesDelivered() >= i,
`At least ${i} readings have been delivered`);
}
observer.disconnect();
resolve(observerChanges);
mockPressureService.startPlatformCollector(sampleInterval / 2);
let i = 0;
// mockPressureService.updatesDelivered() does not necessarily match
// pressureChanges.length, as system load and browser optimizations can
// cause the actual timer used by mockPressureService to deliver readings
// to be a bit slower or faster than requested.
while (pressureChanges.length < 4) {
mockPressureService.setPressureUpdate(
'cpu', readings[i++ % readings.length]);
await t.step_wait(
() => mockPressureService.updatesDelivered() >= i,
`At least ${i} readings have been delivered`);
}
observer.disconnect();

assert_equals(pressureChanges.length, 4);
assert_greater_than(pressureChanges[1][0].time, pressureChanges[0][0].time);
assert_greater_than(pressureChanges[2][0].time, pressureChanges[1][0].time);
assert_greater_than(pressureChanges[3][0].time, pressureChanges[2][0].time);
}, 'Timestamp difference between two changes should be continuously increasing');

pressure_test(async (t, mockPressureService) => {
const readings = ['nominal', 'fair', 'serious', 'critical'];

const sampleInterval = 250;
const pressureChanges = [];
const observer = new PressureObserver(change => {
pressureChanges.push(change);
});
observer.observe('cpu', {sampleInterval});

mockPressureService.startPlatformCollector(sampleInterval / 2);
let i = 0;
// mockPressureService.updatesDelivered() does not necessarily match
// pressureChanges.length, as system load and browser optimizations can
// cause the actual timer used by mockPressureService to deliver readings
// to be a bit slower or faster than requested.
while (pressureChanges.length < 4) {
mockPressureService.setPressureUpdate(
'cpu', readings[i++ % readings.length]);
await t.step_wait(
() => mockPressureService.updatesDelivered() >= i,
`At least ${i} readings have been delivered`);
}
observer.disconnect();

assert_equals(pressureChanges.length, 4);
assert_greater_than_equal(
Expand Down Expand Up @@ -71,5 +112,6 @@ pressure_test(async (t, mockPressureService) => {
// should be deleted. So the second PressureRecord is not discarded even
// though the time interval does not meet the requirement.
assert_less_than(
pressureChanges[1][0].time - pressureChanges[0][0].time, sampleInterval);
(pressureChanges[1][0].time - pressureChanges[0][0].time),
sampleInterval);
}, 'disconnect() should update [[LastRecordMap]]');
26 changes: 10 additions & 16 deletions resources/chromium/mock-pressure-service.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,26 +65,20 @@ class MockPressureService {
if (this.pressureServiceReadingTimerId_ != null)
this.stopPlatformCollector();

// The following code for calculating the timestamp was taken from
// https://source.chromium.org/chromium/chromium/src/+/main:third_party/
// blink/web_tests/http/tests/resources/
// geolocation-mock.js;l=131;drc=37a9b6c03b9bda9fcd62fc0e5e8016c278abd31f

// The new Date().getTime() returns the number of milliseconds since the
// UNIX epoch (1970-01-01 00::00:00 UTC), while |internalValue| of the
// device.mojom.PressureUpdate represents the value of microseconds since
// the Windows FILETIME epoch (1601-01-01 00:00:00 UTC). So add the delta
// when sets the |internalValue|. See more info in //base/time/time.h.
const windowsEpoch = Date.UTC(1601, 0, 1, 0, 0, 0, 0);
const unixEpoch = Date.UTC(1970, 0, 1, 0, 0, 0, 0);
// |epochDeltaInMs| equals to base::Time::kTimeTToMicrosecondsOffset.
const epochDeltaInMs = unixEpoch - windowsEpoch;

this.pressureServiceReadingTimerId_ = self.setInterval(() => {
if (this.pressureUpdate_ === null || this.observers_.length === 0)
return;

// Because we cannot retrieve directly the timeOrigin internal in
// TimeTicks from Chromium, we need to create a timestamp that
// would help to test basic functionality.
// by multiplying performance.timeOrigin by 10 we make sure that the
// origin is higher than the internal time origin in TimeTicks.
// performance.now() allows us to have an increase matching the TimeTicks
// that would be read from the platform collector.
this.pressureUpdate_.timestamp = {
internalValue: BigInt((new Date().getTime() + epochDeltaInMs) * 1000)
internalValue:
Math.round((performance.timeOrigin * 10) + performance.now()) * 1000
};
for (let observer of this.observers_)
observer.onPressureUpdated(this.pressureUpdate_);
Expand Down

0 comments on commit c5ced76

Please sign in to comment.