use core::{
cell::UnsafeCell,
ffi::c_int,
mem::zeroed,
panic::{RefUnwindSafe, UnwindSafe},
ptr::null_mut,
};
use crate::{
bindings::{
rt_atomic_int, rt_sem, rt_sem_post, rt_sem_post_n, rt_sem_timedwait, rt_sem_trywait,
rt_sem_wait, rt_syscall_args, rt_syscall_args_sem_post, rt_syscall_pendable,
rt_syscall_record,
},
list::list_init,
ptr_macros::{ptr_to_field, ptr_to_field_mut},
tick::Utick,
};
#[repr(transparent)]
pub struct Sem {
sem: UnsafeCell<rt_sem>,
}
unsafe impl Send for Sem {}
unsafe impl Sync for Sem {}
impl UnwindSafe for Sem {}
impl RefUnwindSafe for Sem {}
pub(crate) const fn c_sem_init(sem: *mut rt_sem, value: i32, max_value: i32) -> rt_sem {
rt_sem {
value: value as rt_atomic_int,
max_value: max_value as c_int,
wait_list: list_init(ptr_to_field_mut!(sem, wait_list)),
num_waiters: 0,
post_record: rt_syscall_record {
next: null_mut(),
args: unsafe {
let mut x: rt_syscall_args = zeroed();
x.sem_post = rt_syscall_args_sem_post { sem, n: 1 };
x
},
syscall: rt_syscall_pendable::RT_SYSCALL_PENDABLE_SEM_POST,
pending: unsafe { zeroed() },
},
}
}
impl Sem {
#[must_use]
pub const unsafe fn init(this: *const Self, value: i32, max_value: i32) -> Sem {
let sem = UnsafeCell::raw_get(ptr_to_field!(this, sem));
Sem {
sem: UnsafeCell::new(c_sem_init(sem, value, max_value)),
}
}
#[inline]
pub fn post(&self) {
unsafe { rt_sem_post(self.sem.get()) }
}
#[inline]
pub fn post_n(&self, n: i32) {
unsafe { rt_sem_post_n(self.sem.get(), n) }
}
#[inline]
pub fn wait(&self) {
unsafe { rt_sem_wait(self.sem.get()) }
}
#[inline]
pub fn try_wait(&self) -> bool {
unsafe { rt_sem_trywait(self.sem.get()) }
}
#[inline]
pub fn timed_wait(&self, ticks: Utick) -> bool {
unsafe { rt_sem_timedwait(self.sem.get(), ticks) }
}
#[inline]
pub fn acquire(&self) {
self.wait()
}
#[inline]
pub fn release(&self) {
self.post()
}
#[inline]
pub fn access(&self) -> SemGuard<'_> {
self.acquire();
SemGuard { sem: self }
}
}
pub struct SemGuard<'a> {
sem: &'a Sem,
}
impl Drop for SemGuard<'_> {
#[inline]
fn drop(&mut self) {
self.sem.release();
}
}
#[macro_export]
macro_rules! sem {
($name: ident, $count: expr, $max: expr) => {
static $name: $crate::sync::Sem = {
let ptr = &raw const $name;
let count = $count;
let max = $max;
unsafe { $crate::sync::Sem::init(ptr, count, max) }
};
};
($name: ident, $count: expr) => {
$crate::sem!($name, $count, i32::MAX);
};
($name: ident) => {
$crate::sem!($name, 0, i32::MAX);
};
}
#[macro_export]
macro_rules! sem_binary {
($name: ident) => {
$crate::sem!($name, 0, 1);
};
}
#[cfg(test)]
mod tests {
#[test]
fn fast_path() {
sem!(SEM);
SEM.post();
SEM.post();
assert!(SEM.try_wait());
assert!(SEM.try_wait());
assert!(!SEM.try_wait());
}
#[test]
fn binary_fast_path() {
sem_binary!(SEM);
SEM.post();
SEM.post();
assert!(SEM.try_wait());
assert!(!SEM.try_wait());
}
}