diff options
| author | Jake Freeland <jfree@FreeBSD.org> | 2026-03-20 06:31:30 +0000 |
|---|---|---|
| committer | Jake Freeland <jfree@FreeBSD.org> | 2026-03-20 07:15:08 +0000 |
| commit | 834c1ba793d900b791e19ff449b1e0b96fb494a0 (patch) | |
| tree | 5e01d1ca8a6b1ba38f13f9ab1233b9adeccf314a | |
| parent | 2eb0a888917593220c27c688b026767c20fa68ab (diff) | |
timerfd: Add tests
Take Jan Kokemuller's timerfd tests from the epoll-shim project,
stripping out code that isn't directly related to FreeBSD.
Reviewed by: markj
Differential Revision: https://reviews.freebsd.org/D55789
MFC after: 2 weeks
| -rw-r--r-- | tests/sys/kern/Makefile | 3 | ||||
| -rw-r--r-- | tests/sys/kern/timerfd.c | 1318 |
2 files changed, 1321 insertions, 0 deletions
diff --git a/tests/sys/kern/Makefile b/tests/sys/kern/Makefile index 6d59a752bb4f..0e505d6cb51a 100644 --- a/tests/sys/kern/Makefile +++ b/tests/sys/kern/Makefile @@ -53,6 +53,9 @@ ATF_TESTS_C+= subr_physmem_test PLAIN_TESTS_C+= subr_unit_test ATF_TESTS_C+= sysctl_kern_proc ATF_TESTS_C+= sys_getrandom +ATF_TESTS_C+= timerfd +CFLAGS.timerfd+= -I${.CURDIR} +LIBADD.timerfd+= pthread ATF_TESTS_C+= tty_pts ATF_TESTS_C+= unix_dgram ATF_TESTS_C+= unix_passfd_dgram diff --git a/tests/sys/kern/timerfd.c b/tests/sys/kern/timerfd.c new file mode 100644 index 000000000000..37cb4924faf1 --- /dev/null +++ b/tests/sys/kern/timerfd.c @@ -0,0 +1,1318 @@ +/*- + * SPDX-License-Identifier: MIT + * + * Copyright (c) 2016 Jan Kokemüller + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include <atf-c.h> + +#include <sys/types.h> + +#include <sys/event.h> +#include <sys/param.h> +#include <sys/select.h> +#include <sys/time.h> + +#include <errno.h> +#include <signal.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> + +#include <err.h> +#include <poll.h> +#include <pthread.h> +#include <time.h> +#include <unistd.h> + +#include <sys/timerfd.h> + +/* Time in ns that sleeps are allowed to take longer for in unit tests. */ +#define TIMER_SLACK (90000000) + +ATF_TC_WITHOUT_HEAD(timerfd__many_timers); +ATF_TC_BODY(timerfd__many_timers, tc) +{ + int timer_fds[256]; + int i; + + for (i = 0; i < (int)nitems(timer_fds); ++i) { + timer_fds[i] = timerfd_create(CLOCK_MONOTONIC, /**/ + TFD_CLOEXEC | TFD_NONBLOCK); + if (timer_fds[i] < 0 && errno == EMFILE) { + atf_tc_skip("timerfd_create: EMFILE"); + } + ATF_REQUIRE_MSG(timer_fds[i] >= 0, "errno: %d", errno); + } +} + +static uint64_t +wait_for_timerfd(int timerfd) +{ + struct pollfd pfd = { .fd = timerfd, .events = POLLIN }; + + ATF_REQUIRE(poll(&pfd, 1, -1) == 1); + + uint64_t timeouts; + ssize_t r = read(timerfd, &timeouts, sizeof(timeouts)); + + ATF_REQUIRE_MSG(r == (ssize_t)sizeof(timeouts), "%d %d", (int)r, errno); + ATF_REQUIRE(timeouts > 0); + return timeouts; +} + +ATF_TC_WITHOUT_HEAD(timerfd__simple_timer); +ATF_TC_BODY(timerfd__simple_timer, tc) +{ + int timerfd = timerfd_create(CLOCK_MONOTONIC, /**/ + TFD_CLOEXEC | TFD_NONBLOCK); + + ATF_REQUIRE(timerfd >= 0); + + struct itimerspec time = { + .it_value.tv_sec = 0, + .it_value.tv_nsec = 100000000, + }; + + struct timespec b, e; + ATF_REQUIRE(clock_gettime(CLOCK_MONOTONIC, &b) == 0); + + ATF_REQUIRE(timerfd_settime(timerfd, 0, &time, NULL) == 0); + (void)wait_for_timerfd(timerfd); + + ATF_REQUIRE(clock_gettime(CLOCK_MONOTONIC, &e) == 0); + timespecsub(&e, &b, &e); + + ATF_REQUIRE((e.tv_sec == 0 && e.tv_nsec >= 100000000) || e.tv_sec > 0); + ATF_REQUIRE(e.tv_sec == 0 && e.tv_nsec < 100000000 + TIMER_SLACK); + + ATF_REQUIRE(close(timerfd) == 0); +} + +ATF_TC_WITHOUT_HEAD(timerfd__simple_periodic_timer); +ATF_TC_BODY(timerfd__simple_periodic_timer, tc) +{ + int timerfd = timerfd_create(CLOCK_MONOTONIC, /**/ + TFD_CLOEXEC | TFD_NONBLOCK); + + ATF_REQUIRE(timerfd >= 0); + + struct itimerspec time = { + .it_value.tv_sec = 0, + .it_value.tv_nsec = 200000000, + .it_interval.tv_sec = 0, + .it_interval.tv_nsec = 200000000, + }; + + struct timespec b, e; + ATF_REQUIRE(clock_gettime(CLOCK_MONOTONIC, &b) == 0); + + ATF_REQUIRE(timerfd_settime(timerfd, 0, &time, NULL) == 0); + uint64_t timeouts = wait_for_timerfd(timerfd); + + ATF_REQUIRE(clock_gettime(CLOCK_MONOTONIC, &e) == 0); + timespecsub(&e, &b, &e); + + ATF_REQUIRE((e.tv_sec == 0 && e.tv_nsec >= 200000000) || e.tv_sec > 0); + ATF_REQUIRE(timeouts >= 1); + ATF_REQUIRE(e.tv_sec == 0 && e.tv_nsec < 200000000 + TIMER_SLACK); + ATF_REQUIRE(timeouts == 1); + + usleep(400000); + + ATF_REQUIRE(read(timerfd, &timeouts, sizeof(timeouts)) == + (ssize_t)sizeof(timeouts)); + ATF_REQUIRE(timeouts >= 2); + ATF_REQUIRE(timeouts == 2); + + ATF_REQUIRE(close(timerfd) == 0); +} + +ATF_TC_WITHOUT_HEAD(timerfd__complex_periodic_timer); +ATF_TC_BODY(timerfd__complex_periodic_timer, tc) +{ + int timerfd = timerfd_create(CLOCK_MONOTONIC, /**/ + TFD_CLOEXEC | TFD_NONBLOCK); + + ATF_REQUIRE(timerfd >= 0); + + struct itimerspec time = { + .it_value.tv_sec = 0, + .it_value.tv_nsec = 100000000, + .it_interval.tv_sec = 0, + .it_interval.tv_nsec = 200000001, + }; + + struct timespec b, e; + ATF_REQUIRE(clock_gettime(CLOCK_MONOTONIC, &b) == 0); + + ATF_REQUIRE(timerfd_settime(timerfd, 0, &time, NULL) == 0); + uint64_t timeouts = wait_for_timerfd(timerfd); + + ATF_REQUIRE(clock_gettime(CLOCK_MONOTONIC, &e) == 0); + timespecsub(&e, &b, &e); + + ATF_REQUIRE((e.tv_sec == 0 && e.tv_nsec >= 100000000) || e.tv_sec > 0); + ATF_REQUIRE(timeouts >= 1); + ATF_REQUIRE_MSG(e.tv_sec == 0 && e.tv_nsec >= 100000000 && + e.tv_nsec < 100000000 + TIMER_SLACK, + "%ld", (long)e.tv_nsec); + ATF_REQUIRE(timeouts == 1); + + usleep(401000); + + ATF_REQUIRE(read(timerfd, &timeouts, sizeof(timeouts)) == + (ssize_t)sizeof(timeouts)); + ATF_REQUIRE_MSG(timeouts >= 2, "%d", (int)timeouts); + ATF_REQUIRE_MSG(timeouts == 2, "%d", (int)timeouts); + + ATF_REQUIRE(close(timerfd) == 0); +} + +ATF_TC_WITHOUT_HEAD(timerfd__reset_periodic_timer); +ATF_TC_BODY(timerfd__reset_periodic_timer, tc) +{ + int timerfd = timerfd_create(CLOCK_MONOTONIC, /**/ + TFD_CLOEXEC | TFD_NONBLOCK); + + ATF_REQUIRE(timerfd >= 0); + + struct itimerspec time = { + .it_value.tv_sec = 0, + .it_value.tv_nsec = 100000000, + .it_interval.tv_sec = 0, + .it_interval.tv_nsec = 100000000, + }; + + struct timespec b, e; + ATF_REQUIRE(clock_gettime(CLOCK_MONOTONIC, &b) == 0); + + ATF_REQUIRE(timerfd_settime(timerfd, 0, &time, NULL) == 0); + (void)wait_for_timerfd(timerfd); + + time = (struct itimerspec) { + .it_value.tv_sec = 0, + .it_value.tv_nsec = 50000000, + .it_interval.tv_sec = 0, + .it_interval.tv_nsec = 100000000, + }; + + ATF_REQUIRE(timerfd_settime(timerfd, 0, &time, NULL) == 0); + + uint64_t timeouts = wait_for_timerfd(timerfd); + ATF_REQUIRE(timeouts >= 1); + ATF_REQUIRE(timeouts == 1); + + ATF_REQUIRE(clock_gettime(CLOCK_MONOTONIC, &e) == 0); + timespecsub(&e, &b, &e); + ATF_REQUIRE((e.tv_sec == 0 && e.tv_nsec >= 150000000) || e.tv_sec > 0); + ATF_REQUIRE(e.tv_sec == 0 && e.tv_nsec >= 150000000 && + e.tv_nsec < 150000000 + TIMER_SLACK * 2); + + ATF_REQUIRE(close(timerfd) == 0); +} + +ATF_TC_WITHOUT_HEAD(timerfd__reenable_periodic_timer); +ATF_TC_BODY(timerfd__reenable_periodic_timer, tc) +{ + int timerfd = timerfd_create(CLOCK_MONOTONIC, /**/ + TFD_CLOEXEC | TFD_NONBLOCK); + + ATF_REQUIRE(timerfd >= 0); + + struct itimerspec time = { + .it_value.tv_sec = 0, + .it_value.tv_nsec = 100000000, + .it_interval.tv_sec = 0, + .it_interval.tv_nsec = 100000000, + }; + + struct timespec b, e; + ATF_REQUIRE(clock_gettime(CLOCK_MONOTONIC, &b) == 0); + + ATF_REQUIRE(timerfd_settime(timerfd, 0, &time, NULL) == 0); + uint64_t timeouts = wait_for_timerfd(timerfd); + + ATF_REQUIRE(timeouts >= 1); + ATF_REQUIRE(timeouts == 1); + + time = (struct itimerspec) { + .it_value.tv_sec = 0, + .it_value.tv_nsec = 0, + .it_interval.tv_sec = 0, + .it_interval.tv_nsec = 0, + }; + + ATF_REQUIRE(timerfd_settime(timerfd, 0, &time, NULL) == 0); + + struct pollfd pfd = { .fd = timerfd, .events = POLLIN }; + ATF_REQUIRE(poll(&pfd, 1, 250) == 0); + + ATF_REQUIRE(clock_gettime(CLOCK_MONOTONIC, &e) == 0); + timespecsub(&e, &b, &e); + + ATF_REQUIRE((e.tv_sec == 0 && e.tv_nsec >= 350000000) || e.tv_sec > 0); + ATF_REQUIRE(e.tv_sec == 0 && e.tv_nsec >= 350000000 && + e.tv_nsec < 350000000 + TIMER_SLACK * 2); + + time = (struct itimerspec) { + .it_value.tv_sec = 1, + .it_value.tv_nsec = 0, + .it_interval.tv_sec = 1, + .it_interval.tv_nsec = 0, + }; + ATF_REQUIRE(timerfd_settime(timerfd, 0, &time, NULL) == 0); + + ATF_REQUIRE(close(timerfd) == 0); +} + +/* + * Adapted from sghctoma's example here: + * https://github.com/jiixyj/epoll-shim/issues/2 + * + * The SIGUSR1 signal should not kill the process. + */ +ATF_TC_WITHOUT_HEAD(timerfd__expire_five); +ATF_TC_BODY(timerfd__expire_five, tc) +{ + int fd; + struct itimerspec value; + uint64_t total_exp = 0; + + fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK); + ATF_REQUIRE(fd >= 0); + + value.it_value.tv_sec = 3; + value.it_value.tv_nsec = 0; + value.it_interval.tv_sec = 1; + value.it_interval.tv_nsec = 0; + + ATF_REQUIRE(timerfd_settime(fd, 0, &value, NULL) == 0); + + sigset_t sigs; + sigemptyset(&sigs); + sigaddset(&sigs, SIGUSR1); + sigprocmask(SIG_BLOCK, &sigs, NULL); + + kill(getpid(), SIGUSR1); + + for (;;) { + uint64_t exp = wait_for_timerfd(fd); + + printf("timer expired %u times\n", (unsigned)exp); + + total_exp += exp; + if (total_exp >= 5) { + break; + } + } + + ATF_REQUIRE(close(fd) == 0); +} + +ATF_TC_WITHOUT_HEAD(timerfd__simple_gettime); +ATF_TC_BODY(timerfd__simple_gettime, tc) +{ + struct itimerspec curr_value; + + int fd = timerfd_create(CLOCK_MONOTONIC, 0); + ATF_REQUIRE(fd >= 0); + + ATF_REQUIRE(timerfd_gettime(fd, &curr_value) == 0); + + ATF_REQUIRE(curr_value.it_value.tv_sec == 0); + ATF_REQUIRE(curr_value.it_value.tv_nsec == 0); + ATF_REQUIRE(curr_value.it_interval.tv_sec == 0); + ATF_REQUIRE(curr_value.it_interval.tv_nsec == 0); + + struct itimerspec time = { + .it_value.tv_sec = 0, + .it_value.tv_nsec = 100000000, + .it_interval.tv_sec = 0, + .it_interval.tv_nsec = 100000000, + }; + + curr_value = time; + ATF_REQUIRE(timerfd_settime(fd, 0, &time, &curr_value) == 0); + ATF_REQUIRE(curr_value.it_value.tv_sec == 0); + ATF_REQUIRE(curr_value.it_value.tv_nsec == 0); + ATF_REQUIRE(curr_value.it_interval.tv_sec == 0); + ATF_REQUIRE(curr_value.it_interval.tv_nsec == 0); + + ATF_REQUIRE(close(fd) == 0); +} + +ATF_TC_WITHOUT_HEAD(timerfd__simple_blocking_periodic_timer); +ATF_TC_BODY(timerfd__simple_blocking_periodic_timer, tc) +{ + int timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); + + ATF_REQUIRE(timerfd >= 0); + + struct itimerspec time = { + .it_value.tv_sec = 0, + .it_value.tv_nsec = 100000000, + .it_interval.tv_sec = 0, + .it_interval.tv_nsec = 100000000, + }; + + struct timespec b, e; + ATF_REQUIRE(clock_gettime(CLOCK_MONOTONIC, &b) == 0); + + ATF_REQUIRE(timerfd_settime(timerfd, 0, &time, NULL) == 0); + + uint64_t timeouts = 0; + int num_loop_iterations = 0; + + while (timeouts < 3) { + uint64_t timeouts_local; + ATF_REQUIRE( + read(timerfd, &timeouts_local, sizeof(timeouts_local)) == + (ssize_t)sizeof(timeouts_local)); + ATF_REQUIRE(timeouts_local > 0); + + ++num_loop_iterations; + timeouts += timeouts_local; + } + + ATF_REQUIRE(clock_gettime(CLOCK_MONOTONIC, &e) == 0); + timespecsub(&e, &b, &e); + + ATF_REQUIRE((e.tv_sec == 0 && e.tv_nsec >= 300000000) || e.tv_sec > 0); + ATF_REQUIRE(e.tv_sec == 0 && e.tv_nsec >= 300000000 && + e.tv_nsec < 300000000 + TIMER_SLACK); + + ATF_REQUIRE(num_loop_iterations <= 3); + + ATF_REQUIRE(close(timerfd) == 0); +} + +ATF_TC_WITHOUT_HEAD(timerfd__argument_checks); +ATF_TC_BODY(timerfd__argument_checks, tc) +{ + int timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); + ATF_REQUIRE(timerfd >= 0); + + struct itimerspec time = { + .it_value.tv_sec = 0, + .it_value.tv_nsec = 100000000, + .it_interval.tv_sec = 0, + .it_interval.tv_nsec = 100000000, + }; + + ATF_REQUIRE_ERRNO(EFAULT, timerfd_settime(timerfd, 0, NULL, NULL) < 0); + ATF_REQUIRE_ERRNO(EFAULT, timerfd_settime(-2, 0, NULL, NULL) < 0); + ATF_REQUIRE_ERRNO(EBADF, timerfd_settime(-2, 0, &time, NULL) < 0); + ATF_REQUIRE_ERRNO(EFAULT, timerfd_settime(-2, 42, NULL, NULL) < 0); + ATF_REQUIRE_ERRNO(EINVAL, timerfd_settime(-2, 42, &time, NULL) < 0); + ATF_REQUIRE_ERRNO(EINVAL, + timerfd_settime(timerfd, 42, &time, NULL) < 0); + + { + time = (struct itimerspec) { + .it_value.tv_sec = -1, + .it_value.tv_nsec = 100000000, + .it_interval.tv_sec = 0, + .it_interval.tv_nsec = 100000000, + }; + ATF_REQUIRE_ERRNO(EINVAL, + timerfd_settime(timerfd, 0, &time, NULL) < 0); + } + { + time = (struct itimerspec) { + .it_value.tv_sec = 0, + .it_value.tv_nsec = -1, + .it_interval.tv_sec = 0, + .it_interval.tv_nsec = 100000000, + }; + ATF_REQUIRE_ERRNO(EINVAL, + timerfd_settime(timerfd, 0, &time, NULL) < 0); + } + { + time = (struct itimerspec) { + .it_value.tv_sec = 0, + .it_value.tv_nsec = 100000000, + .it_interval.tv_sec = -1, + .it_interval.tv_nsec = 100000000, + }; + ATF_REQUIRE_ERRNO(EINVAL, + timerfd_settime(timerfd, 0, &time, NULL) < 0); + } + { + time = (struct itimerspec) { + .it_value.tv_sec = 0, + .it_value.tv_nsec = 100000000, + .it_interval.tv_sec = 0, + .it_interval.tv_nsec = -1, + }; + ATF_REQUIRE_ERRNO(EINVAL, + timerfd_settime(timerfd, 0, &time, NULL) < 0); + } + { + time = (struct itimerspec) { + .it_value.tv_sec = 0, + .it_value.tv_nsec = 1000000000, + .it_interval.tv_sec = 0, + .it_interval.tv_nsec = 100000000, + }; + ATF_REQUIRE_ERRNO(EINVAL, + timerfd_settime(timerfd, 0, &time, NULL) < 0); + } + { + time = (struct itimerspec) { + .it_value.tv_sec = 0, + .it_value.tv_nsec = 100000000, + .it_interval.tv_sec = 0, + .it_interval.tv_nsec = 1000000000, + }; + ATF_REQUIRE_ERRNO(EINVAL, + timerfd_settime(timerfd, 0, &time, NULL) < 0); + } + + ATF_REQUIRE_ERRNO(EINVAL, + timerfd_create(CLOCK_MONOTONIC | 42, TFD_CLOEXEC)); + ATF_REQUIRE_ERRNO(EINVAL, + timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | 42)); + + ATF_REQUIRE(close(timerfd) == 0); + + struct itimerspec itimerspec; + ATF_REQUIRE_ERRNO(EBADF, timerfd_gettime(timerfd, &itimerspec) < 0); + ATF_REQUIRE_ERRNO(EINVAL, + timerfd_settime(timerfd, 0, &itimerspec, NULL) < 0); +} + +ATF_TC_WITHOUT_HEAD(timerfd__upgrade_simple_to_complex); +ATF_TC_BODY(timerfd__upgrade_simple_to_complex, tc) +{ + int timerfd = timerfd_create(CLOCK_MONOTONIC, /**/ + TFD_CLOEXEC | TFD_NONBLOCK); + + ATF_REQUIRE(timerfd >= 0); + + struct itimerspec time = { + .it_value.tv_sec = 0, + .it_value.tv_nsec = 100000000, + .it_interval.tv_sec = 0, + .it_interval.tv_nsec = 100000000, + }; + + ATF_REQUIRE(timerfd_settime(timerfd, 0, &time, NULL) == 0); + (void)wait_for_timerfd(timerfd); + + time = (struct itimerspec) { + .it_value.tv_sec = 0, + .it_value.tv_nsec = 50000000, + .it_interval.tv_sec = 0, + .it_interval.tv_nsec = 95000000, + }; + + struct timespec b, e; + ATF_REQUIRE(clock_gettime(CLOCK_MONOTONIC, &b) == 0); + + ATF_REQUIRE(timerfd_settime(timerfd, 0, &time, NULL) == 0); + + uint64_t timeouts = wait_for_timerfd(timerfd); + ATF_REQUIRE(timeouts >= 1); + ATF_REQUIRE(timeouts == 1); + + ATF_REQUIRE(clock_gettime(CLOCK_MONOTONIC, &e) == 0); + timespecsub(&e, &b, &e); + ATF_REQUIRE((e.tv_sec == 0 && e.tv_nsec >= 50000000) || e.tv_sec > 0); + ATF_REQUIRE_MSG(e.tv_sec == 0 && e.tv_nsec < 50000000 + TIMER_SLACK, + "%ld", e.tv_nsec); + + timeouts = wait_for_timerfd(timerfd); + ATF_REQUIRE(timeouts >= 1); + ATF_REQUIRE(timeouts == 1); + + ATF_REQUIRE(clock_gettime(CLOCK_MONOTONIC, &e) == 0); + timespecsub(&e, &b, &e); + ATF_REQUIRE((e.tv_sec == 0 && e.tv_nsec >= 145000000) || e.tv_sec > 0); + ATF_REQUIRE(e.tv_sec == 0 && e.tv_nsec >= 145000000 && + e.tv_nsec < 145000000 + TIMER_SLACK); + + ATF_REQUIRE(close(timerfd) == 0); +} + +ATF_TC_WITHOUT_HEAD(timerfd__absolute_timer); +ATF_TC_BODY(timerfd__absolute_timer, tc) +{ + int timerfd = timerfd_create(CLOCK_MONOTONIC, /**/ + TFD_CLOEXEC | TFD_NONBLOCK); + + ATF_REQUIRE(timerfd >= 0); + + struct timespec b, e; + ATF_REQUIRE(clock_gettime(CLOCK_MONOTONIC, &b) == 0); + + struct itimerspec time = { + .it_value = b, + .it_interval.tv_sec = 0, + .it_interval.tv_nsec = 0, + }; + + struct timespec ts_600ms = { + .tv_sec = 0, + .tv_nsec = 600000000, + }; + + timespecadd(&time.it_value, &ts_600ms, &time.it_value); + + ATF_REQUIRE(timerfd_settime(timerfd, /**/ + TFD_TIMER_ABSTIME, &time, NULL) == 0); + + struct pollfd pfd = { .fd = timerfd, .events = POLLIN }; + ATF_REQUIRE(poll(&pfd, 1, -1) == 1); + + // Don't read(2) here! + + ATF_REQUIRE(clock_gettime(CLOCK_MONOTONIC, &e) == 0); + timespecsub(&e, &b, &e); + ATF_REQUIRE(e.tv_sec == 0 && + /* Don't check for this because of spurious wakeups. */ + /* e.tv_nsec >= 600000000 && */ + e.tv_nsec < 600000000 + TIMER_SLACK); + + struct itimerspec zeroed_its = { + .it_value.tv_sec = 0, + .it_value.tv_nsec = 0, + .it_interval.tv_sec = 0, + .it_interval.tv_nsec = 0, + }; + ATF_REQUIRE(timerfd_settime(timerfd, 0, &zeroed_its, NULL) == 0); + + uint64_t timeouts; + ATF_REQUIRE_ERRNO(EAGAIN, + read(timerfd, &timeouts, sizeof(timeouts)) < 0); + + ATF_REQUIRE(poll(&pfd, 1, 0) == 0); + + ATF_REQUIRE(close(timerfd) == 0); +} + +ATF_TC_WITHOUT_HEAD(timerfd__absolute_timer_in_the_past); +ATF_TC_BODY(timerfd__absolute_timer_in_the_past, tc) +{ + int timerfd = timerfd_create(CLOCK_MONOTONIC, /**/ + TFD_CLOEXEC | TFD_NONBLOCK); + + ATF_REQUIRE(timerfd >= 0); + + struct timespec b; + ATF_REQUIRE(clock_gettime(CLOCK_MONOTONIC, &b) == 0); + + { + struct itimerspec time = { + .it_value = b, + .it_interval.tv_sec = 10, + .it_interval.tv_nsec = 0, + }; + time.it_value.tv_sec -= 1; + + ATF_REQUIRE(timerfd_settime(timerfd, /**/ + TFD_TIMER_ABSTIME, &time, NULL) == 0); + + struct pollfd pfd = { .fd = timerfd, .events = POLLIN }; + ATF_REQUIRE(poll(&pfd, 1, 1000) == 1); + } + + { + struct itimerspec time = { + .it_value = b, + .it_interval.tv_sec = 0, + .it_interval.tv_nsec = 10000000, + }; + time.it_value.tv_sec -= 1; + + ATF_REQUIRE(timerfd_settime(timerfd, /**/ + TFD_TIMER_ABSTIME, &time, NULL) == 0); + + struct pollfd pfd = { .fd = timerfd, .events = POLLIN }; + ATF_REQUIRE(poll(&pfd, 1, -1) == 1); + } + + uint64_t timeouts; + ATF_REQUIRE(read(timerfd, &timeouts, sizeof(timeouts)) == + (ssize_t)sizeof(timeouts)); + + ATF_REQUIRE_MSG(timeouts >= 101, "%d", (int)timeouts); + + ATF_REQUIRE(close(timerfd) == 0); +} + +ATF_TC_WITHOUT_HEAD(timerfd__reset_absolute); +ATF_TC_BODY(timerfd__reset_absolute, tc) +{ + int timerfd = timerfd_create(CLOCK_MONOTONIC, /**/ + TFD_CLOEXEC | TFD_NONBLOCK); + + ATF_REQUIRE(timerfd >= 0); + + struct timespec b; + ATF_REQUIRE(clock_gettime(CLOCK_MONOTONIC, &b) == 0); + + { + struct itimerspec time = { + .it_value = b, + }; + time.it_value.tv_sec += 10; + + ATF_REQUIRE(timerfd_settime(timerfd, /**/ + TFD_TIMER_ABSTIME, &time, NULL) == 0); + + struct pollfd pfd = { .fd = timerfd, .events = POLLIN }; + ATF_REQUIRE(poll(&pfd, 1, 100) == 0); + } + + { + struct itimerspec time = { + .it_value = b, + }; + time.it_value.tv_nsec += 500000000; + if (time.it_value.tv_nsec >= 1000000000) { + time.it_value.tv_nsec -= 1000000000; + time.it_value.tv_sec += 1; + } + + ATF_REQUIRE(timerfd_settime(timerfd, /**/ + TFD_TIMER_ABSTIME, &time, NULL) == 0); + + struct pollfd pfd = { .fd = timerfd, .events = POLLIN }; + ATF_REQUIRE(poll(&pfd, 1, 1000) == 1); + } + + uint64_t timeouts; + ATF_REQUIRE(read(timerfd, &timeouts, sizeof(timeouts)) == + (ssize_t)sizeof(timeouts)); + + ATF_REQUIRE_MSG(timeouts == 1, "%d", (int)timeouts); + + ATF_REQUIRE(close(timerfd) == 0); +} + +ATF_TC(timerfd__periodic_timer_performance); +ATF_TC_HEAD(timerfd__periodic_timer_performance, tc) +{ + atf_tc_set_md_var(tc, "timeout", "1"); +} +ATF_TC_BODY(timerfd__periodic_timer_performance, tc) +{ + int timerfd = timerfd_create(CLOCK_MONOTONIC, /**/ + TFD_CLOEXEC | TFD_NONBLOCK); + + ATF_REQUIRE(timerfd >= 0); + + struct itimerspec time = { + .it_value.tv_sec = 0, + .it_value.tv_nsec = 1, + .it_interval.tv_sec = 0, + .it_interval.tv_nsec = 1, + }; + + ATF_REQUIRE(timerfd_settime(timerfd, 0, &time, NULL) == 0); + + usleep(400000); + + struct pollfd pfd = { .fd = timerfd, .events = POLLIN }; + ATF_REQUIRE(poll(&pfd, 1, -1) == 1); + + uint64_t timeouts; + ATF_REQUIRE(read(timerfd, &timeouts, sizeof(timeouts)) == + (ssize_t)sizeof(timeouts)); + ATF_REQUIRE_MSG(timeouts >= 400000000, "%ld", (long)timeouts); + + ATF_REQUIRE(close(timerfd) == 0); +} + +ATF_TC_WITHOUT_HEAD(timerfd__argument_overflow); +ATF_TC_BODY(timerfd__argument_overflow, tc) +{ + int timerfd = timerfd_create(CLOCK_MONOTONIC, /**/ + TFD_CLOEXEC | TFD_NONBLOCK); + ATF_REQUIRE(timerfd >= 0); + { + struct itimerspec time = { + .it_value.tv_sec = 0, + .it_value.tv_nsec = 1, + }; + + ATF_REQUIRE(timerfd_settime(timerfd, /**/ + TFD_TIMER_ABSTIME, &time, NULL) == 0); + + struct pollfd pfd = { .fd = timerfd, .events = POLLIN }; + ATF_REQUIRE(poll(&pfd, 1, -1) == 1); + + uint64_t timeouts; + ATF_REQUIRE(read(timerfd, &timeouts, sizeof(timeouts)) == + (ssize_t)sizeof(timeouts)); + ATF_REQUIRE(timeouts == 1); + + ATF_REQUIRE(read(timerfd, &timeouts, sizeof(timeouts)) < 0); + } + { + struct itimerspec time = { + .it_value.tv_sec = LONG_MAX, + .it_value.tv_nsec = 999999999, + }; + + ATF_REQUIRE(timerfd_settime(timerfd, /**/ + TFD_TIMER_ABSTIME, &time, NULL) == 0); + + struct pollfd pfd = { .fd = timerfd, .events = POLLIN }; + ATF_REQUIRE(poll(&pfd, 1, 500) == 0); + + uint64_t timeouts; + ATF_REQUIRE(read(timerfd, &timeouts, sizeof(timeouts)) < 0); + } + + ATF_REQUIRE(close(timerfd) == 0); +} + +ATF_TC(timerfd__short_evfilt_timer_timeout); +ATF_TC_HEAD(timerfd__short_evfilt_timer_timeout, tc) +{ + atf_tc_set_md_var(tc, "timeout", "30"); +} +ATF_TC_BODY(timerfd__short_evfilt_timer_timeout, tc) +{ + int kq = kqueue(); + ATF_REQUIRE(kq >= 0); + + bool returns_early = false; + + for (int l = 0; l < 10; ++l) { + for (int i = 1; i <= 17; ++i) { + struct kevent kev; + EV_SET(&kev, 0, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 0, i, + 0); + + struct timespec b; + ATF_REQUIRE(clock_gettime(CLOCK_MONOTONIC, &b) == 0); + + ATF_REQUIRE(kevent(kq, &kev, 1, NULL, 0, NULL) == 0); + + ATF_REQUIRE(kevent(kq, NULL, 0, &kev, 1, NULL) == 1); + + struct timespec e; + ATF_REQUIRE(clock_gettime(CLOCK_MONOTONIC, &e) == 0); + + struct timespec diff; + timespecsub(&e, &b, &diff); + + if (diff.tv_sec != 0 || diff.tv_nsec < i * 1000000) { + fprintf(stderr, + "expected: %lldns, got: %lldns\n", + (long long)(i * 1000000LL), + (long long)diff.tv_nsec); + returns_early = true; + goto check; + } + } + } + +check: + ATF_REQUIRE(!returns_early); + + ATF_REQUIRE(close(kq) == 0); + + /* + * timerfd's should never return early, regardless of how + * EVFILT_TIMER behaves. + */ + + int timerfd = timerfd_create(CLOCK_MONOTONIC, /**/ + TFD_CLOEXEC | TFD_NONBLOCK); + + ATF_REQUIRE(timerfd >= 0); + + for (int l = 0; l < 10; ++l) { + for (int i = 1; i <= 17; ++i) { + struct itimerspec time = { + .it_value.tv_sec = 0, + .it_value.tv_nsec = i * 1000000, + }; + + struct timespec b; + ATF_REQUIRE(clock_gettime(CLOCK_MONOTONIC, &b) == 0); + + ATF_REQUIRE( + timerfd_settime(timerfd, 0, &time, NULL) == 0); + (void)wait_for_timerfd(timerfd); + + struct timespec e; + ATF_REQUIRE(clock_gettime(CLOCK_MONOTONIC, &e) == 0); + + struct timespec diff; + timespecsub(&e, &b, &diff); + + ATF_REQUIRE( + diff.tv_sec == 0 && diff.tv_nsec >= i * 1000000); + fprintf(stderr, "%dms, waited %lldns\n", i, + (long long)diff.tv_nsec); + } + } + + ATF_REQUIRE(close(timerfd) == 0); +} + +ATF_TC_WITHOUT_HEAD(timerfd__unmodified_errno); +ATF_TC_BODY(timerfd__unmodified_errno, tc) +{ + ATF_REQUIRE(errno == 0); + int timerfd = timerfd_create(CLOCK_MONOTONIC, /**/ + TFD_CLOEXEC | TFD_NONBLOCK); + ATF_REQUIRE(timerfd >= 0); + ATF_REQUIRE(errno == 0); + + ATF_REQUIRE(timerfd_settime(timerfd, 0, + &(struct itimerspec) { + .it_value.tv_sec = 0, + .it_value.tv_nsec = 100000000, + }, + NULL) == 0); + ATF_REQUIRE(errno == 0); + (void)wait_for_timerfd(timerfd); + ATF_REQUIRE(errno == 0); + + ATF_REQUIRE(timerfd_settime(timerfd, 0, + &(struct itimerspec) { + .it_value.tv_sec = 0, + .it_value.tv_nsec = 0, + }, + NULL) == 0); + ATF_REQUIRE(errno == 0); + + ATF_REQUIRE(timerfd_settime(timerfd, 0, + &(struct itimerspec) { + .it_value.tv_sec = 0, + .it_value.tv_nsec = 0, + }, + NULL) == 0); + ATF_REQUIRE(errno == 0); + + ATF_REQUIRE(close(timerfd) == 0); + ATF_REQUIRE(errno == 0); +} + +ATF_TC_WITHOUT_HEAD(timerfd__reset_to_very_long); +ATF_TC_BODY(timerfd__reset_to_very_long, tc) +{ + ATF_REQUIRE(errno == 0); + int timerfd = timerfd_create(CLOCK_MONOTONIC, /**/ + TFD_CLOEXEC | TFD_NONBLOCK); + ATF_REQUIRE(timerfd >= 0); + ATF_REQUIRE(errno == 0); + + ATF_REQUIRE(timerfd_settime(timerfd, 0, + &(struct itimerspec) { + .it_value.tv_sec = 0, + .it_value.tv_nsec = 100000000, + }, + NULL) == 0); + ATF_REQUIRE(errno == 0); + + ATF_REQUIRE(timerfd_settime(timerfd, 0, + &(struct itimerspec) { + .it_value.tv_sec = 630720000, + .it_value.tv_nsec = 0, + }, + NULL) == 0); + ATF_REQUIRE(errno == 0); + + struct pollfd pfd = { .fd = timerfd, .events = POLLIN }; + ATF_REQUIRE(poll(&pfd, 1, 500) == 0); + uint64_t timeouts; + ssize_t r = read(timerfd, &timeouts, sizeof(timeouts)); + ATF_REQUIRE_ERRNO(EAGAIN, r < 0); + + ATF_REQUIRE(close(timerfd) == 0); + ATF_REQUIRE(errno == EAGAIN); +} + +/* + * Tests requiring root (clock_settime on CLOCK_REALTIME). + * Tests gracefully skip if not running as root. + */ + +static struct timespec current_time; +static void +reset_time(void) +{ + (void)clock_settime(CLOCK_REALTIME, ¤t_time); +} + +static void +clock_settime_or_skip_test(clockid_t clockid, struct timespec const *ts) +{ + int r = clock_settime(clockid, ts); + if (r < 0 && errno == EPERM) { + atf_tc_skip("root required"); + } + ATF_REQUIRE(r == 0); +} + +ATF_TC_WITHOUT_HEAD(timerfd_root__zero_read_on_abs_realtime); +ATF_TC_BODY(timerfd_root__zero_read_on_abs_realtime, tc) +{ + int tfd = timerfd_create(CLOCK_REALTIME, TFD_CLOEXEC); + ATF_REQUIRE(tfd >= 0); + + ATF_REQUIRE(clock_gettime(CLOCK_REALTIME, ¤t_time) == 0); + ATF_REQUIRE(atexit(reset_time) == 0); + + ATF_REQUIRE(timerfd_settime(tfd, TFD_TIMER_ABSTIME, + &(struct itimerspec) { + .it_value = current_time, + .it_interval.tv_sec = 1, + .it_interval.tv_nsec = 0, + }, + NULL) == 0); + + ATF_REQUIRE( + poll(&(struct pollfd) { .fd = tfd, .events = POLLIN }, 1, -1) == 1); + + clock_settime_or_skip_test(CLOCK_REALTIME, + &(struct timespec) { + .tv_sec = current_time.tv_sec - 1, + .tv_nsec = current_time.tv_nsec, + }); + + uint64_t exp; + ssize_t r = read(tfd, &exp, sizeof(exp)); + ATF_REQUIRE_MSG(r == 0, "r: %d, errno: %d", (int)r, errno); + + { + int r = fcntl(tfd, F_GETFL); + ATF_REQUIRE(r >= 0); + r = fcntl(tfd, F_SETFL, r | O_NONBLOCK); + ATF_REQUIRE(r >= 0); + } + + r = read(tfd, &exp, sizeof(exp)); + ATF_REQUIRE_ERRNO(EAGAIN, r < 0); + + current_time.tv_sec += 1; + ATF_REQUIRE(poll(&(struct pollfd) { .fd = tfd, .events = POLLIN }, 1, + 1800) == 1); + r = read(tfd, &exp, sizeof(exp)); + ATF_REQUIRE(r == (ssize_t)sizeof(exp)); + ATF_REQUIRE(exp == 1); + + ATF_REQUIRE(close(tfd) == 0); +} + +ATF_TC_WITHOUT_HEAD(timerfd_root__read_on_abs_realtime_no_interval); +ATF_TC_BODY(timerfd_root__read_on_abs_realtime_no_interval, tc) +{ + int tfd = timerfd_create(CLOCK_REALTIME, TFD_CLOEXEC); + ATF_REQUIRE(tfd >= 0); + + ATF_REQUIRE(clock_gettime(CLOCK_REALTIME, ¤t_time) == 0); + ATF_REQUIRE(atexit(reset_time) == 0); + + ATF_REQUIRE(timerfd_settime(tfd, TFD_TIMER_ABSTIME, + &(struct itimerspec) { + .it_value = current_time, + .it_interval.tv_sec = 0, + .it_interval.tv_nsec = 0, + }, + NULL) == 0); + + ATF_REQUIRE( + poll(&(struct pollfd) { .fd = tfd, .events = POLLIN }, 1, -1) == 1); + + clock_settime_or_skip_test(CLOCK_REALTIME, + &(struct timespec) { + .tv_sec = current_time.tv_sec - 1, + .tv_nsec = current_time.tv_nsec, + }); + + uint64_t exp; + ssize_t r = read(tfd, &exp, sizeof(exp)); + ATF_REQUIRE(r == (ssize_t)sizeof(exp)); + ATF_REQUIRE(exp == 1); + + ATF_REQUIRE(close(tfd) == 0); +} + +ATF_TC_WITHOUT_HEAD(timerfd_root__cancel_on_set); +ATF_TC_BODY(timerfd_root__cancel_on_set, tc) +{ + int tfd = timerfd_create(CLOCK_REALTIME, TFD_CLOEXEC); + ATF_REQUIRE(tfd >= 0); + + ATF_REQUIRE(clock_gettime(CLOCK_REALTIME, ¤t_time) == 0); + ATF_REQUIRE(atexit(reset_time) == 0); + + ATF_REQUIRE( + timerfd_settime(tfd, TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, + &(struct itimerspec) { + .it_value.tv_sec = current_time.tv_sec + 10, + .it_value.tv_nsec = current_time.tv_nsec, + .it_interval.tv_sec = 0, + .it_interval.tv_nsec = 0, + }, + NULL) == 0); + + clock_settime_or_skip_test(CLOCK_REALTIME, ¤t_time); + + ATF_REQUIRE( + poll(&(struct pollfd) { .fd = tfd, .events = POLLIN }, 1, -1) == 1); + + { + int r = timerfd_settime(tfd, + TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, + &(struct itimerspec) { + .it_value.tv_sec = current_time.tv_sec, + .it_value.tv_nsec = current_time.tv_nsec, + .it_interval.tv_sec = 0, + .it_interval.tv_nsec = 0, + }, + NULL); + ATF_REQUIRE_ERRNO(ECANCELED, r < 0); + } + + ATF_REQUIRE(poll(&(struct pollfd) { .fd = tfd, .events = POLLIN }, 1, + 800) == 1); + + uint64_t exp; + ssize_t r; + + r = read(tfd, &exp, sizeof(exp)); + ATF_REQUIRE(r == (ssize_t)sizeof(exp)); + ATF_REQUIRE(exp == 1); + + ATF_REQUIRE( + timerfd_settime(tfd, TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, + &(struct itimerspec) { + .it_value.tv_sec = current_time.tv_sec + 1, + .it_value.tv_nsec = current_time.tv_nsec, + .it_interval.tv_sec = 1, + .it_interval.tv_nsec = 0, + }, + NULL) == 0); + + clock_settime_or_skip_test(CLOCK_REALTIME, ¤t_time); + + ATF_REQUIRE( + poll(&(struct pollfd) { .fd = tfd, .events = POLLIN }, 1, -1) == 1); + + r = read(tfd, &exp, sizeof(exp)); + ATF_REQUIRE_ERRNO(ECANCELED, r < 0); + + r = read(tfd, &exp, sizeof(exp)); + current_time.tv_sec += 1; + ATF_REQUIRE_MSG(r == (ssize_t)sizeof(exp), "%d %d", (int)r, errno); + ATF_REQUIRE(exp == 1); + + ATF_REQUIRE( + timerfd_settime(tfd, TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, + &(struct itimerspec) { + .it_value.tv_sec = current_time.tv_sec + 1, + .it_value.tv_nsec = current_time.tv_nsec, + .it_interval.tv_sec = 1, + .it_interval.tv_nsec = 0, + }, + NULL) == 0); + + clock_settime_or_skip_test(CLOCK_REALTIME, ¤t_time); + current_time.tv_sec += 2; + ATF_REQUIRE(nanosleep(&(struct timespec) { .tv_sec = 2 }, NULL) == 0); + + r = read(tfd, &exp, sizeof(exp)); + ATF_REQUIRE_ERRNO(ECANCELED, r < 0); + + r = poll(&(struct pollfd) { .fd = tfd, .events = POLLIN }, 1, 3000); + ATF_REQUIRE(r == 0); + current_time.tv_sec += 3; + + ATF_REQUIRE(close(tfd) == 0); +} + +ATF_TC_WITHOUT_HEAD(timerfd_root__cancel_on_set_init); +ATF_TC_BODY(timerfd_root__cancel_on_set_init, tc) +{ + int tfd = timerfd_create(CLOCK_REALTIME, TFD_CLOEXEC); + ATF_REQUIRE(tfd >= 0); + + ATF_REQUIRE(clock_gettime(CLOCK_REALTIME, ¤t_time) == 0); + ATF_REQUIRE(atexit(reset_time) == 0); + + clock_settime_or_skip_test(CLOCK_REALTIME, ¤t_time); + + ATF_REQUIRE( + timerfd_settime(tfd, TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, + &(struct itimerspec) { + .it_value.tv_sec = current_time.tv_sec + 10, + .it_value.tv_nsec = current_time.tv_nsec, + .it_interval.tv_sec = 0, + .it_interval.tv_nsec = 0, + }, + NULL) == 0); + + clock_settime_or_skip_test(CLOCK_REALTIME, ¤t_time); + + int r = timerfd_settime(tfd, + TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, + &(struct itimerspec) { + .it_value.tv_sec = current_time.tv_sec + 10, + .it_value.tv_nsec = current_time.tv_nsec, + .it_interval.tv_sec = 0, + .it_interval.tv_nsec = 0, + }, + NULL); + ATF_REQUIRE_ERRNO(ECANCELED, r < 0); + ATF_REQUIRE(close(tfd) == 0); +} + +static void * +clock_change_thread(void *arg) +{ + (void)arg; + + fprintf(stderr, "clock change\n"); + clock_settime_or_skip_test(CLOCK_REALTIME, ¤t_time); + + current_time.tv_sec += 2; + ATF_REQUIRE(nanosleep(&(struct timespec) { .tv_sec = 2 }, NULL) == 0); + + fprintf(stderr, "clock change\n"); + clock_settime_or_skip_test(CLOCK_REALTIME, ¤t_time); + + return NULL; +} + +ATF_TC(timerfd_root__clock_change_notification); +ATF_TC_HEAD(timerfd_root__clock_change_notification, tc) +{ + atf_tc_set_md_var(tc, "timeout", "10"); +} +ATF_TC_BODY(timerfd_root__clock_change_notification, tc) +{ + ATF_REQUIRE(clock_gettime(CLOCK_REALTIME, ¤t_time) == 0); + ATF_REQUIRE(atexit(reset_time) == 0); + + clock_settime_or_skip_test(CLOCK_REALTIME, ¤t_time); + +#define TIME_T_MAX (time_t)((UINTMAX_C(1) << ((sizeof(time_t) << 3) - 1)) - 1) + struct itimerspec its = { + .it_value.tv_sec = TIME_T_MAX, + }; +#undef TIME_T_MAX + + int tfd = timerfd_create(CLOCK_REALTIME, TFD_CLOEXEC); + ATF_REQUIRE(tfd >= 0); + + ATF_REQUIRE( + timerfd_settime(tfd, TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, + &its, NULL) == 0); + + pthread_t clock_changer; + ATF_REQUIRE(pthread_create(&clock_changer, NULL, /**/ + clock_change_thread, NULL) == 0); + + uint64_t exp; + ssize_t r; + + r = read(tfd, &exp, sizeof(exp)); + ATF_REQUIRE_ERRNO(ECANCELED, r < 0); + fprintf(stderr, "clock change detected\n"); + + r = read(tfd, &exp, sizeof(exp)); + ATF_REQUIRE_ERRNO(ECANCELED, r < 0); + fprintf(stderr, "clock change detected\n"); + + ATF_REQUIRE(pthread_join(clock_changer, NULL) == 0); + + ATF_REQUIRE(close(tfd) == 0); +} + +ATF_TC_WITHOUT_HEAD(timerfd_root__advance_time_no_cancel); +ATF_TC_BODY(timerfd_root__advance_time_no_cancel, tc) +{ + int tfd = timerfd_create(CLOCK_REALTIME, TFD_CLOEXEC); + ATF_REQUIRE(tfd >= 0); + + ATF_REQUIRE(clock_gettime(CLOCK_REALTIME, ¤t_time) == 0); + ATF_REQUIRE(atexit(reset_time) == 0); + + ATF_REQUIRE(timerfd_settime(tfd, TFD_TIMER_ABSTIME, + &(struct itimerspec) { + .it_value.tv_sec = current_time.tv_sec + 10, + .it_value.tv_nsec = current_time.tv_nsec, + .it_interval.tv_sec = 0, + .it_interval.tv_nsec = 0, + }, + NULL) == 0); + + current_time.tv_sec += 9; + clock_settime_or_skip_test(CLOCK_REALTIME, ¤t_time); + current_time.tv_sec -= 8; + + { + int r = poll(&(struct pollfd) { .fd = tfd, .events = POLLIN }, + 1, 1800); + ATF_REQUIRE(r == 1); + } + + uint64_t exp; + ssize_t r; + + r = read(tfd, &exp, sizeof(exp)); + ATF_REQUIRE(r == (ssize_t)sizeof(exp)); + ATF_REQUIRE(exp == 1); + + ATF_REQUIRE(close(tfd) == 0); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, timerfd__many_timers); + ATF_TP_ADD_TC(tp, timerfd__simple_timer); + ATF_TP_ADD_TC(tp, timerfd__simple_periodic_timer); + ATF_TP_ADD_TC(tp, timerfd__complex_periodic_timer); + ATF_TP_ADD_TC(tp, timerfd__reset_periodic_timer); + ATF_TP_ADD_TC(tp, timerfd__reenable_periodic_timer); + ATF_TP_ADD_TC(tp, timerfd__expire_five); + ATF_TP_ADD_TC(tp, timerfd__simple_gettime); + ATF_TP_ADD_TC(tp, timerfd__simple_blocking_periodic_timer); + ATF_TP_ADD_TC(tp, timerfd__argument_checks); + ATF_TP_ADD_TC(tp, timerfd__upgrade_simple_to_complex); + ATF_TP_ADD_TC(tp, timerfd__absolute_timer); + ATF_TP_ADD_TC(tp, timerfd__absolute_timer_in_the_past); + ATF_TP_ADD_TC(tp, timerfd__reset_absolute); + ATF_TP_ADD_TC(tp, timerfd__periodic_timer_performance); + ATF_TP_ADD_TC(tp, timerfd__argument_overflow); + ATF_TP_ADD_TC(tp, timerfd__short_evfilt_timer_timeout); + ATF_TP_ADD_TC(tp, timerfd__unmodified_errno); + ATF_TP_ADD_TC(tp, timerfd__reset_to_very_long); + + ATF_TP_ADD_TC(tp, timerfd_root__zero_read_on_abs_realtime); + ATF_TP_ADD_TC(tp, timerfd_root__read_on_abs_realtime_no_interval); + ATF_TP_ADD_TC(tp, timerfd_root__cancel_on_set); + ATF_TP_ADD_TC(tp, timerfd_root__cancel_on_set_init); + ATF_TP_ADD_TC(tp, timerfd_root__clock_change_notification); + ATF_TP_ADD_TC(tp, timerfd_root__advance_time_no_cancel); + + return atf_no_error(); +} |
