use crossbeam::epoch::{pin, Atomic, Guard, Owned, Shared};
use std::sync::atomic::Ordering;
use std::sync::Arc;
pub struct Sender<T> {
inner_tx: Arc<Atomic<T>>,
}
#[derive(Clone)]
pub struct Receiver<T> {
inner_rx: Arc<Atomic<T>>,
}
impl<T> Drop for Sender<T> {
fn drop(&mut self) {
let guard = pin();
let v = self.inner_tx.swap(Shared::null(), Ordering::SeqCst, &guard);
if !v.is_null() {
unsafe {
guard.defer_destroy(v);
}
}
guard.flush();
}
}
pub fn new<T>(v: T) -> (Sender<T>, Receiver<T>) {
let inner = Arc::new(Atomic::new(v));
let tx = Sender {
inner_tx: inner.clone(),
};
let rx = Receiver { inner_rx: inner };
(tx, rx)
}
pub fn empty<T>() -> (Sender<T>, Receiver<T>) {
let inner = Arc::new(Atomic::null());
let tx = Sender {
inner_tx: inner.clone(),
};
let rx = Receiver { inner_rx: inner };
(tx, rx)
}
impl<T> Sender<T> {
pub fn send(&mut self, v: T) {
let guard = pin();
let prev = self.inner_tx.swap(Owned::new(v), Ordering::Release, &guard);
if !prev.is_null() {
unsafe {
guard.defer_destroy(prev);
}
}
}
}
pub struct Borrow<T> {
_guard: Guard,
shared: *const T,
}
impl<T> std::ops::Deref for Borrow<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { self.shared.as_ref().unwrap() }
}
}
impl<T> Receiver<T> {
pub fn borrow(&self) -> Option<Borrow<T>> {
let guard = pin();
let shared = self.inner_rx.load_consume(&guard).as_raw(); if shared.is_null() {
None
} else {
Some(Borrow {
_guard: guard,
shared,
})
}
}
}
impl<T: Clone> Receiver<T> {
pub fn recv(&self) -> Option<T> {
let guard = pin();
let v = self.inner_rx.load_consume(&guard); let inner_ref = unsafe { v.as_ref() };
match inner_ref {
Some(b) => Some(b.clone()),
None => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use rand::Rng;
#[test]
fn single_thread_reader_writer() {
let (mut tx, rx) = new::<u32>(8);
assert_eq!(rx.recv(), Some(8));
tx.send(10);
assert_eq!(rx.recv(), Some(10));
}
#[test]
fn empty_after_drop() {
let (tx, rx) = new::<u32>(42);
assert_eq!(rx.recv(), Some(42));
drop(tx);
assert_eq!(rx.recv(), None);
}
#[test]
fn borrow_after_drop() {
let (tx, rx) = new::<u32>(42);
let b = match rx.borrow() {
Some(s) => s,
None => panic!("Empty borrow after init"),
};
drop(tx);
assert_eq!(rx.recv(), None);
assert_eq!(*b, 42);
}
#[test]
fn one_writer_one_reader_random_waits() {
let (mut tx, rx) = new::<u32>(0);
let t = std::thread::spawn(move || {
let mut count = 0;
let ten_millis = std::time::Duration::from_millis(10);
for _n in 0..50 {
count = count + 1;
tx.send(count);
std::thread::sleep(ten_millis);
}
});
let mut rng = rand::thread_rng();
loop {
let v = rx.recv();
if v == Some(50) || v == None {
break;
}
let wait_time: u64 = rng.gen_range(0..50);
let rand_millis = std::time::Duration::from_millis(wait_time);
std::thread::sleep(rand_millis);
}
t.join().expect("writer didn't close cleanly");
}
#[test]
fn one_writer_one_reader_borrows() {
let (mut tx, rx) = new::<u32>(0);
let t = std::thread::spawn(move || {
let mut count = 0;
let ten_millis = std::time::Duration::from_millis(10);
for _n in 0..50 {
count = count + 1;
tx.send(count);
std::thread::sleep(ten_millis);
}
});
let mut rng = rand::thread_rng();
loop {
match rx.borrow() {
Some(b) if *b == 50 => break,
None => break,
_ => (),
}
let wait_time: u64 = rng.gen_range(0..50);
let rand_millis = std::time::Duration::from_millis(wait_time);
std::thread::sleep(rand_millis);
}
t.join().expect("writer didn't close cleanly");
}
}