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);