1use vstd::{
9 prelude::*,
10 resource::{Loc, frac_opt::Frac},
11};
12
13verus! {
14
15pub tracked struct RcuLeaseAccumulator<T> {
17 frac: Frac<T>,
18}
19
20pub tracked struct RcuReadLease<T> {
22 frac: Frac<T>,
23}
24
25pub tracked struct RcuRegisteredReadLease<K, T> {
32 ghost lease_id: nat,
33 ghost key: K,
34 lease: RcuReadLease<T>,
35}
36
37pub tracked struct RcuActiveReadLeaseRecord<K, W> {
43 ghost key: K,
44 ghost accumulator_id: Loc,
45 ghost fraction: real,
46 witness: W,
47}
48
49pub tracked struct RcuReadLeaseRegistry<K, T, W> {
58 accumulators: Map<K, RcuLeaseAccumulator<T>>,
59 active: Map<nat, RcuActiveReadLeaseRecord<K, W>>,
60 ghost next_lease: nat,
61}
62
63impl<K, W> RcuActiveReadLeaseRecord<K, W> {
64 pub closed spec fn key(self) -> K {
65 self.key
66 }
67
68 pub closed spec fn accumulator_id(self) -> Loc {
69 self.accumulator_id
70 }
71
72 pub closed spec fn fraction(self) -> real {
73 self.fraction
74 }
75
76 pub closed spec fn witness(self) -> W {
77 self.witness
78 }
79}
80
81impl<K, T> RcuRegisteredReadLease<K, T> {
82 pub closed spec fn lease_id(self) -> nat {
83 self.lease_id
84 }
85
86 pub closed spec fn key(self) -> K {
87 self.key
88 }
89
90 pub closed spec fn accumulator_id(self) -> Loc {
91 self.lease.id()
92 }
93
94 pub closed spec fn resource(self) -> T {
95 self.lease.resource()
96 }
97
98 pub closed spec fn fraction(self) -> real {
99 self.lease.fraction()
100 }
101
102 pub proof fn tracked_borrow(tracked &self) -> (tracked resource: &T)
104 ensures
105 *resource == self.resource(),
106 {
107 self.lease.tracked_borrow()
108 }
109}
110
111pub open spec fn active_lease_fraction<K, W>(
113 active: Map<nat, RcuActiveReadLeaseRecord<K, W>>,
114 key: K,
115 upto: nat,
116) -> real
117 decreases upto,
118{
119 if upto == 0 {
120 0real
121 } else {
122 let id = (upto - 1) as nat;
123 active_lease_fraction(active, key, id) + if active.contains_key(id) && active[id].key()
124 == key {
125 active[id].fraction()
126 } else {
127 0real
128 }
129 }
130}
131
132proof fn lemma_active_fraction_insert_above<K, W>(
133 active: Map<nat, RcuActiveReadLeaseRecord<K, W>>,
134 inserted: nat,
135 record: RcuActiveReadLeaseRecord<K, W>,
136 key: K,
137 upto: nat,
138)
139 requires
140 upto <= inserted,
141 ensures
142 active_lease_fraction(active.insert(inserted, record), key, upto) == active_lease_fraction(
143 active,
144 key,
145 upto,
146 ),
147 decreases upto,
148{
149 if upto > 0 {
150 let id = (upto - 1) as nat;
151 lemma_active_fraction_insert_above(active, inserted, record, key, id);
152 assert(id < inserted);
153 assert(active.insert(inserted, record).contains_key(id) == active.contains_key(id));
154 if active.contains_key(id) {
155 assert(active.insert(inserted, record)[id] == active[id]);
156 }
157 }
158}
159
160proof fn lemma_active_fraction_insert_next<K, W>(
161 active: Map<nat, RcuActiveReadLeaseRecord<K, W>>,
162 next: nat,
163 record: RcuActiveReadLeaseRecord<K, W>,
164 key: K,
165)
166 ensures
167 active_lease_fraction(active.insert(next, record), key, next + 1) == active_lease_fraction(
168 active,
169 key,
170 next,
171 ) + if record.key() == key {
172 record.fraction()
173 } else {
174 0real
175 },
176{
177 lemma_active_fraction_insert_above(active, next, record, key, next);
178}
179
180proof fn lemma_active_fraction_remove<K, W>(
181 active: Map<nat, RcuActiveReadLeaseRecord<K, W>>,
182 removed: nat,
183 key: K,
184 upto: nat,
185)
186 requires
187 removed < upto,
188 active.contains_key(removed),
189 ensures
190 active_lease_fraction(active.remove(removed), key, upto) == active_lease_fraction(
191 active,
192 key,
193 upto,
194 ) - if active[removed].key() == key {
195 active[removed].fraction()
196 } else {
197 0real
198 },
199 decreases upto,
200{
201 let id = (upto - 1) as nat;
202 if removed == id {
203 lemma_active_fraction_remove_above(active, removed, key, id);
204 assert(!active.remove(removed).contains_key(id));
205 assert(active_lease_fraction(active.remove(removed), key, upto) == active_lease_fraction(
206 active.remove(removed),
207 key,
208 id,
209 ));
210 assert(active_lease_fraction(active, key, upto) == active_lease_fraction(active, key, id)
211 + if active[removed].key() == key {
212 active[removed].fraction()
213 } else {
214 0real
215 });
216 } else {
217 assert(removed < id);
218 lemma_active_fraction_remove(active, removed, key, id);
219 assert(active.remove(removed).contains_key(id) == active.contains_key(id));
220 if active.contains_key(id) {
221 assert(active.remove(removed)[id] == active[id]);
222 }
223 assert(active_lease_fraction(active.remove(removed), key, upto) == active_lease_fraction(
224 active.remove(removed),
225 key,
226 id,
227 ) + if active.contains_key(id) && active[id].key() == key {
228 active[id].fraction()
229 } else {
230 0real
231 });
232 assert(active_lease_fraction(active, key, upto) == active_lease_fraction(active, key, id)
233 + if active.contains_key(id) && active[id].key() == key {
234 active[id].fraction()
235 } else {
236 0real
237 });
238 }
239}
240
241proof fn lemma_active_fraction_remove_above<K, W>(
242 active: Map<nat, RcuActiveReadLeaseRecord<K, W>>,
243 removed: nat,
244 key: K,
245 upto: nat,
246)
247 requires
248 upto <= removed,
249 ensures
250 active_lease_fraction(active.remove(removed), key, upto) == active_lease_fraction(
251 active,
252 key,
253 upto,
254 ),
255 decreases upto,
256{
257 if upto > 0 {
258 let id = (upto - 1) as nat;
259 lemma_active_fraction_remove_above(active, removed, key, id);
260 assert(id < removed);
261 assert(active.remove(removed).contains_key(id) == active.contains_key(id));
262 if active.contains_key(id) {
263 assert(active.remove(removed)[id] == active[id]);
264 }
265 }
266}
267
268proof fn lemma_active_fraction_zero<K, W>(
269 active: Map<nat, RcuActiveReadLeaseRecord<K, W>>,
270 key: K,
271 upto: nat,
272)
273 requires
274 forall|id: nat| #![auto] id < upto && active.contains_key(id) ==> active[id].key() != key,
275 ensures
276 active_lease_fraction(active, key, upto) == 0real,
277 decreases upto,
278{
279 if upto > 0 {
280 let id = (upto - 1) as nat;
281 lemma_active_fraction_zero(active, key, id);
282 }
283}
284
285impl<T> RcuLeaseAccumulator<T> {
286 pub proof fn new(tracked resource: T) -> (tracked res: Self)
288 ensures
289 res.resource() == resource,
290 res.fraction() == 1real,
291 {
292 let tracked frac = Frac::new(resource);
293 RcuLeaseAccumulator { frac }
294 }
295
296 pub closed spec fn id(self) -> Loc {
298 self.frac.id()
299 }
300
301 pub closed spec fn resource(self) -> T {
303 self.frac.resource()
304 }
305
306 pub closed spec fn fraction(self) -> real {
308 self.frac.frac()
309 }
310
311 pub proof fn split_lease(tracked &mut self) -> (tracked lease: RcuReadLease<T>)
313 ensures
314 final(self).id() == old(self).id(),
315 final(self).resource() == old(self).resource(),
316 lease.id() == old(self).id(),
317 lease.resource() == old(self).resource(),
318 final(self).fraction() == old(self).fraction() / 2real,
319 lease.fraction() == old(self).fraction() / 2real,
320 {
321 let tracked frac = self.frac.split();
322 RcuReadLease { frac }
323 }
324
325 pub proof fn return_lease(tracked &mut self, tracked lease: RcuReadLease<T>)
327 requires
328 old(self).id() == lease.id(),
329 ensures
330 final(self).id() == old(self).id(),
331 final(self).resource() == old(self).resource(),
332 final(self).resource() == lease.resource(),
333 final(self).fraction() == old(self).fraction() + lease.fraction(),
334 {
335 self.frac.combine(lease.frac);
336 }
337
338 pub proof fn reclaim(tracked self) -> (tracked resource: T)
340 requires
341 self.fraction() == 1real,
342 ensures
343 resource == self.resource(),
344 {
345 let tracked (resource, _empty) = self.frac.take_resource();
346 resource
347 }
348
349 pub proof fn lemma_fraction_bounded(tracked &self)
351 ensures
352 0real < self.fraction() <= 1real,
353 {
354 self.frac.bounded();
355 }
356}
357
358impl<T> RcuReadLease<T> {
359 pub closed spec fn id(self) -> Loc {
361 self.frac.id()
362 }
363
364 pub closed spec fn resource(self) -> T {
366 self.frac.resource()
367 }
368
369 pub closed spec fn fraction(self) -> real {
371 self.frac.frac()
372 }
373
374 pub proof fn tracked_borrow(tracked &self) -> (tracked resource: &T)
376 ensures
377 *resource == self.resource(),
378 {
379 self.frac.borrow()
380 }
381
382 pub proof fn lemma_fraction_bounded(tracked &self)
384 ensures
385 0real < self.fraction() <= 1real,
386 {
387 self.frac.bounded();
388 }
389}
390
391impl<K, T, W> RcuReadLeaseRegistry<K, T, W> {
392 pub proof fn empty() -> (tracked res: Self)
394 ensures
395 res.wf(),
396 res.keys() == Set::<K>::empty(),
397 res.active_ids() == Set::<nat>::empty(),
398 res.next_lease() == 0,
399 {
400 RcuReadLeaseRegistry {
401 accumulators: Map::tracked_empty(),
402 active: Map::tracked_empty(),
403 next_lease: 0,
404 }
405 }
406
407 pub closed spec fn keys(self) -> Set<K> {
408 self.accumulators.dom()
409 }
410
411 pub closed spec fn contains(self, key: K) -> bool {
412 self.accumulators.contains_key(key)
413 }
414
415 pub proof fn lemma_contains_iff_key(tracked &self, key: K)
417 ensures
418 self.contains(key) <==> self.keys().contains(key),
419 {
420 }
421
422 pub proof fn lemma_all_contains_iff_keys(tracked &self)
424 ensures
425 forall|key: K| #[trigger] self.contains(key) <==> self.keys().contains(key),
426 {
427 }
428
429 pub closed spec fn accumulator(self, key: K) -> RcuLeaseAccumulator<T>
430 recommends
431 self.contains(key),
432 {
433 self.accumulators[key]
434 }
435
436 pub closed spec fn active_ids(self) -> Set<nat> {
437 self.active.dom()
438 }
439
440 pub closed spec fn active_records(self) -> Map<nat, RcuActiveReadLeaseRecord<K, W>> {
442 self.active
443 }
444
445 pub closed spec fn next_lease(self) -> nat {
446 self.next_lease
447 }
448
449 pub closed spec fn active_record(self, lease_id: nat) -> RcuActiveReadLeaseRecord<K, W>
450 recommends
451 self.active_ids().contains(lease_id),
452 {
453 self.active[lease_id]
454 }
455
456 pub proof fn tracked_borrow_active_witness(tracked &self, lease_id: nat) -> (tracked witness:
462 &W)
463 requires
464 self.active_ids().contains(lease_id),
465 ensures
466 *witness == self.active_record(lease_id).witness(),
467 {
468 let tracked record = self.active.tracked_borrow(lease_id);
469 &record.witness
470 }
471
472 pub proof fn tracked_borrow_active_witness_mut(
477 tracked &mut self,
478 lease_id: nat,
479 ) -> (tracked witness: &mut W)
480 requires
481 old(self).active_ids().contains(lease_id),
482 ensures
483 *witness == old(self).active_record(lease_id).witness(),
484 final(self).keys() == old(self).keys(),
485 final(self).active_ids() == old(self).active_ids(),
486 final(self).next_lease() == old(self).next_lease(),
487 final(self).active_record(lease_id).key() == old(self).active_record(lease_id).key(),
488 final(self).active_record(lease_id).accumulator_id() == old(self).active_record(
489 lease_id,
490 ).accumulator_id(),
491 final(self).active_record(lease_id).fraction() == old(self).active_record(
492 lease_id,
493 ).fraction(),
494 final(self).active_record(lease_id).witness() == *final(witness),
495 forall|other: nat|
496 #![auto]
497 other != lease_id && old(self).active_ids().contains(other)
498 ==> final(self).active_record(other) == old(self).active_record(other),
499 {
500 let tracked record = self.active.tracked_borrow_mut(lease_id);
501 &mut record.witness
502 }
503
504 pub open spec fn has_active(self, key: K) -> bool {
505 exists|lease_id: nat|
506 #![auto]
507 self.active_ids().contains(lease_id) && self.active_record(lease_id).key() == key
508 }
509
510 pub open spec fn wf(self) -> bool {
511 &&& forall|lease_id: nat| #[trigger]
512 self.active_ids().contains(lease_id) ==> {
513 let record = self.active_record(lease_id);
514 &&& lease_id < self.next_lease()
515 &&& self.contains(record.key())
516 &&& record.accumulator_id() == self.accumulator(record.key()).id()
517 &&& record.fraction() > 0real
518 }
519 &&& forall|key: K| #[trigger]
520 self.contains(key) ==> self.accumulator(key).fraction() + active_lease_fraction(
521 self.active_records(),
522 key,
523 self.next_lease(),
524 ) == 1real
525 }
526
527 pub proof fn insert(tracked &mut self, key: K, tracked resource: T)
529 requires
530 old(self).wf(),
531 !old(self).contains(key),
532 ensures
533 final(self).wf(),
534 final(self).keys() == old(self).keys().insert(key),
535 final(self).active_ids() == old(self).active_ids(),
536 final(self).next_lease() == old(self).next_lease(),
537 forall|lease_id: nat|
538 #![auto]
539 old(self).active_ids().contains(lease_id) ==> final(self).active_record(lease_id)
540 == old(self).active_record(lease_id),
541 final(self).contains(key),
542 final(self).accumulator(key).resource() == resource,
543 final(self).accumulator(key).fraction() == 1real,
544 forall|other: K|
545 old(self).contains(other) ==> final(self).accumulator(other) == old(
546 self,
547 ).accumulator(other),
548 {
549 reveal(RcuReadLeaseRegistry::active_ids);
550 reveal(RcuReadLeaseRegistry::active_records);
551 reveal(RcuReadLeaseRegistry::active_record);
552 assert forall|lease_id: nat| #[trigger] old(self).active_ids().contains(lease_id) implies {
553 let record = old(self).active_record(lease_id);
554 &&& lease_id < old(self).next_lease()
555 &&& old(self).contains(record.key())
556 &&& record.accumulator_id() == old(self).accumulator(record.key()).id()
557 &&& record.fraction() > 0real
558 } by {};
559 assert forall|old_key: K| #[trigger] old(self).contains(old_key) implies old(
560 self,
561 ).accumulator(old_key).fraction() + active_lease_fraction(
562 old(self).active_records(),
563 old_key,
564 old(self).next_lease(),
565 ) == 1real by {};
566 let tracked accumulator = RcuLeaseAccumulator::new(resource);
567 self.accumulators.tracked_insert(key, accumulator);
568 assert forall|lease_id: nat| #![auto] self.active_ids().contains(lease_id) implies {
569 &&& lease_id < self.next_lease()
570 &&& self.contains(self.active_record(lease_id).key())
571 &&& self.active_record(lease_id).accumulator_id() == self.accumulator(
572 self.active_record(lease_id).key(),
573 ).id()
574 &&& self.active_record(lease_id).fraction() > 0real
575 } by {
576 assert(old(self).active_ids().contains(lease_id));
577 assert(old(self).active_record(lease_id).key() != key);
578 };
579 assert forall|lease_id: nat|
580 #![auto]
581 lease_id < self.next_lease() && self.active_records().contains_key(
582 lease_id,
583 ) implies self.active_records()[lease_id].key() != key by {
584 assert(old(self).active_ids().contains(lease_id));
585 assert(old(self).contains(old(self).active_record(lease_id).key()));
586 };
587 assert(active_lease_fraction(self.active_records(), key, self.next_lease()) == 0real) by {
588 lemma_active_fraction_zero(self.active_records(), key, self.next_lease());
589 };
590 assert forall|other: K| #![auto] self.contains(other) implies self.accumulator(
591 other,
592 ).fraction() + active_lease_fraction(self.active_records(), other, self.next_lease())
593 == 1real by {
594 if other == key {
595 assert(self.accumulator(key).fraction() == 1real);
596 } else {
597 assert(old(self).contains(other));
598 assert(self.accumulator(other) == old(self).accumulator(other));
599 }
600 };
601 }
602
603 pub proof fn split_lease(tracked &mut self, key: K, tracked witness: W) -> (tracked lease:
605 RcuRegisteredReadLease<K, T>)
606 requires
607 old(self).wf(),
608 old(self).contains(key),
609 ensures
610 final(self).wf(),
611 final(self).keys() == old(self).keys(),
612 forall|candidate: K| #[trigger]
613 final(self).contains(candidate) == old(self).contains(candidate),
614 final(self).next_lease() == old(self).next_lease() + 1,
615 lease.lease_id() == old(self).next_lease(),
616 lease.key() == key,
617 final(self).active_ids() == old(self).active_ids().insert(lease.lease_id()),
618 final(self).active_record(lease.lease_id()).key() == key,
619 final(self).active_record(lease.lease_id()).accumulator_id() == lease.accumulator_id(),
620 final(self).active_record(lease.lease_id()).fraction() == lease.fraction(),
621 final(self).active_record(lease.lease_id()).witness() == witness,
622 forall|lease_id: nat|
623 #![auto]
624 old(self).active_ids().contains(lease_id) ==> final(self).active_record(lease_id)
625 == old(self).active_record(lease_id),
626 lease.accumulator_id() == old(self).accumulator(key).id(),
627 lease.resource() == old(self).accumulator(key).resource(),
628 lease.fraction() == old(self).accumulator(key).fraction() / 2real,
629 final(self).accumulator(key).id() == old(self).accumulator(key).id(),
630 final(self).accumulator(key).resource() == old(self).accumulator(key).resource(),
631 final(self).accumulator(key).fraction() == old(self).accumulator(key).fraction()
632 / 2real,
633 forall|other: K|
634 other != key && old(self).contains(other) ==> final(self).accumulator(other) == old(
635 self,
636 ).accumulator(other),
637 {
638 reveal(RcuReadLeaseRegistry::active_ids);
639 reveal(RcuReadLeaseRegistry::active_records);
640 reveal(RcuReadLeaseRegistry::active_record);
641 assert forall|old_key: K| #[trigger] old(self).contains(old_key) implies old(
642 self,
643 ).accumulator(old_key).fraction() + active_lease_fraction(
644 old(self).active_records(),
645 old_key,
646 old(self).next_lease(),
647 ) == 1real by {};
648 let ghost lease_id = self.next_lease;
649 let tracked accumulator = self.accumulators.tracked_borrow_mut(key);
650 let tracked lease = accumulator.split_lease();
651 lease.lemma_fraction_bounded();
652 let ghost accumulator_id = lease.id();
653 let ghost fraction = lease.fraction();
654 let tracked record = RcuActiveReadLeaseRecord { key, accumulator_id, fraction, witness };
655 self.active.tracked_insert(lease_id, record);
656 self.next_lease = lease_id + 1;
657
658 assert forall|active_id: nat| #![auto] self.active_ids().contains(active_id) implies {
659 &&& active_id < self.next_lease()
660 &&& self.contains(self.active_record(active_id).key())
661 &&& self.active_record(active_id).accumulator_id() == self.accumulator(
662 self.active_record(active_id).key(),
663 ).id()
664 &&& self.active_record(active_id).fraction() > 0real
665 } by {
666 if active_id == lease_id {
667 assert(self.active_record(active_id).fraction() == fraction);
668 } else {
669 assert(old(self).active_ids().contains(active_id));
670 assert(self.active_record(active_id) == old(self).active_record(active_id));
671 }
672 };
673
674 assert forall|other: K| #![auto] self.contains(other) implies self.accumulator(
675 other,
676 ).fraction() + active_lease_fraction(self.active_records(), other, self.next_lease())
677 == 1real by {
678 lemma_active_fraction_insert_next(
679 old(self).active_records(),
680 lease_id,
681 self.active_record(lease_id),
682 other,
683 );
684 if other == key {
685 assert(old(self).accumulator(key).fraction() + active_lease_fraction(
686 old(self).active_records(),
687 key,
688 lease_id,
689 ) == 1real);
690 } else {
691 assert(old(self).contains(other));
692 assert(self.accumulator(other) == old(self).accumulator(other));
693 assert(old(self).accumulator(other).fraction() + active_lease_fraction(
694 old(self).active_records(),
695 other,
696 lease_id,
697 ) == 1real);
698 }
699 };
700 RcuRegisteredReadLease { lease_id, key, lease }
701 }
702
703 pub proof fn return_lease(
705 tracked &mut self,
706 tracked lease: RcuRegisteredReadLease<K, T>,
707 ) -> (tracked witness: W)
708 requires
709 old(self).wf(),
710 old(self).active_ids().contains(lease.lease_id()),
711 old(self).active_record(lease.lease_id()).key() == lease.key(),
712 old(self).active_record(lease.lease_id()).accumulator_id() == lease.accumulator_id(),
713 old(self).active_record(lease.lease_id()).fraction() == lease.fraction(),
714 ensures
715 final(self).wf(),
716 final(self).keys() == old(self).keys(),
717 forall|candidate: K| #[trigger]
718 final(self).contains(candidate) == old(self).contains(candidate),
719 final(self).next_lease() == old(self).next_lease(),
720 final(self).active_ids() == old(self).active_ids().remove(lease.lease_id()),
721 witness == old(self).active_record(lease.lease_id()).witness(),
722 forall|lease_id: nat|
723 #![auto]
724 lease_id != lease.lease_id() && old(self).active_ids().contains(lease_id)
725 ==> final(self).active_record(lease_id) == old(self).active_record(lease_id),
726 final(self).accumulator(lease.key()).id() == old(self).accumulator(lease.key()).id(),
727 final(self).accumulator(lease.key()).resource() == old(self).accumulator(
728 lease.key(),
729 ).resource(),
730 final(self).accumulator(lease.key()).fraction() == old(self).accumulator(
731 lease.key(),
732 ).fraction() + lease.fraction(),
733 forall|other: K|
734 other != lease.key() && old(self).contains(other) ==> final(self).accumulator(other)
735 == old(self).accumulator(other),
736 {
737 reveal(RcuReadLeaseRegistry::active_ids);
738 reveal(RcuReadLeaseRegistry::active_records);
739 reveal(RcuReadLeaseRegistry::active_record);
740 assert forall|old_key: K| #[trigger] old(self).contains(old_key) implies old(
741 self,
742 ).accumulator(old_key).fraction() + active_lease_fraction(
743 old(self).active_records(),
744 old_key,
745 old(self).next_lease(),
746 ) == 1real by {};
747 let ghost lease_id = lease.lease_id;
748 let ghost key = lease.key;
749 let tracked record = self.active.tracked_remove(lease_id);
750 let tracked accumulator = self.accumulators.tracked_borrow_mut(key);
751 accumulator.return_lease(lease.lease);
752
753 assert forall|active_id: nat| #![auto] self.active_ids().contains(active_id) implies {
754 &&& active_id < self.next_lease()
755 &&& self.contains(self.active_record(active_id).key())
756 &&& self.active_record(active_id).accumulator_id() == self.accumulator(
757 self.active_record(active_id).key(),
758 ).id()
759 &&& self.active_record(active_id).fraction() > 0real
760 } by {
761 assert(old(self).active_ids().contains(active_id));
762 assert(active_id != lease_id);
763 assert(self.active_record(active_id) == old(self).active_record(active_id));
764 };
765
766 assert forall|other: K| #![auto] self.contains(other) implies self.accumulator(
767 other,
768 ).fraction() + active_lease_fraction(self.active_records(), other, self.next_lease())
769 == 1real by {
770 lemma_active_fraction_remove(
771 old(self).active_records(),
772 lease_id,
773 other,
774 self.next_lease(),
775 );
776 if other == key {
777 assert(old(self).accumulator(key).fraction() + active_lease_fraction(
778 old(self).active_records(),
779 key,
780 self.next_lease(),
781 ) == 1real);
782 } else {
783 assert(old(self).contains(other));
784 assert(self.accumulator(other) == old(self).accumulator(other));
785 assert(old(self).accumulator(other).fraction() + active_lease_fraction(
786 old(self).active_records(),
787 other,
788 self.next_lease(),
789 ) == 1real);
790 }
791 };
792 record.witness
793 }
794
795 pub proof fn reclaim(tracked &mut self, key: K) -> (tracked resource: T)
797 requires
798 old(self).wf(),
799 old(self).contains(key),
800 !old(self).has_active(key),
801 ensures
802 final(self).wf(),
803 final(self).keys() == old(self).keys().remove(key),
804 final(self).active_ids() == old(self).active_ids(),
805 final(self).active_records() == old(self).active_records(),
806 final(self).next_lease() == old(self).next_lease(),
807 forall|lease_id: nat|
808 #![auto]
809 old(self).active_ids().contains(lease_id) ==> final(self).active_record(lease_id)
810 == old(self).active_record(lease_id),
811 !final(self).contains(key),
812 resource == old(self).accumulator(key).resource(),
813 forall|other: K|
814 other != key && old(self).contains(other) ==> final(self).accumulator(other) == old(
815 self,
816 ).accumulator(other),
817 {
818 reveal(RcuReadLeaseRegistry::active_ids);
819 reveal(RcuReadLeaseRegistry::active_records);
820 reveal(RcuReadLeaseRegistry::active_record);
821 assert forall|old_key: K| #[trigger] old(self).contains(old_key) implies old(
822 self,
823 ).accumulator(old_key).fraction() + active_lease_fraction(
824 old(self).active_records(),
825 old_key,
826 old(self).next_lease(),
827 ) == 1real by {};
828 assert forall|lease_id: nat|
829 #![auto]
830 lease_id < self.next_lease() && self.active_records().contains_key(
831 lease_id,
832 ) implies self.active_records()[lease_id].key() != key by {
833 if self.active_records()[lease_id].key() == key {
834 assert(self.active_ids().contains(lease_id));
835 assert(exists|candidate: nat|
836 #![auto]
837 self.active_ids().contains(candidate) && self.active_record(candidate).key()
838 == key) by {
839 assert(self.active_record(lease_id).key() == key);
840 };
841 assert(self.has_active(key));
842 }
843 };
844 lemma_active_fraction_zero(self.active_records(), key, self.next_lease());
845 assert(self.accumulator(key).fraction() == 1real);
846 let tracked accumulator = self.accumulators.tracked_remove(key);
847 let tracked resource = accumulator.reclaim();
848 assert forall|lease_id: nat| #![auto] self.active_ids().contains(lease_id) implies {
849 &&& lease_id < self.next_lease()
850 &&& self.contains(self.active_record(lease_id).key())
851 &&& self.active_record(lease_id).accumulator_id() == self.accumulator(
852 self.active_record(lease_id).key(),
853 ).id()
854 &&& self.active_record(lease_id).fraction() > 0real
855 } by {
856 assert(old(self).active_ids().contains(lease_id));
857 assert(old(self).active_record(lease_id).key() != key);
858 };
859 assert forall|other: K| #![auto] self.contains(other) implies self.accumulator(
860 other,
861 ).fraction() + active_lease_fraction(self.active_records(), other, self.next_lease())
862 == 1real by {
863 assert(other != key);
864 assert(old(self).contains(other));
865 assert(self.accumulator(other) == old(self).accumulator(other));
866 assert(self.active_records() == old(self).active_records());
867 assert(old(self).accumulator(other).fraction() + active_lease_fraction(
868 old(self).active_records(),
869 other,
870 old(self).next_lease(),
871 ) == 1real);
872 };
873 resource
874 }
875}
876
877proof fn read_lease_registry_reclaims_after_returns<K, T, W>(
879 key: K,
880 tracked resource: T,
881 tracked first_witness: W,
882 tracked second_witness: W,
883) -> (tracked res: T)
884 ensures
885 res == resource,
886{
887 let tracked mut registry = RcuReadLeaseRegistry::empty();
888 registry.insert(key, resource);
889 let tracked first = registry.split_lease(key, first_witness);
890 let tracked second = registry.split_lease(key, second_witness);
891 let tracked _first_witness = registry.return_lease(first);
892 let tracked _second_witness = registry.return_lease(second);
893 assert(!registry.has_active(key));
894 assert(registry.accumulator(key).resource() == resource);
895 let tracked res = registry.reclaim(key);
896 assert(res == resource);
897 res
898}
899
900pub proof fn lease_accumulator_reclaims_after_returns<T>(tracked resource: T) -> (tracked res: T)
903 ensures
904 res == resource,
905{
906 let tracked mut accumulator = RcuLeaseAccumulator::new(resource);
907 let tracked first = accumulator.split_lease();
908 let tracked second = accumulator.split_lease();
909 accumulator.return_lease(first);
910 accumulator.return_lease(second);
911 assert(accumulator.fraction() == 1real);
912 accumulator.reclaim()
913}
914
915}