maitake/task/
builder.rs

1use crate::scheduler::LocalStaticScheduler;
2
3use super::{Future, JoinHandle, Schedule, Storage, TaskRef};
4use core::panic::Location;
5
6/// Builds a new [`Task`] prior to spawning it.
7///
8/// [`Task`]: crate::task::Task
9#[derive(Debug, Clone)]
10pub struct Builder<'a, S> {
11    scheduler: S,
12    settings: Settings<'a>,
13}
14
15/// Configures settings for new tasks.
16#[derive(Debug, Clone)]
17// These fields are currently only read when tracing is enabled.
18#[cfg_attr(
19    not(any(feature = "tracing-01", feature = "tracing-02", test)),
20    allow(dead_code)
21)]
22pub(crate) struct Settings<'a> {
23    pub(super) name: Option<&'a str>,
24    pub(super) kind: &'static str,
25    pub(super) location: Option<Location<'a>>,
26}
27
28impl<'a, S: Schedule + 'static> Builder<'a, S> {
29    pub(crate) const fn new(scheduler: S) -> Self {
30        Self {
31            scheduler,
32            settings: Settings::new(),
33        }
34    }
35
36    /// Adds a name to the tasks produced by this builder.
37    ///
38    /// This will set the `task.name` `tracing` field of spans generated for
39    /// this task, if the "tracing-01" or "tracing-02" feature flags are
40    /// enabled.
41    ///
42    /// By default, tasks are unnamed.
43    pub fn name(self, name: &'a str) -> Self {
44        Self {
45            settings: Settings {
46                name: Some(name),
47                ..self.settings
48            },
49            ..self
50        }
51    }
52
53    /// Adds a static string which describes the type of the configured task.
54    ///
55    /// Generally, this is set by the runtime, rather than by user code &mdash;
56    /// `kind`s should describe general categories of task, such as "local" or
57    /// "blocking", rather than identifying specific tasks in an application. The
58    /// [`name`] field should be used instead for naming specific tasks within
59    /// an application.
60    ///
61    /// This will set the `task.kind` `tracing` field of spans generated for
62    /// this task, if the "tracing-01" or "tracing-02" feature flags are
63    /// enabled.
64    ///
65    /// By default, tasks will have the kind "task".
66    ///
67    /// [`name`]: Self::name
68    pub fn kind(self, kind: &'static str) -> Self {
69        Self {
70            settings: Settings {
71                kind,
72                ..self.settings
73            },
74            ..self
75        }
76    }
77
78    /// Overrides the task's source code location.
79    ///
80    /// By default, tasks will be recorded as having the location from which
81    /// they are spawned. This may be overriden by the runtime if needed.
82    pub fn location(self, location: Location<'a>) -> Self {
83        Self {
84            settings: Settings {
85                location: Some(location),
86                ..self.settings
87            },
88            ..self
89        }
90    }
91
92    /// Spawns a new task in a custom allocation, with this builder's configured settings.
93    ///
94    /// Note that the `StoredTask` *must* be bound to the same scheduler
95    /// instance as this task's scheduler!
96    ///
97    /// This method returns a [`JoinHandle`] that can be used to await the
98    /// task's output. Dropping the [`JoinHandle`] _detaches_ the spawned task,
99    /// allowing it to run in the background without awaiting its output.
100    #[inline]
101    #[track_caller]
102    pub fn spawn_allocated<STO, F>(&self, task: STO::StoredTask) -> JoinHandle<F::Output>
103    where
104        F: Future + Send + 'static,
105        F::Output: Send + 'static,
106        STO: Storage<S, F>,
107    {
108        let (task, join) = TaskRef::build_allocated::<S, F, STO>(&self.settings, task);
109        self.scheduler.schedule(task);
110        join
111    }
112}
113
114impl Builder<'_, &'static LocalStaticScheduler> {
115    /// Spawns a new `!`[`Send`] task in a custom allocation, with this
116    /// builder's configured settings.
117    ///
118    /// This method is capable of spawning futures which do not implement
119    /// `Send`. Therefore, it is only available when this [`Builder`] was
120    /// returned by a [`LocalStaticScheduler`]
121    ///
122    /// Note that the `StoredTask` *must* be bound to the same scheduler
123    /// instance as this task's scheduler!
124    ///
125    /// This method returns a [`JoinHandle`] that can be used to await the
126    /// task's output. Dropping the [`JoinHandle`] _detaches_ the spawned task,
127    /// allowing it to run in the background without awaiting its output.
128    #[inline]
129    #[track_caller]
130    pub fn spawn_local_allocated<STO, F>(&self, task: STO::StoredTask) -> JoinHandle<F::Output>
131    where
132        F: Future + 'static,
133        F::Output: 'static,
134        STO: Storage<&'static LocalStaticScheduler, F>,
135    {
136        let (task, join) =
137            TaskRef::build_allocated::<&'static LocalStaticScheduler, F, STO>(&self.settings, task);
138        self.scheduler.schedule(task);
139        join
140    }
141}
142
143feature! {
144    #![feature = "alloc"]
145    use alloc::boxed::Box;
146    use super::{BoxStorage, Task};
147    use crate::scheduler::LocalScheduler;
148
149    impl<S: Schedule + 'static> Builder<'_, S> {
150        /// Spawns a new task with this builder's configured settings.
151        ///
152        /// This method returns a [`JoinHandle`] that can be used to await the
153        /// task's output. Dropping the [`JoinHandle`] _detaches_ the spawned task,
154        /// allowing it to run in the background without awaiting its output.
155        #[inline]
156        #[track_caller]
157        pub fn spawn<F>(&self, future: F) -> JoinHandle<F::Output>
158        where
159            F: Future + Send + 'static,
160            F::Output: Send + 'static,
161        {
162            let mut task = Box::new(Task::<S, _, BoxStorage>::new(future));
163            task.bind(self.scheduler.clone());
164            let (task, join) = TaskRef::build_allocated::<S, _, BoxStorage>(&self.settings, task);
165            self.scheduler.schedule(task);
166            join
167        }
168    }
169
170    impl Builder<'_, &'static LocalStaticScheduler> {
171        /// Spawns a new `!`[`Send`] task with this builder's configured settings.
172        ///
173        /// This method is capable of spawning futures which do not implement
174        /// [`Send`]. Therefore, it is only available when this [`Builder`] was
175        /// returned by a [`LocalStaticScheduler`]
176        ///
177        /// This method returns a [`JoinHandle`] that can be used to await the
178        /// task's output. Dropping the [`JoinHandle`] _detaches_ the spawned task,
179        /// allowing it to run in the background without awaiting its output.
180        #[inline]
181        #[track_caller]
182        pub fn spawn_local<F>(&self, future: F) -> JoinHandle<F::Output>
183        where
184            F: Future + 'static,
185            F::Output: 'static,
186        {
187            let mut task = Box::new(Task::<&'static LocalStaticScheduler, _, BoxStorage>::new(future));
188            task.bind(self.scheduler);
189            let (task, join) = TaskRef::build_allocated::<&'static LocalStaticScheduler, _, BoxStorage>(&self.settings, task);
190            self.scheduler.schedule(task);
191            join
192        }
193    }
194
195    impl Builder<'_, LocalScheduler> {
196        /// Spawns a new `!`[`Send`] task with this builder's configured settings.
197        ///
198        /// This method is capable of spawning futures which do not implement
199        /// [`Send`]. Therefore, it is only available when this [`Builder`] was
200        /// returned by a [`LocalScheduler`]
201        ///
202        /// This method returns a [`JoinHandle`] that can be used to await the
203        /// task's output. Dropping the [`JoinHandle`] _detaches_ the spawned task,
204        /// allowing it to run in the background without awaiting its output.
205        #[inline]
206        #[track_caller]
207        pub fn spawn_local<F>(&self, future: F) -> JoinHandle<F::Output>
208        where
209            F: Future + 'static,
210            F::Output: 'static,
211        {
212            let mut task = Box::new(Task::<LocalScheduler, _, BoxStorage>::new(future));
213            task.bind(self.scheduler.clone());
214            let (task, join) = TaskRef::build_allocated::<LocalScheduler, _, BoxStorage>(&self.settings, task);
215            self.scheduler.schedule(task);
216            join
217        }
218
219        /// Spawns a new `!`[`Send`] task in a custom allocation, with this
220        /// builder's configured settings.
221        ///
222        /// This method is capable of spawning futures which do not implement
223        /// `Send`. Therefore, it is only available when this [`Builder`] was
224        /// returned by a [`LocalStaticScheduler`]
225        ///
226        /// Note that the `StoredTask` *must* be bound to the same scheduler
227        /// instance as this task's scheduler!
228        ///
229        /// This method returns a [`JoinHandle`] that can be used to await the
230        /// task's output. Dropping the [`JoinHandle`] _detaches_ the spawned task,
231        /// allowing it to run in the background without awaiting its output.
232        #[inline]
233        #[track_caller]
234        pub fn spawn_local_allocated<STO, F>(&self, task: STO::StoredTask) -> JoinHandle<F::Output>
235        where
236            F: Future + 'static,
237            F::Output: 'static,
238            STO: Storage<LocalScheduler, F>,
239        {
240            let (task, join) = TaskRef::build_allocated::<LocalScheduler, F, STO>(&self.settings, task);
241            self.scheduler.schedule(task);
242            join
243        }
244    }
245}
246
247// === impl Settings ===
248
249impl Settings<'_> {
250    /// Returns a new, empty task builder with no settings configured.
251    #[must_use]
252    pub(crate) const fn new() -> Self {
253        Self {
254            name: None,
255            location: None,
256            kind: "task",
257        }
258    }
259}
260
261impl Default for Settings<'_> {
262    fn default() -> Self {
263        Self::new()
264    }
265}