use core::mem::MaybeUninit;
use core::sync::atomic::Ordering::{Acquire, Relaxed, Release};
use crossbeam_utils::CachePadded;
use crate::{unprotected, Atomic, Guard, Owned, Shared};
#[derive(Debug)]
pub struct Queue<T> {
head: CachePadded<Atomic<Node<T>>>,
tail: CachePadded<Atomic<Node<T>>>,
}
struct Node<T> {
data: MaybeUninit<T>,
next: Atomic<Node<T>>,
}
unsafe impl<T: Send> Sync for Queue<T> {}
unsafe impl<T: Send> Send for Queue<T> {}
impl<T> Queue<T> {
pub fn new() -> Queue<T> {
let q = Queue {
head: CachePadded::new(Atomic::null()),
tail: CachePadded::new(Atomic::null()),
};
let sentinel = Owned::new(Node {
data: MaybeUninit::uninit(),
next: Atomic::null(),
});
unsafe {
let guard = unprotected();
let sentinel = sentinel.into_shared(guard);
q.head.store(sentinel, Relaxed);
q.tail.store(sentinel, Relaxed);
q
}
}
#[inline(always)]
fn push_internal(
&self,
onto: Shared<'_, Node<T>>,
new: Shared<'_, Node<T>>,
guard: &Guard,
) -> bool {
let o = unsafe { onto.deref() };
let next = o.next.load(Acquire, guard);
if unsafe { next.as_ref().is_some() } {
let _ = self.tail.compare_and_set(onto, next, Release, guard);
false
} else {
let result = o
.next
.compare_and_set(Shared::null(), new, Release, guard)
.is_ok();
if result {
let _ = self.tail.compare_and_set(onto, new, Release, guard);
}
result
}
}
pub fn push(&self, t: T, guard: &Guard) {
let new = Owned::new(Node {
data: MaybeUninit::new(t),
next: Atomic::null(),
});
let new = Owned::into_shared(new, guard);
loop {
let tail = self.tail.load(Acquire, guard);
if self.push_internal(tail, new, guard) {
break;
}
}
}
#[inline(always)]
fn pop_internal(&self, guard: &Guard) -> Result<Option<T>, ()> {
let head = self.head.load(Acquire, guard);
let h = unsafe { head.deref() };
let next = h.next.load(Acquire, guard);
match unsafe { next.as_ref() } {
Some(n) => unsafe {
self.head
.compare_and_set(head, next, Release, guard)
.map(|_| {
let tail = self.tail.load(Relaxed, guard);
if head == tail {
let _ = self.tail.compare_and_set(tail, next, Release, guard);
}
guard.defer_destroy(head);
Some(n.data.as_ptr().read())
})
.map_err(|_| ())
},
None => Ok(None),
}
}
#[inline(always)]
fn pop_if_internal<F>(&self, condition: F, guard: &Guard) -> Result<Option<T>, ()>
where
T: Sync,
F: Fn(&T) -> bool,
{
let head = self.head.load(Acquire, guard);
let h = unsafe { head.deref() };
let next = h.next.load(Acquire, guard);
match unsafe { next.as_ref() } {
Some(n) if condition(unsafe { &*n.data.as_ptr() }) => unsafe {
self.head
.compare_and_set(head, next, Release, guard)
.map(|_| {
let tail = self.tail.load(Relaxed, guard);
if head == tail {
let _ = self.tail.compare_and_set(tail, next, Release, guard);
}
guard.defer_destroy(head);
Some(n.data.as_ptr().read())
})
.map_err(|_| ())
},
None | Some(_) => Ok(None),
}
}
pub fn try_pop(&self, guard: &Guard) -> Option<T> {
loop {
if let Ok(head) = self.pop_internal(guard) {
return head;
}
}
}
pub fn try_pop_if<F>(&self, condition: F, guard: &Guard) -> Option<T>
where
T: Sync,
F: Fn(&T) -> bool,
{
loop {
if let Ok(head) = self.pop_if_internal(&condition, guard) {
return head;
}
}
}
}
impl<T> Drop for Queue<T> {
fn drop(&mut self) {
unsafe {
let guard = unprotected();
while self.try_pop(guard).is_some() {}
let sentinel = self.head.load(Relaxed, guard);
drop(sentinel.into_owned());
}
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::pin;
use crossbeam_utils::thread;
struct Queue<T> {
queue: super::Queue<T>,
}
impl<T> Queue<T> {
pub fn new() -> Queue<T> {
Queue {
queue: super::Queue::new(),
}
}
pub fn push(&self, t: T) {
let guard = &pin();
self.queue.push(t, guard);
}
pub fn is_empty(&self) -> bool {
let guard = &pin();
let head = self.queue.head.load(Acquire, guard);
let h = unsafe { head.deref() };
h.next.load(Acquire, guard).is_null()
}
pub fn try_pop(&self) -> Option<T> {
let guard = &pin();
self.queue.try_pop(guard)
}
pub fn pop(&self) -> T {
loop {
match self.try_pop() {
None => continue,
Some(t) => return t,
}
}
}
}
const CONC_COUNT: i64 = 1000000;
#[test]
fn push_try_pop_1() {
let q: Queue<i64> = Queue::new();
assert!(q.is_empty());
q.push(37);
assert!(!q.is_empty());
assert_eq!(q.try_pop(), Some(37));
assert!(q.is_empty());
}
#[test]
fn push_try_pop_2() {
let q: Queue<i64> = Queue::new();
assert!(q.is_empty());
q.push(37);
q.push(48);
assert_eq!(q.try_pop(), Some(37));
assert!(!q.is_empty());
assert_eq!(q.try_pop(), Some(48));
assert!(q.is_empty());
}
#[test]
fn push_try_pop_many_seq() {
let q: Queue<i64> = Queue::new();
assert!(q.is_empty());
for i in 0..200 {
q.push(i)
}
assert!(!q.is_empty());
for i in 0..200 {
assert_eq!(q.try_pop(), Some(i));
}
assert!(q.is_empty());
}
#[test]
fn push_pop_1() {
let q: Queue<i64> = Queue::new();
assert!(q.is_empty());
q.push(37);
assert!(!q.is_empty());
assert_eq!(q.pop(), 37);
assert!(q.is_empty());
}
#[test]
fn push_pop_2() {
let q: Queue<i64> = Queue::new();
q.push(37);
q.push(48);
assert_eq!(q.pop(), 37);
assert_eq!(q.pop(), 48);
}
#[test]
fn push_pop_many_seq() {
let q: Queue<i64> = Queue::new();
assert!(q.is_empty());
for i in 0..200 {
q.push(i)
}
assert!(!q.is_empty());
for i in 0..200 {
assert_eq!(q.pop(), i);
}
assert!(q.is_empty());
}
#[test]
fn push_try_pop_many_spsc() {
let q: Queue<i64> = Queue::new();
assert!(q.is_empty());
thread::scope(|scope| {
scope.spawn(|_| {
let mut next = 0;
while next < CONC_COUNT {
if let Some(elem) = q.try_pop() {
assert_eq!(elem, next);
next += 1;
}
}
});
for i in 0..CONC_COUNT {
q.push(i)
}
})
.unwrap();
}
#[test]
fn push_try_pop_many_spmc() {
fn recv(_t: i32, q: &Queue<i64>) {
let mut cur = -1;
for _i in 0..CONC_COUNT {
if let Some(elem) = q.try_pop() {
assert!(elem > cur);
cur = elem;
if cur == CONC_COUNT - 1 {
break;
}
}
}
}
let q: Queue<i64> = Queue::new();
assert!(q.is_empty());
thread::scope(|scope| {
for i in 0..3 {
let q = &q;
scope.spawn(move |_| recv(i, q));
}
scope.spawn(|_| {
for i in 0..CONC_COUNT {
q.push(i);
}
});
})
.unwrap();
}
#[test]
fn push_try_pop_many_mpmc() {
enum LR {
Left(i64),
Right(i64),
}
let q: Queue<LR> = Queue::new();
assert!(q.is_empty());
thread::scope(|scope| {
for _t in 0..2 {
scope.spawn(|_| {
for i in CONC_COUNT - 1..CONC_COUNT {
q.push(LR::Left(i))
}
});
scope.spawn(|_| {
for i in CONC_COUNT - 1..CONC_COUNT {
q.push(LR::Right(i))
}
});
scope.spawn(|_| {
let mut vl = vec![];
let mut vr = vec![];
for _i in 0..CONC_COUNT {
match q.try_pop() {
Some(LR::Left(x)) => vl.push(x),
Some(LR::Right(x)) => vr.push(x),
_ => {}
}
}
let mut vl2 = vl.clone();
let mut vr2 = vr.clone();
vl2.sort();
vr2.sort();
assert_eq!(vl, vl2);
assert_eq!(vr, vr2);
});
}
})
.unwrap();
}
#[test]
fn push_pop_many_spsc() {
let q: Queue<i64> = Queue::new();
thread::scope(|scope| {
scope.spawn(|_| {
let mut next = 0;
while next < CONC_COUNT {
assert_eq!(q.pop(), next);
next += 1;
}
});
for i in 0..CONC_COUNT {
q.push(i)
}
})
.unwrap();
assert!(q.is_empty());
}
#[test]
fn is_empty_dont_pop() {
let q: Queue<i64> = Queue::new();
q.push(20);
q.push(20);
assert!(!q.is_empty());
assert!(!q.is_empty());
assert!(q.try_pop().is_some());
}
}