diff options
-rw-r--r-- | changelog.md | 1 | ||||
-rw-r--r-- | doc/lib.rst | 5 | ||||
-rw-r--r-- | lib/pure/times.nim | 4 | ||||
-rw-r--r-- | lib/std/monotimes.nim | 173 | ||||
-rw-r--r-- | tools/kochdocs.nim | 1 |
5 files changed, 183 insertions, 1 deletions
diff --git a/changelog.md b/changelog.md index e1294bf6e..33fa45b6f 100644 --- a/changelog.md +++ b/changelog.md @@ -23,6 +23,7 @@ - Make public `Sha1Digest` and `Sha1State` types and `newSha1State`, `update` and `finalize` procedures from `sha1` module. (#11694) +- Added the `std/monotimes` module which implements monotonic timestamps. ## Language additions diff --git a/doc/lib.rst b/doc/lib.rst index 1c7426a68..de7b4dd45 100644 --- a/doc/lib.rst +++ b/doc/lib.rst @@ -185,7 +185,10 @@ Generic Operating System Services Module for process communication beyond ``os.execShellCmd``. * `times <times.html>`_ - The ``times`` module contains basic support for working with time. + The ``times`` module contains support for working with time. + +* `std/monotimes <monotimes.html>`_ + The `monotimes` module implements monotonic timestamps. * `dynlib <dynlib.html>`_ This module implements the ability to access symbols from shared libraries. diff --git a/lib/pure/times.nim b/lib/pure/times.nim index 7550a8dea..9d0ce8677 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -186,6 +186,10 @@ day has passed. The ``Duration`` type on the other hand normalizes everything to seconds, and will therefore say that 90000 seconds has passed, which is the same as 25 hours. + + See also + ======== + * `monotimes module <monotimes.html>`_ ]## import strutils, math, options diff --git a/lib/std/monotimes.nim b/lib/std/monotimes.nim new file mode 100644 index 000000000..d2d9717d7 --- /dev/null +++ b/lib/std/monotimes.nim @@ -0,0 +1,173 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2019 Nim contributors +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +##[ + The ``std/monotimes`` module implements monotonic timestamps. A monotonic + timestamp represents the time that has passed since some system defined + point in time. The monotonic timestamps are guaranteed to always increase, + meaning that that the following is guaranteed to work: + + .. code-block:: nim + let a = getMonoTime() + # ... do some work + let b = getMonoTime() + assert a <= b + + This is not guaranteed for the `times.Time` type! This means that the + `MonoTime` should be used when measuring durations of time with + high precision. + + However, since `MonoTime` represents the time that has passed since some + unknown time origin, it cannot be converted to a human readable timestamp. + If this is required, the `times.Time` type should be used instead. + + The `MonoTime` type stores the timestamp in nanosecond resolution, but note + that the actual supported time resolution differs for different systems. + + See also + ======== + * `times module <times.html>`_ +]## + +import times + +type + MonoTime* = object ## Represents a monotonic timestamp. + ticks: int64 + +when defined(macosx): + type + MachTimebaseInfoData {.pure, final, importc: "mach_timebase_info_data_t", + header: "<mach/mach_time.h>".} = object + numer, denom: int32 + + proc mach_absolute_time(): int64 {.importc, header: "<mach/mach.h>".} + proc mach_timebase_info(info: var MachTimebaseInfoData) {.importc, + header: "<mach/mach_time.h>".} + + let machAbsoluteTimeFreq = block: + var freq: MachTimebaseInfoData + mach_timebase_info(freq) + freq + +when defined(js): + proc getJsTicks: float = + {.emit: """ + var isNode = typeof module !== 'undefined' && module.exports + + if (isNode) { + var process = require('process'); + var time = process.hrtime() + return time[0] + time[1] / 1000000000; + } else { + return window.performance.now() * 1000000; + } + """.} + + # Workaround for #6752. + {.push overflowChecks: off.} + proc `-`(a, b: int64): int64 = + system.`-`(a, b) + proc `+`(a, b: int64): int64 = + system.`+`(a, b) + {.pop.} + +elif defined(posix): + import posix + +elif defined(windows): + proc QueryPerformanceCounter(res: var uint64) {. + importc: "QueryPerformanceCounter", stdcall, dynlib: "kernel32".} + proc QueryPerformanceFrequency(res: var uint64) {. + importc: "QueryPerformanceFrequency", stdcall, dynlib: "kernel32".} + + let queryPerformanceCounterFreq = block: + var freq: uint64 + QueryPerformanceFrequency(freq) + 1_000_000_000'u64 div freq + +proc getMonoTime*(): MonoTime {.tags: [TimeEffect].} = + ## Get the current `MonoTime` timestamp. + ## + ## When compiled with the JS backend and executed in a browser, + ## this proc calls `window.performance.now()`, which is not supported by + ## older browsers. See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Performance/now) + ## for more information. + when defined(JS): + let ticks = getJsTicks() + result = MonoTime(ticks: (ticks * 1_000_000_000).int64) + elif defined(macosx): + let ticks = mach_absolute_time() + result = MonoTime(ticks: ticks * machAbsoluteTimeFreq.numer div + machAbsoluteTimeFreq.denom) + elif defined(posix): + var ts: Timespec + discard clock_gettime(CLOCK_MONOTONIC, ts) + result = MonoTime(ticks: ts.tv_sec.int64 * 1_000_000_000 + + ts.tv_nsec.int64) + elif defined(windows): + var ticks: uint64 + QueryPerformanceCounter(ticks) + result = MonoTime(ticks: (ticks * queryPerformanceCounterFreq).int64) + +proc ticks*(t: MonoTime): int64 = + ## Returns the raw ticks value from a `MonoTime`. This value always uses + ## nanosecond time resolution. + t.ticks + +proc `$`*(t: MonoTime): string = + $t.ticks + +proc `-`*(a, b: MonoTime): Duration = + ## Returns the difference between two `MonoTime` timestamps as a `Duration`. + initDuration(nanoseconds = (a.ticks - b.ticks)) + +proc `+`*(a: MonoTime, b: Duration): MonoTime = + ## Increases `a` by `b`. + MonoTime(ticks: a.ticks + b.inNanoseconds) + +proc `-`*(a: MonoTime, b: Duration): MonoTime = + ## Reduces `a` by `b`. + MonoTime(ticks: a.ticks - b.inNanoseconds) + +proc `<`*(a, b: MonoTime): bool = + ## Returns true if `a` happened before `b`. + a.ticks < b.ticks + +proc `<=`*(a, b: MonoTime): bool = + ## Returns true if `a` happened before `b` or if they happened simultaneous. + a.ticks <= b.ticks + +proc `==`*(a, b: MonoTime): bool = + ## Returns true if `a` and `b` happened simultaneous. + a.ticks == b.ticks + +proc high*(typ: typedesc[MonoTime]): MonoTime = + ## Returns the highest representable `MonoTime`. + MonoTime(ticks: high(int64)) + +proc low*(typ: typedesc[MonoTime]): MonoTime = + ## Returns the lowest representable `MonoTime`. + MonoTime(ticks: low(int64)) + +when isMainModule: + let d = initDuration(nanoseconds = 10) + let t1 = getMonoTime() + let t2 = t1 + d + + doAssert t2 - t1 == d + doAssert t1 == t1 + doAssert t1 != t2 + doAssert t2 - d == t1 + doAssert t1 < t2 + doAssert t1 <= t2 + doAssert t1 <= t1 + doAssert not(t2 < t1) + doAssert t1 < high(MonoTime) + doAssert low(MonoTime) < t1 \ No newline at end of file diff --git a/tools/kochdocs.nim b/tools/kochdocs.nim index ab66449ac..248c3fc6b 100644 --- a/tools/kochdocs.nim +++ b/tools/kochdocs.nim @@ -158,6 +158,7 @@ lib/windows/winlean.nim lib/pure/random.nim lib/pure/complex.nim lib/pure/times.nim +lib/std/monotimes.nim lib/pure/osproc.nim lib/pure/pegs.nim lib/pure/dynlib.nim |