Skip to content

Commit

Permalink
Add FutureExt::also_poll
Browse files Browse the repository at this point in the history
  • Loading branch information
coolreader18 committed Mar 8, 2024
1 parent de1a0fd commit 4fcef24
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 1 deletion.
46 changes: 46 additions & 0 deletions futures-util/src/future/future/also_poll.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
use core::pin::Pin;
use futures_core::future::{FusedFuture, Future};
use futures_core::task::{Context, Poll};
use pin_project_lite::pin_project;

use super::Fuse;

pin_project! {
/// Future for the [`also_poll`](super::FutureExt::also_poll) method.
#[derive(Debug)]
#[must_use = "futures do nothing unless you `.await` or poll them"]
pub struct AlsoPoll<Fut, Also> {
#[pin] fut: Fut,
#[pin] also: Fuse<Also>,
}
}

impl<Fut, Also> AlsoPoll<Fut, Also> {
pub(super) fn new(fut: Fut, also: Also) -> Self {
AlsoPoll { fut, also: Fuse::new(also) }
}
}

impl<Fut, Also> Future for AlsoPoll<Fut, Also>
where
Fut: Future,
Also: Future<Output = ()>,
{
type Output = Fut::Output;

fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project();
let _ = this.also.poll(cx);
this.fut.poll(cx)
}
}

impl<Fut, Also> FusedFuture for AlsoPoll<Fut, Also>
where
Fut: FusedFuture,
Also: Future<Output = ()>,
{
fn is_terminated(&self) -> bool {
self.fut.is_terminated()
}
}
50 changes: 50 additions & 0 deletions futures-util/src/future/future/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,9 @@ mod shared;
#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411
pub use self::shared::{Shared, WeakShared};

mod also_poll;
pub use also_poll::AlsoPoll;

impl<T: ?Sized> FutureExt for T where T: Future {}

/// An extension trait for `Future`s that provides a variety of convenient
Expand Down Expand Up @@ -603,4 +606,51 @@ pub trait FutureExt: Future {
_ => None,
}
}

/// While polling this future, also poll another future at the same time,
/// ensuring progress is made on it.
///
/// The `also` future will be polled alongside the `self` future, in a
/// similar way to `join()`, but if `self` finishes first, `also` will
/// just be cancelled. If `also` finishes first, it will simply no longer
/// be polled.
///
/// Note that if `also_poll` is being called in a loop with the same
/// future, you should take care to ensure that the future is fused in some
/// way, or else repeatedly calling it may cause panics.
///
/// # Examples
///
/// ```
/// # use futures::prelude::*;
/// # futures::executor::block_on(async {
/// use futures::future::maybe_done;
///
/// // this may be something that we do not want to leave unpolled or
/// // half-polled while we're in our loop.
/// let task = maybe_done(async {
/// // ...
/// });
/// futures::pin_mut!(task);
///
/// for x in [1, 2, 3] {
/// do_thing(x).also_poll(&mut task).await;
/// }
///
/// (&mut task).await;
/// let result = task.take_output().unwrap();
/// # let _ = result;
/// # async fn do_thing(_x: i32) -> i32 { 0 }
/// # })
/// ```
///
/// (For the above example, simply using `join()` would likely be best, but
/// that may not be feasible in more complex scenarios.)
fn also_poll<Also>(self, also: Also) -> AlsoPoll<Self, Also>
where
Self: Sized,
Also: Future<Output = ()>,
{
assert_future::<Self::Output, _>(AlsoPoll::new(self, also))
}
}
3 changes: 2 additions & 1 deletion futures-util/src/future/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ pub use futures_task::{FutureObj, LocalFutureObj, UnsafeFutureObj};
#[allow(clippy::module_inception)]
mod future;
pub use self::future::{
Flatten, Fuse, FutureExt, Inspect, IntoStream, Map, MapInto, NeverError, Then, UnitError,
AlsoPoll, Flatten, Fuse, FutureExt, Inspect, IntoStream, Map, MapInto, NeverError, Then,
UnitError,
};

#[deprecated(note = "This is now an alias for [Flatten](Flatten)")]
Expand Down

0 comments on commit 4fcef24

Please sign in to comment.