lincheck 0.2.0

A linearizability checker for concurrent data structures
Documentation
use lincheck::{ConcurrentSpec, Lincheck, SequentialSpec};

use loom::sync::Mutex;
use proptest::prelude::*;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Op<T> {
    Push(T),
    Pop,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Ret<T> {
    Push,
    Pop(Option<T>),
}

impl<T: Arbitrary + Clone + 'static> Arbitrary for Op<T> {
    type Parameters = ();
    type Strategy = BoxedStrategy<Self>;

    fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
        prop_oneof![any::<T>().prop_map(Op::Push), Just(Op::Pop),].boxed()
    }
}

struct SequentialStack<T> {
    stack: Vec<T>,
}

impl<T> Default for SequentialStack<T> {
    fn default() -> Self {
        Self { stack: Vec::new() }
    }
}

impl<T> SequentialSpec for SequentialStack<T> {
    type Op = Op<T>;
    type Ret = Ret<T>;

    fn exec(&mut self, op: Self::Op) -> Self::Ret {
        match op {
            Op::Push(value) => {
                self.stack.push(value);
                Ret::Push
            }
            Op::Pop => Ret::Pop(self.stack.pop()),
        }
    }
}

struct ConcurrentStack<T> {
    stack: Mutex<Vec<T>>,
}

impl<T> Default for ConcurrentStack<T> {
    fn default() -> Self {
        Self {
            stack: Mutex::new(Vec::new()),
        }
    }
}

impl<T> ConcurrentSpec for ConcurrentStack<T> {
    type Seq = SequentialStack<T>;

    fn exec(&self, op: Op<T>) -> Ret<T> {
        let mut stack = self.stack.lock().unwrap();
        match op {
            Op::Push(value) => {
                stack.push(value);
                Ret::Push
            }
            Op::Pop => Ret::Pop(stack.pop()),
        }
    }
}

#[test]
fn models_stack() {
    Lincheck::default().verify_or_panic::<ConcurrentStack<u8>>();
}