Skip to content

Conversation

@jgallagher
Copy link
Contributor

I was reading around bits of this code because it's on the scene for the hang in #9569. "Awaiting within select! arms" is one of our red flags for futurelock, and in reading I realized most of these arms were calling methods tagged as async but that didn't actually .await anything themselves. (There's one exception, noted below.)

I think making these synchronous makes the overall select! structure clearer and safer; we no longer have any nested .awaits.

I was reading around bits of this code because it's on the scene for
the hang in #9569. "Awaiting within `select!` arms" is one of our red
flags for futurelock, and in reading I realized most of these arms were
calling methods tagged as `async` but that didn't actually `.await`
anything themselves. (There's one exception, noted below.)

I think making these synchronous makes the overall `select!` structure
clearer and safer; we no longer have any nested `.await`s.
Err(e) => {
let _ = self.writer.shutdown().await;
// We need to shut down the writer - returning an error here
// will cause our caller (`run()`) to stop, which will do so.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is the one method that was actually async, but I think it doesn't need to be: if we return an error here, the next thing run() does is call self.close().await which itself shuts down the writer.

If this is too action-at-a-distance I'm happy to revert this one; we can still trim down the other arms freely.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm all for this change. Thank you!

}

async fn ping(&mut self) -> Result<(), ConnErr> {
fn ping(&mut self) -> Result<(), ConnErr> {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I considered renaming this enqueue_ping(), since it's not actually performing the ping itself. Tiny clarity win, maybe?

Copy link
Contributor

Choose a reason for hiding this comment

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

Strong objection to this one. No methods actually send messages really. They just enqueue data. But even syscalls just put stuff in a buffer to be processed eventually.

I'm a no on extraneous words for this reason.

Copy link
Contributor

@andrewjstone andrewjstone left a comment

Choose a reason for hiding this comment

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

Thanks for the fix!

}

async fn ping(&mut self) -> Result<(), ConnErr> {
fn ping(&mut self) -> Result<(), ConnErr> {
Copy link
Contributor

Choose a reason for hiding this comment

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

Strong objection to this one. No methods actually send messages really. They just enqueue data. But even syscalls just put stuff in a buffer to be processed eventually.

I'm a no on extraneous words for this reason.

Err(e) => {
let _ = self.writer.shutdown().await;
// We need to shut down the writer - returning an error here
// will cause our caller (`run()`) to stop, which will do so.
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm all for this change. Thank you!

@jgallagher jgallagher merged commit 47b72b8 into main Jan 7, 2026
16 checks passed
@jgallagher jgallagher deleted the john/tq-less-async branch January 7, 2026 15:50
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