//! The bounded push queue (SPEC §14.6): capacity 100, the oldest message dropped with a warning when full. //! [`PushQueue::enqueue`] never blocks; the sender task waits on [`PushHub::next`]. use std::collections::VecDeque; use std::sync::{Mutex, MutexGuard}; use tokio::sync::Notify; use super::{PushMessage, PushQueue}; /// Queue capacity (SPEC §14.6). pub const QUEUE_CAPACITY: usize = 100; /// The queue between the request handlers (and the scheduler) and the sender task. #[derive(Debug, Default)] pub struct PushHub { queue: Mutex>, notify: Notify, } impl PushHub { pub fn new() -> Self { Self::default() } fn lock(&self) -> MutexGuard<'_, VecDeque> { match self.queue.lock() { Ok(guard) => guard, Err(poisoned) => poisoned.into_inner(), } } /// Messages waiting. pub fn len(&self) -> usize { self.lock().len() } pub fn is_empty(&self) -> bool { self.len() == 0 } /// The oldest waiting message, if any. pub fn pop(&self) -> Option { self.lock().pop_front() } /// Waits for the next message. pub async fn next(&self) -> PushMessage { loop { if let Some(message) = self.pop() { return message; } self.notify.notified().await; } } } impl PushQueue for PushHub { fn enqueue(&self, message: PushMessage) { { let mut queue = self.lock(); if queue.len() >= QUEUE_CAPACITY && let Some(dropped) = queue.pop_front() { tracing::warn!( push_type = dropped.push_type.as_str(), capacity = QUEUE_CAPACITY, "push queue full: oldest message dropped" ); } queue.push_back(message); } self.notify.notify_one(); } }