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

sync: Add disarm to mpsc::Sender #2358

Merged
merged 4 commits into from
Apr 2, 2020
Merged

sync: Add disarm to mpsc::Sender #2358

merged 4 commits into from
Apr 2, 2020

Conversation

jonhoo
Copy link
Sponsor Contributor

@jonhoo jonhoo commented Apr 1, 2020

Fixes #898.

Motivation

Basically #898. Also needed for tower-rs/tower#408.

Since poll_ready takes up one of the finite number of slots in a bounded channel, callers need to send an item shortly after poll_ready succeeds. If they do not, idle senders may take up all the slots of the channel, and prevent active senders from getting any requests through. Consider this code that forwards from one channel to another:

loop {
  ready!(tx.poll_ready(cx))?;
  if let Some(item) = ready!(rx.poll_recv(cx)) {
    tx.try_send(item)?;
  } else {
    break;
  }
}

If many such forwarders exist, and they all forward into a single (cloned) Sender, then any number of forwarders may be waiting for rx.poll_recv at the same time. While they do, they are effectively each reducing the channel's capacity by 1. If enough of these forwarders are idle, forwarders whose rx do have elements will be unable to find a spot for them through poll_ready, and the system will deadlock.

Solution

disarm solves this problem by allowing you to give up the reserved slot if you find that you have to block. We can then fix the code above by writing:

loop {
  ready!(tx.poll_ready(cx))?;
  let item = rx.poll_recv(cx);
  if let Poll::Ready(Ok(_)) = item {
  } else {
    // give up our send slot, we won't need it for a while
    tx.disarm();
  }
  if let Some(item) = ready!(item) {
    tx.try_send(item)?;
  } else {
    break;
  }
}

@jonhoo jonhoo requested a review from carllerche April 1, 2020 19:45
Copy link
Member

@LucioFranco LucioFranco left a comment

Choose a reason for hiding this comment

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

LGTM, just that one example sticks out.

tokio/src/sync/mpsc/bounded.rs Show resolved Hide resolved
tokio/src/sync/mpsc/bounded.rs Outdated Show resolved Hide resolved
Copy link
Member

@carllerche carllerche left a comment

Choose a reason for hiding this comment

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

I would recommend removing doc hidden. The APIs are public anyway and documented.

tokio/src/sync/mpsc/bounded.rs Outdated Show resolved Hide resolved
tokio/src/sync/mpsc/bounded.rs Outdated Show resolved Hide resolved
@LucioFranco LucioFranco merged commit 7fb1698 into master Apr 2, 2020
@LucioFranco LucioFranco deleted the jonhoo/disarm branch April 2, 2020 15:27
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.

tokio-sync mpsc poll_ready irrevocably reserves slot
3 participants