|
1 | 1 | -------------------------------- MODULE deli -------------------------------- |
2 | 2 | (***************************************************************************) |
3 | | -(* A specification of a deli ordering system with concurrent workers. *) |
4 | | -(* Customers arrive (Arrive) and join a queue. Idle workers asynchronously *) |
5 | | -(* pick up waiting customers (AssignOrder), then complete their order *) |
6 | | -(* (CompleteOrder) and return to Idle. Multiple workers process orders *) |
7 | | -(* in parallel, and customers can arrive unboundedly. *) |
| 3 | +(* A specification of a simple deli ordering system with a ticket queue. *) |
| 4 | +(* Customers arrive, get assigned to a worker, their order is prepared, *) |
| 5 | +(* and then served. The system cycles through Idle → TakingOrder → *) |
| 6 | +(* PreparingOrder → Serving → ReturnToIdle to serve the next customer. *) |
8 | 7 | (***************************************************************************) |
9 | 8 |
|
10 | 9 | EXTENDS Naturals, Sequences |
11 | 10 |
|
12 | | -CONSTANTS Workers, Customers, Null |
| 11 | +CONSTANTS Processes, Null |
13 | 12 |
|
14 | | -VARIABLES orderQueue, \* Seq(Customers) — grows at any time |
15 | | - workerState, \* [Workers -> {"Idle","Preparing"}] |
16 | | - workerCustomer \* [Workers -> Customers ∪ {Null}] |
| 13 | +VARIABLES ticket, worker, customer, state, orderQueue |
17 | 14 |
|
18 | 15 | (***************************************************************************) |
19 | 16 | (* State variables: *) |
| 17 | +(* - ticket: increasing counter issued to arriving customers *) |
| 18 | +(* - worker: the current worker serving an order, or Null if idle *) |
| 19 | +(* - customer: the current customer being served, or Null if idle *) |
| 20 | +(* - state: the system's current phase: *) |
| 21 | +(* (Idle | TakingOrder | PreparingOrder | Serving) *) |
20 | 22 | (* - orderQueue: sequence of customers waiting to be served *) |
21 | | -(* - workerState: mapping from workers to their current state *) |
22 | | -(* (Idle or Preparing) *) |
23 | | -(* - workerCustomer: mapping from workers to the customer they're *) |
24 | | -(* serving, or Null if idle *) |
25 | 23 | (***************************************************************************) |
26 | 24 |
|
27 | | -vars == <<orderQueue, workerState, workerCustomer>> |
28 | | - |
29 | 25 | TypeOK == |
30 | | - /\ orderQueue \in Seq(Customers) |
31 | | - /\ workerState \in [Workers -> {"Idle", "Preparing"}] |
32 | | - /\ workerCustomer \in [Workers -> Customers \cup {Null}] |
| 26 | + /\ ticket \in Nat |
| 27 | + /\ worker \in Processes \cup {Null} |
| 28 | + /\ customer \in Processes \cup {Null} |
| 29 | + /\ state \in {"Idle", "TakingOrder", "PreparingOrder", "Serving"} |
| 30 | + /\ orderQueue \in Seq(Processes) |
33 | 31 |
|
34 | 32 | Init == |
| 33 | + /\ ticket = 0 |
| 34 | + /\ worker = Null |
| 35 | + /\ customer = Null |
| 36 | + /\ state = "Idle" |
35 | 37 | /\ orderQueue = <<>> |
36 | | - /\ workerState = [w \in Workers |-> "Idle"] |
37 | | - /\ workerCustomer = [w \in Workers |-> Null] |
38 | | - |
39 | | -(* Customer arrives at any time — no guard on global state *) |
40 | | -Arrive(c) == |
41 | | - /\ orderQueue' = Append(orderQueue, c) |
42 | | - /\ UNCHANGED <<workerState, workerCustomer>> |
43 | 38 |
|
44 | | -(* Any idle worker picks up the next waiting customer *) |
45 | | -AssignOrder(w) == |
46 | | - /\ workerState[w] = "Idle" |
| 39 | +(* Customer arrives, gets a ticket number, and joins the queue *) |
| 40 | +TakeOrder == |
| 41 | + /\ state = "Idle" |
| 42 | + /\ \E c \in Processes : |
| 43 | + /\ ticket' = ticket + 1 |
| 44 | + /\ orderQueue' = Append(orderQueue, c) |
| 45 | + /\ state' = "TakingOrder" |
| 46 | + /\ UNCHANGED <<worker, customer>> |
| 47 | + |
| 48 | +(* The next customer from the queue is called and a worker is assigned *) |
| 49 | +PrepareOrder == |
| 50 | + /\ state = "TakingOrder" |
47 | 51 | /\ Len(orderQueue) > 0 |
48 | | - /\ workerCustomer' = [workerCustomer EXCEPT ![w] = Head(orderQueue)] |
49 | | - /\ orderQueue' = Tail(orderQueue) |
50 | | - /\ workerState' = [workerState EXCEPT ![w] = "Preparing"] |
51 | | - |
52 | | -(* Worker finishes serving and becomes idle again *) |
53 | | -CompleteOrder(w) == |
54 | | - /\ workerState[w] = "Preparing" |
55 | | - /\ workerState' = [workerState EXCEPT ![w] = "Idle"] |
56 | | - /\ workerCustomer' = [workerCustomer EXCEPT ![w] = Null] |
57 | | - /\ UNCHANGED orderQueue |
| 52 | + /\ LET c == Head(orderQueue) IN |
| 53 | + /\ \E w \in Processes : |
| 54 | + /\ customer' = c |
| 55 | + /\ worker' = w |
| 56 | + /\ orderQueue' = Tail(orderQueue) |
| 57 | + /\ state' = "PreparingOrder" |
| 58 | + /\ UNCHANGED ticket |
| 59 | + |
| 60 | +(* The assigned worker serves the current customer *) |
| 61 | +Serve == |
| 62 | + /\ state = "PreparingOrder" |
| 63 | + /\ state' = "Serving" |
| 64 | + /\ UNCHANGED <<ticket, worker, customer, orderQueue>> |
| 65 | + |
| 66 | +(* Customer is served, worker and customer reset, ready for the next order *) |
| 67 | +ReturnToIdle == |
| 68 | + /\ state = "Serving" |
| 69 | + /\ state' = "Idle" |
| 70 | + /\ worker' = Null |
| 71 | + /\ customer' = Null |
| 72 | + /\ UNCHANGED <<ticket, orderQueue>> |
58 | 73 |
|
59 | 74 | Next == |
60 | | - (\E c \in Customers : Arrive(c)) |
61 | | - \/ (\E w \in Workers : AssignOrder(w)) |
62 | | - \/ (\E w \in Workers : CompleteOrder(w)) |
63 | | - |
64 | | -(* Safety: Worker state is consistent with workerCustomer assignment *) |
65 | | -Consistency == |
66 | | - \A w \in Workers : (workerState[w] = "Idle") => (workerCustomer[w] = Null) |
| 75 | + TakeOrder \/ PrepareOrder \/ Serve \/ ReturnToIdle |
67 | 76 |
|
68 | | -(* Liveness: Every customer in the queue is eventually served *) |
69 | | -EventuallyServed == |
70 | | - \A i \in DOMAIN orderQueue : |
71 | | - LET c == orderQueue[i] IN |
72 | | - <> (\E w \in Workers : workerCustomer[w] = c) |
| 77 | +(* Safety: System stays in one of the allowed states *) |
| 78 | +ValidStates == |
| 79 | + state \in {"Idle", "TakingOrder", "PreparingOrder", "Serving"} |
73 | 80 |
|
74 | | -Fairness == |
75 | | - \A w \in Workers : WF_vars(AssignOrder(w)) /\ WF_vars(CompleteOrder(w)) |
| 81 | +(* Safety: At most one customer is being served at any given time *) |
| 82 | +MutualExclusion == |
| 83 | + (state = "Idle") => (customer = Null /\ worker = Null) |
76 | 84 |
|
77 | 85 | Spec == |
78 | | - Init /\ [][Next]_vars /\ Fairness |
| 86 | + Init /\ [][Next]_<<ticket, worker, customer, state, orderQueue>> |
79 | 87 |
|
80 | 88 | (* Theorems *) |
81 | 89 | THEOREM Spec => []TypeOK |
82 | | -THEOREM Spec => []Consistency |
| 90 | +THEOREM Spec => []ValidStates |
| 91 | +THEOREM Spec => []MutualExclusion |
83 | 92 |
|
84 | 93 | ============================================================================= |
85 | 94 |
|
0 commit comments