hermit/syscalls/
tasks.rs

1use alloc::collections::BTreeMap;
2
3use hermit_sync::InterruptTicketMutex;
4
5use crate::arch::core_local::*;
6use crate::arch::processor::{get_frequency, get_timestamp};
7use crate::config::USER_STACK_SIZE;
8use crate::errno::*;
9use crate::scheduler::PerCoreSchedulerExt;
10use crate::scheduler::task::{Priority, TaskHandle, TaskId};
11use crate::time::timespec;
12use crate::{arch, scheduler};
13
14#[cfg(feature = "newlib")]
15pub type SignalHandler = extern "C" fn(i32);
16pub type Tid = i32;
17
18#[hermit_macro::system]
19#[unsafe(no_mangle)]
20pub extern "C" fn sys_getpid() -> Tid {
21	0
22}
23
24#[cfg(feature = "newlib")]
25#[hermit_macro::system]
26#[unsafe(no_mangle)]
27pub unsafe extern "C" fn sys_getprio(id: *const Tid) -> i32 {
28	let task = core_scheduler().get_current_task_handle();
29
30	if id.is_null() || unsafe { *id } == task.get_id().into() {
31		i32::from(task.get_priority().into())
32	} else {
33		-EINVAL
34	}
35}
36
37#[cfg(feature = "newlib")]
38#[hermit_macro::system]
39#[unsafe(no_mangle)]
40pub unsafe extern "C" fn sys_setprio(_id: *const Tid, _prio: i32) -> i32 {
41	-ENOSYS
42}
43
44fn exit(arg: i32) -> ! {
45	debug!("Exit program with error code {arg}!");
46	super::shutdown(arg)
47}
48
49#[hermit_macro::system]
50#[unsafe(no_mangle)]
51pub extern "C" fn sys_exit(status: i32) -> ! {
52	exit(status)
53}
54
55#[hermit_macro::system]
56#[unsafe(no_mangle)]
57pub extern "C" fn sys_thread_exit(status: i32) -> ! {
58	debug!("Exit thread with error code {status}!");
59	core_scheduler().exit(status)
60}
61
62#[hermit_macro::system]
63#[unsafe(no_mangle)]
64pub extern "C" fn sys_abort() -> ! {
65	exit(-1)
66}
67
68pub(super) fn usleep(usecs: u64) {
69	if usecs >= 10_000 {
70		// Enough time to set a wakeup timer and block the current task.
71		debug!("sys_usleep blocking the task for {usecs} microseconds");
72		let wakeup_time = arch::processor::get_timer_ticks() + usecs;
73		let core_scheduler = core_scheduler();
74		core_scheduler.block_current_task(Some(wakeup_time));
75
76		// Switch to the next task.
77		core_scheduler.reschedule();
78	} else if usecs > 0 {
79		// Not enough time to set a wakeup timer, so just do busy-waiting.
80		let end = arch::processor::get_timestamp() + u64::from(get_frequency()) * usecs;
81		while get_timestamp() < end {
82			core_scheduler().reschedule();
83		}
84	}
85}
86
87#[hermit_macro::system]
88#[unsafe(no_mangle)]
89pub extern "C" fn sys_msleep(ms: u32) {
90	usleep(u64::from(ms) * 1000);
91}
92
93#[hermit_macro::system]
94#[unsafe(no_mangle)]
95pub extern "C" fn sys_usleep(usecs: u64) {
96	usleep(usecs);
97}
98
99#[hermit_macro::system]
100#[unsafe(no_mangle)]
101pub unsafe extern "C" fn sys_nanosleep(rqtp: *const timespec, _rmtp: *mut timespec) -> i32 {
102	assert!(
103		!rqtp.is_null(),
104		"sys_nanosleep called with a zero rqtp parameter"
105	);
106	let requested_time = unsafe { &*rqtp };
107	if requested_time.tv_sec < 0 || requested_time.tv_nsec > 999_999_999 {
108		debug!("sys_nanosleep called with an invalid requested time, returning -EINVAL");
109		return -EINVAL;
110	}
111
112	let microseconds =
113		(requested_time.tv_sec as u64) * 1_000_000 + (requested_time.tv_nsec as u64) / 1_000;
114	usleep(microseconds);
115
116	0
117}
118
119/// Creates a new thread based on the configuration of the current thread.
120#[cfg(feature = "newlib")]
121#[hermit_macro::system]
122#[unsafe(no_mangle)]
123pub unsafe extern "C" fn sys_clone(id: *mut Tid, func: extern "C" fn(usize), arg: usize) -> i32 {
124	let task_id = core_scheduler().clone(func, arg);
125
126	if !id.is_null() {
127		unsafe {
128			*id = task_id.into();
129		}
130	}
131
132	0
133}
134
135#[hermit_macro::system]
136#[unsafe(no_mangle)]
137pub extern "C" fn sys_yield() {
138	core_scheduler().reschedule();
139}
140
141#[cfg(feature = "newlib")]
142#[hermit_macro::system]
143#[unsafe(no_mangle)]
144pub extern "C" fn sys_kill(dest: Tid, signum: i32) -> i32 {
145	debug!("sys_kill is unimplemented, returning -ENOSYS for killing {dest} with signal {signum}");
146	-ENOSYS
147}
148
149#[cfg(feature = "newlib")]
150#[hermit_macro::system]
151#[unsafe(no_mangle)]
152pub extern "C" fn sys_signal(_handler: SignalHandler) -> i32 {
153	debug!("sys_signal is unimplemented");
154	0
155}
156
157#[hermit_macro::system]
158#[unsafe(no_mangle)]
159pub unsafe extern "C" fn sys_spawn2(
160	func: unsafe extern "C" fn(usize),
161	arg: usize,
162	prio: u8,
163	stack_size: usize,
164	selector: isize,
165) -> Tid {
166	unsafe { scheduler::spawn(func, arg, Priority::from(prio), stack_size, selector).into() }
167}
168
169#[hermit_macro::system]
170#[unsafe(no_mangle)]
171pub unsafe extern "C" fn sys_spawn(
172	id: *mut Tid,
173	func: unsafe extern "C" fn(usize),
174	arg: usize,
175	prio: u8,
176	selector: isize,
177) -> i32 {
178	let new_id = unsafe {
179		scheduler::spawn(func, arg, Priority::from(prio), USER_STACK_SIZE, selector).into()
180	};
181
182	if !id.is_null() {
183		unsafe {
184			*id = new_id;
185		}
186	}
187
188	0
189}
190
191#[hermit_macro::system]
192#[unsafe(no_mangle)]
193pub extern "C" fn sys_join(id: Tid) -> i32 {
194	match scheduler::join(TaskId::from(id)) {
195		Ok(()) => 0,
196		_ => -EINVAL,
197	}
198}
199
200/// Mapping between blocked tasks and their TaskHandle
201static BLOCKED_TASKS: InterruptTicketMutex<BTreeMap<TaskId, TaskHandle>> =
202	InterruptTicketMutex::new(BTreeMap::new());
203
204fn block_current_task(timeout: Option<u64>) {
205	let wakeup_time = timeout.map(|t| arch::processor::get_timer_ticks() + t * 1000);
206	let core_scheduler = core_scheduler();
207	let handle = core_scheduler.get_current_task_handle();
208	let tid = core_scheduler.get_current_task_id();
209
210	BLOCKED_TASKS.lock().insert(tid, handle);
211	core_scheduler.block_current_task(wakeup_time);
212}
213
214/// Set the current task state to `blocked`
215#[hermit_macro::system]
216#[unsafe(no_mangle)]
217pub extern "C" fn sys_block_current_task() {
218	block_current_task(None);
219}
220
221/// Set the current task state to `blocked`
222#[hermit_macro::system]
223#[unsafe(no_mangle)]
224pub extern "C" fn sys_block_current_task_with_timeout(timeout: u64) {
225	block_current_task(Some(timeout));
226}
227
228/// Wake up the task with the identifier `id`
229#[hermit_macro::system]
230#[unsafe(no_mangle)]
231pub extern "C" fn sys_wakeup_task(id: Tid) {
232	let task_id = TaskId::from(id);
233
234	if let Some(handle) = BLOCKED_TASKS.lock().remove(&task_id) {
235		core_scheduler().custom_wakeup(handle);
236	}
237}
238
239/// Determine the priority of the current thread
240#[hermit_macro::system]
241#[unsafe(no_mangle)]
242pub extern "C" fn sys_get_priority() -> u8 {
243	core_scheduler().get_current_task_prio().into()
244}
245
246/// Set priority of the thread with the identifier `id`
247#[hermit_macro::system]
248#[unsafe(no_mangle)]
249pub extern "C" fn sys_set_priority(id: Tid, prio: u8) {
250	if prio > 0 {
251		core_scheduler()
252			.set_priority(TaskId::from(id), Priority::from(prio))
253			.expect("Unable to set priority");
254	} else {
255		panic!("Invalid priority {}", prio);
256	}
257}
258
259/// Set priority of the current thread
260#[hermit_macro::system]
261#[unsafe(no_mangle)]
262pub extern "C" fn sys_set_current_task_priority(prio: u8) {
263	if prio > 0 {
264		core_scheduler().set_current_task_priority(Priority::from(prio));
265	} else {
266		panic!("Invalid priority {}", prio);
267	}
268}