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 crate::error::Closed;
9use crate::error::SpawnError;
10use crate::task::id::Id;
11use crate::task::join_handle::JoinHandle;
12use crate::task::{Task, TaskRef};
13use alloc::boxed::Box;
14use core::alloc::Allocator;
15use core::any::type_name;
16use core::panic::Location;
17
18pub struct TaskBuilder<'a, S> {
19 location: Option<Location<'a>>,
20 name: Option<&'a str>,
21 kind: &'a str,
22 schedule: S,
23}
24
25impl<'a, S> TaskBuilder<'a, S>
26where
27 S: Fn(TaskRef) -> Result<(), Closed>,
28{
29 pub fn new(schedule: S) -> Self {
30 Self {
31 location: None,
32 name: None,
33 kind: "task",
34 schedule,
35 }
36 }
37
38 /// Override the name of tasks spawned by this builder.
39 ///
40 /// By default, tasks are unnamed.
41 pub fn name(mut self, name: &'a str) -> Self {
42 self.name = Some(name);
43 self
44 }
45
46 /// Override the kind string of tasks spawned by this builder, this will only show up
47 /// in debug messages and spans.
48 ///
49 /// By default, tasks are of kind `"kind"`.
50 pub fn kind(mut self, kind: &'a str) -> Self {
51 self.kind = kind;
52 self
53 }
54
55 /// Override the source code location that will be associated with tasks spawned by this builder.
56 ///
57 /// By default, tasks will inherit the source code location of where they have been first spawned.
58 pub fn location(mut self, location: Location<'a>) -> Self {
59 self.location = Some(location);
60 self
61 }
62
63 #[inline]
64 #[track_caller]
65 fn build<F>(&self, future: F) -> Task<F>
66 where
67 F: Future + Send,
68 F::Output: 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)
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`] onto the executor using a custom [`Allocator`].
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_in<F, A>(
124 &self,
125 future: F,
126 alloc: A,
127 ) -> Result<JoinHandle<F::Output>, SpawnError>
128 where
129 F: Future + Send,
130 F::Output: Send,
131 A: Allocator,
132 {
133 let task = self.build(future);
134 let task = Box::try_new_in(task, alloc)?;
135 let (task, join) = TaskRef::new_allocated(task);
136
137 (self.schedule)(task)?;
138
139 Ok(join)
140 }
141}