Struct vstd::rwlock::RwLock

source ·
pub struct RwLock<V, Pred: RwLockPredicate<V>> { /* private fields */ }
Expand description

A verified implementation of a reader-writer lock, implemented using atomics and a reference count.

When constructed, you can provide an invariant via the Pred parameter, specifying the allowed values that can go in the lock.

Note that this specification does not verify the absence of dead-locks.

Examples

On construction of a lock, we can specify an invariant for the object that goes inside. One way to do this is by providing a spec_fn, which implements the RwLockPredicate trait.

fn example1() {
    // We can create a lock with an invariant: `v == 5 || v == 13`.
    // Thus only 5 or 13 can be stored in the lock.
    let lock = RwLock::<u64, spec_fn(u64) -> bool>::new(5, Ghost(|v| v == 5 || v == 13));

    let (val, write_handle) = lock.acquire_write();
    assert(val == 5 || val == 13);
    write_handle.release_write(13);

    let read_handle1 = lock.acquire_read();
    let read_handle2 = lock.acquire_read();

    // We can take multiple read handles at the same time:

    let val1 = read_handle1.borrow();
    let val2 = read_handle2.borrow();

    // RwLock has a lemma that both read handles have the same value:

    proof { ReadHandle::lemma_readers_match(&read_handle1, &read_handle2); }
    assert(*val1 == *val2);

    read_handle1.release_read();
    read_handle2.release_read();
}

It’s often easier to implement the RwLockPredicate trait yourself. This way you can have a configurable predicate without needing to work with higher-order functions.

struct FixedParity {
    pub parity: int,
}

impl RwLockPredicate<u64> for FixedParity {
    open spec fn inv(self, v: u64) -> bool {
        v % 2 == self.parity
    }
}

fn example2() {
    // Create a lock that can only store even integers
    let lock_even = RwLock::<u64, FixedParity>::new(20, Ghost(FixedParity { parity: 0 }));

    // Create a lock that can only store odd integers
    let lock_odd = RwLock::<u64, FixedParity>::new(23, Ghost(FixedParity { parity: 1 }));

    let read_handle_even = lock_even.acquire_read();
    let val_even = *read_handle_even.borrow();
    assert(val_even % 2 == 0);

    let read_handle_odd = lock_odd.acquire_read();
    let val_odd = *read_handle_odd.borrow();
    assert(val_odd % 2 == 1);
}

Implementations§

source§

impl<V, Pred: RwLockPredicate<V>> RwLock<V, Pred>

source

pub closed spec fn pred(&self) -> Pred

Predicate configured for this lock instance.

source

pub open spec fn inv(&self, t: V) -> bool

{ self.pred().inv(t) }

Indicates if the value v can be stored in the lock. Per the definition, it depends on [self.pred()], which is configured upon lock construction (RwLock::new).

source

pub exec fn new(t: V, verus_tmp_pred: Ghost<Pred>) -> Self

requires
pred.inv(t),
ensures
s.pred() == pred,
source

pub exec fn acquire_write(&self) -> (V, WriteHandle<'_, V, Pred>)

ensures
({
    let t = ret.0;
    let write_handle = ret.1;
    &&write_handle.rwlock() == *self && self.inv(t)
}),

Acquires an exclusive write-lock. To release it, use WriteHandle::release_write.

source

pub exec fn acquire_read(&self) -> ReadHandle<'_, V, Pred>

ensures
read_handle.rwlock() == *self,
self.inv(read_handle.view()),

Acquires a shared read-lock. To release it, use ReadHandle::release_read.

source

pub exec fn into_inner(self) -> V

ensures
self.inv(v),

Destroys the lock and returns the inner object. Note that this may deadlock if not all locks have been released.

Auto Trait Implementations§

§

impl<V, Pred> !RefUnwindSafe for RwLock<V, Pred>

§

impl<V, Pred> Send for RwLock<V, Pred>
where Pred: Send, V: Send + Sync,

§

impl<V, Pred> Sync for RwLock<V, Pred>
where Pred: Send + Sync, V: Send + Sync,

§

impl<V, Pred> Unpin for RwLock<V, Pred>
where Pred: Unpin, V: Unpin,

§

impl<V, Pred> UnwindSafe for RwLock<V, Pred>

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, 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, U> TryFrom<U> for T
where 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 T
where 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.