use core::cell::UnsafeCell;
use core::sync::atomic::{AtomicUsize, Ordering};
use super::Error;
use crate::syscall;
use crate::sched::event;
pub struct Semaphore {
id: UnsafeCell<usize>,
permits: AtomicUsize,
permits_issued: AtomicUsize,
}
impl Semaphore {
pub const fn new(permits: usize) -> Self {
Semaphore {
id: UnsafeCell::new(0),
permits: AtomicUsize::new(permits),
permits_issued: AtomicUsize::new(0),
}
}
pub fn register(&self) -> Result<(),Error> {
let id = syscall::event_register();
if id == 0 {
Err(Error::OutOfMemory)
} else {
unsafe { self.id.get().write(id); }
Ok(())
}
}
pub fn try_acquire(&self) -> Result<SemaphorePermit<'_>, Error> {
if self.raw_acquire().is_ok() {
Ok(SemaphorePermit::new(&self))
} else {
Err(Error::WouldBlock)
}
}
pub fn acquire(&self, timeout: u32) -> Result<SemaphorePermit<'_>, Error> {
if self.raw_acquire().is_ok() {
return Ok(SemaphorePermit::new(&self));
} else {
let id = unsafe { *self.id.get() };
match syscall::event_await(id, timeout) {
Ok(_) => {
self.raw_acquire().ok();
Ok(SemaphorePermit::new(&self))
},
Err(event::Error::TimeOut) => Err(Error::TimeOut),
Err(_) => Err(Error::Poisoned),
}
}
}
pub fn available_permits(&self) -> usize {
self.permits.load(Ordering::Relaxed) - self.permits_issued.load(Ordering::Relaxed)
}
pub fn add_permits(&self, n: usize) {
self.permits.fetch_add(n, Ordering::Release);
syscall::event_fire(unsafe { *self.id.get() });
}
fn raw_acquire(&self) -> Result<(), ()> {
let permits = self.permits_issued.fetch_add(1, Ordering::Acquire);
if permits >= self.permits.load(Ordering::Relaxed) {
self.permits_issued.fetch_sub(1, Ordering::Release);
Err(())
} else {
Ok(())
}
}
fn raw_release(&self) {
self.permits_issued.fetch_sub(1, Ordering::Release);
syscall::event_fire(unsafe { *self.id.get() });
}
}
unsafe impl Sync for Semaphore {}
pub struct SemaphorePermit<'a> {
semaphore: &'a Semaphore,
}
impl<'a> SemaphorePermit<'a> {
fn new(semaphore: &'a Semaphore) -> Self {
SemaphorePermit {
semaphore,
}
}
pub fn forget(self) {
self.semaphore.permits.fetch_sub(1, Ordering::Relaxed);
}
}
impl<'a> Drop for SemaphorePermit<'a> {
fn drop(&mut self) {
self.semaphore.raw_release();
}
}