[Windows] Deregister channels while the event loop is closing - #3685
Open
jakepetroules wants to merge 1 commit into
Open
[Windows] Deregister channels while the event loop is closing#3685jakepetroules wants to merge 1 commit into
jakepetroules wants to merge 1 commit into
Conversation
The WSAPoll selector trapped with `preconditionFailure("Invalid fd supplied.")`
whenever `WSAPoll` reported `POLLNVAL`, and that was reachable from ordinary
shutdown.
`SelectableEventLoop.deregister` guarded on `isOpen`, which is false both when
the loop is closed *and* when it has merely stopped accepting **new**
registrations. The latter is the state the loop is in during
`Selector.closeGently`, which closes each remaining channel -- so those channels
skipped deregistration entirely and then closed their sockets anyway. The epoll
and kqueue backends tolerate that, because there a registration lives in the
kernel and is dropped along with the descriptor. The WSAPoll backend keeps its
registrations in an array of its own, so it was left holding a stale entry for a
closed socket, and the next `WSAPoll` correctly reported `POLLNVAL` for it.
Deregistering existing channels in that state is valid -- the selector itself is
still open -- so guard on the selector's lifecycle instead. `POLLNVAL` keeps
trapping: like `EBADF`, which NIO already treats as unacceptable, it means we
have lost track of a descriptor, and acting on one that may since have been
reused would misroute I/O to the wrong channel.
This unskips `MultiThreadedEventLoopGroupTests.testShuttingDownFailsRegistration`.
Member
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation:
The WSAPoll selector traps with
preconditionFailure("Invalid fd supplied.")wheneverWSAPollreportsPOLLNVAL, and that is reachable from ordinary shutdown, soMultiThreadedEventLoopGroupTests.testShuttingDownFailsRegistrationis currently skipped on Windows.The root cause is not in the selector.
SelectableEventLoop.deregisterguards onisOpen, which is false both when the loop is closed and when it has merely stopped accepting new registrations. The latter is precisely the state the loop is in duringSelector.closeGently, which closes each remaining channel — so those channels skip deregistration entirely and then close their sockets anyway.The epoll and kqueue backends tolerate that, because there a registration lives in the kernel and is dropped along with the descriptor. The WSAPoll backend keeps its registrations in an array of its own, so it is left holding a stale entry for a closed socket, and the next
WSAPollcorrectly reportsPOLLNVALfor it.Traced on a Windows ARM64 VM by instrumenting the selector and the event loop:
Modifications:
deregisteron the selector's lifecycle rather than onisOpen. Deregistering an existing channel while the loop is closing is valid, because the selector itself is still open at that point.MultiThreadedEventLoopGroupTests.testShuttingDownFailsRegistration.POLLNVALdeliberately keeps trapping. LikeEBADF, which NIO already treats as an unacceptable errno, it means we have lost track of a descriptor; Windows recyclesSOCKEThandles, so continuing against a possibly-reused handle would misroute I/O to the wrong channel. An earlier version of this patch mappedPOLLNVALto.errorinstead — that silently retained the stale entry and gave up that guarantee, so it was dropped in favour of fixing the cause.Note for reviewers: unlike most of the Windows porting patches this touches shared code rather than sitting behind
#if os(Windows). The observable behaviour on Linux/Darwin is unchanged, but they will now issue oneepoll_ctl(EPOLL_CTL_DEL)/kevent delete per channel during event loop shutdown where previously they issued none.Result:
The WSAPoll selector no longer traps during event loop shutdown, and
testShuttingDownFailsRegistrationruns on Windows.Verified:
swift test: 2354 tests, 0 failures.swift testwith and without this change: identical results (2451 tests, same 28 pre-existing failures caused by the network mount used for this port, none in NIOPosix).