use std::{
mem::size_of,
ptr::{self, NonNull, write_bytes},
sync::atomic::{
AtomicPtr,
Ordering::{AcqRel, Acquire, Release},
},
};
use super::{CachedBuf, NUM_CLASSES};
#[repr(C)]
pub(crate) struct FreeNode {
pub(crate) next: *mut FreeNode,
pub(crate) cap: usize,
pub(crate) align: usize,
pub(crate) cacheable: bool,
pub(crate) dirty: bool,
}
pub(crate) const SEALED: *mut FreeNode = usize::MAX as *mut FreeNode;
#[repr(align(64))]
pub(crate) struct Head(AtomicPtr<FreeNode>);
pub(crate) struct CrossThreadInbox {
heads: [Head; NUM_CLASSES],
}
impl CrossThreadInbox {
pub(crate) fn new() -> Self {
Self {
heads: [const { Head(AtomicPtr::new(ptr::null_mut())) }; NUM_CLASSES],
}
}
pub(crate) fn try_push(&self, cls: usize, node: *mut FreeNode) -> bool {
let head_ptr = &self.heads[cls].0;
let mut head = head_ptr.load(Acquire);
loop {
if head == SEALED {
return false;
}
unsafe { (*node).next = head };
match head_ptr.compare_exchange_weak(head, node, Release, Acquire) {
Ok(_) => return true,
Err(actual) => head = actual,
}
}
}
pub(crate) fn claim(&self, cls: usize) -> *mut FreeNode {
let head_ptr = &self.heads[cls].0;
let mut head = head_ptr.load(Acquire);
loop {
if head.is_null() || head == SEALED {
return ptr::null_mut();
}
match head_ptr.compare_exchange_weak(head, ptr::null_mut(), AcqRel, Acquire) {
Ok(_) => return head,
Err(actual) => head = actual,
}
}
}
pub(crate) fn seal_and_drain(&self, cls: usize) -> *mut FreeNode {
self.heads[cls].0.swap(SEALED, AcqRel)
}
}
pub(crate) struct ChainIter {
curr: *mut FreeNode,
}
impl ChainIter {
#[inline]
pub(crate) const fn new(curr: *mut FreeNode) -> Self {
Self { curr }
}
}
impl Iterator for ChainIter {
type Item = CachedBuf;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if self.curr.is_null() || self.curr == SEALED {
return None;
}
unsafe {
let node = self.curr;
let next = (*node).next;
let cap = (*node).cap;
let align = (*node).align;
let cacheable = (*node).cacheable;
let dirty = (*node).dirty;
let ptr = NonNull::new_unchecked(node as *mut u8);
if !dirty {
write_bytes(node as *mut u8, 0, size_of::<FreeNode>());
}
self.curr = next;
Some(CachedBuf {
ptr,
cap,
align,
cacheable,
dirty,
})
}
}
}
#[cfg(test)]
mod tests {
use std::mem::{offset_of, size_of};
use super::{CrossThreadInbox, Head};
#[test]
fn class_heads_are_on_separate_cache_lines() {
assert_eq!(size_of::<Head>(), 64, "单个 class 栈顶必须独占 64B 缓存行");
assert_eq!(size_of::<[Head; 4]>(), 256);
assert_eq!(offset_of!(CrossThreadInbox, heads), 0);
}
}