blob: 3aaef8bdefbbfe74ea654234e6e16e4488208b5e [file] [log] [blame]
use crate::stream::{FuturesUnordered, StreamExt};
use alloc::collections::binary_heap::{BinaryHeap, PeekMut};
use core::cmp::Ordering;
use core::fmt::{self, Debug};
use core::iter::FromIterator;
use core::pin::Pin;
use futures_core::future::Future;
use futures_core::ready;
use futures_core::stream::Stream;
use futures_core::{
task::{Context, Poll},
FusedStream,
};
use pin_project_lite::pin_project;
pin_project! {
#[must_use = "futures do nothing unless you `.await` or poll them"]
#[derive(Debug)]
struct OrderWrapper<T> {
#[pin]
data: T, // A future or a future's output
index: isize,
}
}
impl<T> PartialEq for OrderWrapper<T> {
fn eq(&self, other: &Self) -> bool {
self.index == other.index
}
}
impl<T> Eq for OrderWrapper<T> {}
impl<T> PartialOrd for OrderWrapper<T> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<T> Ord for OrderWrapper<T> {
fn cmp(&self, other: &Self) -> Ordering {
// BinaryHeap is a max heap, so compare backwards here.
other.index.cmp(&self.index)
}
}
impl<T> Future for OrderWrapper<T>
where
T: Future,
{
type Output = OrderWrapper<T::Output>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let index = self.index;
self.project().data.poll(cx).map(|output| OrderWrapper { data: output, index })
}
}
/// An unbounded queue of futures.
///
/// This "combinator" is similar to [`FuturesUnordered`], but it imposes a FIFO
/// order on top of the set of futures. While futures in the set will race to
/// completion in parallel, results will only be returned in the order their
/// originating futures were added to the queue.
///
/// Futures are pushed into this queue and their realized values are yielded in
/// order. This structure is optimized to manage a large number of futures.
/// Futures managed by [`FuturesOrdered`] will only be polled when they generate
/// notifications. This reduces the required amount of work needed to coordinate
/// large numbers of futures.
///
/// When a [`FuturesOrdered`] is first created, it does not contain any futures.
/// Calling [`poll_next`](FuturesOrdered::poll_next) in this state will result
/// in [`Poll::Ready(None)`](Poll::Ready) to be returned. Futures are submitted
/// to the queue using [`push_back`](FuturesOrdered::push_back) (or
/// [`push_front`](FuturesOrdered::push_front)); however, the future will
/// **not** be polled at this point. [`FuturesOrdered`] will only poll managed
/// futures when [`FuturesOrdered::poll_next`] is called. As such, it
/// is important to call [`poll_next`](FuturesOrdered::poll_next) after pushing
/// new futures.
///
/// If [`FuturesOrdered::poll_next`] returns [`Poll::Ready(None)`](Poll::Ready)
/// this means that the queue is currently not managing any futures. A future
/// may be submitted to the queue at a later time. At that point, a call to
/// [`FuturesOrdered::poll_next`] will either return the future's resolved value
/// **or** [`Poll::Pending`] if the future has not yet completed. When
/// multiple futures are submitted to the queue, [`FuturesOrdered::poll_next`]
/// will return [`Poll::Pending`] until the first future completes, even if
/// some of the later futures have already completed.
///
/// Note that you can create a ready-made [`FuturesOrdered`] via the
/// [`collect`](Iterator::collect) method, or you can start with an empty queue
/// with the [`FuturesOrdered::new`] constructor.
///
/// This type is only available when the `std` or `alloc` feature of this
/// library is activated, and it is activated by default.
#[must_use = "streams do nothing unless polled"]
pub struct FuturesOrdered<T: Future> {
in_progress_queue: FuturesUnordered<OrderWrapper<T>>,
queued_outputs: BinaryHeap<OrderWrapper<T::Output>>,
next_incoming_index: isize,
next_outgoing_index: isize,
}
impl<T: Future> Unpin for FuturesOrdered<T> {}
impl<Fut: Future> FuturesOrdered<Fut> {
/// Constructs a new, empty `FuturesOrdered`
///
/// The returned [`FuturesOrdered`] does not contain any futures and, in
/// this state, [`FuturesOrdered::poll_next`] will return
/// [`Poll::Ready(None)`](Poll::Ready).
pub fn new() -> Self {
Self {
in_progress_queue: FuturesUnordered::new(),
queued_outputs: BinaryHeap::new(),
next_incoming_index: 0,
next_outgoing_index: 0,
}
}
/// Returns the number of futures contained in the queue.
///
/// This represents the total number of in-flight futures, both
/// those currently processing and those that have completed but
/// which are waiting for earlier futures to complete.
pub fn len(&self) -> usize {
self.in_progress_queue.len() + self.queued_outputs.len()
}
/// Returns `true` if the queue contains no futures
pub fn is_empty(&self) -> bool {
self.in_progress_queue.is_empty() && self.queued_outputs.is_empty()
}
/// Push a future into the queue.
///
/// This function submits the given future to the internal set for managing.
/// This function will not call [`poll`](Future::poll) on the submitted
/// future. The caller must ensure that [`FuturesOrdered::poll_next`] is
/// called in order to receive task notifications.
#[deprecated(note = "use `push_back` instead")]
pub fn push(&mut self, future: Fut) {
self.push_back(future);
}
/// Pushes a future to the back of the queue.
///
/// This function submits the given future to the internal set for managing.
/// This function will not call [`poll`](Future::poll) on the submitted
/// future. The caller must ensure that [`FuturesOrdered::poll_next`] is
/// called in order to receive task notifications.
pub fn push_back(&mut self, future: Fut) {
let wrapped = OrderWrapper { data: future, index: self.next_incoming_index };
self.next_incoming_index += 1;
self.in_progress_queue.push(wrapped);
}
/// Pushes a future to the front of the queue.
///
/// This function submits the given future to the internal set for managing.
/// This function will not call [`poll`](Future::poll) on the submitted
/// future. The caller must ensure that [`FuturesOrdered::poll_next`] is
/// called in order to receive task notifications. This future will be
/// the next future to be returned complete.
pub fn push_front(&mut self, future: Fut) {
let wrapped = OrderWrapper { data: future, index: self.next_outgoing_index - 1 };
self.next_outgoing_index -= 1;
self.in_progress_queue.push(wrapped);
}
}
impl<Fut: Future> Default for FuturesOrdered<Fut> {
fn default() -> Self {
Self::new()
}
}
impl<Fut: Future> Stream for FuturesOrdered<Fut> {
type Item = Fut::Output;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let this = &mut *self;
// Check to see if we've already received the next value
if let Some(next_output) = this.queued_outputs.peek_mut() {
if next_output.index == this.next_outgoing_index {
this.next_outgoing_index += 1;
return Poll::Ready(Some(PeekMut::pop(next_output).data));
}
}
loop {
match ready!(this.in_progress_queue.poll_next_unpin(cx)) {
Some(output) => {
if output.index == this.next_outgoing_index {
this.next_outgoing_index += 1;
return Poll::Ready(Some(output.data));
} else {
this.queued_outputs.push(output)
}
}
None => return Poll::Ready(None),
}
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.len();
(len, Some(len))
}
}
impl<Fut: Future> Debug for FuturesOrdered<Fut> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "FuturesOrdered {{ ... }}")
}
}
impl<Fut: Future> FromIterator<Fut> for FuturesOrdered<Fut> {
fn from_iter<T>(iter: T) -> Self
where
T: IntoIterator<Item = Fut>,
{
let acc = Self::new();
iter.into_iter().fold(acc, |mut acc, item| {
acc.push_back(item);
acc
})
}
}
impl<Fut: Future> FusedStream for FuturesOrdered<Fut> {
fn is_terminated(&self) -> bool {
self.in_progress_queue.is_terminated() && self.queued_outputs.is_empty()
}
}
impl<Fut: Future> Extend<Fut> for FuturesOrdered<Fut> {
fn extend<I>(&mut self, iter: I)
where
I: IntoIterator<Item = Fut>,
{
for item in iter {
self.push_back(item);
}
}
}