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

Await zmq sends #184

Merged
merged 1 commit into from
May 25, 2022
Merged

Await zmq sends #184

merged 1 commit into from
May 25, 2022

Conversation

minrk
Copy link
Contributor

@minrk minrk commented May 20, 2022

asyncio zmq sends return awaitables, even though they usually return a completed Future, due to optimizations.

Otherwise, we're creating and not awaiting Futures, which can cause ordering problems.

The layering means there are now two additional awaits for a likely already-done future. I don't know the performance cost of that. But if you care to optimize the awaits, you can do:

def send_message(sock) -> Awaitable[Any]:
    return sock.send(...)

...
awaitable = send_message(sock)
f = asyncio.ensure_future(awaitable) # no-op because it's already a Future, but could be a coroutine in the future
if not f.done():
    await f
# most of the time, get here with zero awaits

An extra await can be avoided by changing the type to ->Awaitable[Any] and return sock.send..., if that's preferable.

Found while looking at #183

@welcome
Copy link

welcome bot commented May 20, 2022

Thanks for submitting your first pull request! You are awesome! 🤗

If you haven't done so already, check out Jupyter's Code of Conduct. Also, please make sure you followed the pull request template, as this will help us review your contribution more quickly.
welcome
You can meet the other Jovyans by joining our Discourse forum. There is also a intro thread there where you can stop by and say Hi! 👋

Welcome to the Jupyter community! 🎉

@davidbrochart
Copy link
Collaborator

Thanks for the explanation!
Closing since #183 was merged.

asyncio zmq sends return awaitables, even though they _usually_ return a completed Future,
due to optimizations.

Otherwise, we're creating and not awaiting Futures, which can cause ordering problems.
@minrk
Copy link
Contributor Author

minrk commented May 25, 2022

Reopening. This included 183, it wasn't an alternative. It's still true that async sends aren't being awaited and they probably should be.

@davidbrochart
Copy link
Collaborator

My bad, I didn't pay attention to the awaited sends. Thanks for catching this!
Why do you think they should be awaited, do you think it can lead to messages being sent out-of-order?

@minrk
Copy link
Contributor Author

minrk commented May 25, 2022

If there are errors, they will be raised instead of silently ignored. It shouldn't create any ordering issues, but it's certainly clearer that it can't if you await them. Depending on the stack of coroutines, it is possible that ordering issues could happen.

If pyzmq ever switches to coroutines instead of returning Futures, merely calling a coroutine doesn't actually start anything, so that would mean no sends actually happen at all. You have to call asyncio.ensure_future(coro()) (or create a a Task) to start a coroutine you don't want to wait for.

Note that await send waits for the zmq_send call to complete, not the actual network send to complete. It's still async at the C++ level, and what we are waiting for is really passing pointers to a background thread for processing.

@davidbrochart
Copy link
Collaborator

Thanks!

@davidbrochart davidbrochart merged commit 518250d into jupyter-server:main May 25, 2022
@minrk minrk deleted the await-send branch May 25, 2022 12:11
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.

2 participants