use core::cell::UnsafeCell;
use core::mem::MaybeUninit;
use core::ptr;
use core::sync::atomic::{AtomicUsize, Ordering};
#[repr(align(64))]
pub struct Queue<T, const N: usize> {
data: [UnsafeCell<MaybeUninit<T>>; N],
head: AtomicUsize,
tail: AtomicUsize,
}
pub struct Producer<'a, T, const N: usize> {
queue: &'a Queue<T, N>,
}
pub struct Consumer<'a, T, const N: usize> {
queue: &'a Queue<T, N>,
}
impl<T, const N: usize> Queue<T, N> {
pub const fn new() -> Self {
assert!(N > 0, "Queue size must be greater than 0");
assert!(N.is_power_of_two(), "Queue size must be a power of 2");
Self {
data: unsafe { MaybeUninit::uninit().assume_init() },
head: AtomicUsize::new(0),
tail: AtomicUsize::new(0),
}
}
#[inline]
pub const fn capacity(&self) -> usize {
N
}
#[inline]
pub fn len(&self) -> usize {
let head = self.head.load(Ordering::Acquire);
let tail = self.tail.load(Ordering::Acquire);
(head.wrapping_sub(tail)) & (N - 1)
}
#[inline]
pub fn is_empty(&self) -> bool {
let head = self.head.load(Ordering::Acquire);
let tail = self.tail.load(Ordering::Acquire);
head == tail
}
#[inline]
pub fn is_full(&self) -> bool {
let head = self.head.load(Ordering::Acquire);
let tail = self.tail.load(Ordering::Acquire);
((head + 1) & (N - 1)) == tail
}
pub fn split(&self) -> (Producer<'_, T, N>, Consumer<'_, T, N>) {
(Producer { queue: self }, Consumer { queue: self })
}
}
impl<'a, T, const N: usize> Producer<'a, T, N> {
pub fn push(&mut self, item: T) -> Result<(), QueueError> {
let head = self.queue.head.load(Ordering::Relaxed);
let next_head = (head + 1) & (N - 1);
if next_head == self.queue.tail.load(Ordering::Acquire) {
return Err(QueueError::Full);
}
unsafe {
let slot = &mut *self.queue.data[head].get();
ptr::write(slot.as_mut_ptr(), item);
}
self.queue.head.store(next_head, Ordering::Release);
Ok(())
}
pub fn try_push(&mut self, item: T) -> Result<(), T> {
let head = self.queue.head.load(Ordering::Relaxed);
let next_head = (head + 1) & (N - 1);
if next_head == self.queue.tail.load(Ordering::Acquire) {
return Err(item);
}
unsafe {
let slot = &mut *self.queue.data[head].get();
ptr::write(slot.as_mut_ptr(), item);
}
self.queue.head.store(next_head, Ordering::Release);
Ok(())
}
#[inline]
pub fn is_full(&self) -> bool {
self.queue.is_full()
}
#[inline]
pub fn len(&self) -> usize {
self.queue.len()
}
}
impl<'a, T, const N: usize> Consumer<'a, T, N> {
pub fn pop(&mut self) -> Result<T, QueueError> {
let tail = self.queue.tail.load(Ordering::Relaxed);
if tail == self.queue.head.load(Ordering::Acquire) {
return Err(QueueError::Empty);
}
let item = unsafe {
let slot = &*self.queue.data[tail].get();
ptr::read(slot.as_ptr())
};
let next_tail = (tail + 1) & (N - 1);
self.queue.tail.store(next_tail, Ordering::Release);
Ok(item)
}
#[inline]
pub fn is_empty(&self) -> bool {
self.queue.is_empty()
}
#[inline]
pub fn len(&self) -> usize {
self.queue.len()
}
}
unsafe impl<T: Send, const N: usize> Send for Queue<T, N> {}
unsafe impl<T: Send, const N: usize> Sync for Queue<T, N> {}
unsafe impl<T: Send, const N: usize> Send for Producer<'_, T, N> {}
unsafe impl<T: Send, const N: usize> Send for Consumer<'_, T, N> {}
impl<T, const N: usize> Drop for Queue<T, N> {
fn drop(&mut self) {
let mut consumer = Consumer { queue: self };
while consumer.pop().is_ok() {}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum QueueError {
Full,
Empty,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_queue() {
let queue = Queue::<i32, 8>::new();
assert_eq!(queue.capacity(), 8);
assert_eq!(queue.len(), 0);
assert!(queue.is_empty());
assert!(!queue.is_full());
}
#[test]
fn test_push_pop() {
let queue = Queue::<i32, 8>::new();
let (mut producer, mut consumer) = queue.split();
assert!(producer.push(42).is_ok());
assert_eq!(queue.len(), 1);
assert!(!queue.is_empty());
let value = consumer.pop().unwrap();
assert_eq!(value, 42);
assert_eq!(queue.len(), 0);
assert!(queue.is_empty());
}
#[test]
fn test_fifo_order() {
let queue = Queue::<i32, 8>::new();
let (mut producer, mut consumer) = queue.split();
for i in 0..5 {
producer.push(i).unwrap();
}
for i in 0..5 {
assert_eq!(consumer.pop().unwrap(), i);
}
}
#[test]
fn test_full_queue() {
let queue = Queue::<i32, 4>::new();
let (mut producer, _consumer) = queue.split();
for i in 0..3 {
assert!(producer.push(i).is_ok());
}
assert!(queue.is_full());
assert_eq!(producer.push(99), Err(QueueError::Full));
}
#[test]
fn test_empty_queue() {
let queue = Queue::<i32, 4>::new();
let (_producer, mut consumer) = queue.split();
assert_eq!(consumer.pop(), Err(QueueError::Empty));
}
#[test]
fn test_wraparound() {
let queue = Queue::<i32, 4>::new();
let (mut producer, mut consumer) = queue.split();
for cycle in 0..3 {
for i in 0..3 {
assert!(producer.push(cycle * 10 + i).is_ok());
}
for i in 0..3 {
let value = consumer.pop().unwrap();
assert_eq!(value, cycle * 10 + i);
}
}
}
#[test]
fn test_try_push() {
let queue = Queue::<i32, 4>::new();
let (mut producer, _consumer) = queue.split();
for i in 0..3 {
assert!(producer.try_push(i).is_ok());
}
match producer.try_push(99) {
Err(item) => assert_eq!(item, 99),
Ok(()) => panic!("Should have failed"),
}
}
#[test]
fn test_concurrent_access() {
use std::sync::Arc;
use std::thread;
use std::vec::Vec;
let queue = Arc::new(Queue::<i32, 1024>::new());
let queue_clone = queue.clone();
let producer_handle = thread::spawn(move || {
let (mut producer, _) = queue_clone.split();
for i in 0..1000 {
while producer.push(i).is_err() {
thread::yield_now();
}
}
});
let consumer_handle = thread::spawn(move || {
let (_, mut consumer) = queue.split();
let mut received = Vec::new();
while received.len() < 1000 {
match consumer.pop() {
Ok(value) => received.push(value),
Err(_) => thread::yield_now(),
}
}
received
});
producer_handle.join().unwrap();
let received = consumer_handle.join().unwrap();
for (i, &value) in received.iter().enumerate() {
assert_eq!(value, i as i32);
}
}
}