Skip to content

Commit e10fb39

Browse files
committed
prove: RwLockReadGuard::drop
1 parent b5a7c25 commit e10fb39

3 files changed

Lines changed: 160 additions & 15 deletions

File tree

ostd/src/sync/guard.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ pub trait SpinGuardian {
2222
fn read_guard() -> Self::ReadGuard;
2323
}
2424

25-
verus!{
25+
verus! {
26+
2627
/// The Guard can be transferred atomically.
2728
#[verus_verify]
2829
pub trait GuardTransfer: Sized {
@@ -33,10 +34,11 @@ pub trait GuardTransfer: Sized {
3334
///
3435
/// The original guard must be dropped immediately after calling this method.
3536
fn transfer_to(&mut self) -> Self
36-
no_unwind;
37-
}
37+
no_unwind
38+
;
3839
}
3940

41+
} // verus!
4042
/// A guardian that disables preemption while holding a lock.
4143
#[verifier::external]
4244
pub enum PreemptDisabled {}

ostd/src/sync/rwlock.rs

Lines changed: 154 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ type NoPerm<T> = Empty<PointsTo<T>, V_MAX_PERM_FRACS>;
4141
type UpreaderGuardToken = UniqueToken;
4242

4343
type ReadRetractToken = TokenStorage<V_MAX_READ_RETRACT_FRACS>;
44+
type ReadGuardToken = TokenStorage<V_MAX_READ_GUARDS>;
4445

4546
spec const V_MAX_PERM_FRACS_SPEC: u64 = (MAX_READER + 2) as u64;
4647

@@ -68,6 +69,19 @@ exec const V_MAX_READ_RETRACT_FRACS: u64
6869
(MAX_READER_MASK + 1) as u64
6970
}
7071

