mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-09-07 00:00:08 +02:00
sync: fix missing notification during mpsc close (#2854)
When the mpsc channel receiver closes the channel, receiving should return `None` once all in-progress sends have completed. When a sender reserves capacity, this prevents the receiver from fully shutting down. Previously, when the sender, after reserving capacity, dropped without sending a message, the receiver was not notified. This results in blocking the shutdown process until all sender handles drop. This patch adds a receiver notification when the channel is both closed and all outstanding sends have completed.
This commit is contained in:
@@ -75,7 +75,11 @@ pub(crate) trait Semaphore {
|
|||||||
|
|
||||||
/// The permit is dropped without a value being sent. In this case, the
|
/// The permit is dropped without a value being sent. In this case, the
|
||||||
/// permit must be returned to the semaphore.
|
/// permit must be returned to the semaphore.
|
||||||
fn drop_permit(&self, permit: &mut Self::Permit);
|
///
|
||||||
|
/// # Return
|
||||||
|
///
|
||||||
|
/// Returns true if the permit was acquired.
|
||||||
|
fn drop_permit(&self, permit: &mut Self::Permit) -> bool;
|
||||||
|
|
||||||
fn is_idle(&self) -> bool;
|
fn is_idle(&self) -> bool;
|
||||||
|
|
||||||
@@ -192,7 +196,7 @@ where
|
|||||||
|
|
||||||
pub(crate) fn disarm(&mut self) {
|
pub(crate) fn disarm(&mut self) {
|
||||||
// TODO: should this error if not acquired?
|
// TODO: should this error if not acquired?
|
||||||
self.inner.semaphore.drop_permit(&mut self.permit)
|
self.inner.semaphore.drop_permit(&mut self.permit);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send a message and notify the receiver.
|
/// Send a message and notify the receiver.
|
||||||
@@ -234,7 +238,11 @@ where
|
|||||||
S: Semaphore,
|
S: Semaphore,
|
||||||
{
|
{
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.inner.semaphore.drop_permit(&mut self.permit);
|
let notify = self.inner.semaphore.drop_permit(&mut self.permit);
|
||||||
|
|
||||||
|
if notify && self.inner.semaphore.is_idle() {
|
||||||
|
self.inner.rx_waker.wake();
|
||||||
|
}
|
||||||
|
|
||||||
if self.inner.tx_count.fetch_sub(1, AcqRel) != 1 {
|
if self.inner.tx_count.fetch_sub(1, AcqRel) != 1 {
|
||||||
return;
|
return;
|
||||||
@@ -424,8 +432,10 @@ impl Semaphore for (crate::sync::semaphore_ll::Semaphore, usize) {
|
|||||||
Permit::new()
|
Permit::new()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn drop_permit(&self, permit: &mut Permit) {
|
fn drop_permit(&self, permit: &mut Permit) -> bool {
|
||||||
|
let ret = permit.is_acquired();
|
||||||
permit.release(1, &self.0);
|
permit.release(1, &self.0);
|
||||||
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_permit(&self) {
|
fn add_permit(&self) {
|
||||||
@@ -477,7 +487,9 @@ impl Semaphore for AtomicUsize {
|
|||||||
|
|
||||||
fn new_permit() {}
|
fn new_permit() {}
|
||||||
|
|
||||||
fn drop_permit(&self, _permit: &mut ()) {}
|
fn drop_permit(&self, _permit: &mut ()) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
fn add_permit(&self) {
|
fn add_permit(&self) {
|
||||||
let prev = self.fetch_sub(2, Release);
|
let prev = self.fetch_sub(2, Release);
|
||||||
|
|||||||
@@ -534,3 +534,25 @@ async fn blocking_send_async() {
|
|||||||
let (mut tx, _rx) = mpsc::channel::<()>(1);
|
let (mut tx, _rx) = mpsc::channel::<()>(1);
|
||||||
let _ = tx.blocking_send(());
|
let _ = tx.blocking_send(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ready_close_cancel_bounded() {
|
||||||
|
use futures::future::poll_fn;
|
||||||
|
|
||||||
|
let (mut tx, mut rx) = mpsc::channel::<()>(100);
|
||||||
|
let _tx2 = tx.clone();
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut ready = task::spawn(async { poll_fn(|cx| tx.poll_ready(cx)).await });
|
||||||
|
assert_ready_ok!(ready.poll());
|
||||||
|
}
|
||||||
|
|
||||||
|
rx.close();
|
||||||
|
|
||||||
|
let mut recv = task::spawn(async { rx.recv().await });
|
||||||
|
assert_pending!(recv.poll());
|
||||||
|
|
||||||
|
drop(tx);
|
||||||
|
|
||||||
|
assert!(recv.is_woken());
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user