// 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 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. #![unstable(feature = "semaphore", reason = "the interaction between semaphores and the acquisition/release \ of resources is currently unclear", issue = "27798")] 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. /// /// # Examples /// /// ``` /// #![feature(semaphore)] /// /// 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, 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: isize) -> 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().unwrap(); while *count <= 0 { count = self.cvar.wait(count).unwrap(); } *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().unwrap() += 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 } } } #[stable(feature = "rust1", since = "1.0.0")] impl<'a> Drop for SemaphoreGuard<'a> { fn drop(&mut self) { self.sem.release(); } } #[cfg(test)] mod tests { use prelude::v1::*; use sync::Arc; use super::Semaphore; use sync::mpsc::channel; use thread; #[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(); let _t = thread::spawn(move|| { 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(); let _t = thread::spawn(move|| { s2.acquire(); tx.send(()).unwrap(); }); 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(); let _t = thread::spawn(move|| { s2.release(); let _ = rx.recv(); }); s.acquire(); tx.send(()).unwrap(); } #[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(); let _t = thread::spawn(move|| { let _g = s2.access(); let _ = rx2.recv(); tx1.send(()).unwrap(); }); let _g = s.access(); tx2.send(()).unwrap(); rx1.recv().unwrap(); } #[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(); thread::spawn(move|| { tx.send(()).unwrap(); drop(s2.access()); tx.send(()).unwrap(); }); rx.recv().unwrap(); // wait for child to come alive } rx.recv().unwrap(); // wait for child to be done } }