1 | //! Definition of the [`MaybeDone`] combinator. |
2 | |
3 | use pin_project_lite::pin_project; |
4 | use std::future::{Future, IntoFuture}; |
5 | use std::pin::Pin; |
6 | use std::task::{ready, Context, Poll}; |
7 | |
8 | pin_project! { |
9 | /// A future that may have completed. |
10 | #[derive (Debug)] |
11 | #[project = MaybeDoneProj] |
12 | #[project_replace = MaybeDoneProjReplace] |
13 | #[repr (C)] // https://github.com/rust-lang/miri/issues/3780 |
14 | pub enum MaybeDone<Fut: Future> { |
15 | /// A not-yet-completed future. |
16 | Future { #[pin] future: Fut }, |
17 | /// The output of the completed future. |
18 | Done { output: Fut::Output }, |
19 | /// The empty variant after the result of a [`MaybeDone`] has been |
20 | /// taken using the [`take_output`](MaybeDone::take_output) method. |
21 | Gone, |
22 | } |
23 | } |
24 | |
25 | /// Wraps a future into a `MaybeDone`. |
26 | pub fn maybe_done<F: IntoFuture>(future: F) -> MaybeDone<F::IntoFuture> { |
27 | MaybeDone::Future { |
28 | future: future.into_future(), |
29 | } |
30 | } |
31 | |
32 | impl<Fut: Future> MaybeDone<Fut> { |
33 | /// Returns an [`Option`] containing a mutable reference to the output of the future. |
34 | /// The output of this method will be [`Some`] if and only if the inner |
35 | /// future has been completed and [`take_output`](MaybeDone::take_output) |
36 | /// has not yet been called. |
37 | pub fn output_mut(self: Pin<&mut Self>) -> Option<&mut Fut::Output> { |
38 | match self.project() { |
39 | MaybeDoneProj::Done { output } => Some(output), |
40 | _ => None, |
41 | } |
42 | } |
43 | |
44 | /// Attempts to take the output of a `MaybeDone` without driving it |
45 | /// towards completion. |
46 | #[inline ] |
47 | pub fn take_output(self: Pin<&mut Self>) -> Option<Fut::Output> { |
48 | match *self { |
49 | MaybeDone::Done { .. } => {} |
50 | MaybeDone::Future { .. } | MaybeDone::Gone => return None, |
51 | }; |
52 | if let MaybeDoneProjReplace::Done { output } = self.project_replace(MaybeDone::Gone) { |
53 | Some(output) |
54 | } else { |
55 | unreachable!() |
56 | } |
57 | } |
58 | } |
59 | |
60 | impl<Fut: Future> Future for MaybeDone<Fut> { |
61 | type Output = (); |
62 | |
63 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { |
64 | let output: ::Output = match self.as_mut().project() { |
65 | MaybeDoneProj::Future { future: Pin<&mut Fut> } => ready!(future.poll(cx)), |
66 | MaybeDoneProj::Done { .. } => return Poll::Ready(()), |
67 | MaybeDoneProj::Gone => panic!("MaybeDone polled after value taken" ), |
68 | }; |
69 | self.set(MaybeDone::Done { output }); |
70 | Poll::Ready(()) |
71 | } |
72 | } |
73 | |
74 | // Test for https://github.com/tokio-rs/tokio/issues/6729 |
75 | #[cfg (test)] |
76 | mod miri_tests { |
77 | use super::maybe_done; |
78 | |
79 | use std::{ |
80 | future::Future, |
81 | pin::Pin, |
82 | sync::Arc, |
83 | task::{Context, Poll, Wake}, |
84 | }; |
85 | |
86 | struct ThingAdder<'a> { |
87 | thing: &'a mut String, |
88 | } |
89 | |
90 | impl Future for ThingAdder<'_> { |
91 | type Output = (); |
92 | |
93 | fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> { |
94 | unsafe { |
95 | *self.get_unchecked_mut().thing += ", world" ; |
96 | } |
97 | Poll::Pending |
98 | } |
99 | } |
100 | |
101 | #[test ] |
102 | fn maybe_done_miri() { |
103 | let mut thing = "hello" .to_owned(); |
104 | |
105 | // The async block is necessary to trigger the miri failure. |
106 | #[allow (clippy::redundant_async_block)] |
107 | let fut = async move { ThingAdder { thing: &mut thing }.await }; |
108 | |
109 | let mut fut = maybe_done(fut); |
110 | let mut fut = unsafe { Pin::new_unchecked(&mut fut) }; |
111 | |
112 | let waker = Arc::new(DummyWaker).into(); |
113 | let mut ctx = Context::from_waker(&waker); |
114 | assert_eq!(fut.as_mut().poll(&mut ctx), Poll::Pending); |
115 | assert_eq!(fut.as_mut().poll(&mut ctx), Poll::Pending); |
116 | } |
117 | |
118 | struct DummyWaker; |
119 | |
120 | impl Wake for DummyWaker { |
121 | fn wake(self: Arc<Self>) {} |
122 | } |
123 | } |
124 | |