Module Name: src Committed By: riastradh Date: Thu Dec 19 20:07:29 UTC 2024
Modified Files: src/tests/lib/libc/sys: t_timer_create.c Log Message: t_timer_create: Fix up tests for edge cases. While here, save and restore errno in signal handler. PR kern/58919: timer_settime fails to trigger for past times PR kern/58920: timer_settime fails ETIMEDOUT on negative interval, not EINVAL To generate a diff of this commit: cvs rdiff -u -r1.6 -r1.7 src/tests/lib/libc/sys/t_timer_create.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/tests/lib/libc/sys/t_timer_create.c diff -u src/tests/lib/libc/sys/t_timer_create.c:1.6 src/tests/lib/libc/sys/t_timer_create.c:1.7 --- src/tests/lib/libc/sys/t_timer_create.c:1.6 Wed Dec 18 22:26:53 2024 +++ src/tests/lib/libc/sys/t_timer_create.c Thu Dec 19 20:07:29 2024 @@ -1,4 +1,4 @@ -/* $NetBSD: t_timer_create.c,v 1.6 2024/12/18 22:26:53 riastradh Exp $ */ +/* $NetBSD: t_timer_create.c,v 1.7 2024/12/19 20:07:29 riastradh Exp $ */ /*- * Copyright (c) 2010 The NetBSD Foundation, Inc. @@ -48,6 +48,7 @@ enum mode { static void timer_signal_handler(int signo, siginfo_t *si, void *osi __unused) { + const int errno_save = errno; timer_t *tp; tp = si->si_value.sival_ptr; @@ -56,6 +57,7 @@ timer_signal_handler(int signo, siginfo_ expired = 1; (void)fprintf(stderr, "%s: %s\n", __func__, strsignal(signo)); + errno = errno_save; } static void @@ -106,6 +108,7 @@ timer_signal_create(clockid_t cid, enum */ switch (mode) { case PAST: + ATF_REQUIRE(flags & TIMER_ABSTIME); tim.it_value.tv_sec = -1; break; case EXPIRE: @@ -130,15 +133,8 @@ timer_signal_create(clockid_t cid, enum (long long)t0.tv_sec, (int)t0.tv_nsec); fprintf(stderr, "expire at %lld sec %d nsec\n", (long long)tim.it_value.tv_sec, (int)tim.it_value.tv_nsec); - if (mode == PAST && (flags & TIMER_ABSTIME) == 0) { - atf_tc_expect_fail("PR kern/58919:" - " timer_settime fails to trigger for past times"); - } RL(timer_settime(t, flags, &tim, NULL)); RL(timer_settime(t, flags, &tim, &otim)); - if (mode == PAST && (flags & TIMER_ABSTIME) == 0) { - atf_tc_expect_pass(); - } RL(clock_gettime(cid, &t1)); timespecsub(&t1, &t0, &dt); @@ -147,8 +143,31 @@ timer_signal_create(clockid_t cid, enum /* * Check to make sure the time remaining is at most the - * relative time we expected. + * relative time we expected -- plus slop of up to 2sec, + * because timer_settime rounds the duration up to a multiple + * of a tick period, which is at most 1sec (usually more like + * 10ms or 1ms, and in the future with high-resolution timers + * it'll be more like clock_getres(cid), but we don't have a + * way to get this bound right now), and if we ask for a wakeup + * (say) 0.9tick at a time 0.8tick before the next tick, the + * next tick is too early so we have to wait two ticks. + * + * The main point of this is to make sure that we're not + * getting absolute time by mistake (PR 58917) so the slop of + * 2sec is fine. + * + * Parentheses are required around the argument + * + * &(const struct timespec){2, 0} + * + * to timespecadd because it's a macro and the brilliant C + * preprocessor splits arguments at a comma if they're not + * parenthesized. */ + if (flags & TIMER_ABSTIME) { + timespecadd(&rtim.it_value, (&(const struct timespec){2, 0}), + &rtim.it_value); + } atf_tc_expect_fail("PR kern/58917:" " timer_settime and timerfd_settime return" " absolute time of next event"); @@ -200,14 +219,21 @@ timer_signal_create(clockid_t cid, enum (void)sigprocmask(SIG_UNBLOCK, &set, NULL); switch (mode) { case PAST: - if (flags & TIMER_ABSTIME) { - atf_tc_expect_fail("PR kern/58919:" - " timer_settime fails to trigger for past times"); - } + /* + * Wait for at least one tick to pass. + * + * XXX This does not really follow POSIX, which says + * `If the specified time has already passed, the + * function shall succeed and the expiration + * notification shall be made.' + * (https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/timer_settime.html), + * suggesting that it should be immediate without any + * further delay, but other operating systems also + * sometimes have a small delay. + */ + RL(clock_nanosleep(cid, 0, &(const struct timespec){0, 1}, + NULL)); ATF_CHECK_MSG(expired, "timer failed to fire immediately"); - if (flags & TIMER_ABSTIME) { - atf_tc_expect_pass(); - } break; case EXPIRE: case NOEXPIRE: @@ -368,20 +394,6 @@ ATF_TC_BODY(timer_create_mono_expire_abs timer_signal_create(CLOCK_MONOTONIC, EXPIRE, TIMER_ABSTIME); } -ATF_TC(timer_create_real_past); -ATF_TC_HEAD(timer_create_real_past, tc) -{ - - atf_tc_set_md_var(tc, "descr", - "Checks timer_create(2) with CLOCK_REALTIME and sigevent(3), " - "SIGEV_SIGNAL, with expiration passed before timer_settime(2)"); -} - -ATF_TC_BODY(timer_create_real_past, tc) -{ - timer_signal_create(CLOCK_REALTIME, PAST, 0); -} - ATF_TC(timer_create_real_past_abs); ATF_TC_HEAD(timer_create_real_past_abs, tc) { @@ -397,20 +409,6 @@ ATF_TC_BODY(timer_create_real_past_abs, timer_signal_create(CLOCK_REALTIME, PAST, TIMER_ABSTIME); } -ATF_TC(timer_create_mono_past); -ATF_TC_HEAD(timer_create_mono_past, tc) -{ - - atf_tc_set_md_var(tc, "descr", - "Checks timer_create(2) with CLOCK_MONOTONIC and sigevent(3), " - "SIGEV_SIGNAL, with expiration passed before timer_settime(2)"); -} - -ATF_TC_BODY(timer_create_mono_past, tc) -{ - timer_signal_create(CLOCK_MONOTONIC, PAST, 0); -} - ATF_TC(timer_create_mono_past_abs); ATF_TC_HEAD(timer_create_mono_past_abs, tc) { @@ -436,16 +434,21 @@ ATF_TC_HEAD(timer_invalidtime, tc) ATF_TC_BODY(timer_invalidtime, tc) { const struct itimerspec einval_its[] = { - [0] = { .it_value = { -1, -1 } }, - [1] = { .it_value = { 1, -1 } }, - [2] = { .it_value = { 0, 1000000001 } }, - [3] = { .it_interval = { -1, -1 } }, - [4] = { .it_interval = { 1, -1 } }, - [5] = { .it_interval = { 0, 1000000001 } }, + [0] = { .it_value = {-1, 0} }, + [1] = { .it_value = {0, -1} }, + [2] = { .it_value = {0, 1000000001} }, + [3] = { .it_value = {1, 0}, .it_interval = {-1, 0} }, + [4] = { .it_value = {1, 0}, .it_interval = {0, -1} }, + [5] = { .it_value = {1, 0}, .it_interval = {0, 1000000001} }, }; struct timespec now; + sigset_t sigs, mask; unsigned i; + RL(sigemptyset(&sigs)); + RL(sigaddset(&sigs, SIGALRM)); + RL(sigprocmask(SIG_BLOCK, &sigs, &mask)); + RL(clock_gettime(CLOCK_MONOTONIC, &now)); RL(timer_create(CLOCK_MONOTONIC, NULL, &t)); @@ -453,6 +456,18 @@ ATF_TC_BODY(timer_invalidtime, tc) for (i = 0; i < __arraycount(einval_its); i++) { struct itimerspec its; + fprintf(stderr, "case %u\n", i); + + if (einval_its[i].it_value.tv_sec < 0 || + einval_its[i].it_interval.tv_sec < 0) { + /* + * This returns ETIMEDOUT instead of EINVAL. + */ + atf_tc_expect_fail("PR kern/58920:" + " timer_settime fails ETIMEDOUT" + " on negative interval, not EINVAL"); + } + ATF_CHECK_ERRNO(EINVAL, timer_settime(t, 0, &einval_its[i], NULL) == -1); @@ -461,8 +476,18 @@ ATF_TC_BODY(timer_invalidtime, tc) its.it_value.tv_sec += now.tv_sec + 60; ATF_CHECK_ERRNO(EINVAL, timer_settime(t, TIMER_ABSTIME, &its, NULL) == -1); + + if (einval_its[i].it_value.tv_sec < 0 || + einval_its[i].it_interval.tv_sec < 0) { + atf_tc_expect_pass(); + } } + /* Wait up to 2sec to make sure no timer got set anyway. */ + ATF_CHECK_ERRNO(EAGAIN, + sigtimedwait(&sigs, NULL, &(const struct timespec){2, 0}) == -1); + RL(sigprocmask(SIG_SETMASK, &mask, NULL)); + RL(timer_delete(t)); } @@ -478,9 +503,7 @@ ATF_TP_ADD_TCS(tp) ATF_TP_ADD_TC(tp, timer_create_real_expire_abs); ATF_TP_ADD_TC(tp, timer_create_mono_expire); ATF_TP_ADD_TC(tp, timer_create_mono_expire_abs); - ATF_TP_ADD_TC(tp, timer_create_real_past); ATF_TP_ADD_TC(tp, timer_create_real_past_abs); - ATF_TP_ADD_TC(tp, timer_create_mono_past); ATF_TP_ADD_TC(tp, timer_create_mono_past_abs); ATF_TP_ADD_TC(tp, timer_invalidtime);