#![cfg(loom)]
use core::pin::Pin;
use loom::{
sync::{
Arc,
atomic::{AtomicBool, Ordering},
},
thread,
};
use phylactery::Soul;
#[test]
fn soul_lich_drop_is_synchronized() {
loom::model(|| {
let called = Arc::new(AtomicBool::new(false));
let called_clone = called.clone();
let writer = move || {
called_clone.store(true, Ordering::Release);
};
let soul: Pin<Box<Soul<_>>> = Box::pin(Soul::new(writer));
let lich = soul.as_ref().bind::<dyn Fn() + Send + Sync>();
let handle = thread::spawn(move || {
lich();
});
drop(soul);
handle.join().unwrap();
assert!(called.load(Ordering::Acquire));
});
}
#[test]
fn clone_and_drop_concurrent() {
loom::model(|| {
let soul: Pin<Box<Soul<_>>> = Box::pin(Soul::new(|| {}));
let lich1 = soul.as_ref().bind::<dyn Fn() + Send + Sync>();
let lich2 = lich1.clone();
let h1 = thread::spawn(move || {
drop(lich1);
});
let h2 = thread::spawn(move || {
drop(lich2);
});
drop(soul);
h1.join().unwrap();
h2.join().unwrap();
});
}
#[test]
fn concurrent_bind_and_drop() {
loom::model(|| {
let soul: Pin<Arc<Soul<_>>> = Arc::pin(Soul::new(|| {}));
let lich = soul.as_ref().bind::<dyn Fn() + Send + Sync>();
let soul_clone = soul.clone();
let h1 = thread::spawn(move || {
let lich2 = soul_clone.as_ref().bind::<dyn Fn() + Send + Sync>();
drop(lich2);
});
let h2 = thread::spawn(move || {
drop(lich);
});
drop(soul);
h1.join().unwrap();
h2.join().unwrap();
});
}
#[test]
fn redeem_wakes_sever() {
loom::model(|| {
let soul: Pin<Arc<Soul<_>>> = Arc::pin(Soul::new(|| {}));
let lich = soul.as_ref().bind::<dyn Fn() + Send + Sync>();
let handle = thread::spawn(move || {
let remaining = lich.redeem();
assert_eq!(remaining, 0);
});
Soul::sever(soul);
handle.join().unwrap();
});
}
#[test]
fn multiple_liches_staggered_drop() {
loom::model(|| {
let soul: Pin<Box<Soul<_>>> = Box::pin(Soul::new(|| {}));
let lich1 = soul.as_ref().bind::<dyn Fn() + Send + Sync>();
let lich2 = soul.as_ref().bind::<dyn Fn() + Send + Sync>();
let h1 = thread::spawn(move || {
drop(lich1);
});
let h2 = thread::spawn(move || {
drop(lich2);
});
drop(soul);
h1.join().unwrap();
h2.join().unwrap();
});
}
#[test]
fn try_sever_while_lich_alive() {
loom::model(|| {
let soul: Pin<Arc<Soul<_>>> = Arc::pin(Soul::new(|| {}));
let lich = soul.as_ref().bind::<dyn Fn() + Send + Sync>();
let handle = thread::spawn(move || {
drop(lich);
});
handle.join().unwrap();
assert!(Soul::try_sever(soul).is_ok());
});
}
#[test]
fn sever_concurrent_with_last_drop() {
loom::model(|| {
let soul: Pin<Arc<Soul<_>>> = Arc::pin(Soul::new(|| {}));
let lich = soul.as_ref().bind::<dyn Fn() + Send + Sync>();
let handle = thread::spawn(move || {
lich();
});
Soul::sever(soul);
handle.join().unwrap();
});
}
#[test]
fn concurrent_reads_during_drop() {
loom::model(|| {
let soul: Pin<Box<Soul<_>>> = Box::pin(Soul::new(|| {}));
let lich1 = soul.as_ref().bind::<dyn Fn() + Send + Sync>();
let lich2 = lich1.clone();
let handle = thread::spawn(move || {
lich1();
drop(lich1);
});
drop(lich2);
drop(soul);
handle.join().unwrap();
});
}
#[test]
fn bindings_count_under_contention() {
loom::model(|| {
let soul: Pin<Arc<Soul<_>>> = Arc::pin(Soul::new(|| {}));
let lich = soul.as_ref().bind::<dyn Fn() + Send + Sync>();
let lich_for_clone = lich.clone();
let soul_clone = soul.clone();
let h1 = thread::spawn(move || {
let extra = lich_for_clone.clone();
let _ = soul_clone.bindings();
drop(extra);
drop(lich_for_clone);
});
let h2 = thread::spawn(move || {
drop(lich);
});
h1.join().unwrap();
h2.join().unwrap();
assert_eq!(soul.bindings(), 0);
drop(soul);
});
}
#[test]
fn concurrent_sever_calls() {
loom::model(|| {
let soul: Pin<Arc<Soul<_>>> = Arc::pin(Soul::new(|| {}));
let s1 = soul.clone();
let s2 = soul.clone();
drop(soul);
let h1 = thread::spawn(move || {
Soul::sever(s1);
});
let h2 = thread::spawn(move || {
Soul::sever(s2);
});
h1.join().unwrap();
h2.join().unwrap();
});
}
#[test]
fn bind_clone_redeem_interleaved() {
loom::model(|| {
let soul: Pin<Arc<Soul<_>>> = Arc::pin(Soul::new(|| {}));
let lich1 = soul.as_ref().bind::<dyn Fn() + Send + Sync>();
let soul_clone = soul.clone();
let handle = thread::spawn(move || {
let lich2 = soul_clone.as_ref().bind::<dyn Fn() + Send + Sync>();
lich2();
let _ = lich2.redeem();
});
let _ = lich1.redeem();
handle.join().unwrap();
drop(soul);
});
}
#[test]
fn shared_lich_via_arc() {
loom::model(|| {
let soul: Pin<Box<Soul<_>>> = Box::pin(Soul::new(|| {}));
let lich = soul.as_ref().bind::<dyn Fn() + Send + Sync>();
let lich_arc = Arc::new(lich);
let lich_arc2 = lich_arc.clone();
let handle = thread::spawn(move || {
(&**lich_arc2)();
});
(&**lich_arc)();
handle.join().unwrap();
drop(lich_arc);
drop(soul);
});
}