blob: 60f5f52436638c19d97355c19f8e16bbc7c79c49 [file] [log] [blame]
// Copyright 2025 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Tests which require the UTC timeline to be mutable.
#include <sys/epoll.h>
#include <sys/syscall.h>
#include <sys/time.h>
#include <sys/timerfd.h>
#include <time.h>
#include <unistd.h>
#include <ctime>
#include <thread>
#include <gtest/gtest.h>
#include "src/starnix/tests/syscalls/cpp/test_helper.h"
TEST(TimerFD, AlarmCancelOnSet) {
if (!test_helper::HasSysAdmin()) {
GTEST_SKIP() << "AlarmCancelOnSet test requires CAP_SYS_ADMIN";
}
int epoll_fd = epoll_create(/*ignored*/ 1);
ASSERT_GT(epoll_fd, 0);
int timer_fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK);
ASSERT_GT(timer_fd, 0);
epoll_event ev;
ev.events = EPOLLIN | EPOLLWAKEUP;
ev.data.u32 = 0x42; // ignored
ASSERT_EQ(0, epoll_ctl(epoll_fd, EPOLL_CTL_ADD, timer_fd, &ev));
struct itimerspec wakeup_spec = {};
ASSERT_EQ(0, timerfd_settime(timer_fd, TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, &wakeup_spec,
nullptr));
std::thread test_thread([epoll_fd, timer_fd] {
struct epoll_event events[1];
// When the UTC timeline changes, we should get an event, and reading
// the timer must give us ECANCELED. This is the TFD_TIMER_CANCEL_ON_SET way.
int ev_count = epoll_wait(epoll_fd, events, sizeof(events), -1);
ASSERT_GT(ev_count, 0) << strerror(errno);
uint64_t unused;
ASSERT_EQ(-1, read(timer_fd, &unused, sizeof(unused)));
// This errno read should not race with the one below, since the previous
// line will unblock only once that syscall is complete.
ASSERT_EQ(errno, ECANCELED);
});
// Now, rejigger the UTC timeline. This should cause epoll_wait above to unblock.
struct timeval tv = {};
ASSERT_EQ(0, gettimeofday(&tv, nullptr));
// Using settimeofday, as it is the only syscall allowed to change UTC. Working
// around the libc `settimeofday` which offloads to a different function.
//
// Force a UTC timeline update by winding time into the future. The ability to
// do this in syscall tests requires a special test fixture.
tv.tv_sec += 100000;
ASSERT_EQ(0, syscall(__NR_settimeofday, &tv, nullptr)) << "strerror: " << strerror(errno);
test_thread.join();
close(timer_fd);
close(epoll_fd);
}