Skip to main content

tokio/runtime/task/
harness.rs

1use crate::future::Future;
2use crate::runtime::task::core::{Cell, Core, Header, Trailer};
3use crate::runtime::task::state::{Snapshot, State};
4use crate::runtime::task::waker::waker_ref;
5use crate::runtime::task::{Id, JoinError, Notified, RawTask, Schedule, Task};
6
7#[cfg(tokio_unstable)]
8use crate::runtime::TaskMeta;
9use std::any::Any;
10use std::mem;
11use std::mem::ManuallyDrop;
12use std::panic;
13use std::ptr::NonNull;
14use std::task::{Context, Poll, Waker};
15
16/// Typed raw task handle.
17pub(super) struct Harness<T: Future, S: 'static> {
18    cell: NonNull<Cell<T, S>>,
19}
20
21impl<T, S> Harness<T, S>
22where
23    T: Future,
24    S: 'static,
25{
26    pub(super) unsafe fn from_raw(ptr: NonNull<Header>) -> Harness<T, S> {
27        Harness {
28            cell: ptr.cast::<Cell<T, S>>(),
29        }
30    }
31
32    fn header_ptr(&self) -> NonNull<Header> {
33        self.cell.cast()
34    }
35
36    fn header(&self) -> &Header {
37        unsafe { &*self.header_ptr().as_ptr() }
38    }
39
40    fn state(&self) -> &State {
41        &self.header().state
42    }
43
44    fn trailer(&self) -> &Trailer {
45        unsafe { &self.cell.as_ref().trailer }
46    }
47
48    fn core(&self) -> &Core<T, S> {
49        unsafe { &self.cell.as_ref().core }
50    }
51}
52
53/// Task operations that can be implemented without being generic over the
54/// scheduler or task. Only one version of these methods should exist in the
55/// final binary.
56impl RawTask {
57    pub(super) fn drop_reference(self) {
58        if self.state().ref_dec() {
59            self.dealloc();
60        }
61    }
62
63    /// This call consumes a ref-count and notifies the task. This will create a
64    /// new Notified and submit it if necessary.
65    ///
66    /// The caller does not need to hold a ref-count besides the one that was
67    /// passed to this call.
68    pub(super) fn wake_by_val(&self) {
69        use super::state::TransitionToNotifiedByVal;
70
71        match self.state().transition_to_notified_by_val() {
72            TransitionToNotifiedByVal::Submit => {
73                // The caller has given us a ref-count, and the transition has
74                // created a new ref-count, so we now hold two. We turn the new
75                // ref-count Notified and pass it to the call to `schedule`.
76                //
77                // The old ref-count is retained for now to ensure that the task
78                // is not dropped during the call to `schedule` if the call
79                // drops the task it was given.
80                self.schedule();
81
82                // Now that we have completed the call to schedule, we can
83                // release our ref-count.
84                self.drop_reference();
85            }
86            TransitionToNotifiedByVal::Dealloc => {
87                self.dealloc();
88            }
89            TransitionToNotifiedByVal::DoNothing => {}
90        }
91    }
92
93    /// This call notifies the task. It will not consume any ref-counts, but the
94    /// caller should hold a ref-count.  This will create a new Notified and
95    /// submit it if necessary.
96    pub(super) fn wake_by_ref(&self) {
97        use super::state::TransitionToNotifiedByRef;
98
99        match self.state().transition_to_notified_by_ref() {
100            TransitionToNotifiedByRef::Submit => {
101                // The transition above incremented the ref-count for a new task
102                // and the caller also holds a ref-count. The caller's ref-count
103                // ensures that the task is not destroyed even if the new task
104                // is dropped before `schedule` returns.
105                self.schedule();
106            }
107            TransitionToNotifiedByRef::DoNothing => {}
108        }
109    }
110
111    /// Remotely aborts the task.
112    ///
113    /// The caller should hold a ref-count, but we do not consume it.
114    ///
115    /// This is similar to `shutdown` except that it asks the runtime to perform
116    /// the shutdown. This is necessary to avoid the shutdown happening in the
117    /// wrong thread for non-Send tasks.
118    pub(super) fn remote_abort(&self) {
119        if self.state().transition_to_notified_and_cancel() {
120            // The transition has created a new ref-count, which we turn into
121            // a Notified and pass to the task.
122            //
123            // Since the caller holds a ref-count, the task cannot be destroyed
124            // before the call to `schedule` returns even if the call drops the
125            // `Notified` internally.
126            self.schedule();
127        }
128    }
129
130    /// Try to set the waker notified when the task is complete. Returns true if
131    /// the task has already completed. If this call returns false, then the
132    /// waker will not be notified.
133    pub(super) fn try_set_join_waker(&self, waker: &Waker) -> bool {
134        can_read_output(self.header(), self.trailer(), waker)
135    }
136}
137
138impl<T, S> Harness<T, S>
139where
140    T: Future,
141    S: Schedule,
142{
143    pub(super) fn drop_reference(self) {
144        if self.state().ref_dec() {
145            self.dealloc();
146        }
147    }
148
149    /// Polls the inner future. A ref-count is consumed.
150    ///
151    /// All necessary state checks and transitions are performed.
152    /// Panics raised while polling the future are handled.
153    pub(super) fn poll(self) {
154        // We pass our ref-count to `poll_inner`.
155        match self.poll_inner() {
156            PollFuture::Notified => {
157                // The `poll_inner` call has given us two ref-counts back.
158                // We give one of them to a new task and call `yield_now`.
159                self.core()
160                    .scheduler
161                    .yield_now(Notified(self.get_new_task()));
162
163                // The remaining ref-count is now dropped. We kept the extra
164                // ref-count until now to ensure that even if the `yield_now`
165                // call drops the provided task, the task isn't deallocated
166                // before after `yield_now` returns.
167                self.drop_reference();
168            }
169            PollFuture::Complete => {
170                self.complete();
171            }
172            PollFuture::Dealloc => {
173                self.dealloc();
174            }
175            PollFuture::Done => (),
176        }
177    }
178
179    /// Polls the task and cancel it if necessary. This takes ownership of a
180    /// ref-count.
181    ///
182    /// If the return value is Notified, the caller is given ownership of two
183    /// ref-counts.
184    ///
185    /// If the return value is Complete, the caller is given ownership of a
186    /// single ref-count, which should be passed on to `complete`.
187    ///
188    /// If the return value is `Dealloc`, then this call consumed the last
189    /// ref-count and the caller should call `dealloc`.
190    ///
191    /// Otherwise the ref-count is consumed and the caller should not access
192    /// `self` again.
193    fn poll_inner(&self) -> PollFuture {
194        use super::state::{TransitionToIdle, TransitionToRunning};
195
196        match self.state().transition_to_running() {
197            TransitionToRunning::Success => {
198                // Separated to reduce LLVM codegen
199                fn transition_result_to_poll_future(result: TransitionToIdle) -> PollFuture {
200                    match result {
201                        TransitionToIdle::Ok => PollFuture::Done,
202                        TransitionToIdle::OkNotified => PollFuture::Notified,
203                        TransitionToIdle::OkDealloc => PollFuture::Dealloc,
204                        TransitionToIdle::Cancelled => PollFuture::Complete,
205                    }
206                }
207                let header_ptr = self.header_ptr();
208                let waker_ref = waker_ref::<S>(&header_ptr);
209                let cx = Context::from_waker(&waker_ref);
210                let res = poll_future(self.core(), cx);
211
212                if res == Poll::Ready(()) {
213                    // The future completed. Move on to complete the task.
214                    return PollFuture::Complete;
215                }
216
217                let transition_res = self.state().transition_to_idle();
218                if let TransitionToIdle::Cancelled = transition_res {
219                    // The transition to idle failed because the task was
220                    // cancelled during the poll.
221                    cancel_task(self.core());
222                }
223                transition_result_to_poll_future(transition_res)
224            }
225            TransitionToRunning::Cancelled => {
226                cancel_task(self.core());
227                PollFuture::Complete
228            }
229            TransitionToRunning::Failed => PollFuture::Done,
230            TransitionToRunning::Dealloc => PollFuture::Dealloc,
231        }
232    }
233
234    /// Forcibly shuts down the task.
235    ///
236    /// Attempt to transition to `Running` in order to forcibly shutdown the
237    /// task. If the task is currently running or in a state of completion, then
238    /// there is nothing further to do. When the task completes running, it will
239    /// notice the `CANCELLED` bit and finalize the task.
240    pub(super) fn shutdown(self) {
241        if !self.state().transition_to_shutdown() {
242            // The task is concurrently running. No further work needed.
243            self.drop_reference();
244            return;
245        }
246
247        // By transitioning the lifecycle to `Running`, we have permission to
248        // drop the future.
249        cancel_task(self.core());
250        self.complete();
251    }
252
253    pub(super) fn dealloc(self) {
254        // Observe that we expect to have mutable access to these objects
255        // because we are going to drop them. This only matters when running
256        // under loom.
257        self.trailer().waker.with_mut(|_| ());
258        self.core().stage.with_mut(|_| ());
259
260        // Safety: The caller of this method just transitioned our ref-count to
261        // zero, so it is our responsibility to release the allocation.
262        //
263        // We don't hold any references into the allocation at this point, but
264        // it is possible for another thread to still hold a `&State` into the
265        // allocation if that other thread has decremented its last ref-count,
266        // but has not yet returned from the relevant method on `State`.
267        //
268        // However, the `State` type consists of just an `AtomicUsize`, and an
269        // `AtomicUsize` wraps the entirety of its contents in an `UnsafeCell`.
270        // As explained in the documentation for `UnsafeCell`, such references
271        // are allowed to be dangling after their last use, even if the
272        // reference has not yet gone out of scope.
273        unsafe {
274            drop(Box::from_raw(self.cell.as_ptr()));
275        }
276    }
277
278    // ===== join handle =====
279
280    /// Read the task output into `dst`.
281    pub(super) fn try_read_output(self, dst: &mut Poll<super::Result<T::Output>>, waker: &Waker) {
282        if can_read_output(self.header(), self.trailer(), waker) {
283            *dst = Poll::Ready(self.core().take_output());
284        }
285    }
286
287    pub(super) fn drop_join_handle_slow(self) {
288        // Try to unset `JOIN_INTEREST` and `JOIN_WAKER`. This must be done as a first step in
289        // case the task concurrently completed.
290        let transition = self.state().transition_to_join_handle_dropped();
291
292        if transition.drop_output {
293            // It is our responsibility to drop the output. This is critical as
294            // the task output may not be `Send` and as such must remain with
295            // the scheduler or `JoinHandle`. i.e. if the output remains in the
296            // task structure until the task is deallocated, it may be dropped
297            // by a Waker on any arbitrary thread.
298            //
299            // Panics are delivered to the user via the `JoinHandle`. Given that
300            // they are dropping the `JoinHandle`, we assume they are not
301            // interested in the panic and swallow it.
302            let _ = panic::catch_unwind(panic::AssertUnwindSafe(|| {
303                self.core().drop_future_or_output();
304            }));
305        }
306
307        if transition.drop_waker {
308            // If the JOIN_WAKER flag is unset at this point, the task is either
309            // already terminal or not complete so the `JoinHandle` is responsible
310            // for dropping the waker.
311            // Safety:
312            // If the JOIN_WAKER bit is not set the join handle has exclusive
313            // access to the waker as per rule 2 in task/mod.rs.
314            // This can only be the case at this point in two scenarios:
315            // 1. The task completed and the runtime unset `JOIN_WAKER` flag
316            //    after accessing the waker during task completion. So the
317            //    `JoinHandle` is the only one to access the  join waker here.
318            // 2. The task is not completed so the `JoinHandle` was able to unset
319            //    `JOIN_WAKER` bit itself to get mutable access to the waker.
320            //    The runtime will not access the waker when this flag is unset.
321            unsafe { self.trailer().set_waker(None) };
322        }
323
324        // Drop the `JoinHandle` reference, possibly deallocating the task
325        self.drop_reference();
326    }
327
328    // ====== internal ======
329
330    /// Completes the task. This method assumes that the state is RUNNING.
331    fn complete(self) {
332        // The future has completed and its output has been written to the task
333        // stage. We transition from running to complete.
334        let snapshot = self.state().transition_to_complete();
335
336        // We catch panics here in case dropping the future or waking the
337        // JoinHandle panics.
338        let _ = panic::catch_unwind(panic::AssertUnwindSafe(|| {
339            if !snapshot.is_join_interested() {
340                // The `JoinHandle` is not interested in the output of
341                // this task. It is our responsibility to drop the
342                // output. The join waker was already dropped by the
343                // `JoinHandle` before.
344                self.core().drop_future_or_output();
345            } else if snapshot.is_join_waker_set() {
346                // Notify the waker. Reading the waker field is safe per rule 4
347                // in task/mod.rs, since the JOIN_WAKER bit is set and the call
348                // to transition_to_complete() above set the COMPLETE bit.
349                self.trailer().wake_join();
350
351                // Inform the `JoinHandle` that we are done waking the waker by
352                // unsetting the `JOIN_WAKER` bit. If the `JoinHandle` has
353                // already been dropped and `JOIN_INTEREST` is unset, then we must
354                // drop the waker ourselves.
355                if !self
356                    .state()
357                    .unset_waker_after_complete()
358                    .is_join_interested()
359                {
360                    // SAFETY: We have COMPLETE=1 and JOIN_INTEREST=0, so
361                    // we have exclusive access to the waker.
362                    unsafe { self.trailer().set_waker(None) };
363                }
364            }
365        }));
366
367        // We catch panics here in case invoking a hook panics.
368        //
369        // We call this in a separate block so that it runs after the task appears to have
370        // completed and will still run if the destructor panics.
371        #[cfg(tokio_unstable)]
372        if let Some(f) = self.trailer().hooks.task_terminate_callback.as_ref() {
373            let _ = panic::catch_unwind(panic::AssertUnwindSafe(|| {
374                f(&TaskMeta {
375                    id: self.core().task_id,
376                    spawned_at: self.core().spawned_at.into(),
377                    #[cfg(all(tokio_unstable, feature = "schedule-latency"))]
378                    schedule_latency: None,
379                    _phantom: Default::default(),
380                })
381            }));
382        }
383
384        // The task has completed execution and will no longer be scheduled.
385        let num_release = self.release();
386
387        if self.state().transition_to_terminal(num_release) {
388            self.dealloc();
389        }
390    }
391
392    /// Releases the task from the scheduler. Returns the number of ref-counts
393    /// that should be decremented.
394    fn release(&self) -> usize {
395        // We don't actually increment the ref-count here, but the new task is
396        // never destroyed, so that's ok.
397        let me = ManuallyDrop::new(self.get_new_task());
398
399        if let Some(task) = self.core().scheduler.release(&me) {
400            mem::forget(task);
401            2
402        } else {
403            1
404        }
405    }
406
407    /// Creates a new task that holds its own ref-count.
408    ///
409    /// # Safety
410    ///
411    /// Any use of `self` after this call must ensure that a ref-count to the
412    /// task holds the task alive until after the use of `self`. Passing the
413    /// returned Task to any method on `self` is unsound if dropping the Task
414    /// could drop `self` before the call on `self` returned.
415    fn get_new_task(&self) -> Task<S> {
416        // safety: The header is at the beginning of the cell, so this cast is
417        // safe.
418        unsafe { Task::from_raw(self.cell.cast()) }
419    }
420}
421
422fn can_read_output(header: &Header, trailer: &Trailer, waker: &Waker) -> bool {
423    // Load a snapshot of the current task state
424    let snapshot = header.state.load();
425
426    debug_assert!(snapshot.is_join_interested());
427
428    if !snapshot.is_complete() {
429        // If the task is not complete, try storing the provided waker in the
430        // task's waker field.
431
432        let res = if snapshot.is_join_waker_set() {
433            // If JOIN_WAKER is set, then JoinHandle has previously stored a
434            // waker in the waker field per step (iii) of rule 5 in task/mod.rs.
435
436            // Optimization: if the stored waker and the provided waker wake the
437            // same task, then return without touching the waker field. (Reading
438            // the waker field below is safe per rule 3 in task/mod.rs.)
439            if unsafe { trailer.will_wake(waker) } {
440                return false;
441            }
442
443            // Otherwise swap the stored waker with the provided waker by
444            // following the rule 5 in task/mod.rs.
445            header
446                .state
447                .unset_waker()
448                .and_then(|snapshot| set_join_waker(header, trailer, waker.clone(), snapshot))
449        } else {
450            // If JOIN_WAKER is unset, then JoinHandle has mutable access to the
451            // waker field per rule 2 in task/mod.rs; therefore, skip step (i)
452            // of rule 5 and try to store the provided waker in the waker field.
453            set_join_waker(header, trailer, waker.clone(), snapshot)
454        };
455
456        match res {
457            Ok(_) => return false,
458            Err(snapshot) => {
459                assert!(snapshot.is_complete());
460            }
461        }
462    }
463    true
464}
465
466fn set_join_waker(
467    header: &Header,
468    trailer: &Trailer,
469    waker: Waker,
470    snapshot: Snapshot,
471) -> Result<Snapshot, Snapshot> {
472    assert!(snapshot.is_join_interested());
473    assert!(!snapshot.is_join_waker_set());
474
475    // Safety: Only the `JoinHandle` may set the `waker` field. When
476    // `JOIN_INTEREST` is **not** set, nothing else will touch the field.
477    unsafe {
478        trailer.set_waker(Some(waker));
479    }
480
481    // Update the `JoinWaker` state accordingly
482    let res = header.state.set_join_waker();
483
484    // If the state could not be updated, then clear the join waker
485    if res.is_err() {
486        unsafe {
487            trailer.set_waker(None);
488        }
489    }
490
491    res
492}
493
494enum PollFuture {
495    Complete,
496    Notified,
497    Done,
498    Dealloc,
499}
500
501/// Cancels the task and store the appropriate error in the stage field.
502fn cancel_task<T: Future, S: Schedule>(core: &Core<T, S>) {
503    // Drop the future from a panic guard.
504    let res = panic::catch_unwind(panic::AssertUnwindSafe(|| {
505        core.drop_future_or_output();
506    }));
507
508    core.store_output(Err(panic_result_to_join_error(core.task_id, res)));
509}
510
511fn panic_result_to_join_error(
512    task_id: Id,
513    res: Result<(), Box<dyn Any + Send + 'static>>,
514) -> JoinError {
515    match res {
516        Ok(()) => JoinError::cancelled(task_id),
517        Err(panic) => JoinError::panic(task_id, panic),
518    }
519}
520
521/// Polls the future. If the future completes, the output is written to the
522/// stage field.
523fn poll_future<T: Future, S: Schedule>(core: &Core<T, S>, cx: Context<'_>) -> Poll<()> {
524    // Poll the future.
525    let output = panic::catch_unwind(panic::AssertUnwindSafe(|| {
526        struct Guard<'a, T: Future, S: Schedule> {
527            core: &'a Core<T, S>,
528        }
529        impl<'a, T: Future, S: Schedule> Drop for Guard<'a, T, S> {
530            fn drop(&mut self) {
531                // If the future panics on poll, we drop it inside the panic
532                // guard.
533                self.core.drop_future_or_output();
534            }
535        }
536        let guard = Guard { core };
537        let res = guard.core.poll(cx);
538        mem::forget(guard);
539        res
540    }));
541
542    // Prepare output for being placed in the core stage.
543    let output = match output {
544        Ok(Poll::Pending) => return Poll::Pending,
545        Ok(Poll::Ready(output)) => Ok(output),
546        Err(panic) => Err(panic_to_error(&core.scheduler, core.task_id, panic)),
547    };
548
549    // Catch and ignore panics if the future panics on drop.
550    let res = panic::catch_unwind(panic::AssertUnwindSafe(|| {
551        core.store_output(output);
552    }));
553
554    if res.is_err() {
555        core.scheduler.unhandled_panic();
556    }
557
558    Poll::Ready(())
559}
560
561#[cold]
562fn panic_to_error<S: Schedule>(
563    scheduler: &S,
564    task_id: Id,
565    panic: Box<dyn Any + Send + 'static>,
566) -> JoinError {
567    scheduler.unhandled_panic();
568    JoinError::panic(task_id, panic)
569}