Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] introduce the virtual clock for simulations #407

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
30 changes: 30 additions & 0 deletions chronos/asyncloop.nim
Original file line number Diff line number Diff line change
Expand Up @@ -769,6 +769,36 @@ elif unixPlatform:
var curTime = Moment.now()
var curTimeout = 0

when asyncTimer == "virtual":
# Execute all queued callbacks and at most one timer event at a time.
# This is the closest to the one network event at a time semantics we have in other modalities.
# Enforcing one timer each poll is useful to make it compatible with original semantics when
# network events are replaced by timers for simulation.

# Execute queued callbacks in order.
while loop.callbacks.len > 0:
let cb = loop.callbacks.popFirst()
if not isSentinel(cb):
cb.function(cb.udata)

# Fill callbacks as needed, consuming at most one timer.
if loop.timers.len > 0:
let
timer = loop.timers.pop()
finish = timer.finishAt
callable = timer.function

if not(isNil(callable.function)):
loop.callbacks.addFirst(callable)

# Advance virtual time in simulation until next timer.
Moment.advance(finish - Moment.now())

# Handle idlers here, closest we can get to original definition.
else:
loop.processIdlers()
return

when ioselSupportedPlatform:
let customSet = {Event.Timer, Event.Signal, Event.Process,
Event.Vnode}
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