Merge "master" into "next".
[cascardo/ovs.git] / tests / test-lockfile.c
1 /*
2  * Copyright (c) 2009, 2010 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     /* Making this static prevents a memory leak warning from valgrind for the
104      * parent process, which cannot easily unlock (and free) 'lockfile' because
105      * it can only do so after the child has exited, and it's the caller of
106      * this function that does the wait() call. */
107     static struct lockfile *lockfile;
108
109     assert(lockfile_lock("file", 0, &lockfile) == 0);
110     if (do_fork() == CHILD) {
111         lockfile_unlock(lockfile);
112         assert(lockfile_lock("file", 0, &lockfile) == EAGAIN);
113         exit(11);
114     }
115 }
116
117 static void
118 run_lock_twice_blocks_other_process(void)
119 {
120     struct lockfile *lockfile, *dummy;
121
122     assert(lockfile_lock("file", 0, &lockfile) == 0);
123     assert(lockfile_lock("file", 0, &dummy) == EDEADLK);
124     if (do_fork() == CHILD) {
125         assert(lockfile_lock("file", 0, &dummy) == EAGAIN);
126         exit(11);
127     }
128 }
129
130 static void
131 run_lock_and_unlock_allows_other_process(void)
132 {
133     struct lockfile *lockfile;
134
135     assert(lockfile_lock("file", 0, &lockfile) == 0);
136     lockfile_unlock(lockfile);
137
138     if (do_fork() == CHILD) {
139         assert(lockfile_lock("file", 0, &lockfile) == 0);
140         exit(11);
141     }
142 }
143
144 static void
145 run_lock_timeout_gets_the_lock(void)
146 {
147     struct lockfile *lockfile;
148
149     assert(lockfile_lock("file", 0, &lockfile) == 0);
150
151     if (do_fork() == CHILD) {
152         lockfile_unlock(lockfile);
153         assert(lockfile_lock("file", TIME_UPDATE_INTERVAL * 3,
154                              &lockfile) == 0);
155         exit(11);
156     } else {
157         long long int now = time_msec();
158         while (time_msec() < now + TIME_UPDATE_INTERVAL) {
159             pause();
160         }
161         lockfile_unlock(lockfile);
162     }
163 }
164
165 static void
166 run_lock_timeout_runs_out(void)
167 {
168     struct lockfile *lockfile;
169
170     assert(lockfile_lock("file", 0, &lockfile) == 0);
171
172     if (do_fork() == CHILD) {
173         lockfile_unlock(lockfile);
174         assert(lockfile_lock("file", TIME_UPDATE_INTERVAL,
175                              &lockfile) == ETIMEDOUT);
176         exit(11);
177     } else {
178         long long int now = time_msec();
179         while (time_msec() < now + TIME_UPDATE_INTERVAL * 3) {
180             pause();
181         }
182         lockfile_unlock(lockfile);
183     }
184 }
185
186 static void
187 run_lock_multiple(void)
188 {
189     struct lockfile *a, *b, *c, *dummy;
190
191     assert(lockfile_lock("a", 0, &a) == 0);
192     assert(lockfile_lock("b", 0, &b) == 0);
193     assert(lockfile_lock("c", 0, &c) == 0);
194
195     lockfile_unlock(a);
196     assert(lockfile_lock("a", 0, &a) == 0);
197     assert(lockfile_lock("a", 0, &dummy) == EDEADLK);
198     lockfile_unlock(a);
199
200     lockfile_unlock(b);
201     assert(lockfile_lock("a", 0, &a) == 0);
202
203     lockfile_unlock(c);
204     lockfile_unlock(a);
205 }
206
207 static void
208 run_help(void)
209 {
210     size_t i;
211
212     printf("usage: %s TESTNAME\n"
213            "where TESTNAME is one of the following:\n",
214            program_name);
215     for (i = 0; tests[i].name; i++) {
216         fprintf(stderr, "\t%s\n", tests[i].name);
217     }
218 }
219
220 static const struct test tests[] = {
221 #define TEST(NAME) { #NAME, run_##NAME }
222     TEST(lock_and_unlock),
223     TEST(lock_and_unlock_twice),
224     TEST(lock_blocks_same_process),
225     TEST(lock_blocks_same_process_twice),
226     TEST(lock_blocks_other_process),
227     TEST(lock_twice_blocks_other_process),
228     TEST(lock_and_unlock_allows_other_process),
229     TEST(lock_timeout_gets_the_lock),
230     TEST(lock_timeout_runs_out),
231     TEST(lock_multiple),
232     TEST(help),
233     { 0, 0 }
234 #undef TEST
235 };
236
237 int
238 main(int argc, char *argv[])
239 {
240     size_t i;
241
242     set_program_name(argv[0]);
243     time_init();
244
245     if (argc != 2) {
246         ovs_fatal(0, "exactly one argument required; use \"%s help\" for help",
247                   program_name);
248         return 1;
249     }
250
251     for (i = 0; tests[i].name; i++) {
252         if (!strcmp(argv[1], tests[i].name)) {
253             int n_children;
254             int status;
255
256             (tests[i].function)();
257
258             n_children = 0;
259             while (wait(&status) > 0) {
260                 if (WIFEXITED(status) && WEXITSTATUS(status) == 11) {
261                     n_children++;
262                 } else {
263                     ovs_fatal(0, "child exited in unexpected way: %s",
264                               process_status_msg(status));
265                 }
266             }
267             if (errno != ECHILD) {
268                 ovs_fatal(errno, "wait");
269             }
270
271             printf("%s: success (%d child%s)\n",
272                    tests[i].name, n_children, n_children != 1 ? "ren" : "");
273             exit(0);
274         }
275     }
276     ovs_fatal(0, "unknown test \"%s\"; use \"%s help\" for help",
277               argv[1], program_name);
278 }
279