atom_box 0.3.0

A safe idiomatic Rust implementation of Atomic Box using hazard pointers
Documentation
#[cfg(loom)]
mod loom_test {
    use atom_box::{AtomBox, domain::Domain, domain::ReclaimStrategy};
    use loom::sync::Arc;
    use loom::thread;
    use std::convert::From;

    const ITERATIONS: usize = 2;

    #[derive(Debug)]
    struct Value(usize);

    impl From<usize> for Value {
        fn from(value: usize) -> Self {
            Self(value)
        }
    }

    #[test]
    fn concurrency_swap_with_static_ref() {
        let mut builder = loom::model::Builder::new();
        builder.preemption_bound = Some(6);
        builder.check(|| {
            let test_domain: &'static Domain<1> =
                Box::leak(Box::new(Domain::new(ReclaimStrategy::Eager)));

            let atom_box1: &'static _ =
                Box::leak(Box::new(AtomBox::new_with_domain(Value(0), test_domain)));
            let atom_box2: &'static _ =
                Box::leak(Box::new(AtomBox::new_with_domain(Value(0), test_domain)));

            thread::spawn(move || {
                let mut current_value = 0;
                for _ in 1..=ITERATIONS {
                    let new_value = atom_box1.load();
                    assert!(new_value.0 >= current_value, "Value should not decrease");
                    current_value = (*new_value).0;
                }
            });
            thread::spawn(move || {
                for i in 1..=ITERATIONS {
                    let guard1 = atom_box1.swap(Value(i));
                    let value1 = (*guard1).0;
                    let guard2 = atom_box2.swap_from_guard(guard1);
                    assert!(
                        (*guard2).0 <= value1,
                        "Value in first box should be greater than or equal to value in second box"
                    );
                }
            });
        });
    }

    #[test]
    fn concurrency_test_compare_exchange_with_static_ref() {
        let mut builder = loom::model::Builder::new();
        builder.preemption_bound = Some(3);
        builder.check(|| {
            let test_domain: &'static Domain<1> =
                Box::leak(Box::new(Domain::new(ReclaimStrategy::Eager)));

            let atom_box: &'static _ =
                Box::leak(Box::new(AtomBox::new_with_domain(Value(0), test_domain)));

            let handle1 = thread::spawn(move || {
                let mut current_value = atom_box.load();
                let initial_value = (*current_value).0;
                let _ = loop {
                    let new_value = Value((*current_value).0 + 1);
                    match atom_box.compare_exchange(current_value, new_value) {
                        Ok(value) => {
                            break value;
                        }
                        Err(value) => {
                            current_value = value;
                        }
                    }
                };
                let new_value = atom_box.load();
                assert!(
                    (*new_value).0 > initial_value,
                    "Value should have been increased"
                );
            });
            let handle2 = thread::spawn(move || {
                let mut current_value = atom_box.load();
                let initial_value = (*current_value).0;
                let _ = loop {
                    let new_value = Value((*current_value).0 + 1);
                    match atom_box.compare_exchange(current_value, new_value) {
                        Ok(value) => {
                            break value;
                        }
                        Err(value) => {
                            current_value = value;
                        }
                    }
                };
                let new_value = atom_box.load();
                assert!(
                    (*new_value).0 > initial_value,
                    "Value should have been increased"
                );
            });

            match (handle1.join(), handle2.join()) {
                (Ok(_), Ok(_)) => {
                    assert_eq!(atom_box.load().0, 2, "Final value should be 2");
                }
                _ => {
                    panic!("Thread join failed");
                }
            }
        });
    }

    #[test]
    fn concurrency_test_compare_exchange_weak_with_static_ref() {
        let mut builder = loom::model::Builder::new();
        builder.preemption_bound = Some(3);
        builder.check(|| {
            let test_domain: &'static Domain<1> =
                Box::leak(Box::new(Domain::new(ReclaimStrategy::Eager)));

            let atom_box: &'static _ =
                Box::leak(Box::new(AtomBox::new_with_domain(Value(0), test_domain)));

            let handle1 = thread::spawn(move || {
                let mut current_value = atom_box.load();
                let initial_value = (*current_value).0;
                let _ = loop {
                    let new_value = Value((*current_value).0 + 1);
                    match atom_box.compare_exchange_weak(current_value, new_value) {
                        Ok(value) => {
                            break value;
                        }
                        Err(value) => {
                            current_value = value;
                        }
                    }
                };
                let new_value = atom_box.load();
                assert!(
                    (*new_value).0 > initial_value,
                    "Value should have been increased"
                );
            });
            let handle2 = thread::spawn(move || {
                let mut current_value = atom_box.load();
                let initial_value = (*current_value).0;
                let _ = loop {
                    let new_value = Value((*current_value).0 + 1);
                    match atom_box.compare_exchange_weak(current_value, new_value) {
                        Ok(value) => {
                            break value;
                        }
                        Err(value) => {
                            current_value = value;
                        }
                    }
                };
                let new_value = atom_box.load();
                assert!(
                    (*new_value).0 > initial_value,
                    "Value should have been increased"
                );
            });

            match (handle1.join(), handle2.join()) {
                (Ok(_), Ok(_)) => {
                    assert_eq!(atom_box.load().0, 2, "Final value should be 2");
                }
                _ => {
                    panic!("Thread join failed");
                }
            }
        });
    }

