use crate::{future::DelayedPopFuture, item::DelayItem};
use async_condvar_fair::{BatonExt, Condvar};
use parking_lot::Mutex;
use std::{
collections::VecDeque,
num::NonZeroUsize,
sync::{
atomic::{AtomicU64, Ordering},
Arc,
},
time::{Duration, Instant},
};
struct Inner<T> {
max_size: Option<NonZeroUsize>,
queue: Mutex<VecDeque<DelayItem<T>>>,
size_condvar: Condvar,
reserve_condvar: Condvar,
counter: AtomicU64,
}
pub struct DelayedQueue<T> {
inner: Arc<Inner<T>>,
}
impl<T> DelayedQueue<T> {
pub fn new_bound(size: NonZeroUsize) -> DelayedQueue<T> {
DelayedQueue {
inner: Arc::new(Inner {
max_size: Some(size),
queue: Mutex::new(VecDeque::new()),
size_condvar: Condvar::new(),
reserve_condvar: Condvar::new(),
counter: AtomicU64::new(1),
}),
}
}
pub fn new_unbound() -> DelayedQueue<T> {
DelayedQueue {
inner: Arc::new(Inner {
max_size: None,
queue: Mutex::new(VecDeque::new()),
size_condvar: Condvar::new(),
reserve_condvar: Condvar::new(),
counter: AtomicU64::new(1),
}),
}
}
#[allow(clippy::await_holding_lock)]
pub async fn push(&self, item: T, delay: Duration) {
let this = self.inner.as_ref();
let pop_time = Instant::now() + delay;
let mut lock = loop {
let lock = this.queue.lock();
if let Some(max_size) = this.max_size {
if lock.len() >= max_size.get() {
this.size_condvar.wait_no_relock(lock).await.dispose();
} else {
break lock;
}
} else {
break lock;
}
};
let queue_item = DelayItem {
pop_time,
item,
reserved: Arc::new(AtomicU64::new(0)),
};
if let Some(max_size) = this.max_size {
let will_allocate =
((lock.len() + 1) > lock.capacity()) && ((lock.len() + 1) >= (max_size.get() / 2));
if will_allocate {
let diff = (max_size.get() as i64) - (lock.capacity() as i64);
if diff > 0 {
lock.reserve_exact(diff as usize);
} else {
lock.shrink_to(max_size.get());
}
}
}
lock.push_back(queue_item);
drop(lock);
this.size_condvar.notify_one();
}
pub fn pop(&self) -> DelayedPopFuture<T> {
DelayedPopFuture {
queue: &self.inner.queue,
size_condvar: &self.inner.size_condvar,
reserve_condvar: &self.inner.reserve_condvar,
condvar_future: None,
sleep_future: None,
reserve_waker: None,
future_id: self.inner.counter.fetch_add(1, Ordering::Release),
}
}
}
impl<T> Clone for DelayedQueue<T> {
fn clone(&self) -> Self {
DelayedQueue {
inner: self.inner.clone(),
}
}
}