use super::entry::Geometry;
use crate::sync::pos::{CLOSED_BIT, POS_MASK};
use crate::sync::{
AtomicIsize, AtomicUsize, Backoff,
Ordering::{AcqRel, Acquire, Relaxed},
};
use crate::utils::CachePadded;
const SPIN_ATTEMPTS: usize = if cfg!(loom) { 1 } else { 64 };
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(super) enum Deq {
Item(usize),
EmptyAtTail { closed: bool },
EmptyByThreshold,
}
pub(super) struct IndexRing {
head: CachePadded<AtomicUsize>,
tail: CachePadded<AtomicUsize>,
threshold: CachePadded<AtomicIsize>,
entries: Box<[AtomicUsize]>,
geo: Geometry,
}
impl IndexRing {
pub(super) fn new_empty(geo: Geometry, start: usize) -> Self {
debug_assert!(start >= geo.ring_len() && start % geo.ring_len() == 0);
let past = geo.cycle_of(start) - 1;
let entries = (0..geo.ring_len())
.map(|_| AtomicUsize::new(geo.pack(past, true, geo.bot())))
.collect();
Self {
head: CachePadded::new(AtomicUsize::new(start)),
tail: CachePadded::new(AtomicUsize::new(start)),
threshold: CachePadded::new(AtomicIsize::new(-1)),
entries,
geo,
}
}
pub(super) fn new_full(geo: Geometry, start: usize) -> Self {
let ring = Self::new_empty(geo, start);
let cycle = geo.cycle_of(start);
for i in 0..geo.n() {
let pos = start + i;
ring.entries[geo.slot(pos)].store(geo.pack(cycle, true, geo.initial_index(i)), Relaxed);
}
ring.tail.store(start + geo.n(), Relaxed);
ring.threshold.store(ring.threshold_max(), Relaxed);
ring
}
fn threshold_max(&self) -> isize {
(3 * self.geo.n() - 1) as isize
}
fn entry(&self, pos: usize) -> &AtomicUsize {
&self.entries[self.geo.slot(pos)]
}
pub(super) fn enqueue(&self, index: usize, closable: bool) -> Result<(), ()> {
let g = self.geo;
loop {
let raw = self.tail.fetch_add(1, Relaxed);
if closable && raw & CLOSED_BIT != 0 {
return Err(());
}
let t = raw & POS_MASK;
let tc = g.cycle_of(t);
let slot = self.entry(t);
let mut e = slot.load(Acquire);
loop {
if g.cycle(e) >= tc || g.index(e) != g.bot() {
break; }
if !g.is_safe(e) && self.head.load(Relaxed) > t {
break;
}
match slot.compare_exchange_weak(e, g.pack(tc, true, index), AcqRel, Acquire) {
Ok(_) => {
#[cfg(not(parkring_mutant = "scq_conditional_threshold"))]
self.threshold.swap(self.threshold_max(), AcqRel);
#[cfg(parkring_mutant = "scq_conditional_threshold")]
if self.threshold.load(Relaxed) != self.threshold_max() {
self.threshold.store(self.threshold_max(), Relaxed);
}
return Ok(());
}
Err(current) => e = current,
}
}
}
}
pub(super) fn dequeue(&self, drain: bool) -> Deq {
let g = self.geo;
let head = self.head.load(Relaxed);
let peek = self.entry(head).load(Relaxed);
let item_at_head = g.cycle(peek) == g.cycle_of(head) && g.index(peek) != g.bot();
if !item_at_head {
let tail = self.tail.load(Acquire);
if tail & POS_MASK <= self.head.load(Relaxed) {
return Deq::EmptyAtTail {
closed: tail & CLOSED_BIT != 0,
};
}
}
let mut backoff = Backoff::new();
loop {
let h = self.head.fetch_add(1, Relaxed);
let hc = g.cycle_of(h);
let slot = self.entry(h);
let mut e = slot.load(Acquire);
let mut waited = 0;
loop {
if g.cycle(e) == hc {
debug_assert_ne!(g.index(e), g.bot(), "position {h} consumed twice");
slot.fetch_or(g.bot(), Relaxed);
return Deq::Item(g.index(e));
}
if g.cycle(e) > hc {
break; }
let new = if g.index(e) == g.bot() {
if waited < SPIN_ATTEMPTS && self.tail.load(Relaxed) & POS_MASK > h {
waited += 1;
backoff.spin();
e = slot.load(Acquire);
continue;
}
g.pack(hc, g.is_safe(e), g.bot())
} else {
g.pack(g.cycle(e), false, g.index(e))
};
match slot.compare_exchange_weak(e, new, AcqRel, Acquire) {
Ok(_) => break,
Err(current) => e = current,
}
}
let tail = self.tail.load(Relaxed);
if tail & POS_MASK <= h + 1 {
self.catchup(tail, h + 1);
self.threshold.fetch_sub(1, AcqRel);
return Deq::EmptyAtTail {
closed: tail & CLOSED_BIT != 0,
};
}
if !drain && self.threshold.fetch_sub(1, AcqRel) <= 0 {
return Deq::EmptyByThreshold;
}
}
}
fn catchup(&self, mut tail: usize, mut head: usize) {
loop {
let new = head | (tail & CLOSED_BIT);
match self.tail.compare_exchange_weak(tail, new, Relaxed, Relaxed) {
Ok(_) => return,
Err(current) => tail = current,
}
head = self.head.load(Relaxed);
if tail & POS_MASK >= head {
return;
}
}
}
pub(super) fn close(&self) -> bool {
self.tail.fetch_or(CLOSED_BIT, AcqRel) & CLOSED_BIT == 0
}
pub(super) fn is_closed(&self) -> bool {
self.tail.load(Acquire) & CLOSED_BIT != 0
}
pub(super) fn ready(&self) -> bool {
self.threshold.fetch_add(0, AcqRel);
let tail = self.tail.load(Relaxed);
tail & CLOSED_BIT != 0 || tail & POS_MASK > self.head.load(Relaxed)
}
pub(super) fn len(&self) -> usize {
loop {
let tail = self.tail.load(Acquire);
let head = self.head.load(Acquire);
if self.tail.load(Acquire) == tail {
return (tail & POS_MASK).saturating_sub(head).min(self.geo.n());
}
}
}
pub(super) fn for_each_index(&mut self, mut f: impl FnMut(usize)) {
let g = self.geo;
for entry in &*self.entries {
let index = g.index(entry.load(Relaxed));
if index != g.bot() {
f(index);
}
}
}
}
impl std::fmt::Debug for IndexRing {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("IndexRing")
.field("head", &self.head.load(Relaxed))
.field("tail", &(self.tail.load(Relaxed) & POS_MASK))
.field("threshold", &self.threshold.load(Relaxed))
.field(
"entries",
&self
.entries
.iter()
.map(|e| {
let e = e.load(Relaxed);
let g = self.geo;
(g.cycle(e), g.is_safe(e), g.index(e))
})
.collect::<Vec<_>>(),
)
.finish_non_exhaustive()
}
}