use alloc::collections::BTreeSet;
use core::fmt::{self, Debug};
use core::ops::{Deref, DerefMut};
use core::pin::Pin;
use core::task::{Context, Poll};
use futures_core::stream::Stream;
use futures_core::Future;
use crate::utils::{ChunkedVec, PollState, PollVec, WakerVec};
#[must_use = "`FutureGroup` does nothing if not iterated over"]
#[pin_project::pin_project]
pub struct FutureGroup<F> {
#[pin]
futures: ChunkedVec<F>,
wakers: WakerVec,
states: PollVec,
keys: BTreeSet<usize>,
}
impl<T: Debug> Debug for FutureGroup<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("FutureGroup")
.field("futures", &"[..]")
.field("len", &self.len())
.field("capacity", &self.capacity())
.finish()
}
}
impl<T> Default for FutureGroup<T> {
fn default() -> Self {
Self::new()
}
}
impl<F> FutureGroup<F> {
pub fn new() -> Self {
Self::with_capacity(0)
}
pub fn with_capacity(capacity: usize) -> Self {
Self {
futures: ChunkedVec::with_capacity(capacity),
wakers: WakerVec::new(capacity),
states: PollVec::new(capacity),
keys: BTreeSet::new(),
}
}
#[inline(always)]
pub fn len(&self) -> usize {
self.futures.len()
}
pub fn capacity(&self) -> usize {
self.futures.capacity()
}
pub fn is_empty(&self) -> bool {
self.futures.is_empty()
}
pub fn remove(&mut self, key: Key) -> bool {
let is_present = self.keys.remove(&key.0);
if is_present {
self.states[key.0].set_none();
self.futures.remove(key.0);
}
is_present
}
pub fn contains_key(&mut self, key: Key) -> bool {
self.keys.contains(&key.0)
}
pub fn reserve(&mut self, additional: usize) {
self.futures.reserve(additional);
let new_cap = self.futures.capacity();
self.wakers.resize(new_cap);
self.states.resize(new_cap);
}
}
impl<F: Future> FutureGroup<F> {
pub fn insert(&mut self, future: F) -> Key
where
F: Future,
{
let index = self.futures.insert(future);
self.keys.insert(index);
let new_cap = self.futures.capacity();
self.wakers.resize(new_cap);
self.states.resize(new_cap);
self.states[index].set_pending();
self.wakers.readiness().set_ready(index);
Key(index)
}
#[allow(unused)]
pub(crate) fn insert_pinned(self: Pin<&mut Self>, future: F) -> Key
where
F: Future,
{
let mut this = self.project();
let index = unsafe { this.futures.as_mut().get_unchecked_mut() }.insert(future);
this.keys.insert(index);
let key = Key(index);
let new_cap = this.futures.as_ref().capacity();
this.wakers.resize(new_cap);
this.states.resize(new_cap);
this.states[index].set_pending();
let mut readiness = this.wakers.readiness();
readiness.set_ready(index);
key
}
pub fn keyed(self) -> Keyed<F> {
Keyed { group: self }
}
}
impl<F: Future> FutureGroup<F> {
fn poll_next_inner(
self: Pin<&mut Self>,
cx: &Context<'_>,
) -> Poll<Option<(Key, <F as Future>::Output)>> {
let mut this = self.project();
if this.futures.is_empty() {
return Poll::Ready(None);
}
let mut readiness = this.wakers.readiness();
readiness.set_waker(cx.waker());
if !readiness.any_ready() {
return Poll::Pending;
}
let mut ret = Poll::Pending;
let states = this.states;
let futures = unsafe { this.futures.as_mut().get_unchecked_mut() };
for index in this.keys.iter().cloned() {
if states[index].is_pending() && readiness.clear_ready(index) {
#[allow(clippy::drop_non_drop)]
drop(readiness);
let mut cx = Context::from_waker(this.wakers.get(index).unwrap());
let future = unsafe { Pin::new_unchecked(&mut futures[index]) };
match future.poll(&mut cx) {
Poll::Ready(item) => {
ret = Poll::Ready(Some((Key(index), item)));
states[index] = PollState::None;
futures.remove(index);
break;
}
Poll::Pending => {}
};
readiness = this.wakers.readiness();
}
}
if let Poll::Ready(Some((key, _))) = ret {
this.keys.remove(&key.0);
}
ret
}
}
impl<F: Future> Stream for FutureGroup<F> {
type Item = <F as Future>::Output;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
match self.poll_next_inner(cx) {
Poll::Ready(Some((_key, item))) => Poll::Ready(Some(item)),
Poll::Ready(None) => Poll::Ready(None),
Poll::Pending => Poll::Pending,
}
}
}
impl<F: Future> Extend<F> for FutureGroup<F> {
fn extend<T: IntoIterator<Item = F>>(&mut self, iter: T) {
let iter = iter.into_iter();
let len = iter.size_hint().1.unwrap_or_default();
self.reserve(len);
for future in iter {
self.insert(future);
}
}
}
impl<F: Future> FromIterator<F> for FutureGroup<F> {
fn from_iter<T: IntoIterator<Item = F>>(iter: T) -> Self {
let mut this = Self::new();
this.extend(iter);
this
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Key(usize);
#[derive(Debug)]
#[pin_project::pin_project]
pub struct Keyed<F: Future> {
#[pin]
group: FutureGroup<F>,
}
impl<F: Future> Deref for Keyed<F> {
type Target = FutureGroup<F>;
fn deref(&self) -> &Self::Target {
&self.group
}
}
impl<F: Future> DerefMut for Keyed<F> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.group
}
}
impl<F: Future> Stream for Keyed<F> {
type Item = (Key, <F as Future>::Output);
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let mut this = self.project();
this.group.as_mut().poll_next_inner(cx)
}
}
#[cfg(test)]
mod test {
use super::FutureGroup;
use core::future;
use futures_lite::prelude::*;
#[test]
fn smoke() {
futures_lite::future::block_on(async {
let mut group = FutureGroup::new();
group.insert(future::ready(2));
group.insert(future::ready(4));
let mut out = 0;
while let Some(num) = group.next().await {
out += num;
}
assert_eq!(out, 6);
assert_eq!(group.len(), 0);
assert!(group.is_empty());
});
}
#[test]
fn capacity_grow_on_insert() {
futures_lite::future::block_on(async {
let mut group = FutureGroup::new();
let cap = group.capacity();
group.insert(future::ready(1));
assert!(group.capacity() > cap);
});
}
}