#![doc = include_str!("README.md")]
use crate::ring_buffer::RingBuffer;
use std::sync::{Arc, Condvar, Mutex};
struct Shared<T> {
buffer: RingBuffer<T>,
signal: Condvar,
lock: Mutex<()>,
}
pub struct Sender<T> {
shared: Arc<Shared<T>>,
}
pub struct Receiver<T> {
shared: Arc<Shared<T>>,
}
pub fn channel<T: Send>(capacity: usize) -> (Sender<T>, Receiver<T>) {
let shared = Arc::new(Shared {
buffer: RingBuffer::new(capacity),
signal: Condvar::new(),
lock: Mutex::new(()),
});
(
Sender {
shared: shared.clone(),
},
Receiver { shared },
)
}
impl<T> Sender<T> {
pub fn try_send(&self, item: T) -> Result<(), T> {
match self.shared.buffer.send(item) {
Ok(_) => {
self.shared.signal.notify_one();
Ok(())
}
Err(item) => Err(item),
}
}
pub fn send(&self, mut item: T) {
match self.shared.buffer.send(item) {
Ok(_) => {
self.shared.signal.notify_one();
return;
}
Err(returned_item) => {
item = returned_item;
}
}
let mut guard = self.shared.lock.lock().unwrap();
loop {
match self.shared.buffer.send(item) {
Ok(_) => {
self.shared.signal.notify_one();
return;
}
Err(returned_item) => {
item = returned_item;
guard = self.shared.signal.wait(guard).unwrap();
}
}
}
}
}
impl<T> Receiver<T> {
pub fn try_recv(&self) -> Option<T> {
match self.shared.buffer.recv() {
Some(item) => {
self.shared.signal.notify_one();
Some(item)
}
None => None,
}
}
pub fn recv(&self) -> Option<T> {
if let Some(item) = self.shared.buffer.recv() {
self.shared.signal.notify_one();
return Some(item);
}
let mut guard = self.shared.lock.lock().unwrap();
loop {
match self.shared.buffer.recv() {
Some(item) => {
self.shared.signal.notify_one();
return Some(item);
}
None => {
if Arc::strong_count(&self.shared) == 1 {
return None;
}
guard = self.shared.signal.wait(guard).unwrap();
}
}
}
}
}
impl<T> Drop for Sender<T> {
fn drop(&mut self) {
self.shared.signal.notify_one();
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::{thread, time::Duration};
#[test]
fn test_blocking_send_recv() {
let (tx, rx) = channel(1);
tx.send("hello");
let tx_clone = tx.shared.clone(); let _producer = thread::spawn(move || {
let sender = Sender { shared: tx_clone };
sender.send("world");
});
thread::sleep(Duration::from_millis(50));
assert_eq!(rx.recv(), Some("hello"));
assert_eq!(rx.recv(), Some("world"));
}
#[test]
fn test_blocking_recv() {
let (tx, rx) = channel(4);
let producer = thread::spawn(move || {
thread::sleep(Duration::from_millis(100));
tx.send(42);
});
let start = std::time::Instant::now();
let item = rx.recv();
let duration = start.elapsed();
assert_eq!(item, Some(42));
assert!(duration.as_millis() >= 90, "Did not block");
producer.join().unwrap();
}
#[test]
fn test_disconnection() {
let (tx, rx) = channel(4);
tx.send(1);
tx.send(2);
drop(tx);
assert_eq!(rx.recv(), Some(1));
assert_eq!(rx.recv(), Some(2));
assert_eq!(rx.recv(), None);
}
}