1 /* $NetBSD: t_timer_create.c,v 1.9 2024/12/19 23:41:46 riastradh Exp $ */ 2 3 /*- 4 * Copyright (c) 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 #include <atf-c.h> 30 #include <errno.h> 31 #include <stdio.h> 32 #include <signal.h> 33 #include <string.h> 34 #include <time.h> 35 #include <unistd.h> 36 37 #include "h_macros.h" 38 39 static timer_t t; 40 static sig_atomic_t expired; 41 42 enum mode { 43 PAST, 44 EXPIRE, 45 NOEXPIRE, 46 }; 47 48 static void 49 timer_signal_handler(int signo, siginfo_t *si, void *osi __unused) 50 { 51 const int errno_save = errno; 52 timer_t *tp; 53 54 tp = si->si_value.sival_ptr; 55 56 if (*tp == t && signo == SIGALRM) 57 expired = 1; 58 59 (void)fprintf(stderr, "%s: %s\n", __func__, strsignal(signo)); 60 errno = errno_save; 61 } 62 63 static void 64 timer_signal_create(clockid_t cid, enum mode mode, int flags) 65 { 66 struct itimerspec tim, rtim, otim; 67 struct timespec t0, t1, dt; 68 struct sigaction act; 69 struct sigevent evt; 70 sigset_t set; 71 72 t = 0; 73 expired = 0; 74 75 (void)memset(&evt, 0, sizeof(struct sigevent)); 76 (void)memset(&act, 0, sizeof(struct sigaction)); 77 (void)memset(&tim, 0, sizeof(struct itimerspec)); 78 79 /* 80 * Set handler. 81 */ 82 act.sa_flags = SA_SIGINFO; 83 act.sa_sigaction = timer_signal_handler; 84 85 ATF_REQUIRE(sigemptyset(&set) == 0); 86 ATF_REQUIRE(sigemptyset(&act.sa_mask) == 0); 87 88 /* 89 * Block SIGALRM while configuring the timer. 90 */ 91 ATF_REQUIRE(sigaction(SIGALRM, &act, NULL) == 0); 92 ATF_REQUIRE(sigaddset(&set, SIGALRM) == 0); 93 ATF_REQUIRE(sigprocmask(SIG_SETMASK, &set, NULL) == 0); 94 95 /* 96 * Create the timer (SIGEV_SIGNAL). 97 */ 98 evt.sigev_signo = SIGALRM; 99 evt.sigev_value.sival_ptr = &t; 100 evt.sigev_notify = SIGEV_SIGNAL; 101 102 ATF_REQUIRE(timer_create(cid, &evt, &t) == 0); 103 104 /* 105 * Configure the timer for -1, 1, or 5 sec from now, depending 106 * on whether we want it to have fired, to fire within 2sec, or 107 * to not fire within 2sec. 108 */ 109 switch (mode) { 110 case PAST: 111 ATF_REQUIRE(flags & TIMER_ABSTIME); 112 tim.it_value.tv_sec = -1; 113 break; 114 case EXPIRE: 115 tim.it_value.tv_sec = 1; 116 break; 117 case NOEXPIRE: 118 tim.it_value.tv_sec = 5; 119 break; 120 } 121 tim.it_value.tv_nsec = 0; 122 123 /* 124 * Save the relative time and adjust for absolute time of 125 * requested. 126 */ 127 rtim = tim; 128 RL(clock_gettime(cid, &t0)); 129 if (flags & TIMER_ABSTIME) 130 timespecadd(&t0, &tim.it_value, &tim.it_value); 131 132 fprintf(stderr, "now is %lld sec %d nsec\n", 133 (long long)t0.tv_sec, (int)t0.tv_nsec); 134 fprintf(stderr, "expire at %lld sec %d nsec\n", 135 (long long)tim.it_value.tv_sec, (int)tim.it_value.tv_nsec); 136 RL(timer_settime(t, flags, &tim, NULL)); 137 RL(timer_settime(t, flags, &tim, &otim)); 138 139 RL(clock_gettime(cid, &t1)); 140 timespecsub(&t1, &t0, &dt); 141 fprintf(stderr, "%lld sec %d nsec elapsed\n", 142 (long long)dt.tv_sec, (int)dt.tv_nsec); 143 144 /* 145 * Check to make sure the time remaining is at most the 146 * relative time we expected -- plus slop of up to 2sec, 147 * because timer_settime rounds the duration up to a multiple 148 * of a tick period, which is at most 1sec (usually more like 149 * 10ms or 1ms, and in the future with high-resolution timers 150 * it'll be more like clock_getres(cid), but we don't have a 151 * way to get this bound right now), and if we ask for a wakeup 152 * (say) 0.9tick at a time 0.8tick before the next tick, the 153 * next tick is too early so we have to wait two ticks. 154 * 155 * The main point of this is to make sure that we're not 156 * getting absolute time by mistake (PR 58917) so the slop of 157 * 2sec is fine. 158 * 159 * Parentheses are required around the argument 160 * 161 * &(const struct timespec){2, 0} 162 * 163 * to timespecadd because it's a macro and the brilliant C 164 * preprocessor splits arguments at a comma if they're not 165 * parenthesized. 166 */ 167 if (flags & TIMER_ABSTIME) { 168 timespecadd(&rtim.it_value, (&(const struct timespec){2, 0}), 169 &rtim.it_value); 170 } 171 ATF_CHECK_MSG(timespeccmp(&otim.it_value, &rtim.it_value, <=), 172 "time remaining %lld sec %d nsec," 173 " expected at most %lld sec %d nsec", 174 (long long)otim.it_value.tv_sec, (int)otim.it_value.tv_nsec, 175 (long long)rtim.it_value.tv_sec, (int)rtim.it_value.tv_nsec); 176 177 #if 0 178 /* 179 * Check to make sure that the amount the time remaining has 180 * gone down is at most the time elapsed. 181 * 182 * XXX Currently the time returned by timer_settime is only 183 * good to the nearest kernel tick (typically 10ms or 1ms), not 184 * to the resolution of the underlying clock -- unlike 185 * clock_gettime. So we can't set this bound. Not sure 186 * whether this is a bug or not, hence #if 0 instead of 187 * atf_tc_expect_fail. 188 */ 189 timespecsub(&t1, &t0, &dt); 190 timespecsub(&rtim.it_value, &otim.it_value, &rtim.it_value); 191 ATF_CHECK_MSG(timespeccmp(&rtim.it_value, &dt, <=), 192 "time remaining went down by %lld sec %d nsec," 193 " expected at most %lld sec %d nsec", 194 (long long)rtim.it_value.tv_sec, (int)rtim.it_value.tv_nsec, 195 (long long)dt.tv_sec, (int)dt.tv_nsec); 196 #endif 197 198 /* 199 * Check to make sure the reload interval is what we set. 200 */ 201 ATF_CHECK_MSG(timespeccmp(&otim.it_interval, &rtim.it_interval, ==), 202 "interval %lld sec %d nsec," 203 " expected %lld sec %d nsec", 204 (long long)otim.it_interval.tv_sec, (int)otim.it_interval.tv_nsec, 205 (long long)rtim.it_interval.tv_sec, (int)rtim.it_interval.tv_nsec); 206 207 (void)sigprocmask(SIG_UNBLOCK, &set, NULL); 208 switch (mode) { 209 case PAST: 210 /* 211 * Wait for at least one tick to pass. 212 * 213 * XXX This does not really follow POSIX, which says 214 * `If the specified time has already passed, the 215 * function shall succeed and the expiration 216 * notification shall be made.' 217 * (https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/timer_settime.html), 218 * suggesting that it should be immediate without any 219 * further delay, but other operating systems also 220 * sometimes have a small delay. 221 */ 222 RL(clock_nanosleep(cid, 0, &(const struct timespec){0, 1}, 223 NULL)); 224 ATF_CHECK_MSG(expired, "timer failed to fire immediately"); 225 break; 226 case EXPIRE: 227 case NOEXPIRE: 228 ATF_CHECK_MSG(!expired, "timer fired too soon"); 229 (void)sleep(2); 230 switch (mode) { 231 case PAST: 232 __unreachable(); 233 case EXPIRE: 234 ATF_CHECK_MSG(expired, 235 "timer failed to fire immediately"); 236 break; 237 case NOEXPIRE: 238 ATF_CHECK_MSG(!expired, "timer fired too soon"); 239 break; 240 } 241 break; 242 } 243 244 ATF_REQUIRE(timer_delete(t) == 0); 245 } 246 247 ATF_TC(timer_create_err); 248 ATF_TC_HEAD(timer_create_err, tc) 249 { 250 atf_tc_set_md_var(tc, "descr", 251 "Check errors from timer_create(2) (PR lib/42434)"); 252 } 253 254 ATF_TC_BODY(timer_create_err, tc) 255 { 256 struct sigevent ev; 257 258 (void)memset(&ev, 0, sizeof(struct sigevent)); 259 260 errno = 0; 261 ev.sigev_signo = -1; 262 ev.sigev_notify = SIGEV_SIGNAL; 263 264 ATF_REQUIRE_ERRNO(EINVAL, timer_create(CLOCK_REALTIME, &ev, &t) == -1); 265 266 errno = 0; 267 ev.sigev_signo = SIGUSR1; 268 ev.sigev_notify = SIGEV_THREAD + 100; 269 270 ATF_REQUIRE_ERRNO(EINVAL, timer_create(CLOCK_REALTIME, &ev, &t) == -1); 271 } 272 273 ATF_TC(timer_create_real); 274 ATF_TC_HEAD(timer_create_real, tc) 275 { 276 277 atf_tc_set_md_var(tc, "descr", 278 "Checks timer_create(2) with CLOCK_REALTIME and sigevent(3), " 279 "SIGEV_SIGNAL"); 280 } 281 282 ATF_TC_BODY(timer_create_real, tc) 283 { 284 timer_signal_create(CLOCK_REALTIME, NOEXPIRE, 0); 285 } 286 287 ATF_TC(timer_create_real_abs); 288 ATF_TC_HEAD(timer_create_real_abs, tc) 289 { 290 291 atf_tc_set_md_var(tc, "descr", 292 "Checks timer_create(2) with CLOCK_REALTIME and sigevent(3), " 293 "SIGEV_SIGNAL, using absolute time"); 294 } 295 296 ATF_TC_BODY(timer_create_real_abs, tc) 297 { 298 timer_signal_create(CLOCK_REALTIME, NOEXPIRE, TIMER_ABSTIME); 299 } 300 301 ATF_TC(timer_create_mono); 302 ATF_TC_HEAD(timer_create_mono, tc) 303 { 304 305 atf_tc_set_md_var(tc, "descr", 306 "Checks timer_create(2) with CLOCK_MONOTONIC and sigevent(3), " 307 "SIGEV_SIGNAL"); 308 } 309 310 ATF_TC_BODY(timer_create_mono, tc) 311 { 312 timer_signal_create(CLOCK_MONOTONIC, NOEXPIRE, 0); 313 } 314 315 ATF_TC(timer_create_mono_abs); 316 ATF_TC_HEAD(timer_create_mono_abs, tc) 317 { 318 319 atf_tc_set_md_var(tc, "descr", 320 "Checks timer_create(2) with CLOCK_MONOTONIC and sigevent(3), " 321 "SIGEV_SIGNAL, using absolute time"); 322 } 323 324 ATF_TC_BODY(timer_create_mono_abs, tc) 325 { 326 timer_signal_create(CLOCK_MONOTONIC, NOEXPIRE, TIMER_ABSTIME); 327 } 328 329 ATF_TC(timer_create_real_expire); 330 ATF_TC_HEAD(timer_create_real_expire, tc) 331 { 332 333 atf_tc_set_md_var(tc, "descr", 334 "Checks timer_create(2) with CLOCK_REALTIME and sigevent(3), " 335 "SIGEV_SIGNAL, with expiration"); 336 } 337 338 ATF_TC_BODY(timer_create_real_expire, tc) 339 { 340 timer_signal_create(CLOCK_REALTIME, EXPIRE, 0); 341 } 342 343 ATF_TC(timer_create_real_expire_abs); 344 ATF_TC_HEAD(timer_create_real_expire_abs, tc) 345 { 346 347 atf_tc_set_md_var(tc, "descr", 348 "Checks timer_create(2) with CLOCK_REALTIME and sigevent(3), " 349 "SIGEV_SIGNAL, with expiration, using absolute time"); 350 } 351 352 ATF_TC_BODY(timer_create_real_expire_abs, tc) 353 { 354 timer_signal_create(CLOCK_REALTIME, EXPIRE, TIMER_ABSTIME); 355 } 356 357 ATF_TC(timer_create_mono_expire); 358 ATF_TC_HEAD(timer_create_mono_expire, tc) 359 { 360 361 atf_tc_set_md_var(tc, "descr", 362 "Checks timer_create(2) with CLOCK_MONOTONIC and sigevent(3), " 363 "SIGEV_SIGNAL, with expiration"); 364 } 365 366 ATF_TC_BODY(timer_create_mono_expire, tc) 367 { 368 timer_signal_create(CLOCK_MONOTONIC, EXPIRE, 0); 369 } 370 371 ATF_TC(timer_create_mono_expire_abs); 372 ATF_TC_HEAD(timer_create_mono_expire_abs, tc) 373 { 374 375 atf_tc_set_md_var(tc, "descr", 376 "Checks timer_create(2) with CLOCK_MONOTONIC and sigevent(3), " 377 "SIGEV_SIGNAL, with expiration, using absolute time"); 378 } 379 380 ATF_TC_BODY(timer_create_mono_expire_abs, tc) 381 { 382 timer_signal_create(CLOCK_MONOTONIC, EXPIRE, TIMER_ABSTIME); 383 } 384 385 ATF_TC(timer_create_real_past_abs); 386 ATF_TC_HEAD(timer_create_real_past_abs, tc) 387 { 388 389 atf_tc_set_md_var(tc, "descr", 390 "Checks timer_create(2) with CLOCK_REALTIME and sigevent(3), " 391 "SIGEV_SIGNAL, with expiration passed before timer_settime(2)," 392 " using absolute time"); 393 } 394 395 ATF_TC_BODY(timer_create_real_past_abs, tc) 396 { 397 timer_signal_create(CLOCK_REALTIME, PAST, TIMER_ABSTIME); 398 } 399 400 ATF_TC(timer_create_mono_past_abs); 401 ATF_TC_HEAD(timer_create_mono_past_abs, tc) 402 { 403 404 atf_tc_set_md_var(tc, "descr", 405 "Checks timer_create(2) with CLOCK_MONOTONIC and sigevent(3), " 406 "SIGEV_SIGNAL, with expiration passed before timer_settime(2)," 407 " using absolute time"); 408 } 409 410 ATF_TC_BODY(timer_create_mono_past_abs, tc) 411 { 412 timer_signal_create(CLOCK_MONOTONIC, PAST, TIMER_ABSTIME); 413 } 414 415 ATF_TC(timer_invalidtime); 416 ATF_TC_HEAD(timer_invalidtime, tc) 417 { 418 atf_tc_set_md_var(tc, "descr", 419 "Verify timer_settime(2) rejects invalid times"); 420 } 421 422 ATF_TC_BODY(timer_invalidtime, tc) 423 { 424 const struct itimerspec einval_its[] = { 425 [0] = { .it_value = {-1, 0} }, 426 [1] = { .it_value = {0, -1} }, 427 [2] = { .it_value = {0, 1000000001} }, 428 [3] = { .it_value = {1, 0}, .it_interval = {-1, 0} }, 429 [4] = { .it_value = {1, 0}, .it_interval = {0, -1} }, 430 [5] = { .it_value = {1, 0}, .it_interval = {0, 1000000001} }, 431 }; 432 struct timespec now; 433 sigset_t sigs, mask; 434 unsigned i; 435 436 RL(sigemptyset(&sigs)); 437 RL(sigaddset(&sigs, SIGALRM)); 438 RL(sigprocmask(SIG_BLOCK, &sigs, &mask)); 439 440 RL(clock_gettime(CLOCK_MONOTONIC, &now)); 441 442 RL(timer_create(CLOCK_MONOTONIC, NULL, &t)); 443 444 for (i = 0; i < __arraycount(einval_its); i++) { 445 struct itimerspec its; 446 447 fprintf(stderr, "case %u\n", i); 448 449 ATF_CHECK_ERRNO(EINVAL, 450 timer_settime(t, 0, &einval_its[i], NULL) == -1); 451 452 /* Try the same with an absolute time near now. */ 453 its.it_value = einval_its[i].it_value; 454 its.it_value.tv_sec += now.tv_sec + 60; 455 ATF_CHECK_ERRNO(EINVAL, 456 timer_settime(t, TIMER_ABSTIME, &its, NULL) == -1); 457 } 458 459 /* Wait up to 2sec to make sure no timer got set anyway. */ 460 ATF_CHECK_ERRNO(EAGAIN, 461 sigtimedwait(&sigs, NULL, &(const struct timespec){2, 0}) == -1); 462 RL(sigprocmask(SIG_SETMASK, &mask, NULL)); 463 464 RL(timer_delete(t)); 465 } 466 467 ATF_TP_ADD_TCS(tp) 468 { 469 470 ATF_TP_ADD_TC(tp, timer_create_err); 471 ATF_TP_ADD_TC(tp, timer_create_real); 472 ATF_TP_ADD_TC(tp, timer_create_real_abs); 473 ATF_TP_ADD_TC(tp, timer_create_mono); 474 ATF_TP_ADD_TC(tp, timer_create_mono_abs); 475 ATF_TP_ADD_TC(tp, timer_create_real_expire); 476 ATF_TP_ADD_TC(tp, timer_create_real_expire_abs); 477 ATF_TP_ADD_TC(tp, timer_create_mono_expire); 478 ATF_TP_ADD_TC(tp, timer_create_mono_expire_abs); 479 ATF_TP_ADD_TC(tp, timer_create_real_past_abs); 480 ATF_TP_ADD_TC(tp, timer_create_mono_past_abs); 481 ATF_TP_ADD_TC(tp, timer_invalidtime); 482 483 return atf_no_error(); 484 } 485