-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmodeling_concurrency_safety.dfy
152 lines (137 loc) · 4.92 KB
/
modeling_concurrency_safety.dfy
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
/*
* Model of the ticket system and correctness theorem
* Parts 4 and 5 in the paper
*/
type Process(==) = int // Philosopher
datatype CState = Thinking | Hungry | Eating // Control states
// A class can have state, with multiple fields, methods, a constructor, and declare functions and lemmas
class TicketSystem
{
var ticket: int // Ticket dispenser
var serving: int // Serving display
const P: set<Process> // Fixed set of processes
// State for each process
var cs: map<Process, CState> // (Partial) Map from process to state
var t: map<Process, int> // (Partial) Map from process to ticket number
// Invariant of the system
// Checks that P is a subset of the domain/keys of each map
predicate Valid()
reads this // Depends on the fields on the current class
{
&& cs.Keys == t.Keys == P // Alt. P <= cs.Keys && P <= t.Keys
&& serving <= ticket
&& (forall p :: // ticket help is in range(serving, ticket)
p in P && cs[p] != Thinking
==> serving <= t[p] < ticket
)
&& (forall p, q :: // No other process can have the ticket number equals to serving
p in P && q in P && p != q && cs[p] != Thinking && cs[q] != Thinking
==> t[p] != t[q]
)
&& (forall p :: // We are serving the correct ticket number
p in P && cs[p] == Eating
==> t[p] == serving
)
}
// Initialize the ticket system
constructor (processes: set<Process>)
ensures Valid() // Postcondition
ensures P == processes // Connection between processes and ts.P
{
P := processes;
ticket, serving := 0, 0; // Alt. ticket := serving;
// The two following use map comprehension
cs := map p | p in processes :: Thinking; // The map from p, where p in processes, takes value Thinking
t := map p | p in processes :: 0;
}
// The next three methods are our atomic events
// A Philosopher is Thinking and gets Hungry
method Request(p: Process)
requires Valid() && p in P && cs[p] == Thinking // Control process precondition
modifies this // Depends on the fields on the current class
ensures Valid() // Postcondition
{
t, ticket := t[p := ticket], ticket + 1; // Philosopher gets current ticket, next ticket's number increases
cs := cs[p := Hungry]; // Philosopher's state changes to Hungry
}
// A Philosopher is Hungry and enters the kitchen
method Enter(p: Process)
requires Valid() && p in P && cs[p] == Hungry // Control process precondition
modifies this // Depends on the fields on the current class
ensures Valid() // Postcondition
{
if t[p] == serving // The kitchen is available for this Philosopher
{
cs := cs[p := Eating]; // Philosopher's state changes to Eating
}
}
// A Philosopher is done Eating and leaves the kitchen
method Leave(p: Process)
requires Valid() && p in P && cs[p] == Eating // Control process precondition
modifies this // Depends on the fields on the current class
ensures Valid() // Postcondition
{
//assert t[p] == serving; // Ticket held by p is equal to serving
serving := serving + 1; // Kitchen is ready to serve the next ticket holder
cs := cs[p := Thinking]; // Philosopher's state changes to Thinking
}
// Ensures that no two processes are in the same state
lemma MutualExclusion(p: Process, q: Process)
// Antecedents
requires Valid() && p in P && q in P
requires cs[p] == Eating && cs[q] == Eating
// Conclusion/Proof goal
ensures p == q
{
}
}
/*
* Event scheduler
* Part 6 in the paper
* Part 6.1 for alternatives
*/
method Run(processes: set<Process>)
requires processes != {} // Cannot schedule no processes
decreases * // Needed so that the loop omits termination checks
{
var ts := new TicketSystem(processes);
var schedule := []; // Scheduling choices
var trace := [(ts.ticket, ts.serving, ts.cs, ts.t)]; // Record sequence of states
while true
invariant ts.Valid()
decreases * // Omits termination checks
{
var p :| p in ts.P; // p exists such that p is in ts.P
match ts.cs[p] {
case Thinking => ts.Request(p);
case Hungry => ts.Enter(p);
case Eating => ts.Leave(p);
}
schedule := schedule + [p];
trace:=trace + [(ts.ticket, ts.serving, ts.cs, ts.t)];
}
}
/*
* Event scheduler with planified schedule
* Part 6.2
*/
method RunFromSchedule(processes: set<Process>, schedule: nat -> Process)
requires processes != {}
requires forall n :: schedule(n) in processes
decreases *
{
var ts := new TicketSystem(processes);
var n := 0;
while true
invariant ts.Valid()
decreases * // Omits termination checks
{
var p := schedule(n);
match ts.cs[p] {
case Thinking => ts.Request(p);
case Hungry => ts.Enter(p);
case Eating => ts.Leave(p);
}
n := n + 1;
}
}