1use shuttle_engine::runtime::execution::ExecutionState;
9use shuttle_engine::runtime::task::TaskId;
10use shuttle_engine::runtime::thread;
11use std::error::Error;
12use std::fmt::{Display, Formatter};
13use std::future::Future;
14use std::panic::Location;
15use std::pin::Pin;
16use std::result::Result;
17use std::sync::atomic::{AtomicBool, Ordering};
18use std::sync::Arc;
19use std::task::{Context, Poll, Waker};
20
21pub use shuttle_engine::future::batch_semaphore;
22
23fn spawn_inner<F>(fut: F, caller: &'static Location<'static>) -> JoinHandle<F::Output>
24where
25 F: Future + 'static,
26 F::Output: 'static,
27{
28 let stack_size = ExecutionState::with(|s| s.config.stack_size);
29 let inner = Arc::new(std::sync::Mutex::new(JoinHandleInner::default()));
30 let aborted = Arc::new(AtomicBool::new(false));
31 let task_id = ExecutionState::spawn_future(
32 Wrapper::new(fut, inner.clone(), aborted.clone()),
33 stack_size,
34 None,
35 caller,
36 );
37
38 JoinHandle {
39 task_id,
40 inner,
41 aborted,
42 }
43}
44
45#[track_caller]
47pub fn spawn<F>(fut: F) -> JoinHandle<F::Output>
48where
49 F: Future + Send + 'static,
50 F::Output: Send + 'static,
51{
52 spawn_inner(fut, Location::caller())
53}
54
55#[track_caller]
58pub fn spawn_local<F>(fut: F) -> JoinHandle<F::Output>
59where
60 F: Future + 'static,
61 F::Output: 'static,
62{
63 spawn_inner(fut, Location::caller())
64}
65
66#[derive(Debug, Clone)]
68pub struct AbortHandle {
69 task_id: TaskId,
70 aborted: Arc<AtomicBool>,
71}
72
73impl AbortHandle {
74 pub fn abort(&self) {
79 thread::switch();
83
84 if self.aborted.swap(true, Ordering::Relaxed) {
87 return;
88 }
89 let res = ExecutionState::try_with(|state| {
91 if !state.is_finished() {
92 state.get_mut(self.task_id).abort();
93 }
94 });
95 if let Err(e) = res {
96 tracing::error!("`AbortHandle::abort` failed with error: {e:?}");
97 }
98 }
99
100 pub fn is_finished(&self) -> bool {
105 ExecutionState::with(|state| {
106 let task = state.get(self.task_id);
107 task.finished()
108 })
109 }
110}
111
112unsafe impl Send for AbortHandle {}
113unsafe impl Sync for AbortHandle {}
114
115#[derive(Debug)]
117pub struct JoinHandle<T> {
118 task_id: TaskId,
119 inner: Arc<std::sync::Mutex<JoinHandleInner<T>>>,
120 aborted: Arc<AtomicBool>,
121}
122
123#[derive(Debug)]
124struct JoinHandleInner<T> {
125 result: Option<Result<T, JoinError>>,
126 waker: Option<Waker>,
127}
128
129impl<T> Default for JoinHandleInner<T> {
130 fn default() -> Self {
131 JoinHandleInner {
132 result: None,
133 waker: None,
134 }
135 }
136}
137
138impl<T> JoinHandle<T> {
139 pub fn abort(&self) {
145 thread::switch();
149
150 if self.aborted.swap(true, Ordering::Relaxed) {
153 return;
154 }
155 let res = ExecutionState::try_with(|state| {
157 if !state.is_finished() {
158 state.get_mut(self.task_id).abort();
159 }
160 });
161 if let Err(e) = res {
162 tracing::error!("`JoinHandle::abort` failed with error: {e:?}");
163 }
164 }
165
166 pub fn is_finished(&self) -> bool {
171 ExecutionState::with(|state| {
172 let task = state.get(self.task_id);
173 task.finished()
174 })
175 }
176
177 pub fn abort_handle(&self) -> AbortHandle {
179 AbortHandle {
180 task_id: self.task_id,
181 aborted: self.aborted.clone(),
182 }
183 }
184}
185
186#[derive(Debug)]
189pub enum JoinError {
190 Cancelled,
192}
193
194impl Display for JoinError {
195 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
196 match self {
197 JoinError::Cancelled => write!(f, "task was cancelled"),
198 }
199 }
200}
201
202impl Error for JoinError {}
203
204impl<T> Drop for JoinHandle<T> {
205 fn drop(&mut self) {
206 let res = ExecutionState::try_with(|state| {
209 if !state.is_finished() {
210 state.get_mut(self.task_id).detach();
211 }
212 });
213 if let Err(e) = res {
214 tracing::error!("`JoinHandle::drop` failed with error: {e:?}");
215 }
216 }
217}
218
219impl<T> Future for JoinHandle<T> {
220 type Output = Result<T, JoinError>;
221
222 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
223 let mut lock = self.inner.lock().unwrap();
224 if let Some(result) = lock.result.take() {
225 Poll::Ready(result)
226 } else {
227 lock.waker = Some(cx.waker().clone());
228 Poll::Pending
229 }
230 }
231}
232
233struct Wrapper<F: Future> {
243 future: Option<Pin<Box<F>>>,
246 inner: Arc<std::sync::Mutex<JoinHandleInner<F::Output>>>,
247 aborted: Arc<AtomicBool>,
248}
249
250impl<F> Wrapper<F>
251where
252 F: Future + 'static,
253 F::Output: 'static,
254{
255 fn new(future: F, inner: Arc<std::sync::Mutex<JoinHandleInner<F::Output>>>, aborted: Arc<AtomicBool>) -> Self {
256 Self {
257 future: Some(Box::pin(future)),
258 inner,
259 aborted,
260 }
261 }
262}
263
264impl<F> Wrapper<F>
265where
266 F: Future + 'static,
267 F::Output: 'static,
268{
269 fn finish(&self, result: Result<F::Output, JoinError>) {
272 while let Some(local) = ExecutionState::with(|state| state.current_mut().pop_local()) {
275 drop(local);
276 }
277
278 let mut lock = self.inner.lock().unwrap();
279 lock.result = Some(result);
280 if let Some(waker) = lock.waker.take() {
281 waker.wake();
282 }
283 }
284}
285
286impl<F> Future for Wrapper<F>
287where
288 F: Future + 'static,
289 F::Output: 'static,
290{
291 type Output = ();
292
293 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
294 let this = self.get_mut();
295
296 if this.aborted.load(Ordering::Relaxed) {
298 if ExecutionState::try_with(|state| state.is_finished()).unwrap_or(true) {
302 return Poll::Ready(());
303 }
304
305 this.future.take();
307 this.finish(Err(JoinError::Cancelled));
308 return Poll::Ready(());
309 }
310
311 match this.future.as_mut().unwrap().as_mut().poll(cx) {
312 Poll::Ready(result) => {
313 if ExecutionState::try_with(|state| state.is_finished()).unwrap_or(true) {
317 return Poll::Ready(());
318 }
319
320 this.finish(Ok(result));
321 Poll::Ready(())
322 }
323 Poll::Pending => Poll::Pending,
324 }
325 }
326}
327
328pub fn block_on<F: Future>(future: F) -> F::Output {
330 let mut future = Box::pin(future);
331 let waker = ExecutionState::with(|state| state.current_mut().waker());
332 let cx = &mut Context::from_waker(&waker);
333
334 loop {
341 match future.as_mut().poll(cx) {
342 Poll::Ready(result) => break result,
343 Poll::Pending => {
344 ExecutionState::with(|state| state.current_mut().sleep_unless_woken());
345 thread::switch();
346 }
347 }
348 }
349}
350
351pub async fn yield_now() {
355 struct YieldNow {
357 yielded: bool,
358 }
359
360 impl Future for YieldNow {
361 type Output = ();
362
363 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
364 if self.yielded {
365 return Poll::Ready(());
366 }
367
368 self.yielded = true;
369 cx.waker().wake_by_ref();
370 ExecutionState::request_yield();
371 Poll::Pending
372 }
373 }
374
375 YieldNow { yielded: false }.await
376}