pub struct Resource<P: PCM> { /* private fields */ }Expand description
Interface for PCM / Resource Algebra ghost state.
RA-based ghost state is a well-established theory that is especially useful for verifying concurrent code. An introduction to the concept can be found in Iris: Monoids and Invariants as an Orthogonal Basis for Concurrent Reasoning or Iris from the ground up.
To embed the concept into Verus, we:
- Use a trait,
PCM, to embed the well-formedness laws of a resource algebra - use a “tracked ghost” object,
Resource<P>(this page) to represent ownership of a resource.
Most operations are fairly standard, just “translated” from the usual CSL presentation into Verus.
allocto allocate a resource.jointo combine two resources viaP::op, andsplit, its inverse.validateto assert the validity of any held resource.updateorupdate_nondeterministicto perform a frame-preserving update.
The interface also includes a nontrivial extension for working with shared references to resources.
Shared resources do not compose in a “separating” way via P::op, but rather, in a “potentially overlapping” way (join_shared). Shared resources can also be used to “help” perform frame-preserving updates, as long as they themselves do not change (update_with_shared).
§Examples
See:
- Any of the examples in this directory
- The source code for the fractional resource library
§See also
The “storage protocol” formalism is an even more significant extension with additional capabilities for interacting with shared resources.
VerusSync provides a higher-level “swiss army knife” for building useful ghost resources.
Implementations§
Source§impl<P: PCM> Resource<P>
impl<P: PCM> Resource<P>
Sourcepub proof fn alloc(value: P) -> tracked out : Self
pub proof fn alloc(value: P) -> tracked out : Self
value.valid(),ensuresout.value() == value,Allocate a new resource at a fresh location
Sourcepub proof fn join(tracked self, tracked other: Self) -> tracked out : Self
pub proof fn join(tracked self, tracked other: Self) -> tracked out : Self
self.loc() == other.loc(),ensuresout.loc() == self.loc(),out.value() == P::op(self.value(), other.value()),We can join together two resources at the same location, where we obtain the combination of the two
Sourcepub proof fn split(tracked self, left: P, right: P) -> tracked out : (Self, Self)
pub proof fn split(tracked self, left: P, right: P) -> tracked out : (Self, Self)
self.value() == P::op(left, right),ensuresout.0.loc() == self.loc(),out.1.loc() == self.loc(),out.0.value() == left,out.1.value() == right,If a resource holds the result of a composition, we can split it into the components
Sourcepub proof fn create_unit(loc: Loc) -> tracked out : Self
pub proof fn create_unit(loc: Loc) -> tracked out : Self
out.value() == P::unit(),out.loc() == loc,Create a unit at a particular location
Sourcepub proof fn validate(tracked &self)
pub proof fn validate(tracked &self)
self.value().valid(),Whenever we have a resource, that resource is valid. This intuitively (and inductively)
holds because:
1. We only create valid resources (either via alloc or
create_unit).
2. We can only update the value of a resource via a frame_preserving_update (see
update for more details. Because of the requirement of that
updates are frame preserving this means that joins remain valid.
Sourcepub proof fn update_nondeterministic(tracked self, new_values: Set<P>) -> tracked out : Self
pub proof fn update_nondeterministic(tracked self, new_values: Set<P>) -> tracked out : Self
frame_preserving_update_nondeterministic(self.value(), new_values),ensuresout.loc() == self.loc(),new_values.contains(out.value()),This is a more general version of update.
Sourcepub proof fn update(tracked self, new_value: P) -> tracked out : Self
pub proof fn update(tracked self, new_value: P) -> tracked out : Self
frame_preserving_update(self.value(), new_value),ensuresout.loc() == self.loc(),out.value() == new_value,Update a resource to a new value. This can only be done if the update is frame preserving (i.e., any value that could be composed with the original value can still be composed with the new value).
See frame_preserving_update for more information.
Sourcepub proof fn extract(tracked &mut self) -> tracked other : Self
pub proof fn extract(tracked &mut self) -> tracked other : Self
other.loc() == old(self).loc(),other.value() == old(self).value(),Extracts the resource from r, leaving r unspecified and returning
a new resource holding the previous value of r.
self.loc() == other.loc(),ensuresout.loc() == self.loc(),incl(self.value(), out.value()),incl(other.value(), out.value()),This is useful when you have two (or more) shared resources and want to learn
that they agree, as you can combine this validate, e.g., x.join_shared(y).validate().
Sourcepub proof fn weaken<'a>(tracked &'a self, target: P) -> tracked out : &'a Self
pub proof fn weaken<'a>(tracked &'a self, target: P) -> tracked out : &'a Self
incl(target, self.value()),ensuresout.loc() == self.loc(),out.value() == target,Extract a shared reference to a preceding element in the extension order from a shared reference.
Sourcepub proof fn validate_2(tracked &mut self, tracked other: &Self)
pub proof fn validate_2(tracked &mut self, tracked other: &Self)
old(self).loc() == other.loc(),ensures*self == *old(self),P::op(self.value(), other.value()).valid(),Validate two items at once. If we have two resources at the same location then its composition is valid.
self.loc() == other.loc(),frame_preserving_update_nondeterministic(
P::op(self.value(), other.value()),
set_op(new_values, other.value()),
),ensuresout.loc() == self.loc(),new_values.contains(out.value()),We can do a similar update to update_with_shared for non-deterministic updates
self.loc() == other.loc(),conjunct_shared(self.value(), other.value(), target),ensuresout.loc() == self.loc(),out.value() == target,We can do a similar update to update_with_shared for non-deterministic updates
self.loc() == other.loc(),frame_preserving_update(
P::op(self.value(), other.value()),
P::op(new_value, other.value()),
),ensuresout.loc() == self.loc(),out.value() == new_value,If x · y --> x · z is a frame-perserving update, and we have a shared reference to x,
we can update the y resource to z.
Sourcepub proof fn duplicate_previous(tracked &self, value: P) -> tracked out : Self
pub proof fn duplicate_previous(tracked &self, value: P) -> tracked out : Self
frame_preserving_update(self.value(), P::op(value, self.value())),ensuresout.loc() == self.loc(),out.value() == value,If x --> x · y is a frame-perserving update, and we have a shared reference to x,
we can create a resource to y
Auto Trait Implementations§
impl<P> Freeze for Resource<P>
impl<P> RefUnwindSafe for Resource<P>where
P: RefUnwindSafe,
impl<P> Send for Resource<P>where
P: Send,
impl<P> Sync for Resource<P>where
P: Sync,
impl<P> Unpin for Resource<P>where
P: Unpin,
impl<P> UnsafeUnpin for Resource<P>
impl<P> UnwindSafe for Resource<P>where
P: 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() }