diff options
Diffstat (limited to 'src/libstd/sync/semaphore.rs')
| -rw-r--r-- | src/libstd/sync/semaphore.rs | 195 |
1 files changed, 195 insertions, 0 deletions
diff --git a/src/libstd/sync/semaphore.rs b/src/libstd/sync/semaphore.rs new file mode 100644 index 00000000000..03fb84c38d4 --- /dev/null +++ b/src/libstd/sync/semaphore.rs @@ -0,0 +1,195 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use ops::Drop; +use sync::{Mutex, Condvar}; + +/// A counting, blocking, semaphore. +/// +/// Semaphores are a form of atomic counter where access is only granted if the +/// counter is a positive value. Each acquisition will block the calling thread +/// until the counter is positive, and each release will increment the counter +/// and unblock any threads if necessary. +/// +/// # Example +/// +/// ``` +/// use std::sync::Semaphore; +/// +/// // Create a semaphore that represents 5 resources +/// let sem = Semaphore::new(5); +/// +/// // Acquire one of the resources +/// sem.acquire(); +/// +/// // Acquire one of the resources for a limited period of time +/// { +/// let _guard = sem.access(); +/// // ... +/// } // resources is released here +/// +/// // Release our initially acquired resource +/// sem.release(); +/// ``` +pub struct Semaphore { + lock: Mutex<int>, + cvar: Condvar, +} + +/// An RAII guard which will release a resource acquired from a semaphore when +/// dropped. +pub struct SemaphoreGuard<'a> { + sem: &'a Semaphore, +} + +impl Semaphore { + /// Creates a new semaphore with the initial count specified. + /// + /// The count specified can be thought of as a number of resources, and a + /// call to `acquire` or `access` will block until at least one resource is + /// available. It is valid to initialize a semaphore with a negative count. + pub fn new(count: int) -> Semaphore { + Semaphore { + lock: Mutex::new(count), + cvar: Condvar::new(), + } + } + + /// Acquires a resource of this semaphore, blocking the current thread until + /// it can do so. + /// + /// This method will block until the internal count of the semaphore is at + /// least 1. + pub fn acquire(&self) { + let mut count = self.lock.lock(); + while *count <= 0 { + self.cvar.wait(&count); + } + *count -= 1; + } + + /// Release a resource from this semaphore. + /// + /// This will increment the number of resources in this semaphore by 1 and + /// will notify any pending waiters in `acquire` or `access` if necessary. + pub fn release(&self) { + *self.lock.lock() += 1; + self.cvar.notify_one(); + } + + /// Acquires a resource of this semaphore, returning an RAII guard to + /// release the semaphore when dropped. + /// + /// This function is semantically equivalent to an `acquire` followed by a + /// `release` when the guard returned is dropped. + pub fn access(&self) -> SemaphoreGuard { + self.acquire(); + SemaphoreGuard { sem: self } + } +} + +#[unsafe_destructor] +impl<'a> Drop for SemaphoreGuard<'a> { + fn drop(&mut self) { + self.sem.release(); + } +} + +#[cfg(test)] +mod tests { + use prelude::*; + + use sync::Arc; + use super::Semaphore; + + #[test] + fn test_sem_acquire_release() { + let s = Semaphore::new(1); + s.acquire(); + s.release(); + s.acquire(); + } + + #[test] + fn test_sem_basic() { + let s = Semaphore::new(1); + let _g = s.access(); + } + + #[test] + fn test_sem_as_mutex() { + let s = Arc::new(Semaphore::new(1)); + let s2 = s.clone(); + spawn(proc() { + let _g = s2.access(); + }); + let _g = s.access(); + } + + #[test] + fn test_sem_as_cvar() { + /* Child waits and parent signals */ + let (tx, rx) = channel(); + let s = Arc::new(Semaphore::new(0)); + let s2 = s.clone(); + spawn(proc() { + s2.acquire(); + tx.send(()); + }); + s.release(); + let _ = rx.recv(); + + /* Parent waits and child signals */ + let (tx, rx) = channel(); + let s = Arc::new(Semaphore::new(0)); + let s2 = s.clone(); + spawn(proc() { + s2.release(); + let _ = rx.recv(); + }); + s.acquire(); + tx.send(()); + } + + #[test] + fn test_sem_multi_resource() { + // Parent and child both get in the critical section at the same + // time, and shake hands. + let s = Arc::new(Semaphore::new(2)); + let s2 = s.clone(); + let (tx1, rx1) = channel(); + let (tx2, rx2) = channel(); + spawn(proc() { + let _g = s2.access(); + let _ = rx2.recv(); + tx1.send(()); + }); + let _g = s.access(); + tx2.send(()); + let _ = rx1.recv(); + } + + #[test] + fn test_sem_runtime_friendly_blocking() { + let s = Arc::new(Semaphore::new(1)); + let s2 = s.clone(); + let (tx, rx) = channel(); + { + let _g = s.access(); + spawn(proc() { + tx.send(()); + drop(s2.access()); + tx.send(()); + }); + rx.recv(); // wait for child to come alive + } + rx.recv(); // wait for child to be done + } +} |
