Module Name:    src
Committed By:   thorpej
Date:           Wed Oct 13 04:57:20 UTC 2021

Modified Files:
        src/distrib/sets/lists/debug: mi
        src/distrib/sets/lists/tests: mi
        src/lib/libc/sys: kqueue.2
        src/sys/kern: kern_event.c
        src/sys/sys: event.h
        src/tests/kernel/kqueue: Makefile
Added Files:
        src/tests/kernel/kqueue: t_timer.c

Log Message:
Add support for the NOTE_SECONDS, NOTE_MSECONDS, NOTE_USECONDS,
NOTE_NSECONDS, and NOTE_ABSTIME filter flags to EVFILT_TIMER,
API-compatible with the same in FreeBSD.


To generate a diff of this commit:
cvs rdiff -u -r1.364 -r1.365 src/distrib/sets/lists/debug/mi
cvs rdiff -u -r1.1135 -r1.1136 src/distrib/sets/lists/tests/mi
cvs rdiff -u -r1.53 -r1.54 src/lib/libc/sys/kqueue.2
cvs rdiff -u -r1.131 -r1.132 src/sys/kern/kern_event.c
cvs rdiff -u -r1.47 -r1.48 src/sys/sys/event.h
cvs rdiff -u -r1.6 -r1.7 src/tests/kernel/kqueue/Makefile
cvs rdiff -u -r0 -r1.1 src/tests/kernel/kqueue/t_timer.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/distrib/sets/lists/debug/mi
diff -u src/distrib/sets/lists/debug/mi:1.364 src/distrib/sets/lists/debug/mi:1.365
--- src/distrib/sets/lists/debug/mi:1.364	Sun Oct 10 17:47:38 2021
+++ src/distrib/sets/lists/debug/mi	Wed Oct 13 04:57:19 2021
@@ -1,4 +1,4 @@
-# $NetBSD: mi,v 1.364 2021/10/10 17:47:38 thorpej Exp $
+# $NetBSD: mi,v 1.365 2021/10/13 04:57:19 thorpej Exp $
 ./etc/mtree/set.debug                           comp-sys-root
 ./usr/lib					comp-sys-usr		compatdir
 ./usr/lib/i18n/libBIG5_g.a			comp-c-debuglib		debuglib,compatfile
@@ -1767,6 +1767,7 @@
 ./usr/libdata/debug/usr/tests/kernel/kqueue/t_proc4.debug		tests-kernel-tests	debug,atf,compattestfile
 ./usr/libdata/debug/usr/tests/kernel/kqueue/t_scan.debug		tests-kernel-tests	debug,atf,compattestfile
 ./usr/libdata/debug/usr/tests/kernel/kqueue/t_sig.debug			tests-kernel-tests	debug,atf,compattestfile
+./usr/libdata/debug/usr/tests/kernel/kqueue/t_timer.debug		tests-kernel-tests	debug,atf,compattestfile
 ./usr/libdata/debug/usr/tests/kernel/kqueue/t_vnode.debug		tests-kernel-tests	debug,atf,compattestfile
 ./usr/libdata/debug/usr/tests/kernel/kqueue/write/t_fifo.debug		tests-kernel-tests	debug,atf,compattestfile
 ./usr/libdata/debug/usr/tests/kernel/kqueue/write/t_pipe.debug		tests-kernel-tests	debug,atf,compattestfile

Index: src/distrib/sets/lists/tests/mi
diff -u src/distrib/sets/lists/tests/mi:1.1135 src/distrib/sets/lists/tests/mi:1.1136
--- src/distrib/sets/lists/tests/mi:1.1135	Sun Oct 10 17:47:38 2021
+++ src/distrib/sets/lists/tests/mi	Wed Oct 13 04:57:20 2021
@@ -1,4 +1,4 @@
-# $NetBSD: mi,v 1.1135 2021/10/10 17:47:38 thorpej Exp $
+# $NetBSD: mi,v 1.1136 2021/10/13 04:57:20 thorpej Exp $
 #
 # Note: don't delete entries from here - mark them as "obsolete" instead.
 #
