]>
Commit | Line | Data |
---|---|---|
ac718c9d | 1 | /* |
93ff0290 | 2 | * Copyright (c) 2009, 2010 Nicira Networks. |
ac718c9d BP |
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" | |
1e8cf0f7 | 29 | #include "vlog.h" |
ac718c9d BP |
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 | { | |
93ff0290 BP |
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; | |
ac718c9d BP |
109 | |
110 | assert(lockfile_lock("file", 0, &lockfile) == 0); | |
111 | if (do_fork() == CHILD) { | |
93ff0290 | 112 | lockfile_unlock(lockfile); |
ac718c9d BP |
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) { | |
93ff0290 | 153 | lockfile_unlock(lockfile); |
ac718c9d BP |
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) { | |
93ff0290 | 174 | lockfile_unlock(lockfile); |
ac718c9d BP |
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 | { | |
480ce8ab | 241 | extern struct vlog_module VLM_lockfile; |
ac718c9d BP |
242 | size_t i; |
243 | ||
244 | set_program_name(argv[0]); | |
480ce8ab | 245 | vlog_set_levels(&VLM_lockfile, VLF_ANY_FACILITY, VLL_ERR); |
ac718c9d BP |
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 |