….nim less flaky

(cherry picked from commit 334ac3f588)
This commit is contained in:
Andreas Rumpf
2025-12-17 20:25:51 +01:00
committed by narimiran
parent 244af30c58
commit 90b7517656

View File

@@ -8,7 +8,7 @@ import asyncdispatch, asyncnet
when defined(windows):
from winlean import ERROR_NETNAME_DELETED
else:
from posix import EBADF
from posix import EBADF, ECONNRESET, EPIPE
# This reproduces a case where a socket remains stuck waiting for writes
# even when the socket is closed.
@@ -18,12 +18,37 @@ var port = Port(0)
var sent = 0
proc isExpectedDisconnectionError(errCode: int32): bool =
## Check if an error code indicates an expected disconnection.
## On POSIX systems, the error code depends on timing:
## - EBADF: Socket was closed locally before kernel detected remote state
## - ECONNRESET: Remote peer sent RST packet (detected first)
## - EPIPE: Socket is no longer connected (broken pipe)
## All three are valid disconnection errors for this test scenario.
when defined(windows):
errCode == ERROR_NETNAME_DELETED
else:
errCode == EBADF or errCode == ECONNRESET or errCode == EPIPE
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):
var sendTimedOut = false
try:
# On some platforms (notably macOS ARM64), the kernel may return
# ECONNRESET immediately when detecting a non-responsive connection,
# rather than letting the send stall. We catch this case here.
sendTimedOut = not await withTimeout(sendFut, timeout)
except OSError as e:
if isExpectedDisconnectionError(e.errorCode):
echo("send has errored. As expected. All good!")
quit(QuitSuccess)
else:
raise
if sendTimedOut:
# 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.
@@ -38,26 +63,16 @@ proc keepSendingTo(c: AsyncSocket) {.async.} =
# 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
# and POSIX. 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)
if isExpectedDisconnectionError(errCode):
echo("send has errored. As expected. All good!")
quit(QuitSuccess)
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)
raise newException(ValueError, "Test failed. Send failed with code " & $errCode)
# The write shouldn't succeed and also shouldn't be stalled.
if timeoutFut.read():