Info
This post is auto-generated from RSS feed The Rust Programming Language Forum - Latest topics. Source: Implementors of `Future`
After reading Boats' "Why Async Rust" I've come to notice these (std
(permalink)):
#[stable(feature = "futures_api", since = "1.36.0")]
impl<F: ?Sized + Future + Unpin> Future for &mut F {
type Output = F::Output;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
F::poll(Pin::new(&mut **self), cx)
}
}
#[stable(feature = "futures_api", since = "1.36.0")]
impl<P> Future for Pin<P>
where
P: ops::DerefMut<Target: Future>,
{
type Output = <<P as ops::Deref>::Target as Future>::Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
<P::Target as Future>::poll(self.as_deref_mut(), cx)
}
}
Specifically I'm interested in the reason behind the F: Unpin
trait bound for &mut F
to be Future
. Is it to guard against something like Arc<Mutex<F: !Unpin + Send + Future>>
(playground)?
use std::{
future::poll_fn, hint::spin_loop, mem::replace, pin::Pin, sync::Arc, task::Poll, time::Duration,
};
use tokio::{
join, spawn as task_spawn, sync::Mutex, sync::oneshot::channel, task::spawn_blocking,
time::sleep,
};
#[tokio::main]
async fn main() {
let fut = Arc::new(Mutex::new(sleep(Duration::from_secs(2))));
let h0 = {
let fut = Arc::clone(&fut);
task_spawn(async move {
let fut = &mut *fut.lock().await;
let fut: &mut (dyn Future<Output = ()> + Send) = fut;
let fut = unsafe {
// Supposedly the reason why we need `Unpin` trait bound?
// safety:
// _not_ safe since it's not local-pinning but some arbitrary reference
// in particular it's behind `Arc<Mutex<_>>` s.t. other thread may do bad things
Pin::new_unchecked(fut)
};
fut.await;
})
};
let (tx, rx) = channel();
let mut new_nap = sleep(Duration::from_secs(1));
poll_fn(|cx| {
// Stolen from https://fasterthanli.me/articles/pin-and-suffering
// abusing `tokio::time::Sleep` for demonstration purposes
//
// There might exist some other sorts of `!Unpin` `Future` that's even worse?
// in the sense simply `mem::swap` a fresh, not yet `poll`-ed instance is enough to
// break certain invariants within the type
unsafe {
_ = Pin::new_unchecked(&mut new_nap).as_mut().poll(cx);
};
Poll::Ready(())
})
.await;
let h1 = spawn_blocking(move || {
loop {
match fut.try_lock() {
Ok(mut old_nap) => {
tx.send(replace(&mut *old_nap, new_nap)).unwrap();
break;
}
Err(_) => {
spin_loop();
}
}
}
});
let old_nap = task_spawn(async move { rx.await.unwrap().await });
_ = join!(h0, h1, old_nap);
}
If that's the case, then I don't understand this quote: I'm assuming &mut dyn Future
trait objects here, and from the above experiment, we can see that indeed we do need the Unpin
trait bound, no?
The only one thatโs really bad... is that you need to pin a future trait object to await it
3 posts - 2 participants
๐ท๏ธ Rust_feed