rt/rust/src/sync/semaphore.rs

148 lines
3.4 KiB
Rust

use core::{
cell::UnsafeCell,
ffi::c_int,
marker::PhantomPinned,
mem::{size_of, transmute},
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, rt_syscall_args, rt_syscall_args_sem_post, rt_syscall_record,
},
list::list_init,
tick::Utick,
};
pub struct Semaphore {
s: UnsafeCell<rt_sem>,
_pin_marker: PhantomPinned,
}
unsafe impl Send for Semaphore {}
unsafe impl Sync for Semaphore {}
impl UnwindSafe for Semaphore {}
impl RefUnwindSafe for Semaphore {}
// Also used by cond and queue.
pub(crate) const fn c_sem_init(s: &'static 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(&s.wait_list),
num_waiters: 0,
post_pending: 0,
post_record: rt_syscall_record {
next: null_mut(),
args: unsafe {
let mut x: rt_syscall_args = transmute([0u8; size_of::<rt_syscall_args>()]);
x.sem_post = rt_syscall_args_sem_post {
sem: s as *const rt_sem as *mut rt_sem,
n: 1,
};
x
},
syscall: rt_syscall::RT_SYSCALL_SEM_POST,
},
}
}
impl Semaphore {
pub const fn init(sem: &'static Semaphore, value: i32, max_value: i32) -> Semaphore {
let s = unsafe { &*(sem.s.get() as *const rt_sem) };
Semaphore {
s: UnsafeCell::new(c_sem_init(s, value, max_value)),
_pin_marker: PhantomPinned,
}
}
pub fn post(&self) {
unsafe { rt_sem_post(self.s.get()) }
}
pub fn post_n(&self, n: i32) {
unsafe { rt_sem_post_n(self.s.get(), n) }
}
pub fn wait(&self) {
unsafe { rt_sem_wait(self.s.get()) }
}
pub fn try_wait(&self) -> bool {
unsafe { rt_sem_trywait(self.s.get()) }
}
pub fn timed_wait(&self, ticks: Utick) -> bool {
unsafe { rt_sem_timedwait(self.s.get(), ticks) }
}
pub fn acquire(&self) {
self.wait()
}
pub fn release(&self) {
self.post()
}
pub fn access(&self) -> SemaphoreGuard {
self.acquire();
SemaphoreGuard { sem: self }
}
}
pub struct SemaphoreGuard<'a> {
sem: &'a Semaphore,
}
impl<'a> Drop for SemaphoreGuard<'a> {
fn drop(&mut self) {
self.sem.release();
}
}
#[macro_export]
macro_rules! semaphore {
($name: ident, $count: expr, $max: expr) => {
static $name: $crate::sync::Semaphore = $crate::sync::Semaphore::init(&$name, $count, $max);
};
($name: ident, $count: expr) => {
$crate::semaphore!($name, $count, i32::MAX);
};
($name: ident) => {
$crate::semaphore!($name, 0, i32::MAX);
};
}
#[macro_export]
macro_rules! semaphore_binary {
($name: ident) => {
$crate::semaphore!($name, 0, 1);
};
}
#[cfg(test)]
mod tests {
#[test]
fn fast_path() {
semaphore!(SEM);
SEM.post();
SEM.post();
assert!(SEM.try_wait());
assert!(SEM.try_wait());
assert!(!SEM.try_wait());
}
#[test]
fn binary_fast_path() {
semaphore_binary!(SEM);
SEM.post();
SEM.post();
assert!(SEM.try_wait());
assert!(!SEM.try_wait());
}
}