use core::ptr;
use core::sync::atomic::{AtomicUsize, Ordering};
use kovan_queue::seg_queue::SegQueue;
use crate::sync::internal::{AtomicPtr, take_ptr};
pub(crate) struct Counted<T> {
queue: SegQueue<T>,
count: AtomicUsize,
}
impl<T> Counted<T> {
pub(crate) fn new() -> Self {
Self {
queue: SegQueue::new(),
count: AtomicUsize::new(0),
}
}
pub(crate) fn push(&self, item: T) {
self.queue.push(item);
self.count.fetch_add(1, Ordering::Release);
}
#[inline(always)]
pub(crate) fn is_empty(&self) -> bool {
self.count.load(Ordering::Relaxed) == 0
}
pub(crate) fn holds_any(&self) -> bool {
!self.queue.is_empty()
}
#[cfg(test)]
pub(crate) fn len(&self) -> usize {
self.queue.len()
}
pub(crate) fn take_all(&self, mut take: impl FnMut(T)) {
let counted = self.count.load(Ordering::Acquire);
let mut taken = 0;
while taken < counted {
let Ok(item) = self.queue.try_pop() else {
break;
};
taken += 1;
take(item);
}
if taken > 0 {
self.count.fetch_sub(taken, Ordering::Relaxed);
}
}
}
pub(crate) struct LazyCounted<T> {
queue: AtomicPtr<Counted<T>>,
}
impl<T> LazyCounted<T> {
pub(crate) fn new() -> Self {
Self {
queue: AtomicPtr::new(ptr::null_mut()),
}
}
#[inline(always)]
fn made(&self) -> Option<&Counted<T>> {
unsafe { self.queue.load(Ordering::Acquire).as_ref() }
}
pub(crate) fn push(&self, item: T) {
match self.made() {
Some(queue) => queue.push(item),
None => self.make().push(item),
}
}
#[cold]
#[inline(never)]
fn make(&self) -> &Counted<T> {
let fresh = Box::into_raw(Box::new(Counted::new()));
match self.queue.compare_exchange(
ptr::null_mut(),
fresh,
Ordering::AcqRel,
Ordering::Acquire,
) {
Ok(_) => unsafe { &*fresh },
Err(made) => {
unsafe {
drop(Box::from_raw(fresh));
&*made
}
}
}
}
#[inline(always)]
pub(crate) fn is_empty(&self) -> bool {
self.made().is_none_or(Counted::is_empty)
}
#[inline(always)]
pub(crate) fn take_all(&self, take: impl FnMut(T)) {
if let Some(queue) = self.made() {
queue.take_all(take);
}
}
}
impl<T> Drop for LazyCounted<T> {
fn drop(&mut self) {
let queue = take_ptr(&mut self.queue);
if !queue.is_null() {
drop(unsafe { Box::from_raw(queue) });
}
}
}
#[cfg(all(test, not(loom)))]
mod tests;
#[cfg(all(test, not(loom)))]
mod tests_lazy;