    #[test]
    fn concurrency_swap_with_arc() {
        let mut builder = loom::model::Builder::new();
        builder.preemption_bound = Some(3);
        builder.check(|| {
            let test_domain: &'static Domain<1> =
                Box::leak(Box::new(Domain::new(ReclaimStrategy::Eager)));

            let atom_box1 = Arc::new(AtomBox::new_with_domain(Value(0), test_domain));
            let atom_box2 = Arc::new(AtomBox::new_with_domain(Value(0), test_domain));

            let atom_box = atom_box1.clone();
            thread::spawn(move || {
                let mut current_value = 0;
                for _ in 1..=ITERATIONS {
                    let new_value = atom_box.load();
                    assert!(new_value.0 >= current_value, "Value should not decrease");
                    current_value = (*new_value).0;
                }
            });
            let a_box1 = atom_box1.clone();
            let a_box2 = atom_box2.clone();
            thread::spawn(move || {
                for i in 1..=ITERATIONS {
                    let guard1 = a_box1.swap(Value(i));
                    let value1 = (*guard1).0;
                    let guard2 = a_box2.swap_from_guard(guard1);
                    assert!(
                        (*guard2).0 <= value1,
                        "Value in first box should be greater than or equal to value in second box"
                    );
                }
            });
        });
    }

    #[test]
    fn concurrency_test_compare_exchange_with_arc() {
        let mut builder = loom::model::Builder::new();
        builder.preemption_bound = Some(3);
        builder.check(|| {
            let test_domain: &'static Domain<1> =
                Box::leak(Box::new(Domain::new(ReclaimStrategy::Eager)));

            let atom_box = Arc::new(AtomBox::new_with_domain(Value(0), test_domain));

            let atom_box1 = atom_box.clone();
            let handle1 = thread::spawn(move || {
                let mut current_value = atom_box1.load();
                let initial_value = (*current_value).0;
                let _ = loop {
                    let new_value = Value((*current_value).0 + 1);
                    match atom_box1.compare_exchange(current_value, new_value) {
                        Ok(value) => {
                            break value;
                        }
                        Err(value) => {
                            current_value = value;
                        }
                    }
                };
                let new_value = atom_box1.load();
                assert!(
                    (*new_value).0 > initial_value,
                    "Value should have been increased"
                );
            });

            let atom_box2 = atom_box.clone();
            let handle2 = thread::spawn(move || {
                let mut current_value = atom_box2.load();
                let initial_value = (*current_value).0;
                let _ = loop {
                    let new_value = Value((*current_value).0 + 1);
                    match atom_box2.compare_exchange(current_value, new_value) {
                        Ok(value) => {
                            break value;
                        }
                        Err(value) => {
                            current_value = value;
                        }
                    }
                };
                let new_value = atom_box2.load();
                assert!(
                    (*new_value).0 > initial_value,
                    "Value should have been increased"
                );
            });

            match (handle1.join(), handle2.join()) {
                (Ok(_), Ok(_)) => {
                    assert_eq!(atom_box.load().0, 2, "Final value should be 2");
                }
                _ => {
                    panic!("Thread join failed");
                }
            }
        });
    }

    #[test]
    fn concurrency_test_compare_exchange_weak_with_arc() {
        let mut builder = loom::model::Builder::new();
        builder.preemption_bound = Some(3);
        builder.check(|| {
            let test_domain: &'static Domain<1> =
                Box::leak(Box::new(Domain::new(ReclaimStrategy::Eager)));

            let atom_box = Arc::new(AtomBox::new_with_domain(Value(0), test_domain));

            let atom_box1 = atom_box.clone();
            let handle1 = thread::spawn(move || {
                let mut current_value = atom_box1.load();
                let initial_value = (*current_value).0;
                let _ = loop {
                    let new_value = Value((*current_value).0 + 1);
                    match atom_box1.compare_exchange_weak(current_value, new_value) {
                        Ok(value) => {
                            break value;
                        }
                        Err(value) => {
                            current_value = value;
                        }
                    }
                };
                let new_value = atom_box1.load();
                assert!(
                    (*new_value).0 > initial_value,
                    "Value should have been increased"
                );
            });

            let atom_box2 = atom_box.clone();
            let handle2 = thread::spawn(move || {
                let mut current_value = atom_box2.load();
                let initial_value = (*current_value).0;
                let _ = loop {
                    let new_value = Value((*current_value).0 + 1);
                    match atom_box2.compare_exchange_weak(current_value, new_value) {
                        Ok(value) => {
                            break value;
                        }
                        Err(value) => {
                            current_value = value;
                        }
                    }
                };
                let new_value = atom_box2.load();
                assert!(
                    (*new_value).0 > initial_value,
                    "Value should have been increased"
                );
            });

            match (handle1.join(), handle2.join()) {
                (Ok(_), Ok(_)) => {
                    assert_eq!(atom_box.load().0, 2, "Final value should be 2");
                }
                _ => {
                    panic!("Thread join failed");
                }
            }
        });
    }
}