1use alloc::{boxed::Box, string::String};
4use core::{
5 ptr,
6 sync::atomic::{AtomicBool, Ordering},
7};
8
9use crate::{
10 runtime::{
11 RuntimeStatus,
12 context::{RuntimeIrqGuard, runtime_current_cpu_mut, runtime_task_system},
13 lock::PreemptTicketLock,
14 resource::{
15 ExecutionContextHandle, KernelContextRequest, StackHandle, StackRequest,
16 ThreadResources, TlsHandle,
17 },
18 task_runtime,
19 },
20 sched::{CpuSet, SchedulePolicy},
21 sync::WaitQueue,
22 thread::{
23 SwitchReason, TaskError, ThreadExtension, ThreadExtensionOps, ThreadHandle, ThreadId,
24 ThreadSpec,
25 },
26};
27
28pub const DEFAULT_KERNEL_THREAD_STACK_SIZE: usize = 256 * 1024;
30
31#[derive(Debug)]
33pub struct ThreadBuilder {
34 name: String,
35 stack_size: usize,
36 stack_alignment: usize,
37 guard_size: usize,
38 policy: SchedulePolicy,
39 affinity: Option<CpuSet>,
40 os_extension: Option<ThreadExtension>,
41}
42
43impl ThreadBuilder {
44 pub fn new(name: String) -> Self {
46 Self {
47 name,
48 stack_size: DEFAULT_KERNEL_THREAD_STACK_SIZE,
49 stack_alignment: 16,
50 guard_size: 0,
51 policy: SchedulePolicy::default(),
52 affinity: None,
53 os_extension: None,
54 }
55 }
56
57 pub fn stack_size(mut self, stack_size: usize) -> Self {
59 self.stack_size = stack_size;
60 self
61 }
62
63 pub fn stack_alignment(mut self, stack_alignment: usize) -> Self {
65 self.stack_alignment = stack_alignment;
66 self
67 }
68
69 pub fn guard_size(mut self, guard_size: usize) -> Self {
71 self.guard_size = guard_size;
72 self
73 }
74
75 pub fn policy(mut self, policy: SchedulePolicy) -> Self {
77 self.policy = policy;
78 self
79 }
80
81 pub fn affinity(mut self, affinity: CpuSet) -> Self {
83 self.affinity = Some(affinity);
84 self
85 }
86
87 pub unsafe fn extension(mut self, extension: ThreadExtension) -> Self {
95 self.os_extension = Some(extension);
96 self
97 }
98
99 pub fn spawn<F>(self, entry: F) -> Result<KernelThreadHandle, TaskError>
106 where
107 F: FnOnce() + Send + 'static,
108 {
109 spawn_thread(self, entry)
110 }
111}
112
113#[derive(Debug)]
118#[must_use = "kernel threads must be joined or explicitly detached as permanent"]
119pub struct KernelThreadHandle {
120 thread: Option<ThreadHandle>,
121}
122
123impl KernelThreadHandle {
124 pub fn id(&self) -> ThreadId {
126 self.thread
127 .as_ref()
128 .expect("kernel thread handle is consumed only by ownership methods")
129 .id()
130 }
131
132 pub fn join(mut self) -> Result<(), TaskError> {
139 let handle = self.thread.take().ok_or(TaskError::InvalidConfiguration)?;
140 if crate::thread::current::current_thread_id()? == handle.id() {
141 return Err(TaskError::InvalidConfiguration);
142 }
143 let data = kernel_thread_data(&handle)?;
144 data.join_wait
145 .try_wait_until(|| data.exit_completed.load(Ordering::Acquire))?;
146 reap_joined_thread(handle)
147 }
148
149 pub fn detach_permanent(mut self) {
154 let _thread = self.thread.take();
155 }
156}
157
158fn reap_joined_thread(mut handle: ThreadHandle) -> Result<(), TaskError> {
159 match runtime_task_system()?.reap_thread_handle(handle) {
160 Ok(()) => Ok(()),
161 Err(error)
162 if matches!(
163 error.task_error(),
164 TaskError::ThreadBusy | TaskError::NotExited
165 ) =>
166 {
167 handle = error.into_retry_handle();
168 drop(handle);
169 Ok(())
170 }
171 Err(error) => Err(error.task_error()),
172 }
173}
174
175impl ThreadBuilder {
176 fn stack_request(&self) -> StackRequest {
177 StackRequest {
178 usable_size: self.stack_size,
179 alignment: self.stack_alignment,
180 guard_size: self.guard_size,
181 }
182 }
183}
184
185fn spawn_thread<F>(mut spec: ThreadBuilder, entry: F) -> Result<KernelThreadHandle, TaskError>
196where
197 F: FnOnce() + Send + 'static,
198{
199 validate_spec(&spec)?;
200 let system = runtime_task_system()?;
201 let resources = allocate_thread_resources(system, spec.stack_request())?;
202 let extension_data = Box::into_raw(Box::new(KernelThreadData::new(
203 entry,
204 core::mem::take(&mut spec.name),
205 spec.os_extension.take(),
206 )))
207 .expose_provenance();
208 let extension = unsafe { ThreadExtension::new(extension_data, &KERNEL_THREAD_OPS) };
211 let mut thread_spec = unsafe {
212 ThreadSpec::new(spec.policy)
215 .with_extension(extension)
216 .with_resources(resources)
217 };
218 if let Some(affinity) = spec.affinity.take() {
219 thread_spec = thread_spec.with_affinity(affinity);
220 }
221 let handle = system.create_thread(thread_spec)?;
222
223 let mut irq_guard = RuntimeIrqGuard::enter();
224 let result = runtime_current_cpu_mut(&mut irq_guard)
225 .and_then(|mut cpu| system.start_thread(cpu.as_mut(), handle.id()));
226 drop(irq_guard);
227 if let Err(error) = result {
228 cleanup_unstarted_thread(system, handle);
229 return Err(error);
230 }
231 Ok(KernelThreadHandle {
232 thread: Some(handle),
233 })
234}
235
236type KernelThreadEntry = Box<dyn FnOnce() + Send + 'static>;
237
238struct KernelThreadData {
239 entry: PreemptTicketLock<Option<KernelThreadEntry>>,
240 join_wait: WaitQueue,
241 exit_completed: AtomicBool,
242 os_extension: Option<ThreadExtension>,
243 _name: String,
244}
245
246impl KernelThreadData {
247 fn new(
248 entry: impl FnOnce() + Send + 'static,
249 name: String,
250 os_extension: Option<ThreadExtension>,
251 ) -> Self {
252 Self {
253 entry: PreemptTicketLock::new(Some(Box::new(entry))),
254 join_wait: WaitQueue::new(),
255 exit_completed: AtomicBool::new(false),
256 os_extension,
257 _name: name,
258 }
259 }
260}
261
262static KERNEL_THREAD_OPS: ThreadExtensionOps = ThreadExtensionOps {
263 on_switch_in: kernel_thread_switch_in,
264 on_switch_out: kernel_thread_switch_out,
265 on_exit: kernel_thread_exit,
266 on_deadline_overrun: kernel_thread_deadline_overrun,
267 drop: kernel_thread_drop,
268};
269
270unsafe extern "Rust" fn kernel_thread_switch_in(
271 data: usize,
272 thread: ThreadId,
273 policy: SchedulePolicy,
274 charged_runtime_ns: u64,
275) {
276 let data = unsafe { kernel_thread_data_from_raw(data) };
277 if let Some(extension) = data.os_extension.as_ref() {
278 unsafe {
280 (extension.ops().on_switch_in)(extension.data(), thread, policy, charged_runtime_ns)
281 };
282 }
283}
284
285unsafe extern "Rust" fn kernel_thread_switch_out(
286 data: usize,
287 thread: ThreadId,
288 reason: SwitchReason,
289) {
290 let data = unsafe { kernel_thread_data_from_raw(data) };
291 if let Some(extension) = data.os_extension.as_ref() {
292 unsafe { (extension.ops().on_switch_out)(extension.data(), thread, reason) };
294 }
295}
296
297unsafe extern "Rust" fn kernel_thread_exit(data: usize, thread: ThreadId) {
298 let data = unsafe { kernel_thread_data_from_raw(data) };
299 if let Some(extension) = data.os_extension.as_ref() {
300 unsafe { (extension.ops().on_exit)(extension.data(), thread) };
302 }
303 publish_kernel_thread_exit_completion(data);
304}
305
306fn publish_kernel_thread_exit_completion(data: &KernelThreadData) {
307 if data
308 .exit_completed
309 .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
310 .is_ok()
311 {
312 data.join_wait.notify_all();
313 }
314}
315
316unsafe extern "Rust" fn kernel_thread_deadline_overrun(data: usize, thread: ThreadId) {
317 let data = unsafe { kernel_thread_data_from_raw(data) };
318 if let Some(extension) = data.os_extension.as_ref() {
319 unsafe { (extension.ops().on_deadline_overrun)(extension.data(), thread) };
321 }
322}
323
324unsafe extern "Rust" fn kernel_thread_drop(data: usize) {
325 drop(unsafe { Box::from_raw(ptr::with_exposed_provenance_mut::<KernelThreadData>(data)) });
327}
328
329unsafe fn kernel_thread_data_from_raw(data: usize) -> &'static KernelThreadData {
330 unsafe { &*ptr::with_exposed_provenance::<KernelThreadData>(data) }
333}
334
335unsafe extern "C" fn kernel_thread_entry() -> ! {
336 if let Err(error) = unsafe {
337 crate::runtime::switch::finish_initial_context_switch()
340 } {
341 task_runtime::fatal_invariant(9, error_code(error));
342 }
343 let extension = crate::thread::current::current_thread_extension()
344 .unwrap_or_else(|error| task_runtime::fatal_invariant(10, error_code(error)))
345 .unwrap_or_else(|| task_runtime::fatal_invariant(11, 0));
346 if !core::ptr::eq(extension.ops(), &KERNEL_THREAD_OPS) {
347 task_runtime::fatal_invariant(12, extension.data());
348 }
349 let extension = unsafe {
350 extension.release_for_current_thread_entry()
353 };
354 let data_raw = extension.data();
355 let data = unsafe { &*ptr::with_exposed_provenance::<KernelThreadData>(data_raw) };
360 let Some(entry) = data.entry.lock().take() else {
361 task_runtime::fatal_invariant(13, data_raw);
362 };
363 entry();
364 let exit_permit = crate::thread::current::prepare_current_exit()
365 .unwrap_or_else(|error| task_runtime::fatal_invariant(15, error_code(error)));
366 publish_kernel_thread_exit_completion(data);
371 crate::thread::current::commit_current_exit(exit_permit)
372}
373
374fn validate_spec(spec: &ThreadBuilder) -> Result<(), TaskError> {
375 if spec.stack_size == 0 || spec.stack_alignment == 0 || !spec.stack_alignment.is_power_of_two()
376 {
377 Err(TaskError::InvalidConfiguration)
378 } else {
379 Ok(())
380 }
381}
382
383fn kernel_thread_data(handle: &ThreadHandle) -> Result<&KernelThreadData, TaskError> {
384 let extension = runtime_task_system()?
385 .thread_extension(handle)?
386 .ok_or(TaskError::InvalidConfiguration)?;
387 if !core::ptr::eq(extension.ops(), &KERNEL_THREAD_OPS) {
388 return Err(TaskError::InvalidConfiguration);
389 }
390 Ok(unsafe { &*ptr::with_exposed_provenance::<KernelThreadData>(extension.data()) })
394}
395
396fn allocate_thread_resources(
397 system: &crate::runtime::TaskSystem,
398 request: StackRequest,
399) -> Result<ThreadResources, TaskError> {
400 let stack_result = task_runtime::allocate_stack(request);
401 if stack_result.status != RuntimeStatus::Success {
402 return Err(runtime_error(stack_result.status));
403 }
404 if stack_result.handle == 0 {
405 return Err(TaskError::InvalidRuntimeHandle);
406 }
407 let stack = unsafe { StackHandle::from_raw(stack_result.handle) };
410 let tls_result = task_runtime::allocate_kernel_tls();
411 let tls = match (tls_result.status, tls_result.handle) {
412 (RuntimeStatus::Success, 0) => {
413 return Err(release_partial_thread_resources(
414 system,
415 stack,
416 TlsHandle::NONE,
417 TaskError::InvalidRuntimeHandle,
418 ));
419 }
420 (RuntimeStatus::Success, handle) => {
421 unsafe { TlsHandle::from_raw(handle) }
424 }
425 (RuntimeStatus::Unsupported, _) => TlsHandle::NONE,
426 (status, _) => {
427 return Err(release_partial_thread_resources(
428 system,
429 stack,
430 TlsHandle::NONE,
431 runtime_error(status),
432 ));
433 }
434 };
435 let context_result = task_runtime::create_kernel_context(KernelContextRequest {
436 stack,
437 entry: kernel_thread_entry,
438 tls,
439 });
440 if context_result.status != RuntimeStatus::Success {
441 return Err(release_partial_thread_resources(
442 system,
443 stack,
444 tls,
445 runtime_error(context_result.status),
446 ));
447 }
448 if context_result.handle == 0 {
449 return Err(release_partial_thread_resources(
450 system,
451 stack,
452 tls,
453 TaskError::InvalidRuntimeHandle,
454 ));
455 }
456 Ok(unsafe {
457 ThreadResources::new(
460 ExecutionContextHandle::from_raw(context_result.handle),
461 stack,
462 tls,
463 crate::runtime::resource::AddressSpaceToken::NONE,
464 )
465 })
466}
467
468fn release_partial_thread_resources(
469 system: &crate::runtime::TaskSystem,
470 stack: StackHandle,
471 tls: TlsHandle,
472 creation_error: TaskError,
473) -> TaskError {
474 let resources = unsafe {
475 ThreadResources::new(
478 ExecutionContextHandle::NONE,
479 stack,
480 tls,
481 crate::runtime::resource::AddressSpaceToken::NONE,
482 )
483 };
484 system.release_unpublished_resources(resources);
485 creation_error
486}
487
488fn cleanup_unstarted_thread(system: &crate::runtime::TaskSystem, handle: ThreadHandle) {
489 let thread = handle.id();
490 let _result = system.mark_exited(thread);
491 drop(handle);
492 let _result = system.reap_thread(thread);
493}
494
495const fn runtime_error(status: RuntimeStatus) -> TaskError {
496 TaskError::RuntimeFailure(status as u32)
497}
498
499const fn error_code(error: TaskError) -> usize {
500 match error {
501 TaskError::NotInitialized => 1,
502 TaskError::InvalidRuntimeHandle => 2,
503 TaskError::NoRunnableThread => 3,
504 TaskError::UnsafeContext => 4,
505 _ => 255,
506 }
507}
508
509#[cfg(test)]
510mod tests {
511 use core::sync::atomic::{AtomicUsize, Ordering};
512
513 use super::*;
514
515 static TEST_EXTENSION_OPS: ThreadExtensionOps = ThreadExtensionOps {
516 on_switch_in: test_extension_switch_in,
517 on_switch_out: test_extension_switch_out,
518 on_exit: test_extension_hook,
519 on_deadline_overrun: test_extension_hook,
520 drop: test_extension_drop,
521 };
522
523 #[test]
524 fn dropping_unspawned_builder_releases_owned_extension() {
525 let drops = AtomicUsize::new(0);
526 let extension = unsafe {
527 ThreadExtension::new(
529 (&drops as *const AtomicUsize).expose_provenance(),
530 &TEST_EXTENSION_OPS,
531 )
532 };
533 let builder = unsafe {
534 ThreadBuilder::new(String::from("drop-test")).extension(extension)
536 };
537
538 drop(builder);
539
540 assert_eq!(drops.load(Ordering::Acquire), 1);
541 }
542
543 #[test]
544 fn invalid_spec_releases_extension_before_runtime_lookup() {
545 let drops = AtomicUsize::new(0);
546 let extension = unsafe {
547 ThreadExtension::new(
549 (&drops as *const AtomicUsize).expose_provenance(),
550 &TEST_EXTENSION_OPS,
551 )
552 };
553 let spec = unsafe {
554 ThreadBuilder::new(String::from("invalid-test"))
556 .stack_size(0)
557 .extension(extension)
558 };
559
560 let result = validate_spec(&spec);
561 drop(spec);
562
563 assert_eq!(result.unwrap_err(), TaskError::InvalidConfiguration);
564 assert_eq!(drops.load(Ordering::Acquire), 1);
565 }
566
567 unsafe extern "Rust" fn test_extension_hook(_data: usize, _thread: ThreadId) {}
568
569 unsafe extern "Rust" fn test_extension_switch_in(
570 _data: usize,
571 _thread: ThreadId,
572 _policy: SchedulePolicy,
573 _charged_runtime_ns: u64,
574 ) {
575 }
576
577 unsafe extern "Rust" fn test_extension_switch_out(
578 _data: usize,
579 _thread: ThreadId,
580 _reason: SwitchReason,
581 ) {
582 }
583
584 unsafe extern "Rust" fn test_extension_drop(data: usize) {
585 let drops = unsafe { &*ptr::with_exposed_provenance::<AtomicUsize>(data) };
587 drops.fetch_add(1, Ordering::AcqRel);
588 }
589}