hermit/syscalls/
semaphore.rs

1use alloc::boxed::Box;
2
3use crate::errno::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(errno)]
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 -i32::from(Errno::Inval);
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(errno)]
38#[unsafe(no_mangle)]
39pub unsafe extern "C" fn sys_sem_destroy(sem: *mut sem_t) -> i32 {
40	if sem.is_null() {
41		return -i32::from(Errno::Inval);
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(errno)]
60#[unsafe(no_mangle)]
61pub unsafe extern "C" fn sys_sem_post(sem: *mut sem_t) -> i32 {
62	if sem.is_null() {
63		return -i32::from(Errno::Inval);
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(errno)]
79#[unsafe(no_mangle)]
80pub unsafe extern "C" fn sys_sem_trywait(sem: *mut sem_t) -> i32 {
81	if sem.is_null() {
82		return -i32::from(Errno::Inval);
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		-i32::from(Errno::Canceled)
91	}
92}
93
94unsafe fn sem_timedwait(sem: *mut sem_t, ms: u32) -> i32 {
95	if sem.is_null() {
96		return -i32::from(Errno::Inval);
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) {
104		0
105	} else {
106		-i32::from(Errno::Time)
107	}
108}
109
110/// Try to acquire a lock on a semaphore.
111///
112/// Blocks until semaphore is acquired or until specified time passed
113///
114/// Returns `0` on lock acquire, `-EINVAL` if sem is null, or `-ETIME` on timeout.
115#[hermit_macro::system(errno)]
116#[unsafe(no_mangle)]
117pub unsafe extern "C" fn sys_sem_timedwait(sem: *mut sem_t, ts: *const timespec) -> i32 {
118	if ts.is_null() {
119		unsafe { sem_timedwait(sem, 0) }
120	} else {
121		let mut current_ts = timespec::default();
122
123		unsafe {
124			sys_clock_gettime(CLOCK_REALTIME, &raw mut current_ts);
125
126			let ts = &*ts;
127			let ms: i64 = (ts.tv_sec - current_ts.tv_sec) * 1000
128				+ (i64::from(ts.tv_nsec) - i64::from(current_ts.tv_nsec)) / 1_000_000;
129
130			if ms > 0 {
131				sem_timedwait(sem, ms.try_into().unwrap())
132			} else {
133				0
134			}
135		}
136	}
137}