use std::collections::VecDeque;
use std::sync::mpsc::RecvTimeoutError;
use std::sync::{Arc, Condvar, Mutex, MutexGuard};
use std::time::{Duration, Instant};
pub struct Queue<T> {
capacity: usize,
state: Mutex<State<T>>,
wake: Condvar,
}
struct State<T> {
values: VecDeque<T>,
closed: bool,
waiters: usize,
}
impl<T> Queue<T> {
pub fn new(capacity: usize) -> Arc<Self> {
let capacity = capacity.max(1);
Arc::new(Self {
capacity,
state: Mutex::new(State {
values: VecDeque::with_capacity(capacity),
closed: false,
waiters: 0,
}),
wake: Condvar::new(),
})
}
#[inline]
fn lock(&self) -> MutexGuard<'_, State<T>> {
self.state.lock().unwrap_or_else(|e| e.into_inner())
}
pub fn push(&self, value: T) {
let mut state = self.lock();
if state.closed {
return;
}
let evicted = if state.values.len() >= self.capacity {
state.values.pop_front()
} else {
None
};
state.values.push_back(value);
let notify = state.waiters > 0;
drop(state);
if notify {
self.wake.notify_one();
}
drop(evicted);
}
pub fn recv(&self) -> Option<T> {
let mut state = self.lock();
loop {
if let Some(value) = state.values.pop_front() {
return Some(value);
}
if state.closed {
return None;
}
state.waiters += 1;
state = self.wake.wait(state).unwrap_or_else(|e| e.into_inner());
state.waiters -= 1;
}
}
pub fn try_recv(&self) -> Option<T> {
self.lock().values.pop_front()
}
pub fn recv_deadline(&self, timeout: Duration) -> Result<T, RecvTimeoutError> {
let deadline = Instant::now().checked_add(timeout);
let mut state = self.lock();
loop {
if let Some(value) = state.values.pop_front() {
return Ok(value);
}
if state.closed {
return Err(RecvTimeoutError::Disconnected);
}
state.waiters += 1;
state = match deadline {
Some(deadline) => {
let now = Instant::now();
if now >= deadline {
state.waiters -= 1;
return Err(RecvTimeoutError::Timeout);
}
self.wake
.wait_timeout(state, deadline - now)
.unwrap_or_else(|e| e.into_inner())
.0
}
None => self.wake.wait(state).unwrap_or_else(|e| e.into_inner()),
};
state.waiters -= 1;
}
}
pub fn recv_timeout(&self, timeout: Duration) -> Option<T> {
self.recv_deadline(timeout).ok()
}
pub fn close(&self) {
let mut state = self.lock();
state.closed = true;
let has_waiters = state.waiters > 0;
drop(state);
if has_waiters {
self.wake.notify_all();
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::thread;
#[test]
fn newest_value_wins() {
let q = Queue::new(1);
q.push(1);
q.push(2);
q.push(3);
assert_eq!(q.try_recv(), Some(3));
assert_eq!(q.try_recv(), None);
}
#[test]
fn timeout_and_close_are_distinguished() {
let q: Arc<Queue<u8>> = Queue::new(1);
assert_eq!(
q.recv_deadline(Duration::from_millis(5)),
Err(RecvTimeoutError::Timeout)
);
q.push(7);
q.close();
assert_eq!(q.recv_deadline(Duration::from_millis(5)), Ok(7));
assert_eq!(
q.recv_deadline(Duration::from_millis(5)),
Err(RecvTimeoutError::Disconnected)
);
q.push(9);
assert_eq!(q.try_recv(), None, "push after close is ignored");
}
#[test]
fn blocked_receiver_is_woken_by_push_and_close() {
let q: Arc<Queue<u8>> = Queue::new(1);
let rx = Arc::clone(&q);
let handle = thread::spawn(move || (rx.recv(), rx.recv()));
thread::sleep(Duration::from_millis(30));
q.push(1);
thread::sleep(Duration::from_millis(30));
q.close();
assert_eq!(handle.join().unwrap(), (Some(1), None));
}
#[test]
fn huge_timeout_does_not_panic() {
let q: Arc<Queue<u8>> = Queue::new(1);
q.push(1);
assert_eq!(q.recv_deadline(Duration::MAX), Ok(1));
}
}