Introduce monotonic timer functions. (#24)
* Introduce monotonic timer functions. Old fast timers are available through compiler switch. Add tests for both timers. * Bump version to 2.2.2.
This commit is contained in:
parent
df8d0da251
commit
67e214c5df
|
@ -1,5 +1,5 @@
|
||||||
packageName = "chronos"
|
packageName = "chronos"
|
||||||
version = "2.2.1"
|
version = "2.2.2"
|
||||||
author = "Status Research & Development GmbH"
|
author = "Status Research & Development GmbH"
|
||||||
description = "Chronos"
|
description = "Chronos"
|
||||||
license = "Apache License 2.0 or MIT"
|
license = "Apache License 2.0 or MIT"
|
||||||
|
@ -9,26 +9,45 @@ skipDirs = @["tests"]
|
||||||
|
|
||||||
requires "nim > 0.18.0"
|
requires "nim > 0.18.0"
|
||||||
|
|
||||||
task test, "Run all tests":
|
import ospaths
|
||||||
for tfile in @[
|
|
||||||
"testsync",
|
|
||||||
"testsoon",
|
|
||||||
"testtime",
|
|
||||||
"testfut",
|
|
||||||
"testsignal",
|
|
||||||
"testaddress",
|
|
||||||
"testdatagram",
|
|
||||||
"teststream",
|
|
||||||
"testserver",
|
|
||||||
"testbugs",
|
|
||||||
]:
|
|
||||||
for cmd in @[
|
|
||||||
"nim c -r -d:useSysAssert -d:useGcAssert tests/" & tfile,
|
|
||||||
"nim c -r tests/" & tfile,
|
|
||||||
#"nim c -r --gc:markAndSweep tests/" & tfile,
|
|
||||||
"nim c -r -d:release tests/" & tfile
|
|
||||||
]:
|
|
||||||
echo "\n" & cmd
|
|
||||||
exec cmd
|
|
||||||
rmFile("tests/" & tfile.toExe())
|
|
||||||
|
|
||||||
|
task test, "Run all tests":
|
||||||
|
|
||||||
|
var testFiles = @[
|
||||||
|
"testsync",
|
||||||
|
"testsoon",
|
||||||
|
"testtime",
|
||||||
|
"testfut",
|
||||||
|
"testsignal",
|
||||||
|
"testaddress",
|
||||||
|
"testdatagram",
|
||||||
|
"teststream",
|
||||||
|
"testserver",
|
||||||
|
"testbugs",
|
||||||
|
]
|
||||||
|
|
||||||
|
var testCommands = @[
|
||||||
|
"nim c -r -d:useSysAssert -d:useGcAssert",
|
||||||
|
"nim c -r",
|
||||||
|
"nim c -r -d:release"
|
||||||
|
]
|
||||||
|
|
||||||
|
var timerCommands = @[
|
||||||
|
" -d:asyncTimer=system",
|
||||||
|
" -d:asyncTimer=mono"
|
||||||
|
]
|
||||||
|
|
||||||
|
for tfile in testFiles:
|
||||||
|
if tfile == "testtime":
|
||||||
|
for cmd in testCommands:
|
||||||
|
for def in timerCommands:
|
||||||
|
var commandLine = (cmd & def & " tests") / tfile
|
||||||
|
echo "\n" & commandLine
|
||||||
|
exec commandLine
|
||||||
|
rmFile("tests" / tfile.toExe())
|
||||||
|
else:
|
||||||
|
for cmd in testCommands:
|
||||||
|
var commandLine = (cmd & " tests") / tfile
|
||||||
|
echo "\n" & commandLine
|
||||||
|
exec commandLine
|
||||||
|
rmFile("tests" / tfile.toExe())
|
||||||
|
|
|
@ -165,6 +165,13 @@ export asyncfutures2, timer
|
||||||
|
|
||||||
# TODO: Check if yielded future is nil and throw a more meaningful exception
|
# TODO: Check if yielded future is nil and throw a more meaningful exception
|
||||||
|
|
||||||
|
when defined(windows):
|
||||||
|
import winlean, sets, hashes
|
||||||
|
else:
|
||||||
|
import selectors
|
||||||
|
from posix import EINTR, EAGAIN, EINPROGRESS, EWOULDBLOCK, MSG_PEEK,
|
||||||
|
MSG_NOSIGNAL
|
||||||
|
|
||||||
type
|
type
|
||||||
AsyncError* = object of Exception
|
AsyncError* = object of Exception
|
||||||
## Generic async exception
|
## Generic async exception
|
||||||
|
@ -172,7 +179,7 @@ type
|
||||||
## Timeout exception
|
## Timeout exception
|
||||||
|
|
||||||
TimerCallback* = object
|
TimerCallback* = object
|
||||||
finishAt*: uint64
|
finishAt*: Moment
|
||||||
function*: AsyncCallback
|
function*: AsyncCallback
|
||||||
|
|
||||||
PDispatcherBase = ref object of RootRef
|
PDispatcherBase = ref object of RootRef
|
||||||
|
@ -188,6 +195,24 @@ proc initCallSoonProc() =
|
||||||
if asyncfutures2.getCallSoonProc().isNil:
|
if asyncfutures2.getCallSoonProc().isNil:
|
||||||
asyncfutures2.setCallSoonProc(callSoon)
|
asyncfutures2.setCallSoonProc(callSoon)
|
||||||
|
|
||||||
|
func getAsyncTimestamp*(a: Duration): auto {.inline.} =
|
||||||
|
## Return rounded up value of duration with milliseconds resolution.
|
||||||
|
##
|
||||||
|
## This function also take care on int32 overflow, because Linux and Windows
|
||||||
|
## accepts signed 32bit integer as timeout.
|
||||||
|
let milsec = Millisecond.nanoseconds()
|
||||||
|
let nansec = a.nanoseconds()
|
||||||
|
var res = nansec div milsec
|
||||||
|
let mid = nansec mod milsec
|
||||||
|
when defined(windows):
|
||||||
|
res = min(cast[int64](high(int32) - 1), res)
|
||||||
|
result = cast[DWORD](res)
|
||||||
|
result += DWORD(min(1'i32, cast[int32](mid)))
|
||||||
|
else:
|
||||||
|
res = min(cast[int64](high(int32) - 1), res)
|
||||||
|
result = cast[int32](res)
|
||||||
|
result += min(1, cast[int32](mid))
|
||||||
|
|
||||||
template processTimersGetTimeout(loop, timeout: untyped) =
|
template processTimersGetTimeout(loop, timeout: untyped) =
|
||||||
var count = len(loop.timers)
|
var count = len(loop.timers)
|
||||||
if count > 0:
|
if count > 0:
|
||||||
|
@ -199,10 +224,7 @@ template processTimersGetTimeout(loop, timeout: untyped) =
|
||||||
loop.callbacks.addLast(loop.timers.pop().function)
|
loop.callbacks.addLast(loop.timers.pop().function)
|
||||||
dec(count)
|
dec(count)
|
||||||
if count > 0:
|
if count > 0:
|
||||||
when defined(windows):
|
timeout = (lastFinish - curTime).getAsyncTimestamp()
|
||||||
timeout = DWORD(lastFinish - curTime)
|
|
||||||
else:
|
|
||||||
timeout = int(lastFinish - curTime)
|
|
||||||
|
|
||||||
if timeout == 0:
|
if timeout == 0:
|
||||||
if len(loop.callbacks) == 0:
|
if len(loop.callbacks) == 0:
|
||||||
|
@ -215,7 +237,7 @@ template processTimersGetTimeout(loop, timeout: untyped) =
|
||||||
timeout = 0
|
timeout = 0
|
||||||
|
|
||||||
template processTimers(loop: untyped) =
|
template processTimers(loop: untyped) =
|
||||||
var curTime = fastEpochTime()
|
var curTime = Moment.now()
|
||||||
var count = len(loop.timers)
|
var count = len(loop.timers)
|
||||||
if count > 0:
|
if count > 0:
|
||||||
while count > 0:
|
while count > 0:
|
||||||
|
@ -238,7 +260,6 @@ template processCallbacks(loop: untyped) =
|
||||||
callable.function(callable.udata)
|
callable.function(callable.udata)
|
||||||
|
|
||||||
when defined(windows) or defined(nimdoc):
|
when defined(windows) or defined(nimdoc):
|
||||||
import winlean, sets, hashes
|
|
||||||
type
|
type
|
||||||
WSAPROC_TRANSMITFILE = proc(hSocket: SocketHandle, hFile: Handle,
|
WSAPROC_TRANSMITFILE = proc(hSocket: SocketHandle, hFile: Handle,
|
||||||
nNumberOfBytesToWrite: DWORD,
|
nNumberOfBytesToWrite: DWORD,
|
||||||
|
@ -316,7 +337,7 @@ when defined(windows) or defined(nimdoc):
|
||||||
proc poll*() =
|
proc poll*() =
|
||||||
## Perform single asynchronous step.
|
## Perform single asynchronous step.
|
||||||
let loop = getGlobalDispatcher()
|
let loop = getGlobalDispatcher()
|
||||||
var curTime = fastEpochTime()
|
var curTime = Moment.now()
|
||||||
var curTimeout = DWORD(0)
|
var curTimeout = DWORD(0)
|
||||||
|
|
||||||
# Moving expired timers to `loop.callbacks` and calculate timeout
|
# Moving expired timers to `loop.callbacks` and calculate timeout
|
||||||
|
@ -328,8 +349,10 @@ when defined(windows) or defined(nimdoc):
|
||||||
var customOverlapped: PtrCustomOverlapped
|
var customOverlapped: PtrCustomOverlapped
|
||||||
|
|
||||||
let res = getQueuedCompletionStatus(
|
let res = getQueuedCompletionStatus(
|
||||||
loop.ioPort, addr lpNumberOfBytesTransferred, addr lpCompletionKey,
|
loop.ioPort, addr lpNumberOfBytesTransferred,
|
||||||
cast[ptr POVERLAPPED](addr customOverlapped), curTimeout).bool
|
addr lpCompletionKey, cast[ptr POVERLAPPED](addr customOverlapped),
|
||||||
|
curTimeout).bool
|
||||||
|
|
||||||
if res:
|
if res:
|
||||||
customOverlapped.data.bytesCount = lpNumberOfBytesTransferred
|
customOverlapped.data.bytesCount = lpNumberOfBytesTransferred
|
||||||
customOverlapped.data.errCode = OSErrorCode(-1)
|
customOverlapped.data.errCode = OSErrorCode(-1)
|
||||||
|
@ -428,9 +451,6 @@ when defined(windows) or defined(nimdoc):
|
||||||
return fd in disp.handles
|
return fd in disp.handles
|
||||||
|
|
||||||
else:
|
else:
|
||||||
import selectors
|
|
||||||
from posix import EINTR, EAGAIN, EINPROGRESS, EWOULDBLOCK, MSG_PEEK,
|
|
||||||
MSG_NOSIGNAL
|
|
||||||
type
|
type
|
||||||
AsyncFD* = distinct cint
|
AsyncFD* = distinct cint
|
||||||
|
|
||||||
|
@ -611,7 +631,7 @@ else:
|
||||||
proc poll*() =
|
proc poll*() =
|
||||||
## Perform single asynchronous step.
|
## Perform single asynchronous step.
|
||||||
let loop = getGlobalDispatcher()
|
let loop = getGlobalDispatcher()
|
||||||
var curTime = fastEpochTime()
|
var curTime = Moment.now()
|
||||||
var curTimeout = 0
|
var curTimeout = 0
|
||||||
|
|
||||||
when ioselSupportedPlatform:
|
when ioselSupportedPlatform:
|
||||||
|
@ -655,7 +675,7 @@ else:
|
||||||
proc initAPI() =
|
proc initAPI() =
|
||||||
discard getGlobalDispatcher()
|
discard getGlobalDispatcher()
|
||||||
|
|
||||||
proc addTimer*(at: uint64, cb: CallbackFunc, udata: pointer = nil) =
|
proc addTimer*(at: Moment, cb: CallbackFunc, udata: pointer = nil) =
|
||||||
## Arrange for the callback ``cb`` to be called at the given absolute
|
## Arrange for the callback ``cb`` to be called at the given absolute
|
||||||
## timestamp ``at``. You can also pass ``udata`` to callback.
|
## timestamp ``at``. You can also pass ``udata`` to callback.
|
||||||
let loop = getGlobalDispatcher()
|
let loop = getGlobalDispatcher()
|
||||||
|
@ -663,7 +683,11 @@ proc addTimer*(at: uint64, cb: CallbackFunc, udata: pointer = nil) =
|
||||||
function: AsyncCallback(function: cb, udata: udata))
|
function: AsyncCallback(function: cb, udata: udata))
|
||||||
loop.timers.push(tcb)
|
loop.timers.push(tcb)
|
||||||
|
|
||||||
proc removeTimer*(at: uint64, cb: CallbackFunc, udata: pointer = nil) =
|
proc addTimer*(at: int64, cb: CallbackFunc, udata: pointer = nil) {.
|
||||||
|
inline, deprecated: "Use addTimer(Duration, cb, udata)".} =
|
||||||
|
addTimer(Moment.init(at, Millisecond), cb, udata)
|
||||||
|
|
||||||
|
proc removeTimer*(at: Moment, cb: CallbackFunc, udata: pointer = nil) =
|
||||||
## Remove timer callback ``cb`` with absolute timestamp ``at`` from waiting
|
## Remove timer callback ``cb`` with absolute timestamp ``at`` from waiting
|
||||||
## queue.
|
## queue.
|
||||||
let loop = getGlobalDispatcher()
|
let loop = getGlobalDispatcher()
|
||||||
|
@ -677,18 +701,25 @@ proc removeTimer*(at: uint64, cb: CallbackFunc, udata: pointer = nil) =
|
||||||
if index != -1:
|
if index != -1:
|
||||||
loop.timers.del(index)
|
loop.timers.del(index)
|
||||||
|
|
||||||
proc sleepAsync*(ms: int): Future[void] =
|
proc removeTimer*(at: int64, cb: CallbackFunc, udata: pointer = nil) {.
|
||||||
|
inline, deprecated: "Use removeTimer(Duration, cb, udata)".} =
|
||||||
|
removeTimer(Moment.init(at, Millisecond), cb, udata)
|
||||||
|
|
||||||
|
proc sleepAsync*(ms: Duration): Future[void] =
|
||||||
## Suspends the execution of the current async procedure for the next
|
## Suspends the execution of the current async procedure for the next
|
||||||
## ``ms`` milliseconds.
|
## ``ms`` milliseconds.
|
||||||
var retFuture = newFuture[void]("sleepAsync")
|
var retFuture = newFuture[void]("sleepAsync")
|
||||||
proc completion(data: pointer) =
|
proc completion(data: pointer) =
|
||||||
if not retFuture.finished:
|
if not retFuture.finished:
|
||||||
retFuture.complete()
|
retFuture.complete()
|
||||||
addTimer(fastEpochTime() + uint64(ms),
|
addTimer(Moment.fromNow(ms), completion, cast[pointer](retFuture))
|
||||||
completion, cast[pointer](retFuture))
|
|
||||||
return retFuture
|
return retFuture
|
||||||
|
|
||||||
proc withTimeout*[T](fut: Future[T], timeout: int): Future[bool] =
|
proc sleepAsync*(ms: int): Future[void] {.
|
||||||
|
inline, deprecated: "Use sleepAsync(Duration)".} =
|
||||||
|
result = sleepAsync(ms.milliseconds())
|
||||||
|
|
||||||
|
proc withTimeout*[T](fut: Future[T], timeout: Duration): Future[bool] =
|
||||||
## Returns a future which will complete once ``fut`` completes or after
|
## Returns a future which will complete once ``fut`` completes or after
|
||||||
## ``timeout`` milliseconds has elapsed.
|
## ``timeout`` milliseconds has elapsed.
|
||||||
##
|
##
|
||||||
|
@ -704,11 +735,15 @@ proc withTimeout*[T](fut: Future[T], timeout: int): Future[bool] =
|
||||||
else:
|
else:
|
||||||
if not retFuture.finished:
|
if not retFuture.finished:
|
||||||
retFuture.complete(true)
|
retFuture.complete(true)
|
||||||
addTimer(fastEpochTime() + uint64(timeout), continuation, nil)
|
addTimer(Moment.fromNow(timeout), continuation, nil)
|
||||||
fut.addCallback(continuation)
|
fut.addCallback(continuation)
|
||||||
return retFuture
|
return retFuture
|
||||||
|
|
||||||
proc wait*[T](fut: Future[T], timeout = -1): Future[T] =
|
proc withTimeout*[T](fut: Future[T], timeout: int): Future[bool] {.
|
||||||
|
inline, deprecated: "Use withTimeout(Future[T], Duration)".} =
|
||||||
|
result = withTimeout(fut, timeout.milliseconds())
|
||||||
|
|
||||||
|
proc wait*[T](fut: Future[T], timeout = InfiniteDuration): Future[T] =
|
||||||
## Returns a future which will complete once future ``fut`` completes
|
## Returns a future which will complete once future ``fut`` completes
|
||||||
## or if timeout of ``timeout`` milliseconds has been expired.
|
## or if timeout of ``timeout`` milliseconds has been expired.
|
||||||
##
|
##
|
||||||
|
@ -726,9 +761,9 @@ proc wait*[T](fut: Future[T], timeout = -1): Future[T] =
|
||||||
retFuture.fail(fut.error)
|
retFuture.fail(fut.error)
|
||||||
else:
|
else:
|
||||||
retFuture.complete(fut.read())
|
retFuture.complete(fut.read())
|
||||||
if timeout == -1:
|
if timeout.isInfinite():
|
||||||
retFuture = fut
|
retFuture = fut
|
||||||
elif timeout == 0:
|
elif timeout.isZero():
|
||||||
if fut.finished:
|
if fut.finished:
|
||||||
if fut.failed:
|
if fut.failed:
|
||||||
retFuture.fail(fut.error)
|
retFuture.fail(fut.error)
|
||||||
|
@ -737,10 +772,19 @@ proc wait*[T](fut: Future[T], timeout = -1): Future[T] =
|
||||||
else:
|
else:
|
||||||
retFuture.fail(newException(AsyncTimeoutError, ""))
|
retFuture.fail(newException(AsyncTimeoutError, ""))
|
||||||
else:
|
else:
|
||||||
addTimer(fastEpochTime() + uint64(timeout), continuation, nil)
|
addTimer(Moment.fromNow(timeout), continuation, nil)
|
||||||
fut.addCallback(continuation)
|
fut.addCallback(continuation)
|
||||||
return retFuture
|
return retFuture
|
||||||
|
|
||||||
|
proc wait*[T](fut: Future[T], timeout = -1): Future[T] {.
|
||||||
|
inline, deprecated: "Use wait(Future[T], Duration)".} =
|
||||||
|
if timeout == -1:
|
||||||
|
wait(fut, InfiniteDuration)
|
||||||
|
elif timeout == 0:
|
||||||
|
wait(fut, ZeroDuration)
|
||||||
|
else:
|
||||||
|
wait(fut, timeout.milliseconds())
|
||||||
|
|
||||||
include asyncmacro2
|
include asyncmacro2
|
||||||
|
|
||||||
proc callSoon(cbproc: CallbackFunc, data: pointer = nil) =
|
proc callSoon(cbproc: CallbackFunc, data: pointer = nil) =
|
||||||
|
|
|
@ -8,45 +8,437 @@
|
||||||
# Apache License, version 2.0, (LICENSE-APACHEv2)
|
# Apache License, version 2.0, (LICENSE-APACHEv2)
|
||||||
# MIT license (LICENSE-MIT)
|
# MIT license (LICENSE-MIT)
|
||||||
|
|
||||||
|
|
||||||
## This module implements cross-platform system timer with
|
## This module implements cross-platform system timer with
|
||||||
## milliseconds resolution.
|
## milliseconds resolution.
|
||||||
|
##
|
||||||
|
## Timer supports two types of clocks:
|
||||||
|
## ``system`` uses the most fast OS primitive to obtain wall clock time.
|
||||||
|
## ``mono`` uses monotonic clock time (default).
|
||||||
|
##
|
||||||
|
## ``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>``.
|
||||||
|
const asyncTimer* {.strdefine.} = "mono"
|
||||||
|
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
|
from winlean import DWORD
|
||||||
|
|
||||||
from winlean import DWORD, getSystemTimeAsFileTime, FILETIME
|
when asyncTimer == "system":
|
||||||
|
from winlean import getSystemTimeAsFileTime, FILETIME
|
||||||
|
|
||||||
proc fastEpochTime*(): uint64 {.inline.} =
|
proc fastEpochTime*(): uint64 {.
|
||||||
var t: FILETIME
|
inline, deprecated: "Use Moment.now()".} =
|
||||||
getSystemTimeAsFileTime(t)
|
## Timer resolution is millisecond.
|
||||||
result = ((uint64(t.dwHighDateTime) shl 32) or
|
var t: FILETIME
|
||||||
uint64(t.dwLowDateTime)) div 10_000
|
getSystemTimeAsFileTime(t)
|
||||||
|
result = ((cast[uint64](t.dwHighDateTime) shl 32) or
|
||||||
|
cast[uint64](t.dwLowDateTime)) div 10_000
|
||||||
|
|
||||||
|
proc fastEpochTimeNano(): uint64 {.inline.} =
|
||||||
|
## Timer resolution is nanosecond.
|
||||||
|
var t: FILETIME
|
||||||
|
getSystemTimeAsFileTime(t)
|
||||||
|
result = ((cast[uint64](t.dwHighDateTime) shl 32) or
|
||||||
|
cast[uint64](t.dwLowDateTime)) * 100
|
||||||
|
|
||||||
|
else:
|
||||||
|
proc QueryPerformanceCounter(res: var uint64) {.
|
||||||
|
importc: "QueryPerformanceCounter", stdcall, dynlib: "kernel32".}
|
||||||
|
proc QueryPerformanceFrequency(res: var uint64) {.
|
||||||
|
importc: "QueryPerformanceFrequency", stdcall, dynlib: "kernel32".}
|
||||||
|
|
||||||
|
var queryFrequencyM: uint64
|
||||||
|
var queryFrequencyN: uint64
|
||||||
|
|
||||||
|
proc fastEpochTimeNano(): uint64 {.inline.} =
|
||||||
|
## Procedure's resolution is nanosecond.
|
||||||
|
var res: uint64
|
||||||
|
QueryPerformanceCounter(res)
|
||||||
|
result = res * queryFrequencyN
|
||||||
|
|
||||||
|
proc fastEpochTime*(): uint64 {.
|
||||||
|
inline, deprecated: "Use Moment.now()".} =
|
||||||
|
## Procedure's resolution is millisecond.
|
||||||
|
var res: uint64
|
||||||
|
QueryPerformanceCounter(res)
|
||||||
|
result = res div queryFrequencyM
|
||||||
|
|
||||||
|
proc setupQueryFrequence() =
|
||||||
|
var freq: uint64
|
||||||
|
QueryPerformanceFrequency(freq)
|
||||||
|
if freq < 1000:
|
||||||
|
queryFrequencyM = freq
|
||||||
|
else:
|
||||||
|
queryFrequencyM = freq div 1_000
|
||||||
|
queryFrequencyN = 1_000_000_000'u64 div freq
|
||||||
|
|
||||||
|
setupQueryFrequence()
|
||||||
|
|
||||||
elif defined(macosx):
|
elif defined(macosx):
|
||||||
|
|
||||||
from posix import Timeval
|
when asyncTimer == "system":
|
||||||
|
from posix import Timeval
|
||||||
|
|
||||||
proc posix_gettimeofday(tp: var Timeval, unused: pointer = nil) {.
|
proc posix_gettimeofday(tp: var Timeval, unused: pointer = nil) {.
|
||||||
importc: "gettimeofday", header: "<sys/time.h>".}
|
importc: "gettimeofday", header: "<sys/time.h>".}
|
||||||
|
|
||||||
proc fastEpochTime*(): uint64 {.inline.} =
|
proc fastEpochTime*(): uint64 {.
|
||||||
var t: Timeval
|
inline, deprecated: "Use Moment.now()".} =
|
||||||
posix_gettimeofday(t)
|
## Procedure's resolution is millisecond.
|
||||||
result = (uint64(t.tv_sec) * 1_000 + uint64(t.tv_usec) div 1_000)
|
var t: Timeval
|
||||||
|
posix_gettimeofday(t)
|
||||||
|
result = (cast[uint64](t.tv_sec) * 1_000 +
|
||||||
|
cast[uint64](t.tv_usec) div 1_000)
|
||||||
|
|
||||||
|
proc fastEpochTimeNano(): uint64 {.inline.} =
|
||||||
|
## Procedure's resolution is nanosecond.
|
||||||
|
var t: Timeval
|
||||||
|
posix_gettimeofday(t)
|
||||||
|
result = (cast[uint64](t.tv_sec) * 1_000_000_000 +
|
||||||
|
cast[uint64](t.tv_usec) * 1_000)
|
||||||
|
else:
|
||||||
|
from posix import clock_gettime, Timespec, CLOCK_MONOTONIC
|
||||||
|
|
||||||
|
proc fastEpochTime*(): uint64 {.
|
||||||
|
inline, deprecated: "Use Moment.now()".} =
|
||||||
|
## Procedure's resolution is millisecond.
|
||||||
|
var t: Timespec
|
||||||
|
discard clock_gettime(CLOCK_MONOTONIC, t)
|
||||||
|
result = ((cast[uint64](t.tv_sec) * 1_000) +
|
||||||
|
(cast[uint64](t.tv_nsec) div 1_000_000))
|
||||||
|
|
||||||
|
proc fastEpochTimeNano(): uint64 {.inline.} =
|
||||||
|
## Procedure's resolution is nanosecond.
|
||||||
|
var t: Timespec
|
||||||
|
discard clock_gettime(CLOCK_MONOTONIC, t)
|
||||||
|
result = cast[uint64](t.tv_sec) * 1_000_000_000'u64 +
|
||||||
|
cast[uint64](t.tv_nsec)
|
||||||
|
|
||||||
elif defined(posix):
|
elif defined(posix):
|
||||||
|
from posix import clock_gettime, Timespec, CLOCK_REALTIME, CLOCK_MONOTONIC
|
||||||
|
|
||||||
from posix import clock_gettime, Timespec, CLOCK_REALTIME
|
when asyncTimer == "system":
|
||||||
|
proc fastEpochTime*(): uint64 {.
|
||||||
|
inline, deprecated: "Use Moment.now()".} =
|
||||||
|
## Procedure's resolution is millisecond.
|
||||||
|
var t: Timespec
|
||||||
|
discard clock_gettime(CLOCK_REALTIME, t)
|
||||||
|
result = (cast[uint64](t.tv_sec) * 1_000 +
|
||||||
|
(cast[uint64](t.tv_nsec) div 1_000_000))
|
||||||
|
|
||||||
proc fastEpochTime*(): uint64 {.inline.} =
|
proc fastEpochTimeNano(): uint64 {.inline.} =
|
||||||
var t: Timespec
|
## Procedure's resolution is nanosecond.
|
||||||
discard clock_gettime(CLOCK_REALTIME, t)
|
var t: Timespec
|
||||||
result = (uint64(t.tv_sec) * 1_000 + uint64(t.tv_nsec) div 1_000_000)
|
discard clock_gettime(CLOCK_REALTIME, t)
|
||||||
|
result = cast[uint64](t.tv_sec) * 1_000_000_000'u64 +
|
||||||
|
cast[uint64](t.tv_nsec)
|
||||||
|
|
||||||
|
else:
|
||||||
|
proc fastEpochTime*(): uint64 {.
|
||||||
|
inline, deprecated: "Use Moment.now()".} =
|
||||||
|
## Procedure's resolution is millisecond.
|
||||||
|
var t: Timespec
|
||||||
|
discard clock_gettime(CLOCK_MONOTONIC, t)
|
||||||
|
result = (cast[uint64](t.tv_sec) * 1_000 +
|
||||||
|
(cast[uint64](t.tv_nsec) div 1_000_000))
|
||||||
|
|
||||||
|
proc fastEpochTimeNano(): uint64 {.inline.} =
|
||||||
|
## Procedure's resolution is nanosecond.
|
||||||
|
var t: Timespec
|
||||||
|
discard clock_gettime(CLOCK_MONOTONIC, t)
|
||||||
|
result = cast[uint64](t.tv_sec) * 1_000_000_000'u64 +
|
||||||
|
cast[uint64](t.tv_nsec)
|
||||||
|
|
||||||
elif defined(nimdoc):
|
elif defined(nimdoc):
|
||||||
|
|
||||||
proc fastEpochTime*(): uint64
|
proc fastEpochTime*(): uint64 {.deprecated: "Use Moment.now()".}
|
||||||
## Returns system's timer in milliseconds.
|
## Returns system's timer in milliseconds.
|
||||||
|
|
||||||
else:
|
else:
|
||||||
error("Sorry, your operation system is not yet supported!")
|
error("Sorry, your operation system is not yet supported!")
|
||||||
|
|
||||||
|
type
|
||||||
|
Moment* = object
|
||||||
|
## A Moment in time. Its value has no direct meaning, but can be compared
|
||||||
|
## with other Moments. Moments are captured using a monotonically
|
||||||
|
## non-decreasing clock (by default).
|
||||||
|
value: int64
|
||||||
|
|
||||||
|
Duration* = object
|
||||||
|
## A Duration is the interval between to points in time.
|
||||||
|
value: int64
|
||||||
|
|
||||||
|
when sizeof(int) == 4:
|
||||||
|
type SomeIntegerI64* = SomeSignedInt|uint|uint8|uint16|uint32
|
||||||
|
else:
|
||||||
|
type SomeIntegerI64* = SomeSignedInt|uint8|uint16|uint32
|
||||||
|
|
||||||
|
func `+`*(a: Duration, b: Duration): Duration {.inline.} =
|
||||||
|
## Duration + Duration = Duration
|
||||||
|
result.value = a.value + b.value
|
||||||
|
|
||||||
|
func `+`*(a: Duration, b: Moment): Moment {.inline.} =
|
||||||
|
## Duration + Moment = Moment
|
||||||
|
result.value = a.value + b.value
|
||||||
|
|
||||||
|
func `+`*(a: Moment, b: Duration): Moment {.inline.} =
|
||||||
|
## Moment + Duration = Moment
|
||||||
|
result.value = a.value + b.value
|
||||||
|
|
||||||
|
func `+=`*(a: var Moment, b: Duration) {.inline.} =
|
||||||
|
## Moment += Duration
|
||||||
|
a.value += b.value
|
||||||
|
|
||||||
|
func `+=`*(a: var Duration, b: Duration) {.inline.} =
|
||||||
|
## Duration += Duration
|
||||||
|
a.value += b.value
|
||||||
|
|
||||||
|
func `-`*(a, b: Moment): Duration {.inline.} =
|
||||||
|
## Moment - Moment = Duration
|
||||||
|
##
|
||||||
|
## Note: Duration can't be negative.
|
||||||
|
result.value = if a.value >= b.value: a.value - b.value else: 0'i64
|
||||||
|
|
||||||
|
func `-`*(a: Moment, b: Duration): Moment {.inline.} =
|
||||||
|
## Moment - Duration = Moment
|
||||||
|
##
|
||||||
|
## Note: Moment can be negative
|
||||||
|
result.value = a.value - b.value
|
||||||
|
|
||||||
|
func `-`*(a: Duration, b: Duration): Duration {.inline.} =
|
||||||
|
## Duration - Duration = Duration
|
||||||
|
##
|
||||||
|
## Note: Duration can't be negative.
|
||||||
|
result.value = if a.value >= b.value: a.value - b.value else: 0'i64
|
||||||
|
|
||||||
|
func `-=`*(a: var Duration, b: Duration): Duration {.inline.} =
|
||||||
|
## Duration -= Duration
|
||||||
|
a.value = if a.value >= b.value: a.value - b.value else: 0'i64
|
||||||
|
|
||||||
|
func `-=`*(a: var Moment, b: Duration): Moment {.inline.} =
|
||||||
|
## Moment -= Duration
|
||||||
|
a.value -= b.value
|
||||||
|
|
||||||
|
func `==`*(a, b: Duration): bool {.inline.} =
|
||||||
|
## Returns ``true`` if ``a`` equal to ``b``.
|
||||||
|
result = (a.value == b.value)
|
||||||
|
|
||||||
|
func `==`*(a, b: Moment): bool {.inline.} =
|
||||||
|
## Returns ``true`` if ``a`` equal to ``b``.
|
||||||
|
result = (a.value == b.value)
|
||||||
|
|
||||||
|
func `<`*(a, b: Duration): bool {.inline.} =
|
||||||
|
## Returns ``true`` if ``a`` less then ``b``.
|
||||||
|
result = (a.value < b.value)
|
||||||
|
|
||||||
|
func `<`*(a, b: Moment): bool {.inline.} =
|
||||||
|
## Returns ``true`` if ``a`` less then ``b``.
|
||||||
|
result = (a.value < b.value)
|
||||||
|
|
||||||
|
func `<=`*(a, b: Duration): bool {.inline.} =
|
||||||
|
## Returns ``true`` if ``a`` less or equal ``b``.
|
||||||
|
result = (a.value <= b.value)
|
||||||
|
|
||||||
|
func `<=`*(a, b: Moment): bool {.inline.} =
|
||||||
|
## Returns ``true`` if ``a`` less or equal ``b``.
|
||||||
|
result = (a.value <= b.value)
|
||||||
|
|
||||||
|
func `>`*(a, b: Duration): bool {.inline.} =
|
||||||
|
## Returns ``true`` if ``a`` bigger then ``b``.
|
||||||
|
result = (a.value > b.value)
|
||||||
|
|
||||||
|
func `>`*(a, b: Moment): bool {.inline.} =
|
||||||
|
## Returns ``true`` if ``a`` bigger then ``b``.
|
||||||
|
result = (a.value > b.value)
|
||||||
|
|
||||||
|
func `>=`*(a, b: Duration): bool {.inline.} =
|
||||||
|
## Returns ``true`` if ``a`` bigger or equal ``b``.
|
||||||
|
result = (a.value >= b.value)
|
||||||
|
|
||||||
|
func `>=`*(a, b: Moment): bool {.inline.} =
|
||||||
|
## Returns ``true`` if ``a`` bigger or equal ``b``.
|
||||||
|
result = (a.value >= b.value)
|
||||||
|
|
||||||
|
func `*`*(a: Duration, b: SomeIntegerI64): Duration {.inline.} =
|
||||||
|
## Returns Duration multiplied by scalar integer.
|
||||||
|
result.value = a.value * cast[int64](b)
|
||||||
|
|
||||||
|
func `*`*(a: SomeIntegerI64, b: Duration): Duration {.inline.} =
|
||||||
|
## Returns Duration multiplied by scalar integer.
|
||||||
|
result.value = cast[int64](a) * b.value
|
||||||
|
|
||||||
|
func `div`*(a: Duration, b: SomeIntegerI64): Duration {.inline.} =
|
||||||
|
## Returns Duration which is result of dividing a Duration by scalar integer.
|
||||||
|
result.value = a.value div cast[int64](b)
|
||||||
|
|
||||||
|
const
|
||||||
|
Nanosecond* = Duration(value: 1'i64)
|
||||||
|
Microsecond* = Nanosecond * 1_000'i64
|
||||||
|
Millisecond* = Microsecond * 1_000'i64
|
||||||
|
Second* = Millisecond * 1_000'i64
|
||||||
|
Minute* = Second * 60'i64
|
||||||
|
Hour* = Minute * 60'i64
|
||||||
|
Day* = Hour * 24'i64
|
||||||
|
Week* = Day * 7'i64
|
||||||
|
|
||||||
|
ZeroDuration* = Duration(value: 0'i64)
|
||||||
|
InfiniteDuration* = Duration(value: high(int64))
|
||||||
|
|
||||||
|
func nanoseconds*(v: SomeIntegerI64): Duration {.inline.} =
|
||||||
|
## Initialize Duration with nanoseconds value ``v``.
|
||||||
|
result.value = cast[int64](v)
|
||||||
|
|
||||||
|
func microseconds*(v: SomeIntegerI64): Duration {.inline.} =
|
||||||
|
## Initialize Duration with microseconds value ``v``.
|
||||||
|
result.value = cast[int64](v) * Microsecond.value
|
||||||
|
|
||||||
|
func milliseconds*(v: SomeIntegerI64): Duration {.inline.} =
|
||||||
|
## Initialize Duration with milliseconds value ``v``.
|
||||||
|
result.value = cast[int64](v) * Millisecond.value
|
||||||
|
|
||||||
|
func seconds*(v: SomeIntegerI64): Duration {.inline.} =
|
||||||
|
## Initialize Duration with seconds value ``v``.
|
||||||
|
result.value = cast[int64](v) * Second.value
|
||||||
|
|
||||||
|
func minutes*(v: SomeIntegerI64): Duration {.inline.} =
|
||||||
|
## Initialize Duration with minutes value ``v``.
|
||||||
|
result.value = Minute.value * cast[int64](v)
|
||||||
|
|
||||||
|
func hours*(v: SomeIntegerI64): Duration {.inline.} =
|
||||||
|
## Initialize Duration with hours value ``v``.
|
||||||
|
result.value = cast[int64](v) * Hour.value
|
||||||
|
|
||||||
|
func days*(v: SomeIntegerI64): Duration {.inline.} =
|
||||||
|
## Initialize Duration with days value ``v``.
|
||||||
|
result.value = cast[int64](v) * Day.value
|
||||||
|
|
||||||
|
func weeks*(v: SomeIntegerI64): Duration {.inline.} =
|
||||||
|
## Initialize Duration with weeks value ``v``.
|
||||||
|
result.value = cast[int64](v) * Week.value
|
||||||
|
|
||||||
|
func nanoseconds*(v: Duration): int64 {.inline.} =
|
||||||
|
## Round Duration ``v`` to nanoseconds.
|
||||||
|
result = v.value
|
||||||
|
|
||||||
|
func microseconds*(v: Duration): int64 {.inline.} =
|
||||||
|
## Round Duration ``v`` to microseconds.
|
||||||
|
result = v.value div Microsecond.value
|
||||||
|
|
||||||
|
func milliseconds*(v: Duration): int64 {.inline.} =
|
||||||
|
## Round Duration ``v`` to milliseconds.
|
||||||
|
result = v.value div Millisecond.value
|
||||||
|
|
||||||
|
func seconds*(v: Duration): int64 {.inline.} =
|
||||||
|
## Round Duration ``v`` to seconds.
|
||||||
|
result = v.value div Second.value
|
||||||
|
|
||||||
|
func minutes*(v: Duration): int64 {.inline.} =
|
||||||
|
## Round Duration ``v`` to minutes.
|
||||||
|
result = v.value div Minute.value
|
||||||
|
|
||||||
|
func hours*(v: Duration): int64 {.inline.} =
|
||||||
|
## Round Duration ``v`` to hours.
|
||||||
|
result = v.value div Hour.value
|
||||||
|
|
||||||
|
func days*(v: Duration): int64 {.inline.} =
|
||||||
|
## Round Duration ``v`` to days.
|
||||||
|
result = v.value div Day.value
|
||||||
|
|
||||||
|
func weeks*(v: Duration): int64 {.inline.} =
|
||||||
|
## Round Duration ``v`` to weeks.
|
||||||
|
result = v.value div Week.value
|
||||||
|
|
||||||
|
func nanos*(v: SomeIntegerI64): Duration {.inline.} =
|
||||||
|
result = nanoseconds(v)
|
||||||
|
|
||||||
|
func micros*(v: SomeIntegerI64): Duration {.inline.} =
|
||||||
|
result = microseconds(v)
|
||||||
|
|
||||||
|
func millis*(v: SomeIntegerI64): Duration {.inline.} =
|
||||||
|
result = milliseconds(v)
|
||||||
|
|
||||||
|
func secs*(v: SomeIntegerI64): Duration {.inline.} =
|
||||||
|
result = seconds(v)
|
||||||
|
|
||||||
|
func nanos*(v: Duration): int64 {.inline.} =
|
||||||
|
result = nanoseconds(v)
|
||||||
|
|
||||||
|
func micros*(v: Duration): int64 {.inline.} =
|
||||||
|
result = microseconds(v)
|
||||||
|
|
||||||
|
func millis*(v: Duration): int64 {.inline.} =
|
||||||
|
result = milliseconds(v)
|
||||||
|
|
||||||
|
func secs*(v: Duration): int64 {.inline.} =
|
||||||
|
result = seconds(v)
|
||||||
|
|
||||||
|
func `$`*(a: Duration): string {.inline.} =
|
||||||
|
## Returns string representation of Duration ``a`` as nanoseconds value.
|
||||||
|
var v = a.value
|
||||||
|
if v >= Week.value:
|
||||||
|
result = $(v div Week.value) & "w"
|
||||||
|
v = v mod Week.value
|
||||||
|
if v >= Day.value:
|
||||||
|
result &= $(v div Day.value) & "d"
|
||||||
|
v = v mod Day.value
|
||||||
|
if v >= Hour.value:
|
||||||
|
result &= $(v div Hour.value) & "h"
|
||||||
|
v = v mod Hour.value
|
||||||
|
if v >= Minute.value:
|
||||||
|
result &= $(v div Minute.value) & "m"
|
||||||
|
v = v mod Minute.value
|
||||||
|
if v >= Second.value:
|
||||||
|
result &= $(v div Second.value) & "s"
|
||||||
|
v = v mod Second.value
|
||||||
|
if v >= Millisecond.value:
|
||||||
|
result &= $(v div Millisecond.value) & "ms"
|
||||||
|
v = v mod Millisecond.value
|
||||||
|
if v >= Microsecond.value:
|
||||||
|
result &= $(v div Microsecond.value) & "us"
|
||||||
|
v = v mod Microsecond.value
|
||||||
|
if v >= Nanosecond.value:
|
||||||
|
result &= $(v div Nanosecond.value) & "ns"
|
||||||
|
|
||||||
|
func `$`*(a: Moment): string {.inline.} =
|
||||||
|
## Returns string representation of Moment ``a`` as nanoseconds value.
|
||||||
|
result = $(a.value)
|
||||||
|
result.add("ns")
|
||||||
|
|
||||||
|
func isZero*(a: Duration): bool {.inline.} =
|
||||||
|
## Returns ``true`` if Duration ``a`` is ``0``.
|
||||||
|
result = (a.value == 0)
|
||||||
|
|
||||||
|
func isInfinite*(a: Duration): bool {.inline.} =
|
||||||
|
## Returns ``true`` if Duration ``a`` is infinite.
|
||||||
|
result = (a.value == InfiniteDuration.value)
|
||||||
|
|
||||||
|
proc now*(t: typedesc[Moment]): Moment {.inline.} =
|
||||||
|
## Returns current moment in time as Moment.
|
||||||
|
result.value = cast[int64](fastEpochTimeNano())
|
||||||
|
|
||||||
|
func init*(t: typedesc[Moment], value: int64, precision: Duration): Moment =
|
||||||
|
## Initialize Moment with absolute time value ``value`` with precision
|
||||||
|
## ``precision``.
|
||||||
|
result.value = value * precision.value
|
||||||
|
|
||||||
|
proc fromNow*(t: typedesc[Moment], a: Duration): Moment {.inline.} =
|
||||||
|
## Returns moment in time which is equal to current moment + Duration.
|
||||||
|
result = Moment.now() + a
|
||||||
|
|
||||||
|
when defined(posix):
|
||||||
|
from posix import Time, Suseconds, Timeval, Timespec
|
||||||
|
|
||||||
|
func toTimeval*(a: Duration): Timeval =
|
||||||
|
## Convert Duration ``a`` to ``Timeval`` object.
|
||||||
|
let m = a.value mod Second.value
|
||||||
|
result.tv_sec = cast[Time](a.value div Second.value)
|
||||||
|
result.tv_usec = cast[Suseconds](m div Microsecond.value)
|
||||||
|
|
||||||
|
func toTimespec*(a: Duration): Timespec =
|
||||||
|
## Convert Duration ``a`` to ``Timespec`` object.
|
||||||
|
result.tv_sec = cast[Time](a.value div Second.value)
|
||||||
|
result.tv_nsec = cast[int](a.value mod Second.value)
|
||||||
|
|
|
@ -582,7 +582,7 @@ when defined(windows):
|
||||||
if pipeHandle == INVALID_HANDLE_VALUE:
|
if pipeHandle == INVALID_HANDLE_VALUE:
|
||||||
let err = osLastError()
|
let err = osLastError()
|
||||||
if int32(err) == ERROR_PIPE_BUSY:
|
if int32(err) == ERROR_PIPE_BUSY:
|
||||||
addTimer(fastEpochTime() + 50, pipeContinuation, nil)
|
addTimer(Moment.fromNow(50.milliseconds), pipeContinuation, nil)
|
||||||
else:
|
else:
|
||||||
retFuture.fail(getTransportOsError(err))
|
retFuture.fail(getTransportOsError(err))
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -444,7 +444,7 @@ proc testConnReset(): Future[bool] {.async.} =
|
||||||
var dgram2 = newDatagramTransport(clientMark)
|
var dgram2 = newDatagramTransport(clientMark)
|
||||||
var data = "MESSAGE"
|
var data = "MESSAGE"
|
||||||
asyncCheck dgram2.sendTo(ta, data)
|
asyncCheck dgram2.sendTo(ta, data)
|
||||||
await sleepAsync(1000)
|
await sleepAsync(2000.milliseconds)
|
||||||
result = (counter == 0)
|
result = (counter == 0)
|
||||||
dgram2.close()
|
dgram2.close()
|
||||||
await dgram2.join()
|
await dgram2.join()
|
||||||
|
|
|
@ -10,7 +10,7 @@ import unittest
|
||||||
import ../chronos
|
import ../chronos
|
||||||
|
|
||||||
proc testFuture1(): Future[int] {.async.} =
|
proc testFuture1(): Future[int] {.async.} =
|
||||||
await sleepAsync(100)
|
await sleepAsync(1.milliseconds)
|
||||||
|
|
||||||
proc testFuture2(): Future[int] {.async.} =
|
proc testFuture2(): Future[int] {.async.} =
|
||||||
return 1
|
return 1
|
||||||
|
@ -18,11 +18,14 @@ proc testFuture2(): Future[int] {.async.} =
|
||||||
proc testFuture3(): Future[int] {.async.} =
|
proc testFuture3(): Future[int] {.async.} =
|
||||||
result = await testFuture2()
|
result = await testFuture2()
|
||||||
|
|
||||||
|
proc testFuture100(): Future[int] {.async.} =
|
||||||
|
await sleepAsync(100.milliseconds)
|
||||||
|
|
||||||
proc testFuture4(): Future[int] {.async.} =
|
proc testFuture4(): Future[int] {.async.} =
|
||||||
## Test for not immediately completed future and timeout = -1
|
## Test for not immediately completed future and timeout = -1
|
||||||
result = 0
|
result = 0
|
||||||
try:
|
try:
|
||||||
var res = await wait(testFuture1(), -1)
|
var res = await wait(testFuture1(), InfiniteDuration)
|
||||||
result = 1
|
result = 1
|
||||||
except:
|
except:
|
||||||
result = 0
|
result = 0
|
||||||
|
@ -33,7 +36,7 @@ proc testFuture4(): Future[int] {.async.} =
|
||||||
## Test for immediately completed future and timeout = -1
|
## Test for immediately completed future and timeout = -1
|
||||||
result = 0
|
result = 0
|
||||||
try:
|
try:
|
||||||
var res = await wait(testFuture2(), -1)
|
var res = await wait(testFuture2(), InfiniteDuration)
|
||||||
result = 2
|
result = 2
|
||||||
except:
|
except:
|
||||||
result = 0
|
result = 0
|
||||||
|
@ -44,7 +47,7 @@ proc testFuture4(): Future[int] {.async.} =
|
||||||
## Test for not immediately completed future and timeout = 0
|
## Test for not immediately completed future and timeout = 0
|
||||||
result = 0
|
result = 0
|
||||||
try:
|
try:
|
||||||
var res = await wait(testFuture1(), 0)
|
var res = await wait(testFuture1(), 0.milliseconds)
|
||||||
except AsyncTimeoutError:
|
except AsyncTimeoutError:
|
||||||
result = 3
|
result = 3
|
||||||
|
|
||||||
|
@ -54,7 +57,7 @@ proc testFuture4(): Future[int] {.async.} =
|
||||||
## Test for immediately completed future and timeout = 0
|
## Test for immediately completed future and timeout = 0
|
||||||
result = 0
|
result = 0
|
||||||
try:
|
try:
|
||||||
var res = await wait(testFuture2(), 0)
|
var res = await wait(testFuture2(), 0.milliseconds)
|
||||||
result = 4
|
result = 4
|
||||||
except:
|
except:
|
||||||
result = 0
|
result = 0
|
||||||
|
@ -65,7 +68,7 @@ proc testFuture4(): Future[int] {.async.} =
|
||||||
## Test for future which cannot be completed in timeout period
|
## Test for future which cannot be completed in timeout period
|
||||||
result = 0
|
result = 0
|
||||||
try:
|
try:
|
||||||
var res = await wait(testFuture1(), 50)
|
var res = await wait(testFuture100(), 50.milliseconds)
|
||||||
except AsyncTimeoutError:
|
except AsyncTimeoutError:
|
||||||
result = 5
|
result = 5
|
||||||
|
|
||||||
|
@ -74,7 +77,7 @@ proc testFuture4(): Future[int] {.async.} =
|
||||||
|
|
||||||
## Test for future which will be completed before timeout exceeded.
|
## Test for future which will be completed before timeout exceeded.
|
||||||
try:
|
try:
|
||||||
var res = await wait(testFuture1(), 300)
|
var res = await wait(testFuture100(), 500.milliseconds)
|
||||||
result = 6
|
result = 6
|
||||||
except:
|
except:
|
||||||
result = -6
|
result = -6
|
||||||
|
@ -83,6 +86,8 @@ proc test1(): bool =
|
||||||
var fut = testFuture1()
|
var fut = testFuture1()
|
||||||
poll()
|
poll()
|
||||||
poll()
|
poll()
|
||||||
|
if not fut.finished:
|
||||||
|
poll()
|
||||||
result = fut.finished
|
result = fut.finished
|
||||||
|
|
||||||
proc test2(): bool =
|
proc test2(): bool =
|
||||||
|
@ -138,106 +143,106 @@ proc testAllVarargs(): int =
|
||||||
var completedFutures = 0
|
var completedFutures = 0
|
||||||
|
|
||||||
proc vlient1() {.async.} =
|
proc vlient1() {.async.} =
|
||||||
await sleepAsync(100)
|
await sleepAsync(100.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
|
|
||||||
proc vlient2() {.async.} =
|
proc vlient2() {.async.} =
|
||||||
await sleepAsync(200)
|
await sleepAsync(200.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
|
|
||||||
proc vlient3() {.async.} =
|
proc vlient3() {.async.} =
|
||||||
await sleepAsync(300)
|
await sleepAsync(300.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
|
|
||||||
proc vlient4() {.async.} =
|
proc vlient4() {.async.} =
|
||||||
await sleepAsync(400)
|
await sleepAsync(400.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
|
|
||||||
proc vlient5() {.async.} =
|
proc vlient5() {.async.} =
|
||||||
await sleepAsync(500)
|
await sleepAsync(500.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
|
|
||||||
proc vlient1f() {.async.} =
|
proc vlient1f() {.async.} =
|
||||||
await sleepAsync(100)
|
await sleepAsync(100.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
if true:
|
if true:
|
||||||
raise newException(ValueError, "")
|
raise newException(ValueError, "")
|
||||||
|
|
||||||
proc vlient2f() {.async.} =
|
proc vlient2f() {.async.} =
|
||||||
await sleepAsync(100)
|
await sleepAsync(100.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
if true:
|
if true:
|
||||||
raise newException(ValueError, "")
|
raise newException(ValueError, "")
|
||||||
|
|
||||||
proc vlient3f() {.async.} =
|
proc vlient3f() {.async.} =
|
||||||
await sleepAsync(100)
|
await sleepAsync(100.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
if true:
|
if true:
|
||||||
raise newException(ValueError, "")
|
raise newException(ValueError, "")
|
||||||
|
|
||||||
proc vlient4f() {.async.} =
|
proc vlient4f() {.async.} =
|
||||||
await sleepAsync(100)
|
await sleepAsync(100.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
if true:
|
if true:
|
||||||
raise newException(ValueError, "")
|
raise newException(ValueError, "")
|
||||||
|
|
||||||
proc vlient5f() {.async.} =
|
proc vlient5f() {.async.} =
|
||||||
await sleepAsync(100)
|
await sleepAsync(100.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
if true:
|
if true:
|
||||||
raise newException(ValueError, "")
|
raise newException(ValueError, "")
|
||||||
|
|
||||||
proc client1(): Future[int] {.async.} =
|
proc client1(): Future[int] {.async.} =
|
||||||
await sleepAsync(100)
|
await sleepAsync(100.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
result = 1
|
result = 1
|
||||||
|
|
||||||
proc client2(): Future[int] {.async.} =
|
proc client2(): Future[int] {.async.} =
|
||||||
await sleepAsync(200)
|
await sleepAsync(200.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
result = 1
|
result = 1
|
||||||
|
|
||||||
proc client3(): Future[int] {.async.} =
|
proc client3(): Future[int] {.async.} =
|
||||||
await sleepAsync(300)
|
await sleepAsync(300.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
result = 1
|
result = 1
|
||||||
|
|
||||||
proc client4(): Future[int] {.async.} =
|
proc client4(): Future[int] {.async.} =
|
||||||
await sleepAsync(400)
|
await sleepAsync(400.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
result = 1
|
result = 1
|
||||||
|
|
||||||
proc client5(): Future[int] {.async.} =
|
proc client5(): Future[int] {.async.} =
|
||||||
await sleepAsync(500)
|
await sleepAsync(500.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
result = 1
|
result = 1
|
||||||
|
|
||||||
proc client1f(): Future[int] {.async.} =
|
proc client1f(): Future[int] {.async.} =
|
||||||
await sleepAsync(100)
|
await sleepAsync(100.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
if true:
|
if true:
|
||||||
raise newException(ValueError, "")
|
raise newException(ValueError, "")
|
||||||
|
|
||||||
proc client2f(): Future[int] {.async.} =
|
proc client2f(): Future[int] {.async.} =
|
||||||
await sleepAsync(200)
|
await sleepAsync(200.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
if true:
|
if true:
|
||||||
raise newException(ValueError, "")
|
raise newException(ValueError, "")
|
||||||
|
|
||||||
proc client3f(): Future[int] {.async.} =
|
proc client3f(): Future[int] {.async.} =
|
||||||
await sleepAsync(300)
|
await sleepAsync(300.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
if true:
|
if true:
|
||||||
raise newException(ValueError, "")
|
raise newException(ValueError, "")
|
||||||
|
|
||||||
proc client4f(): Future[int] {.async.} =
|
proc client4f(): Future[int] {.async.} =
|
||||||
await sleepAsync(400)
|
await sleepAsync(400.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
if true:
|
if true:
|
||||||
raise newException(ValueError, "")
|
raise newException(ValueError, "")
|
||||||
|
|
||||||
proc client5f(): Future[int] {.async.} =
|
proc client5f(): Future[int] {.async.} =
|
||||||
await sleepAsync(500)
|
await sleepAsync(500.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
if true:
|
if true:
|
||||||
raise newException(ValueError, "")
|
raise newException(ValueError, "")
|
||||||
|
@ -284,106 +289,106 @@ proc testAllSeq(): int =
|
||||||
var nfutures = newSeq[Future[int]]()
|
var nfutures = newSeq[Future[int]]()
|
||||||
|
|
||||||
proc vlient1() {.async.} =
|
proc vlient1() {.async.} =
|
||||||
await sleepAsync(100)
|
await sleepAsync(100.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
|
|
||||||
proc vlient2() {.async.} =
|
proc vlient2() {.async.} =
|
||||||
await sleepAsync(200)
|
await sleepAsync(200.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
|
|
||||||
proc vlient3() {.async.} =
|
proc vlient3() {.async.} =
|
||||||
await sleepAsync(300)
|
await sleepAsync(300.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
|
|
||||||
proc vlient4() {.async.} =
|
proc vlient4() {.async.} =
|
||||||
await sleepAsync(400)
|
await sleepAsync(400.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
|
|
||||||
proc vlient5() {.async.} =
|
proc vlient5() {.async.} =
|
||||||
await sleepAsync(500)
|
await sleepAsync(500.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
|
|
||||||
proc vlient1f() {.async.} =
|
proc vlient1f() {.async.} =
|
||||||
await sleepAsync(100)
|
await sleepAsync(100.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
if true:
|
if true:
|
||||||
raise newException(ValueError, "")
|
raise newException(ValueError, "")
|
||||||
|
|
||||||
proc vlient2f() {.async.} =
|
proc vlient2f() {.async.} =
|
||||||
await sleepAsync(100)
|
await sleepAsync(100.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
if true:
|
if true:
|
||||||
raise newException(ValueError, "")
|
raise newException(ValueError, "")
|
||||||
|
|
||||||
proc vlient3f() {.async.} =
|
proc vlient3f() {.async.} =
|
||||||
await sleepAsync(100)
|
await sleepAsync(100.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
if true:
|
if true:
|
||||||
raise newException(ValueError, "")
|
raise newException(ValueError, "")
|
||||||
|
|
||||||
proc vlient4f() {.async.} =
|
proc vlient4f() {.async.} =
|
||||||
await sleepAsync(100)
|
await sleepAsync(100.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
if true:
|
if true:
|
||||||
raise newException(ValueError, "")
|
raise newException(ValueError, "")
|
||||||
|
|
||||||
proc vlient5f() {.async.} =
|
proc vlient5f() {.async.} =
|
||||||
await sleepAsync(100)
|
await sleepAsync(100.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
if true:
|
if true:
|
||||||
raise newException(ValueError, "")
|
raise newException(ValueError, "")
|
||||||
|
|
||||||
proc client1(): Future[int] {.async.} =
|
proc client1(): Future[int] {.async.} =
|
||||||
await sleepAsync(100)
|
await sleepAsync(100.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
result = 1
|
result = 1
|
||||||
|
|
||||||
proc client2(): Future[int] {.async.} =
|
proc client2(): Future[int] {.async.} =
|
||||||
await sleepAsync(200)
|
await sleepAsync(200.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
result = 1
|
result = 1
|
||||||
|
|
||||||
proc client3(): Future[int] {.async.} =
|
proc client3(): Future[int] {.async.} =
|
||||||
await sleepAsync(300)
|
await sleepAsync(300.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
result = 1
|
result = 1
|
||||||
|
|
||||||
proc client4(): Future[int] {.async.} =
|
proc client4(): Future[int] {.async.} =
|
||||||
await sleepAsync(400)
|
await sleepAsync(400.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
result = 1
|
result = 1
|
||||||
|
|
||||||
proc client5(): Future[int] {.async.} =
|
proc client5(): Future[int] {.async.} =
|
||||||
await sleepAsync(500)
|
await sleepAsync(500.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
result = 1
|
result = 1
|
||||||
|
|
||||||
proc client1f(): Future[int] {.async.} =
|
proc client1f(): Future[int] {.async.} =
|
||||||
await sleepAsync(100)
|
await sleepAsync(100.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
if true:
|
if true:
|
||||||
raise newException(ValueError, "")
|
raise newException(ValueError, "")
|
||||||
|
|
||||||
proc client2f(): Future[int] {.async.} =
|
proc client2f(): Future[int] {.async.} =
|
||||||
await sleepAsync(200)
|
await sleepAsync(200.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
if true:
|
if true:
|
||||||
raise newException(ValueError, "")
|
raise newException(ValueError, "")
|
||||||
|
|
||||||
proc client3f(): Future[int] {.async.} =
|
proc client3f(): Future[int] {.async.} =
|
||||||
await sleepAsync(300)
|
await sleepAsync(300.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
if true:
|
if true:
|
||||||
raise newException(ValueError, "")
|
raise newException(ValueError, "")
|
||||||
|
|
||||||
proc client4f(): Future[int] {.async.} =
|
proc client4f(): Future[int] {.async.} =
|
||||||
await sleepAsync(400)
|
await sleepAsync(400.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
if true:
|
if true:
|
||||||
raise newException(ValueError, "")
|
raise newException(ValueError, "")
|
||||||
|
|
||||||
proc client5f(): Future[int] {.async.} =
|
proc client5f(): Future[int] {.async.} =
|
||||||
await sleepAsync(500)
|
await sleepAsync(500.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
if true:
|
if true:
|
||||||
raise newException(ValueError, "")
|
raise newException(ValueError, "")
|
||||||
|
@ -464,51 +469,51 @@ proc testAsyncDiscard(): int =
|
||||||
var completedFutures = 0
|
var completedFutures = 0
|
||||||
|
|
||||||
proc client1() {.async.} =
|
proc client1() {.async.} =
|
||||||
await sleepAsync(100)
|
await sleepAsync(100.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
|
|
||||||
proc client2() {.async.} =
|
proc client2() {.async.} =
|
||||||
await sleepAsync(200)
|
await sleepAsync(200.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
|
|
||||||
proc client3() {.async.} =
|
proc client3() {.async.} =
|
||||||
await sleepAsync(300)
|
await sleepAsync(300.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
|
|
||||||
proc client4() {.async.} =
|
proc client4() {.async.} =
|
||||||
await sleepAsync(400)
|
await sleepAsync(400.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
|
|
||||||
proc client5() {.async.} =
|
proc client5() {.async.} =
|
||||||
await sleepAsync(500)
|
await sleepAsync(500.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
|
|
||||||
proc client1f() {.async.} =
|
proc client1f() {.async.} =
|
||||||
await sleepAsync(100)
|
await sleepAsync(100.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
if true:
|
if true:
|
||||||
raise newException(ValueError, "")
|
raise newException(ValueError, "")
|
||||||
|
|
||||||
proc client2f() {.async.} =
|
proc client2f() {.async.} =
|
||||||
await sleepAsync(200)
|
await sleepAsync(200.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
if true:
|
if true:
|
||||||
raise newException(ValueError, "")
|
raise newException(ValueError, "")
|
||||||
|
|
||||||
proc client3f() {.async.} =
|
proc client3f() {.async.} =
|
||||||
await sleepAsync(300)
|
await sleepAsync(300.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
if true:
|
if true:
|
||||||
raise newException(ValueError, "")
|
raise newException(ValueError, "")
|
||||||
|
|
||||||
proc client4f() {.async.} =
|
proc client4f() {.async.} =
|
||||||
await sleepAsync(400)
|
await sleepAsync(400.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
if true:
|
if true:
|
||||||
raise newException(ValueError, "")
|
raise newException(ValueError, "")
|
||||||
|
|
||||||
proc client5f() {.async.} =
|
proc client5f() {.async.} =
|
||||||
await sleepAsync(500)
|
await sleepAsync(500.milliseconds)
|
||||||
inc(completedFutures)
|
inc(completedFutures)
|
||||||
if true:
|
if true:
|
||||||
raise newException(ValueError, "")
|
raise newException(ValueError, "")
|
||||||
|
@ -524,7 +529,7 @@ proc testAsyncDiscard(): int =
|
||||||
asyncDiscard client5()
|
asyncDiscard client5()
|
||||||
asyncDiscard client5f()
|
asyncDiscard client5f()
|
||||||
|
|
||||||
waitFor(sleepAsync(2000))
|
waitFor(sleepAsync(2000.milliseconds))
|
||||||
result = completedFutures
|
result = completedFutures
|
||||||
|
|
||||||
proc testAllZero(): bool =
|
proc testAllZero(): bool =
|
||||||
|
|
|
@ -20,7 +20,7 @@ when not defined(windows):
|
||||||
removeSignal(int(cdata.fd))
|
removeSignal(int(cdata.fd))
|
||||||
|
|
||||||
proc asyncProc() {.async.} =
|
proc asyncProc() {.async.} =
|
||||||
await sleepAsync(500)
|
await sleepAsync(500.milliseconds)
|
||||||
|
|
||||||
proc test(signal, value: int): bool =
|
proc test(signal, value: int): bool =
|
||||||
discard addSignal(signal, signalProc, cast[pointer](value))
|
discard addSignal(signal, signalProc, cast[pointer](value))
|
||||||
|
|
|
@ -41,7 +41,7 @@ proc test1(): uint =
|
||||||
|
|
||||||
proc testProc() {.async.} =
|
proc testProc() {.async.} =
|
||||||
for i in 1..CallSoonTests:
|
for i in 1..CallSoonTests:
|
||||||
await sleepAsync(100)
|
await sleepAsync(100.milliseconds)
|
||||||
timeoutsTest1 += 1
|
timeoutsTest1 += 1
|
||||||
|
|
||||||
proc callbackProc(udata: pointer) {.gcsafe.} =
|
proc callbackProc(udata: pointer) {.gcsafe.} =
|
||||||
|
|
|
@ -6,16 +6,16 @@
|
||||||
# Apache License, version 2.0, (LICENSE-APACHEv2)
|
# Apache License, version 2.0, (LICENSE-APACHEv2)
|
||||||
# MIT license (LICENSE-MIT)
|
# MIT license (LICENSE-MIT)
|
||||||
|
|
||||||
import unittest
|
import os, unittest
|
||||||
import ../chronos, ../chronos/timer
|
import ../chronos, ../chronos/timer
|
||||||
|
|
||||||
const TimersCount = 10
|
const TimersCount = 10
|
||||||
|
|
||||||
proc timeWorker(time: int): Future[int] {.async.} =
|
proc timeWorker(time: Duration): Future[Duration] {.async.} =
|
||||||
var st = fastEpochTime()
|
var st = Moment.now()
|
||||||
await sleepAsync(time)
|
await sleepAsync(time)
|
||||||
var et = fastEpochTime()
|
var et = Moment.now()
|
||||||
result = int(et - st)
|
result = et - st
|
||||||
|
|
||||||
proc waitAll[T](futs: seq[Future[T]]): Future[void] =
|
proc waitAll[T](futs: seq[Future[T]]): Future[void] =
|
||||||
var counter = len(futs)
|
var counter = len(futs)
|
||||||
|
@ -28,25 +28,34 @@ proc waitAll[T](futs: seq[Future[T]]): Future[void] =
|
||||||
fut.addCallback(cb)
|
fut.addCallback(cb)
|
||||||
return retFuture
|
return retFuture
|
||||||
|
|
||||||
proc test(timeout: int): Future[int64] {.async.} =
|
proc test(timeout: Duration): Future[Duration] {.async.} =
|
||||||
var workers = newSeq[Future[int]](TimersCount)
|
var workers = newSeq[Future[Duration]](TimersCount)
|
||||||
for i in 0..<TimersCount:
|
for i in 0..<TimersCount:
|
||||||
workers[i] = timeWorker(timeout)
|
workers[i] = timeWorker(timeout)
|
||||||
await waitAll(workers)
|
await waitAll(workers)
|
||||||
var sum = 0'i64
|
var sum: Duration
|
||||||
for i in 0..<TimersCount:
|
for i in 0..<TimersCount:
|
||||||
var time = workers[i].read()
|
var time = workers[i].read()
|
||||||
sum = sum + time
|
sum = sum + time
|
||||||
result = sum div 10'i64
|
result = sum div 10'i64
|
||||||
|
|
||||||
|
proc testTimer(): bool =
|
||||||
|
let a = Moment.now()
|
||||||
|
waitFor(sleepAsync(1000.milliseconds))
|
||||||
|
let b = Moment.now()
|
||||||
|
let d = b - a
|
||||||
|
result = (d >= 1000.milliseconds) and (d <= 2_000.milliseconds)
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
suite "Asynchronous timers test suite":
|
suite "Asynchronous timers test suite":
|
||||||
|
test "Timer reliability test [" & asyncTimer & "]":
|
||||||
|
check testTimer() == true
|
||||||
test $TimersCount & " timers with 10ms timeout":
|
test $TimersCount & " timers with 10ms timeout":
|
||||||
var res = waitFor(test(10))
|
var res = waitFor(test(10.milliseconds))
|
||||||
check (res >= 10) and (res <= 100)
|
check (res >= 10.milliseconds) and (res <= 100.milliseconds)
|
||||||
test $TimersCount & " timers with 100ms timeout":
|
test $TimersCount & " timers with 100ms timeout":
|
||||||
var res = waitFor(test(100))
|
var res = waitFor(test(100.milliseconds))
|
||||||
check (res >= 100) and (res <= 1000)
|
check (res >= 100.milliseconds) and (res <= 1000.milliseconds)
|
||||||
test $TimersCount & " timers with 1000ms timeout":
|
test $TimersCount & " timers with 1000ms timeout":
|
||||||
var res = waitFor(test(1000))
|
var res = waitFor(test(1000.milliseconds))
|
||||||
check (res >= 1000) and (res <= 5000)
|
check (res >= 1000.milliseconds) and (res <= 5000.milliseconds)
|
||||||
|
|
Loading…
Reference in New Issue