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 —
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}