Files
another-boids-in-rust/vendor/dispatch/src/sem.rs

139 lines
3.6 KiB
Rust

use std::os::raw::c_long;
use std::time::Duration;
use crate::ffi::*;
use crate::{time_after_delay, WaitTimeout};
/// A counting semaphore.
#[derive(Debug)]
pub struct Semaphore {
ptr: dispatch_semaphore_t,
}
impl Semaphore {
/// Creates a new `Semaphore` with an initial value.
///
/// A `Semaphore` created with a value greater than 0 cannot be disposed if
/// it has been decremented below its original value. If there are more
/// successful calls to `wait` than `signal`, the system assumes the
/// `Semaphore` is still in use and will abort if it is disposed.
pub fn new(value: u32) -> Self {
let ptr = unsafe {
dispatch_semaphore_create(value as c_long)
};
Semaphore { ptr }
}
/// Wait for (decrement) self.
pub fn wait(&self) {
let result = unsafe {
dispatch_semaphore_wait(self.ptr, DISPATCH_TIME_FOREVER)
};
assert!(result == 0, "Dispatch semaphore wait errored");
}
/// Wait for (decrement) self until the specified timeout has elapsed.
pub fn wait_timeout(&self, timeout: Duration) -> Result<(), WaitTimeout> {
let when = time_after_delay(timeout);
let result = unsafe {
dispatch_semaphore_wait(self.ptr, when)
};
if result == 0 {
Ok(())
} else {
Err(WaitTimeout { duration: timeout })
}
}
/// Signal (increment) self.
///
/// If the previous value was less than zero, this method wakes a waiting thread.
/// Returns `true` if a thread is woken or `false` otherwise.
pub fn signal(&self) -> bool {
unsafe {
dispatch_semaphore_signal(self.ptr) != 0
}
}
/// Wait to access a resource protected by self.
/// This decrements self and returns a guard that increments when dropped.
pub fn access(&self) -> SemaphoreGuard {
self.wait();
SemaphoreGuard::new(self.clone())
}
/// Wait until the specified timeout to access a resource protected by self.
/// This decrements self and returns a guard that increments when dropped.
pub fn access_timeout(&self, timeout: Duration)
-> Result<SemaphoreGuard, WaitTimeout> {
self.wait_timeout(timeout)?;
Ok(SemaphoreGuard::new(self.clone()))
}
}
unsafe impl Sync for Semaphore {}
unsafe impl Send for Semaphore {}
impl Clone for Semaphore {
fn clone(&self) -> Self {
unsafe {
dispatch_retain(self.ptr);
}
Semaphore { ptr: self.ptr }
}
}
impl Drop for Semaphore {
fn drop(&mut self) {
unsafe {
dispatch_release(self.ptr);
}
}
}
/// An RAII guard which will signal a `Semaphore` when dropped.
#[derive(Debug)]
pub struct SemaphoreGuard {
sem: Semaphore,
}
impl SemaphoreGuard {
fn new(sem: Semaphore) -> SemaphoreGuard {
SemaphoreGuard { sem }
}
/// Drops self, signaling the `Semaphore`.
pub fn signal(self) { }
}
impl Drop for SemaphoreGuard {
fn drop(&mut self) {
self.sem.signal();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_semaphore() {
let sem = Semaphore::new(0);
assert!(!sem.signal());
sem.wait();
assert!(sem.wait_timeout(Duration::from_millis(5)).is_err());
}
#[test]
fn test_semaphore_guard() {
let sem = Semaphore::new(1);
let guard = sem.access();
assert!(sem.access_timeout(Duration::from_millis(5)).is_err());
drop(guard);
assert!(sem.access_timeout(Duration::from_millis(5)).is_ok());
}
}