diff --git a/sys/kern/sys_timerfd.c b/sys/kern/sys_timerfd.c index 565ab3ad6ee6..236dfe8bb96a 100644 --- a/sys/kern/sys_timerfd.c +++ b/sys/kern/sys_timerfd.c @@ -1,608 +1,610 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2014 Dmitry Chagin * Copyright (c) 2023 Jake Freeland * * 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 AUTHOR 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 AUTHOR 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static MALLOC_DEFINE(M_TIMERFD, "timerfd", "timerfd structures"); static struct mtx timerfd_list_lock; static LIST_HEAD(, timerfd) timerfd_list; MTX_SYSINIT(timerfd, &timerfd_list_lock, "timerfd_list_lock", MTX_DEF); static struct unrhdr64 tfdino_unr; #define TFD_NOJUMP 0 /* Realtime clock has not jumped. */ #define TFD_READ 1 /* Jumped, tfd has been read since. */ #define TFD_ZREAD 2 /* Jumped backwards, CANCEL_ON_SET=false. */ #define TFD_CANCELED 4 /* Jumped, CANCEL_ON_SET=true. */ #define TFD_JUMPED (TFD_ZREAD | TFD_CANCELED) /* * One structure allocated per timerfd descriptor. * * Locking semantics: * (t) locked by tfd_lock mtx * (l) locked by timerfd_list_lock sx * (c) const until freeing */ struct timerfd { /* User specified. */ struct itimerspec tfd_time; /* (t) tfd timer */ clockid_t tfd_clockid; /* (c) timing base */ int tfd_flags; /* (c) creation flags */ int tfd_timflags; /* (t) timer flags */ /* Used internally. */ timerfd_t tfd_count; /* (t) expiration count since read */ bool tfd_expired; /* (t) true upon initial expiration */ struct mtx tfd_lock; /* tfd mtx lock */ struct callout tfd_callout; /* (t) expiration notification */ struct selinfo tfd_sel; /* (t) I/O alerts */ struct timespec tfd_boottim; /* (t) cached boottime */ int tfd_jumped; /* (t) timer jump status */ LIST_ENTRY(timerfd) entry; /* (l) entry in list */ /* For stat(2). */ ino_t tfd_ino; /* (c) inode number */ struct timespec tfd_atim; /* (t) time of last read */ struct timespec tfd_mtim; /* (t) time of last settime */ struct timespec tfd_birthtim; /* (c) creation time */ }; static void timerfd_init(void *data) { new_unrhdr64(&tfdino_unr, 1); } SYSINIT(timerfd, SI_SUB_VFS, SI_ORDER_ANY, timerfd_init, NULL); static inline void timerfd_getboottime(struct timespec *ts) { struct timeval tv; getboottime(&tv); TIMEVAL_TO_TIMESPEC(&tv, ts); } /* * Call when a discontinuous jump has occured in CLOCK_REALTIME and * update timerfd's cached boottime. A jump can be triggered using * functions like clock_settime(2) or settimeofday(2). * * Timer is marked TFD_CANCELED if TFD_TIMER_CANCEL_ON_SET is set * and the realtime clock jumps. * Timer is marked TFD_ZREAD if TFD_TIMER_CANCEL_ON_SET is not set, * but the realtime clock jumps backwards. */ void timerfd_jumped(void) { struct timerfd *tfd; struct timespec boottime, diff; if (LIST_EMPTY(&timerfd_list)) return; timerfd_getboottime(&boottime); mtx_lock(&timerfd_list_lock); LIST_FOREACH(tfd, &timerfd_list, entry) { mtx_lock(&tfd->tfd_lock); if (tfd->tfd_clockid != CLOCK_REALTIME || (tfd->tfd_timflags & TFD_TIMER_ABSTIME) == 0 || timespeccmp(&boottime, &tfd->tfd_boottim, ==)) { mtx_unlock(&tfd->tfd_lock); continue; } if (callout_active(&tfd->tfd_callout)) { if ((tfd->tfd_timflags & TFD_TIMER_CANCEL_ON_SET) != 0) tfd->tfd_jumped = TFD_CANCELED; else if (timespeccmp(&boottime, &tfd->tfd_boottim, <)) tfd->tfd_jumped = TFD_ZREAD; /* * Do not reschedule callout when * inside interval time loop. */ if (!tfd->tfd_expired) { timespecsub(&boottime, &tfd->tfd_boottim, &diff); timespecsub(&tfd->tfd_time.it_value, &diff, &tfd->tfd_time.it_value); if (callout_stop(&tfd->tfd_callout) == 1) { callout_schedule_sbt(&tfd->tfd_callout, tstosbt(tfd->tfd_time.it_value), 0, C_ABSOLUTE); } } } tfd->tfd_boottim = boottime; mtx_unlock(&tfd->tfd_lock); } mtx_unlock(&timerfd_list_lock); } static int timerfd_read(struct file *fp, struct uio *uio, struct ucred *active_cred, int flags, struct thread *td) { struct timerfd *tfd = fp->f_data; timerfd_t count; int error = 0; if (uio->uio_resid < sizeof(timerfd_t)) return (EINVAL); mtx_lock(&tfd->tfd_lock); retry: getnanotime(&tfd->tfd_atim); if ((tfd->tfd_jumped & TFD_JUMPED) != 0) { if (tfd->tfd_jumped == TFD_CANCELED) error = ECANCELED; tfd->tfd_jumped = TFD_READ; tfd->tfd_count = 0; mtx_unlock(&tfd->tfd_lock); return (error); } else { tfd->tfd_jumped = TFD_NOJUMP; } if (tfd->tfd_count == 0) { if ((fp->f_flag & FNONBLOCK) != 0) { mtx_unlock(&tfd->tfd_lock); return (EAGAIN); } error = mtx_sleep(&tfd->tfd_count, &tfd->tfd_lock, PCATCH, "tfdrd", 0); if (error == 0) { goto retry; } else { mtx_unlock(&tfd->tfd_lock); return (error); } } count = tfd->tfd_count; tfd->tfd_count = 0; mtx_unlock(&tfd->tfd_lock); error = uiomove(&count, sizeof(timerfd_t), uio); return (error); } static int timerfd_ioctl(struct file *fp, u_long cmd, void *data, struct ucred *active_cred, struct thread *td) { switch (cmd) { case FIOASYNC: if (*(int *)data != 0) atomic_set_int(&fp->f_flag, FASYNC); else atomic_clear_int(&fp->f_flag, FASYNC); return (0); case FIONBIO: if (*(int *)data != 0) atomic_set_int(&fp->f_flag, FNONBLOCK); else atomic_clear_int(&fp->f_flag, FNONBLOCK); return (0); } return (ENOTTY); } static int timerfd_poll(struct file *fp, int events, struct ucred *active_cred, struct thread *td) { struct timerfd *tfd = fp->f_data; int revents = 0; mtx_lock(&tfd->tfd_lock); if ((events & (POLLIN | POLLRDNORM)) != 0 && tfd->tfd_count > 0 && tfd->tfd_jumped != TFD_READ) revents |= events & (POLLIN | POLLRDNORM); if (revents == 0) selrecord(td, &tfd->tfd_sel); mtx_unlock(&tfd->tfd_lock); return (revents); } static void filt_timerfddetach(struct knote *kn) { struct timerfd *tfd = kn->kn_hook; mtx_lock(&tfd->tfd_lock); knlist_remove(&tfd->tfd_sel.si_note, kn, 1); mtx_unlock(&tfd->tfd_lock); } static int filt_timerfdread(struct knote *kn, long hint) { struct timerfd *tfd = kn->kn_hook; mtx_assert(&tfd->tfd_lock, MA_OWNED); kn->kn_data = (int64_t)tfd->tfd_count; return (tfd->tfd_count > 0); } static const struct filterops timerfd_rfiltops = { .f_isfd = 1, .f_detach = filt_timerfddetach, .f_event = filt_timerfdread, }; static int timerfd_kqfilter(struct file *fp, struct knote *kn) { struct timerfd *tfd = fp->f_data; if (kn->kn_filter != EVFILT_READ) return (EINVAL); kn->kn_fop = &timerfd_rfiltops; kn->kn_hook = tfd; knlist_add(&tfd->tfd_sel.si_note, kn, 0); return (0); } static int timerfd_stat(struct file *fp, struct stat *sb, struct ucred *active_cred) { struct timerfd *tfd = fp->f_data; bzero(sb, sizeof(*sb)); sb->st_nlink = fp->f_count - 1; sb->st_uid = fp->f_cred->cr_uid; sb->st_gid = fp->f_cred->cr_gid; sb->st_blksize = PAGE_SIZE; mtx_lock(&tfd->tfd_lock); sb->st_atim = tfd->tfd_atim; sb->st_mtim = tfd->tfd_mtim; mtx_unlock(&tfd->tfd_lock); sb->st_ctim = sb->st_mtim; sb->st_ino = tfd->tfd_ino; sb->st_birthtim = tfd->tfd_birthtim; return (0); } static int timerfd_close(struct file *fp, struct thread *td) { struct timerfd *tfd = fp->f_data; mtx_lock(&timerfd_list_lock); LIST_REMOVE(tfd, entry); mtx_unlock(&timerfd_list_lock); callout_drain(&tfd->tfd_callout); seldrain(&tfd->tfd_sel); knlist_destroy(&tfd->tfd_sel.si_note); mtx_destroy(&tfd->tfd_lock); free(tfd, M_TIMERFD); fp->f_ops = &badfileops; return (0); } static int timerfd_fill_kinfo(struct file *fp, struct kinfo_file *kif, struct filedesc *fdp) { struct timerfd *tfd = fp->f_data; kif->kf_type = KF_TYPE_TIMERFD; kif->kf_un.kf_timerfd.kf_timerfd_clockid = tfd->tfd_clockid; kif->kf_un.kf_timerfd.kf_timerfd_flags = tfd->tfd_flags; kif->kf_un.kf_timerfd.kf_timerfd_addr = (uintptr_t)tfd; return (0); } static const struct fileops timerfdops = { .fo_read = timerfd_read, .fo_write = invfo_rdwr, .fo_truncate = invfo_truncate, .fo_ioctl = timerfd_ioctl, .fo_poll = timerfd_poll, .fo_kqfilter = timerfd_kqfilter, .fo_stat = timerfd_stat, .fo_close = timerfd_close, .fo_chmod = invfo_chmod, .fo_chown = invfo_chown, .fo_sendfile = invfo_sendfile, .fo_fill_kinfo = timerfd_fill_kinfo, .fo_cmp = file_kcmp_generic, .fo_flags = DFLAG_PASSABLE, }; static void timerfd_curval(struct timerfd *tfd, struct itimerspec *old_value) { struct timespec curr_value; mtx_assert(&tfd->tfd_lock, MA_OWNED); *old_value = tfd->tfd_time; if (timespecisset(&tfd->tfd_time.it_value)) { nanouptime(&curr_value); timespecsub(&tfd->tfd_time.it_value, &curr_value, &old_value->it_value); } } static void timerfd_expire(void *arg) { struct timerfd *tfd = (struct timerfd *)arg; - struct timespec uptime; + sbintime_t exp, interval, now, next, diff; ++tfd->tfd_count; tfd->tfd_expired = true; if (timespecisset(&tfd->tfd_time.it_interval)) { + exp = tstosbt(tfd->tfd_time.it_value); + interval = tstosbt(tfd->tfd_time.it_interval); + now = sbinuptime(); + next = now + interval; + /* Count missed events. */ - nanouptime(&uptime); - if (timespeccmp(&uptime, &tfd->tfd_time.it_value, >)) { - timespecsub(&uptime, &tfd->tfd_time.it_value, &uptime); - tfd->tfd_count += tstosbt(uptime) / - tstosbt(tfd->tfd_time.it_interval); + if (now > exp) { + diff = now - exp; + tfd->tfd_count += diff / interval; + next -= diff % interval; } - timespecadd(&tfd->tfd_time.it_value, - &tfd->tfd_time.it_interval, &tfd->tfd_time.it_value); - callout_schedule_sbt(&tfd->tfd_callout, - tstosbt(tfd->tfd_time.it_value), - 0, C_ABSOLUTE); + + callout_schedule_sbt(&tfd->tfd_callout, next, 0, C_ABSOLUTE); + tfd->tfd_time.it_value = sbttots(next); } else { /* Single shot timer. */ callout_deactivate(&tfd->tfd_callout); timespecclear(&tfd->tfd_time.it_value); } wakeup(&tfd->tfd_count); selwakeup(&tfd->tfd_sel); KNOTE_LOCKED(&tfd->tfd_sel.si_note, 0); } int kern_timerfd_create(struct thread *td, int clockid, int flags) { struct file *fp; struct timerfd *tfd; int error, fd, fflags; AUDIT_ARG_VALUE(clockid); AUDIT_ARG_FFLAGS(flags); switch (clockid) { case CLOCK_REALTIME: /* FALLTHROUGH */ case CLOCK_MONOTONIC: /* FALLTHROUGH */ case CLOCK_UPTIME: /* * CLOCK_BOOTTIME should be added once different from * CLOCK_UPTIME */ break; default: return (EINVAL); } if ((flags & ~(TFD_CLOEXEC | TFD_NONBLOCK)) != 0) return (EINVAL); fflags = FREAD; if ((flags & TFD_CLOEXEC) != 0) fflags |= O_CLOEXEC; if ((flags & TFD_NONBLOCK) != 0) fflags |= FNONBLOCK; error = falloc(td, &fp, &fd, fflags); if (error != 0) return (error); tfd = malloc(sizeof(*tfd), M_TIMERFD, M_WAITOK | M_ZERO); tfd->tfd_clockid = (clockid_t)clockid; tfd->tfd_flags = flags; tfd->tfd_ino = alloc_unr64(&tfdino_unr); mtx_init(&tfd->tfd_lock, "timerfd", NULL, MTX_DEF); callout_init_mtx(&tfd->tfd_callout, &tfd->tfd_lock, 0); knlist_init_mtx(&tfd->tfd_sel.si_note, &tfd->tfd_lock); timerfd_getboottime(&tfd->tfd_boottim); getnanotime(&tfd->tfd_birthtim); mtx_lock(&timerfd_list_lock); LIST_INSERT_HEAD(&timerfd_list, tfd, entry); mtx_unlock(&timerfd_list_lock); finit(fp, fflags, DTYPE_TIMERFD, tfd, &timerfdops); fdrop(fp, td); td->td_retval[0] = fd; return (0); } int kern_timerfd_gettime(struct thread *td, int fd, struct itimerspec *curr_value) { struct file *fp; struct timerfd *tfd; int error; error = fget(td, fd, &cap_write_rights, &fp); if (error != 0) return (error); if (fp->f_type != DTYPE_TIMERFD) { fdrop(fp, td); return (EINVAL); } tfd = fp->f_data; mtx_lock(&tfd->tfd_lock); timerfd_curval(tfd, curr_value); mtx_unlock(&tfd->tfd_lock); fdrop(fp, td); return (0); } int kern_timerfd_settime(struct thread *td, int fd, int flags, const struct itimerspec *new_value, struct itimerspec *old_value) { struct file *fp; struct timerfd *tfd; struct timespec ts; int error = 0; if ((flags & ~(TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET)) != 0) return (EINVAL); if (!timespecvalid_interval(&new_value->it_value) || !timespecvalid_interval(&new_value->it_interval)) return (EINVAL); error = fget(td, fd, &cap_write_rights, &fp); if (error != 0) return (error); if (fp->f_type != DTYPE_TIMERFD) { fdrop(fp, td); return (EINVAL); } tfd = fp->f_data; mtx_lock(&tfd->tfd_lock); getnanotime(&tfd->tfd_mtim); tfd->tfd_timflags = flags; /* Store old itimerspec, if applicable. */ if (old_value != NULL) timerfd_curval(tfd, old_value); /* Set new expiration. */ tfd->tfd_time = *new_value; if (timespecisset(&tfd->tfd_time.it_value)) { if ((flags & TFD_TIMER_ABSTIME) == 0) { nanouptime(&ts); timespecadd(&tfd->tfd_time.it_value, &ts, &tfd->tfd_time.it_value); } else if (tfd->tfd_clockid == CLOCK_REALTIME) { /* ECANCELED if unread jump is pending. */ if (tfd->tfd_jumped == TFD_CANCELED) error = ECANCELED; /* Convert from CLOCK_REALTIME to CLOCK_BOOTTIME. */ timespecsub(&tfd->tfd_time.it_value, &tfd->tfd_boottim, &tfd->tfd_time.it_value); } callout_reset_sbt(&tfd->tfd_callout, tstosbt(tfd->tfd_time.it_value), 0, timerfd_expire, tfd, C_ABSOLUTE); } else { callout_stop(&tfd->tfd_callout); } tfd->tfd_count = 0; tfd->tfd_expired = false; tfd->tfd_jumped = TFD_NOJUMP; mtx_unlock(&tfd->tfd_lock); fdrop(fp, td); return (error); } int sys_timerfd_create(struct thread *td, struct timerfd_create_args *uap) { return (kern_timerfd_create(td, uap->clockid, uap->flags)); } int sys_timerfd_gettime(struct thread *td, struct timerfd_gettime_args *uap) { struct itimerspec curr_value; int error; error = kern_timerfd_gettime(td, uap->fd, &curr_value); if (error == 0) error = copyout(&curr_value, uap->curr_value, sizeof(curr_value)); return (error); } int sys_timerfd_settime(struct thread *td, struct timerfd_settime_args *uap) { struct itimerspec new_value, old_value; int error; error = copyin(uap->new_value, &new_value, sizeof(new_value)); if (error != 0) return (error); if (uap->old_value == NULL) { error = kern_timerfd_settime(td, uap->fd, uap->flags, &new_value, NULL); } else { error = kern_timerfd_settime(td, uap->fd, uap->flags, &new_value, &old_value); if (error == 0) error = copyout(&old_value, uap->old_value, sizeof(old_value)); } return (error); } diff --git a/tests/sys/kern/timerfd.c b/tests/sys/kern/timerfd.c index 37cb4924faf1..b24d093b346e 100644 --- a/tests/sys/kern/timerfd.c +++ b/tests/sys/kern/timerfd.c @@ -1,1318 +1,1347 @@ /*- * 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; ssize_t r = read(timerfd, &timeouts, sizeof(timeouts)); ATF_REQUIRE_ERRNO(EAGAIN, r < 0); ATF_REQUIRE(close(timerfd) == 0); ATF_REQUIRE(errno == EAGAIN); } +ATF_TC_WITHOUT_HEAD(timerfd__missed_events); +ATF_TC_BODY(timerfd__missed_events, tc) +{ + struct itimerspec its = { }; + uint64_t timeouts; + int timerfd; + + timerfd = timerfd_create(CLOCK_REALTIME, TFD_CLOEXEC); + ATF_REQUIRE(timerfd >= 0); + + ATF_REQUIRE(clock_gettime(CLOCK_REALTIME, &its.it_value) == 0); + its.it_value.tv_sec -= 1000; + its.it_interval.tv_sec = 1; + + ATF_REQUIRE(timerfd_settime(timerfd, TFD_TIMER_ABSTIME, &its, + NULL) == 0); + + ATF_REQUIRE(read(timerfd, &timeouts, sizeof(timeouts)) == + sizeof(timeouts)); + ATF_REQUIRE_MSG(timeouts == 1001, "%ld", (long)timeouts); + + ATF_REQUIRE(read(timerfd, &timeouts, sizeof(timeouts)) == + sizeof(timeouts)); + ATF_REQUIRE_MSG(timeouts == 1, "%ld", (long)timeouts); + + ATF_REQUIRE(close(timerfd) == 0); +} + /* * 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__missed_events); 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(); }