Skip to main content

AtomicUpdate

Struct AtomicUpdate 

Source
pub struct AtomicUpdate<X, Y, Pred> { /* private fields */ }
Expand description

The atomic update (AU) is a ghost object which encapsulates the linearization point of a logically atomic function.

Logical atomicity is a proof technique that allows us to treat a function as if it was atomic, i.e. as if it evaluates in a single atomic step, even though it might perform multiple exec-mode operations internally. The key idea is that a logically atomic function contains a linearization point (LP), that is, a point in the function which updates the state of the program in a single atomic step of computation. We specify the behavior of such a function by describing the state of the program at four distinct points in time, specifically:

  • (private pre) at the start of the function,
  • (atomic pre) just before the linearization point,
  • (atomic post) just after the linearization point,
  • (private post) at the end of the function.
                       linearization point
                                🠗
├──────────────────────────────┤●├─────────────────────────┤
 private                 atomic   atomic            private
 pre                        pre   post                 post

The AtomicUpdate ghost object is the central abstraction for our implementation if logical atomicity, as it encapsulates the behavior of the function at the linearization point. The atomic update is declared by the atomic specification, it is constructed by the atomic function call (i.e. the “client”), and it is opened/destructed at the linearization point of the logically atomic function (i.e. the “library”).

Implementations§

Source§

impl<X, Y, Pred> AtomicUpdate<X, Y, Pred>

Source

pub uninterp spec fn pred(self) -> Pred

The predicate of the atomic update.

See UpdatePredicate for more information.

Source

pub uninterp spec fn resolves(self) -> bool

A prophesy variable which indicates that an atomic update has been resolved.

Initially, the value of this function is unknown, i.e. we can neither prove that it is true or false. Once the atomic update has been committed using the try_open_atomic_update macro, we learn that au.resolves() is true.

We must be able to prove that au.resolves() when the logically atomic function exits.

Source

pub uninterp spec fn input(self) -> X

A prophesy variable for the input value of the atomic update.

When the atomic update is committed, this variable is resolved to the input value of the atomic update. This variable is used internally in the (private) postcondition of the logically atomic function.

Source

pub uninterp spec fn output(self) -> Y

A prophesy variable for the output value of the atomic update.

When the atomic update is committed, this variable is resolved to the output value of the atomic update. This variable is used internally in the (private) postcondition of the logically atomic function.

Source§

impl<X, Y, Pred: UpdatePredicate<X, Y>> AtomicUpdate<X, Y, Pred>

Source

pub open spec fn req(self, x: X) -> bool

{ self.pred().req(x) }
Source

pub open spec fn ens(self, x: X, y: Y) -> bool

{ self.pred().ens(x, y) }
Source

pub open spec fn outer_mask(self) -> ISet<int>

{ self.pred().outer_mask() }
Source

pub open spec fn inner_mask(self) -> ISet<int>

{ self.pred().inner_mask() }

Trait Implementations§

Source§

impl<X, Y, Pred> Debug for AtomicUpdate<X, Y, Pred>

Source§

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

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

impl<X: Send, Y: Send, Pred> Send for AtomicUpdate<X, Y, Pred>

Mark the AtomicUpdate as Send if both X and Y are also Send.

§SAFETY

While the AtomicUpdate is only a stand-in for a stack of nested callback functions, when the AU is moved to another thread, e.g. by moving it in and out of an atomic invariant, it allows resources to cross thread boundaries with it, so we must ensure the AU is only Send when X: Send and Y: Send.

The predicate type we generate as part of the atomic specification only contains a ghost copy of function arguments, and ghost-mode data is always fine to move between threads. There is no need to restrict it, as it is safe by construction.

Source§

impl<X, Y, Pred> Sync for AtomicUpdate<X, Y, Pred>

Unconditionally mark the AtomicUpdate as Sync.

§SAFETY

A shared reference to an AtomicUpdate is pretty much useless. The only thing the user can do with an AU is open it, which requires full ownership. All methods provided by this type are spec-mode, meaning they can already be used with a much weaker ghost copy of the AU.

Auto Trait Implementations§

§

impl<X, Y, Pred> Freeze for AtomicUpdate<X, Y, Pred>
where Pred: Freeze,

§

impl<X, Y, Pred> RefUnwindSafe for AtomicUpdate<X, Y, Pred>
where Pred: RefUnwindSafe,

§

impl<X, Y, Pred> Unpin for AtomicUpdate<X, Y, Pred>
where Pred: Unpin,

§

impl<X, Y, Pred> UnsafeUnpin for AtomicUpdate<X, Y, Pred>
where Pred: UnsafeUnpin,

§

impl<X, Y, Pred> UnwindSafe for AtomicUpdate<X, Y, Pred>
where Pred: UnwindSafe,

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, VERUS_SPEC__A> FromSpec<T> for VERUS_SPEC__A
where VERUS_SPEC__A: From<T>,

Source§

exec fn obeys_from_spec() -> bool

Source§

exec fn from_spec(v: T) -> VERUS_SPEC__A

Source§

impl<T, U> Into<U> for T
where 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, VERUS_SPEC__A> IntoSpec<T> for VERUS_SPEC__A
where VERUS_SPEC__A: Into<T>,

Source§

exec fn obeys_into_spec() -> bool

Source§

exec fn into_spec(self) -> T

Source§

impl<T, U> IntoSpecImpl<U> for T
where U: From<T>,

Source§

open spec fn obeys_into_spec() -> bool

{ <U as FromSpec<Self>>::obeys_from_spec() }
Source§

open spec fn into_spec(self) -> U

{ U::from_spec(self) }
Source§

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

Source§

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, VERUS_SPEC__A> TryFromSpec<T> for VERUS_SPEC__A
where VERUS_SPEC__A: TryFrom<T>,

Source§

exec fn obeys_try_from_spec() -> bool

Source§

exec fn try_from_spec( v: T, ) -> Result<VERUS_SPEC__A, <VERUS_SPEC__A as TryFrom<T>>::Error>

Source§

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

Source§

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.
Source§

impl<T, VERUS_SPEC__A> TryIntoSpec<T> for VERUS_SPEC__A
where VERUS_SPEC__A: TryInto<T>,

Source§

exec fn obeys_try_into_spec() -> bool

Source§

exec fn try_into_spec(self) -> Result<T, <VERUS_SPEC__A as TryInto<T>>::Error>

Source§

impl<T, U> TryIntoSpecImpl<U> for T
where U: TryFrom<T>,

Source§

open spec fn obeys_try_into_spec() -> bool

{ <U as TryFromSpec<Self>>::obeys_try_from_spec() }
Source§

open spec fn try_into_spec(self) -> Result<U, <U as TryFrom<T>>::Error>

{ <U as TryFromSpec<Self>>::try_from_spec(self) }
§

impl<A> SpecEq<&A> for A
where A: ?Sized,

§

impl<A> SpecEq<&mut A> for A
where A: ?Sized,

§

impl<A> SpecEq<A> for A
where A: ?Sized,

§

impl<A> SpecEq<Ghost<A>> for A

§

impl<A> SpecEq<Tracked<A>> for A