@@ -2187,6 +2187,7 @@
 ./usr/tests/kernel/kqueue/t_proc4			tests-kernel-tests	compattestfile,atf
 ./usr/tests/kernel/kqueue/t_scan			tests-kernel-tests	compattestfile,atf
 ./usr/tests/kernel/kqueue/t_sig				tests-kernel-tests	compattestfile,atf
+./usr/tests/kernel/kqueue/t_timer			tests-kernel-tests	compattestfile,atf
 ./usr/tests/kernel/kqueue/t_vnode			tests-kernel-tests	compattestfile,atf
 ./usr/tests/kernel/kqueue/write				tests-kernel-tests	compattestfile,atf
 ./usr/tests/kernel/kqueue/write/Atffile			tests-kernel-tests	compattestfile,atf

Index: src/lib/libc/sys/kqueue.2
diff -u src/lib/libc/sys/kqueue.2:1.53 src/lib/libc/sys/kqueue.2:1.54
--- src/lib/libc/sys/kqueue.2:1.53	Sat Oct 31 14:35:28 2020
+++ src/lib/libc/sys/kqueue.2	Wed Oct 13 04:57:19 2021
@@ -1,4 +1,4 @@
-.\"	$NetBSD: kqueue.2,v 1.53 2020/10/31 14:35:28 christos Exp $
+.\"	$NetBSD: kqueue.2,v 1.54 2021/10/13 04:57:19 thorpej Exp $
 .\"
 .\" Copyright (c) 2000 Jonathan Lemon
 .\" All rights reserved.
@@ -32,7 +32,7 @@
 .\"
 .\" $FreeBSD: src/lib/libc/sys/kqueue.2,v 1.22 2001/06/27 19:55:57 dd Exp $
 .\"
-.Dd October 30, 2020
+.Dd October 11, 2021
 .Dt KQUEUE 2
 .Os
 .Sh NAME
@@ -521,13 +521,72 @@ Establishes an arbitrary timer identifie
 .Va ident .
 When adding a timer,
 .Va data
-specifies the timeout period in milliseconds.
-The timer will be periodic unless EV_ONESHOT is specified.
+specifies the timeout period in units described below, or, if
+.Dv NOTE_ABSTIME
+is set in
+.Va fflags ,
+specifies the absolute time at which the timer should fire.
+The timer will repeat unless
+.Dv EV_ONESHOT
+is set in
+.Va flags
+or
+.Dv NOTE_ABSTIME
+is set in
+.Va fflags .
 On return,
 .Va data
 contains the number of times the timeout has expired since the last call to
 .Fn kevent .
-This filter automatically sets the EV_CLEAR flag internally.
+This filter automatically sets
+.Dv EV_CLEAR
+in
+.va flags
+for periodic timers.
+Timers created with
+.Dv NOTE_ABSTIME
+remain activated on the kqueue once the absolute time has passed unless
+.Dv EV_CLEAR
+or
+.Dv EV_ONESHOT
+are also specified.
+.Dv CLOCK_REALTIME
+is the reference clock for timers created with
+.Dv NOTE_ABSTIME.
+.Pp
+The filter accepts the following flags in the
+.Va fflags
+argument:
+.Bl -tag -width XXNOTE_TRACKERR
+.It Dv NOTE_SECONDS
+The timer value in
+.Va data
+is expressed in seconds.
+.It Dv NOTE_MSECONDS
+The timer value in
+.Va data
+is expressed in milliseconds.
+.It Dv NOTE_USECONDS
+The timer value in
+.Va data
+is expressed in microseconds.
+.It Dv NOTE_NSECONDS
+The timer value in
+.Va data
+is expressed in nanoseconds.
+.It Dv NOTE_ABSTIME
+The timer value is an absolute time; see discussion above.
+.El
+.Pp
+Note that
+.Dv NOTE_SECONDS ,
+.Dv NOTE_MSECONDS ,
+.Dv NOTE_USECONDS ,
+and
+.Dv NOTE_NSECONDS
+are mutually exclusive; behavior is undefined if more than one are specified.
+If a timer value unit is not specified, the default is
+.Dv NOTE_MSECONDS .
 .It Dv EVFILT_FS
 Establishes a file system monitor.
 Currently it only monitors file system mount and unmount actions.
