Summary
a196c2e fixed SocketPool<ePoll> to treat epoll_wait returning -1/EINTR as "no events" instead of a fatal error. The Poll backend still has the pre-fix behavior, and fixing it turns out to be non-trivial on Windows — hence an issue rather than a PR, to agree on direction first. (The kQueue backend has the same gap; a separate PR for that is in preparation.)
The POSIX bug
Poll.getNotifications() (FlyingSocks/Sources/SocketPool+Poll.swift):
let status = Socket.poll(&buffer, UInt32(buffer.count), interval.milliseconds)
guard status > -1 else {
throw SocketError.makeFailed("poll.getNotifications poll")
}
poll(2) (Apple manpage, Linux equivalent): "[EINTR] A signal is delivered before the time limit expires and before any of the selected events occurs." No events are lost — the correct response is to poll again. Instead the throw unwinds SocketPool.run(), whose defer { cancelAll() } cancels every waiting continuation: a single signal delivery (e.g. a debugger pause/resume) tears down the whole pool for any embedder using .poll.
Why the obvious fix is wrong on Windows
Mirroring a196c2e (if status == -1 && errno == EINTR { return [] }) misbehaves under WinSDK:
errno is shadowed. Socket+WinSock2.swift defines var errno: Int32 { WSAGetLastError() }, so the comparison would see Winsock codes (10000+), not CRT errno values.
EINTR has no Windows alias. The WinSock2 shim deliberately aliases EWOULDBLOCK/EINPROGRESS/EISCONN to their WSAE* equivalents but defines no EINTR, so a bare errno == EINTR may not even compile on Windows.
WSAEINTR means something different. Per Microsoft's Windows Sockets Error Codes, WSAEINTR (10004) is "Interrupted function call — a blocking operation was interrupted by a call to WSACancelBlockingCall" — an explicit cancellation, not a signal interruption — and the WSAPoll documentation does not list it as an expected result. Silently retrying it would be a behavior change, not a bug fix.
Proposed direction
Add the EINTR-retry branch under #if !canImport(WinSDK), matching a196c2e on POSIX platforms and leaving Windows behavior exactly as it is today:
guard status > -1 else {
#if !canImport(WinSDK)
// EINTR (signal) is not a failure: report no events so the caller
// polls again rather than tearing down the server. See poll(2).
if status == -1 && errno == EINTR {
return []
}
#endif
throw SocketError.makeFailed("poll.getNotifications poll")
}
Whether WSAEINTR should also be swallowed on Windows is left as a separate decision. Happy to send a PR for the POSIX-only version if this direction looks right.
🤖 Generated with Claude Code
Summary
a196c2e fixed
SocketPool<ePoll>to treatepoll_waitreturning-1/EINTRas "no events" instead of a fatal error. ThePollbackend still has the pre-fix behavior, and fixing it turns out to be non-trivial on Windows — hence an issue rather than a PR, to agree on direction first. (ThekQueuebackend has the same gap; a separate PR for that is in preparation.)The POSIX bug
Poll.getNotifications()(FlyingSocks/Sources/SocketPool+Poll.swift):poll(2)(Apple manpage, Linux equivalent): "[EINTR] A signal is delivered before the time limit expires and before any of the selected events occurs." No events are lost — the correct response is to poll again. Instead the throw unwindsSocketPool.run(), whosedefer { cancelAll() }cancels every waiting continuation: a single signal delivery (e.g. a debugger pause/resume) tears down the whole pool for any embedder using.poll.Why the obvious fix is wrong on Windows
Mirroring a196c2e (
if status == -1 && errno == EINTR { return [] }) misbehaves under WinSDK:errnois shadowed.Socket+WinSock2.swiftdefinesvar errno: Int32 { WSAGetLastError() }, so the comparison would see Winsock codes (10000+), not CRT errno values.EINTRhas no Windows alias. The WinSock2 shim deliberately aliasesEWOULDBLOCK/EINPROGRESS/EISCONNto theirWSAE*equivalents but defines noEINTR, so a bareerrno == EINTRmay not even compile on Windows.WSAEINTRmeans something different. Per Microsoft's Windows Sockets Error Codes,WSAEINTR(10004) is "Interrupted function call — a blocking operation was interrupted by a call toWSACancelBlockingCall" — an explicit cancellation, not a signal interruption — and the WSAPoll documentation does not list it as an expected result. Silently retrying it would be a behavior change, not a bug fix.Proposed direction
Add the EINTR-retry branch under
#if !canImport(WinSDK), matching a196c2e on POSIX platforms and leaving Windows behavior exactly as it is today:Whether
WSAEINTRshould also be swallowed on Windows is left as a separate decision. Happy to send a PR for the POSIX-only version if this direction looks right.🤖 Generated with Claude Code