use std::sync::atomic::{AtomicU8, AtomicU64, Ordering};
use std::cell::UnsafeCell;
use std::mem::MaybeUninit;
use std::ptr::NonNull;
use std::pin::Pin;
use std::sync::Arc;
use linux_futex::Futex;
#[derive(Debug)]
pub struct Queue<T> {
inner: Pin<Arc<QueueInner<T>>>,
}
#[derive(Debug)]
pub struct WorkerQueue<T> {
queue: Pin<Arc<QueueInner<T>>>,
index: u8,
inner: NonNull<WorkerQueueInner<T>>,
}
unsafe impl<T> Send for WorkerQueue<T> { }
#[derive(Debug)]
struct QueueInner<T> {
workers: [WorkerQueueInner<T>; 64],
workers_count: AtomicU8,
workers_mask: AtomicU64,
}
#[derive(Debug)]
#[repr(align(64))]
struct WorkerQueueInner<T> {
value: UnsafeCell<MaybeUninit<T>>,
state: Futex<linux_futex::Private>,
}
unsafe impl<T> Sync for WorkerQueueInner<T> { }
const STATE_UNINIT: u32 = 0;
const STATE_SET: u32 = 1;
impl<T> Queue<T> {
pub fn new() -> Self {
Self {
inner: Arc::pin(QueueInner {
workers: [ const { WorkerQueueInner {
value: UnsafeCell::new(MaybeUninit::uninit()),
state: Futex::new(STATE_UNINIT),
} }; 64],
workers_count: AtomicU8::new(0),
workers_mask: AtomicU64::new(0),
}),
}
}
pub fn worker(&self) -> Option<WorkerQueue<T>> {
let index = self.inner.workers_count.fetch_add(1, Ordering::Relaxed);
if index as usize >= self.inner.workers.len() {
return None;
}
let inner = &raw const self.inner.workers[index as usize];
let inner = NonNull::new(inner.cast_mut()).unwrap();
Some(WorkerQueue {
queue: self.inner.clone(),
index,
inner,
})
}
pub fn send(&self, value: T) -> Result<(), T> {
let index = loop {
let mask = self.inner.workers_mask.load(Ordering::Relaxed);
if mask == 0 {
return Err(value);
}
let index = mask.trailing_zeros();
if self.inner.workers_mask.compare_exchange_weak(mask, mask & !(1 << index), Ordering::Relaxed, Ordering::Relaxed).is_ok() {
break index;
}
};
let worker = &self.inner.workers[index as usize];
unsafe {
worker.value.get().write(MaybeUninit::new(value));
}
worker.state.value.store(STATE_SET, Ordering::Release);
worker.state.wake(1);
Ok(())
}
}
impl<T> Clone for Queue<T> {
fn clone(&self) -> Self {
Self { inner: self.inner.clone() }
}
}
impl<T> WorkerQueue<T> {
pub fn recv(&mut self) -> T {
let inner = unsafe { self.inner.as_ref() };
self.queue.workers_mask.fetch_or(1 << self.index, Ordering::Relaxed);
let mut cycles = 0u32;
loop {
let state = inner.state.value.load(Ordering::Acquire);
if state == STATE_SET {
break;
}
cycles += 1;
if cycles <= 1000 {
for _ in 0..8 {
std::hint::spin_loop();
}
} else if inner.state.wait(state).is_err() {
break;
}
}
unsafe {
let val = inner.value.get().replace(MaybeUninit::uninit()).assume_init();
inner.state.value.store(STATE_UNINIT, Ordering::Release);
val
}
}
}