Merge pull request #4725 from cheatfate/fixtest_upcoming
upcoming_async: Fix multiple issues and add test.
This commit is contained in:
commit
e554c1d7cf
2 changed files with 159 additions and 25 deletions
|
|
@ -1163,7 +1163,7 @@ when defined(windows) or defined(nimdoc):
|
||||||
## receiving notifies.
|
## receiving notifies.
|
||||||
registerWaitableEvent(FD_WRITE or FD_CONNECT or FD_CLOSE)
|
registerWaitableEvent(FD_WRITE or FD_CONNECT or FD_CLOSE)
|
||||||
|
|
||||||
template registerWaitableHandle(p, hEvent, flags, pcd, handleCallback) =
|
template registerWaitableHandle(p, hEvent, flags, pcd, timeout, handleCallback) =
|
||||||
let handleFD = AsyncFD(hEvent)
|
let handleFD = AsyncFD(hEvent)
|
||||||
pcd.ioPort = p.ioPort
|
pcd.ioPort = p.ioPort
|
||||||
pcd.handleFd = handleFD
|
pcd.handleFd = handleFD
|
||||||
|
|
@ -1177,10 +1177,10 @@ when defined(windows) or defined(nimdoc):
|
||||||
pcd.ovl = ol
|
pcd.ovl = ol
|
||||||
if not registerWaitForSingleObject(addr(pcd.waitFd), hEvent,
|
if not registerWaitForSingleObject(addr(pcd.waitFd), hEvent,
|
||||||
cast[WAITORTIMERCALLBACK](waitableCallback),
|
cast[WAITORTIMERCALLBACK](waitableCallback),
|
||||||
cast[pointer](pcd), INFINITE, flags):
|
cast[pointer](pcd), timeout.Dword, flags):
|
||||||
GC_unref(ol)
|
GC_unref(ol)
|
||||||
deallocShared(cast[pointer](pcd))
|
deallocShared(cast[pointer](pcd))
|
||||||
discard wsaCloseEvent(hEvent)
|
discard closeHandle(hEvent)
|
||||||
raiseOSError(osLastError())
|
raiseOSError(osLastError())
|
||||||
p.handles.incl(handleFD)
|
p.handles.incl(handleFD)
|
||||||
|
|
||||||
|
|
@ -1212,7 +1212,7 @@ when defined(windows) or defined(nimdoc):
|
||||||
deallocShared(cast[pointer](pcd))
|
deallocShared(cast[pointer](pcd))
|
||||||
p.handles.excl(fd)
|
p.handles.excl(fd)
|
||||||
|
|
||||||
registerWaitableHandle(p, hEvent, flags, pcd, timercb)
|
registerWaitableHandle(p, hEvent, flags, pcd, timeout, timercb)
|
||||||
|
|
||||||
proc addProcess*(pid: int, cb: Callback) =
|
proc addProcess*(pid: int, cb: Callback) =
|
||||||
## Registers callback ``cb`` to be called when process with pid ``pid``
|
## Registers callback ``cb`` to be called when process with pid ``pid``
|
||||||
|
|
@ -1236,10 +1236,12 @@ when defined(windows) or defined(nimdoc):
|
||||||
p.handles.excl(fd)
|
p.handles.excl(fd)
|
||||||
discard cb(fd)
|
discard cb(fd)
|
||||||
|
|
||||||
registerWaitableHandle(p, hProcess, flags, pcd, proccb)
|
registerWaitableHandle(p, hProcess, flags, pcd, INFINITE, proccb)
|
||||||
|
|
||||||
proc newAsyncEvent*(): AsyncEvent =
|
proc newAsyncEvent*(): AsyncEvent =
|
||||||
## Creates new ``AsyncEvent`` object.
|
## Creates new ``AsyncEvent`` object.
|
||||||
|
## New ``AsyncEvent`` object is not automatically registered with
|
||||||
|
## dispatcher like ``AsyncSocket``.
|
||||||
var sa = SECURITY_ATTRIBUTES(
|
var sa = SECURITY_ATTRIBUTES(
|
||||||
nLength: sizeof(SECURITY_ATTRIBUTES).cint,
|
nLength: sizeof(SECURITY_ATTRIBUTES).cint,
|
||||||
bInheritHandle: 1
|
bInheritHandle: 1
|
||||||
|
|
@ -1248,14 +1250,15 @@ when defined(windows) or defined(nimdoc):
|
||||||
if event == INVALID_HANDLE_VALUE:
|
if event == INVALID_HANDLE_VALUE:
|
||||||
raiseOSError(osLastError())
|
raiseOSError(osLastError())
|
||||||
result = cast[AsyncEvent](allocShared0(sizeof(AsyncEventImpl)))
|
result = cast[AsyncEvent](allocShared0(sizeof(AsyncEventImpl)))
|
||||||
|
result.hEvent = event
|
||||||
|
|
||||||
proc setEvent*(ev: AsyncEvent) =
|
proc setEvent*(ev: AsyncEvent) =
|
||||||
## Set event ``ev`` to signaled state.
|
## Set event ``ev`` to signaled state.
|
||||||
if setEvent(ev.hEvent) == 0:
|
if setEvent(ev.hEvent) == 0:
|
||||||
raiseOSError(osLastError())
|
raiseOSError(osLastError())
|
||||||
|
|
||||||
proc close*(ev: AsyncEvent) =
|
proc unregister*(ev: AsyncEvent) =
|
||||||
## Closes event ``ev``.
|
## Unregisters event ``ev``.
|
||||||
if ev.hWaiter != 0:
|
if ev.hWaiter != 0:
|
||||||
let p = getGlobalDispatcher()
|
let p = getGlobalDispatcher()
|
||||||
if unregisterWait(ev.hWaiter) == 0:
|
if unregisterWait(ev.hWaiter) == 0:
|
||||||
|
|
@ -1263,7 +1266,12 @@ when defined(windows) or defined(nimdoc):
|
||||||
if err.int32 != ERROR_IO_PENDING:
|
if err.int32 != ERROR_IO_PENDING:
|
||||||
raiseOSError(osLastError())
|
raiseOSError(osLastError())
|
||||||
p.handles.excl(AsyncFD(ev.hEvent))
|
p.handles.excl(AsyncFD(ev.hEvent))
|
||||||
|
ev.hWaiter = 0
|
||||||
|
else:
|
||||||
|
raise newException(ValueError, "Event is not registered!")
|
||||||
|
|
||||||
|
proc close*(ev: AsyncEvent) =
|
||||||
|
## Closes event ``ev``.
|
||||||
if closeHandle(ev.hEvent) == 0:
|
if closeHandle(ev.hEvent) == 0:
|
||||||
raiseOSError(osLastError())
|
raiseOSError(osLastError())
|
||||||
deallocShared(cast[pointer](ev))
|
deallocShared(cast[pointer](ev))
|
||||||
|
|
@ -1281,15 +1289,12 @@ when defined(windows) or defined(nimdoc):
|
||||||
|
|
||||||
proc eventcb(fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
|
proc eventcb(fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
|
||||||
if cb(fd):
|
if cb(fd):
|
||||||
if unregisterWait(pcd.waitFd) == 0:
|
# we need this check to avoid exception, if `unregister(event)` was
|
||||||
let err = osLastError()
|
# called in callback.
|
||||||
if err.int32 != ERROR_IO_PENDING:
|
if ev.hWaiter != 0: unregister(ev)
|
||||||
raiseOSError(osLastError())
|
|
||||||
ev.hWaiter = 0
|
|
||||||
deallocShared(cast[pointer](pcd))
|
deallocShared(cast[pointer](pcd))
|
||||||
p.handles.excl(fd)
|
|
||||||
|
|
||||||
registerWaitableHandle(p, hEvent, flags, pcd, eventcb)
|
registerWaitableHandle(p, hEvent, flags, pcd, INFINITE, eventcb)
|
||||||
ev.hWaiter = pcd.waitFd
|
ev.hWaiter = pcd.waitFd
|
||||||
|
|
||||||
initAll()
|
initAll()
|
||||||
|
|
@ -1319,7 +1324,7 @@ else:
|
||||||
readCB: Callback
|
readCB: Callback
|
||||||
writeCB: Callback
|
writeCB: Callback
|
||||||
|
|
||||||
AsyncEvent* = SelectEvent
|
AsyncEvent* = distinct SelectEvent
|
||||||
|
|
||||||
PDispatcher* = ref object of PDispatcherBase
|
PDispatcher* = ref object of PDispatcherBase
|
||||||
selector: Selector[AsyncData]
|
selector: Selector[AsyncData]
|
||||||
|
|
@ -1368,8 +1373,8 @@ else:
|
||||||
proc unregister*(fd: AsyncFD) =
|
proc unregister*(fd: AsyncFD) =
|
||||||
getGlobalDispatcher().selector.unregister(fd.SocketHandle)
|
getGlobalDispatcher().selector.unregister(fd.SocketHandle)
|
||||||
|
|
||||||
# proc unregister*(ev: AsyncEvent) =
|
proc unregister*(ev: AsyncEvent) =
|
||||||
# getGlobalDispatcher().selector.unregister(SelectEvent(ev))
|
getGlobalDispatcher().selector.unregister(SelectEvent(ev))
|
||||||
|
|
||||||
proc addRead*(fd: AsyncFD, cb: Callback) =
|
proc addRead*(fd: AsyncFD, cb: Callback) =
|
||||||
let p = getGlobalDispatcher()
|
let p = getGlobalDispatcher()
|
||||||
|
|
@ -1409,7 +1414,7 @@ else:
|
||||||
var count = p.selector.selectInto(p.adjustedTimeout(timeout), keys)
|
var count = p.selector.selectInto(p.adjustedTimeout(timeout), keys)
|
||||||
var i = 0
|
var i = 0
|
||||||
while i < count:
|
while i < count:
|
||||||
var update = false
|
var custom = false
|
||||||
var fd = keys[i].fd.SocketHandle
|
var fd = keys[i].fd.SocketHandle
|
||||||
let events = keys[i].events
|
let events = keys[i].events
|
||||||
|
|
||||||
|
|
@ -1420,7 +1425,6 @@ else:
|
||||||
p.selector.withData(fd, adata) do:
|
p.selector.withData(fd, adata) do:
|
||||||
if adata.readCB == cb:
|
if adata.readCB == cb:
|
||||||
adata.readCB = nil
|
adata.readCB = nil
|
||||||
update = true
|
|
||||||
|
|
||||||
if Event.Write in events:
|
if Event.Write in events:
|
||||||
let cb = keys[i].data.writeCB
|
let cb = keys[i].data.writeCB
|
||||||
|
|
@ -1429,24 +1433,29 @@ else:
|
||||||
p.selector.withData(fd, adata) do:
|
p.selector.withData(fd, adata) do:
|
||||||
if adata.writeCB == cb:
|
if adata.writeCB == cb:
|
||||||
adata.writeCB = nil
|
adata.writeCB = nil
|
||||||
update = true
|
|
||||||
|
|
||||||
when supportedPlatform:
|
when supportedPlatform:
|
||||||
if (customSet * events) != {}:
|
if (customSet * events) != {}:
|
||||||
let cb = keys[i].data.readCB
|
let cb = keys[i].data.readCB
|
||||||
doAssert(cb != nil)
|
doAssert(cb != nil)
|
||||||
|
custom = true
|
||||||
if cb(fd.AsyncFD):
|
if cb(fd.AsyncFD):
|
||||||
p.selector.withData(fd, adata) do:
|
p.selector.withData(fd, adata) do:
|
||||||
if adata.readCB == cb:
|
if adata.readCB == cb:
|
||||||
adata.readCB = nil
|
adata.readCB = nil
|
||||||
p.selector.unregister(fd)
|
p.selector.unregister(fd)
|
||||||
|
|
||||||
if update:
|
# because state `data` can be modified in callback we need to update
|
||||||
|
# descriptor events with currently registered callbacks.
|
||||||
|
if not custom:
|
||||||
|
var update = false
|
||||||
var newEvents: set[Event] = {}
|
var newEvents: set[Event] = {}
|
||||||
p.selector.withData(fd, adata) do:
|
p.selector.withData(fd, adata) do:
|
||||||
if adata.readCB != nil: incl(newEvents, Event.Read)
|
if adata.readCB != nil: incl(newEvents, Event.Read)
|
||||||
if adata.writeCB != nil: incl(newEvents, Event.Write)
|
if adata.writeCB != nil: incl(newEvents, Event.Write)
|
||||||
p.selector.updateHandle(fd, newEvents)
|
update = true
|
||||||
|
if update:
|
||||||
|
p.selector.updateHandle(fd, newEvents)
|
||||||
inc(i)
|
inc(i)
|
||||||
|
|
||||||
# Timer processing.
|
# Timer processing.
|
||||||
|
|
@ -1693,15 +1702,15 @@ else:
|
||||||
|
|
||||||
proc newAsyncEvent*(): AsyncEvent =
|
proc newAsyncEvent*(): AsyncEvent =
|
||||||
## Creates new ``AsyncEvent``.
|
## Creates new ``AsyncEvent``.
|
||||||
result = AsyncEvent(ioselectors.newSelectEvent())
|
result = AsyncEvent(newSelectEvent())
|
||||||
|
|
||||||
proc setEvent*(ev: AsyncEvent) =
|
proc setEvent*(ev: AsyncEvent) =
|
||||||
## Sets new ``AsyncEvent`` to signaled state.
|
## Sets new ``AsyncEvent`` to signaled state.
|
||||||
ioselectors.setEvent(SelectEvent(ev))
|
setEvent(SelectEvent(ev))
|
||||||
|
|
||||||
proc close*(ev: AsyncEvent) =
|
proc close*(ev: AsyncEvent) =
|
||||||
## Closes ``AsyncEvent``
|
## Closes ``AsyncEvent``
|
||||||
ioselectors.close(SelectEvent(ev))
|
close(SelectEvent(ev))
|
||||||
|
|
||||||
proc addEvent*(ev: AsyncEvent, cb: Callback) =
|
proc addEvent*(ev: AsyncEvent, cb: Callback) =
|
||||||
## Start watching for event ``ev``, and call callback ``cb``, when
|
## Start watching for event ``ev``, and call callback ``cb``, when
|
||||||
|
|
|
||||||
125
tests/async/tupcoming_async.nim
Normal file
125
tests/async/tupcoming_async.nim
Normal file
|
|
@ -0,0 +1,125 @@
|
||||||
|
discard """
|
||||||
|
output: '''
|
||||||
|
OK
|
||||||
|
OK
|
||||||
|
OK
|
||||||
|
OK
|
||||||
|
'''
|
||||||
|
"""
|
||||||
|
|
||||||
|
when defined(upcoming):
|
||||||
|
import asyncdispatch, times, osproc, streams
|
||||||
|
|
||||||
|
const supportedPlatform = defined(linux) or defined(freebsd) or
|
||||||
|
defined(netbsd) or defined(openbsd) or
|
||||||
|
defined(macosx)
|
||||||
|
|
||||||
|
proc waitEvent(ev: AsyncEvent, closeEvent = false): Future[void] =
|
||||||
|
var retFuture = newFuture[void]("waitEvent")
|
||||||
|
proc cb(fd: AsyncFD): bool =
|
||||||
|
retFuture.complete()
|
||||||
|
if closeEvent:
|
||||||
|
return true
|
||||||
|
else:
|
||||||
|
return false
|
||||||
|
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() =
|
||||||
|
var timeout = 200
|
||||||
|
var errorRate = 10.0
|
||||||
|
var start = epochTime()
|
||||||
|
waitFor(waitTimer(200))
|
||||||
|
var finish = epochTime()
|
||||||
|
var lowlimit = float(timeout) - float(timeout) * errorRate / 100.0
|
||||||
|
var highlimit = float(timeout) + float(timeout) * errorRate / 100.0
|
||||||
|
var elapsed = (finish - start) * 1_000 # convert to milliseconds
|
||||||
|
if elapsed >= lowlimit and elapsed < highlimit:
|
||||||
|
echo "OK"
|
||||||
|
else:
|
||||||
|
echo "timerTest: Timeout = " & $(elapsed) & ", but must be inside of [" &
|
||||||
|
$lowlimit & ", " & $highlimit & ")"
|
||||||
|
|
||||||
|
proc eventTest() =
|
||||||
|
var event = newAsyncEvent()
|
||||||
|
var fut = waitEvent(event)
|
||||||
|
asyncCheck(delayedSet(event, 500))
|
||||||
|
waitFor(fut or waitTimer(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 supportedPlatform:
|
||||||
|
import posix
|
||||||
|
|
||||||
|
proc waitSignal(signal: int): Future[void] =
|
||||||
|
var retFuture = newFuture[void]("waitSignal")
|
||||||
|
proc cb(fd: AsyncFD): bool =
|
||||||
|
retFuture.complete()
|
||||||
|
addSignal(signal, cb)
|
||||||
|
return retFuture
|
||||||
|
|
||||||
|
proc delayedSignal(signal: int, timeout: int): Future[void] {.async.} =
|
||||||
|
await waitTimer(timeout)
|
||||||
|
var pid = posix.getpid()
|
||||||
|
discard posix.kill(pid, signal.cint)
|
||||||
|
|
||||||
|
proc signalTest() =
|
||||||
|
var fut = waitSignal(posix.SIGINT)
|
||||||
|
asyncCheck(delayedSignal(posix.SIGINT, 500))
|
||||||
|
waitFor(fut or waitTimer(1000))
|
||||||
|
if fut.finished:
|
||||||
|
echo "OK"
|
||||||
|
else:
|
||||||
|
echo "signalTest: Timeout expired before signal received!"
|
||||||
|
|
||||||
|
when supportedPlatform:
|
||||||
|
timerTest()
|
||||||
|
eventTest()
|
||||||
|
processTest()
|
||||||
|
signalTest()
|
||||||
|
elif defined(windows):
|
||||||
|
timerTest()
|
||||||
|
eventTest()
|
||||||
|
processTest()
|
||||||
|
echo "OK"
|
||||||
|
else:
|
||||||
|
eventTest()
|
||||||
|
echo "OK\nOK\nOK"
|
||||||
|
else:
|
||||||
|
echo "OK\nOK\nOK\nOK"
|
||||||
Loading…
Add table
Add a link
Reference in a new issue