-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathalist.c
243 lines (213 loc) · 5.69 KB
/
alist.c
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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
/****************************************************************
* Atomic singly linked list (using mutex)
*
* Copyright Alastair Reid 2020
* SPDX-Licence-Identifier: BSD-3-Clause
****************************************************************/
// Builds on top of a simple linked list structure
#include "list.h"
#include "stdlib.h"
#include "threading.h"
// A linked list using a lock to ensure atomic updates
struct list {
struct node* head;
struct mutex* lock;
};
/*@
// Invariant for a list object that should hold whenever the list
// is locked.
//
// Note that it does not mention the mutex itself because that
// would require a cyclic reference to this invariant
predicate_ctor alist_invariant(struct list *l)() =
l->head |-> ?head
&*& list(head)
;
// A list that is currently locked.
//
// Note that it does not say anything about l->head because that
// is not accessible until you acquire the lock.
//
// Note that (unlike standard VeriFast examples), this does
// not require "malloc_block_list(l)" so it is possible to
// use this with stack-allocated and global lists.
predicate alist(struct list *l;) =
l->lock |-> ?mutex
&*& mutex(mutex, alist_invariant(l))
;
@*/
// initialize a previously allocated list object
void alist_init(struct list* l)
//@ requires l->head |-> _ &*& l->lock |-> _;
//@ ensures alist(l);
{
struct node *h = 0;
l->head = h;
//@ close create_mutex_ghost_arg(alist_invariant(l));
//@ close alist_invariant(l)();
struct mutex *m = create_mutex();
l->lock = m;
}
// finalize a list - making it ready to deallocate
void alist_fini(struct list* l)
//@ requires alist(l);
//@ ensures l->head |-> _ &*& l->lock |-> _;
{
mutex_dispose(l->lock);
//@ open alist_invariant(l)();
list_dispose(l->head);
}
// allocate and initialize a list object
struct list* alist_create()
//@ requires true;
//@ ensures alist(result) &*& malloc_block_list(result);
{
struct list* l = malloc(sizeof(struct list));
if (l == 0) abort();
// There are two ways of writing this function - both work
#if 1
alist_init(l);
#else
struct node *h = 0;
l->head = h;
//@ close create_mutex_ghost_arg(alist_invariant(l));
//@ close alist_invariant(l)();
struct mutex *m = create_mutex();
l->lock = m;
#endif
return l;
}
void alist_dispose(struct list* l)
//@ requires alist(l) &*& malloc_block_list(l);
//@ ensures true;
{
// There are two ways of writing this function - both work
#if 1
alist_fini(l);
#else
mutex_dispose(l->lock);
//@ open alist_invariant(l)();
list_dispose(l->head);
#endif
free(l);
}
void alist_cons(int value, struct list* l)
//@ requires [?f]alist(l);
//@ ensures [f]alist(l);
{
mutex_acquire(l->lock);
//@ open alist_invariant(l)();
l->head = cons(value, l->head);
//@ close alist_invariant(l)();
mutex_release(l->lock);
}
int alist_head(struct list* l)
//@ requires [?f]alist(l);
//@ ensures [f]alist(l);
{
mutex_acquire(l->lock);
//@ open alist_invariant(l)();
int r = l->head ? head(l->head) : -1;
//@ close alist_invariant(l)();
mutex_release(l->lock);
return r;
}
void alist_tail(struct list* l)
//@ requires [?f]alist(l);
//@ ensures [f]alist(l);
{
mutex_acquire(l->lock);
//@ open alist_invariant(l)();
if (l->head) {
l->head = tail(l->head);
}
//@ close alist_invariant(l)();
mutex_release(l->lock);
}
/****************************************************************
* Sequential tests
****************************************************************/
// sequential test code written using alist_create
void test_alist1()
//@ requires true;
//@ ensures true;
{
struct list *l = alist_create();
alist_cons(3, l);
alist_cons(4, l);
int x = alist_head(l);
alist_tail(l);
int y = alist_head(l);
alist_tail(l);
// we don't track contents of list so we can't assert anything about x and y
alist_dispose(l);
}
// sequential test code written using list_init
void test_alist2()
//@ requires true;
//@ ensures true;
{
struct list l;
alist_init(&l);
alist_cons(3, &l);
int x = alist_head(&l);
alist_tail(&l);
alist_fini(&l);
}
/****************************************************************
* Concurrent tests
****************************************************************/
/*@
predicate_family_instance thread_run_pre(test_thread)(struct list *l, any info) = [1/2]alist(l);
predicate_family_instance thread_run_post(test_thread)(struct list *l, any info) = [1/2]alist(l);
@*/
void test_thread(struct list *l) //@ : thread_run_joinable
//@ requires thread_run_pre(test_thread)(l, ?info);
//@ ensures thread_run_post(test_thread)(l, info);
{
//@ open thread_run_pre(test_thread)(l, info);
alist_cons(3, l);
//@ close thread_run_post(test_thread)(l, info);
}
struct thread *start_thread(struct list *l)
//@ requires [1/2]alist(l);
//@ ensures thread(result, test_thread, l, _);
{
//@ close thread_run_pre(test_thread)(l, unit);
return thread_start_joinable(test_thread, l);
}
void join_thread(struct thread *t)
//@ requires thread(t, test_thread, ?l, _);
//@ ensures [1/2]alist(l);
{
thread_join(t);
//@ open thread_run_post(test_thread)(l, _);
}
// concurrent test code written using alist_create
void test_alist3()
//@ requires true;
//@ ensures true;
{
struct list *l = alist_create();
struct thread *t1 = start_thread(l);
struct thread *t2 = start_thread(l);
join_thread(t1);
join_thread(t2);
alist_dispose(l);
}
// concurrent test code written using alist_init
void test_alist4()
//@ requires true;
//@ ensures true;
{
struct list l;
alist_init(&l);
struct thread *t1 = start_thread(&l);
struct thread *t2 = start_thread(&l);
join_thread(t1);
join_thread(t2);
alist_fini(&l);
}
/****************************************************************
* End
****************************************************************/