Skip to content

Commit 097e6e4

Browse files
committed
Artifact for POPL 2024 submission
0 parents  commit 097e6e4

12 files changed

+1348
-0
lines changed

Clock/Classifier.agda

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
{-# OPTIONS --safe --without-K --exact-split --no-import-sorts #-}
2+
open import Agda.Primitive
3+
using () renaming (Set to Type)
4+
open import Relation.Binary
5+
using (DecidableEquality)
6+
7+
{-
8+
A clock that classifies actions and tracks a lower bound on
9+
the number of actions occurring in each class.
10+
11+
`Act` is the type of actions that can be performed.
12+
`Cls` is the type of action classes.
13+
Every `Act` is assigned to a `Cls` by the `cls` classification function.
14+
`_≟_` decides if two classes are the same.
15+
16+
Special cases:
17+
- The scalar clock of Lamport, which has only one classification.
18+
- The vector clock of Mattern, and of Fidge, which classifies actions
19+
by who performed them.
20+
- The matrix clock of Sarin and Lynch, and of Raynal and Singhal, which
21+
classifies actions by a tuple of sender and recipient.
22+
-}
23+
module Clock.Classifier
24+
(Cls : Type) (_≟_ : DecidableEquality Cls)
25+
(Act : Type) (cls : Act Cls)
26+
where
27+
open import Data.Bool
28+
using (true; false)
29+
open import Data.Nat
30+
using (ℕ; _+_; _⊔_; _≤_)
31+
open import Data.Nat.Properties
32+
as ℕ-Prop
33+
using ()
34+
open import Data.Bool
35+
using (if_then_else_)
36+
open import Relation.Nullary
37+
using (does; _because_)
38+
39+
open import Clock.Interpret
40+
as Interpret
41+
using (Step; act; merge)
42+
open import Clock.Monotonicity
43+
as Monotonicity
44+
using (Clock)
45+
open Clock
46+
using (≤-refl; ≤-trans; act-mono; merge-mono¹; merge-mono²)
47+
48+
-- A timestamp is a count of actions for every action class.
49+
Time : Type
50+
Time = Cls
51+
52+
alg : Step Act Time Time
53+
-- The increment operation adds one to the class of the associated action.
54+
alg (act a t) = λ c
55+
if does (cls a ≟ c)
56+
then 1 + t c
57+
else 0 + t c
58+
-- The merge operation takes the pointwise least upper bound
59+
-- (i.e. separately for each class).
60+
-- Notice that we cannot simply *add* the counts, because some actions
61+
-- might then be double-counted. Instead, if one clock has observed
62+
-- five actions, and another has observed seven, all we know is that
63+
-- at least 7 actions have definitely occurred.
64+
alg (merge t₁ t₂) = λ c
65+
t₁ c ⊔ t₂ c
66+
67+
-- Timestamps are ordered pointwise (i.e. separately for each class).
68+
_⊑_ : Time Time Type
69+
t₁ ⊑ t₂ = c t₁ c ≤ t₂ c
70+
71+
clock : Clock alg _⊑_
72+
-- _⊑_ is a preorder:
73+
(≤-refl clock) _ _ = ℕ-Prop.≤-refl
74+
(≤-trans clock) _ _ _ t₁≤t₂ t₂≤t₃ = λ s ℕ-Prop.≤-trans (t₁≤t₂ s) (t₂≤t₃ s)
75+
-- The clock operations are increasing on _⊑_:
76+
(act-mono clock) a _ s with cls a ≟ s
77+
... | false because _ = ℕ-Prop.≤-refl
78+
... | true because _ = ℕ-Prop.≤-step ℕ-Prop.≤-refl
79+
(merge-mono¹ clock) _ _ _ = ℕ-Prop.m≤m⊔n _ _
80+
(merge-mono² clock) _ _ _ = ℕ-Prop.m≤n⊔m _ _
81+
82+
-- Obtain a global timestamping function for any execution.
83+
timestamp = Interpret.timestamp alg
84+
-- Obtain a proof of the clock condition for any execution and any initial timestamps.
85+
mono = Monotonicity.timestamp-mono clock

Clock/Interpret.agda

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
{-# OPTIONS --safe --without-K --exact-split --no-import-sorts #-}
2+
open import Agda.Primitive
3+
using () renaming (Set to Type)
4+
5+
module Clock.Interpret where
6+
open import Function
7+
using (_∘_)
8+
open import Data.Sum
9+
using (inj₁; inj₂)
10+
open import Execution.Sites
11+
as Sites
12+
using (Tree; Site; _∗_)
13+
open import Execution.Core
14+
using (perm; tick; fork; join)
15+
using (_⇶[_]_; _⇶_; id; _∥_; _⟫_)
16+
using (Event; Tick)
17+
open import Execution.Causality
18+
as HB
19+
using ()
20+
open Event
21+
using (_,_)
22+
23+
variable
24+
T : Type
25+
Γ Γ₁ Γ₂ Γ₃ Γ₄ : Tree (Tree T)
26+
Action State : Type
27+
28+
Valuation : Type Tree (Tree T) Type
29+
Valuation State Γ = Site Γ State
30+
31+
par : Valuation State Γ₁ Valuation State Γ₂ Valuation State (Γ₁ ∗ Γ₂)
32+
par f g (Site.thereˡ _ ix) = f ix
33+
par f g (Site.thereʳ _ ix) = g ix
34+
35+
left : Valuation State (Γ₁ ∗ Γ₂) Valuation State Γ₁
36+
left f = f ∘ Site.thereˡ _
37+
38+
right : Valuation State (Γ₁ ∗ Γ₂) Valuation State Γ₂
39+
right f = f ∘ Site.thereʳ _
40+
41+
_⊗_ : (Valuation State Γ₁ Valuation State Γ₂ )
42+
(Valuation State Γ₃ Valuation State Γ₄ )
43+
(Valuation State (Γ₁ ∗ Γ₃) Valuation State (Γ₂ ∗ Γ₄))
44+
(f ⊗ g) x = par (f (left x)) (g (right x))
45+
46+
data Step (Action State : Type) : Type where
47+
act : Action State Step Action State
48+
merge : State State Step Action State
49+
50+
-- Given an algebra on steps, we can specify computations
51+
-- on replicas across spatially-distributed sites.
52+
module _ (alg : Step Action State State) where
53+
apply : {k} (exec : Γ₁ ⇶[ k ] Γ₂) (acts : Tick exec Action)
54+
(Valuation State Γ₁ Valuation State Γ₂)
55+
apply id acts = Function.id
56+
apply (perm σ) _ f = f ∘ Sites.‵index (Sites.‵sym σ)
57+
apply fork _ f = Function.const (f Site.here)
58+
apply tick acts f = Function.const (alg (act (acts _) (f Site.here)))
59+
apply join _ f = Function.const (alg (merge (f (Site.thereˡ _ Site.here))
60+
(f (Site.thereʳ _ Site.here)) ))
61+
apply ( left ∥ right) acts = apply left (acts ∘ inj₁) ⊗ apply right (acts ∘ inj₂)
62+
apply (prefix ⟫ suffix) acts = apply suffix (acts ∘ inj₂) ∘ apply prefix (acts ∘ inj₁)
63+
64+
timestamp : {exec : Γ₁ ⇶ Γ₂}
65+
(Tick exec Action)
66+
Valuation State Γ₁
67+
(Event exec State)
68+
timestamp acts init (t , s) = apply (HB.before[ t ]) (acts ∘ HB.tliftˡ t) init s

Clock/Matrix.agda

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
{-# OPTIONS --safe --without-K --exact-split --no-import-sorts #-}
2+
open import Agda.Primitive
3+
using () renaming (Set to Type)
4+
open import Relation.Binary
5+
using (DecidableEquality)
6+
7+
module Clock.Matrix (Pid : Type) (_≟_ : DecidableEquality Pid) where
8+
open import Data.Product
9+
using (_×_)
10+
open import Data.Product.Properties
11+
using (≡-dec)
12+
13+
-- A Raynal-Schiper-Toueg matrix clock classifies actions by a tuple
14+
-- of sender and recipient. This forms a table of quantities, one for
15+
-- each sender-recipient pair.
16+
--
17+
-- This is *distinct* from a Wuu-Bernstein matrix clock, which has an extra
18+
-- step in its merge operation to propagate knowledge of observation.
19+
module RST where
20+
open import Clock.Classifier (Pid × Pid) (≡-dec _≟_ _≟_) public
21+
using (Time; _⊑_; alg; clock; timestamp; mono)
22+
23+
-- A Wuu-Bernstein matrix clock classifies actions by actor and observer.
24+
-- If an action is observed by multiple actors, the action will be counted
25+
-- in multiple places in the matrix. This requires not only a pointwise merge,
26+
-- but also a merge of a sender's row into a receiver's row, to model that
27+
-- the receiver now observes anything that the sender observed at the time
28+
-- the message was sent.
29+
module WB where
30+
open import Data.Unit
31+
using (⊤)
32+
open import Data.Product
33+
using (_,_)
34+
open import Data.Bool
35+
using (true; false)
36+
open import Data.Nat
37+
using (ℕ; _+_; _⊔_; _≤_)
38+
open import Data.Nat.Properties
39+
as ℕ-Prop
40+
using ()
41+
open import Data.Bool
42+
using (if_then_else_)
43+
open import Relation.Nullary
44+
using (does; _because_)
45+
46+
open import Clock.Interpret
47+
as Interpret
48+
using (Step; act; merge)
49+
open import Clock.Monotonicity
50+
as Monotonicity
51+
using (Clock)
52+
open Clock
53+
using (≤-refl; ≤-trans; act-mono; merge-mono¹; merge-mono²)
54+
55+
-- A local process ID, together with a timestamp.
56+
Time = Pid × ((Pid × Pid) ℕ)
57+
58+
-- Ignore the process ID when comparing.
59+
_⊑_ : Time Time Type
60+
(_ , t₁) ⊑ (_ , t₂) = c t₁ c ≤ t₂ c
61+
62+
alg : Step ⊤ Time Time
63+
alg (act _ (self , t)) = self , λ c
64+
if does ((≡-dec _≟_ _≟_) (self , self) c)
65+
then 1 + t c
66+
else 0 + t c
67+
alg (merge (s , t₁) (r , t₂)) = r , λ (i , j)
68+
if does (j ≟ r)
69+
then t₁ (i , s) ⊔ (t₁ (i , j) ⊔ t₂ (i , j))
70+
else 0 ⊔ (t₁ (i , j) ⊔ t₂ (i , j))
71+
72+
clock : Clock alg _⊑_
73+
≤-refl clock _ _ = ℕ-Prop.≤-refl
74+
≤-trans clock _ _ _ t₁≤t₂ t₂≤t₃ = λ s ℕ-Prop.≤-trans (t₁≤t₂ s) (t₂≤t₃ s)
75+
act-mono clock _ (self , _) c with (≡-dec _≟_ _≟_) (self , self) c
76+
... | false because _ = ℕ-Prop.≤-refl
77+
... | true because _ = ℕ-Prop.≤-step ℕ-Prop.≤-refl
78+
merge-mono¹ clock (s , t₁) (r , t₂) (i , j) with j ≟ r
79+
... | false because _ = ℕ-Prop.m≤m⊔n _ _
80+
... | true because _ = ℕ-Prop.≤-trans (ℕ-Prop.m≤m⊔n _ _) (ℕ-Prop.m≤n⊔m (t₁ (i , s)) _)
81+
merge-mono² clock (s , t₁) (r , t₂) (i , j) with j ≟ r
82+
... | false because _ = ℕ-Prop.m≤n⊔m _ _
83+
... | true because _ = ℕ-Prop.≤-trans (ℕ-Prop.m≤n⊔m _ _) (ℕ-Prop.m≤n⊔m (t₁ (i , s)) _)
84+
85+
-- Obtain a global timestamping function for any execution.
86+
timestamp = Interpret.timestamp alg
87+
-- Obtain a proof of the clock condition for any execution and any initial timestamps.
88+
mono = Monotonicity.timestamp-mono clock

0 commit comments

Comments
 (0)