72+
spec const V_MAX_READ_GUARDS_SPEC: u64 = MAX_READER as u64;
73+
74+
#[verifier::when_used_as_spec(V_MAX_READ_GUARDS_SPEC)]
75+
exec const V_MAX_READ_GUARDS: u64
76+
ensures
77+
V_MAX_READ_GUARDS == V_MAX_READ_GUARDS_SPEC,
78+
V_MAX_READ_GUARDS == MAX_READER,
79+
V_MAX_READ_GUARDS < u64::MAX,
80+
{
81+
assert(MAX_READER < u64::MAX) by (compute_only);
82+
MAX_READER as u64
83+
}
84+
7185
tracked struct RwPerms<T> {
7286
/// The fractional permission of the `PCell<T>`. It can be splited up to `V_MAX_PERM_FRACS:= MAX_READER + 2` pieces,
7387
/// which allows at most `MAX_READER` `RwLockReadGuard`s and 1 `RwLockUpgradeableGuard`, and 1 reserved in the lock atomic.
@@ -79,6 +93,8 @@ tracked struct RwPerms<T> {
7993
/// It can be splited up to `V_MAX_READ_RETRACT_FRACS:= 2 * MAX_READER` pieces,
8094
/// which allows at most `2*MAX_READER - 1` `try_read` attempts that will fail to acquire the lock, and 1 reserved in the lock atomic.
8195
read_retract_token: ReadRetractToken,
96+
/// Tracks live `RwLockReadGuard`s.
97+
read_guard_token: ReadGuardToken,
8298
/// The permission to retract the set of `UPGRADEABLE_READER` bit, it can be spilit at two pieces,
8399
/// which allows at most 1 failing `try_upread` to subtract the `UPGRADEABLE_READER` bit, and 1 reserved in the lock atomic.
84100
upgrade_retract_token: UniqueTokenStorage,
@@ -91,6 +107,7 @@ ghost struct RwId {
91107
upgrade_retract_token_id: Loc,
92108
upreader_guard_token_id: Loc,
93109
read_retract_token_id: Loc,
110+
read_guard_token_id: Loc,
94111
}
95112

96113
/// The number of `try_read` operations recorded in the lock atomic (created and ongoing) can never reach `2*MAX_READER` to avoid overflow.
@@ -226,6 +243,7 @@ closed spec fn wf(self) -> bool {
226243
// The number of `try_read` attempts that will fail.
227244
let failed_reader_attempts: int = total_reader_attempts + upgrade_reader_count - total_successful_readers;
228245
&&& g.read_retract_token.frac() + failed_reader_attempts == V_MAX_READ_RETRACT_FRACS
246+
&&& g.read_guard_token.frac() + successful_read_guards == V_MAX_READ_GUARDS
229247
&&& has_upgrade <==> (has_upreader_guard || pending_failed_upgrade_attempt)
230248
&&& !(has_upreader_guard && pending_failed_upgrade_attempt)
231249
&&& g.upreader_guard_token.is_empty() ==> (v & UPGRADEABLE_READER) != 0usize
@@ -237,6 +255,7 @@ closed spec fn wf(self) -> bool {
237255
&&& g.cell_perm is Right <==> has_writer
238256
&&& 0 <= successful_read_guards <= reader_count <= total_reader_attempts
239257
&&& 0 <= g.read_retract_token.frac() <= V_MAX_READ_RETRACT_FRACS
258+
&&& 0 <= g.read_guard_token.frac() <= V_MAX_READ_GUARDS
240259
&&& pending_failed_upgrade_attempt ==> has_upgrade
241260
&&& match g.cell_perm {
242261
Sum::Right(empty) => {
@@ -252,6 +271,7 @@ closed spec fn wf(self) -> bool {
252271
&&& v_id@.upgrade_retract_token_id == g.upgrade_retract_token.id()
253272
&&& v_id@.upreader_guard_token_id == g.upreader_guard_token.id()
254273
&&& v_id@.read_retract_token_id == g.read_retract_token.id()
274+
&&& v_id@.read_guard_token_id == g.read_guard_token.id()
255275
}
256276
}
257277

@@ -319,17 +339,20 @@ impl<T, G> RwLock<T, G> {
319339
proof {lemma_consts_properties();}
320340
let tracked frac_perm = RwFrac::<T>::new(perm);
321341
let tracked read_retract_token = TokenStorage::<V_MAX_READ_RETRACT_FRACS>::new(());
342+
let tracked read_guard_token = TokenStorage::<V_MAX_READ_GUARDS>::new(());
322343
let tracked upgrade_retract_token = UniqueTokenStorage::new(());
323344
let tracked upreader_guard_token = UniqueTokenStorage::new(());
324345
let ghost v_id = RwId {
325346
cell_perm_id: frac_perm.id(),
326347
upgrade_retract_token_id: upgrade_retract_token.id(),
327348
upreader_guard_token_id: upreader_guard_token.id(),
328349
read_retract_token_id: read_retract_token.id(),
350+
read_guard_token_id: read_guard_token.id(),
329351
};
330352
let tracked perms = RwPerms {
331353
cell_perm: Sum::new_left(frac_perm),
332354
read_retract_token,
355+
read_guard_token,
333356
upgrade_retract_token,
334357
upreader_guard_token,
335358
};
@@ -410,6 +433,7 @@ impl<T /*: ?Sized*/ , G: SpinGuardian> RwLock<T, G> {
410433
pub fn try_read(&self) -> Option<RwLockReadGuard<T, G>> {
411434
proof_decl!{
412435
let tracked mut perm: Option<RwFrac<T>> = None;
436+
let tracked mut read_guard_token: Option<Token<V_MAX_READ_GUARDS>> = None;
413437
let tracked mut retract_read_token: Option<Token<V_MAX_READ_RETRACT_FRACS>> = None;
414438
}
415439
proof!{
@@ -433,14 +457,21 @@ impl<T /*: ?Sized*/ , G: SpinGuardian> RwLock<T, G> {
433457
let tracked mut tmp = g.cell_perm.tracked_take_left();
434458
perm = Some(tmp.split(1int));
435459
g.cell_perm = Sum::new_left(tmp);
460+
g.read_guard_token.bounded();
461+
read_guard_token = Some(g.read_guard_token.split_one());
436462
} else {
437463
g.read_retract_token.bounded();
438464
retract_read_token = Some(g.read_retract_token.split_one());
439465
}
440466
}
441467
);
442468
if lock & (WRITER | MAX_READER | BEING_UPGRADED) == 0 {
443-
Some(RwLockReadGuard { inner: self, guard, v_perm: Tracked(perm.tracked_unwrap()) })
469+
Some(RwLockReadGuard {
470+
inner: self,
471+
guard,
472+
v_perm: Tracked(perm.tracked_unwrap()),
473+
v_token: Tracked(read_guard_token.tracked_unwrap()),
474+
})
444475
} else {
445476
// self.lock.fetch_sub(READER, Release);
446477
atomic_with_ghost!(
@@ -635,6 +666,7 @@ pub struct RwLockReadGuard<'a, T /*: ?Sized*/, G: SpinGuardian> {
635666
guard: G::ReadGuard,
636667
inner: &'a RwLock<T, G>,
637668
v_perm: Tracked<RwFrac<T>>,
669+
v_token: Tracked<Token<V_MAX_READ_GUARDS>>,
638670
}
639671

640672
/*
@@ -653,6 +685,8 @@ impl<'a, T, G: SpinGuardian> RwLockReadGuard<'a, T, G> {
653685
&&& self.inner.cell_perm_id() == self.v_perm@.id()
654686
&&& self.inner.cell_id() == self.v_perm@.resource().id()
655687
&&& self.v_perm@.frac() == 1
688+
&&& self.v_token@.id() == self.inner.v_id@.read_guard_token_id
689+
&&& self.v_token@.frac() == 1
656690
}
657691
}
658692
}
@@ -677,19 +711,129 @@ impl<T /*: ?Sized*/, G: SpinGuardian> Deref for RwLockReadGuard<'_, T, G>
677711
}
678712
}
679713

714+
/* impl<T: ?Sized, R: Deref<Target = RwLock<T, G>> + Clone, G: SpinGuardian> Drop
715+
for RwLockReadGuard_<T, R, G>
716+
{
717+
fn drop(&mut self) {
718+
self.inner.lock.fetch_sub(READER, Release);
719+
}
720+
} */
721+
680722
verus!{
681-
#[verus_verify]
682-
impl<T /*: ?Sized*/, G: SpinGuardian> Drop for RwLockReadGuard<'_, T, G>
723+
724+
impl<T /*: ?Sized*/, G: SpinGuardian> RwLockReadGuard<'_, T, G>
683725
{
684-
#[verifier::external_body]
685-
fn drop(&mut self)
686-
opens_invariants none
687-
no_unwind
726+
/// VERUS LIMITATION: We implement `drop` and call it manually because Verus's support for `Drop` is incomplete for now.
727+
#[verus_spec]
728+
fn drop(self)
688729
{
730+
proof! {
731+
use_type_invariant(&self);
732+
use_type_invariant(self.inner);
733+
lemma_consts_properties();
734+
}
735+
let Tracked(perm) = self.v_perm;
736+
let Tracked(token) = self.v_token;
689737
// self.inner.lock.fetch_sub(READER, Release);
690738
atomic_with_ghost!(
691-
&self.inner.lock => fetch_sub(READER);
692-
ghost g => { }
739+
self.inner.lock => fetch_sub(READER);
740+
update prev -> next;
741+
ghost g => {
742+
let prev_usize = prev as usize;
743+
let next_usize = next as usize;
744+
lemma_consts_properties_value(prev_usize);
745+
lemma_consts_properties_prev_next(prev_usize, next_usize);
746+
let ghost total_successful_readers =
747+
if g.cell_perm is Left {
748+
(V_MAX_PERM_FRACS as int) - g.cell_perm->Left_0.frac()
749+
} else {
750+
0int
751+
};
752+
let ghost pending_failed_upgrade_attempt = g.upgrade_retract_token.is_empty();
753+
let ghost upgrade_reader_count =
754+
if (prev_usize & UPGRADEABLE_READER) != 0 && !pending_failed_upgrade_attempt {
755+
1int
756+
} else {
757+
0int
758+
};
759+
let ghost successful_read_guards = total_successful_readers - upgrade_reader_count;
760+
let ghost old_read_guard_frac = g.read_guard_token.frac();
761+
assert(old_read_guard_frac + successful_read_guards == V_MAX_READ_GUARDS as int);
762+
g.read_guard_token.combine(token);
763+
g.read_guard_token.bounded();
764+
assert(old_read_guard_frac + 1int <= V_MAX_READ_GUARDS as int);
765+
assert(old_read_guard_frac < V_MAX_READ_GUARDS as int);
766+
assert(successful_read_guards > 0);
767+
assert(total_successful_readers > 0);
768+
assert((prev_usize & MAX_READER_MASK) != 0);
769+
if g.cell_perm is Right {
770+
assert(total_successful_readers == 0);
771+
assert(false);
772+
}
773+
assert((prev_usize & WRITER) == 0);
774+
let ghost old_remaining_pcell_perms = g.cell_perm->Left_0.frac();
775+
let tracked mut rem = g.cell_perm.tracked_take_left();
776+
rem.combine(perm);
777+
rem.bounded();
778+
assert(rem.frac() == old_remaining_pcell_perms + 1int);
779+
assert((V_MAX_PERM_FRACS as int) - rem.frac() == total_successful_readers - 1int);
780+
assert((next_usize & MAX_READER_MASK) as int == (prev_usize & MAX_READER_MASK) as int - 1int);
781+
assert((next_usize & UPGRADEABLE_READER) == (prev_usize & UPGRADEABLE_READER));
782+
assert((next_usize & WRITER) == (prev_usize & WRITER));
783+
assert((next_usize & BEING_UPGRADED) == (prev_usize & BEING_UPGRADED));
784+
assert((next_usize & WRITER) == 0);
785+
assert(g.read_guard_token.frac() + (successful_read_guards - 1int) == V_MAX_READ_GUARDS as int);
786+
g.cell_perm = Sum::new_left(rem);
787+
let ghost next_pending_failed_upgrade_attempt = g.upgrade_retract_token.is_empty();
788+
assert(next_pending_failed_upgrade_attempt == pending_failed_upgrade_attempt);
789+
let ghost next_upgrade_reader_count =
790+
if (next_usize & UPGRADEABLE_READER) != 0 && !next_pending_failed_upgrade_attempt {
791+
1int
792+
} else {
793+
0int
794+
};
795+
assert(next_upgrade_reader_count == upgrade_reader_count);
796+
let ghost next_total_successful_readers =
797+
if g.cell_perm is Left {
798+
(V_MAX_PERM_FRACS as int) - g.cell_perm->Left_0.frac()
799+
} else {
800+
0int
801+
};
802+
assert(next_total_successful_readers == total_successful_readers - 1int);
803+
let ghost next_successful_read_guards = next_total_successful_readers - next_upgrade_reader_count;
804+
assert(next_successful_read_guards == successful_read_guards - 1int);
805+
assert(g.read_guard_token.frac() + next_successful_read_guards == V_MAX_READ_GUARDS as int);
806+
let ghost next_total_reader_attempts = (next_usize & MAX_READER_MASK) as int;
807+
assert(next_total_reader_attempts == (prev_usize & MAX_READER_MASK) as int - 1int);
808+
let ghost next_reader_count =
809+
if (next_usize & MAX_READER) != 0 {
810+
MAX_READER as int
811+
} else {
812+
(next_usize & READER_MASK) as int
813+
};
814+
let ghost next_failed_reader_attempts =
815+
next_total_reader_attempts + next_upgrade_reader_count - next_total_successful_readers;
816+
let ghost failed_reader_attempts =
817+
((prev_usize & MAX_READER_MASK) as int) + upgrade_reader_count - total_successful_readers;
818+
assert(next_failed_reader_attempts == failed_reader_attempts);
819+
assert(g.read_retract_token.frac() + next_failed_reader_attempts == V_MAX_READ_RETRACT_FRACS);
820+
assert(0 <= next_successful_read_guards);
821+
if (next_usize & MAX_READER) != 0 {
822+
assert(next_reader_count == MAX_READER as int);
823+
assert((next_usize & MAX_READER_MASK) >= MAX_READER) by (bit_vector)
824+
requires
825+
(next_usize & MAX_READER) != 0,
826+
;
827+
} else {
828+
assert((next_usize & MAX_READER_MASK) == (next_usize & READER_MASK)) by (bit_vector)
829+
requires
830+
(next_usize & MAX_READER) == 0,
831+
;
832+
assert(next_reader_count == next_total_reader_attempts);
833+
}
834+
assert(next_reader_count <= next_total_reader_attempts);
835+
assert(next_successful_read_guards <= next_reader_count);
836+
}
693837
);
694838
}
695839
}
@@ -1065,7 +1209,7 @@ impl<T /*: ?Sized*/, G: SpinGuardian> RwLockUpgradeableGuard<'_, T, G>
10651209
use_type_invariant(self.inner);
10661210
lemma_consts_properties();
10671211
}
1068-
let Tracked(perm) = self.v_perm;
1212+
let Tracked(perm) = self.v_perm;
10691213
let Tracked(token) = self.v_token;
10701214
//self.inner.lock.fetch_sub(UPGRADEABLE_READER, Release);
10711215
atomic_with_ghost!(

vstd_extra/src/resource/tokens.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,7 @@ impl<T, const TOTAL: u64> FracGhostStorage<T, TOTAL> {
3636
}
3737

3838
/// Returns the `FracGhost<T,TOTAL>` stored in this `FracGhostStorage`.
39-
pub closed spec fn storage(self) -> FracGhost<T, TOTAL>
40-
{
39+
pub closed spec fn storage(self) -> FracGhost<T, TOTAL> {
4140
self.r->Some_0
4241
}
4342

0 commit comments

Comments
 (0)