Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for wait_readable() and wait_writable() on ProactorEventLoop #831

Merged
merged 38 commits into from
Dec 3, 2024

Conversation

agronholm
Copy link
Owner

@agronholm agronholm commented Dec 1, 2024

Changes

This enables asyncio on Windows to call wait_readable() and wait_writable() by using a selector on a separate thread.

Closes #820.

Checklist

If this is a user-facing code change, like a bugfix or a new feature, please ensure that
you've fulfilled the following conditions (where applicable):

  • You've added tests (in tests/) added which would fail without your patch
  • You've updated the documentation (in docs/, in case of behavior changes or new
    features)
  • You've added a new changelog entry (in docs/versionhistory.rst).

If this is a trivial change, like a typo fix or a code reformatting, then you can ignore
these instructions.

Updating the changelog

If there are no entries after the last release, use **UNRELEASED** as the version.
If, say, your patch fixes issue #123, the entry should look like this:

* Fix big bad boo-boo in task groups (#123 <https://github.com/agronholm/anyio/issues/123>_; PR by @yourgithubaccount)

If there's no issue linked, just link to your pull request instead by updating the
changelog after you've created the PR.

Copy link
Collaborator

@graingert graingert left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks pretty great overall, this is going to be a killer feature.

Just some nits, and I think you need to tweak the socketpair impl

event = asyncio.Event()
try:
loop.add_writer(obj, event.set)
remove_writer = loop.remove_writer
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I think this should be in the except's else clause

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought someone might pick up on that...

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

def __init__(self) -> None:
self._thread = threading.Thread(target=self.run, name="AnyIO socket selector")
self._selector = DefaultSelector()
self._send, self._receive = socket.socketpair()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think both sockets should be non-blocking, and you should ignore BlockingIOError on the send side

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

while not self._closed:
for key, events in self._selector.select():
if key.fileobj is self._receive:
self._receive.recv(10240)
Copy link
Collaborator

@graingert graingert Dec 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this needs to be a loop with a smaller buffer size:

https://github.com/python/cpython/blob/3.13/Lib/asyncio/selector_events.py#L130

Also handing Blocking and Interrupted errors

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possibly both of these should be recv_into?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get BlockingIOError, but why InterruptedError too? System calls are automatically retried by Python whenever they're interrupted.

pass # the loop was already closed

self._selector.unregister(self._receive)
self._receive.close()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be closed using with

Copy link
Collaborator

@graingert graingert Dec 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also think you need to close the selector https://docs.python.org/3/library/selectors.html#selectors.BaseSelector.close

Ah you probably want to close it in the join

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm closing the selector and the socket in stop() now.

src/anyio/_core/_sockets.py Show resolved Hide resolved
@agronholm
Copy link
Owner Author

I messed up somewhere along the way and now it gets stuck in a busy-wait loop somewhere.

@agronholm agronholm requested a review from graingert December 3, 2024 11:38
This does **NOT** work on Windows when using the asyncio backend with a proactor
event loop (default on py3.8+).
On backends where this functionality is not natively provided (asyncio
``ProactorEventLoop`` on Windows) Additionally, on asyncio, this functionality is
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
``ProactorEventLoop`` on Windows) Additionally, on asyncio, this functionality is
``ProactorEventLoop`` on Windows), this functionality is

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Woops. I pushed a better wording just now.

@agronholm agronholm merged commit 0f80611 into master Dec 3, 2024
16 checks passed
@agronholm agronholm deleted the selector-thread-alternate branch December 3, 2024 12:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants