1 /* $NetBSD: t_sem.c,v 1.3 2017/01/14 20:58:20 christos Exp $ */
2
3 /*
4 * Copyright (c) 2008, 2010 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 /*
30 * Copyright (C) 2000 Jason Evans <jasone@freebsd.org>.
31 * All rights reserved.
32 *
33 * Redistribution and use in source and binary forms, with or without
34 * modification, are permitted provided that the following conditions
35 * are met:
36 * 1. Redistributions of source code must retain the above copyright
37 * notice(s), this list of conditions and the following disclaimer as
38 * the first lines of this file unmodified other than the possible
39 * addition of one or more copyright notices.
40 * 2. Redistributions in binary form must reproduce the above copyright
41 * notice(s), this list of conditions and the following disclaimer in
42 * the documentation and/or other materials provided with the
43 * distribution.
44 *
45 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
46 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
48 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE
49 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
50 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
51 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
52 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
53 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
54 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
55 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
56 */
57
58 #include <sys/cdefs.h>
59 __COPYRIGHT("@(#) Copyright (c) 2008, 2010\
60 The NetBSD Foundation, inc. All rights reserved.");
61 __RCSID("$NetBSD: t_sem.c,v 1.3 2017/01/14 20:58:20 christos Exp $");
62
63 #ifdef __FreeBSD__
64 #include <sys/types.h>
65 #include <sys/sysctl.h>
66 #endif
67
68 #include <sys/time.h>
69 #include <sys/wait.h>
70
71 #include <errno.h>
72 #include <fcntl.h>
73 #include <semaphore.h>
74 #include <signal.h>
75 #include <stdio.h>
76 #include <time.h>
77 #include <unistd.h>
78
79 #include <atf-c.h>
80
81 #define NCHILDREN 10
82
83 #define SEM_REQUIRE(x) \
84 ATF_REQUIRE_EQ_MSG(x, 0, "%s", strerror(errno))
85
86 #ifdef __FreeBSD__
87 static bool
machine_is_virtual(void)88 machine_is_virtual(void)
89 {
90 char vm_guest[32];
91 int error;
92
93 error = sysctlbyname("kern.vm_guest", vm_guest,
94 &(size_t){sizeof(vm_guest)}, NULL, 0);
95 ATF_CHECK_EQ_MSG(0, error, "sysctlbyname(kern.vm_guest): %s",
96 strerror(errno));
97 if (error != 0) {
98 return (false);
99 }
100 return (strcmp(vm_guest, "none") != 0);
101 }
102 #endif
103
104 ATF_TC_WITH_CLEANUP(basic);
ATF_TC_HEAD(basic,tc)105 ATF_TC_HEAD(basic, tc)
106 {
107 atf_tc_set_md_var(tc, "descr", "Checks basic functionality of POSIX "
108 "semaphores");
109 }
ATF_TC_BODY(basic,tc)110 ATF_TC_BODY(basic, tc)
111 {
112 int val;
113 sem_t *sem_b;
114
115 if (sysconf(_SC_SEMAPHORES) == -1)
116 atf_tc_skip("POSIX semaphores not supported");
117
118 sem_b = sem_open("/sem_b", O_CREAT | O_EXCL, 0644, 0);
119 ATF_REQUIRE(sem_b != SEM_FAILED);
120
121 ATF_REQUIRE_EQ(sem_getvalue(sem_b, &val), 0);
122 ATF_REQUIRE_EQ(val, 0);
123
124 ATF_REQUIRE_EQ(sem_post(sem_b), 0);
125 ATF_REQUIRE_EQ(sem_getvalue(sem_b, &val), 0);
126 ATF_REQUIRE_EQ(val, 1);
127
128 ATF_REQUIRE_EQ(sem_wait(sem_b), 0);
129 ATF_REQUIRE_EQ(sem_trywait(sem_b), -1);
130 ATF_REQUIRE_EQ(errno, EAGAIN);
131 ATF_REQUIRE_EQ(sem_post(sem_b), 0);
132 ATF_REQUIRE_EQ(sem_trywait(sem_b), 0);
133 ATF_REQUIRE_EQ(sem_post(sem_b), 0);
134 ATF_REQUIRE_EQ(sem_wait(sem_b), 0);
135 ATF_REQUIRE_EQ(sem_post(sem_b), 0);
136
137 ATF_REQUIRE_EQ(sem_close(sem_b), 0);
138 ATF_REQUIRE_EQ(sem_unlink("/sem_b"), 0);
139 }
ATF_TC_CLEANUP(basic,tc)140 ATF_TC_CLEANUP(basic, tc)
141 {
142 (void)sem_unlink("/sem_b");
143 }
144
145 ATF_TC_WITH_CLEANUP(child);
ATF_TC_HEAD(child,tc)146 ATF_TC_HEAD(child, tc)
147 {
148 atf_tc_set_md_var(tc, "descr", "Checks using semaphores to synchronize "
149 "parent with multiple child processes");
150 atf_tc_set_md_var(tc, "timeout", "5");
151 }
ATF_TC_BODY(child,tc)152 ATF_TC_BODY(child, tc)
153 {
154 pid_t children[NCHILDREN];
155 unsigned i, j;
156 sem_t *sem_a;
157 int status;
158
159 pid_t pid;
160
161 if (sysconf(_SC_SEMAPHORES) == -1)
162 atf_tc_skip("POSIX semaphores not supported");
163
164 sem_a = sem_open("/sem_a", O_CREAT | O_EXCL, 0644, 0);
165 ATF_REQUIRE(sem_a != SEM_FAILED);
166
167 for (j = 1; j <= 2; j++) {
168 for (i = 0; i < NCHILDREN; i++) {
169 switch ((pid = fork())) {
170 case -1:
171 atf_tc_fail("fork() returned -1");
172 case 0:
173 printf("PID %d waiting for semaphore...\n",
174 getpid());
175 ATF_REQUIRE_MSG(sem_wait(sem_a) == 0,
176 "sem_wait failed; iteration %d", j);
177 printf("PID %d got semaphore\n", getpid());
178 _exit(0);
179 default:
180 children[i] = pid;
181 break;
182 }
183 }
184
185 for (i = 0; i < NCHILDREN; i++) {
186 usleep(100000);
187 printf("main loop %d: posting...\n", j);
188 ATF_REQUIRE_EQ(sem_post(sem_a), 0);
189 }
190
191 for (i = 0; i < NCHILDREN; i++) {
192 ATF_REQUIRE_EQ(waitpid(children[i], &status, 0), children[i]);
193 ATF_REQUIRE(WIFEXITED(status));
194 ATF_REQUIRE_EQ(WEXITSTATUS(status), 0);
195 }
196 }
197
198 ATF_REQUIRE_EQ(sem_close(sem_a), 0);
199 ATF_REQUIRE_EQ(sem_unlink("/sem_a"), 0);
200 }
ATF_TC_CLEANUP(child,tc)201 ATF_TC_CLEANUP(child, tc)
202 {
203 (void)sem_unlink("/sem_a");
204 }
205
206 static inline void
timespec_add_ms(struct timespec * ts,int ms)207 timespec_add_ms(struct timespec *ts, int ms)
208 {
209 ts->tv_nsec += ms * 1000*1000;
210 if (ts->tv_nsec > 1000*1000*1000) {
211 ts->tv_sec++;
212 ts->tv_nsec -= 1000*1000*1000;
213 }
214 }
215
216 static volatile sig_atomic_t got_sigalrm = 0;
217
218 static void
sigalrm_handler(int sig __unused)219 sigalrm_handler(int sig __unused)
220 {
221 got_sigalrm = 1;
222 }
223
224 #ifdef __FreeBSD__
225 /* This is refactored from the timedwait test case. */
226 static void
setup_signals(void)227 setup_signals(void)
228 {
229 struct sigaction act = {
230 .sa_handler = sigalrm_handler,
231 .sa_flags = 0 /* not SA_RESTART */
232 };
233
234 ATF_REQUIRE_MSG(sigemptyset(&act.sa_mask) == 0,
235 "%s", strerror(errno));
236 ATF_REQUIRE_MSG(sigaction(SIGALRM, &act, NULL) == 0,
237 "%s", strerror(errno));
238 }
239 #endif
240
241 ATF_TC(timedwait);
ATF_TC_HEAD(timedwait,tc)242 ATF_TC_HEAD(timedwait, tc)
243 {
244 atf_tc_set_md_var(tc, "descr", "Tests sem_timedwait(3)");
245 atf_tc_set_md_var(tc, "timeout", "20");
246 }
ATF_TC_BODY(timedwait,tc)247 ATF_TC_BODY(timedwait, tc)
248 {
249 struct timespec ts;
250 sem_t sem;
251
252 SEM_REQUIRE(sem_init(&sem, 0, 0));
253 SEM_REQUIRE(sem_post(&sem));
254 ATF_REQUIRE_MSG(clock_gettime(CLOCK_REALTIME, &ts) == 0,
255 "%s", strerror(errno));
256 timespec_add_ms(&ts, 100);
257 SEM_REQUIRE(sem_timedwait(&sem, &ts));
258 ATF_REQUIRE_ERRNO(ETIMEDOUT, sem_timedwait(&sem, &ts));
259 ts.tv_sec--;
260 ATF_REQUIRE_ERRNO(ETIMEDOUT, sem_timedwait(&sem, &ts));
261 SEM_REQUIRE(sem_post(&sem));
262 SEM_REQUIRE(sem_timedwait(&sem, &ts));
263
264 /* timespec validation, in the past */
265 ts.tv_nsec += 1000*1000*1000;
266 ATF_REQUIRE_ERRNO(EINVAL, sem_timedwait(&sem, &ts));
267 ts.tv_nsec = -1;
268 ATF_REQUIRE_ERRNO(EINVAL, sem_timedwait(&sem, &ts));
269 /* timespec validation, in the future */
270 ATF_REQUIRE_MSG(clock_gettime(CLOCK_REALTIME, &ts) == 0,
271 "%s", strerror(errno));
272 ts.tv_sec++;
273 ts.tv_nsec = 1000*1000*1000;
274 ATF_REQUIRE_ERRNO(EINVAL, sem_timedwait(&sem, &ts));
275 ts.tv_nsec = -1;
276 ATF_REQUIRE_ERRNO(EINVAL, sem_timedwait(&sem, &ts));
277
278 /* EINTR */
279 #ifdef __FreeBSD__
280 /* This is refactored into a function. */
281 setup_signals();
282 #endif
283 struct itimerval it = {
284 .it_value.tv_usec = 50*1000
285 };
286 ATF_REQUIRE_MSG(setitimer(ITIMER_REAL, &it, NULL) == 0,
287 "%s", strerror(errno));
288 ATF_REQUIRE_MSG(clock_gettime(CLOCK_REALTIME, &ts) == 0,
289 "%s", strerror(errno));
290 timespec_add_ms(&ts, 100);
291 ATF_REQUIRE_ERRNO(EINTR, sem_timedwait(&sem, &ts));
292 ATF_REQUIRE_MSG(got_sigalrm, "did not get SIGALRM");
293 }
294
295 #ifdef __FreeBSD__
296
297 ATF_TC(clockwait_monotonic_absolute);
ATF_TC_HEAD(clockwait_monotonic_absolute,tc)298 ATF_TC_HEAD(clockwait_monotonic_absolute, tc)
299 {
300 atf_tc_set_md_var(tc, "descr",
301 "sem_clockwait_np(3) with monotonic clock and absolute time");
302 atf_tc_set_md_var(tc, "timeout", "20");
303 }
ATF_TC_BODY(clockwait_monotonic_absolute,tc)304 ATF_TC_BODY(clockwait_monotonic_absolute, tc)
305 {
306 struct timespec ts;
307 sem_t sem;
308
309 SEM_REQUIRE(sem_init(&sem, 0, 0));
310
311 SEM_REQUIRE(sem_post(&sem));
312 ATF_REQUIRE_MSG(clock_gettime(CLOCK_MONOTONIC, &ts) == 0,
313 "%s", strerror(errno));
314 timespec_add_ms(&ts, 100);
315 SEM_REQUIRE(sem_clockwait_np(&sem, CLOCK_MONOTONIC, TIMER_ABSTIME,
316 &ts, NULL));
317 ATF_REQUIRE_ERRNO(ETIMEDOUT,
318 sem_clockwait_np(&sem, CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, NULL));
319 }
320
321 ATF_TC(clockwait_monotonic_relative);
ATF_TC_HEAD(clockwait_monotonic_relative,tc)322 ATF_TC_HEAD(clockwait_monotonic_relative, tc)
323 {
324 atf_tc_set_md_var(tc, "descr",
325 "sem_clockwait_np(3) with monotonic clock and relative time");
326 atf_tc_set_md_var(tc, "timeout", "20");
327 }
ATF_TC_BODY(clockwait_monotonic_relative,tc)328 ATF_TC_BODY(clockwait_monotonic_relative, tc)
329 {
330 struct timespec ts;
331 sem_t sem;
332
333 SEM_REQUIRE(sem_init(&sem, 0, 0));
334
335 SEM_REQUIRE(sem_post(&sem));
336 ts.tv_sec = 0;
337 ts.tv_nsec = 100*1000*1000;
338 SEM_REQUIRE(sem_clockwait_np(&sem, CLOCK_MONOTONIC, 0,
339 &ts, NULL));
340 ATF_REQUIRE_ERRNO(ETIMEDOUT,
341 sem_clockwait_np(&sem, CLOCK_MONOTONIC, 0, &ts, NULL));
342 }
343
344 ATF_TC(clockwait_absolute_intr_remaining);
ATF_TC_HEAD(clockwait_absolute_intr_remaining,tc)345 ATF_TC_HEAD(clockwait_absolute_intr_remaining, tc)
346 {
347 atf_tc_set_md_var(tc, "descr",
348 "sem_clockwait_np(3) with absolute time does not update "
349 "remaining time on EINTR");
350 atf_tc_set_md_var(tc, "timeout", "20");
351 }
ATF_TC_BODY(clockwait_absolute_intr_remaining,tc)352 ATF_TC_BODY(clockwait_absolute_intr_remaining, tc)
353 {
354 struct timespec ts;
355 sem_t sem;
356
357 SEM_REQUIRE(sem_init(&sem, 0, 0));
358 setup_signals();
359
360 struct timespec remain = {42, 1000*1000*1000};
361 struct itimerval it = {
362 .it_value.tv_usec = 50*1000
363 };
364 ATF_REQUIRE_MSG(setitimer(ITIMER_REAL, &it, NULL) == 0,
365 "%s", strerror(errno));
366 ATF_REQUIRE_MSG(clock_gettime(CLOCK_MONOTONIC, &ts) == 0,
367 "%s", strerror(errno));
368 timespec_add_ms(&ts, 100);
369 ATF_REQUIRE_EQ(-1, sem_clockwait_np(&sem, CLOCK_MONOTONIC,
370 TIMER_ABSTIME, &ts, &remain));
371 ATF_REQUIRE_ERRNO(EINTR, 1);
372 ATF_REQUIRE_MSG(got_sigalrm, "did not get SIGALRM");
373 ATF_REQUIRE_MSG(remain.tv_sec == 42 && remain.tv_nsec == 1000*1000*1000,
374 "an absolute clockwait modified the remaining time on EINTR");
375 }
376
377 ATF_TC(clockwait_relative_intr_remaining);
ATF_TC_HEAD(clockwait_relative_intr_remaining,tc)378 ATF_TC_HEAD(clockwait_relative_intr_remaining, tc)
379 {
380 atf_tc_set_md_var(tc, "descr",
381 "sem_clockwait_np(3) with relative time updates "
382 "remaining time on EINTR");
383 atf_tc_set_md_var(tc, "timeout", "20");
384 }
ATF_TC_BODY(clockwait_relative_intr_remaining,tc)385 ATF_TC_BODY(clockwait_relative_intr_remaining, tc)
386 {
387 struct timespec ts;
388 sem_t sem;
389
390 SEM_REQUIRE(sem_init(&sem, 0, 0));
391 setup_signals();
392
393 struct timespec remain = {42, 1000*1000*1000};
394 struct itimerval it = {
395 .it_value.tv_usec = 50*1000
396 };
397 ATF_REQUIRE_MSG(setitimer(ITIMER_REAL, &it, NULL) == 0,
398 "%s", strerror(errno));
399 ts.tv_sec = 0;
400 ts.tv_nsec = 100*1000*1000;
401 ATF_REQUIRE_EQ(-1, sem_clockwait_np(&sem, CLOCK_MONOTONIC, 0, &ts,
402 &remain));
403 ATF_REQUIRE_ERRNO(EINTR, 1);
404 ATF_REQUIRE_MSG(got_sigalrm, "did not get SIGALRM");
405 ATF_REQUIRE_MSG(remain.tv_sec == 0 &&
406 (remain.tv_nsec >= 25*1000*1000 || machine_is_virtual()) &&
407 remain.tv_nsec <= 75*1000*1000,
408 "the remaining time was not as expected when a relative clockwait"
409 " got EINTR: %jd.%09ld", (uintmax_t)remain.tv_sec, remain.tv_nsec);
410 }
411
412 #endif /* __FreeBSD__ */
413
ATF_TP_ADD_TCS(tp)414 ATF_TP_ADD_TCS(tp)
415 {
416
417 ATF_TP_ADD_TC(tp, basic);
418 ATF_TP_ADD_TC(tp, child);
419 ATF_TP_ADD_TC(tp, timedwait);
420 #ifdef __FreeBSD__
421 ATF_TP_ADD_TC(tp, clockwait_monotonic_absolute);
422 ATF_TP_ADD_TC(tp, clockwait_monotonic_relative);
423 ATF_TP_ADD_TC(tp, clockwait_absolute_intr_remaining);
424 ATF_TP_ADD_TC(tp, clockwait_relative_intr_remaining);
425 #endif
426
427 return atf_no_error();
428 }
429