extern crate atom;
use atom::*;
use std::collections::HashSet;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use std::sync::*;
use std::thread;
#[test]
fn swap() {
let a = Atom::empty();
assert_eq!(a.swap(Box::new(1u8), Ordering::AcqRel), None);
assert_eq!(a.swap(Box::new(2u8), Ordering::AcqRel), Some(Box::new(1u8)));
assert_eq!(a.swap(Box::new(3u8), Ordering::AcqRel), Some(Box::new(2u8)));
}
#[test]
fn take() {
let a = Atom::new(Box::new(7u8));
assert_eq!(a.take(Ordering::Acquire), Some(Box::new(7)));
assert_eq!(a.take(Ordering::Acquire), None);
}
#[test]
fn set_if_none() {
let a = Atom::empty();
assert_eq!(a.set_if_none(Box::new(7u8), Ordering::Release), None);
assert_eq!(
a.set_if_none(Box::new(8u8), Ordering::Release),
Some(Box::new(8u8))
);
}
#[test]
fn compare_and_swap_basics() {
cas_test_basics_helper(|a, cas_val, next_val| {
a.compare_and_swap(cas_val, next_val, Ordering::SeqCst)
});
}
#[test]
fn compare_exchange_basics() {
cas_test_basics_helper(|a, cas_val, next_val| {
a.compare_exchange(cas_val, next_val, Ordering::SeqCst, Ordering::SeqCst)
});
}
#[test]
fn compare_exchange_weak_basics() {
cas_test_basics_helper(|a, cas_val, next_val| {
a.compare_exchange_weak(cas_val, next_val, Ordering::SeqCst, Ordering::SeqCst)
});
}
#[test]
fn compare_and_swap_threads() {
cas_test_threads_helper(|a, cas_val, next_val| {
a.compare_and_swap(cas_val, next_val, Ordering::SeqCst)
});
}
#[test]
fn compare_exchange_threads() {
cas_test_threads_helper(|a, cas_val, next_val| {
a.compare_exchange(cas_val, next_val, Ordering::SeqCst, Ordering::SeqCst)
});
}
#[test]
fn compare_exchange_weak_threads() {
cas_test_threads_helper(|a, cas_val, next_val| {
a.compare_exchange_weak(cas_val, next_val, Ordering::SeqCst, Ordering::SeqCst)
});
}
type TestCASFn = fn(&Atom<Arc<String>>, Option<&Arc<String>>, Option<Arc<String>>)
-> Result<Option<Arc<String>>, (Option<Arc<String>>, *mut Arc<String>)>;
fn cas_test_basics_helper(cas: TestCASFn) {
let cur_val = Arc::new("123".to_owned());
let mut next_val = Arc::new("456".to_owned());
let other_val = Arc::new("1927447".to_owned());
let a = Atom::new(cur_val.clone());
let pcur = IntoRawPtr::into_raw(cur_val.clone());
let pnext = IntoRawPtr::into_raw(next_val.clone());
for attempt in vec![None, Some(&other_val), Some(&Arc::new("wow".to_owned()))] {
let res = cas(&a, attempt, Some(next_val.clone())).unwrap_err();
next_val = res.0.unwrap();
assert_eq!(res.1, pcur as *mut _);
}
let res = cas(&a, Some(&cur_val), Some(next_val.clone()));
assert_eq!(res, Ok(Some(cur_val)));
for attempt in vec![None, Some(&other_val), Some(&Arc::new("wow".to_owned()))] {
let res = cas(&a, attempt, None).unwrap_err();
assert_eq!(res, (None, pnext as *mut _));
}
}
fn cas_test_threads_helper(cas: TestCASFn) {
let cur_val = Arc::new("current".to_owned());
let next_val = Arc::new("next".to_owned());
let other_val = Arc::new("other".to_owned());
let a = Arc::new(Atom::new(cur_val.clone()));
let num_threads = 10;
let cas_thread = num_threads / 2;
let pprevs: Vec<Result<usize, usize>> = (0..num_threads)
.map(|i| {
let a = a.clone();
let cur_val = cur_val.clone();
let next_val = next_val.clone();
let other_val = other_val.clone();
thread::spawn(move || {
let cas_val = Some(if i == cas_thread {
&cur_val
} else {
&other_val
});
match cas(&a, cas_val, Some(next_val.clone())) {
Ok(prev) => {
let prev = prev.unwrap();
assert!(Arc::ptr_eq(&prev, &cur_val));
assert!(!Arc::ptr_eq(&prev, &next_val));
Ok(prev.into_raw() as usize)
}
Err((_, pprev)) => Err(pprev as usize),
}
})
})
.map(|handle| handle.join().unwrap())
.collect();
assert_eq!(pprevs.iter().filter(|pprev| pprev.is_ok()).count(), 1);
let uniq_pprevs: HashSet<_> = pprevs
.into_iter()
.map(|pprev| pprev.unwrap_or_else(|pprev| pprev) as *mut _)
.collect();
assert!(uniq_pprevs.contains(&cur_val.into_raw()));
assert!(!uniq_pprevs.contains(&other_val.into_raw()));
assert_eq!(a.take(Ordering::Relaxed), Some(next_val));
}
#[derive(Clone)]
struct Canary(Arc<AtomicUsize>);
impl Drop for Canary {
fn drop(&mut self) {
self.0.fetch_add(1, Ordering::SeqCst);
}
}
#[test]
fn ensure_drop() {
let v = Arc::new(AtomicUsize::new(0));
let a = Box::new(Canary(v.clone()));
let a = Atom::new(a);
assert_eq!(v.load(Ordering::SeqCst), 0);
drop(a);
assert_eq!(v.load(Ordering::SeqCst), 1);
}
#[test]
fn ensure_drop_arc() {
let v = Arc::new(AtomicUsize::new(0));
let a = Arc::new(Canary(v.clone()));
let a = Atom::new(a);
assert_eq!(v.load(Ordering::SeqCst), 0);
drop(a);
assert_eq!(v.load(Ordering::SeqCst), 1);
}
#[test]
fn ensure_send() {
let atom = Arc::new(Atom::empty());
let wait = Arc::new(Barrier::new(2));
let w = wait.clone();
let a = atom.clone();
thread::spawn(move || {
a.swap(Box::new(7u8), Ordering::AcqRel);
w.wait();
});
wait.wait();
assert_eq!(atom.take(Ordering::Acquire), Some(Box::new(7u8)));
}
#[test]
fn get() {
let atom = Arc::new(AtomSetOnce::empty());
assert_eq!(atom.get(Ordering::Acquire), None);
assert_eq!(atom.set_if_none(Box::new(8u8), Ordering::Release), None);
assert_eq!(atom.get(Ordering::Acquire), Some(&8u8));
}
#[test]
fn get_arc() {
let atom = Arc::new(AtomSetOnce::empty());
assert_eq!(atom.get(Ordering::Acquire), None);
assert_eq!(atom.set_if_none(Arc::new(8u8), Ordering::Release), None);
assert_eq!(atom.get(Ordering::Acquire), Some(&8u8));
let v = Arc::new(AtomicUsize::new(0));
let atom = Arc::new(AtomSetOnce::empty());
atom.get(Ordering::Acquire);
atom.set_if_none(Arc::new(Canary(v.clone())), Ordering::Release);
atom.get(Ordering::Acquire);
drop(atom);
assert_eq!(v.load(Ordering::SeqCst), 1);
}
#[derive(Debug)]
struct Link {
next: Option<Box<Link>>,
value: u32,
}
impl Link {
fn new(v: u32) -> Box<Link> {
Box::new(Link {
next: None,
value: v,
})
}
}
impl GetNextMut for Box<Link> {
type NextPtr = Option<Box<Link>>;
fn get_next(&mut self) -> &mut Option<Box<Link>> {
&mut self.next
}
}
#[test]
fn lifo() {
let atom = Atom::empty();
for i in 0..100 {
let x = atom.replace_and_set_next(Link::new(99 - i), Ordering::Relaxed, Ordering::AcqRel);
assert_eq!(x, i == 0);
}
let expected: Vec<u32> = (0..100).collect();
let mut found = Vec::new();
let mut chain = atom.take(Ordering::Acquire);
while let Some(v) = chain {
found.push(v.value);
chain = v.next;
}
assert_eq!(expected, found);
}
#[allow(dead_code)]
struct LinkCanary {
next: Option<Box<LinkCanary>>,
value: Canary,
}
impl LinkCanary {
fn new(v: Canary) -> Box<LinkCanary> {
Box::new(LinkCanary {
next: None,
value: v,
})
}
}
impl GetNextMut for Box<LinkCanary> {
type NextPtr = Option<Box<LinkCanary>>;
fn get_next(&mut self) -> &mut Option<Box<LinkCanary>> {
&mut self.next
}
}
#[test]
fn lifo_drop() {
let v = Arc::new(AtomicUsize::new(0));
let canary = Canary(v.clone());
let mut link = LinkCanary::new(canary.clone());
link.next = Some(LinkCanary::new(canary.clone()));
let atom = Atom::empty();
atom.replace_and_set_next(link, Ordering::Relaxed, Ordering::AcqRel);
assert_eq!(1, v.load(Ordering::SeqCst));
drop(atom);
assert_eq!(2, v.load(Ordering::SeqCst));
}
#[test]
fn borrow() {
let a = Atom::new(&5);
assert_eq!(a.swap(&7, Ordering::Relaxed), Some(&5));
assert_eq!(a.take(Ordering::Relaxed), Some(&7));
}