wasmtime/runtime/component/concurrent/
abort.rs1use crate::sync::Mutex;
2use alloc::sync::Arc;
3use core::mem::{self, ManuallyDrop};
4use core::pin::Pin;
5use core::task::{Context, Poll, Waker};
6
7pub struct JoinHandle {
17 state: Arc<Mutex<JoinState>>,
23}
24
25enum JoinState {
26 Running {
29 waiting_for_abort_signal: Option<Waker>,
32
33 waiting_for_abort_to_complete: Option<Waker>,
36 },
37
38 AbortRequested {
41 waiting_for_abort_to_complete: Option<Waker>,
42 },
43
44 Complete,
47}
48
49impl JoinHandle {
50 pub fn abort(&self) {
60 let task = {
61 let mut state = self.state.lock();
62
63 match &mut *state {
64 JoinState::Running {
69 waiting_for_abort_signal,
70 waiting_for_abort_to_complete,
71 } => {
72 let task = waiting_for_abort_signal.take();
73 *state = JoinState::AbortRequested {
74 waiting_for_abort_to_complete: waiting_for_abort_to_complete.take(),
75 };
76 task
77 }
78
79 JoinState::AbortRequested { .. } | JoinState::Complete => None,
82 }
83 };
84
85 if let Some(task) = task {
86 task.wake();
87 }
88 }
89
90 pub(crate) fn run<F>(future: F) -> (JoinHandle, impl Future<Output = Option<F::Output>>)
94 where
95 F: Future,
96 {
97 let handle = JoinHandle {
98 state: Arc::new(Mutex::new(JoinState::Running {
99 waiting_for_abort_signal: None,
100 waiting_for_abort_to_complete: None,
101 })),
102 };
103 let future = JoinHandleFuture {
104 future: ManuallyDrop::new(future),
105 state: handle.state.clone(),
106 };
107 (handle, future)
108 }
109}
110
111impl Future for JoinHandle {
112 type Output = ();
113
114 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
115 let mut state = self.state.lock();
116 match &mut *state {
117 JoinState::Running {
120 waiting_for_abort_to_complete,
121 ..
122 }
123 | JoinState::AbortRequested {
124 waiting_for_abort_to_complete,
125 } => {
126 let prev = waiting_for_abort_to_complete.replace(cx.waker().clone());
130 drop(state);
131 drop(prev);
132 Poll::Pending
133 }
134
135 JoinState::Complete => Poll::Ready(()),
137 }
138 }
139}
140
141struct JoinHandleFuture<F> {
142 future: ManuallyDrop<F>,
143 state: Arc<Mutex<JoinState>>,
144}
145
146impl<F> Future for JoinHandleFuture<F>
147where
148 F: Future,
149{
150 type Output = Option<F::Output>;
151
152 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
153 let (state, future) = unsafe {
157 let me = self.get_unchecked_mut();
158 (&me.state, Pin::new_unchecked(&mut *me.future))
159 };
160
161 {
164 let mut state = state.lock();
165 match &mut *state {
166 JoinState::Running {
167 waiting_for_abort_signal,
168 ..
169 } => {
170 let prev = waiting_for_abort_signal.replace(cx.waker().clone());
171 drop(state);
172 drop(prev);
173 }
174 JoinState::AbortRequested { .. } | JoinState::Complete => {
175 return Poll::Ready(None);
176 }
177 }
178 }
179
180 future.poll(cx).map(Some)
181 }
182}
183
184impl<F> Drop for JoinHandleFuture<F> {
185 fn drop(&mut self) {
186 unsafe {
193 ManuallyDrop::drop(&mut self.future);
194 }
195
196 let prev = mem::replace(&mut *self.state.lock(), JoinState::Complete);
201 let task = match prev {
202 JoinState::Running {
203 waiting_for_abort_to_complete,
204 ..
205 }
206 | JoinState::AbortRequested {
207 waiting_for_abort_to_complete,
208 } => waiting_for_abort_to_complete,
209 JoinState::Complete => None,
210 };
211 if let Some(task) = task {
212 task.wake();
213 }
214 }
215}
216
217#[cfg(test)]
218mod tests {
219 use super::JoinHandle;
220 use std::pin::{Pin, pin};
221 use std::task::{Context, Poll, Waker};
222 use tokio::sync::oneshot;
223
224 fn is_ready<F>(future: Pin<&mut F>) -> bool
225 where
226 F: Future,
227 {
228 match future.poll(&mut Context::from_waker(Waker::noop())) {
229 Poll::Ready(_) => true,
230 Poll::Pending => false,
231 }
232 }
233
234 #[tokio::test]
235 async fn abort_in_progress() {
236 let (tx, rx) = oneshot::channel::<()>();
237 let (mut handle, future) = JoinHandle::run(rx);
238 let mut handle = Pin::new(&mut handle);
239 {
240 let mut future = pin!(future);
241 assert!(!is_ready(future.as_mut()));
242 assert!(!is_ready(handle.as_mut()));
243 handle.abort();
244 assert!(is_ready(future.as_mut()));
245 assert!(!is_ready(handle.as_mut()));
246 assert!(!tx.is_closed());
247 }
248 assert!(is_ready(handle.as_mut()));
249 assert!(tx.is_closed());
250 }
251
252 #[tokio::test]
253 async fn abort_complete() {
254 let (tx, rx) = oneshot::channel::<()>();
255 let (mut handle, future) = JoinHandle::run(rx);
256 let mut handle = Pin::new(&mut handle);
257 tx.send(()).unwrap();
258 assert!(!is_ready(handle.as_mut()));
259 {
260 let mut future = pin!(future);
261 assert!(is_ready(future.as_mut()));
262 assert!(!is_ready(handle.as_mut()));
263 }
264 assert!(is_ready(handle.as_mut()));
265 handle.abort();
266 assert!(is_ready(handle.as_mut()));
267 }
268
269 #[tokio::test]
270 async fn abort_dropped() {
271 let (tx, rx) = oneshot::channel::<()>();
272 let (mut handle, future) = JoinHandle::run(rx);
273 let mut handle = Pin::new(&mut handle);
274 drop(future);
275 assert!(is_ready(handle.as_mut()));
276 handle.abort();
277 assert!(is_ready(handle.as_mut()));
278 assert!(tx.is_closed());
279 }
280
281 #[tokio::test]
282 async fn await_completion() {
283 let (tx, rx) = oneshot::channel::<()>();
284 tx.send(()).unwrap();
285 let (handle, future) = JoinHandle::run(rx);
286 let task = tokio::task::spawn(future);
287 handle.await;
288 task.await.unwrap();
289 }
290
291 #[tokio::test]
292 async fn await_abort() {
293 let (tx, rx) = oneshot::channel::<()>();
294 tx.send(()).unwrap();
295 let (handle, future) = JoinHandle::run(rx);
296 handle.abort();
297 let task = tokio::task::spawn(future);
298 handle.await;
299 task.await.unwrap();
300 }
301}