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