-
Notifications
You must be signed in to change notification settings - Fork 218
Expand file tree
/
Copy pathdeli.tla
More file actions
164 lines (128 loc) · 5.89 KB
/
deli.tla
File metadata and controls
164 lines (128 loc) · 5.89 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
-------------------------------- MODULE deli --------------------------------
(***************************************************************************)
(* A specification of a deli ordering system with ticket-based queuing *)
(* and parallel service by multiple workers. *)
(* *)
(* Customers arrive at any time, take the next ticket number, and join a *)
(* FIFO queue. Idle workers independently pick up the next waiting *)
(* customer, prepare their order, serve them, and return to idle. *)
(* Multiple workers operate concurrently, allowing several customers to *)
(* be served in parallel. *)
(***************************************************************************)
EXTENDS Naturals, Sequences, FiniteSets
CONSTANTS Workers, MaxArrivals
VARIABLES
arrivals, \* Nat: count of customers who have arrived
queue, \* Seq(Nat): FIFO queue of ticket numbers waiting
workerState, \* [Workers -> {"Idle", "Preparing", "Serving"}]
workerCustomer, \* [Workers -> Nat]: ticket number each worker handles (0 = none)
served \* Set of ticket numbers already served
vars == <<arrivals, queue, workerState, workerCustomer, served>>
(***************************************************************************)
(* Helper: the set of elements in a sequence. *)
(***************************************************************************)
Range(s) == { s[i] : i \in DOMAIN s }
---------------------------------------------------------------------------
(***************)
(* Type check *)
(***************)
TypeOK ==
/\ arrivals \in Nat
/\ queue \in Seq(1..arrivals)
/\ workerState \in [Workers -> {"Idle", "Preparing", "Serving"}]
/\ workerCustomer \in [Workers -> Nat]
/\ served \subseteq 1..arrivals
---------------------------------------------------------------------------
(*****************)
(* Initial state *)
(*****************)
Init ==
/\ arrivals = 0
/\ queue = <<>>
/\ workerState = [w \in Workers |-> "Idle"]
/\ workerCustomer = [w \in Workers |-> 0]
/\ served = {}
---------------------------------------------------------------------------
(***********)
(* Actions *)
(***********)
(* A new customer arrives, takes the next ticket, and joins the queue. *)
(* This action has no guard and is always enabled; the arrivals count *)
(* is bounded only by the state constraint in the model config. *)
Arrive ==
/\ arrivals' = arrivals + 1
/\ queue' = Append(queue, arrivals + 1)
/\ UNCHANGED <<workerState, workerCustomer, served>>
(* An idle worker picks up the next customer from the head of the queue *)
AssignWorker(w) ==
/\ workerState[w] = "Idle"
/\ queue /= <<>>
/\ workerCustomer' = [workerCustomer EXCEPT ![w] = Head(queue)]
/\ workerState' = [workerState EXCEPT ![w] = "Preparing"]
/\ queue' = Tail(queue)
/\ UNCHANGED <<arrivals, served>>
(* A worker finishes preparing and begins serving *)
FinishPreparing(w) ==
/\ workerState[w] = "Preparing"
/\ workerState' = [workerState EXCEPT ![w] = "Serving"]
/\ UNCHANGED <<arrivals, queue, workerCustomer, served>>
(* A worker completes service: customer is marked served, worker goes idle *)
CompleteService(w) ==
/\ workerState[w] = "Serving"
/\ served' = served \cup {workerCustomer[w]}
/\ workerState' = [workerState EXCEPT ![w] = "Idle"]
/\ workerCustomer' = [workerCustomer EXCEPT ![w] = 0]
/\ UNCHANGED <<arrivals, queue>>
---------------------------------------------------------------------------
(*****************)
(* Specification *)
(*****************)
Next ==
\/ Arrive
\/ \E w \in Workers : AssignWorker(w) \/ FinishPreparing(w) \/ CompleteService(w)
Spec == Init /\ [][Next]_vars
---------------------------------------------------------------------------
(************)
(* Fairness *)
(************)
(* Under weak fairness on each worker's actions, every queued customer *)
(* is eventually served. Without fairness, workers could idle forever. *)
FairSpec == Spec /\ \A w \in Workers :
/\ WF_vars(AssignWorker(w))
/\ WF_vars(FinishPreparing(w))
/\ WF_vars(CompleteService(w))
---------------------------------------------------------------------------
(**************)
(* Invariants *)
(**************)
(* An idle worker has no customer; a busy worker always has one *)
IdleConsistency ==
\A w \in Workers : workerState[w] = "Idle" <=> workerCustomer[w] = 0
(* No two workers handle the same customer simultaneously *)
NoDoubleAssignment ==
\A w1, w2 \in Workers :
w1 /= w2 /\ workerCustomer[w1] /= 0
=> workerCustomer[w1] /= workerCustomer[w2]
(* Every issued ticket is in exactly one logical state: *)
(* queued (waiting), assigned (being prepared or served), or served. *)
TicketStatePartition ==
LET assigned == { workerCustomer[w] : w \in { x \in Workers : workerCustomer[x] /= 0 } }
queued == Range(queue)
issued == 1..arrivals
IN /\ queued \cup assigned \cup served = issued
/\ queued \cap assigned = {}
/\ queued \cap served = {}
/\ assigned \cap served = {}
(* The queue preserves FIFO order: earlier tickets always appear first *)
QueueOrdered ==
\A i, j \in DOMAIN queue : i < j => queue[i] < queue[j]
---------------------------------------------------------------------------
(* State constraint for bounding model checking; not part of the spec. *)
StateConstraint == arrivals <= MaxArrivals
---------------------------------------------------------------------------
THEOREM Spec => []TypeOK
THEOREM Spec => []IdleConsistency
THEOREM Spec => []NoDoubleAssignment
THEOREM Spec => []TicketStatePartition
THEOREM Spec => []QueueOrdered
=============================================================================