use std::cell::UnsafeCell;
use std::mem::MaybeUninit;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::Arc;
pub use crate::TrySendError;
#[repr(align(64))]
struct CachePadded<T>(T);
struct Slot<T> {
seq: AtomicUsize,
val: UnsafeCell<MaybeUninit<T>>,
}
struct Queue<T> {
buffer: Box<[Slot<T>]>,
mask: usize,
enqueue_pos: CachePadded<AtomicUsize>,
dequeue_pos: CachePadded<AtomicUsize>,
producers: CachePadded<AtomicUsize>,
consumer_alive: CachePadded<AtomicBool>,
}
unsafe impl<T: Send> Send for Queue<T> {}
unsafe impl<T: Send> Sync for Queue<T> {}
impl<T> Queue<T> {
fn enqueue(&self, item: T) -> Result<(), TrySendError<T>> {
if !self.consumer_alive.0.load(Ordering::Acquire) {
return Err(TrySendError::Closed(item));
}
let mask = self.mask;
let mut pos = self.enqueue_pos.0.load(Ordering::Relaxed);
loop {
let slot = &self.buffer[pos & mask];
let seq = slot.seq.load(Ordering::Acquire);
let dif = (seq as isize).wrapping_sub(pos as isize);
if dif == 0 {
match self.enqueue_pos.0.compare_exchange_weak(
pos,
pos.wrapping_add(1),
Ordering::Relaxed,
Ordering::Relaxed,
) {
Ok(_) => {
unsafe { (*slot.val.get()).write(item) };
slot.seq.store(pos.wrapping_add(1), Ordering::Release);
return Ok(());
}
Err(actual) => pos = actual,
}
} else if dif < 0 {
if !self.consumer_alive.0.load(Ordering::Acquire) {
return Err(TrySendError::Closed(item));
}
return Err(TrySendError::Full(item));
} else {
pos = self.enqueue_pos.0.load(Ordering::Relaxed);
}
}
}
fn dequeue(&self) -> Option<T> {
let mask = self.mask;
let mut pos = self.dequeue_pos.0.load(Ordering::Relaxed);
loop {
let slot = &self.buffer[pos & mask];
let seq = slot.seq.load(Ordering::Acquire);
let dif = (seq as isize).wrapping_sub(pos.wrapping_add(1) as isize);
if dif == 0 {
match self.dequeue_pos.0.compare_exchange_weak(
pos,
pos.wrapping_add(1),
Ordering::Relaxed,
Ordering::Relaxed,
) {
Ok(_) => {
let item = unsafe { (*slot.val.get()).assume_init_read() };
slot.seq
.store(pos.wrapping_add(mask).wrapping_add(1), Ordering::Release);
return Some(item);
}
Err(actual) => pos = actual,
}
} else if dif < 0 {
return None; } else {
pos = self.dequeue_pos.0.load(Ordering::Relaxed);
}
}
}
fn is_empty(&self) -> bool {
let pos = self.dequeue_pos.0.load(Ordering::Relaxed);
let slot = &self.buffer[pos & self.mask];
let seq = slot.seq.load(Ordering::Acquire);
(seq as isize).wrapping_sub(pos.wrapping_add(1) as isize) < 0
}
}
impl<T> Drop for Queue<T> {
fn drop(&mut self) {
while self.dequeue().is_some() {}
}
}
pub struct Sender<T> {
q: Arc<Queue<T>>,
}
pub struct Receiver<T> {
q: Arc<Queue<T>>,
}
pub fn channel<T>(capacity: usize) -> (Sender<T>, Receiver<T>) {
let cap = capacity.max(1).next_power_of_two();
let mut buffer = Vec::with_capacity(cap);
for i in 0..cap {
buffer.push(Slot {
seq: AtomicUsize::new(i),
val: UnsafeCell::new(MaybeUninit::uninit()),
});
}
let q = Arc::new(Queue {
buffer: buffer.into_boxed_slice(),
mask: cap - 1,
enqueue_pos: CachePadded(AtomicUsize::new(0)),
dequeue_pos: CachePadded(AtomicUsize::new(0)),
producers: CachePadded(AtomicUsize::new(1)),
consumer_alive: CachePadded(AtomicBool::new(true)),
});
(Sender { q: q.clone() }, Receiver { q })
}
impl<T> Sender<T> {
pub fn try_send(&self, item: T) -> Result<(), TrySendError<T>> {
self.q.enqueue(item)
}
pub fn total_enqueued(&self) -> usize {
self.q.enqueue_pos.0.load(Ordering::Relaxed)
}
pub fn total_dequeued(&self) -> usize {
self.q.dequeue_pos.0.load(Ordering::Relaxed)
}
pub fn depth(&self) -> usize {
self.total_enqueued().saturating_sub(self.total_dequeued())
}
}
impl<T> Clone for Sender<T> {
fn clone(&self) -> Self {
self.q.producers.0.fetch_add(1, Ordering::Relaxed);
Sender { q: self.q.clone() }
}
}
impl<T> Drop for Sender<T> {
fn drop(&mut self) {
self.q.producers.0.fetch_sub(1, Ordering::Release);
}
}
impl<T> Receiver<T> {
pub fn try_recv(&self) -> Option<T> {
self.q.dequeue()
}
pub fn producers_alive(&self) -> bool {
self.q.producers.0.load(Ordering::Acquire) > 0
}
pub fn is_empty(&self) -> bool {
self.q.is_empty()
}
}
impl<T> Drop for Receiver<T> {
fn drop(&mut self) {
self.q.consumer_alive.0.store(false, Ordering::Release);
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::atomic::AtomicUsize;
#[test]
fn rounds_capacity_up_to_pow2() {
let (tx, rx) = channel::<u32>(3); for i in 0..4 {
assert!(tx.try_send(i).is_ok());
}
assert!(tx.try_send(99).is_err()); for i in 0..4 {
assert_eq!(rx.try_recv(), Some(i));
}
assert_eq!(rx.try_recv(), None);
}
#[test]
fn producers_alive_tracks_senders() {
let (tx, rx) = channel::<u8>(2);
assert!(rx.producers_alive());
let tx2 = tx.clone();
drop(tx);
assert!(rx.producers_alive()); drop(tx2);
assert!(!rx.producers_alive());
}
#[test]
fn is_empty_is_nondestructive() {
let (tx, rx) = channel::<u8>(4);
assert!(rx.is_empty());
tx.try_send(7).ok();
assert!(!rx.is_empty());
assert_eq!(rx.try_recv(), Some(7));
assert!(rx.is_empty());
}
#[test]
fn try_send_after_receiver_dropped_is_closed() {
let (tx, rx) = channel::<u32>(4);
drop(rx); match tx.try_send(1) {
Err(TrySendError::Closed(v)) => assert_eq!(v, 1), other => panic!("expected Closed, got {other:?}"),
}
}
#[test]
fn multi_producer_single_consumer_no_loss() {
const PRODUCERS: usize = 4;
const PER: usize = 50_000;
let (tx, rx) = channel::<usize>(1024);
let mut handles = vec![];
for p in 0..PRODUCERS {
let tx = tx.clone();
handles.push(std::thread::spawn(move || {
for i in 0..PER {
let mut item = p * PER + i;
loop {
match tx.try_send(item) {
Ok(()) => break,
Err(TrySendError::Full(v)) => {
item = v;
std::hint::spin_loop();
}
Err(TrySendError::Closed(v)) => {
item = v;
std::hint::spin_loop();
}
}
}
}
}));
}
drop(tx);
let total = PRODUCERS * PER;
let mut seen = vec![false; total];
let mut count = 0;
while count < total {
if let Some(v) = rx.try_recv() {
assert!(!seen[v], "duplicate {v}");
seen[v] = true;
count += 1;
} else {
std::hint::spin_loop();
}
}
for h in handles {
h.join().unwrap();
}
assert!(seen.iter().all(|&b| b), "some items lost");
assert_eq!(rx.try_recv(), None);
}
#[test]
fn drops_unconsumed_on_teardown() {
static DROPS: AtomicUsize = AtomicUsize::new(0);
struct Tracked;
impl Drop for Tracked {
fn drop(&mut self) {
DROPS.fetch_add(1, Ordering::SeqCst);
}
}
{
let (tx, _rx) = channel::<Tracked>(4);
tx.try_send(Tracked).ok();
tx.try_send(Tracked).ok();
}
assert_eq!(DROPS.load(Ordering::SeqCst), 2);
}
}