Skip to content
Permalink
Browse files

Use proper timers in sleepAsync.

Fixes #7886.

Fixes #7758 (remember that `poll` waits for 500ms, change the
             timeout to something like 5s and you'll get
             1 poll call for that code)

Fixes #6929.

Fixes #3909.
  • Loading branch information...
dom96 committed Jul 5, 2018
1 parent a1457bf commit df22afbd67d4baca2ebdf3aee51f3558f6ca1484
Showing with 8 additions and 29 deletions.
  1. +8 −29 lib/pure/asyncdispatch.nim
@@ -165,35 +165,14 @@ export asyncfutures, asyncstreams

type
PDispatcherBase = ref object of RootRef
timers*: HeapQueue[tuple[finishAt: float, fut: Future[void]]]
callbacks*: Deque[proc ()]

proc processTimers(p: PDispatcherBase; didSomeWork: var bool) {.inline.} =
#Process just part if timers at a step
var count = p.timers.len
let t = epochTime()
while count > 0 and t >= p.timers[0].finishAt:
p.timers.pop().fut.complete()
dec count
didSomeWork = true

proc processPendingCallbacks(p: PDispatcherBase; didSomeWork: var bool) =
while p.callbacks.len > 0:
var cb = p.callbacks.popFirst()
cb()
didSomeWork = true

proc adjustedTimeout(p: PDispatcherBase, timeout: int): int {.inline.} =
# If dispatcher has active timers this proc returns the timeout
# of the nearest timer. Returns `timeout` otherwise.
result = timeout
if p.timers.len > 0:
let timerTimeout = p.timers[0].finishAt
let curTime = epochTime()
if timeout == -1 or (curTime + (timeout / 1000)) > timerTimeout:
result = int((timerTimeout - curTime) * 1000)
if result < 0: result = 0

proc callSoon(cbproc: proc ()) {.gcsafe.}

proc initCallSoonProc =
@@ -1088,7 +1067,6 @@ else:
proc newDispatcher*(): PDispatcher =
new result
result.selector = newSelector[AsyncData]()
result.timers.newHeapQueue()
result.callbacks = initDeque[proc ()](InitDelayedCallbackListSize)

var gDisp{.threadvar.}: PDispatcher ## Global dispatcher
@@ -1150,7 +1128,7 @@ else:

proc hasPendingOperations*(): bool =
let p = getGlobalDispatcher()
not p.selector.isEmpty() or p.timers.len != 0 or p.callbacks.len != 0
not p.selector.isEmpty() or p.callbacks.len != 0

template processBasicCallbacks(ident, rwlist: untyped) =
# Process pending descriptor and AsyncEvent callbacks.
@@ -1226,14 +1204,14 @@ else:
let customSet = {Event.Timer, Event.Signal, Event.Process,
Event.Vnode}

if p.selector.isEmpty() and p.timers.len == 0 and p.callbacks.len == 0:
if p.selector.isEmpty() and p.callbacks.len == 0:
raise newException(ValueError,
"No handles or timers registered in dispatcher.")

result = false
if not p.selector.isEmpty():
var keys: array[64, ReadyKey]
var count = p.selector.selectInto(p.adjustedTimeout(timeout), keys)
var count = p.selector.selectInto(timeout, keys)
for i in 0..<count:
var custom = false
let fd = keys[i].fd
@@ -1271,8 +1249,6 @@ else:
if wLength > 0: incl(newEvents, Event.Write)
p.selector.updateHandle(SocketHandle(fd), newEvents)

# Timer processing.
processTimers(p, result)
# Callback queue processing
processPendingCallbacks(p, result)

@@ -1517,8 +1493,11 @@ proc sleepAsync*(ms: int | float): Future[void] =
## Suspends the execution of the current async procedure for the next
## ``ms`` milliseconds.
var retFuture = newFuture[void]("sleepAsync")
let p = getGlobalDispatcher()
p.timers.push((epochTime() + (ms / 1000), retFuture))
addTimer(
ms,
oneshot=true,
proc (fd: AsyncFD): bool = retFuture.complete()
)
return retFuture

proc withTimeout*[T](fut: Future[T], timeout: int): Future[bool] =

0 comments on commit df22afb

Please sign in to comment.
You can’t perform that action at this time.