use core::mem::ManuallyDrop;
use crate::{DispatchObject, DispatchRetained};
use crate::{DispatchTime, WaitError};
dispatch_object!(
#[doc(alias = "dispatch_semaphore_t")]
#[doc(alias = "dispatch_semaphore_s")]
pub struct DispatchSemaphore;
);
dispatch_object_not_data!(unsafe DispatchSemaphore);
impl DispatchSemaphore {
#[inline]
pub fn try_acquire(&self, timeout: DispatchTime) -> Result<DispatchSemaphoreGuard, WaitError> {
let result = Self::wait(self, timeout);
match result {
0 => Ok(DispatchSemaphoreGuard(ManuallyDrop::new(self.retain()))),
_ => Err(WaitError::Timeout),
}
}
}
#[derive(Debug)]
pub struct DispatchSemaphoreGuard(ManuallyDrop<DispatchRetained<DispatchSemaphore>>);
impl DispatchSemaphoreGuard {
#[inline]
pub fn release(self) -> bool {
let mut this = ManuallyDrop::new(self);
let semaphore = unsafe { ManuallyDrop::take(&mut this.0) };
semaphore.signal() != 0
}
}
impl Drop for DispatchSemaphoreGuard {
#[inline]
fn drop(&mut self) {
let semaphore = unsafe { ManuallyDrop::take(&mut self.0) };
semaphore.signal();
}
}
#[cfg(test)]
mod tests {
#[allow(unused_imports)]
use super::*;
#[test]
#[cfg(feature = "objc2")]
#[cfg_attr(
not(target_vendor = "apple"),
ignore = "only Apple's libdisptch is interoperable with `objc2`"
)]
fn acquire_release() {
fn retain_count(semaphore: &DispatchSemaphore) -> usize {
unsafe { objc2::msg_send![semaphore, retainCount] }
}
let semaphore = DispatchSemaphore::new(1);
assert_eq!(retain_count(&semaphore), 1);
{
let _guard = semaphore.try_acquire(DispatchTime::NOW).unwrap();
assert_eq!(retain_count(&semaphore), 2);
}
assert_eq!(retain_count(&semaphore), 1);
{
let guard = semaphore.try_acquire(DispatchTime::NOW).unwrap();
assert_eq!(retain_count(&semaphore), 2);
guard.release();
assert_eq!(retain_count(&semaphore), 1);
}
assert_eq!(retain_count(&semaphore), 1);
}
}