use std::marker::PhantomData;
use std::mem::MaybeUninit;
use std::ops::Deref;
use container_of::container_of;
use urcu_cds_sys::lfq;
use crate::rcu::flavor::RcuFlavor;
use crate::utility::*;
pub struct RawNode<T> {
handle: lfq::NodeRcu,
data: T,
}
impl<T> RawNode<T> {
pub fn new(data: T) -> Box<Self> {
let mut handle = MaybeUninit::<lfq::NodeRcu>::uninit();
unsafe { lfq::node_init_rcu(handle.as_mut_ptr()) };
Box::new(Self {
handle: unsafe { handle.assume_init() },
data,
})
}
fn into_handle(self: Box<Self>) -> *mut lfq::NodeRcu {
let node_ptr = Box::into_raw(self);
let node = unsafe { node_ptr.as_mut_unchecked() };
&mut node.handle
}
}
impl<T> Deref for RawNode<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.data
}
}
unsafe impl<T: Send> Send for RawNode<T> {}
unsafe impl<T: Sync> Sync for RawNode<T> {}
pub struct RawQueue<T, F> {
handle: lfq::QueueRcu,
_unsend: PhantomUnsend<(T, F)>,
_unsync: PhantomUnsync<(T, F)>,
}
impl<T, F> RawQueue<T, F> {
pub unsafe fn new() -> Self {
Self {
handle: lfq::QueueRcu {
head: std::ptr::null_mut(),
tail: std::ptr::null_mut(),
queue_call_rcu: None,
},
_unsend: PhantomData,
_unsync: PhantomData,
}
}
pub unsafe fn init(&mut self)
where
F: RcuFlavor,
{
unsafe { lfq::init_rcu(&mut self.handle, F::unchecked_rcu_api().update_call_rcu) };
}
pub unsafe fn enqueue(&self, node: Box<RawNode<T>>) {
let handle = &self.handle as *const lfq::QueueRcu as *mut lfq::QueueRcu;
unsafe { lfq::enqueue_rcu(handle, node.into_handle()) }
}
pub unsafe fn dequeue(&self) -> *mut RawNode<T> {
let handle = &self.handle as *const lfq::QueueRcu as *mut lfq::QueueRcu;
let handle = unsafe { lfq::dequeue_rcu(handle) };
if handle.is_null() {
std::ptr::null_mut()
} else {
container_of!(handle, RawNode<T>, handle)
}
}
pub unsafe fn dequeue_all(&self) -> Vec<*mut RawNode<T>> {
let mut ptrs = Vec::new();
loop {
let ptr = self.dequeue();
if ptr.is_null() {
break;
}
ptrs.push(ptr);
}
ptrs
}
}
impl<T, F> Drop for RawQueue<T, F> {
fn drop(&mut self) {
let ret = unsafe { lfq::destroy_rcu(&mut self.handle) };
if ret != 0 {
log::error!("raw queue was not emptied before dropping");
}
}
}