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 postThe 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>
impl<X, Y, Pred> AtomicUpdate<X, Y, Pred>
Sourcepub uninterp spec fn pred(self) -> Pred
pub uninterp spec fn pred(self) -> Pred
The predicate of the atomic update.
See UpdatePredicate for more information.
Sourcepub uninterp spec fn resolves(self) -> bool
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.
Sourcepub uninterp spec fn input(self) -> X
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.
Sourcepub uninterp spec fn output(self) -> Y
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>
impl<X, Y, Pred: UpdatePredicate<X, Y>> AtomicUpdate<X, Y, Pred>
Sourcepub open spec fn outer_mask(self) -> ISet<int>
pub open spec fn outer_mask(self) -> ISet<int>
{ self.pred().outer_mask() }Sourcepub open spec fn inner_mask(self) -> ISet<int>
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>
impl<X, Y, Pred> Debug for AtomicUpdate<X, Y, Pred>
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.
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> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T, U> IntoSpecImpl<U> for Twhere
U: From<T>,
impl<T, U> IntoSpecImpl<U> for Twhere
U: From<T>,
Source§impl<T, VERUS_SPEC__A> TryFromSpec<T> for VERUS_SPEC__Awhere
VERUS_SPEC__A: TryFrom<T>,
impl<T, VERUS_SPEC__A> TryFromSpec<T> for VERUS_SPEC__Awhere
VERUS_SPEC__A: TryFrom<T>,
Source§exec fn obeys_try_from_spec() -> bool
exec fn obeys_try_from_spec() -> bool
Source§impl<T, VERUS_SPEC__A> TryIntoSpec<T> for VERUS_SPEC__Awhere
VERUS_SPEC__A: TryInto<T>,
impl<T, VERUS_SPEC__A> TryIntoSpec<T> for VERUS_SPEC__Awhere
VERUS_SPEC__A: TryInto<T>,
Source§exec fn obeys_try_into_spec() -> bool
exec fn obeys_try_into_spec() -> bool
Source§impl<T, U> TryIntoSpecImpl<U> for Twhere
U: TryFrom<T>,
impl<T, U> TryIntoSpecImpl<U> for Twhere
U: TryFrom<T>,
Source§open spec fn obeys_try_into_spec() -> bool
open spec fn obeys_try_into_spec() -> bool
{ <U as TryFromSpec<Self>>::obeys_try_from_spec() }