mvcc_cell 0.1.2

Software-transactional memory for Rust
Documentation
use super::*;
use proptest::prelude::*;

proptest!{
    #[test] fn test_write_write(
        a in ".*",
        b in ".*",
    ) {
        let mvcc = Mvcc::new();
        let slot:MvccCell<String> = MvccCell::new(&mvcc, Default::default());

        // Concurrent transactions
        let mut t1 = mvcc.begin();
        let mut t2 = mvcc.begin();

        assert_eq!(t1[&slot], "");
        assert_eq!(t2[&slot], "");

        t1[&slot] = a.clone();

        assert_eq!(&t1[&slot], &a);
        assert_eq!(&t2[&slot], "");

        t2[&slot] = b.clone();

        assert_eq!(&t1[&slot], &a);
        assert_eq!(&t2[&slot], &b);

        // First committer wins
        assert!(t1.try_commit().is_ok());

        assert_eq!(&mvcc.begin()[&slot], &a);
        assert_eq!(&t2[&slot], &b);

        let t2 = t2.try_commit().unwrap_err();

        assert_eq!(&mvcc.begin()[&slot], &a);
        assert_eq!(&t2[&slot], &b);

        let mut t3 = mvcc.begin();

        t3[&slot] = t2[&slot].clone();
        assert_eq!(&t3[&slot], &b);

        assert!(t3.try_commit().is_ok());

        assert_eq!(&mvcc.begin()[&slot], &b);
    }

    #[test] fn test_disjoint_cells(
        a in ".*",
        b in ".*",
    ) {
        let mvcc = Mvcc::new();
        let slot1:MvccCell<String> = MvccCell::new(&mvcc, Default::default());
        let slot2:MvccCell<String> = MvccCell::new(&mvcc, Default::default());

        // Concurrent transactions
        let mut t1 = mvcc.begin();
        let mut t2 = mvcc.begin();

        assert_eq!(&t1[&slot1], "");
        assert_eq!(&t2[&slot2], "");

        t1[&slot1] = a.clone();
        t2[&slot2] = b.clone();

        assert_eq!(&t1[&slot1], &a);
        assert_eq!(&t2[&slot2], &b);
        assert_eq!(&mvcc.begin()[&slot1], "");
        assert_eq!(&mvcc.begin()[&slot2], "");

        assert!(t1.try_commit().is_ok());

        assert_eq!(&t2[&slot2], &b);
        assert_eq!(&mvcc.begin()[&slot1], &a);
        assert_eq!(&mvcc.begin()[&slot2], "");

        assert!(t2.try_commit().is_ok());

        assert_eq!(&mvcc.begin()[&slot1], &a);
        assert_eq!(&mvcc.begin()[&slot2], &b);
    }

    #[test] fn test_read_write(
        a in ".*",
    ) {
        let mvcc = Mvcc::new();
        let slot:MvccCell<String> = MvccCell::new(&mvcc, Default::default());

        // Concurrent transactions
        let mut t1 = mvcc.begin();
        let t2 = mvcc.begin();

        assert_eq!(t1[&slot], "");
        assert_eq!(t2[&slot], "");

        t1[&slot] = a.clone();

        assert_eq!(&t1[&slot], &a);
        assert_eq!(&t2[&slot], "");

        assert!(t2.try_commit().is_ok());
        assert!(t1.try_commit().is_ok());
    }

    #[test] fn test_write_read(
        a in ".*",
    ) {
        let mvcc = Mvcc::new();
        let slot:MvccCell<String> = MvccCell::new(&mvcc, Default::default());

        // Concurrent transactions
        let mut t1 = mvcc.begin();
        let t2 = mvcc.begin();

        assert_eq!(t1[&slot], "");
        assert_eq!(t2[&slot], "");

        t1[&slot] = a.clone();

        assert_eq!(&t1[&slot], &a);
        assert_eq!(&t2[&slot], "");

        assert!(t1.try_commit().is_ok());
        assert!(t2.try_commit().is_err());
    }
}

#[derive(Debug,Copy,Clone)]
enum Fuzzer {
    /// Read from slot
    Read(usize),

    /// Write value to slot
    Write(usize,u32),

    /// Clean history of slot
    Vac(usize),
}

impl Fuzzer {
    fn strategy(slots: impl Clone + Strategy<Value=usize>)->impl Strategy<Value=Self> {
        prop_oneof![
            slots.clone().prop_map(Self::Read),
            (slots.clone(), any::<u32>()).prop_map(|(slot,val)| Self::Write(slot, val)),
            slots.clone().prop_map(Self::Vac),
        ]
    }
}


proptest! {
    #[test] fn fuzz_vacuum(ops in prop::collection::vec(Fuzzer::strategy(0_usize..4), 0..1024)) {
        use Fuzzer::*;

        let mvcc = Mvcc::new();
        let slots = vec![
            MvccCell::new(&mvcc, Box::new(0)),
            MvccCell::new(&mvcc, Box::new(0)),
            MvccCell::new(&mvcc, Box::new(0)),
            MvccCell::new(&mvcc, Box::new(0)),
        ];

        let mut reads = vec![];

        for op in ops {
            let mut txn = mvcc.begin();
            match op {
                Write(i, val) => {
                    txn[&slots[i]] = val;
                    assert!(txn.try_commit().is_ok());
                }
                Read(i) => {
                    reads.push((i, txn[&slots[i]], txn));
                }
                Vac(i) => {
                    let v = Vacuum::new(&mvcc);
                    v.clean(&slots[i]);
                }
            }
        }

        for &(i, val, ref txn) in &reads {
            assert_eq!(txn[&slots[i]], val);
        }
    }
}