Fix rarely appearing Windows bug with close(transport).

Add tests for it.
Add fromProc for all Future[T] in transports.
Add testall to improve tests speed.
Bump version to 2.2.4.
This commit is contained in:
cheatfate 2019-03-31 00:31:10 +02:00
parent 4290e06e77
commit 80ee289847
No known key found for this signature in database
GPG Key ID: 46ADD633A7201F95
17 changed files with 2455 additions and 2412 deletions

View File

@ -5,7 +5,6 @@
# Licensed under either of
# Apache License, version 2.0, (LICENSE-APACHEv2)
# MIT license (LICENSE-MIT)
import chronos/[asyncloop, asyncfutures2, asyncsync, handles, transport,
timer]
export asyncloop, asyncfutures2, asyncsync, handles, transport, timer

View File

@ -1,5 +1,5 @@
packageName = "chronos"
version = "2.2.3"
version = "2.2.4"
author = "Status Research & Development GmbH"
description = "Chronos"
license = "Apache License 2.0 or MIT"
@ -10,43 +10,14 @@ skipDirs = @["tests"]
requires "nim > 0.18.0"
task test, "Run all tests":
var testFiles = @[
"testsync",
"testsoon",
"testtime",
"testfut",
"testsignal",
"testaddress",
"testdatagram",
"teststream",
"testserver",
"testbugs",
var commands = [
"nim c -r -d:useSysAssert -d:useGcAssert tests/testall",
"nim c -r tests/testall",
"nim c -r -d:release tests/testall"
]
var testCommands = @[
"nim c -r -d:useSysAssert -d:useGcAssert",
"nim c -r",
"nim c -r -d:release"
]
var timerCommands = @[
" -d:asyncTimer=system",
" -d:asyncTimer=mono"
]
for tfile in testFiles:
if tfile == "testtime":
for cmd in testCommands:
for def in timerCommands:
var commandLine = cmd & def & " tests/" & tfile
echo "\n" & commandLine
exec commandLine
rmFile("tests/" & tfile.toExe())
else:
for cmd in testCommands:
var commandLine = cmd & " tests/" & tfile
echo "\n" & commandLine
exec commandLine
rmFile("tests/" & tfile.toExe())
echo "\n" & commands[0]
exec commands[0]
echo "\n" & commands[1]
exec commands[1]
echo "\n" & commands[2]
exec commands[2]

View File

@ -40,6 +40,14 @@ type
Future*[T] = ref object of FutureBase ## Typed future.
value: T ## Stored value
FutureStr*[T] = ref object of Future[T]
## Future to hold GC strings
gcholder*: string
FutureSeq*[A, B] = ref object of Future[A]
## Future to hold GC seqs
gcholder*: seq[B]
FutureVar*[T] = distinct Future[T]
FutureError* = object of Exception
@ -92,6 +100,22 @@ proc newFutureVar*[T](fromProc = "unspecified"): FutureVar[T] =
## that this future belongs to, is a good habit as it helps with debugging.
result = FutureVar[T](newFuture[T](fromProc))
proc newFutureSeq*[A, B](fromProc = "unspecified"): FutureSeq[A, B] =
## Create a new future which can hold/preserve GC string until future will
## not be completed.
##
## Specifying ``fromProc``, which is a string specifying the name of the proc
## that this future belongs to, is a good habit as it helps with debugging.
setupFutureBase(fromProc)
proc newFutureStr*[A](fromProc = "unspecified"): FutureStr[A] =
## Create a new future which can hold/preserve GC seq[T] until future will
## not be completed.
##
## Specifying ``fromProc``, which is a string specifying the name of the proc
## that this future belongs to, is a good habit as it helps with debugging.
setupFutureBase(fromProc)
proc clean*[T](future: FutureVar[T]) =
## Resets the ``finished`` status of ``future``.
Future[T](future).finished = false

View File

@ -24,7 +24,7 @@ type
ServerFlags* = enum
## Server's flags
ReuseAddr, ReusePort, TcpNoDelay, NoAutoRead, GCUserData, FirstPipe,
NoPipeFlash
NoPipeFlash, Broadcast
AddressFamily* {.pure.} = enum
None, IPv4, IPv6, Unix
@ -55,14 +55,6 @@ type
Running, # Server running
Closed # Server closed
FutureGCString*[T] = ref object of Future[T]
## Future to hold GC strings
gcholder*: string
FutureGCSeq*[A, B] = ref object of Future[A]
## Future to hold GC seqs
gcholder*: seq[B]
when defined(windows):
type
SocketServer* = ref object of RootRef
@ -510,6 +502,7 @@ when defined(windows):
ERROR_BROKEN_PIPE* = 109
ERROR_PIPE_NOT_CONNECTED* = 233
ERROR_NO_DATA* = 232
ERROR_CONNECTION_ABORTED* = 1236
proc cancelIo*(hFile: HANDLE): WINBOOL
{.stdcall, dynlib: "kernel32", importc: "CancelIo".}

View File

@ -141,8 +141,12 @@ when defined(windows):
transp.buflen = bytesCount
asyncCheck transp.function(transp, raddr)
elif int(err) == ERROR_OPERATION_ABORTED:
# CancelIO() interrupt
# CancelIO() interrupt or closeSocket() call.
transp.state.incl(ReadPaused)
if ReadClosed in transp.state:
# If `ReadClosed` present, then close(transport) was called.
transp.future.complete()
GC_unref(transp)
break
else:
transp.setReadError(err)
@ -179,6 +183,12 @@ when defined(windows):
transp.setReadError(err)
transp.buflen = 0
asyncCheck transp.function(transp, raddr)
else:
# Transport closure happens in callback, and we not started new
# WSARecvFrom session.
if ReadClosed in transp.state:
if not transp.future.finished:
transp.future.complete()
break
proc resumeRead(transp: DatagramTransport) {.inline.} =
@ -450,11 +460,8 @@ proc close*(transp: DatagramTransport) =
## Closes and frees resources of transport ``transp``.
when defined(windows):
if {ReadClosed, WriteClosed} * transp.state == {}:
discard cancelIo(Handle(transp.fd))
closeSocket(transp.fd)
transp.state.incl({WriteClosed, ReadClosed})
transp.future.complete()
GC_unref(transp)
closeSocket(transp.fd)
else:
proc continuation(udata: pointer) =
transp.future.complete()
@ -539,7 +546,7 @@ proc newDatagramTransport6*[T](cbproc: DatagramCallback,
proc join*(transp: DatagramTransport): Future[void] =
## Wait until the transport ``transp`` will be closed.
var retFuture = newFuture[void]("datagramtransport.join")
var retFuture = newFuture[void]("datagram.transport.join")
proc continuation(udata: pointer) = retFuture.complete()
if not transp.future.finished:
transp.future.addCallback(continuation)
@ -556,7 +563,7 @@ proc send*(transp: DatagramTransport, pbytes: pointer,
nbytes: int): Future[void] =
## Send buffer with pointer ``pbytes`` and size ``nbytes`` using transport
## ``transp`` to remote destination address which was bounded on transport.
var retFuture = newFuture[void]()
var retFuture = newFuture[void]("datagram.transport.send(pointer)")
transp.checkClosed(retFuture)
if transp.remote.port == Port(0):
retFuture.fail(newException(TransportError, "Remote peer not set!"))
@ -571,7 +578,7 @@ proc send*(transp: DatagramTransport, pbytes: pointer,
proc send*(transp: DatagramTransport, msg: string, msglen = -1): Future[void] =
## Send string ``msg`` using transport ``transp`` to remote destination
## address which was bounded on transport.
var retFuture = FutureGCString[void]()
var retFuture = newFutureStr[void]("datagram.transport.send(string)")
transp.checkClosed(retFuture)
if not isLiteral(msg):
shallowCopy(retFuture.gcholder, msg)
@ -590,7 +597,7 @@ proc send*[T](transp: DatagramTransport, msg: seq[T],
msglen = -1): Future[void] =
## Send string ``msg`` using transport ``transp`` to remote destination
## address which was bounded on transport.
var retFuture = FutureGCSeq[void, T]()
var retFuture = newFutureSeq[void, T]("datagram.transport.send(seq)")
transp.checkClosed(retFuture)
if not isLiteral(msg):
shallowCopy(retFuture.gcholder, msg)
@ -609,7 +616,7 @@ proc sendTo*(transp: DatagramTransport, remote: TransportAddress,
pbytes: pointer, nbytes: int): Future[void] =
## Send buffer with pointer ``pbytes`` and size ``nbytes`` using transport
## ``transp`` to remote destination address ``remote``.
var retFuture = newFuture[void]()
var retFuture = newFuture[void]("datagram.transport.sendTo(pointer)")
transp.checkClosed(retFuture)
let vector = GramVector(kind: WithAddress, buf: pbytes, buflen: nbytes,
writer: retFuture, address: remote)
@ -622,7 +629,7 @@ proc sendTo*(transp: DatagramTransport, remote: TransportAddress,
msg: string, msglen = -1): Future[void] =
## Send string ``msg`` using transport ``transp`` to remote destination
## address ``remote``.
var retFuture = FutureGCString[void]()
var retFuture = newFutureStr[void]("datagram.transport.sendTo(string)")
transp.checkClosed(retFuture)
if not isLiteral(msg):
shallowCopy(retFuture.gcholder, msg)
@ -642,7 +649,7 @@ proc sendTo*[T](transp: DatagramTransport, remote: TransportAddress,
msg: seq[T], msglen = -1): Future[void] =
## Send sequence ``msg`` using transport ``transp`` to remote destination
## address ``remote``.
var retFuture = FutureGCSeq[void, T]()
var retFuture = newFutureSeq[void, T]("datagram.transport.sendTo(seq)")
transp.checkClosed(retFuture)
if not isLiteral(msg):
shallowCopy(retFuture.gcholder, msg)

View File

@ -343,13 +343,6 @@ when defined(windows):
if ReadPending in transp.state:
## Continuation
transp.state.excl(ReadPending)
if ReadClosed in transp.state:
transp.state.incl({ReadPaused})
if not isNil(transp.reader):
if not transp.reader.finished:
transp.reader.complete()
transp.reader = nil
break
let err = transp.rovl.data.errCode
if err == OSErrorCode(-1):
let bytesCount = transp.rovl.data.bytesCount
@ -364,14 +357,23 @@ when defined(windows):
transp.roffset = transp.offset
if transp.offset == len(transp.buffer):
transp.state.incl(ReadPaused)
elif int(err) == ERROR_OPERATION_ABORTED:
# CancelIO() interrupt
elif int(err) in {ERROR_OPERATION_ABORTED, ERROR_CONNECTION_ABORTED,
ERROR_BROKEN_PIPE, ERROR_NETNAME_DELETED}:
# CancelIO() interrupt or closeSocket() call.
transp.state.incl(ReadPaused)
if ReadClosed in transp.state:
if not isNil(transp.reader):
if not transp.reader.finished:
transp.reader.complete()
transp.reader = nil
# If `ReadClosed` present, then close(transport) was called.
transp.future.complete()
GC_unref(transp)
elif transp.kind == TransportKind.Socket and
(int(err) in {ERROR_NETNAME_DELETED, WSAECONNABORTED}):
transp.state.incl({ReadEof, ReadPaused})
elif transp.kind == TransportKind.Pipe and
(int(err) in {ERROR_BROKEN_PIPE, ERROR_PIPE_NOT_CONNECTED}):
(int(err) in {ERROR_PIPE_NOT_CONNECTED}):
transp.state.incl({ReadEof, ReadPaused})
else:
transp.setReadError(err)
@ -446,6 +448,11 @@ when defined(windows):
if not isNil(transp.reader):
transp.reader.complete()
transp.reader = nil
# Transport close happens in callback, and we not started new
# WSARecvFrom session.
if ReadClosed in transp.state:
if not transp.future.finished:
transp.future.complete()
## Finish Loop
break
@ -602,9 +609,6 @@ when defined(windows):
if server.apending:
## Continuation
server.apending = false
if server.status in {ServerStatus.Stopped, ServerStatus.Closed}:
break
else:
if ovl.data.errCode == OSErrorCode(-1):
var ntransp: StreamTransport
var flags = {WinServerPipe}
@ -619,7 +623,12 @@ when defined(windows):
nil, flags)
asyncCheck server.function(server, ntransp)
elif int32(ovl.data.errCode) == ERROR_OPERATION_ABORTED:
# CancelIO() interrupt
# CancelIO() interrupt or close call.
if server.status == ServerStatus.Closed:
server.loopFuture.complete()
if not isNil(server.udata) and GCUserData in server.flags:
GC_unref(cast[ref int](server.udata))
GC_unref(server)
break
else:
doAssert disconnectNamedPipe(Handle(server.sock)) == 1
@ -627,11 +636,8 @@ when defined(windows):
raiseTransportOsError(osLastError())
else:
## Initiation
if server.status notin {ServerStatus.Stopped, ServerStatus.Closed}:
server.apending = true
if server.status in {ServerStatus.Stopped, ServerStatus.Closed}:
## Server was already stopped/closed exiting
break
var pipeSuffix = $cast[cstring](addr server.local.address_un)
var pipeName = newWideCString(r"\\.\pipe\" & pipeSuffix[1 .. ^1])
var openMode = PIPE_ACCESS_DUPLEX or FILE_FLAG_OVERLAPPED
@ -653,13 +659,25 @@ when defined(windows):
cast[POVERLAPPED](addr server.aovl))
if res == 0:
let err = osLastError()
if int32(err) == ERROR_IO_PENDING:
if int32(err) == ERROR_OPERATION_ABORTED:
server.apending = false
break
elif int32(err) == ERROR_IO_PENDING:
discard
elif int32(err) == ERROR_PIPE_CONNECTED:
discard
else:
raiseTransportOsError(err)
break
else:
# Server close happens in callback, and we are not started new
# connectNamedPipe session.
if server.status == ServerStatus.Closed:
if not server.loopFuture.finished:
server.loopFuture.complete()
if not isNil(server.udata) and GCUserData in server.flags:
GC_unref(cast[ref int](server.udata))
GC_unref(server)
proc acceptLoop(udata: pointer) {.gcsafe, nimcall.} =
var ovl = cast[PtrCustomOverlapped](udata)
@ -670,11 +688,6 @@ when defined(windows):
if server.apending:
## Continuation
server.apending = false
if server.status in {ServerStatus.Stopped, ServerStatus.Closed}:
## Server was already stopped/closed exiting
server.asock.closeSocket()
break
else:
if ovl.data.errCode == OSErrorCode(-1):
if setsockopt(SocketHandle(server.asock), cint(SOL_SOCKET),
cint(SO_UPDATE_ACCEPT_CONTEXT), addr server.sock,
@ -695,18 +708,19 @@ when defined(windows):
asyncCheck server.function(server, ntransp)
elif int32(ovl.data.errCode) == ERROR_OPERATION_ABORTED:
# CancelIO() interrupt
server.asock.closeSocket()
# CancelIO() interrupt or close.
if server.status == ServerStatus.Closed:
server.loopFuture.complete()
if not isNil(server.udata) and GCUserData in server.flags:
GC_unref(cast[ref int](server.udata))
GC_unref(server)
break
else:
server.asock.closeSocket()
raiseTransportOsError(ovl.data.errCode)
else:
## Initiation
if server.status in {ServerStatus.Stopped, ServerStatus.Closed}:
## Server was already stopped/closed exiting
break
if server.status notin {ServerStatus.Stopped, ServerStatus.Closed}:
server.apending = true
server.asock = createAsyncSocket(server.domain, SockType.SOCK_STREAM,
Protocol.IPPROTO_TCP)
@ -734,6 +748,15 @@ when defined(windows):
else:
raiseTransportOsError(err)
break
else:
# Server close happens in callback, and we are not started new
# AcceptEx session.
if server.status == ServerStatus.Closed:
if not server.loopFuture.finished:
server.loopFuture.complete()
if not isNil(server.udata) and GCUserData in server.flags:
GC_unref(cast[ref int](server.udata))
GC_unref(server)
proc resumeRead(transp: StreamTransport) {.inline.} =
transp.state.excl(ReadPaused)
@ -881,7 +904,7 @@ else:
slen: SockLen
sock: AsyncFD
proto: Protocol
var retFuture = newFuture[StreamTransport]("transport.connect")
var retFuture = newFuture[StreamTransport]("stream.transport.connect")
address.toSAddr(saddr, slen)
proto = Protocol.IPPROTO_TCP
if address.family == AddressFamily.Unix:
@ -985,7 +1008,7 @@ proc stop*(server: StreamServer) =
proc join*(server: StreamServer): Future[void] =
## Waits until ``server`` is not closed.
var retFuture = newFuture[void]("stream.server.join")
var retFuture = newFuture[void]("stream.transport.server.join")
proc continuation(udata: pointer) = retFuture.complete()
if not server.loopFuture.finished:
server.loopFuture.addCallback(continuation)
@ -998,6 +1021,7 @@ proc close*(server: StreamServer) =
##
## Please note that release of resources is not completed immediately, to be
## sure all resources got released please use ``await server.join()``.
when not defined(windows):
proc continuation(udata: pointer) =
server.loopFuture.complete()
if not isNil(server.udata) and GCUserData in server.flags:
@ -1007,12 +1031,12 @@ proc close*(server: StreamServer) =
server.status = ServerStatus.Closed
when defined(windows):
if server.local.family in {AddressFamily.IPv4, AddressFamily.IPv6}:
server.sock.closeSocket(continuation)
server.sock.closeSocket()
elif server.local.family in {AddressFamily.Unix}:
if NoPipeFlash notin server.flags:
discard flushFileBuffers(Handle(server.sock))
doAssert disconnectNamedPipe(Handle(server.sock)) == 1
closeHandle(server.sock, continuation)
closeHandle(server.sock)
else:
server.sock.closeSocket(continuation)
@ -1157,7 +1181,7 @@ proc createStreamServer*(host: TransportAddress,
result.init = init
result.bufferSize = bufferSize
result.status = Starting
result.loopFuture = newFuture[void]("stream.server")
result.loopFuture = newFuture[void]("stream.transport.server")
result.udata = udata
result.local = host
@ -1197,7 +1221,7 @@ proc write*(transp: StreamTransport, pbytes: pointer,
nbytes: int): Future[int] =
## Write data from buffer ``pbytes`` with size ``nbytes`` using transport
## ``transp``.
var retFuture = newFuture[int]()
var retFuture = newFuture[int]("stream.transport.write(pointer)")
transp.checkClosed(retFuture)
var vector = StreamVector(kind: DataBuffer, writer: retFuture,
buf: pbytes, buflen: nbytes)
@ -1208,7 +1232,7 @@ proc write*(transp: StreamTransport, pbytes: pointer,
proc write*(transp: StreamTransport, msg: string, msglen = -1): Future[int] =
## Write data from string ``msg`` using transport ``transp``.
var retFuture = FutureGCString[int]()
var retFuture = newFutureStr[int]("stream.transport.write(string)")
transp.checkClosed(retFuture)
if not isLiteral(msg):
shallowCopy(retFuture.gcholder, msg)
@ -1225,7 +1249,7 @@ proc write*(transp: StreamTransport, msg: string, msglen = -1): Future[int] =
proc write*[T](transp: StreamTransport, msg: seq[T], msglen = -1): Future[int] =
## Write sequence ``msg`` using transport ``transp``.
var retFuture = FutureGCSeq[int, T]()
var retFuture = newFutureSeq[int, T]("stream.transport.write(seq)")
transp.checkClosed(retFuture)
if not isLiteral(msg):
shallowCopy(retFuture.gcholder, msg)
@ -1250,7 +1274,7 @@ proc writeFile*(transp: StreamTransport, handle: int,
when defined(windows):
if transp.kind != TransportKind.Socket:
raise newException(TransportNoSupport, "writeFile() is not supported!")
var retFuture = newFuture[int]("transport.writeFile")
var retFuture = newFuture[int]("stream.transport.writeFile")
transp.checkClosed(retFuture)
var vector = StreamVector(kind: DataFile, writer: retFuture,
buf: cast[pointer](size), offset: offset,
@ -1309,6 +1333,7 @@ proc readOnce*(transp: StreamTransport, pbytes: pointer,
## internal buffer, otherwise it will wait until some bytes will be received.
checkClosed(transp)
checkPending(transp)
while true:
if transp.offset == 0:
if (ReadError in transp.state):
@ -1490,6 +1515,7 @@ proc consume*(transp: StreamTransport, n = -1): Future[int] {.async.} =
## Return number of bytes actually consumed
checkClosed(transp)
checkPending(transp)
result = 0
while true:
if (ReadError in transp.state):
@ -1522,7 +1548,7 @@ proc consume*(transp: StreamTransport, n = -1): Future[int] {.async.} =
proc join*(transp: StreamTransport): Future[void] =
## Wait until ``transp`` will not be closed.
var retFuture = newFuture[void]("streamtransport.join")
var retFuture = newFuture[void]("stream.transport.join")
proc continuation(udata: pointer) = retFuture.complete()
if not transp.future.finished:
transp.future.addCallback(continuation)
@ -1542,7 +1568,6 @@ proc close*(transp: StreamTransport) =
if {ReadClosed, WriteClosed} * transp.state == {}:
transp.state.incl({WriteClosed, ReadClosed})
when defined(windows):
discard cancelIo(Handle(transp.fd))
if transp.kind == TransportKind.Pipe:
if WinServerPipe in transp.flags:
if WinNoPipeFlash notin transp.flags:
@ -1551,9 +1576,23 @@ proc close*(transp: StreamTransport) =
else:
if WinNoPipeFlash notin transp.flags:
discard flushFileBuffers(Handle(transp.fd))
if ReadPaused in transp.state:
# If readStreamLoop() is not running we need to finish in
# continuation step.
closeHandle(transp.fd, continuation)
else:
# If readStreamLoop() is running, it will be properly finished inside
# of readStreamLoop().
closeHandle(transp.fd)
elif transp.kind == TransportKind.Socket:
if ReadPaused in transp.state:
# If readStreamLoop() is not running we need to finish in
# continuation step.
closeSocket(transp.fd, continuation)
else:
# If readStreamLoop() is running, it will be properly finished inside
# of readStreamLoop().
closeSocket(transp.fd)
else:
closeSocket(transp.fd, continuation)

View File

@ -5,11 +5,9 @@
# Licensed under either of
# Apache License, version 2.0, (LICENSE-APACHEv2)
# MIT license (LICENSE-MIT)
import strutils, unittest
import ../chronos
when isMainModule:
suite "TransportAddress test suite":
test "initTAddress(string)":
check $initTAddress("0.0.0.0:1") == "0.0.0.0:1"

9
tests/testall.nim Normal file
View File

@ -0,0 +1,9 @@
# Chronos Test Suite
# (c) Copyright 2018-Present
# Status Research & Development GmbH
#
# Licensed under either of
# Apache License, version 2.0, (LICENSE-APACHEv2)
# MIT license (LICENSE-MIT)
import testsync, testsoon, testtime, testfut, testsignal, testaddress,
testdatagram, teststream, testserver, testbugs

View File

@ -5,10 +5,10 @@
# Licensed under either of
# Apache License, version 2.0, (LICENSE-APACHEv2)
# MIT license (LICENSE-MIT)
import unittest
import ../chronos
suite "Asynchronous issues test suite":
const HELLO_PORT = 45679
const TEST_MSG = "testmsg"
const MSG_LEN = TEST_MSG.len()
@ -40,8 +40,6 @@ proc issue6(): Future[bool] {.async.} =
if data.test == "OK":
result = true
when isMainModule:
suite "Asynchronous issues test suite":
test "Issue #6":
var res = waitFor(issue6())
check res == true

View File

@ -5,20 +5,30 @@
# Licensed under either of
# Apache License, version 2.0, (LICENSE-APACHEv2)
# MIT license (LICENSE-MIT)
import strutils, net, unittest
import ../chronos
suite "Datagram Transport test suite":
const
TestsCount = 2000
ClientsCount = 20
MessagesCount = 20
m1 = "sendTo(pointer) test (" & $TestsCount & " messages)"
m2 = "send(pointer) test (" & $TestsCount & " messages)"
m3 = "sendTo(string) test (" & $TestsCount & " messages)"
m4 = "send(string) test (" & $TestsCount & " messages)"
m5 = "sendTo(seq[byte]) test (" & $TestsCount & " messages)"
m6 = "send(seq[byte]) test (" & $TestsCount & " messages)"
m7 = "Unbounded multiple clients with messages (" & $ClientsCount &
" clients x " & $MessagesCount & " messages)"
m8 = "Bounded multiple clients with messages (" & $ClientsCount &
" clients x " & $MessagesCount & " messages)"
proc client1(transp: DatagramTransport,
raddr: TransportAddress): Future[void] {.async.} =
var pbytes: seq[byte]
var nbytes: int
transp.peekMessage(pbytes, nbytes)
var pbytes = transp.getMessage()
var nbytes = len(pbytes)
if nbytes > 0:
var data = newString(nbytes + 1)
copyMem(addr data[0], addr pbytes[0], nbytes)
@ -38,9 +48,8 @@ proc client1(transp: DatagramTransport,
proc client2(transp: DatagramTransport,
raddr: TransportAddress): Future[void] {.async.} =
var pbytes: seq[byte]
var nbytes: int
transp.peekMessage(pbytes, nbytes)
var pbytes = transp.getMessage()
var nbytes = len(pbytes)
if nbytes > 0:
var data = newString(nbytes + 1)
copyMem(addr data[0], addr pbytes[0], nbytes)
@ -66,9 +75,8 @@ proc client2(transp: DatagramTransport,
proc client3(transp: DatagramTransport,
raddr: TransportAddress): Future[void] {.async.} =
var pbytes: seq[byte]
var nbytes: int
transp.peekMessage(pbytes, nbytes)
var pbytes = transp.getMessage()
var nbytes = len(pbytes)
if nbytes > 0:
var data = newString(nbytes + 1)
copyMem(addr data[0], addr pbytes[0], nbytes)
@ -93,9 +101,8 @@ proc client3(transp: DatagramTransport,
proc client4(transp: DatagramTransport,
raddr: TransportAddress): Future[void] {.async.} =
var pbytes: seq[byte]
var nbytes: int
transp.peekMessage(pbytes, nbytes)
var pbytes = transp.getMessage()
var nbytes = len(pbytes)
if nbytes > 0:
var data = newString(nbytes + 1)
copyMem(addr data[0], addr pbytes[0], nbytes)
@ -120,9 +127,8 @@ proc client4(transp: DatagramTransport,
proc client5(transp: DatagramTransport,
raddr: TransportAddress): Future[void] {.async.} =
var pbytes: seq[byte]
var nbytes: int
transp.peekMessage(pbytes, nbytes)
var pbytes = transp.getMessage()
var nbytes = len(pbytes)
if nbytes > 0:
var data = newString(nbytes + 1)
copyMem(addr data[0], addr pbytes[0], nbytes)
@ -147,9 +153,8 @@ proc client5(transp: DatagramTransport,
proc client6(transp: DatagramTransport,
raddr: TransportAddress): Future[void] {.async.} =
var pbytes: seq[byte]
var nbytes: int
transp.peekMessage(pbytes, nbytes)
var pbytes = transp.getMessage()
var nbytes = len(pbytes)
if nbytes > 0:
var data = newString(nbytes + 1)
copyMem(addr data[0], addr pbytes[0], nbytes)
@ -170,9 +175,8 @@ proc client6(transp: DatagramTransport,
proc client7(transp: DatagramTransport,
raddr: TransportAddress): Future[void] {.async.} =
var pbytes: seq[byte]
var nbytes: int
transp.peekMessage(pbytes, nbytes)
var pbytes = transp.getMessage()
var nbytes = len(pbytes)
if nbytes > 0:
var data = newString(nbytes + 1)
copyMem(addr data[0], addr pbytes[0], nbytes)
@ -197,9 +201,8 @@ proc client7(transp: DatagramTransport,
proc client8(transp: DatagramTransport,
raddr: TransportAddress): Future[void] {.async.} =
var pbytes: seq[byte]
var nbytes: int
transp.peekMessage(pbytes, nbytes)
var pbytes = transp.getMessage()
var nbytes = len(pbytes)
if nbytes > 0:
var data = newString(nbytes + 1)
copyMem(addr data[0], addr pbytes[0], nbytes)
@ -224,9 +227,8 @@ proc client8(transp: DatagramTransport,
proc client9(transp: DatagramTransport,
raddr: TransportAddress): Future[void] {.async.} =
var pbytes: seq[byte]
var nbytes: int
transp.peekMessage(pbytes, nbytes)
var pbytes = transp.getMessage()
var nbytes = len(pbytes)
if nbytes > 0:
var data = newString(nbytes + 1)
copyMem(addr data[0], addr pbytes[0], nbytes)
@ -251,9 +253,8 @@ proc client9(transp: DatagramTransport,
proc client10(transp: DatagramTransport,
raddr: TransportAddress): Future[void] {.async.} =
var pbytes: seq[byte]
var nbytes: int
transp.peekMessage(pbytes, nbytes)
var pbytes = transp.getMessage()
var nbytes = len(pbytes)
if nbytes > 0:
var data = newString(nbytes + 1)
copyMem(addr data[0], addr pbytes[0], nbytes)
@ -280,9 +281,8 @@ proc client10(transp: DatagramTransport,
proc client11(transp: DatagramTransport,
raddr: TransportAddress): Future[void] {.async.} =
var pbytes: seq[byte]
var nbytes: int
transp.peekMessage(pbytes, nbytes)
var pbytes = transp.getMessage()
var nbytes = len(pbytes)
if nbytes > 0:
var data = newString(nbytes + 1)
copyMem(addr data[0], addr pbytes[0], nbytes)
@ -375,7 +375,7 @@ proc testSeqSendTo(): Future[int] {.async.} =
result = counter
proc testSeqSend(): Future[int] {.async.} =
## send(string) test
## send(seq) test
var ta = initTAddress("127.0.0.1:33341")
var counter = 0
var dgram1 = newDatagramTransport(client9, udata = addr counter, local = ta)
@ -449,19 +449,22 @@ proc testConnReset(): Future[bool] {.async.} =
dgram2.close()
await dgram2.join()
when isMainModule:
const
m1 = "sendTo(pointer) test (" & $TestsCount & " messages)"
m2 = "send(pointer) test (" & $TestsCount & " messages)"
m3 = "sendTo(string) test (" & $TestsCount & " messages)"
m4 = "send(string) test (" & $TestsCount & " messages)"
m5 = "sendTo(seq[byte]) test (" & $TestsCount & " messages)"
m6 = "send(seq[byte]) test (" & $TestsCount & " messages)"
m7 = "Unbounded multiple clients with messages (" & $ClientsCount &
" clients x " & $MessagesCount & " messages)"
m8 = "Bounded multiple clients with messages (" & $ClientsCount &
" clients x " & $MessagesCount & " messages)"
suite "Datagram Transport test suite":
proc testTransportClose(): Future[bool] {.async.} =
var ta = initTAddress("127.0.0.1:45000")
var counter = 0
proc clientMark(transp: DatagramTransport,
raddr: TransportAddress): Future[void] {.async.} =
discard
var dgram = newDatagramTransport(clientMark, local = ta)
dgram.close()
try:
await wait(dgram.join(), 1.seconds)
result = true
except:
discard
test "close(transport) test":
check waitFor(testTransportClose()) == true
test m1:
check waitFor(testPointerSendTo()) == TestsCount
test m2:

View File

@ -5,12 +5,12 @@
# Licensed under either of
# Apache License, version 2.0, (LICENSE-APACHEv2)
# MIT license (LICENSE-MIT)
import unittest
import ../chronos
suite "Future[T] behavior test suite":
proc testFuture1(): Future[int] {.async.} =
await sleepAsync(1.milliseconds)
await sleepAsync(0.milliseconds)
proc testFuture2(): Future[int] {.async.} =
return 1
@ -537,8 +537,6 @@ proc testAllZero(): bool =
var fut = all(tseq)
result = fut.finished
when isMainModule:
suite "Future[T] behavior test suite":
test "Async undefined behavior (#7758) test":
check test1() == true
test "Immediately completed asynchronous procedure test":

View File

@ -5,10 +5,10 @@
# Licensed under either of
# Apache License, version 2.0, (LICENSE-APACHEv2)
# MIT license (LICENSE-MIT)
import strutils, unittest
import ../chronos
suite "Server's test suite":
type
CustomServer = ref object of StreamServer
test1: string
@ -112,8 +112,7 @@ proc test4(): bool =
udata = co)
result = waitFor client2(server, ta)
when isMainModule:
suite "Server's test suite":
test "Stream Server start/stop test":
check test1() == true
test "Stream Server inherited object test":

View File

@ -5,13 +5,14 @@
# Licensed under either of
# Apache License, version 2.0, (LICENSE-APACHEv2)
# MIT license (LICENSE-MIT)
import unittest, strutils
import ../chronos
when not defined(windows):
import posix
suite "Signal handling test suite":
when not defined(windows):
var signalCounter = 0
proc signalProc(udata: pointer) =
@ -28,15 +29,12 @@ when not defined(windows):
discard posix.kill(posix.getpid(), cint(signal))
waitFor(fut)
signalCounter == value
else:
const
SIGINT = 0
SIGTERM = 0
proc test(signal, value: int): bool = true
when isMainModule:
suite "Signal handling test suite":
test "SIGINT test":
check test(SIGINT, 31337) == true
test "SIGTERM test":

View File

@ -5,10 +5,10 @@
# Licensed under either of
# Apache License, version 2.0, (LICENSE-APACHEv2)
# MIT license (LICENSE-MIT)
import unittest
import ../chronos
suite "callSoon() tests suite":
const CallSoonTests = 10
var soonTest1 = 0'u
var timeoutsTest1 = 0
@ -62,8 +62,6 @@ proc test3(): bool =
poll()
result = soonTest2 == 987654321
when isMainModule:
suite "callSoon() tests suite":
test "User-defined callback argument test":
var values = [0x12345678'u, 0x23456789'u, 0x3456789A'u, 0x456789AB'u,
0x56789ABC'u, 0x6789ABCD'u, 0x789ABCDE'u, 0x89ABCDEF'u,

View File

@ -5,7 +5,6 @@
# Licensed under either of
# Apache License, version 2.0, (LICENSE-APACHEv2)
# MIT license (LICENSE-MIT)
import strutils, unittest, os
import ../chronos
@ -14,6 +13,7 @@ when defined(windows):
else:
import posix
suite "Stream Transport test suite":
const
ConstantMessage = "SOMEDATA"
BigMessagePattern = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
@ -24,6 +24,38 @@ const
MessageSize = 20
FilesCount = 10
m1 = "readLine() multiple clients with messages (" & $ClientsCount &
" clients x " & $MessagesCount & " messages)"
m2 = "readExactly() multiple clients with messages (" & $ClientsCount &
" clients x " & $MessagesCount & " messages)"
m3 = "readUntil() multiple clients with messages (" & $ClientsCount &
" clients x " & $MessagesCount & " messages)"
m4 = "writeFile() multiple clients (" & $FilesCount & " files)"
m5 = "write(string)/read(int) multiple clients (" & $ClientsCount &
" clients x " & $MessagesCount & " messages)"
m6 = "write(seq[byte])/consume(int)/read(int) multiple clients (" &
$ClientsCount & " clients x " & $MessagesCount & " messages)"
m7 = "readLine() buffer overflow test"
m8 = "readUntil() buffer overflow test"
m11 = "readExactly() unexpected disconnect test"
m12 = "readUntil() unexpected disconnect test"
m13 = "readLine() unexpected disconnect empty string test"
m14 = "Closing socket while operation pending test (issue #8)"
m15 = "Connection refused test"
m16 = "readOnce() read until atEof() test"
when defined(windows):
var addresses = [
initTAddress("127.0.0.1:33335"),
initTAddress(r"/LOCAL\testpipe")
]
else:
var addresses = [
initTAddress("127.0.0.1:33335"),
initTAddress(r"/tmp/testpipe")
]
var prefixes = ["[IP] ", "[UNIX] "]
proc serveClient1(server: StreamServer, transp: StreamTransport) {.async.} =
while not transp.atEof():
var data = await transp.readLine()
@ -577,8 +609,8 @@ proc test13(address: TransportAddress): Future[int] {.async.} =
server.close()
await server.join()
proc serveClient14(server: StreamServer, transp: StreamTransport) {.async.} =
discard
# proc serveClient14(server: StreamServer, transp: StreamTransport) {.async.} =
# discard
proc test14(address: TransportAddress): Future[int] {.async.} =
var subres = 0
@ -646,41 +678,22 @@ proc test16(address: TransportAddress): Future[int] {.async.} =
server.close()
await server.join()
when isMainModule:
const
m1 = "readLine() multiple clients with messages (" & $ClientsCount &
" clients x " & $MessagesCount & " messages)"
m2 = "readExactly() multiple clients with messages (" & $ClientsCount &
" clients x " & $MessagesCount & " messages)"
m3 = "readUntil() multiple clients with messages (" & $ClientsCount &
" clients x " & $MessagesCount & " messages)"
m4 = "writeFile() multiple clients (" & $FilesCount & " files)"
m5 = "write(string)/read(int) multiple clients (" & $ClientsCount &
" clients x " & $MessagesCount & " messages)"
m6 = "write(seq[byte])/consume(int)/read(int) multiple clients (" &
$ClientsCount & " clients x " & $MessagesCount & " messages)"
m7 = "readLine() buffer overflow test"
m8 = "readUntil() buffer overflow test"
m11 = "readExactly() unexpected disconnect test"
m12 = "readUntil() unexpected disconnect test"
m13 = "readLine() unexpected disconnect empty string test"
m14 = "Closing socket while operation pending test (issue #8)"
m15 = "Connection refused test"
m16 = "readOnce() read until atEof() test"
proc testCloseTransport(address: TransportAddress): Future[int] {.async.} =
proc client(server: StreamServer, transp: StreamTransport) {.async.} =
discard
var server = createStreamServer(address, client, {ReuseAddr})
server.start()
server.stop
server.close()
try:
await wait(server.join(), 1.seconds)
result = 1
except:
discard
when defined(windows):
var addresses = [
initTAddress("127.0.0.1:33335"),
initTAddress(r"/LOCAL\testpipe")
]
else:
var addresses = [
initTAddress("127.0.0.1:33335"),
initTAddress(r"/tmp/testpipe")
]
var prefixes = ["[IP] ", "[UNIX] "]
suite "Stream Transport test suite":
for i in 0..<len(addresses):
test prefixes[i] & "close(transport) test":
check waitFor(testCloseTransport(addresses[i])) == 1
test prefixes[i] & m8:
check waitFor(test8(addresses[i])) == 1
test prefixes[i] & m7:

View File

@ -5,10 +5,10 @@
# Licensed under either of
# Apache License, version 2.0, (LICENSE-APACHEv2)
# MIT license (LICENSE-MIT)
import unittest
import ../chronos
suite "Asynchronous sync primitives test suite":
var testLockResult = ""
var testEventResult = ""
var testQueue1Result = 0
@ -196,8 +196,6 @@ proc test9(): bool =
q.putNoWait(5)
result = (5 in q and not(6 in q))
when isMainModule:
suite "Asynchronous sync primitives test suite":
test "AsyncLock() behavior test":
check test1() == "0123456789"
test "AsyncEvent() behavior test":

View File

@ -5,10 +5,10 @@
# Licensed under either of
# Apache License, version 2.0, (LICENSE-APACHEv2)
# MIT license (LICENSE-MIT)
import os, unittest
import ../chronos, ../chronos/timer
suite "Asynchronous timers test suite":
const TimersCount = 10
proc timeWorker(time: Duration): Future[Duration] {.async.} =
@ -46,8 +46,6 @@ proc testTimer(): bool =
let d = b - a
result = (d >= 1000.milliseconds) and (d <= 2_000.milliseconds)
when isMainModule:
suite "Asynchronous timers test suite":
test "Timer reliability test [" & asyncTimer & "]":
check testTimer() == true
test $TimersCount & " timers with 10ms timeout":