use std::ptr::null_mut;
use super::{atomic_try_update, bits::FlagU64, Atom, Node, NodeIterator};
pub trait Countable {
fn get_count(&self) -> u64;
}
struct CountingClaimHead<T: Countable> {
next: *mut Node<T>,
count_and_claim: FlagU64,
}
pub struct WriteOrderingQueue<T>
where
T: Send + Countable,
{
head: Atom<CountingClaimHead<T>, u128>,
}
impl<T> Default for WriteOrderingQueue<T>
where
T: Send + Countable,
{
fn default() -> WriteOrderingQueue<T> {
WriteOrderingQueue::<T> {
head: Atom::default(),
}
}
}
impl<T> WriteOrderingQueue<T>
where
T: Send + Countable,
{
pub fn push(&self, val: T) -> (u64, bool) {
let sz = val.get_count();
#[allow(unused_mut)]
let mut node = Box::into_raw(Box::new(Node {
val,
next: std::ptr::null_mut(),
}));
unsafe {
atomic_try_update(&self.head, |head: &mut CountingClaimHead<T>| {
(*node).next = head.next;
head.next = node;
let old_count = head.count_and_claim.get_val();
let have_claim = !head.count_and_claim.get_flag();
head.count_and_claim.set_val(old_count + sz);
head.count_and_claim.set_flag(true); (true, (old_count, have_claim))
})
}
}
pub fn consume_or_release_claim(&self) -> (NodeIterator<T>, bool) {
let (node, had_claim, claimed) = unsafe {
atomic_try_update(&self.head, |head| {
let ret = head.next;
let had_claim = head.count_and_claim.get_flag();
head.next = null_mut();
if ret.is_null() {
head.count_and_claim.set_flag(false);
(true, (ret, had_claim, false)) } else {
(true, (ret, had_claim, true))
}
})
};
assert!(
had_claim,
"cannot call consume_or_release_claim unless you have the claim!"
);
(NodeIterator::new(node).rev(), claimed)
}
pub fn get_offset(&self) -> u64 {
unsafe { atomic_try_update(&self.head, |head| (false, head.count_and_claim.get_val())) }
}
}