Repository navigation
Keep watching an fd after a poll error on Linux - #791
Open
Ayush701-code-zm wants to merge 1 commit into
Open
Ayush701-code-zm wants to merge 1 commit into
Ayush701-code-zm wants to merge 1 commit into
Conversation
On Linux, libuv reports POLLERR to the poll callback as UV_EBADF after it has stopped the watcher. UVPoll handled this as a fatal error: it ran the reader and the writer once, dropped them and closed the handle, which stayed in Loop._polls. The fd was then no longer watched, a later add_writer() raised RuntimeError and re-arming from the callback did nothing. Match asyncio (level-triggered epoll) instead: run the callbacks as for a ready fd and start the poll again, so they keep being called until they are removed. If the fd is gone or the restart fails, close the handle, remove it from Loop._polls so that a later add_reader() or add_writer() creates a new one, and report the error to the exception handler. Fixes MagicStack#787.
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.
Fixes #787
On Linux, libuv reports
POLLERRon a polled fd asUV_EBADFafter it has stopped the watcher.__on_uvpoll_eventpassed that toUVPoll._fatal_error, which ran the callbacks once, closed the poll and left the closedUVPollinLoop._polls. After that the fd was no longer watched, a lateradd_writer()raisedRuntimeError: ... the handler is closed, and re-arming from inside the callback did nothing.This change makes a poll error behave like asyncio's level-triggered epoll:
_on_poll_errorruns the reader and writer callbacks as for a ready fd, then restarts the watcher with the current mask if a reader or writer is still set. The callbacks keep being called until they are removed, as in asyncio.os.fstat, since restarting a watcher on a closed fd makes libuv abort inepoll_ctl) oruv_poll_startfails,_close_on_errordrops the poll fromLoop._polls, closes it and reports the error to the exception handler. A lateradd_reader()/add_writer()then creates a new poll._fatal_erroris unchanged and still used for the start/stop failure paths.Tests: four Linux-only tests in
tests/test_base.py, run for both uvloop and asyncio, based on the reproducers in the issue (writer keeps being called after the read end is closed,add_writerafter the error with and withoutremove_writer, and a callback re-arming itself). They fail on master for uvloop and pass for asyncio; with this change all pass. The full test suite passes locally (Linux x86_64, Python 3.12, bundled libuv 1.53.0).One thing to note: like asyncio, an fd stuck in an error state now keeps calling its callback every iteration until it is removed. If you'd prefer only the smaller fix (dropping the closed poll from
_pollsand reporting the error), I can trim it to that.