use std::ops::Drop;
use std::sync::{Condvar, Mutex};
pub struct Semaphore {
lock: Mutex<isize>,
cvar: Condvar,
}
pub struct SemaphoreGuard<'a> {
sem: &'a Semaphore,
}
impl Semaphore {
pub fn new(count: isize) -> Semaphore {
Semaphore {
lock: Mutex::new(count),
cvar: Condvar::new(),
}
}
pub fn acquire(&self) {
let mut count = self.lock.lock().unwrap();
while *count <= 0 {
count = self.cvar.wait(count).unwrap();
}
*count -= 1;
}
pub fn release(&self) {
*self.lock.lock().unwrap() += 1;
self.cvar.notify_one();
}
pub fn access(&self) -> SemaphoreGuard {
self.acquire();
SemaphoreGuard { sem: self }
}
}
impl<'a> Drop for SemaphoreGuard<'a> {
fn drop(&mut self) {
self.sem.release();
}
}
#[cfg(test)]
mod tests {
use std::prelude::v1::*;
use std::sync::Arc;
use super::Semaphore;
use std::sync::mpsc::channel;
use std::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() {
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();
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() {
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(); }
rx.recv().unwrap(); }
}