Skip to content

Commit 12a5ba9

Browse files
committed
resource: add combinatorial oneshot example
1 parent e8d770e commit 12a5ba9

3 files changed

Lines changed: 234 additions & 48 deletions

File tree

examples/resource/log.rs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -222,9 +222,9 @@ impl<T> LogResource<T> {
222222
requires
223223
old(self)@ is FullAuthority,
224224
ensures
225-
self@ is FullAuthority,
226-
self.id() == old(self).id(),
227-
self@.log() == old(self)@.log() + seq![v],
225+
final(self)@ is FullAuthority,
226+
final(self).id() == old(self).id(),
227+
final(self)@.log() == old(self)@.log() + seq![v],
228228
{
229229
let value = LogResourceValue::<T>::FullAuthority { log: self@.log() + seq![v] };
230230
update_mut(&mut self.r, value);
@@ -236,11 +236,11 @@ impl<T> LogResource<T> {
236236
old(other)@ is HalfAuthority,
237237
old(self).id() == old(other).id(),
238238
ensures
239-
self@ is HalfAuthority,
240-
self.id() == old(self).id(),
241-
other.id() == old(self).id(),
242-
self@.log() == old(self)@.log() + seq![v],
243-
other@ == self@,
239+
final(self)@ is HalfAuthority,
240+
final(self).id() == old(self).id(),
241+
final(other).id() == old(other).id(),
242+
final(self)@.log() == old(self)@.log() + seq![v],
243+
final(other)@ == final(self)@,
244244
{
245245
self.r.validate_2(&other.r);
246246
let new_log = self@.log() + seq![v];
@@ -263,12 +263,12 @@ impl<T> LogResource<T> {
263263
requires
264264
old(self).id() == other.id(),
265265
ensures
266-
self@ == old(self)@,
267-
is_prefix(self@.log(), other@.log()) || is_prefix(other@.log(), self@.log()),
268-
self@ is HalfAuthority ==> is_prefix(other@.log(), self@.log()),
269-
self@ is FullAuthority ==> is_prefix(other@.log(), self@.log()),
270-
other@ is HalfAuthority ==> is_prefix(self@.log(), other@.log()),
271-
other@ is FullAuthority ==> is_prefix(self@.log(), other@.log()),
266+
final(self)@ == old(self)@,
267+
is_prefix(final(self)@.log(), other@.log()) || is_prefix(other@.log(), final(self)@.log()),
268+
final(self)@ is HalfAuthority ==> is_prefix(other@.log(), final(self)@.log()),
269+
final(self)@ is FullAuthority ==> is_prefix(other@.log(), final(self)@.log()),
270+
other@ is HalfAuthority ==> is_prefix(final(self)@.log(), other@.log()),
271+
other@ is FullAuthority ==> is_prefix(final(self)@.log(), other@.log()),
272272
{
273273
self.r.validate_2(&other.r)
274274
}

examples/resource/monotonic_counter.rs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -260,8 +260,8 @@ impl MonotonicCounterResource {
260260
requires
261261
old(self)@ is FullRightToAdvance,
262262
ensures
263-
self.id() == old(self).id(),
264-
self@ == (MonotonicCounterResourceValue::FullRightToAdvance {
263+
final(self).id() == old(self).id(),
264+
final(self)@ == (MonotonicCounterResourceValue::FullRightToAdvance {
265265
value: old(self)@->FullRightToAdvance_value + 1,
266266
}),
267267
{
@@ -284,10 +284,10 @@ impl MonotonicCounterResource {
284284
old(other)@ is HalfRightToAdvance,
285285
ensures
286286
old(self)@ == old(other)@,
287-
self.id() == old(self).id(),
288-
other.id() == old(self).id(),
289-
other@ == self@,
290-
self@ == (MonotonicCounterResourceValue::HalfRightToAdvance {
287+
final(self).id() == old(self).id(),
288+
final(other).id() == old(other).id(),
289+
final(self)@ == final(other)@,
290+
final(self)@ == (MonotonicCounterResourceValue::HalfRightToAdvance {
291291
value: old(self)@->HalfRightToAdvance_value + 1,
292292
}),
293293
{
@@ -313,11 +313,11 @@ impl MonotonicCounterResource {
313313
requires
314314
old(self).id() == other.id(),
315315
ensures
316-
self@ == old(self)@,
317-
self@ is LowerBound && other@ is FullRightToAdvance ==> self@.n() <= other@.n(),
318-
other@ is LowerBound && self@ is FullRightToAdvance ==> other@.n() <= self@.n(),
319-
self@ is LowerBound && other@ is HalfRightToAdvance ==> self@.n() <= other@.n(),
320-
other@ is LowerBound && self@ is HalfRightToAdvance ==> other@.n() <= self@.n(),
316+
final(self)@ == old(self)@,
317+
final(self)@ is LowerBound && other@ is FullRightToAdvance ==> final(self)@.n() <= other@.n(),
318+
other@ is LowerBound && final(self)@ is FullRightToAdvance ==> other@.n() <= final(self)@.n(),
319+
final(self)@ is LowerBound && other@ is HalfRightToAdvance ==> final(self)@.n() <= other@.n(),
320+
other@ is LowerBound && final(self)@ is HalfRightToAdvance ==> other@.n() <= final(self)@.n(),
321321
{
322322
self.r.validate_2(&other.r)
323323
}

examples/resource/oneshot.rs

Lines changed: 209 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -69,26 +69,30 @@ use verus_builtin::*;
6969
use verus_builtin_macros::*;
7070
use vstd::prelude::*;
7171
use vstd::resource;
72+
use vstd::resource::agree::AgreementRA;
73+
use vstd::resource::algebra;
7274
use vstd::resource::algebra::ResourceAlgebra;
73-
use vstd::resource::pcm::Resource;
75+
use vstd::resource::frac::FractionRA;
76+
use vstd::resource::pcm;
7477
use vstd::resource::pcm::PCM;
78+
use vstd::resource::product::ProductRA;
7579
use vstd::resource::update_and_redistribute;
7680
use vstd::resource::update_mut;
7781
use vstd::resource::Loc;
7882

7983
verus! {
8084

81-
// A one-shot resource represents one of the following four resources:
82-
//
83-
// `FullRightToComplete` -- the authority to complete the one-shot;
84-
//
85-
// `HalfRightToComplete` -- half of the authority to complete the
86-
// one-shot, which can be combined with another half to make a full
87-
// authority; or
88-
//
89-
// `Complete` -- knowledge that the one-shot has completed.
90-
//
91-
// `Empty` - no permission at all.
85+
/// A one-shot resource represents one of the following four resources:
86+
///
87+
/// `FullRightToComplete` -- the authority to complete the one-shot;
88+
///
89+
/// `HalfRightToComplete` -- half of the authority to complete the
90+
/// one-shot, which can be combined with another half to make a full
91+
/// authority; or
92+
///
93+
/// `Complete` -- knowledge that the one-shot has completed.
94+
///
95+
/// `Empty` - no permission at all.
9296
pub enum OneShotResourceValue {
9397
FullRightToComplete,
9498
HalfRightToComplete,
@@ -142,7 +146,7 @@ impl PCM for OneShotResourceValue {
142146
}
143147

144148
pub struct OneShotResource {
145-
r: Resource<OneShotResourceValue>,
149+
r: pcm::Resource<OneShotResourceValue>,
146150
}
147151

148152
impl OneShotResource {
@@ -162,7 +166,7 @@ impl OneShotResource {
162166
resource@ is FullRightToComplete,
163167
{
164168
let v = OneShotResourceValue::FullRightToComplete { };
165-
let tracked mut r = Resource::<OneShotResourceValue>::alloc(v);
169+
let tracked mut r = pcm::Resource::<OneShotResourceValue>::alloc(v);
166170
OneShotResource { r }
167171
}
168172

@@ -194,7 +198,7 @@ impl OneShotResource {
194198
requires
195199
old(self)@ is FullRightToComplete,
196200
ensures
197-
self@ is Complete,
201+
final(self)@ is Complete,
198202
{
199203
let v = OneShotResourceValue::Complete { };
200204
update_mut(&mut self.r, v);
@@ -221,10 +225,10 @@ impl OneShotResource {
221225
!(old(other)@ is Empty),
222226
ensures
223227
old(other)@ is HalfRightToComplete,
224-
self@ is Complete,
225-
other@ is Complete,
226-
self.id() == old(self).id(),
227-
other.id() == old(self).id(),
228+
final(self).id() == old(self).id(),
229+
final(other).id() == old(self).id(),
230+
final(self)@ is Complete,
231+
final(other)@ is Complete,
228232
{
229233
self.r.validate();
230234
other.r.validate();
@@ -258,16 +262,169 @@ impl OneShotResource {
258262
other@ is Complete,
259263
!(old(self)@ is Empty),
260264
ensures
261-
self.id() == old(self).id(),
262-
self@ == old(self)@,
263-
self@ is Complete,
265+
final(self).id() == old(self).id(),
266+
final(self)@ == old(self)@,
267+
final(self)@ is Complete,
268+
{
269+
self.r.validate_2(&other.r);
270+
}
271+
}
272+
273+
/// A one-shot resource represents one of the following four resources:
274+
///
275+
/// `FullRightToComplete` -- the authority to complete the one-shot;
276+
///
277+
/// `HalfRightToComplete` -- half of the authority to complete the
278+
/// one-shot, which can be combined with another half to make a full
279+
/// authority; or
280+
///
281+
/// `Complete` -- knowledge that the one-shot has completed.
282+
///
283+
/// `Empty` - no permission at all.
284+
pub type OneShotCarrier<T> = ProductRA<FractionRA, Option<AgreementRA<T>>>;
285+
286+
pub struct OneShotResource2<T> {
287+
r: algebra::Resource<OneShotCarrier<T>>,
288+
}
289+
290+
impl<T> OneShotResource2<T> {
291+
pub closed spec fn loc(self) -> Loc {
292+
self.r.loc()
293+
}
294+
295+
/// The view of the underlying resource
296+
pub closed spec fn view(self) -> Option<T> {
297+
match self.r.value().right {
298+
Some(AgreementRA::Agree(x)) => Some(x),
299+
_ => None
300+
}
301+
}
302+
303+
pub closed spec fn fraction(self) -> real {
304+
self.r.value().left.frac()
305+
}
306+
307+
pub proof fn alloc() -> (tracked resource: Self)
308+
ensures
309+
resource@ is None,
310+
resource.fraction() == 1real,
311+
{
312+
let v = OneShotCarrier { left: FractionRA::new(1real), right: None };
313+
let tracked r = algebra::Resource::<OneShotCarrier<T>>::alloc(v);
314+
OneShotResource2 { r }
315+
}
316+
317+
/// This function splits full authority to perform a one-shot
318+
/// into two half authorities to perform it.
319+
// TODO(bsdinis): make this using shared ref
320+
pub proof fn split(tracked self) -> (tracked r: (Self, Self))
321+
requires
322+
self@ is None,
323+
self.fraction() == 1real,
324+
ensures
325+
({
326+
let (half1, half2) = r;
327+
&&& half1@ is None
328+
&&& half2@ is None
329+
&&& half1.fraction() == 0.5real
330+
&&& half2.fraction() == 0.5real
331+
&&& half1.loc() == self.loc()
332+
&&& half2.loc() == self.loc()
333+
}),
334+
{
335+
self.r.validate();
336+
assert(self.r.value().right is None);
337+
let half = OneShotCarrier { left: FractionRA::new(0.5real), right: self.r.value().right };
338+
assert(self.r.value().left == FractionRA::op(half.left, half.left));
339+
assert(self.r.value().right == Option::op(half.right, half.right));
340+
let tracked (r1, r2) = self.r.split(half, half);
341+
(OneShotResource2 { r: r1 }, OneShotResource2 { r: r2 })
342+
}
343+
344+
// This function performs a one-shot given a resource representing
345+
// full authority to complete the one-shot.
346+
//
347+
// Upon return, the passed-in resource will have been transformed
348+
// into knowledge that the one-shot has been performed.
349+
pub proof fn shoot(tracked &mut self, v: T)
350+
requires
351+
old(self)@ is None,
352+
old(self).fraction() == 1real,
353+
ensures
354+
final(self)@ == Some(v)
355+
{
356+
let new_carrier = OneShotCarrier { left: self.r.value().left, right: Some(AgreementRA::Agree(v)) };
357+
// TODO(bsdinis): need the resource lib
358+
// update_mut(&mut self.r, new_carrier);
359+
admit()
360+
}
361+
362+
// This function performs a one-shot given two resources, the
363+
// first of which represents an incomplete one-shot (and half the
364+
// authority needed to perform it). The resources must have the
365+
// same `loc()`, meaning they're talking about the same one-shot.
366+
//
367+
// Upon return, the passed-in resources will have both been
368+
// transformed into knowledge that the one-shot has been
369+
// performed.
370+
//
371+
// The caller of this function only needs to know that `self`
372+
// provides half authority and that `other` isn't `Empty`. Upon
373+
// return the caller will learn that *both* the resources had
374+
// provided half authority at call time. However, those resources
375+
// were transformed so they don't provide that authority anymore.
376+
pub proof fn shoot_with_two_halves(tracked &mut self, tracked other: &mut Self, v: T)
377+
requires
378+
old(other).loc() == old(self).loc(),
379+
old(self)@ is None,
380+
old(self).fraction() + old(other).fraction() == 1real,
381+
ensures
382+
old(other)@ is None,
383+
final(self).loc() == old(self).loc(),
384+
final(other).loc() == old(self).loc(),
385+
final(self).fraction() == old(self).fraction(),
386+
final(other).fraction() == old(other).fraction(),
387+
final(self)@ == Some(v),
388+
final(other)@ == Some(v),
389+
{
390+
self.r.validate_2(&other.r);
391+
let new_self_carrier = OneShotCarrier { left: self.r.value().left, right: Some(AgreementRA::Agree(v)) };
392+
let new_other_carrier = OneShotCarrier { left: other.r.value().left, right: Some(AgreementRA::Agree(v)) };
393+
// TODO(bsdinis): we want the full lib for ras too
394+
// update_and_redistribute(&mut self.r, &mut other.r, v, v);
395+
admit()
396+
}
397+
398+
// This function duplicates a one-shot resource representing knowledge of completion.
399+
pub proof fn duplicate(tracked self) -> (tracked r: (Self, Self))
400+
requires
401+
self@ is Some,
402+
ensures
403+
r.0.loc() == self.loc(),
404+
r.1.loc() == self.loc(),
405+
r.0@ == self@,
406+
r.1@ == self@,
407+
{
408+
self.r.validate();
409+
let half = OneShotCarrier { left: FractionRA::new(self.fraction()/2real), right: self.r.value().right };
410+
let tracked (r1, r2) = self.r.split(half, half);
411+
(OneShotResource2 { r: r1 }, OneShotResource2 { r: r2 })
412+
}
413+
414+
pub proof fn lemma_agree(tracked &mut self, tracked other: &Self)
415+
requires
416+
other.loc() == old(self).loc(),
417+
other@ is Some,
418+
ensures
419+
final(self).loc() == old(self).loc(),
420+
final(self)@ == old(self)@,
264421
{
265422
self.r.validate_2(&other.r);
266423
}
267424
}
268425

269426
// This example illustrates some uses of the one-shot functions.
270-
fn main() {
427+
fn test_manual() {
271428
let tracked full = OneShotResource::alloc();
272429
proof {
273430
full.perform();
@@ -292,4 +449,33 @@ fn main() {
292449
assert(knowledge@ is Complete);
293450
}
294451

452+
fn test_combinator() {
453+
let tracked full = OneShotResource2::<int>::alloc();
454+
proof {
455+
full.shoot(2);
456+
}
457+
assert(full@ == Some(2int));
458+
let tracked different_oneshot = OneShotResource2::<int>::alloc();
459+
let tracked (mut half1, mut half2) = different_oneshot.split();
460+
let ghost id = half1.loc();
461+
assert(half1.loc() == half2.loc());
462+
assert(half1@ is None);
463+
assert(half2@ is None);
464+
assert(half1.fraction() == 0.5real);
465+
assert(half2.fraction() == 0.5real);
466+
proof {
467+
half1.shoot_with_two_halves(&mut half2, 3);
468+
}
469+
assert(half1.loc() == id);
470+
assert(half2.loc() == id);
471+
assert(half1@ == Some(3int));
472+
assert(half2@ == Some(3int));
473+
/* TODO(bsdinis): need shared refs
474+
let tracked knowledge = half1.duplicate();
475+
assert(knowledge.loc() == id);
476+
assert(half1.loc() == id);
477+
assert(knowledge@ is Complete);
478+
*/
479+
}
480+
295481
} // verus!

0 commit comments

Comments
 (0)