mirror of
https://github.com/fafhrd91/actix-net
synced 2024-11-27 16:52:58 +01:00
address soundness footgun in poll_fn
This commit is contained in:
parent
cb83922b29
commit
ade71b7bd3
@ -1,4 +1,4 @@
|
|||||||
//! Asynchronous values.
|
//! Helpers for constructing futures.
|
||||||
|
|
||||||
mod either;
|
mod either;
|
||||||
mod poll_fn;
|
mod poll_fn;
|
||||||
|
@ -8,6 +8,31 @@ use core::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// Creates a future driven by the provided function that receives a task context.
|
/// 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]
|
#[inline]
|
||||||
pub fn poll_fn<F, T>(f: F) -> PollFn<F>
|
pub fn poll_fn<F, T>(f: F) -> PollFn<F>
|
||||||
where
|
where
|
||||||
@ -16,13 +41,11 @@ where
|
|||||||
PollFn { f }
|
PollFn { f }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A Future driven by the inner function.
|
/// Future for the [`poll_fn`] function.
|
||||||
pub struct PollFn<F> {
|
pub struct PollFn<F> {
|
||||||
f: F,
|
f: F,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F> Unpin for PollFn<F> {}
|
|
||||||
|
|
||||||
impl<F> fmt::Debug for PollFn<F> {
|
impl<F> fmt::Debug for PollFn<F> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_struct("PollFn").finish()
|
f.debug_struct("PollFn").finish()
|
||||||
@ -36,15 +59,22 @@ where
|
|||||||
type Output = T;
|
type Output = T;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
(self.f)(cx)
|
// 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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use std::marker::PhantomPinned;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
static_assertions::assert_impl_all!(PollFn<()>: Unpin);
|
||||||
|
static_assertions::assert_not_impl_all!(PollFn<PhantomPinned>: Unpin);
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_poll_fn() {
|
async fn test_poll_fn() {
|
||||||
let res = poll_fn(|_| Poll::Ready(42)).await;
|
let res = poll_fn(|_| Poll::Ready(42)).await;
|
||||||
@ -64,4 +94,29 @@ mod tests {
|
|||||||
.await;
|
.await;
|
||||||
assert_eq!(res, 42);
|
assert_eq!(res, 42);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// following soundness tests taken from https://github.com/tokio-rs/tokio/pull/5087
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn require_send<T: Send>(_t: &T) {}
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn require_sync<T: Sync>(_t: &T) {}
|
||||||
|
|
||||||
|
trait AmbiguousIfUnpin<A> {
|
||||||
|
fn some_item(&self) {}
|
||||||
|
}
|
||||||
|
impl<T: ?Sized> AmbiguousIfUnpin<()> for T {}
|
||||||
|
impl<T: ?Sized + Unpin> 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);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -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::{
|
use core::{
|
||||||
future::Future,
|
future::Future,
|
||||||
@ -6,7 +6,7 @@ use core::{
|
|||||||
task::{Context, Poll},
|
task::{Context, Poll},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Future for the [`ready`](ready()) function.
|
/// Future for the [`ready`] function.
|
||||||
///
|
///
|
||||||
/// Panic will occur if polled more than once.
|
/// Panic will occur if polled more than once.
|
||||||
///
|
///
|
||||||
|
Loading…
Reference in New Issue
Block a user