safa_api/syscalls/
thread.rs

1use core::{num::NonZero, time::Duration};
2
3use safa_abi::{
4    errors::ErrorStatus,
5    process::{RawContextPriority, RawTSpawnConfig},
6};
7
8use crate::{
9    exported_func,
10    syscalls::types::{Cid, OptionalPtrMut, RequiredPtr, RequiredPtrMut, SyscallResult},
11};
12
13use super::{define_syscall, SyscallNum};
14
15define_syscall! {
16    SyscallNum::SysTExit => {
17        /// Exits the current thread, threads don't have an exit code
18        /// however if the thread was the last thread in the process,
19        /// then the process will exit with code [`code`]
20        syst_exit(code: usize) unreachable
21    },
22    SyscallNum::SysTWait => {
23        /// Waits for a child thread with the cid `cid` to exit
24        ///
25        /// # Returns
26        /// - [`ErrorStatus::InvalidTid`] if thread doesn't exist at the time of wait
27        syst_wait(cid: Cid)
28    },
29    SyscallNum::SysTSleep => {
30      /// Sleeps for N ms
31      ///
32      /// should always succeed
33      syst_sleep(n: usize)
34    },
35    SyscallNum::SysTYield => {
36        /// Switches to the next thread in the thread queue of the current CPU
37        sysyield()
38    },
39}
40
41/// Exits the current thread, threads don't have an exit code
42/// however if the thread was the last thread in the process,
43/// then the process will exit with code `code`
44#[inline]
45pub fn exit(code: usize) -> ! {
46    syst_exit(code)
47}
48
49/// Switches to the next thread in the thread queue of the current CPU
50#[inline]
51pub fn yield_now() {
52    debug_assert!(sysyield().is_success())
53}
54
55#[inline]
56/// Waits for the thread with the id `cid` to exit
57//
58/// # Returns
59///
60/// - [`ErrorStatus::InvalidTid`] if the target thread doesn't exist at the time of wait
61pub fn wait(cid: Cid) -> Result<(), ErrorStatus> {
62    err_from_u16!(syst_wait(cid))
63}
64
65#[inline]
66/// Sleeps for a given duration
67///
68/// # Returns
69/// - Err(()) if duration as milliseconds is larger than usize::MAX
70pub fn sleep(duration: Duration) -> Result<(), ()> {
71    let ms: usize = duration.as_millis() as usize;
72    Ok(assert!(syst_sleep(ms).is_success()))
73}
74
75define_syscall! {
76    SyscallNum::SysTSpawn => {
77        /// Spawns a thread at the entry point `entry_point` with the config `config`
78        ///
79        /// - if `dest_cid` is not null it will be set to the spawned thread's ID (CID or TID)
80        syst_spawn_raw(entry_point: usize, config: RequiredPtr<RawTSpawnConfig>, dest_cid: OptionalPtrMut<Cid>)
81    },
82}
83
84exported_func! {
85    /// Spawns a thread as a child of self
86    /// # Arguments
87    /// - `entry_point`: a pointer to the main function of the thread,
88    /// the main function looks like this: `fn main(thread_id: Cid, argument_ptr: usize)` see `dest_cid` below for thread_id, argument_ptr == `argument_ptr`
89    ///
90    /// - `argument_ptr`: a pointer to the arguments that will be passed to the thread, this pointer will be based as is,
91    /// and therefore can also be used to pass a single usize argument
92    ///
93    /// - `priotrity`: the pritority of the thread in the thread queue, will default to the parent's
94    ///
95    /// - `dest_cid`: if not null, will be set to the thread ID of the spawned thread
96    extern "C" fn syst_spawn(
97        entry_point: usize,
98        argument_ptr: *const (),
99        priority: RawContextPriority,
100        custom_stack_size: Option<NonZero<usize>>,
101        dest_cid: OptionalPtrMut<Cid>,
102    ) -> SyscallResult {
103        let mut config = RawTSpawnConfig::new(
104            argument_ptr,
105            priority.into(),
106            None,
107            custom_stack_size.into(),
108        );
109        let config = unsafe {RequiredPtr::new_unchecked(&raw mut config)};
110        syst_spawn_raw(entry_point, config, dest_cid)
111    }
112}
113
114#[inline]
115/// Spawns a thread as a child of self
116/// unlike [`spawn`], this will pass no arguments to the thread
117/// # Arguments
118/// - `entry_point`: a pointer to the main function of the thread
119///
120/// - `priotrity`: the pritority of the thread in the thread queue, will default to the parent's
121///
122/// # Returns
123/// - the thread ID of the spawned thread
124pub fn spawn3(
125    entry_point: fn(thread_id: Cid) -> !,
126    priority: RawContextPriority,
127    custom_stack_size: Option<NonZero<usize>>,
128) -> Result<Cid, ErrorStatus> {
129    spawn_inner(
130        entry_point as usize,
131        core::ptr::null(),
132        priority,
133        custom_stack_size,
134    )
135}
136
137#[inline]
138/// Spawns a thread as a child of self
139/// unlike [`spawn`], instead of taking a reference as an argument to pass to the thread, this will take a usize
140/// # Arguments
141/// - `entry_point`: a pointer to the main function of the thread
142///
143/// - `argument`: a usize argument that will be passed to the thread
144///
145/// - `priotrity`: the pritority of the thread in the thread queue, will default to the parent's
146///
147/// # Returns
148/// - the thread ID of the spawned thread
149pub fn spawn2(
150    entry_point: fn(thread_id: Cid, argument: usize) -> !,
151    argument: usize,
152    priority: RawContextPriority,
153    custom_stack_size: Option<NonZero<usize>>,
154) -> Result<Cid, ErrorStatus> {
155    spawn_inner(
156        entry_point as usize,
157        argument as *const (),
158        priority,
159        custom_stack_size,
160    )
161}
162
163#[inline]
164/// Spawns a thread as a child of self
165/// # Arguments
166/// - `entry_point`: a pointer to the main function of the thread
167///
168/// - `argument_ptr`: a pointer to the arguments that will be passed to the thread, this pointer will be based as is,
169///
170/// - `priotrity`: the pritority of the thread in the thread queue, will default to the parent's
171///
172/// # Returns
173/// - the thread ID of the spawned thread
174pub fn spawn<T>(
175    entry_point: fn(thread_id: Cid, argument_ptr: &'static T) -> !,
176    argument_ptr: &'static T,
177    priority: RawContextPriority,
178    custom_stack_size: Option<NonZero<usize>>,
179) -> Result<Cid, ErrorStatus> {
180    spawn_inner(
181        entry_point as usize,
182        argument_ptr as *const T as *const (),
183        priority,
184        custom_stack_size,
185    )
186}
187
188#[inline(always)]
189fn spawn_inner(
190    entry_point: usize,
191    argument_ptr: *const (),
192    priority: RawContextPriority,
193    custom_stack_size: Option<NonZero<usize>>,
194) -> Result<Cid, ErrorStatus> {
195    let mut cid = 0;
196    let ptr = RequiredPtrMut::new(&raw mut cid).into();
197    err_from_u16!(
198        syst_spawn(
199            entry_point,
200            argument_ptr,
201            priority.into(),
202            custom_stack_size.into(),
203            ptr
204        ),
205        cid
206    )
207}