1use alloc::string::String;
4
5use crate::{
6 runtime::{
7 RuntimeStatus,
8 context::runtime_task_system,
9 resource::{
10 ExecutionContextHandle, KernelContextRequest, StackHandle, StackRequest,
11 ThreadResources, TlsHandle,
12 },
13 task_runtime,
14 },
15 sched::{CpuSet, SchedulePolicy},
16 thread::{
17 TaskError, ThreadExtension, ThreadHandle, ThreadSpec,
18 execution::{PreparedThread, ThreadExecution, thread_entry},
19 },
20};
21
22pub const DEFAULT_KERNEL_THREAD_STACK_SIZE: usize = 256 * 1024;
24
25#[derive(Debug)]
27pub struct ThreadBuilder {
28 name: String,
29 stack_size: usize,
30 stack_alignment: usize,
31 guard_size: usize,
32 policy: SchedulePolicy,
33 affinity: Option<CpuSet>,
34 os_extension: Option<ThreadExtension>,
35}
36
37impl ThreadBuilder {
38 pub fn new(name: String) -> Self {
40 Self {
41 name,
42 stack_size: DEFAULT_KERNEL_THREAD_STACK_SIZE,
43 stack_alignment: 16,
44 guard_size: 0,
45 policy: SchedulePolicy::default(),
46 affinity: None,
47 os_extension: None,
48 }
49 }
50 pub fn stack_size(mut self, size: usize) -> Self {
52 self.stack_size = size;
53 self
54 }
55 pub fn stack_alignment(mut self, alignment: usize) -> Self {
57 self.stack_alignment = alignment;
58 self
59 }
60 pub fn guard_size(mut self, size: usize) -> Self {
62 self.guard_size = size;
63 self
64 }
65 pub fn policy(mut self, policy: SchedulePolicy) -> Self {
67 self.policy = policy;
68 self
69 }
70 pub fn affinity(mut self, affinity: CpuSet) -> Self {
72 self.affinity = Some(affinity);
73 self
74 }
75 pub fn extension(mut self, extension: ThreadExtension) -> Self {
77 self.os_extension = Some(extension);
78 self
79 }
80
81 pub fn prepare(
83 self,
84 entry: impl FnOnce() + Send + 'static,
85 ) -> Result<PreparedThread, TaskError> {
86 unsafe {
89 self.prepare_with(entry, |request, _trampoline| {
90 allocate_thread_resources(runtime_task_system()?, request)
91 })
92 }
93 }
94
95 pub fn spawn(self, entry: impl FnOnce() + Send + 'static) -> Result<ThreadHandle, TaskError> {
97 self.prepare(entry)?.publish()
98 }
99
100 pub unsafe fn prepare_with(
107 mut self,
108 entry: impl FnOnce() + Send + 'static,
109 resources: impl FnOnce(
110 StackRequest,
111 unsafe extern "C" fn() -> !,
112 ) -> Result<ThreadResources, TaskError>,
113 ) -> Result<PreparedThread, TaskError> {
114 validate_spec(&self)?;
115 let system = runtime_task_system()?;
116 let execution = crate::thread::allocation::try_arc(ThreadExecution::new(
117 crate::thread::allocation::try_box(entry)?,
118 core::mem::take(&mut self.name),
119 ))?;
120 let resources = resources(self.stack_request(), thread_entry)?;
121 let mut spec = unsafe { ThreadSpec::new(self.policy).with_resources(resources) };
123 spec.execution = Some(execution);
124 if let Some(extension) = self.os_extension.take() {
125 spec = spec.with_extension(extension);
126 }
127 if let Some(affinity) = self.affinity.take() {
128 spec = spec.with_affinity(affinity);
129 }
130 Ok(PreparedThread::new(system.create_thread(spec)?))
131 }
132
133 fn stack_request(&self) -> StackRequest {
134 StackRequest {
135 usable_size: self.stack_size,
136 alignment: self.stack_alignment,
137 guard_size: self.guard_size,
138 }
139 }
140}
141
142fn validate_spec(spec: &ThreadBuilder) -> Result<(), TaskError> {
143 if spec.stack_size == 0 || spec.stack_alignment == 0 || !spec.stack_alignment.is_power_of_two()
144 {
145 Err(TaskError::InvalidConfiguration)
146 } else {
147 Ok(())
148 }
149}
150
151fn allocate_thread_resources(
152 system: &crate::runtime::TaskSystem,
153 request: StackRequest,
154) -> Result<ThreadResources, TaskError> {
155 let stack_result = task_runtime::allocate_stack(request);
156 if stack_result.status != RuntimeStatus::Success {
157 return Err(runtime_error(stack_result.status));
158 }
159 if stack_result.handle == 0 {
160 return Err(TaskError::InvalidRuntimeHandle);
161 }
162 let stack = unsafe { StackHandle::from_raw(stack_result.handle) };
165 let tls_result = task_runtime::allocate_kernel_tls();
166 let tls = match (tls_result.status, tls_result.handle) {
167 (RuntimeStatus::Success, 0) => {
168 return Err(release_partial_thread_resources(
169 system,
170 stack,
171 TlsHandle::NONE,
172 TaskError::InvalidRuntimeHandle,
173 ));
174 }
175 (RuntimeStatus::Success, handle) => {
176 unsafe { TlsHandle::from_raw(handle) }
179 }
180 (RuntimeStatus::Unsupported, _) => TlsHandle::NONE,
181 (status, _) => {
182 return Err(release_partial_thread_resources(
183 system,
184 stack,
185 TlsHandle::NONE,
186 runtime_error(status),
187 ));
188 }
189 };
190 let context_result = task_runtime::create_kernel_context(KernelContextRequest {
191 stack,
192 entry: thread_entry,
193 tls,
194 });
195 if context_result.status != RuntimeStatus::Success {
196 return Err(release_partial_thread_resources(
197 system,
198 stack,
199 tls,
200 runtime_error(context_result.status),
201 ));
202 }
203 if context_result.handle == 0 {
204 return Err(release_partial_thread_resources(
205 system,
206 stack,
207 tls,
208 TaskError::InvalidRuntimeHandle,
209 ));
210 }
211 Ok(unsafe {
212 ThreadResources::new(
215 ExecutionContextHandle::from_raw(context_result.handle),
216 stack,
217 tls,
218 crate::runtime::resource::AddressSpaceToken::NONE,
219 )
220 })
221}
222
223fn release_partial_thread_resources(
224 system: &crate::runtime::TaskSystem,
225 stack: StackHandle,
226 tls: TlsHandle,
227 creation_error: TaskError,
228) -> TaskError {
229 let resources = unsafe {
230 ThreadResources::new(
233 ExecutionContextHandle::NONE,
234 stack,
235 tls,
236 crate::runtime::resource::AddressSpaceToken::NONE,
237 )
238 };
239 system.release_unpublished_resources(resources);
240 creation_error
241}
242
243const fn runtime_error(status: RuntimeStatus) -> TaskError {
244 TaskError::RuntimeFailure(status as u32)
245}
246#[cfg(test)]
247mod tests {
248 use core::{
249 ptr,
250 sync::atomic::{AtomicUsize, Ordering},
251 };
252
253 use super::*;
254 use crate::thread::{SwitchReason, ThreadExtensionOps, ThreadId};
255
256 static TEST_EXTENSION_OPS: ThreadExtensionOps = ThreadExtensionOps {
257 on_switch_in: test_extension_switch_in,
258 on_switch_out: test_extension_switch_out,
259 on_exit: test_extension_hook,
260 on_deadline_overrun: test_extension_hook,
261 drop: test_extension_drop,
262 };
263
264 #[test]
265 fn dropping_unspawned_builder_releases_owned_extension() {
266 let drops = AtomicUsize::new(0);
267 let extension = unsafe {
268 ThreadExtension::new(
270 (&drops as *const AtomicUsize).expose_provenance(),
271 &TEST_EXTENSION_OPS,
272 )
273 };
274 let builder = ThreadBuilder::new(String::from("drop-test")).extension(extension);
275
276 drop(builder);
277
278 assert_eq!(drops.load(Ordering::Acquire), 1);
279 }
280
281 #[test]
282 fn invalid_spec_releases_extension_before_runtime_lookup() {
283 let drops = AtomicUsize::new(0);
284 let extension = unsafe {
285 ThreadExtension::new(
287 (&drops as *const AtomicUsize).expose_provenance(),
288 &TEST_EXTENSION_OPS,
289 )
290 };
291 let spec = {
292 ThreadBuilder::new(String::from("invalid-test"))
294 .stack_size(0)
295 .extension(extension)
296 };
297
298 let result = validate_spec(&spec);
299 drop(spec);
300
301 assert_eq!(result.unwrap_err(), TaskError::InvalidConfiguration);
302 assert_eq!(drops.load(Ordering::Acquire), 1);
303 }
304
305 unsafe extern "Rust" fn test_extension_hook(_data: usize, _thread: ThreadId) {}
306
307 unsafe extern "Rust" fn test_extension_switch_in(
308 _data: usize,
309 _thread: ThreadId,
310 _policy: SchedulePolicy,
311 _charged_runtime_ns: u64,
312 ) {
313 }
314
315 unsafe extern "Rust" fn test_extension_switch_out(
316 _data: usize,
317 _thread: ThreadId,
318 _reason: SwitchReason,
319 ) {
320 }
321
322 unsafe extern "Rust" fn test_extension_drop(data: usize) {
323 let drops = unsafe { &*ptr::with_exposed_provenance::<AtomicUsize>(data) };
325 drops.fetch_add(1, Ordering::AcqRel);
326 }
327}