futures-rs
futures-rs copied to clipboard
Proposal: Add a future that can be paused and restarted
Sometimes, you'd like to temporarily suspend a future until something else happens. I propose a Pausable<F>
API that allows one to render a Future
as stopped.
Public API
pub trait FutureExt {
/* ... */
fn pausable(self) -> Pausable<F>;
}
pub struct Pausable<F> { /* ... */ }
pub struct PausableHandle { /* ... */ }
impl<F: Future> Future for Pausable<F> {
type Output = F::Output;
/* ... */
}
impl<F> Pausable<F> {
pub fn pause_handle(&self) -> PausableHandle { /* ... */ }
}
impl PausableHandle {
pub fn pause(&self) { /* ... */ }
pub fn unpause(&self) { /* ... */ }
}
Reference Level
This would be implemented by having a SharedState
struct between Pausable
and PausableHandle
through an Arc
that looks like this:
struct SharedState {
paused: AtomicBool,
waker: AtomicWaker,
}
PausableHandle
would then be implemented like this (ignoring atomics):
pub struct PausableHandle {
state: Arc<SharedState>,
}
impl PausableHandle {
pub fn pause(&self) {
self.state.store(true);
}
pub fn unpause(&self) {
self.state.store(false);
self.waker.wake();
}
}
Pausable<F>
would then be implemented like this:
#[pin_project]
pub struct Pausable<F> {
#[pin]
fut: F,
state: Arc<SharedState>,
}
impl<F> {
pub fn handle(&self) -> PausableHandle {
PausableHandle { state: self.state.clone() }
}
// also have pause() and unpause() from the handle struct here
}
impl<F: Future> Future for Pausable<F> {
type Item = F::Item;
fn poll(&mut self, cx: &mut Context) -> Poll<Item> {
if self.state.paused.load() {
// store waker so we're woken up on unpause
self.state.waker.register(cx.waker());
Poll::Pending
} else {
self.fut.poll(cx)
}
}
}