@@ -781,3 +840,15 @@ The
 .Va udata
 type was changed from intptr_t to void * in
 .Nx 10.0 .
+.Pp
+Support for
+.Dv NOTE_SECONDS ,
+.Dv NOTE_MSECONDS ,
+.Dv NOTE_USECONDS ,
+.Dv NOTE_NSECONDS ,
+and
+.Dv NOTE_ABSTIME
+filter flags for
+.Dv EVFILT_TIMER
+was added in
+.Nx 10.0 .

Index: src/sys/kern/kern_event.c
diff -u src/sys/kern/kern_event.c:1.131 src/sys/kern/kern_event.c:1.132
--- src/sys/kern/kern_event.c:1.131	Mon Oct 11 01:07:36 2021
+++ src/sys/kern/kern_event.c	Wed Oct 13 04:57:19 2021
@@ -1,4 +1,4 @@
-/*	$NetBSD: kern_event.c,v 1.131 2021/10/11 01:07:36 thorpej Exp $	*/
+/*	$NetBSD: kern_event.c,v 1.132 2021/10/13 04:57:19 thorpej Exp $	*/
 
 /*-
  * Copyright (c) 2008, 2009, 2021 The NetBSD Foundation, Inc.
@@ -63,7 +63,7 @@
 #endif /* _KERNEL_OPT */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: kern_event.c,v 1.131 2021/10/11 01:07:36 thorpej Exp $");
