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