From 2565d3d102efd21ba02ed1f3b96d892fe2637d2b Mon Sep 17 00:00:00 2001 From: PMunch Date: Sun, 22 Sep 2019 07:24:14 +0200 Subject: [PATCH] Fix issue with long wait for passed timer [bugfix] (#12221) * Fix issue with long wait for passed timer [bugfix] This fixes a small issue where a timer that had been completed while code executed would still wait for more events before being considered completed. This would in some scenarios incur a 500ms delay to the completion of a timer. * Refactor logic into * Add test case based on original issue * Use longer timeouts to be more lenient in checking * Revert to short timeouts, but widen the accepted range * Widen accepted range further, it is meant to check for a 500ms delay after all * Increase poll timeout to make it easier to detect mistakes --- lib/pure/asyncdispatch.nim | 12 ++++++--- tests/async/t12221.nim | 52 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 3 deletions(-) create mode 100644 tests/async/t12221.nim diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index 7c1b701b81..f4dc3de392 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -210,7 +210,12 @@ proc processPendingCallbacks(p: PDispatcherBase; didSomeWork: var bool) = cb() didSomeWork = true -proc adjustTimeout(pollTimeout: int, nextTimer: Option[int]): int {.inline.} = +proc adjustTimeout( + p: PDispatcherBase, pollTimeout: int, nextTimer: Option[int] +): int {.inline.} = + if p.callbacks.len != 0: + return 0 + if nextTimer.isNone() or pollTimeout == -1: return pollTimeout @@ -324,7 +329,7 @@ when defined(windows) or defined(nimdoc): result = false let nextTimer = processTimers(p, result) - let at = adjustTimeout(timeout, nextTimer) + let at = adjustTimeout(p, timeout, nextTimer) var llTimeout = if at == -1: winlean.INFINITE else: at.int32 @@ -1284,7 +1289,8 @@ else: result = false var keys: array[64, ReadyKey] let nextTimer = processTimers(p, result) - var count = p.selector.selectInto(adjustTimeout(timeout, nextTimer), keys) + var count = + p.selector.selectInto(adjustTimeout(p, timeout, nextTimer), keys) for i in 0.. target - 1000, "Took too short, should've taken " & + $target & "ms, but took " & $(timeTook*1000) & "ms") + assert(timeTook*1000 < target + 1000, "Took too long, should've taken " & + $target & "ms, but took " & $(timeTook*1000) & "ms") + +var + start: float + fut: Future[void] + +# NOTE: this uses poll(3000) to limit timing error potential. +start = epochTime() +fut = sleepAsync(50) and sleepAsync(150) and doubleSleep(40) +while not fut.finished: + poll(3000) +assertTime(150, epochTime() - start) + +start = epochTime() +fut = sleepAsync(50) and sleepAsync(150) and doubleSleep(100) +while not fut.finished: + poll(3000) +assertTime(200, epochTime() - start) + +start = epochTime() +fut = sleepAsync(50) and sleepAsync(150) and doubleSleep(40) and sleepAsync(300) +while not fut.finished: + poll(3000) +assertTime(300, epochTime() - start) + +start = epochTime() +fut = sleepAsync(50) and sleepAsync(150) and doubleSleep(100) and sleepAsync(300) +while not fut.finished: + poll(3000) +assertTime(300, epochTime() - start) + +start = epochTime() +fut = (sleepAsync(50) and sleepAsync(150) and doubleSleep(40)) or sleepAsync(700) +while not fut.finished: + poll(3000) +assertTime(150, epochTime() - start) + +start = epochTime() +fut = (sleepAsync(50) and sleepAsync(150) and doubleSleep(100)) or sleepAsync(700) +while not fut.finished: + poll(3000) +assertTime(200, epochTime() - start)