use std::fmt;
use std::time::{Duration, Instant};
use crate::error::{
PopError, PopTimeoutError, PushError, PushTimeoutError, TryPopError, TryPushError,
};
use crate::queue::slot::Slot;
use crate::sync::pos::{CLOSED_BIT, MAX_CAPACITY, POS_MASK, pos_add, pos_diff};
use crate::sync::{
AtomicUsize, Backoff,
Ordering::{AcqRel, Acquire, Relaxed, Release},
WaitQueue,
};
use crate::traits::forward_bounded_queue;
use crate::utils::CachePadded;
pub struct LockFreeQueue<T> {
head: CachePadded<AtomicUsize>,
tail: CachePadded<AtomicUsize>,
consumers: WaitQueue,
producers: WaitQueue,
slots: Box<[Slot<T>]>,
mask: usize,
}
impl<T> LockFreeQueue<T> {
#[must_use]
pub fn new(capacity: usize) -> Self {
Self::with_start_position(capacity, 0)
}
fn with_start_position(capacity: usize, start: usize) -> Self {
assert!(capacity > 0, "capacity must be non-zero");
assert!(capacity <= MAX_CAPACITY, "capacity too large");
debug_assert_eq!(start & CLOSED_BIT, 0);
let capacity = capacity.next_power_of_two().max(2);
let mask = capacity - 1;
let slots = (0..capacity)
.map(|i| Slot::new(pos_add(start, i.wrapping_sub(start) & mask)))
.collect();
Self {
head: CachePadded::new(AtomicUsize::new(start)),
tail: CachePadded::new(AtomicUsize::new(start)),
consumers: WaitQueue::new(),
producers: WaitQueue::new(),
slots,
mask,
}
}
#[inline]
pub fn capacity(&self) -> usize {
self.mask + 1
}
#[inline]
fn slot(&self, pos: usize) -> &Slot<T> {
&self.slots[pos & self.mask]
}
pub fn try_push(&self, item: T) -> Result<(), TryPushError<T>> {
let mut backoff = Backoff::new();
let mut tail = self.tail.load(Relaxed);
loop {
if tail & CLOSED_BIT != 0 {
return Err(TryPushError::Closed(item));
}
let slot = self.slot(tail);
let seq = slot.sequence.load(Acquire);
let diff = pos_diff(seq, tail);
if diff == 0 {
match self
.tail
.compare_exchange_weak(tail, pos_add(tail, 1), AcqRel, Relaxed)
{
Ok(_) => {
unsafe { slot.write(item) };
slot.sequence.store(pos_add(tail, 1), Release);
self.consumers.notify_one();
return Ok(());
}
Err(actual) => {
tail = actual;
backoff.spin();
}
}
} else if diff < 0 {
let head = self.head.load(Acquire);
let current = self.tail.load(Relaxed);
if current == tail && pos_diff(tail, head) >= self.capacity() as isize {
return Err(TryPushError::Full(item));
}
backoff.spin();
tail = current;
} else {
backoff.snooze();
tail = self.tail.load(Relaxed);
}
}
}
pub fn try_pop(&self) -> Result<T, TryPopError> {
let mut backoff = Backoff::new();
let mut head = self.head.load(Relaxed);
loop {
let slot = self.slot(head);
let seq = slot.sequence.load(Acquire);
let diff = pos_diff(seq, pos_add(head, 1));
if diff == 0 {
match self
.head
.compare_exchange_weak(head, pos_add(head, 1), AcqRel, Relaxed)
{
Ok(_) => {
let item = unsafe { slot.read() };
slot.sequence.store(pos_add(head, self.capacity()), Release);
self.producers.notify_one();
return Ok(item);
}
Err(actual) => {
head = actual;
backoff.spin();
}
}
} else if diff < 0 {
let tail = self.tail.load(Acquire);
if tail & POS_MASK == head {
return Err(if tail & CLOSED_BIT == 0 {
TryPopError::Empty
} else {
TryPopError::Closed
});
}
backoff.spin();
head = self.head.load(Relaxed);
} else {
backoff.snooze();
head = self.head.load(Relaxed);
}
}
}
fn pop_ready(&self) -> bool {
let tail = self.tail.fetch_add(0, AcqRel);
let head = self.head.load(Relaxed);
tail & CLOSED_BIT != 0 || tail & POS_MASK != head
}
fn push_ready(&self) -> bool {
let head = self.head.fetch_add(0, AcqRel);
let tail = self.tail.load(Relaxed);
tail & CLOSED_BIT != 0 || pos_diff(tail, head) < self.capacity() as isize
}
fn push_until(
&self,
mut item: T,
deadline: Option<Instant>,
) -> Result<(), PushTimeoutError<T>> {
let mut backoff = Backoff::new();
loop {
match self.try_push(item) {
Ok(()) => return Ok(()),
Err(TryPushError::Closed(v)) => return Err(PushTimeoutError::Closed(v)),
Err(TryPushError::Full(v)) => item = v,
}
if deadline.is_some_and(|d| Instant::now() >= d) {
return Err(PushTimeoutError::Timeout(item));
}
if backoff.is_completed() {
self.producers.wait_until(|| self.push_ready(), deadline);
backoff.reset();
} else {
backoff.snooze();
}
}
}
fn pop_until(&self, deadline: Option<Instant>) -> Result<T, PopTimeoutError> {
let mut backoff = Backoff::new();
loop {
match self.try_pop() {
Ok(v) => return Ok(v),
Err(TryPopError::Closed) => return Err(PopTimeoutError::Closed),
Err(TryPopError::Empty) => {}
}
if deadline.is_some_and(|d| Instant::now() >= d) {
return Err(PopTimeoutError::Timeout);
}
if backoff.is_completed() {
self.consumers.wait_until(|| self.pop_ready(), deadline);
backoff.reset();
} else {
backoff.snooze();
}
}
}
pub fn push(&self, item: T) -> Result<(), PushError<T>> {
match self.push_until(item, None) {
Ok(()) => Ok(()),
Err(PushTimeoutError::Closed(v)) => Err(PushError(v)),
Err(PushTimeoutError::Timeout(_)) => unreachable!("no deadline was set"),
}
}
pub fn pop(&self) -> Result<T, PopError> {
match self.pop_until(None) {
Ok(v) => Ok(v),
Err(PopTimeoutError::Closed) => Err(PopError),
Err(PopTimeoutError::Timeout) => unreachable!("no deadline was set"),
}
}
pub fn push_timeout(&self, item: T, timeout: Duration) -> Result<(), PushTimeoutError<T>> {
self.push_until(item, Instant::now().checked_add(timeout))
}
pub fn pop_timeout(&self, timeout: Duration) -> Result<T, PopTimeoutError> {
self.pop_until(Instant::now().checked_add(timeout))
}
pub fn close(&self) -> bool {
let previous = self.tail.fetch_or(CLOSED_BIT, AcqRel);
let newly_closed = previous & CLOSED_BIT == 0;
if newly_closed {
self.consumers.notify_all();
self.producers.notify_all();
}
newly_closed
}
pub fn is_closed(&self) -> bool {
self.tail.load(Acquire) & CLOSED_BIT != 0
}
pub fn len(&self) -> usize {
loop {
let tail = self.tail.load(Acquire);
let head = self.head.load(Acquire);
if self.tail.load(Acquire) == tail {
let len = pos_diff(tail, head).clamp(0, self.capacity() as isize);
return len.unsigned_abs();
}
}
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn is_full(&self) -> bool {
self.len() == self.capacity()
}
}
impl<T> Drop for LockFreeQueue<T> {
fn drop(&mut self) {
if !std::mem::needs_drop::<T>() {
return;
}
let head = self.head.load(Relaxed);
let tail = self.tail.load(Relaxed) & POS_MASK;
let mut pos = head;
while pos != tail {
let slot = self.slot(pos);
debug_assert_eq!(slot.sequence.load(Relaxed), pos_add(pos, 1));
unsafe { slot.drop_in_place() };
pos = pos_add(pos, 1);
}
}
}
impl<T> fmt::Debug for LockFreeQueue<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("LockFreeQueue")
.field("capacity", &self.capacity())
.field("len", &self.len())
.field("closed", &self.is_closed())
.finish_non_exhaustive()
}
}
forward_bounded_queue!(LockFreeQueue);
#[cfg(all(test, not(loom)))]
mod tests {
use super::*;
#[test]
fn positions_wrap_around_the_closed_bit() {
let q = LockFreeQueue::with_start_position(4, POS_MASK - 2);
for round in 0..5 {
for i in 0..4 {
q.try_push(round * 10 + i).unwrap();
}
assert!(q.try_push(99).unwrap_err().is_full());
assert_eq!(q.len(), 4);
for i in 0..4 {
assert_eq!(q.try_pop(), Ok(round * 10 + i));
}
assert_eq!(q.try_pop(), Err(TryPopError::Empty));
assert!(!q.is_closed());
}
}
#[test]
fn drop_after_wrap_releases_exactly_the_live_items() {
use crate::sync::Arc;
let marker = Arc::new(());
let q = LockFreeQueue::with_start_position(4, POS_MASK - 1);
for _ in 0..4 {
q.try_push(Arc::clone(&marker)).unwrap();
}
drop(q.try_pop().unwrap());
q.try_push(Arc::clone(&marker)).unwrap();
assert_eq!(Arc::strong_count(&marker), 5);
drop(q);
assert_eq!(Arc::strong_count(&marker), 1);
}
}