nuclei/rt/task/mod.rs
1//! Tasks: a spawned future, the [`Runnable`] a run queue holds for it, and
2//! the [`Task`] handle that awaits its output.
3//!
4//! A task is one allocation (see `raw`) with one state word. Spawning
5//! returns its `Runnable`, already scheduled, and its handle. Running the
6//! runnable polls the future once; a waker that fires makes a new runnable
7//! and hands it to the task's [`Schedule`], which puts it in a run queue. A
8//! wake during the poll only marks the task, and the run hands it to the
9//! schedule once the poll is over, so a task is never polled twice at once
10//! and never re-polled from inside its own poll.
11//!
12//! The semantics are those of the established task crate the old executor
13//! was built on:
14//!
15//! - A poll that panics completes the task with the panic, and awaiting the
16//! handle resumes it; a detached task's panic is dropped (the panic hook
17//! has already reported it).
18//! - Dropping the handle cancels the task: its future is dropped by the next
19//! run, or right away by the runnable's drop. [`Task::detach`] lets it run
20//! to the end instead, and [`Task::cancel`] cancels and returns the output
21//! if the task had completed.
22//! - Dropping a runnable without running it cancels the task and drops its
23//! future there; awaiting the handle then panics, and a
24//! [`FallibleTask`] returns `None`.
25//!
26//! Two fixes the established crate has not released are part of this one:
27//! the poll's waker is the task's canonical waker (`will_wake` holds between
28//! it and its clones), and the state is read again after the poll, so the
29//! exchange after a poll that woke the task or cloned its waker does not
30//! start from a value it is sure to miss.
31
32mod header;
33mod join;
34#[cfg(all(test, loom))]
35mod loom_tests;
36pub(crate) mod names;
37mod raw;
38mod state;
39#[cfg(all(test, not(loom)))]
40mod tests;
41
42use core::future::Future;
43use core::mem::ManuallyDrop;
44use core::ptr::NonNull;
45use core::task::{RawWaker, Waker};
46
47use self::header::Header;
48pub use self::join::{AbortHandle, FallibleTask, Join, JoinError, Task};
49
50/// A task's id: unique among the tasks of the process, never reused.
51#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
52pub struct Id(pub(super) core::num::NonZeroU64);
53
54impl core::fmt::Display for Id {
55 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
56 self.0.fmt(f)
57 }
58}
59
60/// The id of the task being polled on this thread, `None` outside a task (in
61/// `block_on`'s own future, on a blocking-pool thread).
62pub(crate) fn current_id() -> Option<Id> {
63 crate::coop::current_task()
64}
65
66use self::raw::RawTask;
67
68/// How a task came to be scheduled.
69#[derive(Clone, Copy, Debug, PartialEq, Eq)]
70pub(crate) struct ScheduleInfo {
71 /// The task woke itself during its own poll (a yield, an IO operation
72 /// that completed at once): a run queue gives other tasks a turn first.
73 pub(crate) woken_while_running: bool,
74}
75
76impl ScheduleInfo {
77 pub(crate) const fn new(woken_while_running: bool) -> Self {
78 Self {
79 woken_while_running,
80 }
81 }
82}
83
84/// Where a woken task goes.
85///
86/// # Safety
87///
88/// `schedule` is called with `self` borrowed from the task's own memory.
89/// Once it has handed the runnable to a place another thread takes it from,
90/// that thread may run the task to its end and free it, `self` included: an
91/// implementation then must not touch `self` again unless it took a
92/// reference to the task before the handoff (a [`Runnable::waker`]).
93pub(crate) unsafe trait Schedule {
94 fn schedule(&self, runnable: Runnable, info: ScheduleInfo);
95
96 /// Drops the task's future (`drop` does), where and as the schedule
97 /// wants it dropped.
98 fn drop_future(&self, drop: impl FnOnce()) {
99 drop();
100 }
101
102 /// Just before each poll of the task's future.
103 fn before_poll(&self, _id: Id) {}
104
105 /// Just after each poll of the task's future.
106 fn after_poll(&self, _id: Id) {}
107
108 /// The task's future was dropped: it completed or was cancelled.
109 fn terminated(&self, _id: Id) {}
110
111 /// A poll of the task's future panicked, its outcome now.
112 fn panicked(&self, _id: Id) {}
113}
114
115/// A scheduled task: running it polls the task's future once.
116///
117/// Dropping a runnable without running it cancels the task and drops its
118/// future on the dropping thread.
119pub(crate) struct Runnable {
120 ptr: NonNull<()>,
121}
122
123// SAFETY: a runnable is a reference to a task whose future, for a `spawn`ed
124// task, is `Send`; for a task of `spawn_unchecked` the caller promised that
125// the runnable is run and dropped only where that is sound.
126unsafe impl Send for Runnable {}
127// SAFETY: `&Runnable` only reads the header or clones a waker.
128unsafe impl Sync for Runnable {}
129
130impl Runnable {
131 /// # Safety
132 ///
133 /// `ptr` is a task pointer whose reference the runnable takes over.
134 pub(crate) unsafe fn from_raw(ptr: NonNull<()>) -> Self {
135 Self { ptr }
136 }
137
138 /// The task pointer, keeping the reference: [`from_raw`](Self::from_raw)
139 /// makes the runnable again.
140 pub(crate) fn into_raw(self) -> NonNull<()> {
141 ManuallyDrop::new(self).ptr
142 }
143
144 fn header(&self) -> &Header {
145 // SAFETY: the runnable's reference keeps the task alive.
146 unsafe { self.ptr.cast::<Header>().as_ref() }
147 }
148
149 /// The task's inbox link, keeping the reference:
150 /// [`from_link`](Self::from_link) makes the runnable again.
151 pub(crate) fn into_link(self) -> NonNull<crate::rt::inbox::Link> {
152 let header = self.into_raw().cast::<Header>();
153 // SAFETY: a field of the live header the reference keeps.
154 unsafe { NonNull::new_unchecked(&raw mut (*header.as_ptr()).link) }
155 }
156
157 /// # Safety
158 ///
159 /// `link` came from [`into_link`](Self::into_link), whose reference the
160 /// runnable takes over.
161 pub(crate) unsafe fn from_link(link: NonNull<crate::rt::inbox::Link>) -> Self {
162 let offset = core::mem::offset_of!(Header, link);
163 Self {
164 // SAFETY: the link is the `link` field of a task's header.
165 ptr: unsafe { NonNull::new_unchecked(link.as_ptr().cast::<u8>().sub(offset).cast()) },
166 }
167 }
168
169 /// Polls the task's future once. Returns true when the task woke itself
170 /// during the poll and was handed to its schedule again.
171 pub(crate) fn run(self) -> bool {
172 let ptr = self.into_raw().as_ptr();
173 // SAFETY: the runnable's reference goes to the run.
174 unsafe { ((*ptr.cast::<Header>()).vtable.run)(ptr) }
175 }
176
177 /// Hands the runnable back to the task's schedule.
178 pub(crate) fn schedule(self) {
179 let ptr = self.into_raw().as_ptr();
180 // SAFETY: the runnable's reference goes to the new runnable.
181 unsafe { ((*ptr.cast::<Header>()).vtable.schedule)(ptr, ScheduleInfo::new(false)) }
182 }
183
184 /// A waker of the task, which holds its own reference.
185 pub(crate) fn waker(&self) -> Waker {
186 let ptr = self.ptr.as_ptr().cast_const();
187 // SAFETY: the borrowed waker is never dropped, so it counts no
188 // reference; its clone counts its own.
189 let borrowed = ManuallyDrop::new(unsafe {
190 Waker::from_raw(RawWaker::new(ptr, &self.header().vtable.waker))
191 });
192 (*borrowed).clone()
193 }
194}
195
196impl Drop for Runnable {
197 fn drop(&mut self) {
198 use self::state::{AWAITER, CLOSED, COMPLETED, SCHEDULED};
199 let ptr = self.ptr.as_ptr().cast_const();
200 let header = self.header();
201 let mut state = header.state.load(crate::sync::internal::Ordering::Acquire);
202 loop {
203 if state & (COMPLETED | CLOSED) != 0 {
204 break;
205 }
206 match header.state.compare_exchange_weak(
207 state,
208 state | CLOSED,
209 crate::sync::internal::Ordering::AcqRel,
210 crate::sync::internal::Ordering::Acquire,
211 ) {
212 Ok(_) => break,
213 Err(actual) => state = actual,
214 }
215 }
216 // SAFETY: a runnable exists only for a task that has not completed,
217 // so its future is there, and the runnable owns it now.
218 unsafe { (header.vtable.drop_future)(ptr) };
219 let state = header
220 .state
221 .fetch_and(!SCHEDULED, crate::sync::internal::Ordering::AcqRel);
222 if state & AWAITER != 0 {
223 header.notify(None);
224 }
225 // SAFETY: the runnable's reference.
226 unsafe { (header.vtable.drop_ref)(ptr) };
227 }
228}
229
230impl core::fmt::Debug for Runnable {
231 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
232 f.debug_struct("Runnable").field("task", &self.ptr).finish()
233 }
234}
235
236/// Spawns `future` as a task that `schedule` places when it is woken.
237/// Returns the task's runnable, scheduled once and not yet handed to
238/// `schedule`, and its handle.
239#[inline(always)]
240pub(crate) fn spawn<F, S>(future: F, schedule: S) -> (Runnable, Task<F::Output>)
241where
242 F: Future + Send + 'static,
243 F::Output: Send + 'static,
244 S: Schedule + Send + Sync + 'static,
245{
246 // SAFETY: everything the task holds may cross threads and lives as long
247 // as it needs to.
248 unsafe { spawn_unchecked(future, schedule) }
249}
250
251/// [`spawn`] without the `Send` and `'static` bounds.
252///
253/// # Safety
254///
255/// The future is polled and dropped only on a thread where that is sound
256/// (its owner's, for a future that is not `Send`), everything it borrows
257/// outlives the task, and the schedule may be called from any thread that
258/// holds a waker.
259#[inline(always)]
260pub(crate) unsafe fn spawn_unchecked<F, S>(future: F, schedule: S) -> (Runnable, Task<F::Output>)
261where
262 F: Future,
263 S: Schedule,
264{
265 let ptr = RawTask::<F, S>::allocate(future, schedule);
266 // SAFETY: a new task holds the runnable's reference and the handle's flag.
267 unsafe { (Runnable::from_raw(ptr), Task::from_raw(ptr)) }
268}
269
270/// Runs `f`, aborting the process if it panics: what a destructor or a waker
271/// that panics inside a task transition gets, as unwinding there would leave
272/// the state word half done.
273#[inline]
274pub(crate) fn abort_on_panic<R>(f: impl FnOnce() -> R) -> R {
275 struct Abort;
276 impl Drop for Abort {
277 fn drop(&mut self) {
278 std::process::abort();
279 }
280 }
281 let abort = Abort;
282 let out = f();
283 core::mem::forget(abort);
284 out
285}