-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
213 additions
and
47 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
use ffi::*; | ||
use {Timeout, WaitTimeout}; | ||
|
||
/// A counting semaphore. | ||
/// | ||
/// Calls to `Semaphore::signal` must be balanced with calls to `Semaphore::wait`. | ||
/// Attempting to dispose of a semaphore with a count lower than value causes an EXC_BAD_INSTRUCTION exception. | ||
#[derive(Debug)] | ||
pub struct Semaphore { | ||
ptr: dispatch_semaphore_t, | ||
} | ||
|
||
impl Semaphore { | ||
/// Creates new counting semaphore with an initial value. | ||
/// | ||
/// Passing zero for the value is useful for | ||
/// when two threads need to reconcile the completion of a particular event. | ||
/// Passing a value greater than zero is useful for managing a finite pool of resources, | ||
/// where the pool size is equal to the value. | ||
pub fn new(n: u64) -> Self { | ||
let ptr = unsafe { dispatch_semaphore_create(n as i64) }; | ||
|
||
Semaphore { ptr } | ||
} | ||
|
||
/// Wait (decrement) for a semaphore. | ||
/// | ||
/// Decrement the counting semaphore. | ||
pub fn wait(&self) -> Result<(), WaitTimeout> { | ||
self.wait_timeout(DISPATCH_TIME_FOREVER) | ||
} | ||
|
||
/// Wait (decrement) for a semaphoreor until the specified timeout has elapsed. | ||
/// | ||
/// Decrement the counting semaphore. | ||
pub fn wait_timeout<T: Timeout>(&self, timeout: T) -> Result<(), WaitTimeout> { | ||
let when = timeout.as_raw(); | ||
|
||
let n = unsafe { dispatch_semaphore_wait(self.ptr, when) }; | ||
|
||
if n == 0 { | ||
Ok(()) | ||
} else { | ||
Err(WaitTimeout) | ||
} | ||
} | ||
|
||
/// Signal (increment) a semaphore. | ||
/// | ||
/// Increment the counting semaphore. | ||
/// If the previous value was less than zero, this function wakes a waiting thread before returning. | ||
/// | ||
/// This function returns `true` if a thread is woken. Otherwise, `false` is returned. | ||
pub fn signal(&self) -> bool { | ||
unsafe { dispatch_semaphore_signal(self.ptr) != 0 } | ||
} | ||
} | ||
|
||
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); | ||
} | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
|
||
#[test] | ||
fn test_semaphore() { | ||
let sem = Semaphore::new(1); | ||
|
||
assert!(sem.wait().is_ok()); | ||
assert_eq!(sem.wait_timeout(0).unwrap_err(), WaitTimeout); | ||
|
||
assert!(!sem.signal()); | ||
assert!(sem.wait_timeout(DISPATCH_TIME_FOREVER).is_ok()); | ||
|
||
// Calls to dispatch_semaphore_signal must be balanced with calls to wait(). | ||
// Attempting to dispose of a semaphore with a count lower than value causes an EXC_BAD_INSTRUCTION exception. | ||
assert!(!sem.signal()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
use std::ptr; | ||
use std::time::{Duration, Instant, SystemTime, SystemTimeError, UNIX_EPOCH}; | ||
|
||
use libc::timespec; | ||
|
||
use ffi::*; | ||
|
||
/// A type indicating whether a timed wait on a dispatch object returned due to a time out or not. | ||
#[derive(Clone, Copy, Debug, PartialEq)] | ||
pub struct WaitTimeout; | ||
|
||
/// When to timeout. | ||
pub trait Timeout { | ||
/// Extracts the raw `dispatch_time_t`. | ||
fn as_raw(self) -> dispatch_time_t; | ||
} | ||
|
||
impl<T: Timeout> Timeout for Option<T> { | ||
fn as_raw(self) -> dispatch_time_t { | ||
if let Some(timeout) = self { | ||
timeout.as_raw() | ||
} else { | ||
DISPATCH_TIME_NOW | ||
} | ||
} | ||
} | ||
|
||
impl Timeout for i32 { | ||
fn as_raw(self) -> dispatch_time_t { | ||
Duration::from_millis(self as u64).as_raw() | ||
} | ||
} | ||
|
||
impl Timeout for u32 { | ||
fn as_raw(self) -> dispatch_time_t { | ||
Duration::from_millis(self as u64).as_raw() | ||
} | ||
} | ||
|
||
impl Timeout for Duration { | ||
fn as_raw(self) -> dispatch_time_t { | ||
after(self) | ||
} | ||
} | ||
|
||
impl Timeout for dispatch_time_t { | ||
fn as_raw(self) -> dispatch_time_t { | ||
self | ||
} | ||
} | ||
|
||
impl Timeout for Instant { | ||
fn as_raw(self) -> dispatch_time_t { | ||
self.duration_since(Instant::now()).as_raw() | ||
} | ||
} | ||
|
||
impl Timeout for SystemTime { | ||
fn as_raw(self) -> dispatch_time_t { | ||
self.duration_since(SystemTime::now()).unwrap().as_raw() | ||
} | ||
} | ||
|
||
/// Returns a `dispatch_time_t` corresponding to the given time. | ||
pub fn after(delay: Duration) -> dispatch_time_t { | ||
delay | ||
.as_secs() | ||
.checked_mul(1_000_000_000) | ||
.and_then(|i| i.checked_add(delay.subsec_nanos() as u64)) | ||
.and_then(|i| { | ||
if i < (i64::max_value() as u64) { | ||
Some(i as i64) | ||
} else { | ||
None | ||
} | ||
}) | ||
.map_or(DISPATCH_TIME_FOREVER, |i| unsafe { | ||
dispatch_time(DISPATCH_TIME_NOW, i) | ||
}) | ||
} | ||
|
||
/// Returns a `dispatch_time_t` corresponding to the wall time. | ||
pub fn now() -> dispatch_time_t { | ||
unsafe { dispatch_walltime(ptr::null(), 0) } | ||
} | ||
|
||
/// Returns a `dispatch_time_t` corresponding to the given time. | ||
pub fn at(tm: SystemTime) -> Result<dispatch_time_t, SystemTimeError> { | ||
let dur = tm.duration_since(UNIX_EPOCH)?; | ||
let ts = timespec { | ||
tv_sec: dur.as_secs() as i64, | ||
tv_nsec: dur.subsec_nanos() as i64, | ||
}; | ||
|
||
Ok(unsafe { dispatch_walltime(&ts, 0) }) | ||
} |