pub struct LinearizabilityTester<ThreadId, RefObj: SequentialSpec> { /* private fields */ }
Expand description

This tester captures a potentially concurrent history of operations and validates that it adheres to a SequentialSpec based on the linearizability consistency model. This model requires that operations be applied atomically and that sequenced (non-concurrent) operations are applied in order.

If you’re not sure whether to pick this or SequentialConsistencyTester, favor LinearizabilityTester.

Linearizability

Unlike with sequential consistency, all sequenced (non-concurrent) operations must respect a happens-before relationship, even across threads, which ensures that histories respect “real time” ordering (defined more precisely below). Anomalies are prevented because threads all agree on the viable order of operations. For example, the later read by Thread 2 must read the value of Thread 1’s write (rather than a differing earlier value) since those two operations are not concurrent:

          -----------Time------------------------------>
Thread 1: [write invoked... and returns]
Thread 2:                                 [read invoked... and returns]

While “real time” is a common way to phrase an implicit total ordering on non-concurrent events spanning threads, a more precise way to think about this is that prior to Thread 2 starting its read, Thread 1 is capable of communicating with Thread 2 indicating that the write finished. This perspective avoids introducing the notion of a shared global time, which is often a misleading perspective when it comes to distributed systems (or even modern physics in general).

The SequentialSpec will imply additional ordering constraints based on semantics specific to each operation. For example, a value cannot be popped off a stack before it is pushed. It is then the responsibility of this tester to establish whether a valid total ordering of events exists under these constraints.

See also: SequentialConsistencyTester.

Implementations§

source§

impl<T: Ord, RefObj: SequentialSpec> LinearizabilityTester<T, RefObj>

source

pub fn new(init_ref_obj: RefObj) -> Self

Constructs a LinearizabilityTester.

source

pub fn len(&self) -> usize

Indicates the aggregate number of operations completed or in flight across all threads.

source§

impl<T, RefObj> LinearizabilityTester<T, RefObj>where T: Copy + Debug + Ord, RefObj: Clone + SequentialSpec, RefObj::Op: Clone + Debug, RefObj::Ret: Clone + Debug + PartialEq,

source

pub fn serialized_history(&self) -> Option<Vec<(RefObj::Op, RefObj::Ret)>>

Attempts to serialize the recorded partially ordered operation history into a total order that is consistent with a reference object’s operational semantics.

Trait Implementations§

source§

impl<ThreadId: Clone, RefObj: Clone + SequentialSpec> Clone for LinearizabilityTester<ThreadId, RefObj>where RefObj::Op: Clone, RefObj::Ret: Clone,

source§

fn clone(&self) -> LinearizabilityTester<ThreadId, RefObj>

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<T, RefObj> ConsistencyTester<T, RefObj> for LinearizabilityTester<T, RefObj>where T: Copy + Debug + Ord, RefObj: Clone + SequentialSpec, RefObj::Op: Clone + Debug, RefObj::Ret: Clone + Debug + PartialEq,

source§

fn on_invoke( &mut self, thread_id: T, op: RefObj::Op ) -> Result<&mut Self, String>

Indicates that a thread invoked an operation. Returns Ok(...) if the history is valid, even if it is not lineariable.

See LinearizabilityTester::serialized_history.

source§

fn on_return( &mut self, thread_id: T, ret: RefObj::Ret ) -> Result<&mut Self, String>

Indicates that a thread’s earlier operation invocation returned. Returns Ok(...) if the history is valid, even if it is not linearizable.

See LinearizabilityTester::serialized_history.

source§

fn is_consistent(&self) -> bool

Indicates whether the recorded history is linearizable.

source§

fn on_invret( &mut self, thread_id: T, op: RefObj::Op, ret: RefObj::Ret ) -> Result<&mut Self, String>

A helper that indicates both an operation and corresponding return value for a thread. Returns Ok(...) if the history is valid, even if it is not consistent.
source§

impl<ThreadId: Debug, RefObj: Debug + SequentialSpec> Debug for LinearizabilityTester<ThreadId, RefObj>where RefObj::Op: Debug, RefObj::Ret: Debug,

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<T: Ord, RefObj> Default for LinearizabilityTester<T, RefObj>where RefObj: Default + SequentialSpec,

source§

fn default() -> Self

Returns the “default value” for a type. Read more
source§

impl<ThreadId: Hash, RefObj: Hash + SequentialSpec> Hash for LinearizabilityTester<ThreadId, RefObj>where RefObj::Op: Hash, RefObj::Ret: Hash,

source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · source§

fn hash_slice<H>(data: &[Self], state: &mut H)where H: Hasher, Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
source§

impl<ThreadId: PartialEq, RefObj: PartialEq + SequentialSpec> PartialEq<LinearizabilityTester<ThreadId, RefObj>> for LinearizabilityTester<ThreadId, RefObj>where RefObj::Op: PartialEq, RefObj::Ret: PartialEq,

source§

fn eq(&self, other: &LinearizabilityTester<ThreadId, RefObj>) -> bool

This method tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
source§

impl<T, RefObj> Serialize for LinearizabilityTester<T, RefObj>where RefObj: Serialize + SequentialSpec, RefObj::Op: Serialize, RefObj::Ret: Serialize, T: Ord + Serialize,

source§

fn serialize<Ser: Serializer>(&self, ser: Ser) -> Result<Ser::Ok, Ser::Error>

Serialize this value into the given Serde serializer. Read more
source§

impl<ThreadId: Eq, RefObj: Eq + SequentialSpec> Eq for LinearizabilityTester<ThreadId, RefObj>where RefObj::Op: Eq, RefObj::Ret: Eq,

source§

impl<ThreadId, RefObj: SequentialSpec> StructuralEq for LinearizabilityTester<ThreadId, RefObj>

source§

impl<ThreadId, RefObj: SequentialSpec> StructuralPartialEq for LinearizabilityTester<ThreadId, RefObj>

Auto Trait Implementations§

§

impl<ThreadId, RefObj> RefUnwindSafe for LinearizabilityTester<ThreadId, RefObj>where RefObj: RefUnwindSafe, ThreadId: RefUnwindSafe, <RefObj as SequentialSpec>::Op: RefUnwindSafe, <RefObj as SequentialSpec>::Ret: RefUnwindSafe,

§

impl<ThreadId, RefObj> Send for LinearizabilityTester<ThreadId, RefObj>where RefObj: Send, ThreadId: Send, <RefObj as SequentialSpec>::Op: Send, <RefObj as SequentialSpec>::Ret: Send,

§

impl<ThreadId, RefObj> Sync for LinearizabilityTester<ThreadId, RefObj>where RefObj: Sync, ThreadId: Sync, <RefObj as SequentialSpec>::Op: Sync, <RefObj as SequentialSpec>::Ret: Sync,

§

impl<ThreadId, RefObj> Unpin for LinearizabilityTester<ThreadId, RefObj>where RefObj: Unpin,

§

impl<ThreadId, RefObj> UnwindSafe for LinearizabilityTester<ThreadId, RefObj>where RefObj: UnwindSafe, ThreadId: RefUnwindSafe, <RefObj as SequentialSpec>::Op: RefUnwindSafe, <RefObj as SequentialSpec>::Ret: RefUnwindSafe,

Blanket Implementations§

source§

impl<T> Any for Twhere T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<Q, K> Equivalent<K> for Qwhere Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T, U> Into<U> for Twhere U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T> ToOwned for Twhere T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere V: MultiLane<T>,

§

fn vzip(self) -> V