mirror of
https://github.com/nim-lang/Nim.git
synced 2026-01-03 19:52:36 +00:00
refs https://github.com/nim-lang/Nim/pull/25353 make tasyncclosestall… (#25366)
….nim less flaky
(cherry picked from commit 334ac3f588)
This commit is contained in:
@@ -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():
|
||||
|
||||
Reference in New Issue
Block a user