use biasedrc::Brc;
use std::sync::Barrier;
use std::sync::atomic::{AtomicU32, Ordering};
struct DropCounter<'a>(&'a AtomicU32);
impl Drop for DropCounter<'_> {
fn drop(&mut self) {
let _ = self
.0
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| {
Some(x.checked_add(1).unwrap())
});
}
}
#[test]
fn requires_merge() {
let counter = AtomicU32::new(0);
let one = Brc::new(DropCounter(&counter));
std::thread::scope(|scope| {
scope
.spawn(move || {
let two = Brc::clone(&one);
drop(one);
biasedrc::collect_force();
drop(two);
})
.join()
.unwrap();
assert_eq!(counter.load(Ordering::SeqCst), 0);
biasedrc::collect_force();
assert_eq!(counter.load(Ordering::SeqCst), 1);
});
}
#[test]
fn requires_merge_then_still_shared() {
let drop_counter = AtomicU32::new(0);
let (sender, receiver) = crossbeam_channel::bounded(0);
let begin_collection = Barrier::new(2);
let end_collection = Barrier::new(2);
std::thread::scope(|scope| {
scope.spawn(|| {
let sender = sender;
let biased = Brc::new(DropCounter(&drop_counter));
sender.send([Brc::clone(&biased), biased]).unwrap();
begin_collection.wait();
biasedrc::collect_force();
end_collection.wait();
});
scope.spawn(|| {
let receiver = receiver;
let [biased1, biased2] = receiver.recv().unwrap();
assert_eq!(Brc::shared_count(&biased1), 0);
drop(biased1); assert_eq!(Brc::shared_count(&biased2), -1);
begin_collection.wait();
end_collection.wait();
assert_eq!(Brc::shared_count(&biased2), 1);
assert_eq!(drop_counter.load(Ordering::SeqCst), 0);
drop(biased2);
assert_eq!(drop_counter.load(Ordering::SeqCst), 1);
});
});
}
#[derive(Default, Copy, Clone, Debug)]
enum BehaviorAfterMerge {
#[default]
ImmediateDestruction,
StillShared,
}
#[test]
fn requires_merge_after_thread_death() {
requires_merge_after_thread_death_with(BehaviorAfterMerge::ImmediateDestruction);
}
#[test]
fn requires_merge_after_thread_death_then_still_shared() {
requires_merge_after_thread_death_with(BehaviorAfterMerge::StillShared);
}
fn requires_merge_after_thread_death_with(mode: BehaviorAfterMerge) {
let counter = AtomicU32::new(0);
let (sender, receiver) = crossbeam_channel::bounded(0);
std::thread::scope(|scope| {
let first = scope.spawn(|| {
let sender = sender;
let obj = Brc::new(DropCounter(&counter));
sender.send(obj).unwrap();
});
scope.spawn(|| {
let receiver = receiver;
let biased = receiver.recv().unwrap();
let shared = match mode {
BehaviorAfterMerge::ImmediateDestruction => None,
BehaviorAfterMerge::StillShared => Some(Brc::clone(&biased)),
};
first.join().expect("Failed to join first thread");
assert_eq!(counter.load(Ordering::SeqCst), 0);
drop(biased);
assert_eq!(
counter.load(Ordering::SeqCst),
match mode {
BehaviorAfterMerge::ImmediateDestruction => 1,
BehaviorAfterMerge::StillShared => 0,
}
);
drop(shared);
assert_eq!(counter.load(Ordering::SeqCst), 1);
});
});
}
#[test]
fn dominated_biased() {
let one = Brc::new(42);
std::thread::scope(|scope| {
scope.spawn(|| {
let two = Brc::clone(&one);
drop(two);
});
scope.spawn(|| {
let three = Brc::clone(&one);
drop(three);
});
});
drop(one);
}
#[test]
fn unbias() {
use biasedrc::BiasedCountError::{NotBiased, WrongThread};
let counter = AtomicU32::new(0);
let finish_unbias = Barrier::new(2);
let (send_biased, recv_biased) = crossbeam_channel::bounded(0);
let (send_back_biased, recv_back_biased) = crossbeam_channel::bounded(0);
std::thread::scope(|scope| {
scope.spawn(|| {
let send_biased = send_biased;
let recv_back_biased = recv_back_biased;
let biased = Brc::new(DropCounter(&counter)); assert_eq!(Brc::biased_and_shared_counts(&biased), (Ok(1), 0));
send_biased.send(biased).unwrap();
let biased = recv_back_biased.recv().unwrap();
assert_eq!(Brc::biased_and_shared_counts(&biased), (Ok(1), 1));
drop(biased);
assert_eq!(counter.load(Ordering::SeqCst), 0);
finish_unbias.wait(); });
scope.spawn(|| {
let recv_biased = recv_biased;
let send_back_biased = send_back_biased;
let biased = recv_biased.recv().unwrap();
assert_eq!(
Brc::biased_and_shared_counts(&biased),
(Err(WrongThread), 0)
);
let shared = Brc::clone(&biased);
assert_eq!(
Brc::biased_and_shared_counts(&shared),
(Err(WrongThread), 1)
);
send_back_biased.send(biased).unwrap();
finish_unbias.wait();
assert_eq!(Brc::biased_and_shared_counts(&shared), (Err(NotBiased), 1));
drop(shared);
assert_eq!(counter.load(Ordering::SeqCst), 1);
});
});
}