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