async_task/task.rs
1use core::fmt;
2use core::future::Future;
3use core::marker::PhantomData;
4use core::mem::ManuallyDrop;
5use core::pin::Pin;
6use core::ptr::NonNull;
7use core::sync::atomic::Ordering;
8use core::task::{Context, Poll};
9
10use crate::header::{Header, HeaderWithMetadata};
11use crate::raw::{OpenSchedule, Panic};
12use crate::state::*;
13use crate::ScheduleInfo;
14
15/// A spawned task.
16///
17/// A [`Task`] can be awaited to retrieve the output of its future.
18///
19/// Dropping a [`Task`] cancels it, which means its future won't be polled again. To drop the
20/// [`Task`] handle without canceling it, use [`detach()`][`Task::detach()`] instead. To cancel a
21/// task gracefully and wait until it is fully destroyed, use the [`cancel()`][Task::cancel()]
22/// method.
23///
24/// Note that canceling a task actually wakes it and reschedules one last time. Then, the executor
25/// can destroy the task by simply dropping its [`Runnable`][`super::Runnable`] or by invoking
26/// [`run()`][`super::Runnable::run()`].
27///
28/// # Examples
29///
30/// ```
31/// use smol::{future, Executor};
32/// use std::thread;
33///
34/// let ex = Executor::new();
35///
36/// // Spawn a future onto the executor.
37/// let task = ex.spawn(async {
38/// println!("Hello from a task!");
39/// 1 + 2
40/// });
41///
42/// // Run an executor thread.
43/// thread::spawn(move || future::block_on(ex.run(future::pending::<()>())));
44///
45/// // Wait for the task's output.
46/// assert_eq!(future::block_on(task), 3);
47/// ```
48#[must_use = "tasks get canceled when dropped, use `.detach()` to run them in the background"]
49pub struct Task<T, M = ()> {
50 /// A raw task pointer.
51 pub(crate) ptr: NonNull<()>,
52
53 /// A marker capturing generic types `T` and `M`.
54 pub(crate) _marker: PhantomData<(T, M)>,
55}
56
57unsafe impl<T: Send, M: Send + Sync> Send for Task<T, M> {}
58unsafe impl<T, M: Send + Sync> Sync for Task<T, M> {}
59
60impl<T, M> Unpin for Task<T, M> {}
61
62#[cfg(feature = "std")]
63impl<T, M> std::panic::UnwindSafe for Task<T, M> {}
64#[cfg(feature = "std")]
65impl<T, M> std::panic::RefUnwindSafe for Task<T, M> {}
66
67impl<T, M> Task<T, M> {
68 /// Detaches the task to let it keep running in the background.
69 ///
70 /// # Examples
71 ///
72 /// ```
73 /// use smol::{Executor, Timer};
74 /// use std::time::Duration;
75 ///
76 /// let ex = Executor::new();
77 ///
78 /// // Spawn a daemon future.
79 /// ex.spawn(async {
80 /// loop {
81 /// println!("I'm a daemon task looping forever.");
82 /// Timer::after(Duration::from_secs(1)).await;
83 /// }
84 /// })
85 /// .detach();
86 /// ```
87 pub fn detach(self) {
88 let mut handle = DetachHandle {
89 task: ManuallyDrop::new(self),
90 owns_task: true,
91 };
92 let _out = set_detached::<T>(handle.task.ptr.as_ptr(), &mut handle.owns_task);
93 }
94
95 /// Cancels the task and waits for it to stop running.
96 ///
97 /// Returns the task's output if it was completed just before it got canceled, or [`None`] if
98 /// it didn't complete.
99 ///
100 /// While it's possible to simply drop the [`Task`] to cancel it, this is a cleaner way of
101 /// canceling because it also waits for the task to stop running.
102 ///
103 /// # Examples
104 ///
105 /// ```
106 /// # if cfg!(miri) { return; } // Miri does not support epoll
107 /// use smol::{future, Executor, Timer};
108 /// use std::thread;
109 /// use std::time::Duration;
110 ///
111 /// let ex = Executor::new();
112 ///
113 /// // Spawn a daemon future.
114 /// let task = ex.spawn(async {
115 /// loop {
116 /// println!("Even though I'm in an infinite loop, you can still cancel me!");
117 /// Timer::after(Duration::from_secs(1)).await;
118 /// }
119 /// });
120 ///
121 /// // Run an executor thread.
122 /// thread::spawn(move || future::block_on(ex.run(future::pending::<()>())));
123 ///
124 /// future::block_on(async {
125 /// Timer::after(Duration::from_secs(3)).await;
126 /// task.cancel().await;
127 /// });
128 /// ```
129 pub async fn cancel(self) -> Option<T> {
130 let this = self;
131 set_canceled(this.ptr.as_ptr());
132 this.fallible().await
133 }
134
135 /// Converts this task into a [`FallibleTask`].
136 ///
137 /// Like [`Task`], a fallible task will poll the task's output until it is
138 /// completed or cancelled due to its [`Runnable`][`super::Runnable`] being
139 /// dropped without being run. Resolves to the task's output when completed,
140 /// or [`None`] if it didn't complete.
141 ///
142 /// # Examples
143 ///
144 /// ```
145 /// use smol::{future, Executor};
146 /// use std::thread;
147 ///
148 /// let ex = Executor::new();
149 ///
150 /// // Spawn a future onto the executor.
151 /// let task = ex.spawn(async {
152 /// println!("Hello from a task!");
153 /// 1 + 2
154 /// })
155 /// .fallible();
156 ///
157 /// // Run an executor thread.
158 /// thread::spawn(move || future::block_on(ex.run(future::pending::<()>())));
159 ///
160 /// // Wait for the task's output.
161 /// assert_eq!(future::block_on(task), Some(3));
162 /// ```
163 ///
164 /// ```
165 /// use smol::future;
166 ///
167 /// // Schedule function which drops the runnable without running it.
168 /// let schedule = move |runnable| drop(runnable);
169 ///
170 /// // Create a task with the future and the schedule function.
171 /// let (runnable, task) = async_task::spawn(async {
172 /// println!("Hello from a task!");
173 /// 1 + 2
174 /// }, schedule);
175 /// runnable.schedule();
176 ///
177 /// // Wait for the task's output.
178 /// assert_eq!(future::block_on(task.fallible()), None);
179 /// ```
180 pub fn fallible(self) -> FallibleTask<T, M> {
181 FallibleTask { task: self }
182 }
183
184 fn header_with_metadata(&self) -> &HeaderWithMetadata<M> {
185 let ptr = self.ptr.as_ptr();
186 let header = ptr as *const HeaderWithMetadata<M>;
187 unsafe { &*header }
188 }
189
190 /// Returns `true` if the current task is finished.
191 ///
192 /// Note that in a multithreaded environment, this task can change finish immediately after calling this function.
193 pub fn is_finished(&self) -> bool {
194 let ptr = self.ptr.as_ptr();
195 let header = ptr as *const Header;
196
197 unsafe {
198 let state = (*header).state.load(Ordering::Acquire);
199 state & (CLOSED | COMPLETED) != 0
200 }
201 }
202
203 /// Get the metadata associated with this task.
204 ///
205 /// Tasks can be created with a metadata object associated with them; by default, this
206 /// is a `()` value. See the [`Builder::metadata()`] method for more information.
207 pub fn metadata(&self) -> &M {
208 let ptr = self.ptr.as_ptr();
209 let header = ptr as *const HeaderWithMetadata<M>;
210 &unsafe { &*header }.metadata
211 }
212}
213
214struct DetachHandle<T, M> {
215 task: ManuallyDrop<Task<T, M>>,
216 owns_task: bool,
217}
218
219impl<T, M> Drop for DetachHandle<T, M> {
220 fn drop(&mut self) {
221 if self.owns_task {
222 unsafe { ManuallyDrop::drop(&mut self.task) }
223 }
224 }
225}
226
227/// Puts the task in detached state.
228#[inline(never)]
229fn set_detached<T>(ptr: *const (), owns_task: &mut bool) -> Option<Result<T, Panic>> {
230 let header = ptr as *const Header;
231
232 unsafe {
233 // A place where the output will be stored in case it needs to be dropped.
234 let mut output = None;
235
236 // Optimistically assume the `Task` is being detached just after creating the task.
237 // This is a common case so if the `Task` is detached, the overhead of it is only one
238 // compare-exchange operation.
239 if let Err(mut state) = (*header).state.compare_exchange_weak(
240 SCHEDULED | TASK | REFERENCE,
241 SCHEDULED | REFERENCE,
242 Ordering::AcqRel,
243 Ordering::Acquire,
244 ) {
245 loop {
246 // If the task has been completed but not yet closed, that means its output
247 // must be dropped.
248 if state & COMPLETED != 0 && state & CLOSED == 0 {
249 // Mark the task as closed in order to grab its output.
250 match (*header).state.compare_exchange_weak(
251 state,
252 state | CLOSED,
253 Ordering::AcqRel,
254 Ordering::Acquire,
255 ) {
256 Ok(_) => {
257 // Read the output.
258 output = Some(
259 ((*header).vtable.get_output(ptr) as *mut Result<T, Panic>).read(),
260 );
261
262 // Update the state variable because we're continuing the loop.
263 state |= CLOSED;
264 }
265 Err(s) => state = s,
266 }
267 } else {
268 // If this is the last reference to the task and it's not closed, then
269 // close it and schedule one more time so that its future gets dropped by
270 // the executor.
271 let new = if state & (!(REFERENCE - 1) | CLOSED) == 0 {
272 SCHEDULED | CLOSED | REFERENCE
273 } else {
274 state & !TASK
275 };
276
277 // A last reference that is still open has to be scheduled so the
278 // executor drops the future. Count that publication before the swap.
279 let schedules = state & (!(REFERENCE - 1) | CLOSED) == 0;
280 if schedules {
281 ((*header).vtable.begin_schedule)(ptr);
282 }
283
284 match (*header).state.compare_exchange_weak(
285 state,
286 new,
287 Ordering::AcqRel,
288 Ordering::Acquire,
289 ) {
290 Ok(_) => {
291 *owns_task = false;
292 // If this is the last reference to the task, we need to either
293 // schedule dropping its future or destroy it.
294 if schedules {
295 let _held = OpenSchedule::after_begin(ptr);
296 ((*header).vtable.before_enqueue)(ptr);
297 ((*header).vtable.enqueue)(ptr, ScheduleInfo::new(false));
298 } else if state & !(REFERENCE - 1) == 0 {
299 ((*header).vtable.destroy)(ptr);
300 }
301
302 break;
303 }
304 Err(s) => {
305 if schedules {
306 ((*header).vtable.end_schedule)(ptr);
307 }
308 state = s;
309 }
310 }
311 }
312 }
313 } else {
314 *owns_task = false;
315 }
316
317 output
318 }
319}
320
321/// Puts the task in canceled state.
322#[inline(never)]
323fn set_canceled(ptr: *const ()) {
324 let header = ptr as *const Header;
325
326 unsafe {
327 let mut state = (*header).state.load(Ordering::Acquire);
328
329 loop {
330 // If the task has been completed or closed, it can't be canceled.
331 if state & (COMPLETED | CLOSED) != 0 {
332 break;
333 }
334
335 // If the task is not scheduled nor running, we'll need to schedule it.
336 let schedules = state & (SCHEDULED | RUNNING) == 0;
337 let new = if schedules {
338 (state | SCHEDULED | CLOSED) + REFERENCE
339 } else {
340 state | CLOSED
341 };
342
343 // Count the publication before the swap that commits to enqueueing.
344 if schedules {
345 ((*header).vtable.begin_schedule)(ptr);
346 }
347
348 // Mark the task as closed.
349 match (*header).state.compare_exchange_weak(
350 state,
351 new,
352 Ordering::AcqRel,
353 Ordering::Acquire,
354 ) {
355 Ok(_) => {
356 // If the task is not scheduled nor running, schedule it one more time so
357 // that its future gets dropped by the executor.
358 if schedules {
359 let _held = OpenSchedule::after_begin(ptr);
360 ((*header).vtable.before_enqueue)(ptr);
361 ((*header).vtable.enqueue)(ptr, ScheduleInfo::new(false));
362 }
363
364 // Notify the awaiter that the task has been closed.
365 if state & AWAITER != 0 {
366 (*header).notify(None);
367 }
368
369 break;
370 }
371 Err(s) => {
372 if schedules {
373 ((*header).vtable.end_schedule)(ptr);
374 }
375 state = s;
376 }
377 }
378 }
379 }
380}
381
382/// Polls the task to retrieve its output.
383///
384/// Returns `Some` if the task has completed or `None` if it was closed.
385///
386/// A task becomes closed in the following cases:
387///
388/// 1. It gets canceled by `Runnable::drop()`, `Task::drop()`, or `Task::cancel()`.
389/// 2. Its output gets awaited by the `Task`.
390/// 3. It panics while polling the future.
391/// 4. It is completed and the `Task` gets dropped.
392fn poll_task<T>(ptr: *const (), cx: &mut Context<'_>) -> Poll<Option<T>> {
393 let header = ptr as *const Header;
394
395 unsafe {
396 let mut state = (*header).state.load(Ordering::Acquire);
397
398 loop {
399 // If the task has been closed, notify the awaiter and return `None`.
400 if state & CLOSED != 0 {
401 // If the task is scheduled or running, we need to wait until its future is
402 // dropped.
403 if state & (SCHEDULED | RUNNING) != 0 {
404 // Replace the waker with one associated with the current task.
405 (*header).register(cx.waker());
406
407 // Reload the state after registering. It is possible changes occurred just
408 // before registration so we need to check for that.
409 state = (*header).state.load(Ordering::Acquire);
410
411 // If the task is still scheduled or running, we need to wait because its
412 // future is not dropped yet.
413 if state & (SCHEDULED | RUNNING) != 0 {
414 return Poll::Pending;
415 }
416 }
417
418 // Even though the awaiter is most likely the current task, it could also be
419 // another task.
420 (*header).notify(Some(cx.waker()));
421 return Poll::Ready(None);
422 }
423
424 // If the task is not completed, register the current task.
425 if state & COMPLETED == 0 {
426 // Replace the waker with one associated with the current task.
427 (*header).register(cx.waker());
428
429 // Reload the state after registering. It is possible that the task became
430 // completed or closed just before registration so we need to check for that.
431 state = (*header).state.load(Ordering::Acquire);
432
433 // If the task has been closed, restart.
434 if state & CLOSED != 0 {
435 continue;
436 }
437
438 // If the task is still not completed, we're blocked on it.
439 if state & COMPLETED == 0 {
440 return Poll::Pending;
441 }
442 }
443
444 // Since the task is now completed, mark it as closed in order to grab its output.
445 match (*header).state.compare_exchange(
446 state,
447 state | CLOSED,
448 Ordering::AcqRel,
449 Ordering::Acquire,
450 ) {
451 Ok(_) => {
452 // Notify the awaiter. Even though the awaiter is most likely the current
453 // task, it could also be another task.
454 if state & AWAITER != 0 {
455 (*header).notify(Some(cx.waker()));
456 }
457
458 // Take the output from the task.
459 let output = (*header).vtable.get_output(ptr) as *mut Result<T, Panic>;
460 let output = output.read();
461
462 // Propagate the panic if the task panicked.
463 let output = match output {
464 Ok(output) => output,
465 Err(panic) => {
466 #[cfg(feature = "std")]
467 std::panic::resume_unwind(panic);
468
469 #[cfg(not(feature = "std"))]
470 match panic {}
471 }
472 };
473
474 return Poll::Ready(Some(output));
475 }
476 Err(s) => state = s,
477 }
478 }
479 }
480}
481
482impl<T, M> Drop for Task<T, M> {
483 fn drop(&mut self) {
484 let ptr = self.ptr.as_ptr();
485 set_canceled(ptr);
486 set_detached::<T>(ptr, &mut false);
487 }
488}
489
490impl<T, M> Future for Task<T, M> {
491 type Output = T;
492
493 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
494 match poll_task::<T>(self.ptr.as_ptr(), cx) {
495 Poll::Ready(t) => Poll::Ready(t.expect("Task polled after completion")),
496 Poll::Pending => Poll::Pending,
497 }
498 }
499}
500
501impl<T, M: fmt::Debug> fmt::Debug for Task<T, M> {
502 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
503 f.debug_struct("Task")
504 .field("header", self.header_with_metadata())
505 .finish()
506 }
507}
508
509/// A spawned task with a fallible response.
510///
511/// This type behaves like [`Task`], however it produces an `Option<T>` when
512/// polled and will return `None` if the executor dropped its
513/// [`Runnable`][`super::Runnable`] without being run.
514///
515/// This can be useful to avoid the panic produced when polling the `Task`
516/// future if the executor dropped its `Runnable`.
517#[must_use = "tasks get canceled when dropped, use `.detach()` to run them in the background"]
518pub struct FallibleTask<T, M = ()> {
519 task: Task<T, M>,
520}
521
522impl<T, M> FallibleTask<T, M> {
523 /// Detaches the task to let it keep running in the background.
524 ///
525 /// # Examples
526 ///
527 /// ```
528 /// use smol::{Executor, Timer};
529 /// use std::time::Duration;
530 ///
531 /// let ex = Executor::new();
532 ///
533 /// // Spawn a daemon future.
534 /// ex.spawn(async {
535 /// loop {
536 /// println!("I'm a daemon task looping forever.");
537 /// Timer::after(Duration::from_secs(1)).await;
538 /// }
539 /// })
540 /// .fallible()
541 /// .detach();
542 /// ```
543 pub fn detach(self) {
544 self.task.detach()
545 }
546
547 /// Cancels the task and waits for it to stop running.
548 ///
549 /// Returns the task's output if it was completed just before it got canceled, or [`None`] if
550 /// it didn't complete.
551 ///
552 /// While it's possible to simply drop the [`Task`] to cancel it, this is a cleaner way of
553 /// canceling because it also waits for the task to stop running.
554 ///
555 /// # Examples
556 ///
557 /// ```
558 /// # if cfg!(miri) { return; } // Miri does not support epoll
559 /// use smol::{future, Executor, Timer};
560 /// use std::thread;
561 /// use std::time::Duration;
562 ///
563 /// let ex = Executor::new();
564 ///
565 /// // Spawn a daemon future.
566 /// let task = ex.spawn(async {
567 /// loop {
568 /// println!("Even though I'm in an infinite loop, you can still cancel me!");
569 /// Timer::after(Duration::from_secs(1)).await;
570 /// }
571 /// })
572 /// .fallible();
573 ///
574 /// // Run an executor thread.
575 /// thread::spawn(move || future::block_on(ex.run(future::pending::<()>())));
576 ///
577 /// future::block_on(async {
578 /// Timer::after(Duration::from_secs(3)).await;
579 /// task.cancel().await;
580 /// });
581 /// ```
582 pub async fn cancel(self) -> Option<T> {
583 self.task.cancel().await
584 }
585
586 /// Returns `true` if the current task is finished.
587 ///
588 /// Note that in a multithreaded environment, this task can change finish immediately after calling this function.
589 pub fn is_finished(&self) -> bool {
590 self.task.is_finished()
591 }
592}
593
594impl<T, M> Future for FallibleTask<T, M> {
595 type Output = Option<T>;
596
597 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
598 poll_task::<T>(self.task.ptr.as_ptr(), cx)
599 }
600}
601
602impl<T, M: fmt::Debug> fmt::Debug for FallibleTask<T, M> {
603 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
604 f.debug_struct("FallibleTask")
605 .field("header", self.task.header_with_metadata())
606 .finish()
607 }
608}