use super::{AtomicU64, Notify, Ordering};
pub(crate) struct EnqueueSequencer {
next_ticket: AtomicU64,
serving: AtomicU64,
notify: Notify,
}
impl EnqueueSequencer {
pub(crate) fn new() -> Self {
Self {
next_ticket: AtomicU64::new(0),
serving: AtomicU64::new(0),
notify: Notify::new(),
}
}
pub(crate) fn reserve_ticket(&self) -> u64 {
self.next_ticket.fetch_add(1, Ordering::Relaxed)
}
pub(crate) async fn wait_turn(&self, ticket: u64) {
let notified = self.notify.notified();
tokio::pin!(notified);
loop {
let _newly_registered = notified.as_mut().enable();
if self.serving.load(Ordering::Acquire) >= ticket {
return;
}
notified.as_mut().await;
notified.set(self.notify.notified());
}
}
pub(crate) fn advance_past(&self, ticket: u64) {
let _previous = self
.serving
.fetch_max(ticket.saturating_add(1), Ordering::AcqRel);
self.notify.notify_waiters();
}
}
impl std::fmt::Debug for EnqueueSequencer {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("EnqueueSequencer")
.field("next_ticket", &self.next_ticket.load(Ordering::Relaxed))
.field("serving", &self.serving.load(Ordering::Relaxed))
.finish_non_exhaustive()
}
}
pub(crate) struct EnqueueTurn<'a> {
pub(crate) sequencer: &'a EnqueueSequencer,
pub(crate) ticket: u64,
pub(crate) advanced: bool,
}
impl EnqueueTurn<'_> {
pub(crate) fn advance(&mut self) {
if !self.advanced {
self.sequencer.advance_past(self.ticket);
self.advanced = true;
}
}
}
impl Drop for EnqueueTurn<'_> {
fn drop(&mut self) {
self.advance();
}
}