xref: /freebsd/contrib/netbsd-tests/lib/librt/t_sem.c (revision 43a5ec4eb41567cc92586503212743d89686d78f)
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
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);
105 ATF_TC_HEAD(basic, tc)
106 {
107 	atf_tc_set_md_var(tc, "descr", "Checks basic functionality of POSIX "
108 	    "semaphores");
109 }
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 }
140 ATF_TC_CLEANUP(basic, tc)
141 {
142 	(void)sem_unlink("/sem_b");
143 }
144 
145 ATF_TC_WITH_CLEANUP(child);
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 }
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 }
201 ATF_TC_CLEANUP(child, tc)
202 {
203 	(void)sem_unlink("/sem_a");
204 }
205 
206 static inline void
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
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
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);
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 }
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);
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 }
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);
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 }
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);
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 }
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);
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 }
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 
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