// Copyright (c) 2019 Emmanuel Gil Peyrot // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. use core::cmp::Ordering; use core::fmt; use core::task::{Context, Poll}; use std::collections::VecDeque; use std::io; use futures::ready; use tokio::sync::{mpsc, watch}; use crate::Stanza; #[derive(Debug, Clone)] pub struct OpaqueIoError { kind: io::ErrorKind, message: String, } impl OpaqueIoError { pub fn kind(&self) -> io::ErrorKind { self.kind } pub fn into_io_error(self) -> io::Error { io::Error::new(self.kind, self.message) } pub fn to_io_error(&self) -> io::Error { io::Error::new(self.kind, self.message.clone()) } } impl From for OpaqueIoError { fn from(other: io::Error) -> Self { >::from(&other) } } impl From<&io::Error> for OpaqueIoError { fn from(other: &io::Error) -> Self { Self { kind: other.kind(), message: other.to_string(), } } } impl fmt::Display for OpaqueIoError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str(&self.message) } } impl core::error::Error for OpaqueIoError {} /// The five stages of stanza transmission. #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] pub enum StanzaStage { /// The stanza is in the transmit queue, but has not been serialised or /// sent to the stream yet. Queued, /// The stanza was successfully serialised and put into the transmit /// buffers. Sent, /// The stanza has been acked by the peer using XEP-0198 or comparable /// means. /// /// **Note:** This state is only ever reached on streams where XEP-0198 /// was succesfully negotiated. Acked, /// Stanza transmission or serialisation failed. Failed, /// The stanza was dropped from the transmit queue before it could be /// sent. /// /// This may happen if the stream breaks in a fatal, panick-y way. Dropped, } impl From<&StanzaState> for StanzaStage { fn from(other: &StanzaState) -> Self { match other { StanzaState::Queued => Self::Queued, StanzaState::Sent { .. } => Self::Sent, StanzaState::Acked { .. } => Self::Acked, StanzaState::Failed { .. } => Self::Failed, StanzaState::Dropped => Self::Dropped, } } } impl PartialEq for StanzaState { fn eq(&self, other: &StanzaStage) -> bool { StanzaStage::from(self).eq(other) } } impl PartialEq for StanzaStage { fn eq(&self, other: &StanzaState) -> bool { self.eq(&Self::from(other)) } } impl PartialOrd for StanzaState { fn partial_cmp(&self, other: &StanzaStage) -> Option { StanzaStage::from(self).partial_cmp(other) } } impl PartialOrd for StanzaStage { fn partial_cmp(&self, other: &StanzaState) -> Option { self.partial_cmp(&Self::from(other)) } } /// State of a stanza in transit to the peer. #[derive(Debug, Clone)] pub enum StanzaState { /// The stanza has been enqueued in the local queue but not sent yet. Queued, /// The stanza has been sent to the server, but there is no proof that it /// has been received by the server yet. Sent { /* /// The time from when the stanza was enqueued until the time it was /// sent on the stream. queue_delay: Duration, */ }, /// Confirmation that the stanza has been seen by the server has been /// received. Acked { /* /// The time from when the stanza was enqueued until the time it was /// sent on the stream. queue_delay: Duration, /// The time between sending the stanza on the stream and receiving /// confirmation from the server. ack_delay: Duration, */ }, /// Sending the stanza has failed in a non-recoverable manner. Failed { /// The error which caused the sending to fail. error: OpaqueIoError, }, /// The stanza was dropped out of the queue for unspecified reasons, /// such as the stream breaking in a fatal, panick-y way. Dropped, } /// Track stanza transmission through the /// [`StanzaStream`][`super::StanzaStream`] up to the peer. #[derive(Clone)] pub struct StanzaToken { inner: watch::Receiver, } impl StanzaToken { /// Wait for the stanza transmission to reach the given state. /// /// If the stanza is removed from tracking before that state is reached, /// `None` is returned. pub async fn wait_for(&mut self, state: StanzaStage) -> Option { self.inner .wait_for(|st| *st >= state) .await .map(|x| x.clone()) .ok() } pub(crate) fn into_stream(self) -> tokio_stream::wrappers::WatchStream { tokio_stream::wrappers::WatchStream::new(self.inner) } /// Read the current transmission state. pub fn state(&self) -> StanzaState { self.inner.borrow().clone() } } pub(super) struct QueueEntry { pub stanza: Box, pub token: watch::Sender, } impl QueueEntry { pub fn untracked(st: Box) -> Self { Self::tracked(st).0 } pub fn tracked(st: Box) -> (Self, StanzaToken) { let (tx, rx) = watch::channel(StanzaState::Queued); let token = StanzaToken { inner: rx }; ( QueueEntry { stanza: st, token: tx, }, token, ) } } /// Reference to a transmit queue entry. /// /// On drop, the entry is returned to the queue. pub(super) struct TransmitQueueRef<'x, T> { q: &'x mut VecDeque, } impl TransmitQueueRef<'_, T> { /// Take the item out of the queue. pub fn take(self) -> T { // Unwrap: when this type is created, a check is made that the queue // actually has a front item and because we borrow, that also cannot // change. self.q.pop_front().unwrap() } } /// A transmit queue coupled to an [`mpsc::Receiver`]. /// /// The transmit queue will by default only allow one element to reside in the /// queue outside the inner `Receiver`: the main queueing happens inside the /// receiver and is governed by its queue depth and associated backpressure. /// /// However, the queue does allow prepending elements to the front, which is /// useful for retransmitting items. pub(super) struct TransmitQueue { inner: mpsc::Receiver, peek: VecDeque, } impl TransmitQueue { /// Create a new transmission queue around an existing mpsc receiver. pub fn wrap(ch: mpsc::Receiver) -> Self { Self { inner: ch, peek: VecDeque::with_capacity(1), } } /// Create a new mpsc channel and wrap the receiving side in a /// transmission queue pub fn channel(depth: usize) -> (mpsc::Sender, Self) { let (tx, rx) = mpsc::channel(depth); (tx, Self::wrap(rx)) } /// Poll the queue for the next item to transmit. pub fn poll_next(&mut self, cx: &mut Context) -> Poll>> { if !self.peek.is_empty() { // Cannot use `if let Some(.) = .` here because of a borrowchecker // restriction. If the reference is created before the branch is // entered, it will think it needs to be borrowed until the end // of the function (and that will conflict with the mutable // borrow we do for `self.peek.push_back` below). // See also https://github.com/rust-lang/rust/issues/54663. return Poll::Ready(Some(TransmitQueueRef { q: &mut self.peek })); } else { // The target size for the queue is 1, effectively acting as an // Option. In some cases, we need more than one, but that is // always only a temporary burst (e.g. SM resumption // retransmissions), so we release the memory as soon as possible // after that. // Even though the target size is 1, we don't want to be pedantic // about this and we don't want to reallocate often. Some short // bursts are ok, and given that the stanzas inside QueueEntry // elements (the main use case for this type) are boxed anyway, // the size of the elements is rather small. if self.peek.capacity() > 32 { // We do not use shrink_to here, because we are *certain* that // we won't need a larger capacity any time soon, and // allocators may avoid moving data around. let mut new = VecDeque::new(); core::mem::swap(&mut self.peek, &mut new); } } match ready!(self.inner.poll_recv(cx)) { None => Poll::Ready(None), Some(v) => { self.peek.push_back(v); Poll::Ready(Some(TransmitQueueRef { q: &mut self.peek })) } } } /// Requeue a sequence of items to the front of the queue. /// /// This function preserves ordering of the elements in `iter`, meaning /// that the first item from `iter` is going to be the next item yielded /// by `poll_take` or `poll_peek`. pub fn requeue_all>(&mut self, iter: I) { let iter = iter.into_iter(); let to_reserve = iter.size_hint().1.unwrap_or(iter.size_hint().0); self.peek.reserve(to_reserve); let mut n = 0; for item in iter { self.peek.push_front(item); n += 1; } // Now we need to revert the order: we pushed the elements to the // front, so if we now read back from the front via poll_peek or // poll_take, that will cause them to be read in reverse order. The // following loop fixes that. for i in 0..(n / 2) { let j = n - (i + 1); self.peek.swap(i, j); } } /// Enqueues an item to be sent after all items in the *local* queue, but /// *before* all items which are still inside the inner `mpsc` channel. pub fn enqueue(&mut self, item: T) { self.peek.push_back(item); } /// Return true if the sender side of the queue is closed. /// /// Note that there may still be items which can be retrieved from the /// queue even though it has been closed. pub fn is_closed(&self) -> bool { self.inner.is_closed() } } impl TransmitQueue { /// Fail all currently queued items with the given error. /// /// Future items will not be affected. pub fn fail(&mut self, error: &OpaqueIoError) { for item in self.peek.drain(..) { item.token.send_replace(StanzaState::Failed { error: error.clone(), }); } while let Ok(item) = self.inner.try_recv() { item.token.send_replace(StanzaState::Failed { error: error.clone(), }); } self.peek.shrink_to(1); } }