Module Name:    src
Committed By:   riastradh
Date:           Wed Dec 18 22:26:53 UTC 2024

Modified Files:
        src/tests/lib/libc/sys: t_timer_create.c

Log Message:
t_timer_create: Add some more test cases.

PR kern/58917: timer_settime and timerfd_settime return absolute time
of next event

PR kern/58919: timer_settime fails to trigger for past times


To generate a diff of this commit:
cvs rdiff -u -r1.5 -r1.6 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.5 src/tests/lib/libc/sys/t_timer_create.c:1.6
--- src/tests/lib/libc/sys/t_timer_create.c:1.5	Mon Jan 16 16:32:13 2017
+++ src/tests/lib/libc/sys/t_timer_create.c	Wed Dec 18 22:26:53 2024
@@ -1,4 +1,4 @@
-/*	$NetBSD: t_timer_create.c,v 1.5 2017/01/16 16:32:13 christos Exp $ */
+/*	$NetBSD: t_timer_create.c,v 1.6 2024/12/18 22:26:53 riastradh Exp $ */
 
 /*-
  * Copyright (c) 2010 The NetBSD Foundation, Inc.
@@ -34,8 +34,16 @@
 #include <time.h>
 #include <unistd.h>
 
+#include "h_macros.h"
+
 static timer_t t;
-static bool fail = true;
+static sig_atomic_t expired;
+
+enum mode {
+	PAST,
+	EXPIRE,
+	NOEXPIRE,
+};
 
 static void
 timer_signal_handler(int signo, siginfo_t *si, void *osi __unused)
@@ -45,21 +53,22 @@ timer_signal_handler(int signo, siginfo_
 	tp = si->si_value.sival_ptr;
 
 	if (*tp == t && signo == SIGALRM)
-		fail = false;
+		expired = 1;
 
 	(void)fprintf(stderr, "%s: %s\n", __func__, strsignal(signo));
 }
 
 static void
-timer_signal_create(clockid_t cid, bool expire)
+timer_signal_create(clockid_t cid, enum mode mode, int flags)
 {
-	struct itimerspec tim;
+	struct itimerspec tim, rtim, otim;
+	struct timespec t0, t1, dt;
 	struct sigaction act;
 	struct sigevent evt;
 	sigset_t set;
 
 	t = 0;
-	fail = true;
+	expired = 0;
 
 	(void)memset(&evt, 0, sizeof(struct sigevent));
 	(void)memset(&act, 0, sizeof(struct sigaction));
@@ -91,22 +100,131 @@ timer_signal_create(clockid_t cid, bool 
 	ATF_REQUIRE(timer_create(cid, &evt, &t) == 0);
 
 	/*
-	 * Start the timer. After this, unblock the signal.
+	 * Configure the timer for -1, 1, or 5 sec from now, depending
+	 * on whether we want it to have fired, to fire within 2sec, or
+	 * to not fire within 2sec.
 	 */
-	tim.it_value.tv_sec = expire ? 5 : 1;
+	switch (mode) {
+	case PAST:
+		tim.it_value.tv_sec = -1;
+		break;
+	case EXPIRE:
+		tim.it_value.tv_sec = 1;
+		break;
+	case NOEXPIRE:
+		tim.it_value.tv_sec = 5;
+		break;
+	}
 	tim.it_value.tv_nsec = 0;
 
