1#![allow(non_snake_case)]
2#![allow(unused_imports)]
3#![allow(non_shorthand_field_patterns)]
4
5use super::atomic_ghost::*;
6use super::cell::CellId;
7use super::cell::pcell_maybe_uninit as un;
8use super::invariant::InvariantPredicate;
9use super::modes::*;
10use super::multiset::*;
11use super::prelude::*;
12use super::set::*;
13use core::marker::PhantomData;
14use verus_state_machines_macros::tokenized_state_machine_vstd;
15
16tokenized_state_machine_vstd!(
17RwLockToks<K, V, Pred: InvariantPredicate<K, V>> {
18 fields {
19 #[sharding(constant)]
20 pub k: K,
21
22 #[sharding(constant)]
23 pub pred: PhantomData<Pred>,
24
25 #[sharding(variable)]
26 pub flag_exc: bool,
27
28 #[sharding(variable)]
29 pub flag_rc: nat,
30
31 #[sharding(storage_option)]
32 pub storage: Option<V>,
33
34 #[sharding(option)]
35 pub pending_writer: Option<()>,
36
37 #[sharding(option)]
38 pub writer: Option<()>,
39
40 #[sharding(multiset)]
41 pub pending_reader: Multiset<()>,
42
43 #[sharding(multiset)]
44 pub reader: Multiset<V>,
45 }
46
47 init!{
48 initialize_full(k: K, t: V) {
49 require Pred::inv(k, t);
50 init k = k;
51 init pred = PhantomData;
52 init flag_exc = false;
53 init flag_rc = 0;
54 init storage = Option::Some(t);
55 init pending_writer = Option::None;
56 init writer = Option::None;
57 init pending_reader = Multiset::empty();
58 init reader = Multiset::empty();
59 }
60 }
61
62 #[inductive(initialize_full)]
63 fn initialize_full_inductive(post: Self, k: K, t: V) {
64 broadcast use group_multiset_axioms;
65 }
66
67 transition!{
69 acquire_read_start() {
70 update flag_rc = pre.flag_rc + 1;
71 add pending_reader += {()};
72 }
73 }
74
75 transition!{
78 acquire_read_end() {
79 require(pre.flag_exc == false);
80
81 remove pending_reader -= {()};
82
83 birds_eye let x: V = pre.storage->0;
84 add reader += {x};
85
86 assert Pred::inv(pre.k, x);
87 }
88 }
89
90 transition!{
93 acquire_read_abandon() {
94 remove pending_reader -= {()};
95 assert(pre.flag_rc >= 1);
96 update flag_rc = (pre.flag_rc - 1) as nat;
97 }
98 }
99
100 transition!{
103 acquire_exc_start() {
104 require(pre.flag_exc == false);
105 update flag_exc = true;
106 add pending_writer += Some(());
107 }
108 }
109
110 transition!{
114 acquire_exc_end() {
115 require(pre.flag_rc == 0);
116
117 remove pending_writer -= Some(());
118
119 add writer += Some(());
120
121 birds_eye let x = pre.storage->0;
122 withdraw storage -= Some(x);
123
124 assert Pred::inv(pre.k, x);
125 }
126 }
127
128 transition!{
131 release_exc(x: V) {
132 require Pred::inv(pre.k, x);
133 remove writer -= Some(());
134
135 update flag_exc = false;
136
137 deposit storage += Some(x);
138 }
139 }
140
141 property!{
143 read_guard(x: V) {
144 have reader >= {x};
145 guard storage >= Some(x);
146 }
147 }
148
149 property!{
150 read_match(x: V, y: V) {
151 have reader >= {x};
152 have reader >= {y};
153 assert(equal(x, y));
154 }
155 }
156
157 #[transition]
159 transition!{
160 release_shared(x: V) {
161 remove reader -= {x};
162
163 assert(pre.flag_rc >= 1) by {
164 assert(equal(pre.storage, Option::Some(x)));
166 };
168 update flag_rc = (pre.flag_rc - 1) as nat;
169 }
170 }
171
172 #[invariant]
173 pub fn exc_bit_matches(&self) -> bool {
174 (if self.flag_exc { 1 } else { 0 as int }) ==
175 (if self.pending_writer is Some { 1 } else { 0 as int }) as int
176 + (if self.writer is Some { 1 } else { 0 as int }) as int
177 }
178
179 #[invariant]
180 pub fn count_matches(&self) -> bool {
181 self.flag_rc == self.pending_reader.count(())
182 + self.reader.count(self.storage->0)
183 }
184
185 #[invariant]
186 pub fn reader_agrees_storage(&self) -> bool {
187 forall |t: V| imply(#[trigger] self.reader.count(t) > 0,
188 equal(self.storage, Option::Some(t)))
189 }
190
191 #[invariant]
192 pub fn writer_agrees_storage(&self) -> bool {
193 imply(self.writer is Some, self.storage is None)
194 }
195
196 #[invariant]
197 pub fn writer_agrees_storage_rev(&self) -> bool {
198 imply(self.storage is None, self.writer is Some)
199 }
200
201 #[invariant]
202 pub fn sto_user_inv(&self) -> bool {
203 self.storage.is_some() ==> Pred::inv(self.k, self.storage.unwrap())
204 }
205
206 #[inductive(acquire_read_start)]
207 fn acquire_read_start_inductive(pre: Self, post: Self) {
208 broadcast use group_multiset_axioms;
209 }
210
211 #[inductive(acquire_read_end)]
212 fn acquire_read_end_inductive(pre: Self, post: Self) {
213 broadcast use group_multiset_axioms;
214 }
215
216 #[inductive(acquire_read_abandon)]
217 fn acquire_read_abandon_inductive(pre: Self, post: Self) {
218 broadcast use group_multiset_axioms;
219 }
220
221 #[inductive(acquire_exc_start)]
222 fn acquire_exc_start_inductive(pre: Self, post: Self) { }
223
224 #[inductive(acquire_exc_end)]
225 fn acquire_exc_end_inductive(pre: Self, post: Self) { }
226
227 #[inductive(release_exc)]
228 fn release_exc_inductive(pre: Self, post: Self, x: V) { }
229
230 #[inductive(release_shared)]
231 fn release_shared_inductive(pre: Self, post: Self, x: V) {
232 broadcast use group_multiset_axioms;
233 assert(equal(pre.storage, Option::Some(x)));
234 }
235});
236
237verus! {
238
239pub trait RwLockPredicate<V>: Sized {
240 spec fn inv(self, v: V) -> bool;
241}
242
243impl<V> RwLockPredicate<V> for spec_fn(V) -> bool {
244 open spec fn inv(self, v: V) -> bool {
245 self(v)
246 }
247}
248
249ghost struct InternalPred<V, Pred> {
250 v: V,
251 pred: Pred,
252}
253
254impl<V, Pred: RwLockPredicate<V>> InvariantPredicate<
255 (Pred, CellId),
256 un::PointsTo<V>,
257> for InternalPred<V, Pred> {
258 closed spec fn inv(k: (Pred, CellId), v: un::PointsTo<V>) -> bool {
259 v.id() == k.1 && v.is_init() && k.0.inv(v.value())
260 }
261}
262
263struct_with_invariants!{
264 pub struct RwLock<V, Pred: RwLockPredicate<V>> {
339 cell: un::PCell<V>,
340 exc: AtomicBool<_, RwLockToks::flag_exc<(Pred, CellId), un::PointsTo<V>, InternalPred<V, Pred>>, _>,
341 rc: AtomicUsize<_, RwLockToks::flag_rc<(Pred, CellId), un::PointsTo<V>, InternalPred<V, Pred>>, _>,
342
343 inst: Tracked<RwLockToks::Instance<(Pred, CellId), un::PointsTo<V>, InternalPred<V, Pred>>>,
344 pred: Ghost<Pred>,
345 }
346
347 #[verifier::type_invariant]
348 spec fn wf(&self) -> bool {
349 invariant on exc with (inst) is (v: bool, g: RwLockToks::flag_exc<(Pred, CellId), un::PointsTo<V>, InternalPred<V, Pred>>) {
350 g.instance_id() == inst@.id()
351 && g.value() == v
352 }
353
354 invariant on rc with (inst) is (v: usize, g: RwLockToks::flag_rc<(Pred, CellId), un::PointsTo<V>, InternalPred<V, Pred>>) {
355 g.instance_id() == inst@.id()
356 && g.value() == v
357 }
358
359 predicate {
360 self.inst@.k() == (self.pred@, self.cell.id())
361 }
362 }
363}
364
365pub struct WriteHandle<'a, V, Pred: RwLockPredicate<V>> {
375 handle: Tracked<RwLockToks::writer<(Pred, CellId), un::PointsTo<V>, InternalPred<V, Pred>>>,
376 perm: Tracked<un::PointsTo<V>>,
377 rwlock: &'a RwLock<V, Pred>,
378}
379
380pub struct ReadHandle<'a, V, Pred: RwLockPredicate<V>> {
386 handle: Tracked<RwLockToks::reader<(Pred, CellId), un::PointsTo<V>, InternalPred<V, Pred>>>,
387 rwlock: &'a RwLock<V, Pred>,
388}
389
390impl<'a, V, Pred: RwLockPredicate<V>> WriteHandle<'a, V, Pred> {
391 #[verifier::type_invariant]
392 spec fn wf_write_handle(self) -> bool {
393 equal(self.perm@.id(), self.rwlock.cell.id()) && self.perm@.is_uninit() && equal(
394 self.handle@.instance_id(),
395 self.rwlock.inst@.id(),
396 ) && self.rwlock.wf()
397 }
398
399 pub closed spec fn rwlock(self) -> RwLock<V, Pred> {
400 *self.rwlock
401 }
402
403 pub fn release_write(self, new_val: V)
404 requires
405 self.rwlock().inv(new_val),
406 {
407 proof {
408 use_type_invariant(&self);
409 }
410 let WriteHandle { handle: Tracked(handle), perm: Tracked(mut perm), rwlock } = self;
411 self.rwlock.cell.put(Tracked(&mut perm), new_val);
412
413 atomic_with_ghost!(
414 &rwlock.exc => store(false);
415 ghost g =>
416 {
417 self.rwlock.inst.borrow().release_exc(perm, &mut g, perm, handle);
418 });
419 }
420}
421
422impl<'a, V, Pred: RwLockPredicate<V>> ReadHandle<'a, V, Pred> {
423 #[verifier::type_invariant]
424 spec fn wf_read_handle(self) -> bool {
425 equal(self.handle@.instance_id(), self.rwlock.inst@.id())
426 && self.handle@.element().is_init() && equal(
427 self.handle@.element().id(),
428 self.rwlock.cell.id(),
429 ) && self.rwlock.wf()
430 }
431
432 pub closed spec fn view(self) -> V {
433 self.handle@.element().value()
434 }
435
436 pub closed spec fn rwlock(self) -> RwLock<V, Pred> {
437 *self.rwlock
438 }
439
440 pub fn borrow<'b>(&'b self) -> (val: &'b V)
442 ensures
443 val == self.view(),
444 {
445 proof {
446 use_type_invariant(self);
447 }
448 let tracked perm = self.rwlock.inst.borrow().read_guard(
449 self.handle@.element(),
450 self.handle.borrow(),
451 );
452 self.rwlock.cell.borrow(Tracked(&perm))
453 }
454
455 pub proof fn lemma_readers_match(
456 tracked read_handle1: &ReadHandle<V, Pred>,
457 tracked read_handle2: &ReadHandle<V, Pred>,
458 )
459 requires
460 read_handle1.rwlock() == read_handle2.rwlock(),
461 ensures
462 (equal(read_handle1.view(), read_handle2.view())),
463 {
464 use_type_invariant(read_handle1);
465 use_type_invariant(read_handle2);
466 read_handle1.rwlock.inst.borrow().read_match(
467 read_handle1.handle@.element(),
468 read_handle2.handle@.element(),
469 &read_handle1.handle.borrow(),
470 &read_handle2.handle.borrow(),
471 );
472 }
473
474 pub fn release_read(self) {
475 proof {
476 use_type_invariant(&self);
477 }
478 let ReadHandle { handle: Tracked(handle), rwlock } = self;
479
480 let _ =
481 atomic_with_ghost!(
482 &rwlock.rc => fetch_sub(1);
483 ghost g =>
484 {
485 rwlock.inst.borrow().release_shared(handle.element(), &mut g, handle);
486 });
487 }
488}
489
490impl<V, Pred: RwLockPredicate<V>> RwLock<V, Pred> {
491 pub closed spec fn pred(&self) -> Pred {
493 self.pred@
494 }
495
496 pub open spec fn inv(&self, val: V) -> bool {
499 self.pred().inv(val)
500 }
501
502 pub fn new(val: V, Ghost(pred): Ghost<Pred>) -> (s: Self)
503 requires
504 pred.inv(val),
505 ensures
506 s.pred() == pred,
507 {
508 let (cell, Tracked(perm)) = un::PCell::<V>::new(val);
509
510 let tracked (Tracked(inst), Tracked(flag_exc), Tracked(flag_rc), _, _, _, _) =
511 RwLockToks::Instance::<
512 (Pred, CellId),
513 un::PointsTo<V>,
514 InternalPred<V, Pred>,
515 >::initialize_full((pred, cell.id()), perm, Option::Some(perm));
516 let inst = Tracked(inst);
517
518 let exc = AtomicBool::new(Ghost(inst), false, Tracked(flag_exc));
519 let rc = AtomicUsize::new(Ghost(inst), 0, Tracked(flag_rc));
520
521 RwLock { cell, exc, rc, inst, pred: Ghost(pred) }
522 }
523
524 #[verifier::exec_allows_no_decreases_clause]
530 pub fn acquire_write(&self) -> (ret: (V, WriteHandle<'_, V, Pred>))
531 ensures
532 ({
533 let val = ret.0;
534 let write_handle = ret.1;
535 &&& write_handle.rwlock() == *self
536 &&& self.inv(val)
537 }),
538 {
539 proof {
540 use_type_invariant(self);
541 }
542 let mut done = false;
543 let tracked mut token: Option<
544 RwLockToks::pending_writer<(Pred, CellId), un::PointsTo<V>, InternalPred<V, Pred>>,
545 > = Option::None;
546 while !done
547 invariant
548 done ==> token.is_some() && equal(token->0.instance_id(), self.inst@.id()),
549 self.wf(),
550 {
551 let result =
552 atomic_with_ghost!(
553 &self.exc => compare_exchange(false, true);
554 returning res;
555 ghost g =>
556 {
557 if res is Ok {
558 token = Option::Some(self.inst.borrow().acquire_exc_start(&mut g));
559 }
560 });
561
562 done =
563 match result {
564 Result::Ok(_) => true,
565 _ => false,
566 };
567 }
568 loop
569 invariant
570 token is Some && equal(token->0.instance_id(), self.inst@.id()),
571 self.wf(),
572 {
573 let tracked mut perm_opt: Option<un::PointsTo<V>> = None;
574 let tracked mut handle_opt: Option<
575 RwLockToks::writer<(Pred, CellId), un::PointsTo<V>, InternalPred<V, Pred>>,
576 > = None;
577
578 let result =
579 atomic_with_ghost!(
580 &self.rc => load();
581 returning res;
582 ghost g =>
583 {
584 if res == 0 {
585 let tracked tok = match token { Option::Some(t) => t, Option::None => proof_from_false() };
586 let tracked x = self.inst.borrow().acquire_exc_end(&g, tok);
587 token = None;
588 let tracked (_, Tracked(perm), Tracked(exc_handle)) = x;
589 perm_opt = Some(perm);
590 handle_opt = Some(exc_handle);
591 }
592 });
593
594 if result == 0 {
595 let tracked mut perm = match perm_opt {
596 Option::Some(t) => t,
597 Option::None => proof_from_false(),
598 };
599 let tracked handle = match handle_opt {
600 Option::Some(t) => t,
601 Option::None => proof_from_false(),
602 };
603 let t = self.cell.take(Tracked(&mut perm));
604 let write_handle = WriteHandle {
605 perm: Tracked(perm),
606 handle: Tracked(handle),
607 rwlock: self,
608 };
609 return (t, write_handle);
610 }
611 }
612 }
613
614 #[verifier::exec_allows_no_decreases_clause]
620 pub fn acquire_read(&self) -> (read_handle: ReadHandle<'_, V, Pred>)
621 ensures
622 read_handle.rwlock() == *self,
623 self.inv(read_handle.view()),
624 {
625 proof {
626 use_type_invariant(self);
627 }
628 loop
629 invariant
630 self.wf(),
631 {
632 let val = atomic_with_ghost!(&self.rc => load(); ghost g => { });
633
634 let tracked mut token: Option<
635 RwLockToks::pending_reader<(Pred, CellId), un::PointsTo<V>, InternalPred<V, Pred>>,
636 > = Option::None;
637
638 if val < usize::MAX {
639 let result =
640 atomic_with_ghost!(
641 &self.rc => compare_exchange(val, val + 1);
642 returning res;
643 ghost g =>
644 {
645 if res is Ok {
646 token = Option::Some(self.inst.borrow().acquire_read_start(&mut g));
647 }
648 });
649
650 match result {
651 Result::Ok(_) => {
652 let tracked mut handle_opt: Option<
653 RwLockToks::reader<
654 (Pred, CellId),
655 un::PointsTo<V>,
656 InternalPred<V, Pred>,
657 >,
658 > = None;
659
660 let result =
661 atomic_with_ghost!(
662 &self.exc => load();
663 returning res;
664 ghost g =>
665 {
666 if res == false {
667 let tracked tok = match token { Option::Some(t) => t, Option::None => proof_from_false() };
668 let tracked x = self.inst.borrow().acquire_read_end(&g, tok);
669 token = None;
670 let tracked (_, Tracked(exc_handle)) = x;
671 handle_opt = Some(exc_handle);
672 }
673 });
674
675 if result == false {
676 let tracked handle = match handle_opt {
677 Option::Some(t) => t,
678 Option::None => proof_from_false(),
679 };
680 let read_handle = ReadHandle { handle: Tracked(handle), rwlock: self };
681 return read_handle;
682 } else {
683 let _ =
684 atomic_with_ghost!(
685 &self.rc => fetch_sub(1);
686 ghost g =>
687 {
688 let tracked tok = match token { Option::Some(t) => t, Option::None => proof_from_false() };
689 self.inst.borrow().acquire_read_abandon(&mut g, tok);
690 });
691 }
692 },
693 _ => {},
694 }
695 }
696 }
697 }
698
699 #[verifier::exec_allows_no_decreases_clause]
702 pub fn into_inner(self) -> (v: V)
703 ensures
704 self.inv(v),
705 {
706 let (v, _write_handle) = self.acquire_write();
707 v
708 }
709}
710
711}