* ttables: smaller table, 5x speedup * thavlak: less iterations, less loops; 30% speedup * tasyncclosestall: shorter timeout; 35% speedup * gcleak4: less iterations, 2x speedup * ttimes: remove deprecated stuff * tdangerisrelease: remove cpp backend, 3x speedup * tfrexp1: smaller range, 2x speedup * trtree: fix warnings, less iterations, 6x speedup * tasyncawait_cyclebreaker: smaller swarm size; 2x speedup * trealloc: smaller number of iterations; 10x speedup * towned_binary_tree: less iterations, 4x speedup * tclosure: remove unused code, less iterations; 2x speedup * twaitany: less durations; 1.4x speedup * tasync_misc: less iterations, 2x speedup * t8535: smaller sleep, 1.5x speedup * tmanyjoin: smaller sleep, 2x speedup * t12221: shorter sleeps, removed two slower tests; 1.6x speedup * tfuturestream: smaller sleep; 1.5x speedup * growobjcrash: less iterations; 2x speedup * ttryrecv: smaller sleep; 1.5x speedup * treusetvar: less threads; 2x speedup * delete tthreadanalysis2, basically a duplicate of tthreadanalysis * t7758: less iterations, 1.5x speedup * tasyncawait: smaller swarm, less messages; 1.5x speedup * tjsandnativeasync: smaller sleep, 1.5x speedup * tpendingcheck: smaller sleep, 1.5x speedup * remove rodfiles test category * move tseq from its own category to 'collections' category * remove unneeded tests and helpers from 'assert' category * stdlib: merge tbitops2 into tbitops * remove 'trepr2' from 'stdlib' cat * merge 'tstreams' into one file * remove 'tinefficient_const_table' from 'ccbugs' cat * merge 'tcollections_to_string' into 'tcollections' * tblocking_channel: smaller sleep, small speedup * tconvexhull: less iterartions; 1.2x speedup * merge 'tdeepcopy2' into 'tdeepcopy' * merge 'tdisjoint_slice2' into 'tdisjoint_slice1' * tmissing_deepcopy: smaller sequence * tsendtwice: smaller arrays; 5x speedup * remove 'tindexerrorformatbounds' * disable multimethod tests * remove 'gc:none' and 'refc' without 'd:useRealtimeGC' from gc tests * koch.nim: bootstrap just with '-d:release', no need for 'csource' * add github workflow for documentation * testament: no need for 8 sub-second decimals
101 lines
3.4 KiB
Nim
101 lines
3.4 KiB
Nim
discard """
|
|
disabled: "windows"
|
|
outputsub: "send has errored. As expected. All good!"
|
|
exitcode: 0
|
|
"""
|
|
import asyncdispatch, asyncnet
|
|
|
|
when defined(windows):
|
|
from winlean import ERROR_NETNAME_DELETED
|
|
else:
|
|
from posix import EBADF
|
|
|
|
# This reproduces a case where a socket remains stuck waiting for writes
|
|
# even when the socket is closed.
|
|
const
|
|
timeout = 8000
|
|
var port = Port(0)
|
|
|
|
var sent = 0
|
|
|
|
proc keepSendingTo(c: AsyncSocket) {.async.} =
|
|
while true:
|
|
# This write will eventually get stuck because the client is not reading
|
|
# its messages.
|
|
let sendFut = c.send("Foobar" & $sent & "\n", flags = {})
|
|
if not await withTimeout(sendFut, timeout):
|
|
# The write is stuck. Let's simulate a scenario where the socket
|
|
# does not respond to PING messages, and we close it. The above future
|
|
# should complete after the socket is closed, not continue stalling.
|
|
echo("Socket has stalled, closing it")
|
|
c.close()
|
|
|
|
let timeoutFut = withTimeout(sendFut, timeout)
|
|
yield timeoutFut
|
|
if timeoutFut.failed:
|
|
let errCode = ((ref OSError)(timeoutFut.error)).errorCode
|
|
# The behaviour differs across platforms. On Windows ERROR_NETNAME_DELETED
|
|
# is raised which we classif as a "diconnection error", hence we overwrite
|
|
# the flags above in the `send` call so that this error is raised.
|
|
#
|
|
# On Linux the EBADF error code is raised, this is because the socket
|
|
# is closed.
|
|
#
|
|
# This means that by default the behaviours will differ between Windows
|
|
# and Linux. I think this is fine though, it makes sense mainly because
|
|
# Windows doesn't use a IO readiness model. We can fix this later if
|
|
# necessary to reclassify ERROR_NETNAME_DELETED as not a "disconnection
|
|
# error" (TODO)
|
|
when defined(windows):
|
|
if errCode == ERROR_NETNAME_DELETED:
|
|
echo("send has errored. As expected. All good!")
|
|
quit(QuitSuccess)
|
|
else:
|
|
raise newException(ValueError, "Test failed. Send failed with code " & $errCode)
|
|
else:
|
|
if errCode == EBADF:
|
|
echo("send has errored. As expected. All good!")
|
|
quit(QuitSuccess)
|
|
else:
|
|
raise newException(ValueError, "Test failed. Send failed with code " & $errCode)
|
|
|
|
# The write shouldn't succeed and also shouldn't be stalled.
|
|
if timeoutFut.read():
|
|
raise newException(ValueError, "Test failed. Send was expected to fail.")
|
|
else:
|
|
raise newException(ValueError, "Test failed. Send future is still stalled.")
|
|
sent.inc(1)
|
|
|
|
proc startClient() {.async.} =
|
|
let client = newAsyncSocket()
|
|
await client.connect("localhost", port)
|
|
echo("Connected")
|
|
|
|
let firstLine = await client.recvLine()
|
|
echo("Received first line as a client: ", firstLine)
|
|
echo("Now not reading anymore")
|
|
while true: await sleepAsync(1000)
|
|
|
|
proc debug() {.async.} =
|
|
while true:
|
|
echo("Sent ", sent)
|
|
await sleepAsync(1000)
|
|
|
|
proc server() {.async.} =
|
|
var s = newAsyncSocket()
|
|
s.setSockOpt(OptReuseAddr, true)
|
|
s.bindAddr(port)
|
|
s.listen()
|
|
let (addr2, port2) = s.getLocalAddr
|
|
port = port2
|
|
|
|
# We're now ready to accept connections, so start the client
|
|
asyncCheck startClient()
|
|
asyncCheck debug()
|
|
|
|
while true:
|
|
let client = await accept(s)
|
|
asyncCheck keepSendingTo(client)
|
|
|
|
when isMainModule:
|
|
waitFor server()
|