#![allow(unsafe_code)]
#[cfg(not(all(loom, test)))]
mod radix;
#[cfg(not(all(loom, test)))]
mod ring;
#[cfg(not(all(loom, test)))]
pub(super) use self::radix::Radix;
pub(super) trait MessageQueue<T> {
fn push(&self, value: T);
unsafe fn try_pop(&self) -> Option<T>;
fn len(&self) -> usize;
}
#[cfg(not(all(loom, test)))]
pub(super) enum Bounded<T> {
Ring(ring::Ring<T>),
Radix(Radix<T>),
}
#[cfg(not(all(loom, test)))]
impl<T> Bounded<T> {
pub(super) fn with_capacity(capacity: usize) -> Self {
if (capacity as u64) <= ring::MAX_CELLS {
Self::Ring(ring::Ring::new(capacity))
} else {
Self::Radix(Radix::new())
}
}
}
#[cfg(not(all(loom, test)))]
impl<T> MessageQueue<T> for Bounded<T> {
#[inline]
fn push(&self, value: T) {
match self {
Self::Ring(ring) => ring.push(value),
Self::Radix(radix) => radix.push(value),
}
}
#[inline]
unsafe fn try_pop(&self) -> Option<T> {
match self {
Self::Ring(ring) => unsafe { ring.try_pop() },
Self::Radix(radix) => unsafe { radix.try_pop() },
}
}
fn len(&self) -> usize {
match self {
Self::Ring(ring) => ring.len(),
Self::Radix(radix) => radix.len(),
}
}
}
#[cfg(not(all(loom, test)))]
impl<T> MessageQueue<T> for Radix<T> {
#[inline]
fn push(&self, value: T) {
Radix::push(self, value);
}
#[inline]
unsafe fn try_pop(&self) -> Option<T> {
unsafe { Radix::try_pop(self) }
}
fn len(&self) -> usize {
Radix::len(self)
}
}
#[cfg(all(loom, test))]
const LOOM_CELLS: usize = 8;
#[cfg(all(loom, test))]
struct LoomCell<T> {
ready: crate::sync::internal::AtomicBool,
claimed: std::sync::atomic::AtomicBool,
value: crate::sync::internal::UnsafeCell<Option<T>>,
}
#[cfg(all(loom, test))]
pub(super) struct Queue<T> {
tail: crate::sync::internal::AtomicU64,
head: crate::sync::internal::AtomicU64,
cells: [LoomCell<T>; LOOM_CELLS],
}
#[cfg(all(loom, test))]
unsafe impl<T: Send> Send for Queue<T> {}
#[cfg(all(loom, test))]
unsafe impl<T: Send> Sync for Queue<T> {}
#[cfg(all(loom, test))]
impl<T> Queue<T> {
pub(super) fn new() -> Self {
Self {
tail: crate::sync::internal::AtomicU64::new(0),
head: crate::sync::internal::AtomicU64::new(0),
cells: core::array::from_fn(|_| LoomCell {
ready: crate::sync::internal::AtomicBool::new(false),
claimed: std::sync::atomic::AtomicBool::new(false),
value: crate::sync::internal::UnsafeCell::new(None),
}),
}
}
pub(super) fn push(&self, value: T) {
use crate::sync::internal::Ordering;
let pos = self.tail.fetch_add(1, Ordering::SeqCst) as usize;
assert!(
pos < LOOM_CELLS,
"a loom model sent more than {LOOM_CELLS} values"
);
let cell = &self.cells[pos];
cell.claimed
.store(true, std::sync::atomic::Ordering::Relaxed);
cell.value.with_mut(|v| unsafe { *v = Some(value) });
cell.ready.store(true, Ordering::SeqCst);
}
pub(super) unsafe fn try_pop(&self) -> Option<T> {
use crate::sync::internal::Ordering;
let pos = self.head.load(Ordering::Relaxed) as usize;
let cell = self.cells.get(pos)?;
if !cell.ready.load(Ordering::SeqCst) {
if cell.claimed.load(std::sync::atomic::Ordering::Relaxed) {
crate::sync::loom_model::busy_look();
}
return None;
}
let value = cell.value.with_mut(|v| unsafe { (*v).take() });
self.head.store(pos as u64 + 1, Ordering::Relaxed);
value
}
pub(super) fn len(&self) -> usize {
use crate::sync::internal::Ordering;
let front = self.head.load(Ordering::Acquire);
let back = self.tail.load(Ordering::Acquire);
back.saturating_sub(front) as usize
}
}
#[cfg(all(loom, test))]
pub(super) type Radix<T> = Queue<T>;
#[cfg(all(loom, test))]
pub(super) type Bounded<T> = Queue<T>;
#[cfg(all(loom, test))]
impl<T> Queue<T> {
pub(super) fn with_capacity(_capacity: usize) -> Self {
Self::new()
}
}
#[cfg(all(loom, test))]
impl<T> MessageQueue<T> for Queue<T> {
fn push(&self, value: T) {
Queue::push(self, value);
}
unsafe fn try_pop(&self) -> Option<T> {
unsafe { Queue::try_pop(self) }
}
fn len(&self) -> usize {
Queue::len(self)
}
}