use core::cell::UnsafeCell;
use core::marker::PhantomData;
use core::mem::MaybeUninit;
use core::ptr;
use kovan::{
Atomic, CachePadded, Guard, Reclaimable, RetiredNode, Shared as Ptr, alloc_node_uninit,
free_node, pin, retire_reclaimable,
};
use crate::sync::internal::{AtomicBool, AtomicU64, Ordering};
pub(super) const CELLS: u64 = 32;
const FAN_BITS: u32 = 6;
const FAN: usize = 1 << FAN_BITS;
const MAX_LEVEL: u32 = 10;
const WINDOW: u64 = 64;
const fn span(level: u32) -> u64 {
if level >= MAX_LEVEL {
u64::MAX
} else {
1 << (FAN_BITS * level)
}
}
struct Cell<T> {
ready: AtomicBool,
value: UnsafeCell<MaybeUninit<T>>,
}
#[repr(C)]
struct Header {
retired: RetiredNode,
free: unsafe fn(*mut Header),
}
unsafe impl Reclaimable for Header {
fn retired_node(&self) -> &RetiredNode {
&self.retired
}
fn retired_node_mut(&mut self) -> &mut RetiredNode {
&mut self.retired
}
unsafe fn dealloc(node: *mut Self) {
unsafe { ((*node).free)(node) }
}
}
#[repr(C)]
struct Leaf<T> {
header: Header,
cells: [Cell<T>; CELLS as usize],
}
#[repr(C)]
struct Inner {
header: Header,
level: u32,
children: [Atomic<Header>; FAN],
}
unsafe fn free_leaf<T>(header: *mut Header) {
unsafe { free_node(header.cast::<Leaf<T>>()) }
}
unsafe fn free_inner(header: *mut Header) {
unsafe { free_node(header.cast::<Inner>()) }
}
impl<T> Leaf<T> {
fn alloc() -> *mut Header {
let p = alloc_node_uninit::<Self>().cast::<Self>();
unsafe {
(&raw mut (*p).header).write(Header {
retired: RetiredNode::new(),
free: free_leaf::<T>,
});
let cells = (&raw mut (*p).cells).cast::<Cell<T>>();
for i in 0..CELLS as usize {
(&raw mut (*cells.add(i)).ready).write(AtomicBool::new(false));
}
}
p.cast()
}
}
impl Inner {
fn alloc(level: u32) -> *mut Header {
let p = alloc_node_uninit::<Self>().cast::<Self>();
unsafe {
(&raw mut (*p).header).write(Header {
retired: RetiredNode::new(),
free: free_inner,
});
(&raw mut (*p).level).write(level);
let children = (&raw mut (*p).children).cast::<Atomic<Header>>();
for i in 0..FAN {
children.add(i).write(Atomic::null());
}
}
p.cast()
}
}
struct Head<T> {
pos: AtomicU64,
leaf: UnsafeCell<(*mut Leaf<T>, u64)>,
ahead: AtomicU64,
}
pub(in crate::sync::mpsc) struct Radix<T> {
tail: CachePadded<AtomicU64>,
root: CachePadded<Atomic<Header>>,
head: CachePadded<Head<T>>,
_values: PhantomData<T>,
}
unsafe impl<T: Send> Send for Radix<T> {}
unsafe impl<T: Send> Sync for Radix<T> {}
impl<T> Radix<T> {
pub(in crate::sync::mpsc) fn new() -> Self {
Self::starting_at(0)
}
pub(super) fn starting_at(start: u64) -> Self {
let level = (1..MAX_LEVEL)
.find(|&level| start / CELLS < span(level))
.unwrap_or(MAX_LEVEL);
let queue = Self {
tail: CachePadded::new(AtomicU64::new(start)),
root: CachePadded::new(Atomic::new(Inner::alloc(level))),
head: CachePadded::new(Head {
pos: AtomicU64::new(start),
leaf: UnsafeCell::new((ptr::null_mut(), 0)),
ahead: AtomicU64::new(0),
}),
_values: PhantomData,
};
let guard = pin();
let first = queue.leaf(start / CELLS, true, &guard);
unsafe { *queue.head.leaf.get() = (first.cast_mut(), start / CELLS) };
drop(guard);
queue
}
#[inline]
pub(in crate::sync::mpsc) fn push(&self, value: T) {
let guard = pin();
let pos = self.tail.fetch_add(1, Ordering::SeqCst);
self.write(pos, value, &guard);
}
fn write(&self, pos: u64, value: T, guard: &Guard) {
let leaf = self.leaf(pos / CELLS, true, guard);
let cell = unsafe { &(*leaf).cells[(pos % CELLS) as usize] };
unsafe { cell.value.get().write(MaybeUninit::new(value)) };
cell.ready.store(true, Ordering::SeqCst);
}
fn leaf(&self, n: u64, make: bool, guard: &Guard) -> *const Leaf<T> {
let Some(mut node) = self.root_for(n, make, guard) else {
return ptr::null();
};
loop {
let inner = unsafe { &*node.cast::<Inner>() };
let level = inner.level;
let index = ((n >> (FAN_BITS * (level - 1))) as usize) & (FAN - 1);
let child = Self::child(&inner.children[index], level - 1, make, guard);
if child.is_null() || level == 1 {
return child.cast();
}
node = child;
}
}
fn root_for(&self, n: u64, make: bool, guard: &Guard) -> Option<*const Header> {
loop {
let root = self.root.load(Ordering::SeqCst, guard);
let level = unsafe { (*root.as_raw().cast::<Inner>()).level };
if n < span(level) {
return Some(root.as_raw());
}
if !make {
return None;
}
self.grow(root, level, guard);
}
}
#[cold]
#[inline(never)]
fn grow(&self, root: Ptr<'_, Header>, level: u32, guard: &Guard) {
let higher = Inner::alloc(level + 1);
unsafe { (*higher.cast::<Inner>()).children[0].store(root, Ordering::Relaxed) };
let higher_ptr = unsafe { Ptr::from_raw(higher) };
if self
.root
.compare_exchange(root, higher_ptr, Ordering::SeqCst, Ordering::SeqCst, guard)
.is_err()
{
unsafe { free_inner(higher) };
}
}
fn child(slot: &Atomic<Header>, level: u32, make: bool, guard: &Guard) -> *const Header {
let found = slot.load(Ordering::SeqCst, guard);
if !found.is_null() || !make {
return found.as_raw();
}
Self::make_child(slot, level, guard)
}
#[cold]
#[inline(never)]
fn make_child(slot: &Atomic<Header>, level: u32, guard: &Guard) -> *const Header {
let fresh = if level == 0 {
Leaf::<T>::alloc()
} else {
Inner::alloc(level)
};
let (fresh_ptr, none) = unsafe { (Ptr::from_raw(fresh), Ptr::from_raw(ptr::null_mut())) };
if slot
.compare_exchange(none, fresh_ptr, Ordering::SeqCst, Ordering::SeqCst, guard)
.is_err()
{
unsafe { ((*fresh).free)(fresh) };
}
slot.load(Ordering::SeqCst, guard).as_raw()
}
#[inline]
pub(in crate::sync::mpsc) unsafe fn try_pop(&self) -> Option<T> {
let head = self.head.pos.load(Ordering::Relaxed);
if let Some(cell) = unsafe { self.head_cell(head) } {
if cell.ready.load(Ordering::SeqCst) {
let value = unsafe { cell.value.get().read().assume_init() };
unsafe { self.taken(head, head) };
return Some(value);
}
}
unsafe { self.take_past(head) }
}
#[inline]
unsafe fn head_cell(&self, head: u64) -> Option<&Cell<T>> {
let (mut leaf, number) = unsafe { *self.head.leaf.get() };
if head / CELLS != number {
leaf = unsafe { self.move_head_leaf(head, number) }?;
}
Some(unsafe { &(*leaf).cells[(head % CELLS) as usize] })
}
#[inline(never)]
unsafe fn move_head_leaf(&self, head: u64, mut number: u64) -> Option<*mut Leaf<T>> {
let guard = pin();
let mut leaf = ptr::null_mut();
while head / CELLS != number {
let next = self.leaf(number + 1, false, &guard);
if next.is_null() {
return None;
}
unsafe { self.leave(number, &guard) };
(leaf, number) = (next.cast_mut(), number + 1);
unsafe { *self.head.leaf.get() = (leaf, number) };
}
Some(leaf)
}
#[inline(never)]
unsafe fn take_past(&self, head: u64) -> Option<T> {
let ahead = self.head.ahead.load(Ordering::Relaxed);
if ahead & 0b10 == 0 && !unsafe { self.next_ready(head) } {
return None;
}
let tail = self.tail.load(Ordering::SeqCst);
if tail <= head + 1 {
return None;
}
let guard = pin();
let ready = |pos: u64| {
let leaf = self.leaf(pos / CELLS, false, &guard);
(!leaf.is_null())
.then(|| unsafe { &(*leaf).cells[(pos % CELLS) as usize] })
.filter(|cell| cell.ready.load(Ordering::SeqCst))
};
let end = tail.min(head + WINDOW);
let found = (head + 1..end)
.find(|&pos| ahead & (1 << (pos - head)) == 0 && ready(pos).is_some())?;
let pos = (head..found)
.find(|&pos| (pos == head || ahead & (1 << (pos - head)) == 0) && ready(pos).is_some())
.unwrap_or(found);
let cell = ready(pos)?;
let value = unsafe { cell.value.get().read().assume_init() };
drop(guard);
unsafe { self.taken(head, pos) };
Some(value)
}
unsafe fn next_ready(&self, head: u64) -> bool {
let next = head + 1;
let (leaf, number) = unsafe { *self.head.leaf.get() };
let leaf = if next / CELLS == number {
leaf.cast_const()
} else {
self.leaf(next / CELLS, false, &pin())
};
!leaf.is_null()
&& unsafe { &(*leaf).cells[(next % CELLS) as usize] }
.ready
.load(Ordering::SeqCst)
}
unsafe fn taken(&self, head: u64, pos: u64) {
let mut ahead = self.head.ahead.load(Ordering::Relaxed);
if pos != head {
self.head
.ahead
.store(ahead | (1 << (pos - head)), Ordering::Relaxed);
return;
}
let mut next = head + 1;
ahead >>= 1;
while ahead & 1 != 0 {
next += 1;
ahead >>= 1;
}
self.head.ahead.store(ahead, Ordering::Relaxed);
self.head.pos.store(next, Ordering::Relaxed);
}
unsafe fn leave(&self, number: u64, guard: &Guard) {
let root = self.root.load(Ordering::SeqCst, guard).as_raw();
let mut node = root;
let mut ended: [*mut Header; MAX_LEVEL as usize + 1] =
[ptr::null_mut(); MAX_LEVEL as usize + 1];
let mut count = 0;
loop {
let inner = unsafe { &*node.cast::<Inner>() };
let level = inner.level;
let index = ((number >> (FAN_BITS * (level - 1))) as usize) & (FAN - 1);
let child = unsafe { inner.children[index].load_unprotected(Ordering::Acquire, guard) }
.as_raw();
if level == 1 {
ended[count] = child;
count += 1;
break;
}
if (number + 1).is_multiple_of(span(level - 1)) {
ended[count] = child;
count += 1;
}
node = child;
}
for &node in &ended[..count] {
unsafe { retire_reclaimable(node) };
}
}
pub(in crate::sync::mpsc) fn len(&self) -> usize {
let front = self.head.pos.load(Ordering::Acquire);
let ahead = u64::from(self.head.ahead.load(Ordering::Acquire).count_ones());
let back = self.tail.load(Ordering::Acquire);
usize::try_from(back.saturating_sub(front).saturating_sub(ahead)).unwrap_or(usize::MAX)
}
}
impl<T> Drop for Radix<T> {
fn drop(&mut self) {
let head = *self.head.pos.get_mut();
let ahead = *self.head.ahead.get_mut();
let tail = *self.tail.get_mut();
let (_, from) = *self.head.leaf.get_mut();
let guard = pin();
let root = unsafe { self.root.load_unprotected(Ordering::Relaxed, &guard) }.as_raw();
drop(guard);
unsafe { free_tree::<T>(root, 0, from, (head, ahead), tail) };
}
}
unsafe fn free_tree<T>(
node: *mut Header,
base: u64,
from: u64,
(head, ahead): (u64, u64),
tail: u64,
) {
let inner = unsafe { &*node.cast::<Inner>() };
let level = inner.level;
let child_span = span(level - 1);
let guard = pin();
for (i, slot) in inner.children.iter().enumerate() {
let child_base = base.saturating_add(child_span.saturating_mul(i as u64));
if child_base.saturating_add(child_span) <= from {
continue;
}
let child = unsafe { slot.load_unprotected(Ordering::Relaxed, &guard) }.as_raw();
if child.is_null() {
continue;
}
if level == 1 {
let leaf = unsafe { &mut *child.cast::<Leaf<T>>() };
let start = child_base * CELLS;
for pos in head.max(start)..tail.min(start + CELLS) {
let cell = &mut leaf.cells[(pos - start) as usize];
let taken_ahead = pos - head < WINDOW && ahead & (1 << (pos - head)) != 0;
if *cell.ready.get_mut() && !taken_ahead {
unsafe { cell.value.get_mut().assume_init_drop() };
}
}
unsafe { free_leaf::<T>(child) };
} else {
unsafe { free_tree::<T>(child, child_base, from, (head, ahead), tail) };
}
}
drop(guard);
unsafe { free_inner(node) };
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Arc;
use std::sync::atomic::AtomicUsize;
fn pop<T>(queue: &Radix<T>) -> Option<T> {
unsafe { queue.try_pop() }
}
#[test]
fn values_come_out_in_order_across_leaves() {
let queue = Radix::new();
for i in 0..CELLS * 5 {
queue.push(i);
}
assert_eq!(queue.len(), (CELLS * 5) as usize);
for i in 0..CELLS * 5 {
assert_eq!(pop(&queue), Some(i));
}
assert_eq!(pop(&queue), None);
assert_eq!(queue.len(), 0);
}
#[test]
fn a_queue_drained_at_each_leaf_end_goes_on_into_the_next() {
let queue = Radix::new();
for i in 0..CELLS * 3 {
queue.push(i);
assert_eq!(pop(&queue), Some(i));
assert_eq!(pop(&queue), None, "taken once");
}
}
#[test]
fn the_root_grows_as_positions_pass_its_span() {
let leaves_of_level_1 = span(1);
let start = (leaves_of_level_1 - 2) * CELLS;
let queue = Radix::starting_at(start);
let n = CELLS * 6;
for i in 0..n {
queue.push(start + i);
}
for i in 0..n {
assert_eq!(pop(&queue), Some(start + i));
}
let guard = pin();
let root = queue.root.load(Ordering::SeqCst, &guard).as_raw();
assert_eq!(unsafe { (*root.cast::<Inner>()).level }, 2);
drop(guard);
let far = span(4) * CELLS - 3;
let queue = Radix::starting_at(far);
for i in 0..8 {
queue.push(far + i);
}
for i in 0..8 {
assert_eq!(pop(&queue), Some(far + i));
}
}
#[test]
fn a_value_left_in_the_queue_is_dropped_once_with_it() {
struct Counted(Arc<AtomicUsize>);
impl Drop for Counted {
fn drop(&mut self) {
self.0.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
}
}
let drops = Arc::new(AtomicUsize::new(0));
let start = (span(1) - 1) * CELLS;
let queue = Radix::starting_at(start);
for _ in 0..CELLS * 2 + 6 {
queue.push(Counted(Arc::clone(&drops)));
}
for _ in 0..CELLS + 2 {
assert!(pop(&queue).is_some());
}
assert_eq!(
drops.load(std::sync::atomic::Ordering::SeqCst),
(CELLS + 2) as usize
);
drop(queue);
assert_eq!(
drops.load(std::sync::atomic::Ordering::SeqCst),
(CELLS * 2 + 6) as usize
);
}
fn stall<T>(queue: &Radix<T>) -> u64 {
queue.tail.fetch_add(1, Ordering::SeqCst)
}
fn finish<T>(queue: &Radix<T>, pos: u64, value: T) {
queue.write(pos, value, &pin());
}
#[test]
fn a_stalled_send_holds_back_no_one_else() {
let queue = Radix::new();
let stalled = stall(&queue);
queue.push(1);
queue.push(2);
assert_eq!(pop(&queue), Some(1));
assert_eq!(pop(&queue), Some(2));
assert_eq!(pop(&queue), None);
assert_eq!(queue.len(), 1, "the stalled position counts");
finish(&queue, stalled, 0);
assert_eq!(pop(&queue), Some(0));
assert_eq!(pop(&queue), None);
assert_eq!(queue.len(), 0);
queue.push(3);
assert_eq!(pop(&queue), Some(3));
}
#[test]
fn values_past_a_stall_come_out_across_leaves() {
let queue = Radix::new();
for i in 0..CELLS - 2 {
queue.push(i);
assert_eq!(pop(&queue), Some(i));
}
let stalled = stall(&queue);
for i in 0..40 {
queue.push(1000 + i);
}
for i in 0..40 {
assert_eq!(pop(&queue), Some(1000 + i));
}
assert_eq!(pop(&queue), None);
finish(&queue, stalled, 7);
assert_eq!(pop(&queue), Some(7));
for i in 0..CELLS * 3 {
queue.push(i);
}
for i in 0..CELLS * 3 {
assert_eq!(pop(&queue), Some(i));
}
assert_eq!(pop(&queue), None);
}
#[test]
fn two_stalls_in_a_row_hold_back_what_is_behind_them_until_one_is_written() {
let queue = Radix::new();
let first = stall(&queue);
let second = stall(&queue);
queue.push(2);
assert_eq!(pop(&queue), None, "behind two stalls");
finish(&queue, second, 1);
assert_eq!(pop(&queue), Some(1));
assert_eq!(pop(&queue), Some(2));
assert_eq!(pop(&queue), None);
finish(&queue, first, 0);
assert_eq!(pop(&queue), Some(0));
assert_eq!(pop(&queue), None);
assert_eq!(queue.len(), 0);
}
#[test]
fn values_past_the_window_wait_for_the_stall() {
let queue = Radix::new();
let stalled = stall(&queue);
for i in 1..100u64 {
queue.push(i);
}
for i in 1..WINDOW {
assert_eq!(pop(&queue), Some(i));
}
assert_eq!(pop(&queue), None, "past the window");
finish(&queue, stalled, 0);
assert_eq!(pop(&queue), Some(0));
for i in WINDOW..100 {
assert_eq!(pop(&queue), Some(i));
}
assert_eq!(pop(&queue), None);
}
#[test]
fn a_drop_after_values_taken_ahead_drops_the_rest_once() {
struct Counted(Arc<AtomicUsize>);
impl Drop for Counted {
fn drop(&mut self) {
self.0.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
}
}
let drops = Arc::new(AtomicUsize::new(0));
let queue = Radix::new();
let stalled = stall(&queue);
for _ in 0..5 {
queue.push(Counted(Arc::clone(&drops)));
}
for _ in 0..3 {
assert!(pop(&queue).is_some());
}
finish(&queue, stalled, Counted(Arc::clone(&drops)));
assert_eq!(drops.load(std::sync::atomic::Ordering::SeqCst), 3);
drop(queue);
assert_eq!(drops.load(std::sync::atomic::Ordering::SeqCst), 6);
}
#[test]
fn values_that_borrow_go_through() {
let words = [String::from("a"), String::from("b")];
let queue = Radix::new();
for _ in 0..CELLS * 2 + 1 {
queue.push(&words[0]);
queue.push(&words[1]);
}
let mut got = 0;
while let Some(w) = pop(&queue) {
assert!(w == "a" || w == "b");
got += 1;
}
assert_eq!(got, CELLS * 4 + 2);
drop(queue);
kovan::flush();
}
#[test]
fn concurrent_pushes_each_arrive_once_and_in_order() {
const PUSHERS: u64 = 4;
let each: u64 = if cfg!(miri) { 3 * CELLS } else { 20_000 };
let start = (span(1) - 1) * CELLS;
let queue = Arc::new(Radix::<(u64, u64)>::starting_at(start));
let pushers: Vec<_> = (0..PUSHERS)
.map(|p| {
let queue = Arc::clone(&queue);
std::thread::spawn(move || {
for i in 0..each {
queue.push((p, i));
}
})
})
.collect();
let mut next = [0u64; PUSHERS as usize];
let mut got = 0;
while got < PUSHERS * each {
match pop(&queue) {
Some((p, i)) => {
assert_eq!(i, next[p as usize], "pusher {p} out of order");
next[p as usize] += 1;
got += 1;
}
None => std::hint::spin_loop(),
}
}
for p in pushers {
p.join().unwrap();
}
assert_eq!(pop(&queue), None);
}
}