1 | use std::alloc::Layout; |
2 | use std::fmt; |
3 | use std::future::{self, Future}; |
4 | use std::mem::{self, ManuallyDrop}; |
5 | use std::pin::Pin; |
6 | use std::ptr; |
7 | use std::task::{Context, Poll}; |
8 | |
9 | /// A reusable `Pin<Box<dyn Future<Output = T> + Send + 'a>>`. |
10 | /// |
11 | /// This type lets you replace the future stored in the box without |
12 | /// reallocating when the size and alignment permits this. |
13 | pub struct ReusableBoxFuture<'a, T> { |
14 | boxed: Pin<Box<dyn Future<Output = T> + Send + 'a>>, |
15 | } |
16 | |
17 | impl<'a, T> ReusableBoxFuture<'a, T> { |
18 | /// Create a new `ReusableBoxFuture<T>` containing the provided future. |
19 | pub fn new<F>(future: F) -> Self |
20 | where |
21 | F: Future<Output = T> + Send + 'a, |
22 | { |
23 | Self { |
24 | boxed: Box::pin(future), |
25 | } |
26 | } |
27 | |
28 | /// Replace the future currently stored in this box. |
29 | /// |
30 | /// This reallocates if and only if the layout of the provided future is |
31 | /// different from the layout of the currently stored future. |
32 | pub fn set<F>(&mut self, future: F) |
33 | where |
34 | F: Future<Output = T> + Send + 'a, |
35 | { |
36 | if let Err(future) = self.try_set(future) { |
37 | *self = Self::new(future); |
38 | } |
39 | } |
40 | |
41 | /// Replace the future currently stored in this box. |
42 | /// |
43 | /// This function never reallocates, but returns an error if the provided |
44 | /// future has a different size or alignment from the currently stored |
45 | /// future. |
46 | pub fn try_set<F>(&mut self, future: F) -> Result<(), F> |
47 | where |
48 | F: Future<Output = T> + Send + 'a, |
49 | { |
50 | // If we try to inline the contents of this function, the type checker complains because |
51 | // the bound `T: 'a` is not satisfied in the call to `pending()`. But by putting it in an |
52 | // inner function that doesn't have `T` as a generic parameter, we implicitly get the bound |
53 | // `F::Output: 'a` transitively through `F: 'a`, allowing us to call `pending()`. |
54 | #[inline (always)] |
55 | fn real_try_set<'a, F>( |
56 | this: &mut ReusableBoxFuture<'a, F::Output>, |
57 | future: F, |
58 | ) -> Result<(), F> |
59 | where |
60 | F: Future + Send + 'a, |
61 | { |
62 | // future::Pending<T> is a ZST so this never allocates. |
63 | let boxed = mem::replace(&mut this.boxed, Box::pin(future::pending())); |
64 | reuse_pin_box(boxed, future, |boxed| this.boxed = Pin::from(boxed)) |
65 | } |
66 | |
67 | real_try_set(self, future) |
68 | } |
69 | |
70 | /// Get a pinned reference to the underlying future. |
71 | pub fn get_pin(&mut self) -> Pin<&mut (dyn Future<Output = T> + Send)> { |
72 | self.boxed.as_mut() |
73 | } |
74 | |
75 | /// Poll the future stored inside this box. |
76 | pub fn poll(&mut self, cx: &mut Context<'_>) -> Poll<T> { |
77 | self.get_pin().poll(cx) |
78 | } |
79 | } |
80 | |
81 | impl<T> Future for ReusableBoxFuture<'_, T> { |
82 | type Output = T; |
83 | |
84 | /// Poll the future stored inside this box. |
85 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> { |
86 | Pin::into_inner(self).get_pin().poll(cx) |
87 | } |
88 | } |
89 | |
90 | // The only method called on self.boxed is poll, which takes &mut self, so this |
91 | // struct being Sync does not permit any invalid access to the Future, even if |
92 | // the future is not Sync. |
93 | unsafe impl<T> Sync for ReusableBoxFuture<'_, T> {} |
94 | |
95 | impl<T> fmt::Debug for ReusableBoxFuture<'_, T> { |
96 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
97 | f.debug_struct("ReusableBoxFuture" ).finish() |
98 | } |
99 | } |
100 | |
101 | fn reuse_pin_box<T: ?Sized, U, O, F>(boxed: Pin<Box<T>>, new_value: U, callback: F) -> Result<O, U> |
102 | where |
103 | F: FnOnce(Box<U>) -> O, |
104 | { |
105 | let layout = Layout::for_value::<T>(&*boxed); |
106 | if layout != Layout::new::<U>() { |
107 | return Err(new_value); |
108 | } |
109 | |
110 | // SAFETY: We don't ever construct a non-pinned reference to the old `T` from now on, and we |
111 | // always drop the `T`. |
112 | let raw: *mut T = Box::into_raw(unsafe { Pin::into_inner_unchecked(boxed) }); |
113 | |
114 | // When dropping the old value panics, we still want to call `callback` — so move the rest of |
115 | // the code into a guard type. |
116 | let guard = CallOnDrop::new(|| { |
117 | let raw: *mut U = raw.cast::<U>(); |
118 | unsafe { raw.write(new_value) }; |
119 | |
120 | // SAFETY: |
121 | // - `T` and `U` have the same layout. |
122 | // - `raw` comes from a `Box` that uses the same allocator as this one. |
123 | // - `raw` points to a valid instance of `U` (we just wrote it in). |
124 | let boxed = unsafe { Box::from_raw(raw) }; |
125 | |
126 | callback(boxed) |
127 | }); |
128 | |
129 | // Drop the old value. |
130 | unsafe { ptr::drop_in_place(raw) }; |
131 | |
132 | // Run the rest of the code. |
133 | Ok(guard.call()) |
134 | } |
135 | |
136 | struct CallOnDrop<O, F: FnOnce() -> O> { |
137 | f: ManuallyDrop<F>, |
138 | } |
139 | |
140 | impl<O, F: FnOnce() -> O> CallOnDrop<O, F> { |
141 | fn new(f: F) -> Self { |
142 | let f = ManuallyDrop::new(f); |
143 | Self { f } |
144 | } |
145 | fn call(self) -> O { |
146 | let mut this = ManuallyDrop::new(self); |
147 | let f = unsafe { ManuallyDrop::take(&mut this.f) }; |
148 | f() |
149 | } |
150 | } |
151 | |
152 | impl<O, F: FnOnce() -> O> Drop for CallOnDrop<O, F> { |
153 | fn drop(&mut self) { |
154 | let f = unsafe { ManuallyDrop::take(&mut self.f) }; |
155 | f(); |
156 | } |
157 | } |
158 | |