aboutsummaryrefslogtreecommitdiff
path: root/tests/sys/kqueue/libkqueue/timer.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/sys/kqueue/libkqueue/timer.c')
-rw-r--r--tests/sys/kqueue/libkqueue/timer.c343
1 files changed, 331 insertions, 12 deletions
diff --git a/tests/sys/kqueue/libkqueue/timer.c b/tests/sys/kqueue/libkqueue/timer.c
index 12b324b4eef8..51e1cdf1ac82 100644
--- a/tests/sys/kqueue/libkqueue/timer.c
+++ b/tests/sys/kqueue/libkqueue/timer.c
@@ -19,8 +19,58 @@
#include "common.h"
#include <sys/time.h>
+#define MILLION 1000000
+#define THOUSAND 1000
+#define SEC_TO_MS(t) ((t) * THOUSAND) /* Convert seconds to milliseconds. */
+#define SEC_TO_US(t) ((t) * MILLION) /* Convert seconds to microseconds. */
+#define MS_TO_US(t) ((t) * THOUSAND) /* Convert milliseconds to microseconds. */
+#define US_TO_NS(t) ((t) * THOUSAND) /* Convert microseconds to nanoseconds. */
+
int kqfd;
+/* Get the current time with microsecond precision. Used for
+ * sub-second timing to make some timer tests run faster.
+ */
+static long
+now(void)
+{
+
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+ return SEC_TO_US(tv.tv_sec) + tv.tv_usec;
+}
+
+/* Sleep for a given number of milliseconds. The timeout is assumed to
+ * be less than 1 second.
+ */
+void
+mssleep(int t)
+{
+
+ struct timespec stime = {
+ .tv_sec = 0,
+ .tv_nsec = US_TO_NS(MS_TO_US(t)),
+ };
+
+ nanosleep(&stime, NULL);
+}
+
+/* Sleep for a given number of microseconds. The timeout is assumed to
+ * be less than 1 second.
+ */
+void
+ussleep(int t)
+{
+
+ struct timespec stime = {
+ .tv_sec = 0,
+ .tv_nsec = US_TO_NS(t),
+ };
+
+ nanosleep(&stime, NULL);
+}
+
void
test_kevent_timer_add(void)
{
@@ -170,16 +220,17 @@ test_abstime(void)
{
const char *test_id = "kevent(EVFILT_TIMER, EV_ONESHOT, NOTE_ABSTIME)";
struct kevent kev;
- time_t when;
+ time_t start;
+ time_t stop;
const int timeout = 3;
test_begin(test_id);
test_no_kevents();
- when = time(NULL);
+ start = time(NULL);
EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT,
- NOTE_ABSTIME | NOTE_SECONDS, when + timeout, NULL);
+ NOTE_ABSTIME | NOTE_SECONDS, start + timeout, NULL);
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
err(1, "%s", test_id);
@@ -188,8 +239,9 @@ test_abstime(void)
kev.data = 1;
kev.fflags = 0;
kevent_cmp(&kev, kevent_get(kqfd));
- if (time(NULL) < when + timeout)
- err(1, "too early %jd %jd", time(), when + timeout);
+ stop = time(NULL);
+ if (stop < start + timeout)
+ err(1, "too early %jd %jd", (intmax_t)stop, (intmax_t)(start + timeout));
/* Check if the event occurs again */
sleep(3);
@@ -198,16 +250,283 @@ test_abstime(void)
success();
}
+static void
+test_update(void)
+{
+ const char *test_id = "kevent(EVFILT_TIMER (UPDATE), EV_ADD | EV_ONESHOT)";
+ struct kevent kev;
+ long elapsed;
+ long start;
+
+ test_begin(test_id);
+
+ test_no_kevents();
+
+ /* First set the timer to 1 second */
+ EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT,
+ NOTE_USECONDS, SEC_TO_US(1), (void *)1);
+ start = now();
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ /* Now reduce the timer to 1 ms */
+ EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT,
+ NOTE_USECONDS, MS_TO_US(1), (void *)2);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ /* Wait for the event */
+ kev.flags |= EV_CLEAR;
+ kev.fflags &= ~NOTE_USECONDS;
+ kev.data = 1;
+ kevent_cmp(&kev, kevent_get(kqfd));
+ elapsed = now() - start;
+
+ /* Check that the timer expired after at least 1 ms, but less than
+ * 1 second. This check is to make sure that the original 1 second
+ * timeout was not used.
+ */
+ printf("timer expired after %ld us\n", elapsed);
+ if (elapsed < MS_TO_US(1))
+ errx(1, "early timer expiration: %ld us", elapsed);
+ if (elapsed > SEC_TO_US(1))
+ errx(1, "late timer expiration: %ld us", elapsed);
+
+ success();
+}
+
+static void
+test_update_equal(void)
+{
+ const char *test_id = "kevent(EVFILT_TIMER (UPDATE=), EV_ADD | EV_ONESHOT)";
+ struct kevent kev;
+ long elapsed;
+ long start;
+
+ test_begin(test_id);
+
+ test_no_kevents();
+
+ /* First set the timer to 1 ms */
+ EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT,
+ NOTE_USECONDS, MS_TO_US(1), NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ /* Sleep for a significant fraction of the timeout. */
+ ussleep(600);
+
+ /* Now re-add the timer with the same parameters */
+ start = now();
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ /* Wait for the event */
+ kev.flags |= EV_CLEAR;
+ kev.fflags &= ~NOTE_USECONDS;
+ kev.data = 1;
+ kevent_cmp(&kev, kevent_get(kqfd));
+ elapsed = now() - start;
+
+ /* Check that the timer expired after at least 1 ms. This check is
+ * to make sure that the timer re-started and that the event is
+ * not from the original add of the timer.
+ */
+ printf("timer expired after %ld us\n", elapsed);
+ if (elapsed < MS_TO_US(1))
+ errx(1, "early timer expiration: %ld us", elapsed);
+
+ success();
+}
+
+static void
+test_update_expired(void)
+{
+ const char *test_id = "kevent(EVFILT_TIMER (UPDATE EXP), EV_ADD | EV_ONESHOT)";
+ struct kevent kev;
+ long elapsed;
+ long start;
+
+ test_begin(test_id);
+
+ test_no_kevents();
+
+ /* Set the timer to 1ms */
+ EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT,
+ NOTE_USECONDS, MS_TO_US(1), NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ /* Wait for 2 ms to give the timer plenty of time to expire. */
+ mssleep(2);
+
+ /* Now re-add the timer */
+ start = now();
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ /* Wait for the event */
+ kev.flags |= EV_CLEAR;
+ kev.fflags &= ~NOTE_USECONDS;
+ kev.data = 1;
+ kevent_cmp(&kev, kevent_get(kqfd));
+ elapsed = now() - start;
+
+ /* Check that the timer expired after at least 1 ms. This check
+ * is to make sure that the timer re-started and that the event is
+ * not from the original add (and expiration) of the timer.
+ */
+ printf("timer expired after %ld us\n", elapsed);
+ if (elapsed < MS_TO_US(1))
+ errx(1, "early timer expiration: %ld us", elapsed);
+
+ /* Make sure the re-added timer does not fire. In other words,
+ * test that the event received above was the only event from the
+ * add and re-add of the timer.
+ */
+ mssleep(2);
+ test_no_kevents();
+
+ success();
+}
+
+static void
+test_update_periodic(void)
+{
+ const char *test_id = "kevent(EVFILT_TIMER (UPDATE), periodic)";
+ struct kevent kev;
+ long elapsed;
+ long start;
+ long stop;
+
+ test_begin(test_id);
+
+ test_no_kevents();
+
+ EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD, 0, SEC_TO_MS(1), NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ /* Retrieve the event */
+ kev.flags = EV_ADD | EV_CLEAR;
+ kev.data = 1;
+ kevent_cmp(&kev, kevent_get(kqfd));
+
+ /* Check if the event occurs again */
+ sleep(1);
+ kevent_cmp(&kev, kevent_get(kqfd));
+
+ /* Re-add with new timeout. */
+ EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD, 0, SEC_TO_MS(2), NULL);
+ start = now();
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ /* Retrieve the event */
+ kev.flags = EV_ADD | EV_CLEAR;
+ kev.data = 1;
+ kevent_cmp(&kev, kevent_get(kqfd));
+
+ stop = now();
+ elapsed = stop - start;
+
+ /* Check that the timer expired after at least 2 ms.
+ */
+ printf("timer expired after %ld us\n", elapsed);
+ if (elapsed < MS_TO_US(2))
+ errx(1, "early timer expiration: %ld us", elapsed);
+
+ /* Delete the event */
+ kev.flags = EV_DELETE;
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ success();
+}
+
+static void
+test_update_timing(void)
+{
+#define MIN_SLEEP 500
+#define MAX_SLEEP 1500
+ const char *test_id = "kevent(EVFILT_TIMER (UPDATE TIMING), EV_ADD | EV_ONESHOT)";
+ struct kevent kev;
+ int iteration;
+ int sleeptime;
+ long elapsed;
+ long start;
+ long stop;
+
+ test_begin(test_id);
+
+ test_no_kevents();
+
+ /* Re-try the update tests with a variety of delays between the
+ * original timer activation and the update of the timer. The goal
+ * is to show that in all cases the only timer event that is
+ * received is from the update and not the original timer add.
+ */
+ for (sleeptime = MIN_SLEEP, iteration = 1;
+ sleeptime < MAX_SLEEP;
+ ++sleeptime, ++iteration) {
+
+ /* First set the timer to 1 ms */
+ EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT,
+ NOTE_USECONDS, MS_TO_US(1), NULL);
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ /* Delay; the delay ranges from less than to greater than the
+ * timer period.
+ */
+ ussleep(sleeptime);
+
+ /* Now re-add the timer with the same parameters */
+ start = now();
+ if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
+ err(1, "%s", test_id);
+
+ /* Wait for the event */
+ kev.flags |= EV_CLEAR;
+ kev.fflags &= ~NOTE_USECONDS;
+ kev.data = 1;
+ kevent_cmp(&kev, kevent_get(kqfd));
+ stop = now();
+ elapsed = stop - start;
+
+ /* Check that the timer expired after at least 1 ms. This
+ * check is to make sure that the timer re-started and that
+ * the event is not from the original add of the timer.
+ */
+ if (elapsed < MS_TO_US(1))
+ errx(1, "early timer expiration: %ld us", elapsed);
+
+ /* Make sure the re-added timer does not fire. In other words,
+ * test that the event received above was the only event from
+ * the add and re-add of the timer.
+ */
+ mssleep(2);
+ test_no_kevents_quietly();
+ }
+
+ success();
+}
+
void
test_evfilt_timer()
{
kqfd = kqueue();
- test_kevent_timer_add();
- test_kevent_timer_del();
- test_kevent_timer_get();
- test_oneshot();
- test_periodic();
- test_abstime();
- disable_and_enable();
+ test_kevent_timer_add();
+ test_kevent_timer_del();
+ test_kevent_timer_get();
+ test_oneshot();
+ test_periodic();
+ test_abstime();
+ test_update();
+ test_update_equal();
+ test_update_expired();
+ test_update_timing();
+ test_update_periodic();
+ disable_and_enable();
close(kqfd);
}