Release Open vSwitch 1.1.0-pre1
[sliver-openvswitch.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 #include "vlog.h"
30
31 #undef NDEBUG
32 #include <assert.h>
33
34 struct test {
35     const char *name;
36     void (*function)(void);
37 };
38
39 static const struct test tests[];
40
41 static void
42 run_lock_and_unlock(void)
43 {
44     struct lockfile *lockfile;
45
46     assert(lockfile_lock("file", 0, &lockfile) == 0);
47     lockfile_unlock(lockfile);
48 }
49
50 static void
51 run_lock_and_unlock_twice(void)
52 {
53     struct lockfile *lockfile;
54
55     assert(lockfile_lock("file", 0, &lockfile) == 0);
56     lockfile_unlock(lockfile);
57
58     assert(lockfile_lock("file", 0, &lockfile) == 0);
59     lockfile_unlock(lockfile);
60 }
61
62 static void
63 run_lock_blocks_same_process(void)
64 {
65     struct lockfile *lockfile;
66
67     assert(lockfile_lock("file", 0, &lockfile) == 0);
68     assert(lockfile_lock("file", 0, &lockfile) == EDEADLK);
69     lockfile_unlock(lockfile);
70 }
71
72 static void
73 run_lock_blocks_same_process_twice(void)
74 {
75     struct lockfile *lockfile;
76
77     assert(lockfile_lock("file", 0, &lockfile) == 0);
78     assert(lockfile_lock("file", 0, &lockfile) == EDEADLK);
79     assert(lockfile_lock("file", 0, &lockfile) == EDEADLK);
80     lockfile_unlock(lockfile);
81 }
82
83 static enum { PARENT, CHILD }
84 do_fork(void)
85 {
86     switch (fork()) {
87     case 0:
88         time_postfork();
89         lockfile_postfork();
90         return CHILD;
91
92     default:
93         return PARENT;
94
95     case -1:
96         /* Error. */
97         ovs_fatal(errno, "fork failed");
98     }
99 }
100
101 static void
102 run_lock_blocks_other_process(void)
103 {
104     /* Making this static prevents a memory leak warning from valgrind for the
105      * parent process, which cannot easily unlock (and free) 'lockfile' because
106      * it can only do so after the child has exited, and it's the caller of
107      * this function that does the wait() call. */
108     static struct lockfile *lockfile;
109
110     assert(lockfile_lock("file", 0, &lockfile) == 0);
111     if (do_fork() == CHILD) {
112         lockfile_unlock(lockfile);
113         assert(lockfile_lock("file", 0, &lockfile) == EAGAIN);
114         exit(11);
115     }
116 }
117
118 static void
119 run_lock_twice_blocks_other_process(void)
120 {
121     struct lockfile *lockfile, *dummy;
122
123     assert(lockfile_lock("file", 0, &lockfile) == 0);
124     assert(lockfile_lock("file", 0, &dummy) == EDEADLK);
125     if (do_fork() == CHILD) {
126         assert(lockfile_lock("file", 0, &dummy) == EAGAIN);
127         exit(11);
128     }
129 }
130
131 static void
132 run_lock_and_unlock_allows_other_process(void)
133 {
134     struct lockfile *lockfile;
135
136     assert(lockfile_lock("file", 0, &lockfile) == 0);
137     lockfile_unlock(lockfile);
138
139     if (do_fork() == CHILD) {
140         assert(lockfile_lock("file", 0, &lockfile) == 0);
141         exit(11);
142     }
143 }
144
145 static void
146 run_lock_timeout_gets_the_lock(void)
147 {
148     struct lockfile *lockfile;
149
150     assert(lockfile_lock("file", 0, &lockfile) == 0);
151
152     if (do_fork() == CHILD) {
153         lockfile_unlock(lockfile);
154         assert(lockfile_lock("file", TIME_UPDATE_INTERVAL * 3,
155                              &lockfile) == 0);
156         exit(11);
157     } else {
158         long long int now = time_msec();
159         while (time_msec() < now + TIME_UPDATE_INTERVAL) {
160             pause();
161         }
162         lockfile_unlock(lockfile);
163     }
164 }
165
166 static void
167 run_lock_timeout_runs_out(void)
168 {
169     struct lockfile *lockfile;
170
171     assert(lockfile_lock("file", 0, &lockfile) == 0);
172
173     if (do_fork() == CHILD) {
174         lockfile_unlock(lockfile);
175         assert(lockfile_lock("file", TIME_UPDATE_INTERVAL,
176                              &lockfile) == ETIMEDOUT);
177         exit(11);
178     } else {
179         long long int now = time_msec();
180         while (time_msec() < now + TIME_UPDATE_INTERVAL * 3) {
181             pause();
182         }
183         lockfile_unlock(lockfile);
184     }
185 }
186
187 static void
188 run_lock_multiple(void)
189 {
190     struct lockfile *a, *b, *c, *dummy;
191
192     assert(lockfile_lock("a", 0, &a) == 0);
193     assert(lockfile_lock("b", 0, &b) == 0);
194     assert(lockfile_lock("c", 0, &c) == 0);
195
196     lockfile_unlock(a);
197     assert(lockfile_lock("a", 0, &a) == 0);
198     assert(lockfile_lock("a", 0, &dummy) == EDEADLK);
199     lockfile_unlock(a);
200
201     lockfile_unlock(b);
202     assert(lockfile_lock("a", 0, &a) == 0);
203
204     lockfile_unlock(c);
205     lockfile_unlock(a);
206 }
207
208 static void
209 run_help(void)
210 {
211     size_t i;
212
213     printf("usage: %s TESTNAME\n"
214            "where TESTNAME is one of the following:\n",
215            program_name);
216     for (i = 0; tests[i].name; i++) {
217         fprintf(stderr, "\t%s\n", tests[i].name);
218     }
219 }
220
221 static const struct test tests[] = {
222 #define TEST(NAME) { #NAME, run_##NAME }
223     TEST(lock_and_unlock),
224     TEST(lock_and_unlock_twice),
225     TEST(lock_blocks_same_process),
226     TEST(lock_blocks_same_process_twice),
227     TEST(lock_blocks_other_process),
228     TEST(lock_twice_blocks_other_process),
229     TEST(lock_and_unlock_allows_other_process),
230     TEST(lock_timeout_gets_the_lock),
231     TEST(lock_timeout_runs_out),
232     TEST(lock_multiple),
233     TEST(help),
234     { 0, 0 }
235 #undef TEST
236 };
237
238 int
239 main(int argc, char *argv[])
240 {
241     extern struct vlog_module VLM_lockfile;
242     size_t i;
243
244     set_program_name(argv[0]);
245     vlog_set_levels(&VLM_lockfile, VLF_ANY_FACILITY, VLL_ERR);
246
247     if (argc != 2) {
248         ovs_fatal(0, "exactly one argument required; use \"%s help\" for help",
249                   program_name);
250         return 1;
251     }
252
253     for (i = 0; tests[i].name; i++) {
254         if (!strcmp(argv[1], tests[i].name)) {
255             int n_children;
256             int status;
257
258             (tests[i].function)();
259
260             n_children = 0;
261             while (wait(&status) > 0) {
262                 if (WIFEXITED(status) && WEXITSTATUS(status) == 11) {
263                     n_children++;
264                 } else {
265                     ovs_fatal(0, "child exited in unexpected way: %s",
266                               process_status_msg(status));
267                 }
268             }
269             if (errno != ECHILD) {
270                 ovs_fatal(errno, "wait");
271             }
272
273             printf("%s: success (%d child%s)\n",
274                    tests[i].name, n_children, n_children != 1 ? "ren" : "");
275             exit(0);
276         }
277     }
278     ovs_fatal(0, "unknown test \"%s\"; use \"%s help\" for help",
279               argv[1], program_name);
280 }
281