Skip to content

Commit

Permalink
introduce the virtual clock for simulations
Browse files Browse the repository at this point in the history
This allows running code written for Chronos using
`-d:asyncTimer=virtual`
, turning it into a simple event-based simulation.

Signed-off-by: Csaba Kiraly <csaba.kiraly@gmail.com>
  • Loading branch information
cskiraly committed Jun 12, 2023
1 parent 6525f4c commit 5b9ec08
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 4 deletions.
12 changes: 11 additions & 1 deletion chronos/asyncloop.nim
Original file line number Diff line number Diff line change
Expand Up @@ -781,8 +781,14 @@ elif unixPlatform:
# Moving expired timers to `loop.callbacks` and calculate timeout.
loop.processTimersGetTimeout(curTimeout)

let timeout =
when asyncTimer == "virtual":
0
else:
curTimeout

# Processing IO descriptors and all hardware events.
let count = loop.selector.selectInto(curTimeout, loop.keys)
let count = loop.selector.selectInto(timeout, loop.keys)
for i in 0..<count:
let fd = loop.keys[i].fd
let events = loop.keys[i].events
Expand Down Expand Up @@ -821,6 +827,10 @@ elif unixPlatform:
# All callbacks done, skip `processCallbacks` at start.
loop.callbacks.addFirst(SentinelCallback)

# Advance virtual time in simulation if nothing expected in curTimeout millisec
when asyncTimer == "virtual":
Moment.advance(curTimeout.milliseconds)

else:
proc initAPI() = discard
proc globalInit() = discard
Expand Down
26 changes: 23 additions & 3 deletions chronos/timer.nim
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,21 @@
## This module implements cross-platform system timer with
## milliseconds resolution.
##
## Timer supports two types of clocks:
## Timer supports three types of clocks:
## ``system`` uses the most fast OS primitive to obtain wall clock time.
## ``mono`` uses monotonic clock time (default).
## ``virtual`` is for event-based simulations only.
##
## ``system`` clock is affected by discontinuous jumps in the system time. This
## clock is significantly faster then ``mono`` clock in most of the cases.
##
## ``mono`` clock is not affected by discontinuous jumps in the system time.
## This clock is slower then ``system`` clock.
##
## You can specify which timer you want to use ``-d:asyncTimer=<system/mono>``.
## ``virtual`` clock is not related to wall clock or system time. It simply
## jumps through the event queue in time order, counting execution time.
##
## You can specify which timer you want to use ``-d:asyncTimer=<system/mono/virtual>``.
import stew/base10

const asyncTimer* {.strdefine.} = "mono"
Expand All @@ -31,7 +35,18 @@ when (NimMajor, NimMinor) < (1, 4):
else:
{.push raises: [].}

when defined(windows):
when asyncTimer == "virtual":
var curTime: uint64 = 0
proc fastEpochTime*(): uint64 {.
inline, deprecated: "Use Moment.now()".} =
## Procedure's resolution is millisecond.
curTime div 1_000_000

proc fastEpochTimeNano(): uint64 {.inline.} =
## Procedure's resolution is nanosecond.
curTime

elif defined(windows):
when asyncTimer == "system":
from winlean import getSystemTimeAsFileTime, FILETIME

Expand Down Expand Up @@ -503,3 +518,8 @@ when defined(posix):
tv_sec: Time(a.value div Second.value),
tv_nsec: int(a.value mod Second.value)
)

when asyncTimer == "virtual":
proc advance*(t: typedesc[Moment], a: Duration) =
## # Advance virtual time by given duration
curTime += a.nanoseconds.uint64

0 comments on commit 5b9ec08

Please sign in to comment.