1use core::fmt;
4use core::future::Future;
5use core::marker::PhantomData;
6use core::mem::{ManuallyDrop, MaybeUninit};
7use core::pin::Pin;
8use core::ptr::NonNull;
9use core::task::{Context, Poll};
10use std::panic::{RefUnwindSafe, UnwindSafe};
11
12use super::header::Header;
13use super::raw::Panic;
14use super::state::{AWAITER, CLOSED, COMPLETED, FLAGS, REFERENCE, RUNNING, SCHEDULED, TASK};
15use crate::sync::internal::Ordering;
16
17#[must_use = "dropping a task cancels it; call `detach` to let it run"]
32pub struct Task<T> {
33 ptr: NonNull<()>,
34 _output: PhantomData<T>,
35}
36
37unsafe impl<T: Send> Send for Task<T> {}
40unsafe impl<T> Sync for Task<T> {}
42
43impl<T> UnwindSafe for Task<T> {}
44impl<T> RefUnwindSafe for Task<T> {}
45
46impl<T> Unpin for Task<T> {}
47
48impl<T> Task<T> {
49 pub(super) unsafe fn from_raw(ptr: NonNull<()>) -> Self {
54 Self {
55 ptr,
56 _output: PhantomData,
57 }
58 }
59
60 fn header(&self) -> &Header {
61 unsafe { self.ptr.cast::<Header>().as_ref() }
63 }
64
65 #[inline(always)]
67 pub fn detach(self) {
68 let mut this = ManuallyDrop::new(self);
69 if this
72 .header()
73 .state
74 .compare_exchange_weak(
75 SCHEDULED | TASK | REFERENCE,
76 SCHEDULED | REFERENCE,
77 Ordering::AcqRel,
78 Ordering::Acquire,
79 )
80 .is_ok()
81 {
82 return;
83 }
84 this.detach_late();
85 }
86
87 #[inline(never)]
90 fn detach_late(&mut self) {
91 drop(self.set_detached());
92 }
93
94 pub async fn cancel(self) -> Option<T> {
97 let mut this = self;
98 this.set_canceled();
99 core::future::poll_fn(|cx| this.poll_task(cx)).await
100 }
101
102 pub fn fallible(self) -> FallibleTask<T> {
105 FallibleTask { task: self }
106 }
107
108 pub fn is_finished(&self) -> bool {
111 self.header().state.load(Ordering::Acquire) & (COMPLETED | CLOSED) != 0
112 }
113
114 fn set_canceled(&mut self) {
116 unsafe { cancel(self.ptr.as_ptr().cast_const()) }
118 }
119
120 pub fn id(&self) -> super::Id {
122 self.header().id
123 }
124
125 pub fn abort(&self) {
130 unsafe { cancel(self.ptr.as_ptr().cast_const()) }
132 }
133
134 pub fn abort_handle(&self) -> AbortHandle {
137 let ptr = self.ptr.as_ptr().cast_const();
138 let header = self.header();
139 let waker = core::mem::ManuallyDrop::new(unsafe {
144 core::task::Waker::from_raw(core::task::RawWaker::new(ptr, &header.vtable.waker))
145 });
146 AbortHandle {
147 waker: (*waker).clone(),
148 id: header.id,
149 }
150 }
151
152 pub fn join(self) -> Join<T> {
156 Join { task: self }
157 }
158}
159
160unsafe fn cancel(ptr: *const ()) {
167 let header = unsafe { &*ptr.cast::<Header>() };
169 {
170 let mut state = header.state.load(Ordering::Acquire);
171 loop {
172 if state & (COMPLETED | CLOSED) != 0 {
173 return;
174 }
175 let idle = state & (SCHEDULED | RUNNING) == 0;
176 let new = if idle {
177 (state | SCHEDULED | CLOSED) + REFERENCE
178 } else {
179 state | CLOSED
180 };
181 match header.state.compare_exchange_weak(
182 state,
183 new,
184 Ordering::AcqRel,
185 Ordering::Acquire,
186 ) {
187 Ok(_) => {
188 if idle {
189 unsafe { (header.vtable.schedule)(ptr, super::ScheduleInfo::new(false)) };
192 }
193 if state & AWAITER != 0 {
194 header.notify(None);
195 }
196 return;
197 }
198 Err(actual) => state = actual,
199 }
200 }
201 }
202}
203
204impl<T> Task<T> {
205 fn set_detached(&mut self) -> Option<Result<T, Panic>> {
208 let ptr = self.ptr.as_ptr().cast_const();
209 let header = self.header();
210 let mut output = None;
211 let Err(mut state) = header.state.compare_exchange_weak(
214 SCHEDULED | TASK | REFERENCE,
215 SCHEDULED | REFERENCE,
216 Ordering::AcqRel,
217 Ordering::Acquire,
218 ) else {
219 return None;
220 };
221 loop {
222 if state & COMPLETED != 0 && state & CLOSED == 0 {
223 match header.state.compare_exchange_weak(
225 state,
226 state | CLOSED,
227 Ordering::AcqRel,
228 Ordering::Acquire,
229 ) {
230 Ok(_) => {
231 output = Some(self.read_output());
232 state |= CLOSED;
233 }
234 Err(actual) => state = actual,
235 }
236 continue;
237 }
238 let last = state & !FLAGS == 0;
241 let new = if last && state & CLOSED == 0 {
242 SCHEDULED | CLOSED | REFERENCE
243 } else {
244 state & !TASK
245 };
246 match header.state.compare_exchange_weak(
247 state,
248 new,
249 Ordering::AcqRel,
250 Ordering::Acquire,
251 ) {
252 Ok(_) => {
253 if last {
254 unsafe {
258 if state & CLOSED == 0 {
259 (header.vtable.schedule)(ptr, super::ScheduleInfo::new(false));
260 } else {
261 (header.vtable.destroy)(ptr);
262 }
263 }
264 }
265 return output;
266 }
267 Err(actual) => state = actual,
268 }
269 }
270 }
271
272 fn read_output(&self) -> Result<T, Panic> {
274 let ptr = self.ptr.as_ptr().cast_const();
275 let mut out = MaybeUninit::<Result<T, Panic>>::uninit();
276 unsafe {
279 (self.header().vtable.read_output)(ptr, out.as_mut_ptr().cast());
280 out.assume_init()
281 }
282 }
283
284 #[inline(always)]
287 fn poll_task(&mut self, cx: &mut Context<'_>) -> Poll<Option<T>> {
288 match self.poll_raw(cx) {
289 Poll::Ready(Some(Ok(output))) => Poll::Ready(Some(output)),
290 Poll::Ready(Some(Err(panic))) => std::panic::resume_unwind(panic),
291 Poll::Ready(None) => Poll::Ready(None),
292 Poll::Pending => Poll::Pending,
293 }
294 }
295
296 #[inline(always)]
300 fn poll_raw(&mut self, cx: &mut Context<'_>) -> Poll<Option<Result<T, Panic>>> {
301 let header = self.header();
302 let mut state = header.state.load(Ordering::Acquire);
303 loop {
304 if state & CLOSED != 0 {
305 if state & (SCHEDULED | RUNNING) != 0 {
308 header.register(cx.waker());
309 state = header.state.load(Ordering::Acquire);
310 if state & (SCHEDULED | RUNNING) != 0 {
311 return Poll::Pending;
312 }
313 }
314 header.notify(Some(cx.waker()));
315 return Poll::Ready(None);
316 }
317 if state & COMPLETED == 0 {
318 header.register(cx.waker());
319 state = header.state.load(Ordering::Acquire);
322 if state & CLOSED != 0 {
323 continue;
324 }
325 if state & COMPLETED == 0 {
326 return Poll::Pending;
327 }
328 }
329 match header.state.compare_exchange(
330 state,
331 state | CLOSED,
332 Ordering::AcqRel,
333 Ordering::Acquire,
334 ) {
335 Ok(_) => {
336 if state & AWAITER != 0 {
337 header.notify(Some(cx.waker()));
338 }
339 return Poll::Ready(Some(self.read_output()));
340 }
341 Err(actual) => state = actual,
342 }
343 }
344 }
345}
346
347impl<T> Drop for Task<T> {
348 #[inline]
349 fn drop(&mut self) {
350 if self.header().state.load(Ordering::Acquire) == COMPLETED | CLOSED | TASK {
354 unsafe { (self.header().vtable.destroy)(self.ptr.as_ptr().cast_const()) };
356 return;
357 }
358 self.cancel_and_let_go();
359 }
360}
361
362impl<T> Task<T> {
363 #[inline(never)]
366 fn cancel_and_let_go(&mut self) {
367 self.set_canceled();
368 drop(self.set_detached());
369 }
370}
371
372impl<T> Future for Task<T> {
373 type Output = T;
374
375 #[expect(
376 clippy::panic,
377 reason = "documented under `# Panics` of `Task`; `Task::fallible` answers `None` instead"
378 )]
379 #[inline(always)]
380 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> {
381 match self.poll_task(cx) {
382 Poll::Ready(Some(output)) => Poll::Ready(output),
383 Poll::Ready(None) => panic!(
384 "the task has no output: it was cancelled or its runtime shut down before it \
385 completed, or it was awaited again after completing"
386 ),
387 Poll::Pending => Poll::Pending,
388 }
389 }
390}
391
392impl<T> fmt::Debug for Task<T> {
393 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
394 let state = self.header().state.load(Ordering::Acquire);
395 f.debug_struct("Task")
396 .field("scheduled", &(state & SCHEDULED != 0))
397 .field("running", &(state & RUNNING != 0))
398 .field("completed", &(state & COMPLETED != 0))
399 .field("closed", &(state & CLOSED != 0))
400 .finish()
401 }
402}
403
404#[must_use = "dropping a task cancels it; call `detach` to let it run"]
409pub struct FallibleTask<T> {
410 task: Task<T>,
411}
412
413impl<T> FallibleTask<T> {
414 pub fn detach(self) {
416 self.task.detach()
417 }
418
419 pub async fn cancel(self) -> Option<T> {
421 self.task.cancel().await
422 }
423
424 pub fn is_finished(&self) -> bool {
426 self.task.is_finished()
427 }
428
429 pub fn id(&self) -> super::Id {
431 self.task.id()
432 }
433}
434
435impl<T> Future for FallibleTask<T> {
436 type Output = Option<T>;
437
438 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<T>> {
439 self.task.poll_task(cx)
440 }
441}
442
443impl<T> fmt::Debug for FallibleTask<T> {
444 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
445 f.debug_tuple("FallibleTask").field(&self.task).finish()
446 }
447}
448
449#[must_use = "dropping it cancels the task, as dropping the `Task` does"]
451pub struct Join<T> {
452 task: Task<T>,
453}
454
455impl<T> Join<T> {
456 pub fn detach(self) {
458 self.task.detach()
459 }
460
461 pub fn id(&self) -> super::Id {
463 self.task.id()
464 }
465}
466
467impl<T> Future for Join<T> {
468 type Output = Result<T, JoinError>;
469
470 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
471 let id = self.task.id();
472 self.task.poll_raw(cx).map(|output| match output {
473 Some(Ok(output)) => Ok(output),
474 Some(Err(panic)) => Err(JoinError {
475 id,
476 panic: Some(panic),
477 }),
478 None => Err(JoinError { id, panic: None }),
479 })
480 }
481}
482
483impl<T> fmt::Debug for Join<T> {
484 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
485 f.debug_tuple("Join").field(&self.task).finish()
486 }
487}
488
489pub struct JoinError {
492 id: super::Id,
493 panic: Option<Panic>,
494}
495
496impl JoinError {
497 pub fn is_cancelled(&self) -> bool {
499 self.panic.is_none()
500 }
501
502 pub fn is_panic(&self) -> bool {
504 self.panic.is_some()
505 }
506
507 #[expect(clippy::panic, reason = "documented under `# Panics`")]
514 pub fn into_panic(self) -> Box<dyn core::any::Any + Send + 'static> {
515 match self.try_into_panic() {
516 Ok(panic) => panic,
517 Err(error) => panic!("`JoinError::into_panic` on a cancelled task ({error})"),
518 }
519 }
520
521 pub fn try_into_panic(self) -> Result<Box<dyn core::any::Any + Send + 'static>, JoinError> {
524 match self.panic {
525 Some(panic) => Ok(panic),
526 None => Err(self),
527 }
528 }
529
530 pub fn id(&self) -> super::Id {
532 self.id
533 }
534}
535
536impl fmt::Display for JoinError {
537 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
538 match &self.panic {
539 None => write!(f, "task {} was cancelled", self.id),
540 Some(panic) => match panic
541 .downcast_ref::<&str>()
542 .copied()
543 .or_else(|| panic.downcast_ref::<String>().map(String::as_str))
544 {
545 Some(message) => write!(f, "task {} panicked: {message}", self.id),
546 None => write!(f, "task {} panicked", self.id),
547 },
548 }
549 }
550}
551
552impl fmt::Debug for JoinError {
553 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
554 f.debug_struct("JoinError")
555 .field("id", &self.id)
556 .field("cancelled", &self.is_cancelled())
557 .finish()
558 }
559}
560
561impl std::error::Error for JoinError {}
562
563impl From<JoinError> for std::io::Error {
564 fn from(error: JoinError) -> Self {
565 std::io::Error::other(error.to_string())
566 }
567}
568
569#[derive(Clone)]
573pub struct AbortHandle {
574 waker: core::task::Waker,
576 id: super::Id,
577}
578
579impl AbortHandle {
580 pub fn abort(&self) {
582 unsafe { cancel(self.waker.data()) }
584 }
585
586 pub fn is_finished(&self) -> bool {
588 let header = unsafe { &*self.waker.data().cast::<Header>() };
590 header.state.load(Ordering::Acquire) & (COMPLETED | CLOSED) != 0
591 }
592
593 pub fn id(&self) -> super::Id {
595 self.id
596 }
597}
598
599impl fmt::Debug for AbortHandle {
600 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
601 f.debug_struct("AbortHandle").field("id", &self.id).finish()
602 }
603}