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