From 7532b37405c9365f3f2935633111f309234297b2 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Thu, 23 Aug 2018 15:31:28 +0100 Subject: [PATCH 1/3] Don't skip poll() when no handles are present. (#8727) Fixes #7886. Fixes #7758. Fixes #6929. Fixes #3909. Replaces #8209. --- lib/pure/asyncdispatch.nim | 136 ++++++++++++++++++------------------- tests/async/t7758.nim | 17 +++++ 2 files changed, 84 insertions(+), 69 deletions(-) create mode 100644 tests/async/t7758.nim diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index dfc7201b8c..df37cab37f 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -299,50 +299,49 @@ when defined(windows) or defined(nimdoc): "No handles or timers registered in dispatcher.") result = false - if p.handles.len != 0: - let at = p.adjustedTimeout(timeout) - var llTimeout = - if at == -1: winlean.INFINITE - else: at.int32 + let at = p.adjustedTimeout(timeout) + var llTimeout = + if at == -1: winlean.INFINITE + else: at.int32 - var lpNumberOfBytesTransferred: Dword - var lpCompletionKey: ULONG_PTR - var customOverlapped: PCustomOverlapped - let res = getQueuedCompletionStatus(p.ioPort, - addr lpNumberOfBytesTransferred, addr lpCompletionKey, - cast[ptr POVERLAPPED](addr customOverlapped), llTimeout).bool - result = true + var lpNumberOfBytesTransferred: Dword + var lpCompletionKey: ULONG_PTR + var customOverlapped: PCustomOverlapped + let res = getQueuedCompletionStatus(p.ioPort, + addr lpNumberOfBytesTransferred, addr lpCompletionKey, + cast[ptr POVERLAPPED](addr customOverlapped), llTimeout).bool + result = true - # http://stackoverflow.com/a/12277264/492186 - # TODO: http://www.serverframework.com/handling-multiple-pending-socket-read-and-write-operations.html - if res: - # This is useful for ensuring the reliability of the overlapped struct. + # http://stackoverflow.com/a/12277264/492186 + # TODO: http://www.serverframework.com/handling-multiple-pending-socket-read-and-write-operations.html + if res: + # This is useful for ensuring the reliability of the overlapped struct. + assert customOverlapped.data.fd == lpCompletionKey.AsyncFD + + customOverlapped.data.cb(customOverlapped.data.fd, + lpNumberOfBytesTransferred, OSErrorCode(-1)) + + # If cell.data != nil, then system.protect(rawEnv(cb)) was called, + # so we need to dispose our `cb` environment, because it is not needed + # anymore. + if customOverlapped.data.cell.data != nil: + system.dispose(customOverlapped.data.cell) + + GC_unref(customOverlapped) + else: + let errCode = osLastError() + if customOverlapped != nil: assert customOverlapped.data.fd == lpCompletionKey.AsyncFD - customOverlapped.data.cb(customOverlapped.data.fd, - lpNumberOfBytesTransferred, OSErrorCode(-1)) - - # If cell.data != nil, then system.protect(rawEnv(cb)) was called, - # so we need to dispose our `cb` environment, because it is not needed - # anymore. + lpNumberOfBytesTransferred, errCode) if customOverlapped.data.cell.data != nil: system.dispose(customOverlapped.data.cell) - GC_unref(customOverlapped) else: - let errCode = osLastError() - if customOverlapped != nil: - assert customOverlapped.data.fd == lpCompletionKey.AsyncFD - customOverlapped.data.cb(customOverlapped.data.fd, - lpNumberOfBytesTransferred, errCode) - if customOverlapped.data.cell.data != nil: - system.dispose(customOverlapped.data.cell) - GC_unref(customOverlapped) - else: - if errCode.int32 == WAIT_TIMEOUT: - # Timed out - result = false - else: raiseOSError(errCode) + if errCode.int32 == WAIT_TIMEOUT: + # Timed out + result = false + else: raiseOSError(errCode) # Timer processing. processTimers(p, result) @@ -1231,45 +1230,44 @@ else: "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) - for i in 0.. 0: incl(newEvents, Event.Read) - if wLength > 0: incl(newEvents, Event.Write) - p.selector.updateHandle(SocketHandle(fd), newEvents) + # because state `data` can be modified in callback we need to update + # descriptor events with currently registered callbacks. + if not custom: + var newEvents: set[Event] = {} + if rLength != -1 and wLength != -1: + if rLength > 0: incl(newEvents, Event.Read) + if wLength > 0: incl(newEvents, Event.Write) + p.selector.updateHandle(SocketHandle(fd), newEvents) # Timer processing. processTimers(p, result) diff --git a/tests/async/t7758.nim b/tests/async/t7758.nim new file mode 100644 index 0000000000..57d0f4004c --- /dev/null +++ b/tests/async/t7758.nim @@ -0,0 +1,17 @@ +discard """ + file: "t7758.nim" + exitcode: 0 +""" +import asyncdispatch + +proc task() {.async.} = + await sleepAsync(1000) + +when isMainModule: + var counter = 0 + var f = task() + while not f.finished: + inc(counter) + poll() + +doAssert counter == 2 \ No newline at end of file From 42333d27d0165e0e28e39ee7b409e0ae301e241f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oscar=20Nihlg=C3=A5rd?= Date: Thu, 23 Aug 2018 17:55:53 +0200 Subject: [PATCH 2/3] Don't assume utcOffset == +0 for old dates on Windows (#8744) --- lib/pure/times.nim | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/pure/times.nim b/lib/pure/times.nim index 6251c70d91..9f6ea5722b 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -957,6 +957,17 @@ else: result.inc tm.second proc getLocalOffsetAndDst(unix: int64): tuple[offset: int, dst: bool] = + # Windows can't handle unix < 0, so we fall back to unix = 0. + # FIXME: This should be improved by falling back to the WinAPI instead. + when defined(windows): + if unix < 0: + var a = 0.CTime + let tmPtr = localtime(addr(a)) + if not tmPtr.isNil: + let tm = tmPtr[] + return ((0 - tm.toAdjUnix).int, false) + return (0, false) + var a = unix.CTime let tmPtr = localtime(addr(a)) if not tmPtr.isNil: From 964f1ff7a77e0cc1c73f12d02969421c36d32e1c Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Thu, 23 Aug 2018 17:38:33 +0100 Subject: [PATCH 3/3] Adds test case for #6846. (#8729) --- tests/async/t6846.nim | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 tests/async/t6846.nim diff --git a/tests/async/t6846.nim b/tests/async/t6846.nim new file mode 100644 index 0000000000..687a3f8654 --- /dev/null +++ b/tests/async/t6846.nim @@ -0,0 +1,16 @@ +discard """ + exitcode: 0 + output: "hello world" + disabled: windows +""" + +import asyncdispatch +import asyncfile +import times + +var asyncStdout = 1.AsyncFD.newAsyncFile() +proc doStuff: Future[void] {.async.} = + await asyncStdout.write "hello world\n" + +let fut = doStuff() +doAssert fut.finished, "Poll is needed unnecessarily. See #6846." \ No newline at end of file