diff options
author | Florian Dold <florian@dold.me> | 2024-02-19 20:07:37 +0100 |
---|---|---|
committer | Florian Dold <florian@dold.me> | 2024-02-19 20:07:45 +0100 |
commit | 194eeea6aaf4ab401825aa07073b3e0a4f9d3c1b (patch) | |
tree | 2ac2ff60c526153a8b53bac84989fa65278a6079 /packages/taler-util/src/timer.ts | |
parent | e1a7bf4825162b4b95669ae6b4552589f4a64217 (diff) | |
download | wallet-core-194eeea6aaf4ab401825aa07073b3e0a4f9d3c1b.tar.xz |
restructure
Diffstat (limited to 'packages/taler-util/src/timer.ts')
-rw-r--r-- | packages/taler-util/src/timer.ts | 213 |
1 files changed, 213 insertions, 0 deletions
diff --git a/packages/taler-util/src/timer.ts b/packages/taler-util/src/timer.ts new file mode 100644 index 000000000..8db024512 --- /dev/null +++ b/packages/taler-util/src/timer.ts @@ -0,0 +1,213 @@ +/* + This file is part of GNU Taler + (C) 2017-2019 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +/** + * Cross-platform timers. + * + * NodeJS and the browser use slightly different timer API, + * this abstracts over these differences. + */ + +/** + * Imports. + */ +import { Logger, Duration } from "@gnu-taler/taler-util"; + +const logger = new Logger("timer.ts"); + +/** + * Cancelable timer. + */ +export interface TimerHandle { + clear(): void; + + /** + * Make sure the event loop exits when the timer is the + * only event left. Has no effect in the browser. + */ + unref(): void; +} + +class IntervalHandle { + constructor(public h: any) {} + + clear(): void { + clearInterval(this.h); + } + + /** + * Make sure the event loop exits when the timer is the + * only event left. Has no effect in the browser. + */ + unref(): void { + if (typeof this.h === "object" && "unref" in this.h) { + this.h.unref(); + } + } +} + +class TimeoutHandle { + constructor(public h: any) {} + + clear(): void { + clearTimeout(this.h); + } + + /** + * Make sure the event loop exits when the timer is the + * only event left. Has no effect in the browser. + */ + unref(): void { + if (typeof this.h === "object" && "unref" in this.h) { + this.h.unref(); + } + } +} + +/** + * Get a performance counter in nanoseconds. + */ +export const performanceNow: () => bigint = (() => { + // @ts-ignore + if (typeof process !== "undefined" && process.hrtime) { + return () => { + return process.hrtime.bigint(); + }; + } + + // @ts-ignore + if (typeof performance !== "undefined") { + // @ts-ignore + return () => BigInt(Math.floor(performance.now() * 1000)) * BigInt(1000); + } + + return () => BigInt(new Date().getTime()) * BigInt(1000) * BigInt(1000); +})(); + +const nullTimerHandle = { + clear() { + // do nothing + return; + }, + unref() { + // do nothing + return; + }, +}; + +/** + * Group of timers that can be destroyed at once. + */ +export interface TimerAPI { + after(delayMs: number, callback: () => void): TimerHandle; + every(delayMs: number, callback: () => void): TimerHandle; +} + +export class SetTimeoutTimerAPI implements TimerAPI { + /** + * Call a function every time the delay given in milliseconds passes. + */ + every(delayMs: number, callback: () => void): TimerHandle { + return new IntervalHandle(setInterval(callback, delayMs)); + } + + /** + * Call a function after the delay given in milliseconds passes. + */ + after(delayMs: number, callback: () => void): TimerHandle { + return new TimeoutHandle(setTimeout(callback, delayMs)); + } +} + +export const timer = new SetTimeoutTimerAPI(); + +/** + * Implementation of [[TimerGroup]] using setTimeout + */ +export class TimerGroup { + private stopped = false; + + private readonly timerMap: { [index: number]: TimerHandle } = {}; + + private idGen = 1; + + constructor(public readonly timerApi: TimerAPI) {} + + stopCurrentAndFutureTimers(): void { + this.stopped = true; + for (const x in this.timerMap) { + if (!this.timerMap.hasOwnProperty(x)) { + continue; + } + this.timerMap[x].clear(); + delete this.timerMap[x]; + } + } + + resolveAfter(delayMs: Duration): Promise<void> { + return new Promise<void>((resolve, reject) => { + if (delayMs.d_ms !== "forever") { + this.after(delayMs.d_ms, () => { + resolve(); + }); + } + }); + } + + after(delayMs: number, callback: () => void): TimerHandle { + if (this.stopped) { + logger.warn("dropping timer since timer group is stopped"); + return nullTimerHandle; + } + const h = this.timerApi.after(delayMs, callback); + const myId = this.idGen++; + this.timerMap[myId] = h; + + const tm = this.timerMap; + + return { + clear() { + h.clear(); + delete tm[myId]; + }, + unref() { + h.unref(); + }, + }; + } + + every(delayMs: number, callback: () => void): TimerHandle { + if (this.stopped) { + logger.warn("dropping timer since timer group is stopped"); + return nullTimerHandle; + } + const h = this.timerApi.every(delayMs, callback); + const myId = this.idGen++; + this.timerMap[myId] = h; + + const tm = this.timerMap; + + return { + clear() { + h.clear(); + delete tm[myId]; + }, + unref() { + h.unref(); + }, + }; + } +} |