205 lines
5.7 KiB
Rust
205 lines
5.7 KiB
Rust
// 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.
|
|
|
|
#![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<isize>,
|
|
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
|
|
}
|
|
}
|