diff --git a/lib/pure/ioselectors.nim b/lib/pure/ioselectors.nim index 28e6ef3d0d..5745d0b725 100644 --- a/lib/pure/ioselectors.nim +++ b/lib/pure/ioselectors.nim @@ -18,10 +18,12 @@ ## Supported features: files, sockets, pipes, timers, processes, signals ## and user events. ## -## Fully supported OS: MacOSX, FreeBSD, OpenBSD, NetBSD, Linux. +## Fully supported OS: MacOSX, FreeBSD, OpenBSD, NetBSD, Linux (except +## for Android). ## ## Partially supported OS: Windows (only sockets and user events), ## Solaris (files, sockets, handles and user events). +## Android (files, sockets, handles and user events). ## ## TODO: ``/dev/poll``, ``event ports`` and filesystem events. @@ -29,9 +31,11 @@ import os const hasThreadSupport = compileOption("threads") and defined(threadsafe) -const supportedPlatform = defined(macosx) or defined(freebsd) or - defined(netbsd) or defined(openbsd) or - (defined(linux) and not defined(android)) +const ioselSupportedPlatform* = defined(macosx) or defined(freebsd) or + defined(netbsd) or defined(openbsd) or + (defined(linux) and not defined(android)) + ## This constant is used to determine whether the destination platform is + ## fully supported by ``ioselectors`` module. const bsdPlatform = defined(macosx) or defined(freebsd) or defined(netbsd) or defined(openbsd) @@ -244,7 +248,7 @@ else: skey.key.fd = pkeyfd skey.key.data = pdata - when supportedPlatform: + when ioselSupportedPlatform: template blockSignals(newmask: var Sigset, oldmask: var Sigset) = when hasThreadSupport: if posix.pthread_sigmask(SIG_BLOCK, newmask, oldmask) == -1: diff --git a/lib/upcoming/asyncdispatch.nim b/lib/upcoming/asyncdispatch.nim index 9a35cf3c89..ca5c1f64c3 100644 --- a/lib/upcoming/asyncdispatch.nim +++ b/lib/upcoming/asyncdispatch.nim @@ -1092,11 +1092,6 @@ else: import ioselectors from posix import EINTR, EAGAIN, EINPROGRESS, EWOULDBLOCK, MSG_PEEK, MSG_NOSIGNAL - - const supportedPlatform = defined(linux) or defined(freebsd) or - defined(netbsd) or defined(openbsd) or - defined(macosx) - type AsyncFD* = distinct cint Callback = proc (fd: AsyncFD): bool {.closure,gcsafe.} @@ -1191,7 +1186,7 @@ else: var keys: array[64, ReadyKey[AsyncData]] let p = getGlobalDispatcher() - when supportedPlatform: + when ioselSupportedPlatform: let customSet = {Event.Timer, Event.Signal, Event.Process, Event.Vnode, Event.User} @@ -1225,7 +1220,7 @@ else: else: break - when supportedPlatform: + when ioselSupportedPlatform: if (customSet * events) != {}: for node in keys[i].data.readCBs[].nodes(): let cb = node.value @@ -1234,6 +1229,15 @@ else: if cb(fd.AsyncFD): keys[i].data.readCBs[].remove(node) p.selector.unregister(fd) + else: + if Event.User in events or events == {Event.Error}: + for node in keys[i].data.readCBs[].nodes(): + let cb = node.value + custom = true + if cb != nil: + if cb(fd.AsyncFD): + keys[i].data.readCBs[].remove(node) + p.selector.unregister(fd) # because state `data` can be modified in callback we need to update # descriptor events with currently registered callbacks. @@ -1496,7 +1500,7 @@ else: addRead(socket, cb) return retFuture - when supportedPlatform: + when ioselSupportedPlatform: proc addTimer*(timeout: int, oneshot: bool, cb: Callback) = ## Start watching for timeout expiration, and then call the diff --git a/tests/async/tioselectors.nim b/tests/async/tioselectors.nim index 71d901e69a..e6b4196a38 100644 --- a/tests/async/tioselectors.nim +++ b/tests/async/tioselectors.nim @@ -12,11 +12,10 @@ template processTest(t, x: untyped) = if not x: echo(t & " FAILED\r\n") when not defined(windows): - import os, posix, osproc, nativesockets, times + import os, posix, nativesockets, times - const supportedPlatform = defined(macosx) or defined(freebsd) or - defined(netbsd) or defined(openbsd) or - defined(linux) + when ioselSupportedPlatform: + import osproc proc socket_notification_test(): bool = proc create_test_socket(): SocketHandle = @@ -143,7 +142,7 @@ when not defined(windows): selector.close() result = true - when supportedPlatform: + when ioselSupportedPlatform: proc timer_notification_test(): bool = var selector = newSelector[int]() var timer = selector.registerTimer(100, false, 0) @@ -462,7 +461,7 @@ when not defined(windows): when hasThreadSupport: processTest("Multithreaded user event notification test...", mt_event_test()) - when supportedPlatform: + when ioselSupportedPlatform: processTest("Timer notification test...", timer_notification_test()) processTest("Process notification test...", process_notification_test()) processTest("Signal notification test...", signal_notification_test()) diff --git a/tests/async/tupcoming_async.nim b/tests/async/tupcoming_async.nim index 137794afd1..7d255f2133 100644 --- a/tests/async/tupcoming_async.nim +++ b/tests/async/tupcoming_async.nim @@ -8,11 +8,12 @@ OK """ when defined(upcoming): - import asyncdispatch, times, osproc, streams + import asyncdispatch, times, streams, posix + from ioselectors import ioselSupportedPlatform - const supportedPlatform = defined(linux) or defined(freebsd) or - defined(netbsd) or defined(openbsd) or - defined(macosx) + proc delayedSet(ev: AsyncEvent, timeout: int): Future[void] {.async.} = + await sleepAsync(timeout) + ev.setEvent() proc waitEvent(ev: AsyncEvent, closeEvent = false): Future[void] = var retFuture = newFuture[void]("waitEvent") @@ -25,56 +26,55 @@ when defined(upcoming): addEvent(ev, cb) return retFuture - proc waitTimer(timeout: int): Future[void] = - var retFuture = newFuture[void]("waitTimer") - proc cb(fd: AsyncFD): bool = - retFuture.complete() - addTimer(timeout, true, cb) - return retFuture - - proc waitProcess(p: Process): Future[void] = - var retFuture = newFuture[void]("waitProcess") - proc cb(fd: AsyncFD): bool = - retFuture.complete() - addProcess(p.processID(), cb) - return retFuture - - proc delayedSet(ev: AsyncEvent, timeout: int): Future[void] {.async.} = - await waitTimer(timeout) - ev.setEvent() - - proc timerTest() = - waitFor(waitTimer(200)) - echo "OK" - proc eventTest() = var event = newAsyncEvent() var fut = waitEvent(event) asyncCheck(delayedSet(event, 500)) - waitFor(fut or waitTimer(1000)) + waitFor(fut or sleepAsync(1000)) if fut.finished: echo "OK" else: echo "eventTest: Timeout expired before event received!" - proc processTest() = - when defined(windows): - var process = startProcess("ping.exe", "", - ["127.0.0.1", "-n", "2", "-w", "100"], nil, - {poStdErrToStdOut, poUsePath, poInteractive, - poDemon}) - else: - var process = startProcess("/bin/sleep", "", ["1"], nil, - {poStdErrToStdOut, poUsePath}) - var fut = waitProcess(process) - waitFor(fut or waitTimer(2000)) - if fut.finished and process.peekExitCode() == 0: - echo "OK" - else: - echo "processTest: Timeout expired before process exited!" + when ioselSupportedPlatform or defined(windows): - when supportedPlatform: - import posix + import osproc + + proc waitTimer(timeout: int): Future[void] = + var retFuture = newFuture[void]("waitTimer") + proc cb(fd: AsyncFD): bool = + retFuture.complete() + addTimer(timeout, true, cb) + return retFuture + + proc waitProcess(p: Process): Future[void] = + var retFuture = newFuture[void]("waitProcess") + proc cb(fd: AsyncFD): bool = + retFuture.complete() + addProcess(p.processID(), cb) + return retFuture + + proc timerTest() = + waitFor(waitTimer(200)) + echo "OK" + + proc processTest() = + when defined(windows): + var process = startProcess("ping.exe", "", + ["127.0.0.1", "-n", "2", "-w", "100"], nil, + {poStdErrToStdOut, poUsePath, poInteractive, + poDemon}) + else: + var process = startProcess("/bin/sleep", "", ["1"], nil, + {poStdErrToStdOut, poUsePath}) + var fut = waitProcess(process) + waitFor(fut or waitTimer(2000)) + if fut.finished and process.peekExitCode() == 0: + echo "OK" + else: + echo "processTest: Timeout expired before process exited!" + + when ioselSupportedPlatform: proc waitSignal(signal: int): Future[void] = var retFuture = newFuture[void]("waitSignal") @@ -97,7 +97,7 @@ when defined(upcoming): else: echo "signalTest: Timeout expired before signal received!" - when supportedPlatform: + when ioselSupportedPlatform: timerTest() eventTest() processTest()