chiark / gitweb /
resolved: rework logic so that we can share transactions between queries of different...
[elogind.git] / src / test / test-barrier.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2014 David Herrmann <dh.herrmann@gmail.com>
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 /*
23  * IPC barrier tests
24  * These tests verify the correct behavior of the IPC Barrier implementation.
25  * Note that the tests use alarm-timers to verify dead-locks and timeouts. These
26  * might not work on slow machines where 20ms are too short to perform specific
27  * operations (though, very unlikely). In case that turns out true, we have to
28  * increase it at the slightly cost of lengthen test-duration on other machines.
29  */
30
31 #include <errno.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <sys/time.h>
35 #include <sys/wait.h>
36 #include <unistd.h>
37
38 #include "barrier.h"
39 #include "def.h"
40 #include "util.h"
41
42 /* 20ms to test deadlocks; All timings use multiples of this constant as
43  * alarm/sleep timers. If this timeout is too small for slow machines to perform
44  * the requested operations, we have to increase it. On an i7 this works fine
45  * with 1ms base-time, so 20ms should be just fine for everyone. */
46 #define BASE_TIME 20
47
48 static void malarm(unsigned long msecs) {
49         struct itimerval v = { };
50
51         timeval_store(&v.it_value, msecs * USEC_PER_MSEC);
52         assert_se(setitimer(ITIMER_REAL, &v, NULL) >= 0);
53 }
54
55 static void msleep(unsigned long msecs) {
56         assert_se(msecs < MSEC_PER_SEC);
57         usleep(msecs * USEC_PER_MSEC);
58 }
59
60 #define TEST_BARRIER(_FUNCTION, _CHILD_CODE, _WAIT_CHILD, _PARENT_CODE, _WAIT_PARENT)  \
61         static void _FUNCTION(void) {                                   \
62                 Barrier b = BARRIER_NULL;                               \
63                 pid_t pid1, pid2;                                       \
64                                                                         \
65                 assert_se(barrier_create(&b) >= 0);                     \
66                                                                         \
67                 pid1 = fork();                                          \
68                 assert_se(pid1 >= 0);                                   \
69                 if (pid1 == 0) {                                        \
70                         barrier_set_role(&b, BARRIER_CHILD);            \
71                         { _CHILD_CODE; }                                \
72                         exit(42);                                       \
73                 }                                                       \
74                                                                         \
75                 pid2 = fork();                                          \
76                 assert_se(pid2 >= 0);                                   \
77                 if (pid2 == 0) {                                        \
78                         barrier_set_role(&b, BARRIER_PARENT);           \
79                         { _PARENT_CODE; }                               \
80                         exit(42);                                       \
81                 }                                                       \
82                                                                         \
83                 barrier_destroy(&b);                                    \
84                 malarm(999);                                            \
85                 { _WAIT_CHILD; }                                        \
86                 { _WAIT_PARENT; }                                       \
87                 malarm(0);                                              \
88         }
89
90 #define TEST_BARRIER_WAIT_SUCCESS(_pid) \
91                 ({                                                      \
92                         int pidr, status;                               \
93                         pidr = waitpid(_pid, &status, 0);               \
94                         assert_se(pidr == _pid);                        \
95                         assert_se(WIFEXITED(status));                   \
96                         assert_se(WEXITSTATUS(status) == 42);           \
97                 })
98
99 #define TEST_BARRIER_WAIT_ALARM(_pid) \
100                 ({                                                      \
101                         int pidr, status;                               \
102                         pidr = waitpid(_pid, &status, 0);               \
103                         assert_se(pidr == _pid);                        \
104                         assert_se(WIFSIGNALED(status));                 \
105                         assert_se(WTERMSIG(status) == SIGALRM);         \
106                 })
107
108 /*
109  * Test basic sync points
110  * This places a barrier in both processes and waits synchronously for them.
111  * The timeout makes sure the sync works as expected. The msleep() on one side
112  * makes sure the exit of the parent does not overwrite previous barriers. Due
113  * to the msleep(), we know that the parent already exited, thus there's a
114  * pending HUP on the pipe. However, the barrier_sync() prefers reads on the
115  * eventfd, thus we can safely wait on the barrier.
116  */
117 TEST_BARRIER(test_barrier_sync,
118         ({
119                 malarm(BASE_TIME * 10);
120                 assert_se(barrier_place(&b));
121                 msleep(BASE_TIME * 2);
122                 assert_se(barrier_sync(&b));
123         }),
124         TEST_BARRIER_WAIT_SUCCESS(pid1),
125         ({
126                 malarm(BASE_TIME * 10);
127                 assert_se(barrier_place(&b));
128                 assert_se(barrier_sync(&b));
129         }),
130         TEST_BARRIER_WAIT_SUCCESS(pid2));
131
132 /*
133  * Test wait_next()
134  * This places a barrier in the parent and syncs on it. The child sleeps while
135  * the parent places the barrier and then waits for a barrier. The wait will
136  * succeed as the child hasn't read the parent's barrier, yet. The following
137  * barrier and sync synchronize the exit.
138  */
139 TEST_BARRIER(test_barrier_wait_next,
140         ({
141                 msleep(100);
142                 malarm(BASE_TIME * 10);
143                 assert_se(barrier_wait_next(&b));
144                 assert_se(barrier_place(&b));
145                 assert_se(barrier_sync(&b));
146         }),
147         TEST_BARRIER_WAIT_SUCCESS(pid1),
148         ({
149                 malarm(400);
150                 assert_se(barrier_place(&b));
151                 assert_se(barrier_sync(&b));
152         }),
153         TEST_BARRIER_WAIT_SUCCESS(pid2));
154
155 /*
156  * Test wait_next() multiple times
157  * This places two barriers in the parent and waits for the child to exit. The
158  * child sleeps 20ms so both barriers _should_ be in place. It then waits for
159  * the parent to place the next barrier twice. The first call will fetch both
160  * barriers and return. However, the second call will stall as the parent does
161  * not place a 3rd barrier (the sleep caught two barriers). wait_next() is does
162  * not look at barrier-links so this stall is expected. Thus this test times
163  * out.
164  */
165 TEST_BARRIER(test_barrier_wait_next_twice,
166         ({
167                 msleep(BASE_TIME);
168                 malarm(BASE_TIME);
169                 assert_se(barrier_wait_next(&b));
170                 assert_se(barrier_wait_next(&b));
171                 assert_se(0);
172         }),
173         TEST_BARRIER_WAIT_ALARM(pid1),
174         ({
175                 malarm(BASE_TIME * 10);
176                 assert_se(barrier_place(&b));
177                 assert_se(barrier_place(&b));
178                 msleep(BASE_TIME * 2);
179         }),
180         TEST_BARRIER_WAIT_SUCCESS(pid2));
181
182 /*
183  * Test wait_next() with local barriers
184  * This is the same as test_barrier_wait_next_twice, but places local barriers
185  * between both waits. This does not have any effect on the wait so it times out
186  * like the other test.
187  */
188 TEST_BARRIER(test_barrier_wait_next_twice_local,
189         ({
190                 msleep(BASE_TIME);
191                 malarm(BASE_TIME);
192                 assert_se(barrier_wait_next(&b));
193                 assert_se(barrier_place(&b));
194                 assert_se(barrier_place(&b));
195                 assert_se(barrier_wait_next(&b));
196                 assert_se(0);
197         }),
198         TEST_BARRIER_WAIT_ALARM(pid1),
199         ({
200                 malarm(BASE_TIME * 10);
201                 assert_se(barrier_place(&b));
202                 assert_se(barrier_place(&b));
203                 msleep(BASE_TIME * 2);
204         }),
205         TEST_BARRIER_WAIT_SUCCESS(pid2));
206
207 /*
208  * Test wait_next() with sync_next()
209  * This is again the same as test_barrier_wait_next_twice but uses a
210  * synced wait as the second wait. This works just fine because the local state
211  * has no barriers placed, therefore, the remote is always in sync.
212  */
213 TEST_BARRIER(test_barrier_wait_next_twice_sync,
214         ({
215                 msleep(BASE_TIME);
216                 malarm(BASE_TIME);
217                 assert_se(barrier_wait_next(&b));
218                 assert_se(barrier_sync_next(&b));
219         }),
220         TEST_BARRIER_WAIT_SUCCESS(pid1),
221         ({
222                 malarm(BASE_TIME * 10);
223                 assert_se(barrier_place(&b));
224                 assert_se(barrier_place(&b));
225         }),
226         TEST_BARRIER_WAIT_SUCCESS(pid2));
227
228 /*
229  * Test wait_next() with sync_next() and local barriers
230  * This is again the same as test_barrier_wait_next_twice_local but uses a
231  * synced wait as the second wait. This works just fine because the local state
232  * is in sync with the remote.
233  */
234 TEST_BARRIER(test_barrier_wait_next_twice_local_sync,
235         ({
236                 msleep(BASE_TIME);
237                 malarm(BASE_TIME);
238                 assert_se(barrier_wait_next(&b));
239                 assert_se(barrier_place(&b));
240                 assert_se(barrier_place(&b));
241                 assert_se(barrier_sync_next(&b));
242         }),
243         TEST_BARRIER_WAIT_SUCCESS(pid1),
244         ({
245                 malarm(BASE_TIME * 10);
246                 assert_se(barrier_place(&b));
247                 assert_se(barrier_place(&b));
248         }),
249         TEST_BARRIER_WAIT_SUCCESS(pid2));
250
251 /*
252  * Test sync_next() and sync()
253  * This tests sync_*() synchronizations and makes sure they work fine if the
254  * local state is behind the remote state.
255  */
256 TEST_BARRIER(test_barrier_sync_next,
257         ({
258                 malarm(BASE_TIME * 10);
259                 assert_se(barrier_sync_next(&b));
260                 assert_se(barrier_sync(&b));
261                 assert_se(barrier_place(&b));
262                 assert_se(barrier_place(&b));
263                 assert_se(barrier_sync_next(&b));
264                 assert_se(barrier_sync_next(&b));
265                 assert_se(barrier_sync(&b));
266         }),
267         TEST_BARRIER_WAIT_SUCCESS(pid1),
268         ({
269                 malarm(BASE_TIME * 10);
270                 msleep(BASE_TIME);
271                 assert_se(barrier_place(&b));
272                 assert_se(barrier_place(&b));
273                 assert_se(barrier_sync(&b));
274         }),
275         TEST_BARRIER_WAIT_SUCCESS(pid2));
276
277 /*
278  * Test sync_next() and sync() with local barriers
279  * This tests timeouts if sync_*() is used if local barriers are placed but the
280  * remote didn't place any.
281  */
282 TEST_BARRIER(test_barrier_sync_next_local,
283         ({
284                 malarm(BASE_TIME);
285                 assert_se(barrier_place(&b));
286                 assert_se(barrier_sync_next(&b));
287                 assert_se(0);
288         }),
289         TEST_BARRIER_WAIT_ALARM(pid1),
290         ({
291                 msleep(BASE_TIME * 2);
292         }),
293         TEST_BARRIER_WAIT_SUCCESS(pid2));
294
295 /*
296  * Test sync_next() and sync() with local barriers and abortion
297  * This is the same as test_barrier_sync_next_local but aborts the sync in the
298  * parent. Therefore, the sync_next() succeeds just fine due to the abortion.
299  */
300 TEST_BARRIER(test_barrier_sync_next_local_abort,
301         ({
302                 malarm(BASE_TIME * 10);
303                 assert_se(barrier_place(&b));
304                 assert_se(!barrier_sync_next(&b));
305         }),
306         TEST_BARRIER_WAIT_SUCCESS(pid1),
307         ({
308                 assert_se(barrier_abort(&b));
309         }),
310         TEST_BARRIER_WAIT_SUCCESS(pid2));
311
312 /*
313  * Test matched wait_abortion()
314  * This runs wait_abortion() with remote abortion.
315  */
316 TEST_BARRIER(test_barrier_wait_abortion,
317         ({
318                 malarm(BASE_TIME * 10);
319                 assert_se(barrier_wait_abortion(&b));
320         }),
321         TEST_BARRIER_WAIT_SUCCESS(pid1),
322         ({
323                 assert_se(barrier_abort(&b));
324         }),
325         TEST_BARRIER_WAIT_SUCCESS(pid2));
326
327 /*
328  * Test unmatched wait_abortion()
329  * This runs wait_abortion() without any remote abortion going on. It thus must
330  * timeout.
331  */
332 TEST_BARRIER(test_barrier_wait_abortion_unmatched,
333         ({
334                 malarm(BASE_TIME);
335                 assert_se(barrier_wait_abortion(&b));
336                 assert_se(0);
337         }),
338         TEST_BARRIER_WAIT_ALARM(pid1),
339         ({
340                 msleep(BASE_TIME * 2);
341         }),
342         TEST_BARRIER_WAIT_SUCCESS(pid2));
343
344 /*
345  * Test matched wait_abortion() with local abortion
346  * This runs wait_abortion() with local and remote abortion.
347  */
348 TEST_BARRIER(test_barrier_wait_abortion_local,
349         ({
350                 malarm(BASE_TIME * 10);
351                 assert_se(barrier_abort(&b));
352                 assert_se(!barrier_wait_abortion(&b));
353         }),
354         TEST_BARRIER_WAIT_SUCCESS(pid1),
355         ({
356                 assert_se(barrier_abort(&b));
357         }),
358         TEST_BARRIER_WAIT_SUCCESS(pid2));
359
360 /*
361  * Test unmatched wait_abortion() with local abortion
362  * This runs wait_abortion() with only local abortion. This must time out.
363  */
364 TEST_BARRIER(test_barrier_wait_abortion_local_unmatched,
365         ({
366                 malarm(BASE_TIME);
367                 assert_se(barrier_abort(&b));
368                 assert_se(!barrier_wait_abortion(&b));
369                 assert_se(0);
370         }),
371         TEST_BARRIER_WAIT_ALARM(pid1),
372         ({
373                 msleep(BASE_TIME * 2);
374         }),
375         TEST_BARRIER_WAIT_SUCCESS(pid2));
376
377 /*
378  * Test child exit
379  * Place barrier and sync with the child. The child only exits()s, which should
380  * cause an implicit abortion and wake the parent.
381  */
382 TEST_BARRIER(test_barrier_exit,
383         ({
384         }),
385         TEST_BARRIER_WAIT_SUCCESS(pid1),
386         ({
387                 malarm(BASE_TIME * 10);
388                 assert_se(barrier_place(&b));
389                 assert_se(!barrier_sync(&b));
390         }),
391         TEST_BARRIER_WAIT_SUCCESS(pid2));
392
393 /*
394  * Test child exit with sleep
395  * Same as test_barrier_exit but verifies the test really works due to the
396  * child-exit. We add a usleep() which triggers the alarm in the parent and
397  * causes the test to time out.
398  */
399 TEST_BARRIER(test_barrier_no_exit,
400         ({
401                 msleep(BASE_TIME * 2);
402         }),
403         TEST_BARRIER_WAIT_SUCCESS(pid1),
404         ({
405                 malarm(BASE_TIME);
406                 assert_se(barrier_place(&b));
407                 assert_se(!barrier_sync(&b));
408         }),
409         TEST_BARRIER_WAIT_ALARM(pid2));
410
411 /*
412  * Test pending exit against sync
413  * The parent places a barrier *and* exits. The 20ms wait in the child
414  * guarantees both are pending. However, our logic prefers pending barriers over
415  * pending exit-abortions (unlike normal abortions), thus the wait_next() must
416  * succeed, same for the sync_next() as our local barrier-count is smaller than
417  * the remote. Once we place a barrier our count is equal, so the sync still
418  * succeeds. Only if we place one more barrier, we're ahead of the remote, thus
419  * we will fail due to HUP on the pipe.
420  */
421 TEST_BARRIER(test_barrier_pending_exit,
422         ({
423                 malarm(BASE_TIME * 4);
424                 msleep(BASE_TIME * 2);
425                 assert_se(barrier_wait_next(&b));
426                 assert_se(barrier_sync_next(&b));
427                 assert_se(barrier_place(&b));
428                 assert_se(barrier_sync_next(&b));
429                 assert_se(barrier_place(&b));
430                 assert_se(!barrier_sync_next(&b));
431         }),
432         TEST_BARRIER_WAIT_SUCCESS(pid1),
433         ({
434                 assert_se(barrier_place(&b));
435         }),
436         TEST_BARRIER_WAIT_SUCCESS(pid2));
437
438 int main(int argc, char *argv[]) {
439         log_parse_environment();
440         log_open();
441
442         test_barrier_sync();
443         test_barrier_wait_next();
444         test_barrier_wait_next_twice();
445         test_barrier_wait_next_twice_sync();
446         test_barrier_wait_next_twice_local();
447         test_barrier_wait_next_twice_local_sync();
448         test_barrier_sync_next();
449         test_barrier_sync_next_local();
450         test_barrier_sync_next_local_abort();
451         test_barrier_wait_abortion();
452         test_barrier_wait_abortion_unmatched();
453         test_barrier_wait_abortion_local();
454         test_barrier_wait_abortion_local_unmatched();
455         test_barrier_exit();
456         test_barrier_no_exit();
457         test_barrier_pending_exit();
458
459         return 0;
460 }