#[cfg(test)]
mod usage_test {
use simple_left_right::{Absorb, WriteGuard, Writer};
use std::{cell::Cell, hint, time::Duration};
fn spin_lock<T: Absorb<O>, O>(writer: &mut Writer<T, O>) -> WriteGuard<'_, T, O> {
while writer.try_lock().is_none() {
hint::spin_loop();
}
writer.try_lock().unwrap()
}
#[derive(Clone)]
pub struct CounterAddOp(i32);
impl Absorb<CounterAddOp> for i32 {
fn absorb(&mut self, operation: CounterAddOp) {
*self += operation.0;
}
}
impl Absorb<CounterAddOp> for Cell<i32> {
fn absorb(&mut self, operation: CounterAddOp) {
self.set(self.get() + operation.0);
}
}
#[test]
fn send_writer() {
let mut writer = Writer::new(0);
let (send, rcv) = std::sync::mpsc::channel::<Writer<i32, CounterAddOp>>();
std::thread::spawn(move || {
let writer = rcv.recv().unwrap();
assert_eq!(*writer.read(), 1);
});
writer.try_lock().unwrap().apply_op(CounterAddOp(1));
send.send(writer).unwrap();
}
#[test]
fn write_guard_drop() {
let mut writer = Writer::new(0);
let mut reader = writer.build_reader().unwrap();
assert_eq!(*reader.lock(), 0);
let mut write_lock = writer.try_lock().unwrap();
assert_eq!(*write_lock.read(), 0);
write_lock.apply_op(CounterAddOp(2));
assert_eq!(*write_lock.read(), 2);
drop(write_lock);
let write_lock = writer.try_lock().unwrap();
assert_eq!(*write_lock.read(), 2);
write_lock.swap();
assert_eq!(*reader.lock(), 2);
assert_eq!(*writer.try_lock().unwrap().read(), 2);
}
#[test]
fn write_try_lock() {
let mut writer = Writer::new(0);
let mut lock = writer.try_lock().unwrap();
lock.apply_op(CounterAddOp(1));
assert_eq!(*lock.read(), 1);
std::mem::drop(lock);
assert_eq!(*writer.try_lock().unwrap().read(), 1);
}
#[test]
fn writer_as_ref() {
let mut writer = Writer::new(0);
assert_eq!(*writer.read(), 0);
writer.try_lock().unwrap().apply_op(CounterAddOp(3));
assert_eq!(*writer.read(), 3);
}
#[test]
fn single_thread() {
let mut writer = Writer::new(0);
let mut reader = writer.build_reader().unwrap();
let read_lock = reader.lock();
assert_eq!(*read_lock, 0);
let mut write_lock = writer.try_lock().unwrap();
write_lock.apply_op(CounterAddOp(1));
assert_eq!(*read_lock, 0);
drop(read_lock);
let read_lock = reader.lock();
assert_eq!(*read_lock, 0);
write_lock.swap();
assert!(writer.try_lock().is_none());
drop(read_lock);
let read_lock = reader.lock();
assert_eq!(*read_lock, 1);
let write_lock = writer.try_lock().unwrap();
write_lock.swap();
drop(read_lock);
let read_lock = reader.lock();
assert_eq!(*read_lock, 1);
}
#[test]
fn block() {
let mut writer = Writer::new(0);
let mut reader = writer.build_reader().unwrap();
writer.try_lock().unwrap().apply_op(CounterAddOp(2));
std::thread::spawn(move || {
let lock = reader.lock();
assert_eq!(*lock, 0);
std::thread::sleep(Duration::from_secs(2));
drop(lock);
assert_eq!(*reader.lock(), 2);
});
std::thread::sleep(Duration::from_secs(1));
writer.try_lock().unwrap().swap();
let write_lock = spin_lock(&mut writer);
assert_eq!(*write_lock.read(), 2);
}
#[test]
fn multi_thread() {
let mut writer = Writer::new(0);
let mut reader = writer.build_reader().unwrap();
let mut write_lock = writer.try_lock().unwrap();
let thread = std::thread::spawn(move || {
let read_lock = reader.lock();
assert_eq!(*read_lock, 0);
std::thread::sleep(Duration::from_secs(3));
assert_eq!(*read_lock, 0);
drop(read_lock);
});
std::thread::sleep(Duration::from_secs(1));
write_lock.apply_op(CounterAddOp(1));
write_lock.swap();
assert!(writer.try_lock().is_none());
thread.join().unwrap();
let _write_lock = writer.try_lock().unwrap();
}
#[test]
fn double_reader() {
let mut writer: Writer<i32, CounterAddOp> = Writer::new(0);
let _reader = writer.build_reader().unwrap();
assert!(writer.build_reader().is_none());
}
#[test]
#[cfg_attr(miri, ignore)]
fn reader_leak() {
let mut writer: Writer<i32, CounterAddOp> = Writer::new(0);
let reader = writer.build_reader().unwrap();
drop(reader);
let reader = writer.build_reader().unwrap();
std::mem::forget(reader);
assert!(writer.build_reader().is_none());
}
#[test]
fn reader_rebuild() {
let mut writer: Writer<i32, CounterAddOp> = Writer::default();
let reader = writer.build_reader().unwrap();
drop(reader);
let _reader = writer.build_reader().unwrap();
}
#[test]
fn default_init() {
let mut writer: Writer<i32, CounterAddOp> = Writer::default();
let mut reader = writer.build_reader().unwrap();
assert_eq!(*reader.lock(), i32::default());
assert_eq!(*writer.read(), i32::default());
}
#[test]
#[should_panic]
fn panic_default() {
struct PanicOnDefault {}
impl Default for PanicOnDefault {
fn default() -> Self {
panic!()
}
}
impl Absorb<()> for PanicOnDefault {
fn absorb(&mut self, _: ()) {
panic!()
}
}
let mut writer: Writer<PanicOnDefault, ()> = Writer::default();
writer.try_lock().unwrap();
}
#[test]
#[should_panic]
fn panic_clone() {
struct PanicOnClone {}
impl Clone for PanicOnClone {
fn clone(&self) -> Self {
panic!()
}
}
impl Absorb<()> for PanicOnClone {
fn absorb(&mut self, _: ()) {
panic!()
}
}
let mut writer: Writer<PanicOnClone, ()> = Writer::new(PanicOnClone {});
writer.try_lock().unwrap();
}
#[test]
#[should_panic]
fn panic_absorb() {
#[derive(Default)]
struct Empty {}
impl Absorb<()> for Empty {
fn absorb(&mut self, _: ()) {
panic!()
}
}
let mut writer: Writer<Empty, ()> = Writer::default();
let mut reader = writer.build_reader().unwrap();
let read_lock = reader.lock();
let mut lock = writer.try_lock().unwrap();
lock.apply_op(());
_ = *read_lock;
}
#[test]
#[cfg_attr(miri, ignore)]
fn two_thread_loops() {
let mut writer = Writer::new(0);
let mut reader = writer.build_reader().unwrap();
std::thread::spawn(move || {
let mut value = 0;
for _ in 0..1_000_000 {
let lock = reader.lock();
assert!(*lock <= value);
value = *lock;
assert!(*lock == value);
}
});
for _ in 0..1_000_000 {
let value = *writer.read();
let mut lock = spin_lock(&mut writer);
assert!(value <= *lock.read());
lock.apply_op(CounterAddOp(1));
drop(lock);
}
}
}