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 {} microseconds", usecs);
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!(
146		"sys_kill is unimplemented, returning -ENOSYS for killing {} with signal {}",
147		dest, signum
148	);
149	-ENOSYS
150}
151
152#[cfg(feature = "newlib")]
153#[hermit_macro::system]
154#[unsafe(no_mangle)]
155pub extern "C" fn sys_signal(_handler: SignalHandler) -> i32 {
156	debug!("sys_signal is unimplemented");
157	0
158}
159
160#[hermit_macro::system]
161#[unsafe(no_mangle)]
162pub unsafe extern "C" fn sys_spawn2(
163	func: unsafe extern "C" fn(usize),
164	arg: usize,
165	prio: u8,
166	stack_size: usize,
167	selector: isize,
168) -> Tid {
169	unsafe { scheduler::spawn(func, arg, Priority::from(prio), stack_size, selector).into() }
170}
171
172#[hermit_macro::system]
173#[unsafe(no_mangle)]
174pub unsafe extern "C" fn sys_spawn(
175	id: *mut Tid,
176	func: unsafe extern "C" fn(usize),
177	arg: usize,
178	prio: u8,
179	selector: isize,
180) -> i32 {
181	let new_id = unsafe {
182		scheduler::spawn(func, arg, Priority::from(prio), USER_STACK_SIZE, selector).into()
183	};
184
185	if !id.is_null() {
186		unsafe {
187			*id = new_id;
188		}
189	}
190
191	0
192}
193
194#[hermit_macro::system]
195#[unsafe(no_mangle)]
196pub extern "C" fn sys_join(id: Tid) -> i32 {
197	match scheduler::join(TaskId::from(id)) {
198		Ok(()) => 0,
199		_ => -EINVAL,
200	}
201}
202
203/// Mapping between blocked tasks and their TaskHandle
204static BLOCKED_TASKS: InterruptTicketMutex<BTreeMap<TaskId, TaskHandle>> =
205	InterruptTicketMutex::new(BTreeMap::new());
206
207fn block_current_task(timeout: Option<u64>) {
208	let wakeup_time = timeout.map(|t| arch::processor::get_timer_ticks() + t * 1000);
209	let core_scheduler = core_scheduler();
210	let handle = core_scheduler.get_current_task_handle();
211	let tid = core_scheduler.get_current_task_id();
212
213	BLOCKED_TASKS.lock().insert(tid, handle);
214	core_scheduler.block_current_task(wakeup_time);
215}
216
217/// Set the current task state to `blocked`
218#[hermit_macro::system]
219#[unsafe(no_mangle)]
220pub extern "C" fn sys_block_current_task() {
221	block_current_task(None);
222}
223
224/// Set the current task state to `blocked`
225#[hermit_macro::system]
226#[unsafe(no_mangle)]
227pub extern "C" fn sys_block_current_task_with_timeout(timeout: u64) {
228	block_current_task(Some(timeout));
229}
230
231/// Wake up the task with the identifier `id`
232#[hermit_macro::system]
233#[unsafe(no_mangle)]
234pub extern "C" fn sys_wakeup_task(id: Tid) {
235	let task_id = TaskId::from(id);
236
237	if let Some(handle) = BLOCKED_TASKS.lock().remove(&task_id) {
238		core_scheduler().custom_wakeup(handle);
239	}
240}
241
242/// Determine the priority of the current thread
243#[hermit_macro::system]
244#[unsafe(no_mangle)]
245pub extern "C" fn sys_get_priority() -> u8 {
246	core_scheduler().get_current_task_prio().into()
247}
248
249/// Set priority of the thread with the identifier `id`
250#[hermit_macro::system]
251#[unsafe(no_mangle)]
252pub extern "C" fn sys_set_priority(id: Tid, prio: u8) {
253	if prio > 0 {
254		core_scheduler()
255			.set_priority(TaskId::from(id), Priority::from(prio))
256			.expect("Unable to set priority");
257	} else {
258		panic!("Invalid priority {}", prio);
259	}
260}
261
262/// Set priority of the current thread
263#[hermit_macro::system]
264#[unsafe(no_mangle)]
265pub extern "C" fn sys_set_current_task_priority(prio: u8) {
266	if prio > 0 {
267		core_scheduler().set_current_task_priority(Priority::from(prio));
268	} else {
269		panic!("Invalid priority {}", prio);
270	}
271}