+__KERNEL_RCSID(0, "$NetBSD: kern_event.c,v 1.132 2021/10/13 04:57:19 thorpej Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -1156,31 +1156,84 @@ static void
 filt_timerexpire(void *knx)
 {
 	struct knote *kn = knx;
-	int tticks;
 
 	mutex_enter(&kqueue_timer_lock);
 	kn->kn_data++;
 	knote_activate(kn);
-	if ((kn->kn_flags & EV_ONESHOT) == 0) {
-		tticks = mstohz(kn->kn_sdata);
-		if (tticks <= 0)
-			tticks = 1;
-		callout_schedule((callout_t *)kn->kn_hook, tticks);
+	if (kn->kn_sdata != (uintptr_t)-1) {
+		KASSERT(kn->kn_sdata > 0 && kn->kn_sdata <= INT_MAX);
+		callout_schedule((callout_t *)kn->kn_hook,
+		    (int)kn->kn_sdata);
 	}
 	mutex_exit(&kqueue_timer_lock);
 }
 
-/*
- * data contains amount of time to sleep, in milliseconds
- */
 static int
 filt_timerattach(struct knote *kn)
 {
 	callout_t *calloutp;
 	struct kqueue *kq;
-	int tticks;
+	struct timespec ts;
+	int tticks, flags = 0;
+
+	if (kn->kn_sfflags & ~(NOTE_TIMER_UNITMASK | NOTE_ABSTIME)) {
+		return EINVAL;
+	}
+
+	/*
+	 * Convert the event 'data' to a timespec, then convert the
+	 * timespec to callout ticks.
+	 */
+	switch (kn->kn_sfflags & NOTE_TIMER_UNITMASK) {
+	case NOTE_SECONDS:
+		ts.tv_sec = kn->kn_sdata;
+		ts.tv_nsec = 0;
+		break;
+
+	case NOTE_MSECONDS:		/* == historical value 0 */
+		ts.tv_sec = kn->kn_sdata / 1000;
+		ts.tv_nsec = (kn->kn_sdata % 1000) * 1000000;
+		break;
+
+	case NOTE_USECONDS:
+		ts.tv_sec = kn->kn_sdata / 1000000;
+		ts.tv_nsec = (kn->kn_sdata % 1000000) * 1000;
+		break;
+
+	case NOTE_NSECONDS:
+		ts.tv_sec = kn->kn_sdata / 1000000000;
+		ts.tv_nsec = kn->kn_sdata % 1000000000;
+		break;
+
+	default:
+		return EINVAL;
+	}
+
+	if (kn->kn_sfflags & NOTE_ABSTIME) {
+		struct timespec deadline = ts;
+
+		/*
+		 * Get current time.
+		 *
+		 * XXX This is CLOCK_REALTIME.  There is no way to
+		 * XXX specify CLOCK_MONOTONIC.
+		 */
+		nanotime(&ts);
 
-	tticks = mstohz(kn->kn_sdata);
+		/* If we're past the deadline, then the event will fire. */
+		if (timespeccmp(&deadline, &ts, <=)) {
+			kn->kn_data = 1;
+			return 0;
+		}
+
+		/* Calculate how much time is left. */
+		timespecsub(&deadline, &ts, &ts);
+	} else {
+		/* EV_CLEAR automatically set for relative timers. */
+		flags |= EV_CLEAR;
+	}
+
+	tticks = tstohz(&ts);
 
 	/* if the supplied value is under our resolution, use 1 tick */
 	if (tticks == 0) {
@@ -1189,6 +1242,15 @@ filt_timerattach(struct knote *kn)
 		tticks = 1;
 	}
 
+	if ((kn->kn_flags & EV_ONESHOT) != 0 ||
+	    (kn->kn_sfflags & NOTE_ABSTIME) != 0) {
+		/* Timer does not repeat. */
+		kn->kn_sdata = (uintptr_t)-1;
+	} else {
+		KASSERT((uintptr_t)tticks != (uintptr_t)-1);
+		kn->kn_sdata = tticks;
+	}
+
 	if (atomic_inc_uint_nv(&kq_ncallouts) >= kq_calloutmax ||
 	    (calloutp = kmem_alloc(sizeof(*calloutp), KM_NOSLEEP)) == NULL) {
 		atomic_dec_uint(&kq_ncallouts);
@@ -1198,7 +1260,7 @@ filt_timerattach(struct knote *kn)
 
 	kq = kn->kn_kq;
 	mutex_spin_enter(&kq->kq_lock);
-	kn->kn_flags |= EV_CLEAR;		/* automatically set */
+	kn->kn_flags |= flags;
 	kn->kn_hook = calloutp;
 	mutex_spin_exit(&kq->kq_lock);
 

Index: src/sys/sys/event.h
diff -u src/sys/sys/event.h:1.47 src/sys/sys/event.h:1.48
--- src/sys/sys/event.h:1.47	Mon Oct 11 01:21:28 2021
+++ src/sys/sys/event.h	Wed Oct 13 04:57:19 2021
@@ -1,4 +1,4 @@
-/*	$NetBSD: event.h,v 1.47 2021/10/11 01:21:28 thorpej Exp $	*/
+/*	$NetBSD: event.h,v 1.48 2021/10/13 04:57:19 thorpej Exp $	*/
 
 /*-
  * Copyright (c) 1999,2000,2001 Jonathan Lemon <jle...@freebsd.org>
@@ -160,6 +160,16 @@ _EV_SET(struct kevent *_kevp, uintptr_t 
 #define	NOTE_TRACKERR	0x00000002U		/* could not track child */
 #define	NOTE_CHILD	0x00000004U		/* am a child process */
 
+/* additional flags for EVFILT_TIMER */
+#define	NOTE_MSECONDS	0x00000000U		/* data is milliseconds */
+#define	NOTE_SECONDS	0x00000001U		/* data is seconds */
+#define	NOTE_USECONDS	0x00000002U		/* data is microseconds */
+#define	NOTE_NSECONDS	0x00000003U		/* data is nanoseconds */
+#define	NOTE_ABSTIME	0x00000010U		/* timeout is absolute */
+#ifdef _KERNEL
+#define	NOTE_TIMER_UNITMASK 0x0003U
+#endif /* _KERNEL */
+
 /*
  * This is currently visible to userland to work around broken
  * programs which pull in <sys/proc.h> or <sys/select.h>.

Index: src/tests/kernel/kqueue/Makefile
diff -u src/tests/kernel/kqueue/Makefile:1.6 src/tests/kernel/kqueue/Makefile:1.7
--- src/tests/kernel/kqueue/Makefile:1.6	Sun Oct 10 17:47:39 2021
+++ src/tests/kernel/kqueue/Makefile	Wed Oct 13 04:57:19 2021
@@ -1,4 +1,4 @@
-# $NetBSD: Makefile,v 1.6 2021/10/10 17:47:39 thorpej Exp $
+# $NetBSD: Makefile,v 1.7 2021/10/13 04:57:19 thorpej Exp $
 
 WARNS?=6
 NOMAN=		# defined
@@ -17,6 +17,7 @@ TESTS_C+=	t_proc3
 TESTS_C+=	t_proc4
 TESTS_C+=	t_scan
 TESTS_C+=	t_sig
+TESTS_C+=	t_timer
 TESTS_C+=	t_vnode
 
 LDADD.t_scan+=	-lpthread

Added files:

Index: src/tests/kernel/kqueue/t_timer.c
diff -u /dev/null src/tests/kernel/kqueue/t_timer.c:1.1
--- /dev/null	Wed Oct 13 04:57:20 2021
+++ src/tests/kernel/kqueue/t_timer.c	Wed Oct 13 04:57:19 2021
@@ -0,0 +1,274 @@
+/* $NetBSD: t_timer.c,v 1.1 2021/10/13 04:57:19 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2021 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: t_timer.c,v 1.1 2021/10/13 04:57:19 thorpej Exp $");
+
+#include <sys/types.h>
+#include <sys/event.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+ATF_TC(basic_timer);
+ATF_TC_HEAD(basic_timer, tc)
+{
+	atf_tc_set_md_var(tc, "descr",
+	    "tests basic EVFILT_TIMER functionality");
+}
+
+#define	TIME1		1000		/* 1000ms -> 1s */
+#define	TIME1_COUNT	5
+#define	TIME2		6000		/* 6000ms -> 6s */
+
+#define	TIME1_TOTAL_SEC	((TIME1 * TIME1_COUNT) / 1000)
+#define	TIME2_TOTAL_SEC	(TIME2 / 1000)
+
+ATF_TC_BODY(basic_timer, tc)
+{
+	struct kevent event[2];
+	int ntimer1 = 0, ntimer2 = 0;
+	struct timespec ots, ts;
+	int kq;
+
+	ATF_REQUIRE((kq = kqueue()) >= 0);
+
+	EV_SET(&event[0], 1, EVFILT_TIMER, EV_ADD, 0, TIME1, NULL);
+	EV_SET(&event[1], 2, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 0, TIME2, NULL);
+
+	ATF_REQUIRE(kevent(kq, event, 2, NULL, 0, NULL) == 0);
+	ATF_REQUIRE(clock_gettime(CLOCK_MONOTONIC, &ots) == 0);
+
+	for (;;) {
+		ATF_REQUIRE(kevent(kq, NULL, 0, event, 1, NULL) == 1);
+		ATF_REQUIRE(event[0].filter == EVFILT_TIMER);
+		ATF_REQUIRE(event[0].ident == 1 ||
+			    event[0].ident == 2);
+		if (event[0].ident == 1) {
+			ATF_REQUIRE(ntimer1 < TIME1_COUNT);
+			if (++ntimer1 == TIME1_COUNT) {
+				/*
+				 * Make sure TIME1_TOTAL_SEC seconds have
+				 * elapsed, allowing for a little slop.
+				 */
+				ATF_REQUIRE(clock_gettime(CLOCK_MONOTONIC,
+				    &ts) == 0);
+				timespecsub(&ts, &ots, &ts);
+				ATF_REQUIRE(ts.tv_sec ==
+					    (TIME1_TOTAL_SEC - 1) ||
+				    ts.tv_sec == TIME1_TOTAL_SEC);
+				if (ts.tv_sec == TIME1_TOTAL_SEC - 1) {
+					ATF_REQUIRE(ts.tv_nsec >=
+					    900000000);
+				}
+				EV_SET(&event[0], 1, EVFILT_TIMER, EV_DELETE,
+				    0, 0, NULL);
+				ATF_REQUIRE(kevent(kq, event, 1, NULL, 0,
+				    NULL) == 0);
+			}
+		} else {
+			ATF_REQUIRE(ntimer1 == TIME1_COUNT);
+			ATF_REQUIRE(ntimer2 == 0);
+			ntimer2++;
+			/*
+			 * Make sure TIME2_TOTAL_SEC seconds have
+			 * elapsed, allowing for a little slop.
+			 */
+			ATF_REQUIRE(clock_gettime(CLOCK_MONOTONIC,
+			    &ts) == 0);
+			timespecsub(&ts, &ots, &ts);
+			ATF_REQUIRE(ts.tv_sec ==
+				    (TIME2_TOTAL_SEC - 1) ||
+			    ts.tv_sec == TIME2_TOTAL_SEC);
+			if (ts.tv_sec == TIME2_TOTAL_SEC - 1) {
+				ATF_REQUIRE(ts.tv_nsec >= 900000000);
+			}
+			EV_SET(&event[0], 2, EVFILT_TIMER, EV_DELETE,
+			    0, 0, NULL);
+			ATF_REQUIRE_ERRNO(ENOENT,
+			    kevent(kq, event, 1, NULL, 0, NULL) == -1);
+			break;
+		}
+	}
+
+	/*
+	 * Now block in kqueue for TIME2_TOTAL_SEC, and ensure we
+	 * don't receive any new events.
+	 */
+	ATF_REQUIRE(clock_gettime(CLOCK_MONOTONIC, &ots) == 0);
+	ts.tv_sec = TIME2_TOTAL_SEC;
+	ts.tv_nsec = 0;
+	ATF_REQUIRE(kevent(kq, NULL, 0, event, 1, &ts) == 0);
+	ATF_REQUIRE(clock_gettime(CLOCK_MONOTONIC, &ts) == 0);
+	timespecsub(&ts, &ots, &ts);
+	ATF_REQUIRE(ts.tv_sec == (TIME2_TOTAL_SEC - 1) ||
+	    ts.tv_sec == TIME2_TOTAL_SEC ||
+	    ts.tv_sec == (TIME2_TOTAL_SEC + 1));
+	if (ts.tv_sec == TIME2_TOTAL_SEC - 1) {
+		ATF_REQUIRE(ts.tv_nsec >= 900000000);
+	} else if (ts.tv_sec == TIME2_TOTAL_SEC + 1) {
+		ATF_REQUIRE(ts.tv_nsec < 500000000);
+	}
+}
+
+ATF_TC(count_expirations);
+ATF_TC_HEAD(count_expirations, tc)
+{
+	atf_tc_set_md_var(tc, "descr",
+	    "tests counting timer expirations");
+}
+
+ATF_TC_BODY(count_expirations, tc)
+{
+	struct kevent event[1];
+	struct timespec ts = { 0, 0 };
+	struct timespec sleepts;
+	int kq;
+
+	ATF_REQUIRE((kq = kqueue()) >= 0);
+
+	EV_SET(&event[0], 1, EVFILT_TIMER, EV_ADD, 0, TIME1, NULL);
+	ATF_REQUIRE(kevent(kq, event, 1, NULL, 0, NULL) == 0);
+
+	/* Sleep a little longer to mitigate timing jitter. */
+	sleepts.tv_sec = TIME1_TOTAL_SEC;
+	sleepts.tv_nsec = 500000000;
+	ATF_REQUIRE(nanosleep(&sleepts, NULL) == 0);
+
+	ATF_REQUIRE(kevent(kq, NULL, 0, event, 1, &ts) == 1);
+	ATF_REQUIRE(event[0].ident == 1);
+	ATF_REQUIRE(event[0].data == TIME1_COUNT ||
+		    event[0].data == TIME1_COUNT + 1);
+}
+
+ATF_TC(abstime);
+ATF_TC_HEAD(abstime, tc)
+{
+	atf_tc_set_md_var(tc, "descr",
+	    "tests timers with NOTE_ABSTIME");
+}
+
+ATF_TC_BODY(abstime, tc)
+{
+	struct kevent event[1];
+	struct timespec ts, ots;
+	time_t seconds;
+	int kq;
+
+	ATF_REQUIRE((kq = kqueue()) >= 0);
+
+	ATF_REQUIRE(clock_gettime(CLOCK_REALTIME, &ots) == 0);
+	ATF_REQUIRE(ots.tv_sec < INTPTR_MAX - TIME1_TOTAL_SEC);
+
+	seconds = ots.tv_sec + TIME1_TOTAL_SEC;
+	if (ots.tv_nsec >= 500000000) {
+		seconds++;
+	}
+
+	EV_SET(&event[0], 1, EVFILT_TIMER, EV_ADD,
+	    NOTE_ABSTIME | NOTE_SECONDS, seconds, NULL);
+	ATF_REQUIRE(kevent(kq, event, 1, event, 1, NULL) == 1);
+
+	ATF_REQUIRE(clock_gettime(CLOCK_REALTIME, &ts) == 0);
+	timespecsub(&ts, &ots, &ts);
+
+	/*
+	 * We're not going for precision here; just verify that it was
+	 * delivered anywhere between 4.5-6.whatever seconds later.
+	 */
+	ATF_REQUIRE(ts.tv_sec >= 4 && ts.tv_sec <= 6);
+	if (ts.tv_sec == 4) {
+		ATF_REQUIRE(ts.tv_nsec >= 500000000);
+	}
+
+	ts.tv_sec = 0;
+	ts.tv_nsec = 0;
+	ATF_REQUIRE(kevent(kq, NULL, 0, event, 1, &ts) == 1);
+}
+
+#define	PREC_TIMEOUT_SEC	2
+
+static void
+do_test_timer_units(const char *which, uint32_t fflag, int64_t data)
+{
+	struct kevent event[1];
+	struct timespec ts, ots;
+	int kq;
+
+	ATF_REQUIRE((kq = kqueue()) >= 0);
+
+	EV_SET(&event[0], 1, EVFILT_TIMER, EV_ADD | EV_ONESHOT,
+	    fflag, data, NULL);
+
+	ATF_REQUIRE(clock_gettime(CLOCK_MONOTONIC, &ots) == 0); 
+	ATF_REQUIRE(kevent(kq, event, 1, event, 1, NULL) == 1);
+	ATF_REQUIRE(clock_gettime(CLOCK_MONOTONIC, &ts) == 0);
+
+	timespecsub(&ts, &ots, &ts);
+	ATF_REQUIRE_MSG(ts.tv_sec == (PREC_TIMEOUT_SEC - 1) ||
+		    ts.tv_sec == PREC_TIMEOUT_SEC,
+		    "units '%s' failed [sec]", which);
+	if (ts.tv_sec == PREC_TIMEOUT_SEC - 1) {
+		ATF_REQUIRE_MSG(ts.tv_nsec >= 900000000,
+		"units '%s' failed [nsec]", which);
+	}
+
+	(void)close(kq);
+}
+
+#define	test_timer_units(fflag, data)				\
+	do_test_timer_units(#fflag, fflag, data)
+
+ATF_TC(timer_units);
+ATF_TC_HEAD(timer_units, tc)
+{
+	atf_tc_set_md_var(tc, "descr",
+	    "tests timers with NOTE_* units modifiers");
+}
+
+ATF_TC_BODY(timer_units, tc)
+{
+	test_timer_units(NOTE_SECONDS,  PREC_TIMEOUT_SEC);
+	test_timer_units(NOTE_MSECONDS, PREC_TIMEOUT_SEC * 1000);
+	test_timer_units(NOTE_USECONDS, PREC_TIMEOUT_SEC * 1000000);
+	test_timer_units(NOTE_NSECONDS, PREC_TIMEOUT_SEC * 1000000000);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+	ATF_TP_ADD_TC(tp, basic_timer);
+	ATF_TP_ADD_TC(tp, count_expirations);
+	ATF_TP_ADD_TC(tp, abstime);
+	ATF_TP_ADD_TC(tp, timer_units);
+
+	return atf_no_error();
+}

Reply via email to