use std::marker::PhantomData;
use std::sync::Arc;
use std::sync::Mutex;
use std::sync::atomic::{AtomicBool, Ordering};
use crate::queue::{Consumer, Producer};
pub struct PerThreadConsumer {
pub(crate) consumer: Mutex<Consumer>,
pub(crate) alive: Arc<AtomicBool>,
}
pub struct PerThreadProducer {
pub(crate) producer: Producer,
pub(crate) alive: Arc<AtomicBool>,
_not_send: PhantomData<*mut ()>,
}
impl Drop for PerThreadProducer {
fn drop(&mut self) {
self.alive.store(false, Ordering::Release);
}
}
pub fn new(capacity: usize) -> (PerThreadProducer, PerThreadConsumer) {
let (producer, consumer) = crate::queue::new(capacity);
let alive = Arc::new(AtomicBool::new(true));
let pt_producer = PerThreadProducer {
producer,
alive: Arc::clone(&alive),
_not_send: PhantomData,
};
let pt_consumer = PerThreadConsumer {
consumer: Mutex::new(consumer),
alive,
};
(pt_producer, pt_consumer)
}
#[cfg(test)]
mod tests {
use std::sync::atomic::Ordering;
use super::*;
#[test]
fn alive_starts_true() {
let (producer, consumer) = new(64);
assert!(
producer.alive.load(Ordering::Acquire),
"producer.alive must be true after construction",
);
assert!(
consumer.alive.load(Ordering::Acquire),
"consumer.alive must be true after construction",
);
}
#[test]
fn both_halves_share_same_arc() {
let (producer, consumer) = new(64);
assert!(
Arc::ptr_eq(&producer.alive, &consumer.alive),
"producer and consumer must share the same Arc<AtomicBool>",
);
}
#[test]
fn drop_handle_sets_alive_false() {
let (producer, consumer) = new(64);
drop(producer);
assert!(
!consumer.alive.load(Ordering::Acquire),
"consumer.alive must be false after PerThreadProducer is dropped",
);
}
#[test]
#[expect(
clippy::significant_drop_tightening,
reason = "the guard is needed for all operations through the end of the test"
)]
fn queue_roundtrip_through_wrappers() {
let (mut producer, consumer) = new(64);
producer
.producer
.write(4, |buf| buf.copy_from_slice(&[1u8, 2, 3, 4]))
.expect("write must succeed on a fresh queue");
let mut consumer = consumer.consumer.lock().unwrap();
assert_eq!(
consumer.available(),
4,
"consumer must see 4 bytes after the write",
);
assert_eq!(
consumer.peek(4),
&[1u8, 2, 3, 4],
"peek must return the written bytes without consuming them",
);
assert_eq!(
consumer.available(),
4,
"available must still be 4 after peek",
);
let got = consumer.read(4, <[u8]>::to_vec);
assert_eq!(got, &[1u8, 2, 3, 4], "read must return the written bytes");
assert_eq!(
consumer.available(),
0,
"queue must be empty after consuming all bytes",
);
}
}