use super::task::UnsafeCell;
use commonware_utils::Widen;
use crossbeam_utils::CachePadded;
use std::{cell::Cell, marker::PhantomData, mem::MaybeUninit, thread};
cfg_if::cfg_if! {
if #[cfg(feature = "loom")] {
use loom::sync::{Arc, atomic::{AtomicBool, AtomicU32, AtomicU64, Ordering}};
} else {
use std::sync::{Arc, atomic::{AtomicBool, AtomicU32, AtomicU64, Ordering}};
}
}
pub const CAPACITY: usize = if cfg!(feature = "loom") { 4 } else { 256 };
const HALF: u32 = CAPACITY as u32 / 2;
pub const STEALABLE: usize = 2;
const MASK: usize = CAPACITY - 1;
const REAL_BITS: u32 = 55;
const REAL_MASK: u64 = (1 << REAL_BITS) - 1;
const _: () = assert!(CAPACITY.is_power_of_two() && CAPACITY < 1 << (u64::BITS - REAL_BITS));
const _: () = assert!(REAL_BITS >= u32::BITS);
struct Inner<T> {
head: AtomicU64,
tail: AtomicU32,
owned: AtomicBool,
exclusive: bool,
slots: Box<[UnsafeCell<MaybeUninit<T>>]>,
}
unsafe impl<T: Send> Sync for Inner<T> {}
unsafe impl<T: Send> Send for Inner<T> {}
pub struct Queue<T>(
Arc<CachePadded<Inner<T>>>,
);
pub struct Owner<T>(
Arc<CachePadded<Inner<T>>>,
PhantomData<Cell<()>>,
);
pub enum Push<'a, T> {
Pushed(
usize,
),
Overflow(
Overflow<'a, T>,
T,
),
Claimed(
T,
),
}
pub struct Overflow<'a, T> {
owner: &'a mut Owner<T>,
next: u32,
end: u32,
}
const fn unpack(head: u64) -> (u64, u32) {
(head & REAL_MASK, (head >> REAL_BITS) as u32)
}
const fn pack(real: u64, gap: u32) -> u64 {
(gap as u64) << REAL_BITS | (real & REAL_MASK)
}
const fn steal(real: u64, gap: u32) -> u32 {
(real as u32).wrapping_sub(gap)
}
impl<T> Queue<T> {
pub fn shared() -> Self {
Self::at(0, false)
}
pub fn exclusive() -> Self {
Self::at(0, true)
}
fn at(real: u64, exclusive: bool) -> Self {
assert!(real <= REAL_MASK, "start must fit the head's count");
Self(Arc::new(CachePadded::new(Inner {
head: AtomicU64::new(pack(real, 0)),
tail: AtomicU32::new(real as u32),
owned: AtomicBool::new(false),
exclusive,
slots: (0..CAPACITY)
.map(|_| UnsafeCell::new(MaybeUninit::uninit()))
.collect(),
})))
}
pub fn owner(&self) -> Owner<T> {
assert!(
!self.0.owned.swap(true, Ordering::AcqRel),
"queue owner must be taken at most once"
);
Owner(self.0.clone(), PhantomData)
}
pub fn is_stealable(&self) -> bool {
let (real, gap) = unpack(self.0.head.load(Ordering::Acquire));
let tail = self.0.tail.load(Ordering::Acquire);
gap == 0 && tail.wrapping_sub(real as u32) >= STEALABLE as u32
}
#[must_use]
pub fn steal_into(&self, into: &mut Owner<T>) -> usize {
assert!(!self.0.exclusive, "steal source must be a shared queue");
assert!(
!Arc::ptr_eq(&into.0, &self.0),
"steal destination must differ from the source queue"
);
let tail = into.0.tail.load(Ordering::Relaxed);
let (real, gap) = unpack(into.0.head.load(Ordering::Acquire));
if tail.wrapping_sub(steal(real, gap)) > HALF {
return 0;
}
let Some((first, count)) = self.claim() else {
return 0;
};
for offset in 0..count {
let value = unsafe { self.0.read((first as u32).wrapping_add(offset)) };
unsafe { into.0.write(tail.wrapping_add(offset), value) };
}
self.release_claim(first, count);
into.0
.tail
.store(tail.wrapping_add(count), Ordering::Release);
Widen::widen(count)
}
fn claim(&self) -> Option<(u64, u32)> {
let mut head = self.0.head.load(Ordering::Acquire);
loop {
let (real, gap) = unpack(head);
if gap != 0 {
return None;
}
let tail = self.0.tail.load(Ordering::Acquire);
let available = tail.wrapping_sub(real as u32);
if available > CAPACITY as u32 {
head = self.0.head.load(Ordering::Acquire);
continue;
}
let count = available / 2;
if count == 0 {
return None;
}
assert!(count <= HALF, "claim must not exceed half the queue");
let claimed = pack(real.wrapping_add(u64::from(count)), count);
match self
.0
.head
.compare_exchange(head, claimed, Ordering::AcqRel, Ordering::Acquire)
{
Ok(_) => return Some((real, count)),
Err(actual) => head = actual,
}
}
}
fn release_claim(&self, first: u64, count: u32) {
let mut head = pack(first.wrapping_add(u64::from(count)), count);
loop {
let (real, _) = unpack(head);
match self.0.head.compare_exchange(
head,
pack(real, 0),
Ordering::AcqRel,
Ordering::Acquire,
) {
Ok(_) => return,
Err(actual) => head = actual,
}
}
}
}
impl<T> Owner<T> {
pub fn len(&self) -> usize {
let (real, _) = unpack(self.0.head.load(Ordering::Acquire));
let tail = self.0.tail.load(Ordering::Relaxed);
Widen::widen(tail.wrapping_sub(real as u32))
}
pub fn room(&self) -> usize {
let (real, gap) = unpack(self.0.head.load(Ordering::Acquire));
let tail = self.0.tail.load(Ordering::Relaxed);
CAPACITY - Widen::widen(tail.wrapping_sub(steal(real, gap)))
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn push(&mut self, value: T) -> Push<'_, T> {
self.push_with(value, || {})
}
fn push_with(&mut self, value: T, mut after_snapshot: impl FnMut()) -> Push<'_, T> {
loop {
let head = self.0.head.load(Ordering::Acquire);
let (real, gap) = unpack(head);
let tail = self.0.tail.load(Ordering::Relaxed);
after_snapshot();
if tail.wrapping_sub(steal(real, gap)) < CAPACITY as u32 {
unsafe { self.0.write(tail, value) };
let tail = tail.wrapping_add(1);
self.0.tail.store(tail, Ordering::Release);
return Push::Pushed(Widen::widen(tail.wrapping_sub(real as u32)));
}
if gap != 0 {
return Push::Claimed(value);
}
assert_eq!(
tail.wrapping_sub(real as u32),
CAPACITY as u32,
"queue must be full to overflow"
);
if self
.0
.head
.compare_exchange(
head,
pack(real.wrapping_add(Widen::widen(CAPACITY)), 0),
Ordering::AcqRel,
Ordering::Acquire,
)
.is_err()
{
continue;
}
self.0
.tail
.store(tail.wrapping_add(HALF), Ordering::Release);
return Push::Overflow(
Overflow {
owner: self,
next: (real as u32).wrapping_add(HALF),
end: tail,
},
value,
);
}
}
#[must_use]
#[inline]
pub fn pop(&mut self) -> Option<T> {
if self.0.exclusive {
let (real, _) = unpack(self.0.head.load(Ordering::Relaxed));
let tail = self.0.tail.load(Ordering::Relaxed);
if real as u32 == tail {
return None;
}
self.0
.head
.store(pack(real.wrapping_add(1), 0), Ordering::Relaxed);
return Some(unsafe { self.0.read(real as u32) });
}
let mut head = self.0.head.load(Ordering::Acquire);
loop {
let (real, gap) = unpack(head);
let position = real as u32;
let tail = self.0.tail.load(Ordering::Relaxed);
if position == tail {
return None;
}
if gap == 0 && tail.wrapping_sub(position) == 1 {
self.0
.head
.store(pack(real.wrapping_add(1), 0), Ordering::Release);
return Some(unsafe { self.0.read(position) });
}
let next = pack(real.wrapping_add(1), if gap == 0 { 0 } else { gap + 1 });
match self
.0
.head
.compare_exchange(head, next, Ordering::AcqRel, Ordering::Acquire)
{
Ok(_) => {
return Some(unsafe { self.0.read(position) });
}
Err(actual) => head = actual,
}
}
}
pub fn push_batch(&mut self, mut values: impl ExactSizeIterator<Item = T>) {
let count = values.len();
assert!(count <= self.room(), "batch must fit the queue's room");
let tail = self.0.tail.load(Ordering::Relaxed);
for offset in 0..count {
let value = values
.next()
.expect("batch must yield at least its reported length");
unsafe { self.0.write(tail.wrapping_add(offset as u32), value) };
}
assert!(
values.next().is_none(),
"batch must yield at most its reported length"
);
self.0
.tail
.store(tail.wrapping_add(count as u32), Ordering::Release);
}
}
impl<T> Inner<T> {
unsafe fn read(&self, position: u32) -> T {
self.slots[Widen::widen(position) & MASK].with_mut(|ptr| {
unsafe { (*ptr).assume_init_read() }
})
}
unsafe fn write(&self, position: u32, value: T) {
self.slots[Widen::widen(position) & MASK].with_mut(|ptr| {
unsafe { ptr.write(MaybeUninit::new(value)) };
});
}
}
impl<T> Iterator for Overflow<'_, T> {
type Item = T;
fn next(&mut self) -> Option<T> {
if self.next == self.end {
return None;
}
let position = self.next;
self.next = self.next.wrapping_add(1);
Some(unsafe { self.owner.0.read(position) })
}
fn size_hint(&self) -> (usize, Option<usize>) {
let count = Widen::widen(self.end.wrapping_sub(self.next));
(count, Some(count))
}
}
impl<T> ExactSizeIterator for Overflow<'_, T> {}
impl<T> Drop for Overflow<'_, T> {
fn drop(&mut self) {
if !thread::panicking() {
assert_eq!(
self.next, self.end,
"overflow must be exhausted before it drops"
);
}
}
}
impl<T> Drop for Owner<T> {
fn drop(&mut self) {
if !thread::panicking() {
assert!(self.is_empty(), "owner must be empty when it drops");
}
}
}
impl<T> Drop for Inner<T> {
fn drop(&mut self) {
if !thread::panicking() {
let (real, gap) = unpack(self.head.load(Ordering::Relaxed));
assert_eq!(gap, 0, "queue must have no pending claim when it drops");
assert_eq!(
real as u32,
self.tail.load(Ordering::Relaxed),
"queue must be empty when it drops"
);
}
}
}
#[cfg(test)]
#[commonware_macros::test_group("miri")]
pub mod tests {
use super::*;
use std::{
panic::{AssertUnwindSafe, catch_unwind},
sync::{Barrier, atomic::AtomicUsize, mpsc},
};
impl<T> Queue<T> {
pub(crate) fn hold_claim(&self, count: u32) -> (u64, u32) {
let head = self.0.head.load(Ordering::Acquire);
let (real, gap) = unpack(head);
let tail = self.0.tail.load(Ordering::Acquire);
assert_eq!(gap, 0);
assert!(count > 0 && count <= HALF && count <= tail.wrapping_sub(real as u32));
assert!(
self.0
.head
.compare_exchange(
head,
pack(real + u64::from(count), count),
Ordering::AcqRel,
Ordering::Acquire
)
.is_ok()
);
(real, count)
}
pub(crate) fn finish_claim(&self, claim: (u64, u32)) -> Vec<T> {
let (first, count) = claim;
let values = (0..count)
.map(|offset| {
unsafe { self.0.read((first as u32).wrapping_add(offset)) }
})
.collect();
self.release_claim(first, count);
values
}
}
fn push<T>(owner: &mut Owner<T>, value: T) {
assert!(matches!(owner.push(value), Push::Pushed(_)));
}
fn drain<T>(owner: &mut Owner<T>) -> Vec<T> {
std::iter::from_fn(|| owner.pop()).collect()
}
fn assert_head<T>(queue: &Queue<T>, real: u64, gap: u32) {
assert_eq!(
queue.0.head.load(Ordering::Acquire),
(u64::from(gap) << REAL_BITS) | (real & REAL_MASK)
);
}
fn take_unowned<T>(queue: &Queue<T>) -> Vec<T> {
let (real, gap) = unpack(queue.0.head.load(Ordering::Acquire));
assert_eq!(gap, 0);
let tail = queue.0.tail.load(Ordering::Acquire);
let count = tail.wrapping_sub(real as u32);
let values = (0..count)
.map(|offset| {
unsafe { queue.0.read((real as u32).wrapping_add(offset)) }
})
.collect();
queue
.0
.head
.store(pack(real + u64::from(count), 0), Ordering::Release);
values
}
struct Counted(
usize,
Arc<AtomicUsize>,
);
impl Drop for Counted {
fn drop(&mut self) {
self.1.fetch_add(1, Ordering::Relaxed);
}
}
const WRAPS: [u64; 2] = [u32::MAX as u64, REAL_MASK];
#[test]
fn test_fifo_up_to_capacity() {
for queue in [Queue::shared(), Queue::exclusive()] {
let mut owner = queue.owner();
assert!(owner.is_empty());
assert_eq!(owner.room(), CAPACITY);
for value in 0..CAPACITY {
match owner.push(value) {
Push::Pushed(len) => assert_eq!(len, value + 1),
_ => panic!("push below capacity must publish"),
}
}
assert_eq!(owner.len(), CAPACITY);
assert_eq!(owner.room(), 0);
assert_eq!(drain(&mut owner), (0..CAPACITY).collect::<Vec<_>>());
owner.push_batch(0..CAPACITY);
assert_eq!(drain(&mut owner), (0..CAPACITY).collect::<Vec<_>>());
push(&mut owner, 0);
push(&mut owner, 1);
assert_eq!(owner.pop(), Some(0));
push(&mut owner, 2);
assert_eq!(drain(&mut owner), [1, 2]);
}
}
#[test]
fn test_positions_wrap_across_the_maximum() {
for (end, exclusive) in WRAPS
.into_iter()
.flat_map(|end| [(end, false), (end, true)])
{
let queue = Queue::at(end - 7, exclusive);
let mut owner = queue.owner();
owner.push_batch(0..CAPACITY);
assert_eq!(drain(&mut owner), (0..CAPACITY).collect::<Vec<_>>());
owner.push_batch(0..CAPACITY);
match owner.push(CAPACITY) {
Push::Overflow(batch, value) => {
assert_eq!(
batch.collect::<Vec<_>>(),
(CAPACITY / 2..CAPACITY).collect::<Vec<_>>()
);
assert_eq!(value, CAPACITY);
}
_ => panic!("full wrapping queue must overflow"),
}
assert_eq!(drain(&mut owner), (0..CAPACITY / 2).collect::<Vec<_>>());
}
}
#[test]
fn test_lone_value_pops_across_the_maximum() {
for start in [0, u64::from(u32::MAX), REAL_MASK] {
let queue = Queue::at(start, false);
let mut owner = queue.owner();
push(&mut owner, 7);
assert!(!queue.is_stealable());
assert_eq!(owner.pop(), Some(7));
let next = (start + 1) & REAL_MASK;
assert_eq!(unpack(queue.0.head.load(Ordering::Acquire)), (next, 0));
assert!(!queue.is_stealable());
assert!(owner.pop().is_none());
}
}
#[test]
fn test_overflow_keeps_the_oldest_half() {
for queue in [Queue::shared(), Queue::exclusive()] {
let mut owner = queue.owner();
owner.push_batch(0..CAPACITY);
match owner.push(CAPACITY) {
Push::Overflow(mut batch, value) => {
assert_eq!(batch.len(), CAPACITY / 2);
assert_eq!(batch.next(), Some(CAPACITY / 2));
assert_eq!(batch.len(), CAPACITY / 2 - 1);
let mut exported = vec![CAPACITY / 2];
exported.extend(batch);
exported.push(value);
assert_eq!(exported, (CAPACITY / 2..=CAPACITY).collect::<Vec<_>>());
}
_ => panic!("full queue must overflow"),
}
assert_eq!(owner.len(), CAPACITY / 2);
assert_eq!(owner.room(), CAPACITY / 2);
owner.push_batch(CAPACITY + 1..CAPACITY + 1 + CAPACITY / 2);
let mut expected = (0..CAPACITY / 2).collect::<Vec<_>>();
expected.extend(CAPACITY + 1..CAPACITY + 1 + CAPACITY / 2);
assert_eq!(drain(&mut owner), expected);
}
}
#[test]
fn test_steal_wraps_across_the_maximum() {
for end in WRAPS {
let source = Queue::at(end - 7, false);
let destination = Queue::at(end - 3, false);
let mut owner = source.owner();
let mut into = destination.owner();
owner.push_batch(0..CAPACITY);
assert_eq!(source.steal_into(&mut into), CAPACITY / 2);
let mut values = drain(&mut into);
values.extend(drain(&mut owner));
assert_eq!(values, (0..CAPACITY).collect::<Vec<_>>());
}
}
#[test]
fn test_claim_and_pops_across_the_maximum() {
for end in WRAPS {
let queue = Queue::at(end - u64::from(HALF) - 7, false);
let mut owner = queue.owner();
owner.push_batch(0..CAPACITY);
let claim = queue.hold_claim(HALF);
assert_eq!(owner.len(), CAPACITY / 2);
assert_eq!(owner.room(), 0);
assert!(matches!(owner.push(CAPACITY), Push::Claimed(value) if value == CAPACITY));
let popped = drain(&mut owner);
assert_eq!(popped, (CAPACITY / 2..CAPACITY).collect::<Vec<_>>());
assert_head(&queue, end + u64::from(HALF) - 7, CAPACITY as u32);
assert_eq!(owner.room(), 0);
assert!(!queue.is_stealable());
let mut seen = queue.finish_claim(claim);
assert_eq!(owner.room(), CAPACITY);
seen.extend(popped);
assert_eq!(seen, (0..CAPACITY).collect::<Vec<_>>());
}
}
#[test]
fn test_every_head_update_preserves_the_widened_count() {
for start in [
u64::from(u32::MAX) - 1,
u64::from(u32::MAX),
(1u64 << 32) + 7,
REAL_MASK - 1,
REAL_MASK,
] {
for exclusive in [false, true] {
let queue = Queue::at(start, exclusive);
let mut owner = queue.owner();
owner.push_batch(0..2);
assert_eq!(owner.pop(), Some(0));
assert_head(&queue, start + 1, 0);
assert_eq!(owner.pop(), Some(1));
assert_head(&queue, start + 2, 0);
}
for exclusive in [false, true] {
let queue = Queue::at(start, exclusive);
let mut owner = queue.owner();
owner.push_batch(0..CAPACITY);
match owner.push(CAPACITY) {
Push::Overflow(batch, value) => {
assert_eq!(
batch.collect::<Vec<_>>(),
(CAPACITY / 2..CAPACITY).collect::<Vec<_>>()
);
assert_eq!(value, CAPACITY);
}
_ => panic!("full queue must overflow"),
}
assert_head(&queue, start + Widen::widen(CAPACITY), 0);
assert_eq!(drain(&mut owner), (0..CAPACITY / 2).collect::<Vec<_>>());
}
for pop_past_claim in [false, true] {
let queue = Queue::at(start, false);
let mut owner = queue.owner();
owner.push_batch(0..CAPACITY);
let claim = queue.claim().expect("full queue must be claimable");
assert_eq!(claim, (start, HALF));
assert_head(&queue, start + u64::from(HALF), HALF);
let mut seen = Vec::new();
let advance = if pop_past_claim {
seen.push(owner.pop().expect("owner must retain half the values"));
assert_head(&queue, start + u64::from(HALF) + 1, HALF + 1);
u64::from(HALF) + 1
} else {
u64::from(HALF)
};
seen.extend(queue.finish_claim(claim));
assert_head(&queue, start + advance, 0);
seen.extend(drain(&mut owner));
seen.sort_unstable();
assert_eq!(seen, (0..CAPACITY).collect::<Vec<_>>());
}
}
}
#[test]
fn test_stale_head_matches_only_after_a_full_period() {
let head = |start: u64| {
let queue = Queue::at(start & REAL_MASK, false);
let mut owner = queue.owner();
owner.push_batch(0..2);
let head = queue.0.head.load(Ordering::Acquire);
drain(&mut owner);
head
};
let stale = head(3);
for count in [1, Widen::widen(CAPACITY), 1 << 32, 1 << 54, REAL_MASK] {
assert_ne!(head(3 + count), stale);
}
assert_eq!(head(3 + REAL_MASK + 1), stale);
}
#[test]
fn test_steal_takes_half_rounded_down() {
for available in [1usize, 2, 3, 7, 8, 255, 256] {
let source = Queue::shared();
let destination = Queue::shared();
let mut owner = source.owner();
let mut into = destination.owner();
owner.push_batch(0..available);
let count = available / 2;
assert_eq!(source.is_stealable(), count != 0);
assert_eq!(source.steal_into(&mut into), count);
assert_eq!(drain(&mut into), (0..count).collect::<Vec<_>>());
assert_eq!(drain(&mut owner), (count..available).collect::<Vec<_>>());
}
}
#[test]
fn test_steal_room_counts_a_pending_claim() {
let source = Queue::shared();
let destination = Queue::shared();
let mut owner = source.owner();
let mut into = destination.owner();
owner.push_batch(CAPACITY..2 * CAPACITY);
into.push_batch(0..CAPACITY);
let claim = destination.hold_claim(HALF);
let mut seen = drain(&mut into);
assert!(into.is_empty());
assert_eq!(into.room(), 0);
assert!(!destination.is_stealable());
assert_eq!(source.steal_into(&mut into), 0);
assert_eq!(owner.len(), CAPACITY);
seen.extend(destination.finish_claim(claim));
assert_eq!(into.room(), CAPACITY);
assert_eq!(source.steal_into(&mut into), CAPACITY / 2);
seen.extend(drain(&mut into));
seen.extend(drain(&mut owner));
seen.sort_unstable();
assert_eq!(seen, (0..2 * CAPACITY).collect::<Vec<_>>());
}
#[test]
fn test_steal_needs_half_the_destination_free() {
for (held, moved) in [
(Widen::widen(HALF), CAPACITY / 2),
(Widen::widen(HALF) + 1, 0),
] {
let source = Queue::shared();
let destination = Queue::shared();
let mut owner = source.owner();
let mut into = destination.owner();
owner.push_batch(0..CAPACITY);
into.push_batch(CAPACITY..CAPACITY + held);
assert_eq!(source.steal_into(&mut into), moved);
let mut seen = drain(&mut into);
seen.extend(drain(&mut owner));
seen.sort_unstable();
assert_eq!(seen, (0..CAPACITY + held).collect::<Vec<_>>());
}
}
#[test]
fn test_full_queue_with_a_claim_moves_only_the_new_value() {
let queue = Queue::shared();
let mut owner = queue.owner();
owner.push_batch(0..CAPACITY);
let claim = queue.hold_claim(HALF);
assert!(matches!(owner.push(CAPACITY), Push::Claimed(value) if value == CAPACITY));
assert!(!queue.is_stealable());
let mut seen = queue.finish_claim(claim);
seen.extend(drain(&mut owner));
assert_eq!(seen, (0..CAPACITY).collect::<Vec<_>>());
}
#[test]
fn test_second_thief_backs_off_a_pending_claim() {
let source = Queue::shared();
let destination = Queue::shared();
let mut owner = source.owner();
let mut into = destination.owner();
owner.push_batch(0..CAPACITY);
let claim = source.hold_claim(HALF);
assert_eq!(source.steal_into(&mut into), 0);
let mut seen = source.finish_claim(claim);
seen.extend(drain(&mut owner));
assert_eq!(seen, (0..CAPACITY).collect::<Vec<_>>());
}
#[test]
fn test_claim_records_its_count_until_released() {
let queue = Queue::shared();
let mut owner = queue.owner();
owner.push_batch(0..CAPACITY);
let claim = queue.claim().expect("full queue must be claimable");
assert_eq!(claim, (0, HALF));
assert_eq!(owner.len(), CAPACITY / 2);
assert_eq!(owner.room(), 0);
assert!(queue.claim().is_none());
assert!(!queue.is_stealable());
assert!(matches!(owner.push(CAPACITY), Push::Claimed(value) if value == CAPACITY));
let mut seen = queue.finish_claim(claim);
assert_eq!(owner.room(), CAPACITY / 2);
seen.extend(drain(&mut owner));
assert_eq!(seen, (0..CAPACITY).collect::<Vec<_>>());
}
#[test]
fn test_pending_claim_with_room_allows_a_push() {
let queue = Queue::shared();
let mut owner = queue.owner();
owner.push_batch(0..CAPACITY - 1);
let claim = queue.hold_claim(HALF);
assert!(matches!(owner.push(CAPACITY - 1), Push::Pushed(len) if len == CAPACITY / 2));
let mut seen = queue.finish_claim(claim);
seen.extend(drain(&mut owner));
assert_eq!(seen, (0..CAPACITY).collect::<Vec<_>>());
}
#[test]
fn test_claim_between_push_snapshot_and_exchange_retries() {
let queue = Queue::shared();
let mut owner = queue.owner();
owner.push_batch(0..CAPACITY);
let mut claim = None;
let mut attempts = 0;
match owner.push_with(CAPACITY, || {
attempts += 1;
if claim.is_none() {
claim = Some(queue.hold_claim(HALF));
}
}) {
Push::Claimed(value) => assert_eq!(value, CAPACITY),
_ => panic!("retry must hand back only the new value"),
}
assert_eq!(attempts, 2);
let mut seen = queue.finish_claim(claim.unwrap());
seen.extend(drain(&mut owner));
assert_eq!(seen, (0..CAPACITY).collect::<Vec<_>>());
}
#[test]
fn test_completed_steal_between_push_snapshot_and_exchange() {
let queue = Queue::shared();
let mut owner = queue.owner();
owner.push_batch(0..CAPACITY);
let (start_tx, start_rx) = mpsc::channel();
let (done_tx, done_rx) = mpsc::channel();
let thief = thread::spawn(move || {
let destination = Queue::shared();
let mut into = destination.owner();
start_rx.recv().unwrap();
assert_eq!(queue.steal_into(&mut into), CAPACITY / 2);
let values = drain(&mut into);
done_tx.send(()).unwrap();
values
});
let mut first = true;
let result = owner.push_with(CAPACITY, || {
if first {
first = false;
start_tx.send(()).unwrap();
done_rx.recv().unwrap();
}
});
assert!(matches!(result, Push::Pushed(len) if len == CAPACITY / 2 + 1));
drop(result);
let mut seen = thief.join().unwrap();
seen.extend(drain(&mut owner));
seen.sort_unstable();
assert_eq!(seen, (0..=CAPACITY).collect::<Vec<_>>());
}
#[test]
fn test_batch_asserts_room() {
let queue = Queue::<usize>::shared();
let mut owner = queue.owner();
owner.push_batch(0..CAPACITY - 1);
assert!(catch_unwind(AssertUnwindSafe(|| owner.push_batch(0..2))).is_err());
assert_eq!(drain(&mut owner), (0..CAPACITY - 1).collect::<Vec<_>>());
owner.push_batch(0..CAPACITY);
let claim = queue.hold_claim(HALF);
let mut seen = drain(&mut owner);
assert!(owner.is_empty());
assert!(
catch_unwind(AssertUnwindSafe(
|| owner.push_batch(std::iter::once(CAPACITY))
))
.is_err()
);
seen.extend(queue.finish_claim(claim));
seen.sort_unstable();
assert_eq!(seen, (0..CAPACITY).collect::<Vec<_>>());
}
#[test]
fn test_nonempty_owner_drop_panics() {
let queue = Queue::shared();
let mut owner = queue.owner();
push(&mut owner, 1);
let result = catch_unwind(AssertUnwindSafe(|| drop(owner)));
assert!(result.is_err());
assert_eq!(take_unowned(&queue), [1]);
}
#[test]
fn test_unexhausted_overflow_drop_panics() {
let queue = Queue::shared();
let mut owner = queue.owner();
owner.push_batch(0..CAPACITY);
let result = catch_unwind(AssertUnwindSafe(|| match owner.push(CAPACITY) {
Push::Overflow(batch, _) => drop(batch),
_ => panic!("full queue must overflow"),
}));
assert!(result.is_err());
assert_eq!(drain(&mut owner), (0..CAPACITY / 2).collect::<Vec<_>>());
}
#[test]
fn test_unwinding_skips_drop_assertions() {
assert!(
catch_unwind(|| {
let queue = Queue::shared();
let mut owner = queue.owner();
owner.push_batch(0..CAPACITY);
let _batch = match owner.push(CAPACITY) {
Push::Overflow(batch, _) => batch,
_ => panic!("full queue must overflow"),
};
panic!("unwind with an unexhausted overflow");
})
.is_err()
);
}
#[test]
fn test_second_owner_panics() {
let queue = Queue::<usize>::shared();
let _owner = queue.owner();
assert!(catch_unwind(AssertUnwindSafe(|| queue.owner())).is_err());
}
#[test]
fn test_self_steal_panics() {
let queue = Queue::<usize>::shared();
let mut owner = queue.owner();
assert!(catch_unwind(AssertUnwindSafe(|| queue.steal_into(&mut owner))).is_err());
}
#[test]
fn test_steal_from_an_exclusive_queue_panics() {
let source = Queue::exclusive();
let destination = Queue::shared();
let mut owner = source.owner();
let mut into = destination.owner();
owner.push_batch(0..4);
assert!(catch_unwind(AssertUnwindSafe(|| source.steal_into(&mut into))).is_err());
assert_eq!(drain(&mut owner), [0, 1, 2, 3]);
}
#[test]
fn test_owner_and_two_thieves_account_for_every_value_once() {
let total = if cfg!(miri) { 64 } else { 4096 };
let queue = Arc::new(Queue::<Counted>::shared());
let mut owner = queue.owner();
let done = Arc::new(AtomicBool::new(false));
let barrier = Arc::new(Barrier::new(3));
let drops = Arc::new(AtomicUsize::new(0));
let thieves: Vec<_> = (0..2)
.map(|_| {
let queue = queue.clone();
let done = done.clone();
let barrier = barrier.clone();
thread::spawn(move || {
let destination = Queue::shared();
let mut into = destination.owner();
let mut seen = Vec::new();
barrier.wait();
loop {
if queue.steal_into(&mut into) > 0 {
for value in drain(&mut into) {
seen.push(value.0);
}
} else if done.load(Ordering::Acquire) {
break;
} else {
thread::yield_now();
}
}
seen
})
})
.collect();
let mut seen = Vec::new();
barrier.wait();
for value in 0..total {
match owner.push(Counted(value, drops.clone())) {
Push::Pushed(_) => {}
Push::Claimed(value) => seen.push(value.0),
Push::Overflow(batch, value) => {
seen.extend(batch.map(|value| value.0));
seen.push(value.0);
}
}
if value % 3 == 0
&& let Some(value) = owner.pop()
{
seen.push(value.0);
}
}
done.store(true, Ordering::Release);
for thief in thieves {
seen.extend(thief.join().unwrap());
}
seen.extend(drain(&mut owner).into_iter().map(|value| value.0));
seen.sort_unstable();
assert_eq!(seen, (0..total).collect::<Vec<_>>());
assert_eq!(drops.load(Ordering::Relaxed), total);
}
#[test]
fn test_handles_are_send_and_the_owner_is_not_sync() {
fn send<T: Send>() {}
fn sync<T: Sync>() {}
trait AmbiguousIfSync<A> {
fn check() {}
}
impl<T: ?Sized> AmbiguousIfSync<()> for T {}
struct SyncMarker;
impl<T: ?Sized + Sync> AmbiguousIfSync<SyncMarker> for T {}
send::<Queue<Cell<usize>>>();
sync::<Queue<Cell<usize>>>();
send::<Owner<Cell<usize>>>();
let _ = <Owner<usize> as AmbiguousIfSync<_>>::check;
}
}
#[cfg(all(test, feature = "loom"))]
mod loom_tests {
use super::*;
use loom::thread;
fn drain<T>(owner: &mut Owner<T>) -> Vec<T> {
std::iter::from_fn(|| owner.pop()).collect()
}
fn collect_push(owner: &mut Owner<usize>, value: usize, seen: &mut Vec<usize>) {
match owner.push(value) {
Push::Pushed(_) => {}
Push::Claimed(value) => seen.push(value),
Push::Overflow(batch, value) => {
seen.extend(batch);
seen.push(value);
}
}
}
fn steal_once(queue: &Queue<usize>) -> Vec<usize> {
let destination = Queue::shared();
let mut into = destination.owner();
let moved = queue.steal_into(&mut into);
let seen = drain(&mut into);
assert_eq!(seen.len(), moved);
seen
}
#[test]
fn test_lone_pops_refill_and_overflow_across_the_maximum_against_a_thief() {
for start in [u64::from(u32::MAX) - 1, REAL_MASK - 3] {
let mut builder = loom::model::Builder::new();
builder.preemption_bound = Some(3);
builder.check(move || {
let queue = Queue::at(start, false);
let mut owner = queue.owner();
let thief = thread::spawn(move || {
let mut seen = steal_once(&queue);
seen.extend(steal_once(&queue));
seen
});
let mut seen = Vec::new();
for value in 0..2 {
collect_push(&mut owner, value, &mut seen);
seen.extend(owner.pop());
}
for value in 2..=CAPACITY + 2 {
collect_push(&mut owner, value, &mut seen);
}
seen.extend(drain(&mut owner));
seen.extend(thief.join().unwrap());
seen.sort_unstable();
assert_eq!(seen, (0..=CAPACITY + 2).collect::<Vec<_>>());
});
}
}
#[test]
fn test_owner_drops_while_a_thief_holds_a_claim() {
loom::model(|| {
let queue = Queue::at(u64::from(u32::MAX) - 1, false);
let mut owner = queue.owner();
owner.push_batch(0..CAPACITY);
let claim = queue.hold_claim(HALF);
let thief = thread::spawn(move || queue.finish_claim(claim));
let mut seen = drain(&mut owner);
drop(owner);
seen.extend(thief.join().unwrap());
seen.sort_unstable();
assert_eq!(seen, (0..CAPACITY).collect::<Vec<_>>());
});
}
#[test]
fn test_owner_pop_against_two_thieves() {
let mut builder = loom::model::Builder::new();
builder.preemption_bound = Some(2);
builder.check(|| {
let queue = Arc::new(Queue::shared());
let mut owner = queue.owner();
owner.push_batch(0..3);
let thieves: Vec<_> = (0..2)
.map(|_| {
let queue = queue.clone();
thread::spawn(move || steal_once(&queue))
})
.collect();
let mut seen = Vec::new();
seen.extend(owner.pop());
for thief in thieves {
seen.extend(thief.join().unwrap());
}
seen.extend(drain(&mut owner));
seen.sort_unstable();
assert_eq!(seen, vec![0, 1, 2]);
});
}
#[test]
fn test_push_against_steal_tracks_cells() {
loom::model(|| {
let queue = Queue::at(u64::from(u32::MAX) - 1, false);
let mut owner = queue.owner();
owner.push_batch(0..2);
let thief = thread::spawn(move || steal_once(&queue));
let mut seen = Vec::new();
seen.extend(owner.pop());
seen.extend(owner.pop());
collect_push(&mut owner, 2, &mut seen);
collect_push(&mut owner, 3, &mut seen);
seen.extend(thief.join().unwrap());
seen.extend(drain(&mut owner));
seen.sort_unstable();
assert_eq!(seen, vec![0, 1, 2, 3]);
});
}
#[test]
fn test_lone_value_pop_against_a_thief() {
let mut builder = loom::model::Builder::new();
builder.preemption_bound = Some(2);
builder.check(|| {
let queue = Queue::shared();
let mut owner = queue.owner();
let thief = thread::spawn(move || {
for _ in 0..2 {
assert!(!queue.is_stealable());
assert!(steal_once(&queue).is_empty());
}
});
assert!(matches!(owner.push(7), Push::Pushed(1)));
assert_eq!(owner.pop(), Some(7));
thief.join().unwrap();
assert!(owner.is_empty());
});
}
#[test]
fn test_full_overflow_against_a_thief() {
loom::model(|| {
let queue = Queue::shared();
let mut owner = queue.owner();
owner.push_batch(0..CAPACITY);
let thief = thread::spawn(move || steal_once(&queue));
let mut seen = Vec::new();
match owner.push_with(CAPACITY, thread::yield_now) {
Push::Pushed(_) => {}
Push::Claimed(value) => seen.push(value),
Push::Overflow(batch, value) => {
seen.extend(batch);
seen.push(value);
}
}
seen.extend(thief.join().unwrap());
seen.extend(drain(&mut owner));
seen.sort_unstable();
assert_eq!(seen, (0..=CAPACITY).collect::<Vec<_>>());
});
}
#[test]
fn test_stolen_values_are_published_and_slots_reused() {
let mut builder = loom::model::Builder::new();
builder.preemption_bound = Some(2);
builder.check(|| {
let sources: Vec<_> = (0..3).map(|_| Arc::new(Queue::shared())).collect();
let mut owners: Vec<_> = sources.iter().map(|queue| queue.owner()).collect();
for (index, owner) in owners.iter_mut().enumerate() {
owner.push_batch(index * CAPACITY..(index + 1) * CAPACITY);
}
let destination = Arc::new(Queue::shared());
let mut into = destination.owner();
into.push_batch(100..102);
let transferring = thread::spawn(move || {
for queue in sources {
let _ = queue.steal_into(&mut into);
}
into
});
let stealing = thread::spawn(move || {
let last = Queue::shared();
let mut owner = last.owner();
let mut seen = Vec::new();
for _ in 0..3 {
let _ = destination.steal_into(&mut owner);
seen.extend(drain(&mut owner));
}
seen
});
let mut seen = stealing.join().unwrap();
let mut into = transferring.join().unwrap();
seen.extend(drain(&mut into));
for owner in &mut owners {
seen.extend(drain(owner));
}
seen.sort_unstable();
let mut expected: Vec<_> = (0..3 * CAPACITY).collect();
expected.extend(100..102);
assert_eq!(seen, expected);
});
}
#[test]
fn test_batch_after_a_release_reuses_the_claimed_slots() {
loom::model(|| {
let queue = Queue::shared();
let mut owner = queue.owner();
owner.push_batch(0..CAPACITY);
let thief = thread::spawn(move || steal_once(&queue));
let mut seen = drain(&mut owner);
let room = owner.room();
owner.push_batch(CAPACITY..CAPACITY + room);
seen.extend(drain(&mut owner));
seen.extend(thief.join().unwrap());
seen.sort_unstable();
assert_eq!(seen, (0..CAPACITY + room).collect::<Vec<_>>());
});
}
}