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