hermit/syscalls/
semaphore.rs

1use alloc::boxed::Box;
2
3use crate::errno::*;
4use crate::synch::semaphore::Semaphore;
5use crate::syscalls::{CLOCK_REALTIME, sys_clock_gettime};
6use crate::time::timespec;
7
8#[allow(non_camel_case_types)]
9pub type sem_t = *const Semaphore;
10
11/// Create a new, unnamed semaphore.
12///
13/// This function can be used to get the raw memory location of a semaphore.
14///
15/// Stores the raw memory location of the new semaphore in parameter `sem`.
16/// Returns `0` on success, `-EINVAL` if `sem` is null.
17#[hermit_macro::system]
18#[unsafe(no_mangle)]
19pub unsafe extern "C" fn sys_sem_init(sem: *mut sem_t, pshared: i32, value: u32) -> i32 {
20	if sem.is_null() || pshared != 0 {
21		return -EINVAL;
22	}
23
24	// Create a new boxed semaphore and return a pointer to the raw memory.
25	let boxed_semaphore = Box::new(Semaphore::new(value as isize));
26	unsafe {
27		*sem = Box::into_raw(boxed_semaphore);
28	}
29	0
30}
31
32/// Destroy and deallocate a semaphore.
33///
34/// This function can be used to manually deallocate a semaphore via a reference.
35///
36/// Returns `0` on success, `-EINVAL` if `sem` is null.
37#[hermit_macro::system]
38#[unsafe(no_mangle)]
39pub unsafe extern "C" fn sys_sem_destroy(sem: *mut sem_t) -> i32 {
40	if sem.is_null() {
41		return -EINVAL;
42	}
43
44	// Consume the pointer to the raw memory into a Box again
45	// and drop the Box to free the associated memory.
46	unsafe {
47		drop(Box::from_raw((*sem).cast_mut()));
48	}
49	0
50}
51
52/// Release a semaphore.
53///
54/// This function can be used to allow the next blocked waiter to access this semaphore.
55/// It will notify the next waiter that `sem` is available.
56/// The semaphore is not deallocated after being released.
57///
58/// Returns `0` on success, or `-EINVAL` if `sem` is null.
59#[hermit_macro::system]
60#[unsafe(no_mangle)]
61pub unsafe extern "C" fn sys_sem_post(sem: *mut sem_t) -> i32 {
62	if sem.is_null() {
63		return -EINVAL;
64	}
65
66	// Get a reference to the given semaphore and release it.
67	let semaphore = unsafe { &**sem };
68	semaphore.release();
69	0
70}
71
72/// Try to acquire a lock on a semaphore.
73///
74/// This function does not block if the acquire fails.
75/// If the acquire fails (i.e. the semaphore's count is already 0), the function returns immediately.
76///
77/// Returns `0` on lock acquire, `-EINVAL` if `sem` is null, or `-ECANCELED` if the decrement fails.
78#[hermit_macro::system]
79#[unsafe(no_mangle)]
80pub unsafe extern "C" fn sys_sem_trywait(sem: *mut sem_t) -> i32 {
81	if sem.is_null() {
82		return -EINVAL;
83	}
84
85	// Get a reference to the given semaphore and acquire it in a non-blocking fashion.
86	let semaphore = unsafe { &**sem };
87	if semaphore.try_acquire() {
88		0
89	} else {
90		-ECANCELED
91	}
92}
93
94unsafe fn sem_timedwait(sem: *mut sem_t, ms: u32) -> i32 {
95	if sem.is_null() {
96		return -EINVAL;
97	}
98
99	let delay = if ms > 0 { Some(u64::from(ms)) } else { None };
100
101	// Get a reference to the given semaphore and wait until we have acquired it or the wakeup time has elapsed.
102	let semaphore = unsafe { &**sem };
103	if semaphore.acquire(delay) { 0 } else { -ETIME }
104}
105
106/// Try to acquire a lock on a semaphore.
107///
108/// Blocks until semaphore is acquired or until specified time passed
109///
110/// Returns `0` on lock acquire, `-EINVAL` if sem is null, or `-ETIME` on timeout.
111#[hermit_macro::system]
112#[unsafe(no_mangle)]
113pub unsafe extern "C" fn sys_sem_timedwait(sem: *mut sem_t, ts: *const timespec) -> i32 {
114	if ts.is_null() {
115		unsafe { sem_timedwait(sem, 0) }
116	} else {
117		let mut current_ts = timespec::default();
118
119		unsafe {
120			sys_clock_gettime(CLOCK_REALTIME, &mut current_ts);
121
122			let ts = &*ts;
123			let ms: i64 = (ts.tv_sec - current_ts.tv_sec) * 1000
124				+ (i64::from(ts.tv_nsec) - i64::from(current_ts.tv_nsec)) / 1_000_000;
125
126			if ms > 0 {
127				sem_timedwait(sem, ms.try_into().unwrap())
128			} else {
129				0
130			}
131		}
132	}
133}