148 lines
3.4 KiB
Rust
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());
|
|
}
|
|
}
|