refactor(server): simplify drain (#2328)
The current implementation of `drain` uses a `tokio::sync::watch` channel to send the shutdown signal, and a `tokio::sync::mpsc` to signal when all draining tasks have completed. No data is ever actually sent on the MPSC; instead, it is simply used to notify the task that signalled the drain when all draining tasks have been dropped. Tokio 0.3's `watch::Sender` has a `closed` method that can be used to await the dropping of all receivers. This can be used instead of the MPSC channel. This commit updates `drain` to use `watch::Sender::closed` instead. This has fewer moving parts, and may have slightly less overhead (as it doesn't require additional allocation forthe MPSC which is never actually used). Signed-off-by: Eliza Weisman <eliza@buoyant.io>
This commit is contained in:
@@ -1,37 +1,23 @@
|
||||
use std::mem;
|
||||
|
||||
use pin_project::pin_project;
|
||||
use tokio::stream::Stream;
|
||||
use tokio::sync::{mpsc, watch};
|
||||
use tokio::sync::watch;
|
||||
|
||||
use super::{task, Future, Never, Pin, Poll};
|
||||
use super::{task, Future, Pin, Poll};
|
||||
|
||||
pub fn channel() -> (Signal, Watch) {
|
||||
let (tx, rx) = watch::channel(());
|
||||
let (drained_tx, drained_rx) = mpsc::channel(1);
|
||||
(
|
||||
Signal {
|
||||
drained_rx,
|
||||
_tx: tx,
|
||||
},
|
||||
Watch { drained_tx, rx },
|
||||
)
|
||||
(Signal { tx }, Watch { rx })
|
||||
}
|
||||
|
||||
pub struct Signal {
|
||||
drained_rx: mpsc::Receiver<Never>,
|
||||
_tx: watch::Sender<()>,
|
||||
tx: watch::Sender<()>,
|
||||
}
|
||||
|
||||
#[pin_project::pin_project]
|
||||
pub struct Draining {
|
||||
#[pin]
|
||||
drained_rx: mpsc::Receiver<Never>,
|
||||
}
|
||||
pub struct Draining(Pin<Box<dyn Future<Output = ()> + Send + Sync>>);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Watch {
|
||||
drained_tx: mpsc::Sender<Never>,
|
||||
rx: watch::Receiver<()>,
|
||||
}
|
||||
|
||||
@@ -42,7 +28,7 @@ pub struct Watching<F, FN> {
|
||||
future: F,
|
||||
state: State<FN>,
|
||||
watch: Pin<Box<dyn Future<Output = ()> + Send + Sync>>,
|
||||
_drained_tx: mpsc::Sender<Never>,
|
||||
_rx: watch::Receiver<()>,
|
||||
}
|
||||
|
||||
enum State<F> {
|
||||
@@ -52,21 +38,16 @@ enum State<F> {
|
||||
|
||||
impl Signal {
|
||||
pub fn drain(self) -> Draining {
|
||||
// Simply dropping `self.tx` will signal the watchers
|
||||
Draining {
|
||||
drained_rx: self.drained_rx,
|
||||
}
|
||||
let _ = self.tx.send(());
|
||||
Draining(Box::pin(async move { self.tx.closed().await }))
|
||||
}
|
||||
}
|
||||
|
||||
impl Future for Draining {
|
||||
type Output = ();
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
|
||||
match ready!(self.project().drained_rx.poll_next(cx)) {
|
||||
Some(never) => match never {},
|
||||
None => Poll::Ready(()),
|
||||
}
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
|
||||
Pin::new(&mut self.as_mut().0).poll(cx)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,14 +57,17 @@ impl Watch {
|
||||
F: Future,
|
||||
FN: FnOnce(Pin<&mut F>),
|
||||
{
|
||||
let Self { drained_tx, mut rx } = self;
|
||||
let Self { mut rx } = self;
|
||||
let _rx = rx.clone();
|
||||
Watching {
|
||||
future,
|
||||
state: State::Watch(on_drain),
|
||||
watch: Box::pin(async move {
|
||||
let _ = rx.changed().await;
|
||||
}),
|
||||
_drained_tx: drained_tx,
|
||||
// Keep the receiver alive until the future completes, so that
|
||||
// dropping it can signal that draining has completed.
|
||||
_rx,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user