use core::ptr;
use std::marker::PhantomData;
use std::sync::atomic::{AtomicPtr, Ordering};
struct Node<T> {
next: *mut Node<T>,
value: T,
}
pub struct Mpsc<T> {
head: AtomicPtr<Node<T>>,
_owns: PhantomData<Box<Node<T>>>,
}
unsafe impl<T: Send> Send for Mpsc<T> {}
unsafe impl<T: Send> Sync for Mpsc<T> {}
impl<T> Default for Mpsc<T> {
fn default() -> Self {
Mpsc::new()
}
}
impl<T> std::fmt::Debug for Mpsc<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Mpsc").field("empty", &self.is_empty()).finish()
}
}
impl<T> Mpsc<T> {
pub const fn new() -> Mpsc<T> {
Mpsc { head: AtomicPtr::new(ptr::null_mut()), _owns: PhantomData }
}
pub fn push(&self, value: T) {
let node = Box::into_raw(Box::new(Node { next: ptr::null_mut(), value }));
let mut head = self.head.load(Ordering::Relaxed);
loop {
unsafe { (*node).next = head };
match self.head.compare_exchange_weak(head, node, Ordering::SeqCst, Ordering::Relaxed) {
Ok(_) => return,
Err(h) => head = h,
}
}
}
#[inline]
pub fn is_empty(&self) -> bool {
self.head.load(Ordering::SeqCst).is_null()
}
pub fn take(&self, into: &mut Vec<T>) {
if self.head.load(Ordering::Relaxed).is_null() {
return;
}
let mut p = self.head.swap(ptr::null_mut(), Ordering::Acquire);
let start = into.len();
while !p.is_null() {
let node = unsafe { Box::from_raw(p) };
p = node.next;
into.push(node.value);
}
into[start..].reverse();
}
}
impl<T> Drop for Mpsc<T> {
fn drop(&mut self) {
let mut p = *self.head.get_mut();
while !p.is_null() {
let node = unsafe { Box::from_raw(p) };
p = node.next;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Arc;
#[test]
fn values_come_out_in_push_order() {
let q = Mpsc::new();
let mut v = Vec::new();
q.take(&mut v);
assert!(v.is_empty() && q.is_empty());
for i in 0..5 {
q.push(i);
}
assert!(!q.is_empty());
v.push(-1);
q.take(&mut v);
assert_eq!(v, [-1, 0, 1, 2, 3, 4]);
assert!(q.is_empty());
}
#[test]
fn many_producers_lose_nothing_and_keep_their_own_order() {
let q = Arc::new(Mpsc::new());
let producers: Vec<_> = (0..8u64)
.map(|p| {
let q = q.clone();
std::thread::spawn(move || {
for i in 0..20_000u64 {
q.push((p, i));
}
})
})
.collect();
let mut seen = vec![0u64; 8];
let mut got = 0;
let mut buf = Vec::new();
while got < 8 * 20_000 {
q.take(&mut buf);
for (p, i) in buf.drain(..) {
assert_eq!(seen[p as usize], i, "producer {p} out of order");
seen[p as usize] += 1;
got += 1;
}
}
for t in producers {
t.join().unwrap();
}
assert!(q.is_empty());
}
#[test]
fn dropping_a_full_queue_drops_its_values() {
let marker = Arc::new(());
let q = Mpsc::new();
for _ in 0..10 {
q.push(marker.clone());
}
assert_eq!(Arc::strong_count(&marker), 11);
drop(q);
assert_eq!(Arc::strong_count(&marker), 1);
}
}