diff --git a/actix-utils/src/future/mod.rs b/actix-utils/src/future/mod.rs index be3807bf..399b54d2 100644 --- a/actix-utils/src/future/mod.rs +++ b/actix-utils/src/future/mod.rs @@ -1,4 +1,4 @@ -//! Asynchronous values. +//! Helpers for constructing futures. mod either; mod poll_fn; diff --git a/actix-utils/src/future/poll_fn.rs b/actix-utils/src/future/poll_fn.rs index 31421b45..15d93dbd 100644 --- a/actix-utils/src/future/poll_fn.rs +++ b/actix-utils/src/future/poll_fn.rs @@ -8,6 +8,31 @@ use core::{ }; /// Creates a future driven by the provided function that receives a task context. +/// +/// # Examples +/// ``` +/// # use std::task::Poll; +/// # use actix_utils::future::poll_fn; +/// # async fn test_poll_fn() { +/// let res = poll_fn(|_| Poll::Ready(42)).await; +/// assert_eq!(res, 42); +/// +/// let mut i = 5; +/// let res = poll_fn(|cx| { +/// i -= 1; +/// +/// if i > 0 { +/// cx.waker().wake_by_ref(); +/// Poll::Pending +/// } else { +/// Poll::Ready(42) +/// } +/// }) +/// .await; +/// assert_eq!(res, 42); +/// # } +/// # actix_rt::Runtime::new().unwrap().block_on(test_poll_fn()); +/// ``` #[inline] pub fn poll_fn(f: F) -> PollFn where @@ -16,13 +41,11 @@ where PollFn { f } } -/// A Future driven by the inner function. +/// Future for the [`poll_fn`] function. pub struct PollFn { f: F, } -impl Unpin for PollFn {} - impl fmt::Debug for PollFn { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("PollFn").finish() @@ -36,15 +59,22 @@ where type Output = T; #[inline] - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - (self.f)(cx) + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + // SAFETY: we are not moving out of the pinned field + // see https://github.com/rust-lang/rust/pull/102737 + (unsafe { &mut self.get_unchecked_mut().f })(cx) } } #[cfg(test)] mod tests { + use std::marker::PhantomPinned; + use super::*; + static_assertions::assert_impl_all!(PollFn<()>: Unpin); + static_assertions::assert_not_impl_all!(PollFn: Unpin); + #[actix_rt::test] async fn test_poll_fn() { let res = poll_fn(|_| Poll::Ready(42)).await; @@ -64,4 +94,29 @@ mod tests { .await; assert_eq!(res, 42); } + + // following soundness tests taken from https://github.com/tokio-rs/tokio/pull/5087 + + #[allow(dead_code)] + fn require_send(_t: &T) {} + #[allow(dead_code)] + fn require_sync(_t: &T) {} + + trait AmbiguousIfUnpin { + fn some_item(&self) {} + } + impl AmbiguousIfUnpin<()> for T {} + impl AmbiguousIfUnpin<[u8; 0]> for T {} + + const _: fn() = || { + let pinned = std::marker::PhantomPinned; + let f = poll_fn(move |_| { + // Use `pinned` to take ownership of it. + let _ = &pinned; + std::task::Poll::Pending::<()> + }); + require_send(&f); + require_sync(&f); + AmbiguousIfUnpin::some_item(&f); + }; } diff --git a/actix-utils/src/future/ready.rs b/actix-utils/src/future/ready.rs index cd375c1f..31eb6551 100644 --- a/actix-utils/src/future/ready.rs +++ b/actix-utils/src/future/ready.rs @@ -1,4 +1,4 @@ -//! When MSRV is 1.48, replace with `core::future::Ready` and `core::future::ready()`. +//! When `core::future::Ready` has a `into_inner()` method, this can be deprecated. use core::{ future::Future, @@ -6,7 +6,7 @@ use core::{ task::{Context, Poll}, }; -/// Future for the [`ready`](ready()) function. +/// Future for the [`ready`] function. /// /// Panic will occur if polled more than once. ///