Implement library for lockfiles and use it in cfg code.
[sliver-openvswitch.git] / tests / test-lockfile.c
1 /*
2  * Copyright (c) 2009 Nicira Networks.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <config.h>
18
19 #include "lockfile.h"
20
21 #include <errno.h>
22 #include <stdlib.h>
23 #include <sys/wait.h>
24 #include <unistd.h>
25
26 #include "process.h"
27 #include "timeval.h"
28 #include "util.h"
29
30 #undef NDEBUG
31 #include <assert.h>
32
33 struct test {
34     const char *name;
35     void (*function)(void);
36 };
37
38 static const struct test tests[];
39
40 static void
41 run_lock_and_unlock(void)
42 {
43     struct lockfile *lockfile;
44
45     assert(lockfile_lock("file", 0, &lockfile) == 0);
46     lockfile_unlock(lockfile);
47 }
48
49 static void
50 run_lock_and_unlock_twice(void)
51 {
52     struct lockfile *lockfile;
53
54     assert(lockfile_lock("file", 0, &lockfile) == 0);
55     lockfile_unlock(lockfile);
56
57     assert(lockfile_lock("file", 0, &lockfile) == 0);
58     lockfile_unlock(lockfile);
59 }
60
61 static void
62 run_lock_blocks_same_process(void)
63 {
64     struct lockfile *lockfile;
65
66     assert(lockfile_lock("file", 0, &lockfile) == 0);
67     assert(lockfile_lock("file", 0, &lockfile) == EDEADLK);
68     lockfile_unlock(lockfile);
69 }
70
71 static void
72 run_lock_blocks_same_process_twice(void)
73 {
74     struct lockfile *lockfile;
75
76     assert(lockfile_lock("file", 0, &lockfile) == 0);
77     assert(lockfile_lock("file", 0, &lockfile) == EDEADLK);
78     assert(lockfile_lock("file", 0, &lockfile) == EDEADLK);
79     lockfile_unlock(lockfile);
80 }
81
82 static enum { PARENT, CHILD }
83 do_fork(void)
84 {
85     switch (fork()) {
86     case 0:
87         time_postfork();
88         lockfile_postfork();
89         return CHILD;
90
91     default:
92         return PARENT;
93
94     case -1:
95         /* Error. */
96         ovs_fatal(errno, "fork failed");
97     }
98 }
99
100 static void
101 run_lock_blocks_other_process(void)
102 {
103     struct lockfile *lockfile;
104
105     assert(lockfile_lock("file", 0, &lockfile) == 0);
106     if (do_fork() == CHILD) {
107         assert(lockfile_lock("file", 0, &lockfile) == EAGAIN);
108         exit(11);
109     }
110 }
111
112 static void
113 run_lock_twice_blocks_other_process(void)
114 {
115     struct lockfile *lockfile, *dummy;
116
117     assert(lockfile_lock("file", 0, &lockfile) == 0);
118     assert(lockfile_lock("file", 0, &dummy) == EDEADLK);
119     if (do_fork() == CHILD) {
120         assert(lockfile_lock("file", 0, &dummy) == EAGAIN);
121         exit(11);
122     }
123 }
124
125 static void
126 run_lock_and_unlock_allows_other_process(void)
127 {
128     struct lockfile *lockfile;
129
130     assert(lockfile_lock("file", 0, &lockfile) == 0);
131     lockfile_unlock(lockfile);
132
133     if (do_fork() == CHILD) {
134         assert(lockfile_lock("file", 0, &lockfile) == 0);
135         exit(11);
136     }
137 }
138
139 static void
140 run_lock_timeout_gets_the_lock(void)
141 {
142     struct lockfile *lockfile;
143
144     assert(lockfile_lock("file", 0, &lockfile) == 0);
145
146     if (do_fork() == CHILD) {
147         assert(lockfile_lock("file", TIME_UPDATE_INTERVAL * 3,
148                              &lockfile) == 0);
149         exit(11);
150     } else {
151         long long int now = time_msec();
152         while (time_msec() < now + TIME_UPDATE_INTERVAL) {
153             pause();
154         }
155         lockfile_unlock(lockfile);
156     }
157 }
158
159 static void
160 run_lock_timeout_runs_out(void)
161 {
162     struct lockfile *lockfile;
163
164     assert(lockfile_lock("file", 0, &lockfile) == 0);
165
166     if (do_fork() == CHILD) {
167         assert(lockfile_lock("file", TIME_UPDATE_INTERVAL,
168                              &lockfile) == ETIMEDOUT);
169         exit(11);
170     } else {
171         long long int now = time_msec();
172         while (time_msec() < now + TIME_UPDATE_INTERVAL * 3) {
173             pause();
174         }
175         lockfile_unlock(lockfile);
176     }
177 }
178
179 static void
180 run_lock_multiple(void)
181 {
182     struct lockfile *a, *b, *c, *dummy;
183
184     assert(lockfile_lock("a", 0, &a) == 0);
185     assert(lockfile_lock("b", 0, &b) == 0);
186     assert(lockfile_lock("c", 0, &c) == 0);
187
188     lockfile_unlock(a);
189     assert(lockfile_lock("a", 0, &a) == 0);
190     assert(lockfile_lock("a", 0, &dummy) == EDEADLK);
191     lockfile_unlock(a);
192
193     lockfile_unlock(b);
194     assert(lockfile_lock("a", 0, &a) == 0);
195
196     lockfile_unlock(c);
197     lockfile_unlock(a);
198 }
199
200 static void
201 run_help(void)
202 {
203     size_t i;
204
205     printf("usage: %s TESTNAME\n"
206            "where TESTNAME is one of the following:\n",
207            program_name);
208     for (i = 0; tests[i].name; i++) {
209         fprintf(stderr, "\t%s\n", tests[i].name);
210     }
211 }
212
213 static const struct test tests[] = {
214 #define TEST(NAME) { #NAME, run_##NAME }
215     TEST(lock_and_unlock),
216     TEST(lock_and_unlock_twice),
217     TEST(lock_blocks_same_process),
218     TEST(lock_blocks_same_process_twice),
219     TEST(lock_blocks_other_process),
220     TEST(lock_twice_blocks_other_process),
221     TEST(lock_and_unlock_allows_other_process),
222     TEST(lock_timeout_gets_the_lock),
223     TEST(lock_timeout_runs_out),
224     TEST(lock_multiple),
225     TEST(help),
226     { 0, 0 }
227 #undef TEST
228 };
229
230 int
231 main(int argc, char *argv[])
232 {
233     size_t i;
234
235     set_program_name(argv[0]);
236     time_init();
237
238     if (argc != 2) {
239         ovs_fatal(0, "exactly one argument required; use \"%s help\" for help",
240                   program_name);
241         return 1;
242     }
243
244     for (i = 0; tests[i].name; i++) {
245         if (!strcmp(argv[1], tests[i].name)) {
246             int n_children;
247             int status;
248
249             (tests[i].function)();
250
251             n_children = 0;
252             while (wait(&status) > 0) {
253                 if (WIFEXITED(status) && WEXITSTATUS(status) == 11) {
254                     n_children++;
255                 } else {
256                     ovs_fatal(0, "child exited in unexpected way: %s",
257                               process_status_msg(status));
258                 }
259             }
260             if (errno != ECHILD) {
261                 ovs_fatal(errno, "wait");
262             }
263
264             printf("%s: success (%d child%s)\n",
265                    tests[i].name, n_children, n_children != 1 ? "ren" : "");
266             exit(0);
267         }
268     }
269     ovs_fatal(0, "unknown test \"%s\"; use \"%s help\" for help",
270               argv[1], program_name);
271 }
272