|
| 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