// Copyright 2021 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see .
//! Metered variant of oneshot channels to be able to extract delays caused by delayed responses.
use std::{
ops::Deref,
pin::Pin,
task::{Context, Poll},
};
use futures::{
channel::oneshot::{self, Canceled, Cancellation},
future::{Fuse, FusedFuture},
prelude::*,
};
use futures_timer::Delay;
use crate::{CoarseDuration, CoarseInstant};
/// Provides the reason for termination.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum Reason {
Completion = 1,
Cancellation = 2,
HardTimeout = 3,
}
/// Obtained measurements by the `Receiver` side of the `MeteredOneshot`.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Measurements {
/// Duration between first poll and polling termination.
first_poll_till_end: CoarseDuration,
/// Duration starting with creation until polling termination.
creation_till_end: CoarseDuration,
/// Reason for resolving the future.
reason: Reason,
}
impl Measurements {
/// Obtain the duration of a finished or canceled
/// `oneshot` channel.
pub fn duration_since_first_poll(&self) -> &CoarseDuration {
&self.first_poll_till_end
}
/// Obtain the duration of a finished or canceled
/// `oneshot` channel.
pub fn duration_since_creation(&self) -> &CoarseDuration {
&self.creation_till_end
}
/// Obtain the reason to the channel termination.
pub fn reason(&self) -> &Reason {
&self.reason
}
}
/// Create a new pair of `OneshotMetered{Sender,Receiver}`.
pub fn channel(
name: &'static str,
soft_timeout: CoarseDuration,
hard_timeout: CoarseDuration,
) -> (MeteredSender, MeteredReceiver) {
let (tx, rx) = oneshot::channel();
(
MeteredSender { inner: tx },
MeteredReceiver {
name,
inner: rx,
soft_timeout,
hard_timeout,
soft_timeout_fut: None,
hard_timeout_fut: None,
first_poll_timestamp: None,
creation_timestamp: CoarseInstant::now(),
},
)
}
#[allow(missing_docs)]
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("Oneshot was canceled.")]
Canceled(#[source] Canceled, Measurements),
#[error("Oneshot did not receive a response within {}", CoarseDuration::as_f64(.0))]
HardTimeout(CoarseDuration, Measurements),
}
impl Measurable for Error {
fn measurements(&self) -> Measurements {
match self {
Self::Canceled(_, measurements) => measurements.clone(),
Self::HardTimeout(_, measurements) => measurements.clone(),
}
}
}
/// Oneshot sender, created by [`channel`].
#[derive(Debug)]
pub struct MeteredSender {
inner: oneshot::Sender<(CoarseInstant, T)>,
}
impl MeteredSender {
/// Send a value.
pub fn send(self, t: T) -> Result<(), T> {
let Self { inner } = self;
inner.send((CoarseInstant::now(), t)).map_err(|(_, t)| t)
}
/// Poll if the thing is already canceled.
pub fn poll_canceled(&mut self, ctx: &mut Context<'_>) -> Poll<()> {
self.inner.poll_canceled(ctx)
}
/// Access the cancellation object.
pub fn cancellation(&mut self) -> Cancellation<'_, (CoarseInstant, T)> {
self.inner.cancellation()
}
/// Check the cancellation state.
pub fn is_canceled(&self) -> bool {
self.inner.is_canceled()
}
/// Verify if the `receiver` is connected to the `sender` [`Self`].
pub fn is_connected_to(&self, receiver: &MeteredReceiver) -> bool {
self.inner.is_connected_to(&receiver.inner)
}
}
/// Oneshot receiver, created by [`channel`].
#[derive(Debug)]
pub struct MeteredReceiver {
name: &'static str,
inner: oneshot::Receiver<(CoarseInstant, T)>,
/// Soft timeout, on expire a warning is printed.
soft_timeout_fut: Option>,
soft_timeout: CoarseDuration,
/// Hard timeout, terminating the sender.
hard_timeout_fut: Option,
hard_timeout: CoarseDuration,
/// The first time the receiver was polled.
first_poll_timestamp: Option,
creation_timestamp: CoarseInstant,
}
impl MeteredReceiver {
pub fn close(&mut self) {
self.inner.close()
}
/// Attempts to receive a message outside of the context of a task.
///
/// A return value of `None` must be considered immediately stale (out of
/// date) unless [`close`](MeteredReceiver::close) has been called first.
///
/// Returns an error if the sender was dropped.
pub fn try_recv(&mut self) -> Result