use num_traits::ToPrimitive;
use std::ops::Drop;
use std::sync::{Condvar, Mutex};
pub struct Semaphore {
lock: Mutex<isize>,
cvar: Condvar,
}
pub struct SemaphoreGuard<'a> {
sem: &'a Semaphore,
lease_count: usize,
}
impl Semaphore {
pub fn new(count: isize) -> Semaphore {
Semaphore {
lock: Mutex::new(count),
cvar: Condvar::new(),
}
}
pub fn acquire(&self, lease_count: usize) {
let lease_count = lease_count.to_isize().unwrap();
let mut count = self.lock.lock().unwrap();
while *count < lease_count {
count = self.cvar.wait(count).unwrap();
}
*count -= lease_count;
}
pub fn release(&self, lease_count: usize) {
let lease_count = lease_count.to_isize().unwrap();
*self.lock.lock().unwrap() += lease_count;
self.cvar.notify_one();
}
#[allow(dead_code)]
pub fn access(&self, lease_count: usize) -> SemaphoreGuard {
self.acquire(lease_count);
SemaphoreGuard {
sem: self,
lease_count,
}
}
}
impl<'a> Drop for SemaphoreGuard<'a> {
fn drop(&mut self) {
self.sem.release(self.lease_count);
}
}
#[cfg(test)]
mod tests {
use super::Semaphore;
use std::sync::mpsc::channel;
use std::sync::Arc;
use std::thread;
#[test]
fn test_sem_acquire_release() {
let s = Semaphore::new(1);
s.acquire(1);
s.release(1);
s.acquire(1);
}
#[test]
fn test_sem_multi_acquire_release() {
let s = Semaphore::new(2);
s.acquire(1);
s.acquire(1);
s.release(1);
s.release(1);
s.acquire(2);
}
#[test]
fn test_sem_basic() {
let s = Semaphore::new(1);
let _g = s.access(1);
}
#[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(1);
});
let _g = s.access(1);
}
#[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(1);
tx.send(()).unwrap();
});
s.release(1);
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(1);
let _ = rx.recv();
});
s.acquire(1);
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(1);
let _ = rx2.recv();
tx1.send(()).unwrap();
});
let _g = s.access(1);
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(1);
thread::spawn(move || {
tx.send(()).unwrap();
drop(s2.access(1));
tx.send(()).unwrap();
});
rx.recv().unwrap(); }
rx.recv().unwrap(); }
}