]> git.proxmox.com Git - mirror_ovs.git/blob - tests/test-lockfile.c
Merge "master" into "next".
[mirror_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