-	ATF_REQUIRE(timer_settime(t, 0, &tim, NULL) == 0);
+	/*
+	 * Save the relative time and adjust for absolute time of
+	 * requested.
+	 */
+	rtim = tim;
+	RL(clock_gettime(cid, &t0));
+	if (flags & TIMER_ABSTIME)
+		timespecadd(&t0, &tim.it_value, &tim.it_value);
+
+	fprintf(stderr, "now is %lld sec %d nsec\n",
+	    (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);
+	fprintf(stderr, "%lld sec %d nsec elapsed\n",
+	    (long long)dt.tv_sec, (int)dt.tv_nsec);
 
-	(void)sigprocmask(SIG_UNBLOCK, &set, NULL);
-	(void)sleep(2);
+	/*
+	 * Check to make sure the time remaining is at most the
+	 * relative time we expected.
+	 */
+	atf_tc_expect_fail("PR kern/58917:"
+	    " timer_settime and timerfd_settime return"
+	    " absolute time of next event");
+	ATF_CHECK_MSG(timespeccmp(&otim.it_value, &rtim.it_value, <=),
+	    "time remaining %lld sec %d nsec,"
+	    " expected at most %lld sec %d nsec",
+	    (long long)otim.it_value.tv_sec, (int)otim.it_value.tv_nsec,
+	    (long long)rtim.it_value.tv_sec, (int)rtim.it_value.tv_nsec);
+	atf_tc_expect_pass();
+
+	/*
+	 * Until we fix PR kern/58917, adjust it to be relative to the
+	 * start time.
+	 */
+	timespecsub(&otim.it_value, &t0, &otim.it_value);
+	fprintf(stderr, "adjust otim to %lld sec %d nsec\n",
+	    (long long)otim.it_value.tv_sec, (int)otim.it_value.tv_nsec);
 
-	if (expire) {
-		if (!fail)
-			atf_tc_fail("timer fired too soon");
-	} else {
-		if (fail)
-			atf_tc_fail("timer failed to fire");
+#if 0
+	/*
+	 * Check to make sure that the amount the time remaining has
+	 * gone down is at most the time elapsed.
+	 *
+	 * XXX Currently the time returned by timer_settime is only
+	 * good to the nearest kernel tick (typically 10ms or 1ms), not
+	 * to the resolution of the underlying clock -- unlike
+	 * clock_gettime.  So we can't set this bound.  Not sure
+	 * whether this is a bug or not, hence #if 0 instead of
+	 * atf_tc_expect_fail.
+	 */
+	timespecsub(&t1, &t0, &dt);
+	timespecsub(&rtim.it_value, &otim.it_value, &rtim.it_value);
+	ATF_CHECK_MSG(timespeccmp(&rtim.it_value, &dt, <=),
+	    "time remaining went down by %lld sec %d nsec,"
+	    " expected at most %lld sec %d nsec",
+	    (long long)rtim.it_value.tv_sec, (int)rtim.it_value.tv_nsec,
+	    (long long)dt.tv_sec, (int)dt.tv_nsec);
+#endif
+
+	/*
+	 * Check to make sure the reload interval is what we set.
+	 */
+	ATF_CHECK_MSG(timespeccmp(&otim.it_interval, &rtim.it_interval, ==),
+	    "interval %lld sec %d nsec,"
+	    " expected %lld sec %d nsec",
+	    (long long)otim.it_interval.tv_sec, (int)otim.it_interval.tv_nsec,
+	    (long long)rtim.it_interval.tv_sec, (int)rtim.it_interval.tv_nsec);
+
+	(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");
+		}
+		ATF_CHECK_MSG(expired, "timer failed to fire immediately");
+		if (flags & TIMER_ABSTIME) {
+			atf_tc_expect_pass();
+		}
+		break;
+	case EXPIRE:
+	case NOEXPIRE:
+		ATF_CHECK_MSG(!expired, "timer fired too soon");
+		(void)sleep(2);
+		switch (mode) {
+		case PAST:
+			__unreachable();
+		case EXPIRE:
+			ATF_CHECK_MSG(expired,
+			    "timer failed to fire immediately");
+			break;
+		case NOEXPIRE:
+			ATF_CHECK_MSG(!expired, "timer fired too soon");
+			break;
+		}
+		break;
 	}
 
 	ATF_REQUIRE(timer_delete(t) == 0);
@@ -116,7 +234,7 @@ ATF_TC(timer_create_err);
 ATF_TC_HEAD(timer_create_err, tc)
 {
 	atf_tc_set_md_var(tc, "descr",
-	    "Check errors from timer_create(2) (PR lib/42434");
+	    "Check errors from timer_create(2) (PR lib/42434)");
 }
 
 ATF_TC_BODY(timer_create_err, tc)
@@ -149,7 +267,21 @@ ATF_TC_HEAD(timer_create_real, tc)
 
 ATF_TC_BODY(timer_create_real, tc)
 {
-	timer_signal_create(CLOCK_REALTIME, false);
+	timer_signal_create(CLOCK_REALTIME, NOEXPIRE, 0);
+}
+
+ATF_TC(timer_create_real_abs);
+ATF_TC_HEAD(timer_create_real_abs, tc)
+{
+
+	atf_tc_set_md_var(tc, "descr",
+	    "Checks timer_create(2) with CLOCK_REALTIME and sigevent(3), "
+	    "SIGEV_SIGNAL, using absolute time");
+}
+
+ATF_TC_BODY(timer_create_real_abs, tc)
+{
+	timer_signal_create(CLOCK_REALTIME, NOEXPIRE, TIMER_ABSTIME);
 }
 
 ATF_TC(timer_create_mono);
