This repository has been archived by the owner on Oct 16, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 5ba656e
Showing
21 changed files
with
3,040 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
root = true | ||
|
||
[*] | ||
end_of_line = lf | ||
insert_final_newline = true | ||
charset = utf-8 | ||
indent_size = 2 | ||
indent_style = space | ||
trim_trailing_whitespace = true | ||
|
||
[*.js] | ||
max_line_length = 120 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
node_modules |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# hr-time | ||
|
||
An implementation of the W3C High Resolution Time Level 2 specification [[HR-TIME]][]. | ||
|
||
[[HR-TIME]]: https://w3c.github.io/hr-time/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
"use strict"; | ||
|
||
const { getGlobalMonotonicClockMS } = require("./lib/global-monotonic-clock"); | ||
const { Performance } = require("./lib/performance"); | ||
const clockIsAccurate = require("./lib/clock-is-accurate"); | ||
|
||
module.exports = { | ||
Performance, | ||
getGlobalMonotonicClockMS, | ||
clockIsAccurate | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
"use strict"; | ||
|
||
// This files implements the calculation of the offset between the global monotonic clock and UNIX time. This value is | ||
// known as |t1| in the calculation of "time origin timestamp" in the spec. This value needs to be calculated once and | ||
// can be used in all subsequent Performance instances. | ||
// | ||
// However, if the clock is not fast enough, the export is undefined to signify that we should use Date.now() to get the | ||
// time origin timestamp with millisecond accuracy, per spec. | ||
|
||
const { getGlobalMonotonicClockMS } = require("./global-monotonic-clock"); | ||
const clockIsAccurate = require("./clock-is-accurate"); | ||
|
||
// This function assumes the clock is accurate. | ||
function calculateClockOffset() { | ||
const start = Date.now(); | ||
let cur = start; | ||
while (cur === start) { | ||
cur = Date.now(); | ||
} | ||
|
||
// At this point |cur| "just" became equal to the next millisecond -- the unseen digits after |cur| are approximately | ||
// all 0, and |cur| is the closest to the actual value of the UNIX time. Now, get the current global monotonic clock | ||
// value and do the remaining calculations. | ||
|
||
return cur - getGlobalMonotonicClockMS(); | ||
} | ||
|
||
if (clockIsAccurate) { | ||
// Warm up the function. | ||
calculateClockOffset(); | ||
calculateClockOffset(); | ||
calculateClockOffset(); | ||
|
||
module.exports = calculateClockOffset; | ||
} else { | ||
module.exports = undefined; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
"use strict"; | ||
|
||
const { hrtime } = require("./utils"); | ||
|
||
// The HR-TIME spec calls for 5-μs accuracy. Check that we have that in both hrtime() and Date.now(). | ||
|
||
function testClockAccuracy() { | ||
// Test hrtime() first. The check is simpler and more stable, and we use hrtime() to measure Date.now()'s performance. | ||
const roundTrip = hrtime(hrtime()); | ||
if (roundTrip[0] > 1 || roundTrip[1] > 5e3 * 2) { | ||
return false; | ||
} | ||
|
||
// Test Date.now() twice: first with a looser bound (10 μs) but with a smaller run time to filter out very bad | ||
// Date.now() performance, and then with a tighter bound (5 μs) to check we have the accuracy we need. | ||
let times; | ||
let cur; | ||
let start; | ||
let end; | ||
|
||
times = 100; | ||
start = hrtime(); | ||
while (times-- > 0) { | ||
cur = Date.now(); | ||
} | ||
end = hrtime(start); | ||
if ((end[0] * 1e9 + end[1]) > 1000000) { | ||
return false; | ||
} | ||
|
||
times = 10000; | ||
start = hrtime(); | ||
while (times-- > 0) { | ||
cur = Date.now(); | ||
} | ||
end = hrtime(start); | ||
if ((end[0] * 1e9 + end[1]) > 50000000) { | ||
return false; | ||
} | ||
|
||
return true; | ||
} | ||
|
||
// Warm up the function. | ||
testClockAccuracy(); | ||
testClockAccuracy(); | ||
testClockAccuracy(); | ||
|
||
const TIMES = 5; | ||
const THRESHOLD = .6 * TIMES; | ||
let accurates = 0; | ||
for (let i = 0; i < TIMES; i++) { | ||
if (testClockAccuracy()) { | ||
accurates++; | ||
} | ||
} | ||
|
||
const isAccurate = accurates >= THRESHOLD; | ||
|
||
module.exports = isAccurate; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
"use strict"; | ||
|
||
const { hrtime, toMS } = require("./utils"); | ||
|
||
// Returns the DOMHighResTimeStamp representing the high resolution time value of the global monotonic clock. | ||
function getGlobalMonotonicClockMS() { | ||
return toMS(hrtime()); | ||
} | ||
|
||
module.exports = { getGlobalMonotonicClockMS }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
"use strict"; | ||
|
||
// Actual implementation of the Performance class. | ||
|
||
const clockIsAccurate = require("./clock-is-accurate"); | ||
const calculateClockOffset = require("./calculate-clock-offset"); | ||
const { hrtime, toMS } = require("./utils"); | ||
|
||
const kTimeOrigin = Symbol("time origin"); | ||
const kTimeOriginTimestamp = Symbol("time origin timestamp"); | ||
|
||
class Performance { | ||
constructor() { | ||
// Time origin. | ||
const timeOrigin = hrtime(); | ||
this[kTimeOrigin] = timeOrigin; | ||
|
||
if (clockIsAccurate) { | ||
// Let |t1| be the DOMHighResTimeStamp representing the high resolution Unix time at which the global monotonic | ||
// clock is zero. This has to be calculated for every Performance object to account for clock drifts. | ||
const t1 = calculateClockOffset(); | ||
|
||
// Let |t2| be the DOMHighResTimeStamp representing the high resolution time value of the global monotonic clock | ||
// at global's time origin. | ||
const t2 = toMS(timeOrigin); | ||
|
||
// Return the sum of |t1| and |t2|. | ||
this[kTimeOriginTimestamp] = t1 + t2; | ||
} else { | ||
// Clock isn't accurate enough. Use millisecond accuracy per spec. | ||
const cur = Date.now(); | ||
this[kTimeOriginTimestamp] = cur; | ||
} | ||
} | ||
|
||
// The timeOrigin getter actually returns the time origin timestamp, not the raw time origin. | ||
get timeOrigin() { | ||
return this[kTimeOriginTimestamp]; | ||
} | ||
|
||
now() { | ||
const diff = toMS(hrtime(this[kTimeOrigin])); | ||
return clockIsAccurate ? diff : Math.round(diff); | ||
} | ||
} | ||
|
||
module.exports = { Performance }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
"use strict"; | ||
|
||
// Browserify's process implementation doesn't have hrtime, and this package is small so not much of a burden for | ||
// Node.js users. | ||
const hrtime = require("browser-process-hrtime"); | ||
|
||
function toMS([sec, nanosec]) { | ||
return sec * 1e3 + nanosec / 1e6; | ||
} | ||
|
||
module.exports = { hrtime, toMS }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
{ | ||
"name": "hr-time", | ||
"version": "1.0.0", | ||
"description": "An implementation of the W3C High Resolution Time Level 2 specification.", | ||
"main": "index.js", | ||
"repository": "https://github.com/jsdom/hr-time", | ||
"author": "Timothy Gu <[email protected]>", | ||
"license": "MIT", | ||
"private": false, | ||
"dependencies": { | ||
"browser-process-hrtime": "^0.1.2" | ||
}, | ||
"devDependencies": { | ||
"jest": "^22.0.4" | ||
}, | ||
"scripts": { | ||
"test": "jest" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
"use strict"; | ||
|
||
const FACTOR = require("./utils/slow-down-clock"); | ||
|
||
const { Performance } = require("../"); | ||
const timeoutTest = require("./utils/timeout-test"); | ||
|
||
test("mocked clock drift", () => { | ||
const performance = new Performance(); | ||
return timeoutTest(performance, FACTOR); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
"use strict"; | ||
|
||
require("./utils/inaccurate-clock")("Date.now"); | ||
|
||
const { clockIsAccurate, Performance } = require("../"); | ||
const timeoutTest = require("./utils/timeout-test"); | ||
const checkPerformanceNowIntegers = require("./utils/performance-now-integers"); | ||
|
||
test("mocked inaccurate clock: only Date.now()", () => { | ||
const performance = new Performance(); | ||
|
||
expect(clockIsAccurate).toBe(false); | ||
checkPerformanceNowIntegers(performance); | ||
return timeoutTest(performance); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
"use strict"; | ||
|
||
require("./utils/inaccurate-clock")("process.hrtime"); | ||
|
||
const { clockIsAccurate, Performance } = require("../"); | ||
const timeoutTest = require("./utils/timeout-test"); | ||
const checkPerformanceNowIntegers = require("./utils/performance-now-integers"); | ||
|
||
test("mocked inaccurate clock: only process.hrtime()", () => { | ||
const performance = new Performance(); | ||
|
||
expect(clockIsAccurate).toBe(false); | ||
checkPerformanceNowIntegers(performance); | ||
return timeoutTest(performance); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
"use strict"; | ||
|
||
require("./utils/inaccurate-clock")("all"); | ||
|
||
const { clockIsAccurate, Performance } = require("../"); | ||
const timeoutTest = require("./utils/timeout-test"); | ||
const checkPerformanceNowIntegers = require("./utils/performance-now-integers"); | ||
|
||
test("mocked inaccurate clock: all clocks", () => { | ||
const performance = new Performance(); | ||
|
||
expect(clockIsAccurate).toBe(false); | ||
checkPerformanceNowIntegers(performance); | ||
return timeoutTest(performance); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
"use strict"; | ||
|
||
const { clockIsAccurate, Performance } = require("../"); | ||
const timeoutTest = require("./utils/timeout-test"); | ||
const checkPerformanceNowSubmillisecond = require("./utils/performance-now-submillisecond"); | ||
const checkPerformanceNowIntegers = require("./utils/performance-now-integers"); | ||
|
||
test("normal timing, without any mocked functions", () => { | ||
const performance = new Performance(); | ||
|
||
if (clockIsAccurate) { | ||
checkPerformanceNowSubmillisecond(performance); | ||
} else { | ||
checkPerformanceNowIntegers(performance); | ||
} | ||
|
||
return timeoutTest(performance); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
"use strict"; | ||
|
||
// Emulate inaccurate clock. | ||
|
||
const realHrtime = process.hrtime; | ||
|
||
function delay(func) { | ||
return (...args) => { | ||
const delayNS = 1e6; // 1 ms delay in clock | ||
const start = realHrtime(); | ||
let duration = realHrtime(start); | ||
while (duration[0] < 1 && duration[1] < delayNS) { | ||
duration = realHrtime(start); | ||
} | ||
return func(...args); | ||
}; | ||
} | ||
|
||
function mock(target) { | ||
if (target === "all" || target === "Date.now") { | ||
Date.now = delay(Date.now); | ||
} | ||
if (target === "all" || target === "process.hrtime") { | ||
process.hrtime = delay(process.hrtime); | ||
} | ||
} | ||
|
||
module.exports = mock; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
"use strict"; | ||
|
||
// Check performance.now() always returns integers. | ||
|
||
const checkPerformanceNowIntegers = performance => { | ||
const start = performance.now(); | ||
expect(Number.isInteger(start)).toBe(true); | ||
let end; | ||
do { | ||
end = performance.now(); | ||
} while (end === start); | ||
expect(Number.isInteger(end)).toBe(true); | ||
}; | ||
|
||
module.exports = checkPerformanceNowIntegers; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
"use strict"; | ||
|
||
// Check fractional parts of performance.now(). | ||
|
||
const checkPerformanceNowSubmillisecond = performance => { | ||
const start = performance.now(); | ||
const end = performance.now(); | ||
expect(end - start).toBeGreaterThan(0); | ||
expect(end - start).toBeLessThan(1); | ||
}; | ||
|
||
module.exports = checkPerformanceNowSubmillisecond; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
"use strict"; | ||
|
||
// Emulate clock drift. | ||
|
||
const realDateNow = Date.now; | ||
|
||
const FACTOR = 0.6; | ||
const start = realDateNow(); | ||
|
||
Date.now = () => { | ||
return start + Math.round((realDateNow() - start) * FACTOR); | ||
}; | ||
|
||
module.exports = FACTOR; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
"use strict"; | ||
|
||
const { getGlobalMonotonicClockMS } = require("../../"); | ||
|
||
const TIMEOUT = 2000; | ||
const DELTA = 20; // ±20 ms | ||
// Jest expects accuracy to be expressed in terms of number of digits after the decimal point. | ||
const NUM_DIGITS = -Math.log10(DELTA * 2); | ||
|
||
function timeoutTest(performance, FACTOR = 1) { | ||
const startDateNow = Date.now(); | ||
const startGlobalMonotonicClock = getGlobalMonotonicClockMS(); | ||
|
||
return new Promise(resolve => { | ||
setTimeout(resolve, TIMEOUT); | ||
}).then(() => { | ||
expect(performance.now()).toBeCloseTo(TIMEOUT, NUM_DIGITS); | ||
expect(Date.now() - startDateNow).toBeCloseTo(TIMEOUT * FACTOR, NUM_DIGITS); | ||
expect(getGlobalMonotonicClockMS() - startGlobalMonotonicClock).toBeCloseTo(TIMEOUT, NUM_DIGITS); | ||
}); | ||
}; | ||
|
||
module.exports = timeoutTest; |
Oops, something went wrong.