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
|
type X;
function {:inline} Inv(ghostLock: X, currsize: int, newsize: int) : (bool)
{
currsize <= newsize && (ghostLock == nil <==> currsize == newsize)
}
procedure {:yields} YieldToReadCache({:linear "tid"} tid': X) returns ({:linear "tid"} tid: X)
requires tid' != nil;
requires Inv(ghostLock, currsize, newsize);
ensures Inv(ghostLock, currsize, newsize);
ensures old(currsize) <= currsize;
ensures tid == tid';
{
tid := tid';
yield;
assert Inv(ghostLock, currsize, newsize);
assert old(currsize) <= currsize;
assert tid != nil;
}
procedure {:yields} YieldToUpdateCache({:linear "tid"} tid': X, i: int) returns ({:linear "tid"} tid: X)
requires tid' != nil;
requires Inv(ghostLock, currsize, newsize);
requires ghostLock == tid';
requires currsize <= i;
ensures Inv(ghostLock, currsize, newsize);
ensures old(currsize) <= currsize;
ensures tid == tid';
ensures ghostLock == tid;
ensures currsize <= i;
ensures newsize == old(newsize);
{
tid := tid';
yield;
assert Inv(ghostLock, currsize, newsize);
assert old(currsize) <= currsize;
assert tid != nil;
assert ghostLock == tid;
assert currsize <= i;
assert newsize == old(newsize);
}
var ghostLock: X;
var lock: X;
const nil: X;
var currsize: int;
var newsize: int;
procedure acquire(tid: X);
modifies lock;
ensures old(lock) == nil && lock == tid;
procedure release(tid: X);
modifies lock;
requires lock == tid;
ensures lock == nil;
procedure {:yields} Read ({:linear "tid"} tid': X, start : int, size : int) returns (bytesread : int)
requires tid' != nil;
requires 0 <= start && 0 <= size;
requires Inv(ghostLock, currsize, newsize);
ensures 0 <= bytesread && bytesread <= size;
{
var copy_currsize: int;
var i, j, tmp : int;
var {:linear "tid"} tid: X;
tid := tid';
bytesread := size;
call acquire(tid);
i := currsize;
if (start + size <= i) {
call release(tid);
call tid := YieldToReadCache(tid);
goto COPY_TO_BUFFER;
} else if (newsize > i) {
bytesread := if (start <= i) then (i - start) else 0;
call release(tid);
call tid := YieldToReadCache(tid);
goto COPY_TO_BUFFER;
} else {
newsize := start + size;
ghostLock := tid;
call release(tid);
call tid := YieldToUpdateCache(tid, i);
goto READ_DEVICE;
}
READ_DEVICE:
while (i < start + size)
invariant Inv(ghostLock, currsize, newsize);
invariant ghostLock == tid;
invariant tid != nil;
invariant currsize <= i;
invariant newsize == start + size;
{
call tid := YieldToUpdateCache(tid, i);
assert currsize <= i;
i := i + 1;
}
call acquire(tid);
tmp := newsize;
currsize := tmp;
ghostLock := nil;
call release(tid);
call tid := YieldToReadCache(tid);
COPY_TO_BUFFER:
j := 0;
while(j < bytesread)
invariant 0 <= j;
invariant Inv(ghostLock, currsize, newsize);
invariant tid != nil;
invariant bytesread == 0 || start + bytesread <= currsize;
{
call tid := YieldToReadCache(tid);
assert start + j < currsize;
j := j + 1;
}
}
procedure {:yields} {:stable} thread({:linear "tid"} tid': X)
requires tid' != nil;
requires Inv(ghostLock, currsize, newsize);
{
var start, size, bytesread: int;
var {:linear "tid"} tid: X;
tid := tid';
havoc start, size;
assume (0 <= start && 0 <= size);
call bytesread := Read(tid, start, size);
}
procedure {:entrypoint} {:yields} main()
requires currsize == 0 && newsize == 0 && ghostLock == nil && lock == nil;
{
var {:linear "tid"} tid: X;
while (*)
{
call tid := Allocate();
async call thread(tid);
}
}
procedure Allocate() returns ({:linear "tid"} xls: X);
ensures xls != nil;
|