Next Generation WASM Microkernel Operating System
1// Copyright 2025. Jonas Kruckenberg
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8use alloc::boxed::Box;
9use core::any::type_name;
10use core::panic::Location;
11
12use crate::error::{Closed, SpawnError};
13use crate::task::id::Id;
14use crate::task::join_handle::JoinHandle;
15use crate::task::{Task, TaskRef};
16
17pub struct TaskBuilder<'a, S> {
18 location: Option<Location<'a>>,
19 name: Option<&'a str>,
20 kind: &'a str,
21 schedule: S,
22}
23
24impl<'a, S> TaskBuilder<'a, S>
25where
26 S: Fn(TaskRef) -> Result<(), Closed>,
27{
28 pub fn new(schedule: S) -> Self {
29 Self {
30 location: None,
31 name: None,
32 kind: "task",
33 schedule,
34 }
35 }
36
37 /// Override the name of tasks spawned by this builder.
38 ///
39 /// By default, tasks are unnamed.
40 pub fn name(mut self, name: &'a str) -> Self {
41 self.name = Some(name);
42 self
43 }
44
45 /// Override the kind string of tasks spawned by this builder, this will only show up
46 /// in debug messages and spans.
47 ///
48 /// By default, tasks are of kind `"kind"`.
49 pub fn kind(mut self, kind: &'a str) -> Self {
50 self.kind = kind;
51 self
52 }
53
54 /// Override the source code location that will be associated with tasks spawned by this builder.
55 ///
56 /// By default, tasks will inherit the source code location of where they have been first spawned.
57 pub fn location(mut self, location: Location<'a>) -> Self {
58 self.location = Some(location);
59 self
60 }
61
62 #[inline]
63 #[track_caller]
64 fn build<F, M>(&self, future: F, metadata: M) -> Task<F, M>
65 where
66 F: Future + Send,
67 F::Output: Send,
68 M: Send,
69 {
70 let id = Id::next();
71
72 let loc = self.location.as_ref().unwrap_or(Location::caller());
73 let span = tracing::trace_span!(
74 "task",
75 task.tid = id.as_u64(),
76 task.name = ?self.name,
77 task.kind = self.kind,
78 task.output = %type_name::<F::Output>(),
79 loc.file = loc.file(),
80 loc.line = loc.line(),
81 loc.col = loc.column(),
82 );
83
84 Task::new(future, id, span, metadata)
85 }
86
87 /// Attempt spawn this [`Future`] onto the executor.
88 ///
89 /// This method returns a [`TaskRef`] which can be used to spawn it onto an [`crate::executor::Executor`]
90 /// and a [`JoinHandle`] which can be used to await the futures output as well as control some aspects
91 /// of its runtime behaviour (such as cancelling it).
92 ///
93 /// # Errors
94 ///
95 /// Returns [`AllocError`] when allocation of the task fails.
96 #[inline]
97 #[track_caller]
98 pub fn try_spawn<F>(&self, future: F) -> Result<JoinHandle<F::Output, ()>, SpawnError>
99 where
100 F: Future + Send,
101 F::Output: Send,
102 {
103 let task = self.build(future, ());
104 let task = Box::try_new(task)?;
105 let (task, join) = TaskRef::new_allocated(task);
106
107 (self.schedule)(task)?;
108
109 Ok(join)
110 }
111
112 /// Attempt spawn this [`Future`] with the provided metadata onto the executor.
113 ///
114 /// This method returns a [`TaskRef`] which can be used to spawn it onto an [`crate::executor::Executor`]
115 /// and a [`JoinHandle`] which can be used to await the futures output as well as control some aspects
116 /// of its runtime behaviour (such as cancelling it).
117 ///
118 /// # Errors
119 ///
120 /// Returns [`AllocError`] when allocation of the task fails.
121 #[inline]
122 #[track_caller]
123 pub fn try_spawn_with_metadata<F, M>(
124 &self,
125 future: F,
126 metadata: M,
127 ) -> Result<JoinHandle<F::Output, M>, SpawnError>
128 where
129 F: Future + Send,
130 F::Output: Send,
131 M: Send,
132 {
133 let task = self.build(future, metadata);
134 let task = Box::try_new(task)?;
135 let (task, join) = TaskRef::new_allocated(task);
136
137 (self.schedule)(task)?;
138
139 Ok(join)
140 }
141}