use crate::unwrap_poison;
use std::collections::VecDeque;
use std::io;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering::SeqCst;
use std::sync::{Condvar, Mutex, MutexGuard};
use std::time::Duration;
const HIGH_WATERMARK: usize = 8096;
const LOW_WATERMARK: usize = 4096;
#[derive(Debug, Default)]
pub struct Queue {
dead: AtomicBool,
buffer: Mutex<VecDeque<Vec<u8>>>,
cond: Condvar,
}
impl Queue {
pub fn kill(&self) {
self.dead.store(true, SeqCst);
let guard = self.buffer.lock();
self.cond.notify_all();
drop(guard);
}
fn flush_count(
&self,
count: usize,
timeout: Option<Duration>,
) -> io::Result<MutexGuard<'_, VecDeque<Vec<u8>>>> {
let mut guard = unwrap_poison(self.buffer.lock())?;
while guard.len() > count {
if self.dead.load(SeqCst) {
return Err(io::Error::new(io::ErrorKind::BrokenPipe, "dead"));
}
if let Some(dur) = timeout.as_ref().copied() {
let (grd, timeout) = unwrap_poison(self.cond.wait_timeout(guard, dur))?;
if timeout.timed_out() {
return Err(io::Error::from(io::ErrorKind::TimedOut));
}
guard = grd;
continue;
}
guard = unwrap_poison(self.cond.wait(guard))?;
}
if self.dead.load(SeqCst) {
return Err(io::Error::new(io::ErrorKind::BrokenPipe, "dead"));
}
Ok(guard)
}
pub fn flush_low(&self, timeout: Option<Duration>) -> io::Result<()> {
drop(self.flush_count(LOW_WATERMARK, timeout)?);
Ok(())
}
pub fn flush_zero(&self) -> io::Result<()> {
drop(self.flush_count(0, None)?);
Ok(())
}
pub fn await_pop<T>(
&self,
outer_guard: MutexGuard<'_, T>,
duration: Option<Duration>,
) -> io::Result<()> {
let mut guard = unwrap_poison(self.buffer.lock())?;
drop(outer_guard);
while guard.is_empty() {
if self.dead.load(SeqCst) {
return Err(io::Error::new(io::ErrorKind::BrokenPipe, "dead"));
}
if let Some(dur) = duration.as_ref().copied() {
let (grd, timeout) = unwrap_poison(self.cond.wait_timeout(guard, dur))?;
if timeout.timed_out() {
return Err(io::Error::from(io::ErrorKind::TimedOut));
}
guard = grd;
continue;
}
guard = unwrap_poison(self.cond.wait(guard))?;
}
drop(guard);
Ok(())
}
pub fn try_pop(&self) -> io::Result<Option<Vec<u8>>> {
let mut guard = unwrap_poison(self.buffer.lock())?;
if let Some(pop) = guard.pop_front() {
self.cond.notify_all();
return Ok(Some(pop));
}
if self.dead.load(SeqCst) {
return Err(io::Error::new(io::ErrorKind::BrokenPipe, "dead"));
}
drop(guard);
Ok(None)
}
pub fn pop(&self) -> io::Result<Vec<u8>> {
let mut guard = unwrap_poison(self.buffer.lock())?;
loop {
if let Some(pop) = guard.pop_front() {
self.cond.notify_all();
return Ok(pop);
}
if self.dead.load(SeqCst) {
return Err(io::Error::new(io::ErrorKind::BrokenPipe, "dead"));
}
guard = unwrap_poison(self.cond.wait(guard))?;
}
}
pub fn push(&self, data: Vec<u8>) -> io::Result<()> {
let mut guard = self.flush_count(HIGH_WATERMARK, None)?; guard.push_back(data);
self.cond.notify_all();
drop(guard);
Ok(())
}
}