From nobody Fri Mar 20 07:16:13 2026 X-Original-To: dev-commits-src-main@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4fcYl152CJz6VVXY for ; Fri, 20 Mar 2026 07:16:13 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R12" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4fcYl13JFyz4LGv for ; Fri, 20 Mar 2026 07:16:13 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1773990973; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=lBTKmthTedgMfW9HRY4+UVCvjx1Od1+n02GNtNNVIOU=; b=MSgrdf15A5SE0EfkXCwxm3wFqp8WLlFb39TFiAbOvwAAYmNS4S0n7MLoVkrnqBysR+bKJe 6ub4D9GCsU9rdGD/Ic/6DZNMP7zwPuOwPnHEq8RZMuG070OgyQOgJ/rwBZMiZHMtdxj4g5 gjtRDwLwBjUrbiUydcWfdFVOjKRxNPLHQGFCWmCqD3j6P/g2Mge29rsb8BrIWXGZX3+bqD bpAYT6Bxo5TLtn2lLBmp8wpIj9STsbruE82qJs6DWmIAZLAwWI3I9dV5l2YlVs/mzTrKiS n+guJ/xJzpNfRtynRBOhIzZEV5jPLYCamy0sPDhM/2NZOrxIJHu/JFs6i+0Evg== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1773990973; a=rsa-sha256; cv=none; b=taB0BUPEEkKo9krRtNl+RdLC49YkmZRuhYy2DNJUcw0yFdUJzpfoaadKCbViaBXRD5tots 8cpXO3e8z/1wOsRYFumVZa5FL6vQdMR2Cg1kAo6tAZULpm5GLZ9EaYREd1oeoGiBjEnB1F UTWXmghMkucfg9ZL2sPD8rytnuNecu67h2a3gJbDqWUCchQA1sc8gXFWPzK/BnhwtHBo2N DTRNx6OfAbLQb2IuPtbj8q7U9is/eXVyxyzOehYQn5hzIOrZfgvS4FSIqirLXn6hbPMTgQ RdMVZ3mWbFm6HMArAmR/hQXUGNMOVJulkvXYHjau2FFv5uDum9s5NH9j1QAwKA== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1773990973; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=lBTKmthTedgMfW9HRY4+UVCvjx1Od1+n02GNtNNVIOU=; b=vz9mUh/tNhUOMmOTnOtT3yMyhoGeeFSCu6lstwxZso3k6QBfZ3/R6YTCitNvywX7rjr5b7 O21K2+4qo7aWM8BFl9VdCmZyW+F+LK19FMoPqvXNEbzo+eUvzk6aXXNlSIZWQu7iGOQ4jj jsng+iX53IK8S+w49/KtCgIijFKO87cTZ45TuRx/fBplqjnTKVBe6fe0bi0jWI2JSR7BKK 3iQMcbdw5JD/xsv3hQZgFpoAwtGDuaFLNnkP03D/ri2UTv4QVN7gY7I6KzNeaQUHacMurF I2zYmREoJkj7qRm4Gh/R9D5OE9AaTLR8jvgMFXd0CCPUsop8GpmL0p2mdLbAFw== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4fcYl12X9fz1F5 for ; Fri, 20 Mar 2026 07:16:13 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 3cff9 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Fri, 20 Mar 2026 07:16:13 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Jake Freeland Subject: git: 834c1ba793d9 - main - timerfd: Add tests List-Id: Commit messages for the main branch of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-main List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-main@freebsd.org Sender: owner-dev-commits-src-main@FreeBSD.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: jfree X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 834c1ba793d900b791e19ff449b1e0b96fb494a0 Auto-Submitted: auto-generated Date: Fri, 20 Mar 2026 07:16:13 +0000 Message-Id: <69bcf43d.3cff9.4a9cb230@gitrepo.freebsd.org> The branch main has been updated by jfree: URL: https://cgit.FreeBSD.org/src/commit/?id=834c1ba793d900b791e19ff449b1e0b96fb494a0 commit 834c1ba793d900b791e19ff449b1e0b96fb494a0 Author: Jake Freeland AuthorDate: 2026-03-20 06:31:30 +0000 Commit: Jake Freeland CommitDate: 2026-03-20 07:15:08 +0000 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 --- tests/sys/kern/Makefile | 3 + tests/sys/kern/timerfd.c | 1318 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1321 insertions(+) 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 + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +/* 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; *** 374 LINES SKIPPED ***