mirror of
https://github.com/tokio-rs/tokio.git
synced 2025-10-01 12:20:39 +00:00
sync: add broadcast::Sender::closed
(#6685)
This commit is contained in:
parent
5f3296df77
commit
5c8cd33820
@ -1,7 +1,7 @@
|
|||||||
use std::sync::{self, MutexGuard, TryLockError};
|
use std::sync::{self, MutexGuard, TryLockError};
|
||||||
|
|
||||||
/// Adapter for `std::Mutex` that removes the poisoning aspects
|
/// Adapter for `std::Mutex` that removes the poisoning aspects
|
||||||
/// from its api.
|
/// from its API.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct Mutex<T: ?Sized>(sync::Mutex<T>);
|
pub(crate) struct Mutex<T: ?Sized>(sync::Mutex<T>);
|
||||||
|
|
||||||
|
@ -301,6 +301,8 @@ pub mod error {
|
|||||||
|
|
||||||
use self::error::{RecvError, SendError, TryRecvError};
|
use self::error::{RecvError, SendError, TryRecvError};
|
||||||
|
|
||||||
|
use super::Notify;
|
||||||
|
|
||||||
/// Data shared between senders and receivers.
|
/// Data shared between senders and receivers.
|
||||||
struct Shared<T> {
|
struct Shared<T> {
|
||||||
/// slots in the channel.
|
/// slots in the channel.
|
||||||
@ -314,6 +316,9 @@ struct Shared<T> {
|
|||||||
|
|
||||||
/// Number of outstanding Sender handles.
|
/// Number of outstanding Sender handles.
|
||||||
num_tx: AtomicUsize,
|
num_tx: AtomicUsize,
|
||||||
|
|
||||||
|
/// Notify when the last subscribed [`Receiver`] drops.
|
||||||
|
notify_last_rx_drop: Notify,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Next position to write a value.
|
/// Next position to write a value.
|
||||||
@ -528,6 +533,7 @@ impl<T> Sender<T> {
|
|||||||
waiters: LinkedList::new(),
|
waiters: LinkedList::new(),
|
||||||
}),
|
}),
|
||||||
num_tx: AtomicUsize::new(1),
|
num_tx: AtomicUsize::new(1),
|
||||||
|
notify_last_rx_drop: Notify::new(),
|
||||||
});
|
});
|
||||||
|
|
||||||
Sender { shared }
|
Sender { shared }
|
||||||
@ -805,6 +811,50 @@ impl<T> Sender<T> {
|
|||||||
Arc::ptr_eq(&self.shared, &other.shared)
|
Arc::ptr_eq(&self.shared, &other.shared)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A future which completes when the number of [Receiver]s subscribed to this `Sender` reaches
|
||||||
|
/// zero.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use futures::FutureExt;
|
||||||
|
/// use tokio::sync::broadcast;
|
||||||
|
///
|
||||||
|
/// #[tokio::main]
|
||||||
|
/// async fn main() {
|
||||||
|
/// let (tx, mut rx1) = broadcast::channel::<u32>(16);
|
||||||
|
/// let mut rx2 = tx.subscribe();
|
||||||
|
///
|
||||||
|
/// tokio::spawn(async move {
|
||||||
|
/// assert_eq!(rx1.recv().await.unwrap(), 10);
|
||||||
|
/// });
|
||||||
|
///
|
||||||
|
/// let _ = tx.send(10);
|
||||||
|
/// assert!(tx.closed().now_or_never().is_none());
|
||||||
|
///
|
||||||
|
/// let _ = tokio::spawn(async move {
|
||||||
|
/// assert_eq!(rx2.recv().await.unwrap(), 10);
|
||||||
|
/// }).await;
|
||||||
|
///
|
||||||
|
/// assert!(tx.closed().now_or_never().is_some());
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub async fn closed(&self) {
|
||||||
|
loop {
|
||||||
|
let notified = self.shared.notify_last_rx_drop.notified();
|
||||||
|
|
||||||
|
{
|
||||||
|
// Ensure the lock drops if the channel isn't closed
|
||||||
|
let tail = self.shared.tail.lock();
|
||||||
|
if tail.closed {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
notified.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn close_channel(&self) {
|
fn close_channel(&self) {
|
||||||
let mut tail = self.shared.tail.lock();
|
let mut tail = self.shared.tail.lock();
|
||||||
tail.closed = true;
|
tail.closed = true;
|
||||||
@ -819,8 +869,14 @@ fn new_receiver<T>(shared: Arc<Shared<T>>) -> Receiver<T> {
|
|||||||
|
|
||||||
assert!(tail.rx_cnt != MAX_RECEIVERS, "max receivers");
|
assert!(tail.rx_cnt != MAX_RECEIVERS, "max receivers");
|
||||||
|
|
||||||
tail.rx_cnt = tail.rx_cnt.checked_add(1).expect("overflow");
|
if tail.rx_cnt == 0 {
|
||||||
|
// Potentially need to re-open the channel, if a new receiver has been added between calls
|
||||||
|
// to poll(). Note that we use rx_cnt == 0 instead of is_closed since is_closed also
|
||||||
|
// applies if the sender has been dropped
|
||||||
|
tail.closed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
tail.rx_cnt = tail.rx_cnt.checked_add(1).expect("overflow");
|
||||||
let next = tail.pos;
|
let next = tail.pos;
|
||||||
|
|
||||||
drop(tail);
|
drop(tail);
|
||||||
@ -1346,6 +1402,12 @@ impl<T> Drop for Receiver<T> {
|
|||||||
|
|
||||||
tail.rx_cnt -= 1;
|
tail.rx_cnt -= 1;
|
||||||
let until = tail.pos;
|
let until = tail.pos;
|
||||||
|
let remaining_rx = tail.rx_cnt;
|
||||||
|
|
||||||
|
if remaining_rx == 0 {
|
||||||
|
self.shared.notify_last_rx_drop.notify_waiters();
|
||||||
|
tail.closed = true;
|
||||||
|
}
|
||||||
|
|
||||||
drop(tail);
|
drop(tail);
|
||||||
|
|
||||||
|
@ -656,3 +656,54 @@ async fn receiver_recv_is_cooperative() {
|
|||||||
_ = tokio::task::yield_now() => {},
|
_ = tokio::task::yield_now() => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn broadcast_sender_closed() {
|
||||||
|
let (tx, rx) = broadcast::channel::<()>(1);
|
||||||
|
let rx2 = tx.subscribe();
|
||||||
|
|
||||||
|
let mut task = task::spawn(tx.closed());
|
||||||
|
assert_pending!(task.poll());
|
||||||
|
|
||||||
|
drop(rx);
|
||||||
|
assert!(!task.is_woken());
|
||||||
|
assert_pending!(task.poll());
|
||||||
|
|
||||||
|
drop(rx2);
|
||||||
|
assert!(task.is_woken());
|
||||||
|
assert_ready!(task.poll());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn broadcast_sender_closed_with_extra_subscribe() {
|
||||||
|
let (tx, rx) = broadcast::channel::<()>(1);
|
||||||
|
let rx2 = tx.subscribe();
|
||||||
|
|
||||||
|
let mut task = task::spawn(tx.closed());
|
||||||
|
assert_pending!(task.poll());
|
||||||
|
|
||||||
|
drop(rx);
|
||||||
|
assert!(!task.is_woken());
|
||||||
|
assert_pending!(task.poll());
|
||||||
|
|
||||||
|
drop(rx2);
|
||||||
|
assert!(task.is_woken());
|
||||||
|
|
||||||
|
let rx3 = tx.subscribe();
|
||||||
|
assert_pending!(task.poll());
|
||||||
|
|
||||||
|
drop(rx3);
|
||||||
|
assert!(task.is_woken());
|
||||||
|
assert_ready!(task.poll());
|
||||||
|
|
||||||
|
let mut task2 = task::spawn(tx.closed());
|
||||||
|
assert_ready!(task2.poll());
|
||||||
|
|
||||||
|
let rx4 = tx.subscribe();
|
||||||
|
let mut task3 = task::spawn(tx.closed());
|
||||||
|
assert_pending!(task3.poll());
|
||||||
|
|
||||||
|
drop(rx4);
|
||||||
|
assert!(task3.is_woken());
|
||||||
|
assert_ready!(task3.poll());
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user