@@ -163,7 +295,21 @@ ATF_TC_HEAD(timer_create_mono, tc)
 
 ATF_TC_BODY(timer_create_mono, tc)
 {
-	timer_signal_create(CLOCK_MONOTONIC, false);
+	timer_signal_create(CLOCK_MONOTONIC, NOEXPIRE, 0);
+}
+
+ATF_TC(timer_create_mono_abs);
+ATF_TC_HEAD(timer_create_mono_abs, tc)
+{
+
+	atf_tc_set_md_var(tc, "descr",
+	    "Checks timer_create(2) with CLOCK_MONOTONIC and sigevent(3), "
+	    "SIGEV_SIGNAL, using absolute time");
+}
+
+ATF_TC_BODY(timer_create_mono_abs, tc)
+{
+	timer_signal_create(CLOCK_MONOTONIC, NOEXPIRE, TIMER_ABSTIME);
 }
 
 ATF_TC(timer_create_real_expire);
@@ -177,7 +323,21 @@ ATF_TC_HEAD(timer_create_real_expire, tc
 
 ATF_TC_BODY(timer_create_real_expire, tc)
 {
-	timer_signal_create(CLOCK_REALTIME, true);
+	timer_signal_create(CLOCK_REALTIME, EXPIRE, 0);
+}
+
+ATF_TC(timer_create_real_expire_abs);
+ATF_TC_HEAD(timer_create_real_expire_abs, tc)
+{
+
+	atf_tc_set_md_var(tc, "descr",
+	    "Checks timer_create(2) with CLOCK_REALTIME and sigevent(3), "
+	    "SIGEV_SIGNAL, with expiration, using absolute time");
+}
+
+ATF_TC_BODY(timer_create_real_expire_abs, tc)
+{
+	timer_signal_create(CLOCK_REALTIME, EXPIRE, TIMER_ABSTIME);
 }
 
 ATF_TC(timer_create_mono_expire);
@@ -191,7 +351,119 @@ ATF_TC_HEAD(timer_create_mono_expire, tc
 
 ATF_TC_BODY(timer_create_mono_expire, tc)
 {
-	timer_signal_create(CLOCK_MONOTONIC, true);
+	timer_signal_create(CLOCK_MONOTONIC, EXPIRE, 0);
+}
+
+ATF_TC(timer_create_mono_expire_abs);
+ATF_TC_HEAD(timer_create_mono_expire_abs, tc)
+{
+
+	atf_tc_set_md_var(tc, "descr",
+	    "Checks timer_create(2) with CLOCK_MONOTONIC and sigevent(3), "
+	    "SIGEV_SIGNAL, with expiration, using absolute time");
+}
+
+ATF_TC_BODY(timer_create_mono_expire_abs, tc)
+{
+	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)
+{
+
+	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),"
+	    " using absolute time");
+}
+
+ATF_TC_BODY(timer_create_real_past_abs, tc)
+{
+	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)
+{
+
+	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),"
+	    " using absolute time");
+}
+
+ATF_TC_BODY(timer_create_mono_past_abs, tc)
+{
+	timer_signal_create(CLOCK_MONOTONIC, PAST, TIMER_ABSTIME);
+}
+
+ATF_TC(timer_invalidtime);
+ATF_TC_HEAD(timer_invalidtime, tc)
+{
+	atf_tc_set_md_var(tc, "descr",
+	    "Verify timer_settime(2) rejects invalid times");
+}
+
+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 } },
+	};
+	struct timespec now;
+	unsigned i;
+
+	RL(clock_gettime(CLOCK_MONOTONIC, &now));
+
+	RL(timer_create(CLOCK_MONOTONIC, NULL, &t));
+
+	for (i = 0; i < __arraycount(einval_its); i++) {
+		struct itimerspec its;
+
+		ATF_CHECK_ERRNO(EINVAL,
+		    timer_settime(t, 0, &einval_its[i], NULL) == -1);
+
+		/* Try the same with an absolute time near now. */
+		its.it_value = einval_its[i].it_value;
+		its.it_value.tv_sec += now.tv_sec + 60;
+		ATF_CHECK_ERRNO(EINVAL,
+		    timer_settime(t, TIMER_ABSTIME, &its, NULL) == -1);
+	}
+
+	RL(timer_delete(t));
 }
 
 ATF_TP_ADD_TCS(tp)
@@ -199,9 +471,18 @@ ATF_TP_ADD_TCS(tp)
 
 	ATF_TP_ADD_TC(tp, timer_create_err);
 	ATF_TP_ADD_TC(tp, timer_create_real);
+	ATF_TP_ADD_TC(tp, timer_create_real_abs);
 	ATF_TP_ADD_TC(tp, timer_create_mono);
+	ATF_TP_ADD_TC(tp, timer_create_mono_abs);
 	ATF_TP_ADD_TC(tp, timer_create_real_expire);
+	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);
 
 	return atf_no_error();
 }

Reply via email to