diff options
Diffstat (limited to 'test/syscalls/linux/timers.cc')
-rw-r--r-- | test/syscalls/linux/timers.cc | 662 |
1 files changed, 0 insertions, 662 deletions
diff --git a/test/syscalls/linux/timers.cc b/test/syscalls/linux/timers.cc deleted file mode 100644 index 2f92c27da..000000000 --- a/test/syscalls/linux/timers.cc +++ /dev/null @@ -1,662 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <signal.h> -#include <sys/resource.h> -#include <sys/time.h> -#include <syscall.h> -#include <time.h> -#include <unistd.h> - -#include <atomic> - -#include "gtest/gtest.h" -#include "absl/flags/flag.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "test/util/cleanup.h" -#include "test/util/logging.h" -#include "test/util/multiprocess_util.h" -#include "test/util/posix_error.h" -#include "test/util/signal_util.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -ABSL_FLAG(bool, timers_test_sleep, false, - "If true, sleep forever instead of running tests."); - -using ::testing::_; -using ::testing::AnyOf; - -namespace gvisor { -namespace testing { -namespace { - -#ifndef CPUCLOCK_PROF -#define CPUCLOCK_PROF 0 -#endif // CPUCLOCK_PROF - -PosixErrorOr<absl::Duration> ProcessCPUTime(pid_t pid) { - // Use pid-specific CPUCLOCK_PROF, which is the clock used to enforce - // RLIMIT_CPU. - clockid_t clockid = (~static_cast<clockid_t>(pid) << 3) | CPUCLOCK_PROF; - - struct timespec ts; - int ret = clock_gettime(clockid, &ts); - if (ret < 0) { - return PosixError(errno, "clock_gettime failed"); - } - - return absl::DurationFromTimespec(ts); -} - -void NoopSignalHandler(int signo) { - TEST_CHECK_MSG(SIGXCPU == signo, - "NoopSigHandler did not receive expected signal"); -} - -void UninstallingSignalHandler(int signo) { - TEST_CHECK_MSG(SIGXCPU == signo, - "UninstallingSignalHandler did not receive expected signal"); - struct sigaction rev_action; - rev_action.sa_handler = SIG_DFL; - rev_action.sa_flags = 0; - sigemptyset(&rev_action.sa_mask); - sigaction(SIGXCPU, &rev_action, nullptr); -} - -TEST(TimerTest, ProcessKilledOnCPUSoftLimit) { - constexpr absl::Duration kSoftLimit = absl::Seconds(1); - constexpr absl::Duration kHardLimit = absl::Seconds(3); - - struct rlimit cpu_limits; - cpu_limits.rlim_cur = absl::ToInt64Seconds(kSoftLimit); - cpu_limits.rlim_max = absl::ToInt64Seconds(kHardLimit); - - int pid = fork(); - MaybeSave(); - if (pid == 0) { - TEST_PCHECK(setrlimit(RLIMIT_CPU, &cpu_limits) == 0); - MaybeSave(); - for (;;) { - } - } - ASSERT_THAT(pid, SyscallSucceeds()); - auto c = Cleanup([pid] { - int status; - EXPECT_THAT(waitpid(pid, &status, 0), SyscallSucceedsWithValue(pid)); - EXPECT_TRUE(WIFSIGNALED(status)); - EXPECT_EQ(WTERMSIG(status), SIGXCPU); - }); - - // Wait for the child to exit, but do not reap it. This will allow us to check - // its CPU usage while it is zombied. - EXPECT_THAT(waitid(P_PID, pid, nullptr, WEXITED | WNOWAIT), - SyscallSucceeds()); - - // Assert that the child spent 1s of CPU before getting killed. - // - // We must be careful to use CPUCLOCK_PROF, the same clock used for RLIMIT_CPU - // enforcement, to get correct results. Note that this is slightly different - // from rusage-reported CPU usage: - // - // RLIMIT_CPU, CPUCLOCK_PROF use kernel/sched/cputime.c:thread_group_cputime. - // rusage uses kernel/sched/cputime.c:thread_group_cputime_adjusted. - absl::Duration cpu = ASSERT_NO_ERRNO_AND_VALUE(ProcessCPUTime(pid)); - EXPECT_GE(cpu, kSoftLimit); - - // Child did not make it to the hard limit. - // - // Linux sends SIGXCPU synchronously with CPU tick updates. See - // kernel/time/timer.c:update_process_times: - // => account_process_tick // update task CPU usage. - // => run_posix_cpu_timers // enforce RLIMIT_CPU, sending signal. - // - // Thus, only chance for this to flake is if the system time required to - // deliver the signal exceeds 2s. - EXPECT_LT(cpu, kHardLimit); -} - -TEST(TimerTest, ProcessPingedRepeatedlyAfterCPUSoftLimit) { - struct sigaction new_action; - new_action.sa_handler = UninstallingSignalHandler; - new_action.sa_flags = 0; - sigemptyset(&new_action.sa_mask); - - constexpr absl::Duration kSoftLimit = absl::Seconds(1); - constexpr absl::Duration kHardLimit = absl::Seconds(10); - - struct rlimit cpu_limits; - cpu_limits.rlim_cur = absl::ToInt64Seconds(kSoftLimit); - cpu_limits.rlim_max = absl::ToInt64Seconds(kHardLimit); - - int pid = fork(); - MaybeSave(); - if (pid == 0) { - TEST_PCHECK(sigaction(SIGXCPU, &new_action, nullptr) == 0); - MaybeSave(); - TEST_PCHECK(setrlimit(RLIMIT_CPU, &cpu_limits) == 0); - MaybeSave(); - for (;;) { - } - } - ASSERT_THAT(pid, SyscallSucceeds()); - auto c = Cleanup([pid] { - int status; - EXPECT_THAT(waitpid(pid, &status, 0), SyscallSucceedsWithValue(pid)); - EXPECT_TRUE(WIFSIGNALED(status)); - EXPECT_EQ(WTERMSIG(status), SIGXCPU); - }); - - // Wait for the child to exit, but do not reap it. This will allow us to check - // its CPU usage while it is zombied. - EXPECT_THAT(waitid(P_PID, pid, nullptr, WEXITED | WNOWAIT), - SyscallSucceeds()); - - absl::Duration cpu = ASSERT_NO_ERRNO_AND_VALUE(ProcessCPUTime(pid)); - // Following signals come every CPU second. - EXPECT_GE(cpu, kSoftLimit + absl::Seconds(1)); - - // Child did not make it to the hard limit. - // - // As above, should not flake. - EXPECT_LT(cpu, kHardLimit); -} - -TEST(TimerTest, ProcessKilledOnCPUHardLimit) { - struct sigaction new_action; - new_action.sa_handler = NoopSignalHandler; - new_action.sa_flags = 0; - sigemptyset(&new_action.sa_mask); - - constexpr absl::Duration kSoftLimit = absl::Seconds(1); - constexpr absl::Duration kHardLimit = absl::Seconds(3); - - struct rlimit cpu_limits; - cpu_limits.rlim_cur = absl::ToInt64Seconds(kSoftLimit); - cpu_limits.rlim_max = absl::ToInt64Seconds(kHardLimit); - - int pid = fork(); - MaybeSave(); - if (pid == 0) { - TEST_PCHECK(sigaction(SIGXCPU, &new_action, nullptr) == 0); - MaybeSave(); - TEST_PCHECK(setrlimit(RLIMIT_CPU, &cpu_limits) == 0); - MaybeSave(); - for (;;) { - } - } - ASSERT_THAT(pid, SyscallSucceeds()); - auto c = Cleanup([pid] { - int status; - EXPECT_THAT(waitpid(pid, &status, 0), SyscallSucceedsWithValue(pid)); - EXPECT_TRUE(WIFSIGNALED(status)); - EXPECT_EQ(WTERMSIG(status), SIGKILL); - }); - - // Wait for the child to exit, but do not reap it. This will allow us to check - // its CPU usage while it is zombied. - EXPECT_THAT(waitid(P_PID, pid, nullptr, WEXITED | WNOWAIT), - SyscallSucceeds()); - - absl::Duration cpu = ASSERT_NO_ERRNO_AND_VALUE(ProcessCPUTime(pid)); - EXPECT_GE(cpu, kHardLimit); -} - -// RAII type for a kernel "POSIX" interval timer. (The kernel provides system -// calls such as timer_create that behave very similarly, but not identically, -// to those described by timer_create(2); in particular, the kernel does not -// implement SIGEV_THREAD. glibc builds POSIX-compliant interval timers based on -// these kernel interval timers.) -// -// Compare implementation to FileDescriptor. -class IntervalTimer { - public: - IntervalTimer() = default; - - explicit IntervalTimer(int id) { set_id(id); } - - IntervalTimer(IntervalTimer&& orig) : id_(orig.release()) {} - - IntervalTimer& operator=(IntervalTimer&& orig) { - if (this == &orig) return *this; - reset(orig.release()); - return *this; - } - - IntervalTimer(const IntervalTimer& other) = delete; - IntervalTimer& operator=(const IntervalTimer& other) = delete; - - ~IntervalTimer() { reset(); } - - int get() const { return id_; } - - int release() { - int const id = id_; - id_ = -1; - return id; - } - - void reset() { reset(-1); } - - void reset(int id) { - if (id_ >= 0) { - TEST_PCHECK(syscall(SYS_timer_delete, id_) == 0); - MaybeSave(); - } - set_id(id); - } - - PosixErrorOr<struct itimerspec> Set( - int flags, const struct itimerspec& new_value) const { - struct itimerspec old_value = {}; - if (syscall(SYS_timer_settime, id_, flags, &new_value, &old_value) < 0) { - return PosixError(errno, "timer_settime"); - } - MaybeSave(); - return old_value; - } - - PosixErrorOr<struct itimerspec> Get() const { - struct itimerspec curr_value = {}; - if (syscall(SYS_timer_gettime, id_, &curr_value) < 0) { - return PosixError(errno, "timer_gettime"); - } - MaybeSave(); - return curr_value; - } - - PosixErrorOr<int> Overruns() const { - int rv = syscall(SYS_timer_getoverrun, id_); - if (rv < 0) { - return PosixError(errno, "timer_getoverrun"); - } - MaybeSave(); - return rv; - } - - private: - void set_id(int id) { id_ = std::max(id, -1); } - - // Kernel timer_t is int; glibc timer_t is void*. - int id_ = -1; -}; - -PosixErrorOr<IntervalTimer> TimerCreate(clockid_t clockid, - const struct sigevent& sev) { - int timerid; - int ret = syscall(SYS_timer_create, clockid, &sev, &timerid); - if (ret < 0) { - return PosixError(errno, "timer_create"); - } - if (ret > 0) { - return PosixError(EINVAL, "timer_create should never return positive"); - } - MaybeSave(); - return IntervalTimer(timerid); -} - -// See timerfd.cc:TimerSlack() for rationale. -constexpr absl::Duration kTimerSlack = absl::Milliseconds(500); - -TEST(IntervalTimerTest, IsInitiallyStopped) { - struct sigevent sev = {}; - sev.sigev_notify = SIGEV_NONE; - const auto timer = - ASSERT_NO_ERRNO_AND_VALUE(TimerCreate(CLOCK_MONOTONIC, sev)); - const struct itimerspec its = ASSERT_NO_ERRNO_AND_VALUE(timer.Get()); - EXPECT_EQ(0, its.it_value.tv_sec); - EXPECT_EQ(0, its.it_value.tv_nsec); -} - -// Kernel can create multiple timers without issue. -// -// Regression test for gvisor.dev/issue/1738. -TEST(IntervalTimerTest, MultipleTimers) { - struct sigevent sev = {}; - sev.sigev_notify = SIGEV_NONE; - const auto timer1 = - ASSERT_NO_ERRNO_AND_VALUE(TimerCreate(CLOCK_MONOTONIC, sev)); - const auto timer2 = - ASSERT_NO_ERRNO_AND_VALUE(TimerCreate(CLOCK_MONOTONIC, sev)); -} - -TEST(IntervalTimerTest, SingleShotSilent) { - struct sigevent sev = {}; - sev.sigev_notify = SIGEV_NONE; - const auto timer = - ASSERT_NO_ERRNO_AND_VALUE(TimerCreate(CLOCK_MONOTONIC, sev)); - - constexpr absl::Duration kDelay = absl::Seconds(1); - struct itimerspec its = {}; - its.it_value = absl::ToTimespec(kDelay); - ASSERT_NO_ERRNO(timer.Set(0, its)); - - // The timer should count down to 0 and stop since the interval is zero. No - // overruns should be counted. - absl::SleepFor(kDelay + kTimerSlack); - its = ASSERT_NO_ERRNO_AND_VALUE(timer.Get()); - EXPECT_EQ(0, its.it_value.tv_sec); - EXPECT_EQ(0, its.it_value.tv_nsec); - EXPECT_THAT(timer.Overruns(), IsPosixErrorOkAndHolds(0)); -} - -TEST(IntervalTimerTest, PeriodicSilent) { - struct sigevent sev = {}; - sev.sigev_notify = SIGEV_NONE; - const auto timer = - ASSERT_NO_ERRNO_AND_VALUE(TimerCreate(CLOCK_MONOTONIC, sev)); - - constexpr absl::Duration kPeriod = absl::Seconds(1); - struct itimerspec its = {}; - its.it_value = its.it_interval = absl::ToTimespec(kPeriod); - ASSERT_NO_ERRNO(timer.Set(0, its)); - - absl::SleepFor(kPeriod * 3 + kTimerSlack); - - // The timer should still be running. - its = ASSERT_NO_ERRNO_AND_VALUE(timer.Get()); - EXPECT_TRUE(its.it_value.tv_nsec != 0 || its.it_value.tv_sec != 0); - - // Timer expirations are not counted as overruns under SIGEV_NONE. - EXPECT_THAT(timer.Overruns(), IsPosixErrorOkAndHolds(0)); -} - -std::atomic<int> counted_signals; - -void IntervalTimerCountingSignalHandler(int sig, siginfo_t* info, - void* ucontext) { - counted_signals.fetch_add(1 + info->si_overrun); -} - -TEST(IntervalTimerTest, PeriodicGroupDirectedSignal) { - constexpr int kSigno = SIGUSR1; - constexpr int kSigvalue = 42; - - // Install our signal handler. - counted_signals.store(0); - struct sigaction sa = {}; - sa.sa_sigaction = IntervalTimerCountingSignalHandler; - sigemptyset(&sa.sa_mask); - sa.sa_flags = SA_SIGINFO; - const auto scoped_sigaction = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(kSigno, sa)); - - // Ensure that kSigno is unblocked on at least one thread. - const auto scoped_sigmask = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_UNBLOCK, kSigno)); - - struct sigevent sev = {}; - sev.sigev_notify = SIGEV_SIGNAL; - sev.sigev_signo = kSigno; - sev.sigev_value.sival_int = kSigvalue; - auto timer = ASSERT_NO_ERRNO_AND_VALUE(TimerCreate(CLOCK_MONOTONIC, sev)); - - constexpr absl::Duration kPeriod = absl::Seconds(1); - constexpr int kCycles = 3; - struct itimerspec its = {}; - its.it_value = its.it_interval = absl::ToTimespec(kPeriod); - ASSERT_NO_ERRNO(timer.Set(0, its)); - - absl::SleepFor(kPeriod * kCycles + kTimerSlack); - EXPECT_GE(counted_signals.load(), kCycles); -} - -// From Linux's include/uapi/asm-generic/siginfo.h. -#ifndef sigev_notify_thread_id -#define sigev_notify_thread_id _sigev_un._tid -#endif - -TEST(IntervalTimerTest, PeriodicThreadDirectedSignal) { - constexpr int kSigno = SIGUSR1; - constexpr int kSigvalue = 42; - - // Block kSigno so that we can accumulate overruns. - sigset_t mask; - sigemptyset(&mask); - sigaddset(&mask, kSigno); - const auto scoped_sigmask = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_BLOCK, mask)); - - struct sigevent sev = {}; - sev.sigev_notify = SIGEV_THREAD_ID; - sev.sigev_signo = kSigno; - sev.sigev_value.sival_int = kSigvalue; - sev.sigev_notify_thread_id = gettid(); - auto timer = ASSERT_NO_ERRNO_AND_VALUE(TimerCreate(CLOCK_MONOTONIC, sev)); - - constexpr absl::Duration kPeriod = absl::Seconds(1); - constexpr int kCycles = 3; - struct itimerspec its = {}; - its.it_value = its.it_interval = absl::ToTimespec(kPeriod); - ASSERT_NO_ERRNO(timer.Set(0, its)); - absl::SleepFor(kPeriod * kCycles + kTimerSlack); - - // At least kCycles expirations should have occurred, resulting in kCycles-1 - // overruns (the first expiration sent the signal successfully). - siginfo_t si; - struct timespec zero_ts = absl::ToTimespec(absl::ZeroDuration()); - ASSERT_THAT(sigtimedwait(&mask, &si, &zero_ts), - SyscallSucceedsWithValue(kSigno)); - EXPECT_EQ(si.si_signo, kSigno); - EXPECT_EQ(si.si_code, SI_TIMER); - EXPECT_EQ(si.si_timerid, timer.get()); - EXPECT_GE(si.si_overrun, kCycles - 1); - EXPECT_EQ(si.si_int, kSigvalue); - - // Kill the timer, then drain any additional signal it may have enqueued. We - // can't do this before the preceding sigtimedwait because stopping or - // deleting the timer resets si_overrun to 0. - timer.reset(); - sigtimedwait(&mask, &si, &zero_ts); -} - -TEST(IntervalTimerTest, OtherThreadGroup) { - constexpr int kSigno = SIGUSR1; - - // Create a subprocess that does nothing until killed. - pid_t child_pid; - const auto sp = ASSERT_NO_ERRNO_AND_VALUE(ForkAndExec( - "/proc/self/exe", ExecveArray({"timers", "--timers_test_sleep"}), - ExecveArray(), &child_pid, nullptr)); - - // Verify that we can't create a timer that would send signals to it. - struct sigevent sev = {}; - sev.sigev_notify = SIGEV_THREAD_ID; - sev.sigev_signo = kSigno; - sev.sigev_notify_thread_id = child_pid; - EXPECT_THAT(TimerCreate(CLOCK_MONOTONIC, sev), PosixErrorIs(EINVAL, _)); -} - -TEST(IntervalTimerTest, RealTimeSignalsAreNotDuplicated) { - const int kSigno = SIGRTMIN; - constexpr int kSigvalue = 42; - - // Block signo so that we can accumulate overruns. - sigset_t mask; - sigemptyset(&mask); - sigaddset(&mask, kSigno); - const auto scoped_sigmask = ScopedSignalMask(SIG_BLOCK, mask); - - struct sigevent sev = {}; - sev.sigev_notify = SIGEV_THREAD_ID; - sev.sigev_signo = kSigno; - sev.sigev_value.sival_int = kSigvalue; - sev.sigev_notify_thread_id = gettid(); - const auto timer = - ASSERT_NO_ERRNO_AND_VALUE(TimerCreate(CLOCK_MONOTONIC, sev)); - - constexpr absl::Duration kPeriod = absl::Seconds(1); - constexpr int kCycles = 3; - struct itimerspec its = {}; - its.it_value = its.it_interval = absl::ToTimespec(kPeriod); - ASSERT_NO_ERRNO(timer.Set(0, its)); - absl::SleepFor(kPeriod * kCycles + kTimerSlack); - - // Stop the timer so that no further signals are enqueued after sigtimedwait. - struct timespec zero_ts = absl::ToTimespec(absl::ZeroDuration()); - its.it_value = its.it_interval = zero_ts; - ASSERT_NO_ERRNO(timer.Set(0, its)); - - // The timer should have sent only a single signal, even though the kernel - // supports enqueueing of multiple RT signals. - siginfo_t si; - ASSERT_THAT(sigtimedwait(&mask, &si, &zero_ts), - SyscallSucceedsWithValue(kSigno)); - EXPECT_EQ(si.si_signo, kSigno); - EXPECT_EQ(si.si_code, SI_TIMER); - EXPECT_EQ(si.si_timerid, timer.get()); - // si_overrun was reset by timer_settime. - EXPECT_EQ(si.si_overrun, 0); - EXPECT_EQ(si.si_int, kSigvalue); - EXPECT_THAT(sigtimedwait(&mask, &si, &zero_ts), - SyscallFailsWithErrno(EAGAIN)); -} - -TEST(IntervalTimerTest, AlreadyPendingSignal) { - constexpr int kSigno = SIGUSR1; - constexpr int kSigvalue = 42; - - // Block kSigno so that we can accumulate overruns. - sigset_t mask; - sigemptyset(&mask); - sigaddset(&mask, kSigno); - const auto scoped_sigmask = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_BLOCK, mask)); - - // Send ourselves a signal, preventing the timer from enqueuing. - ASSERT_THAT(tgkill(getpid(), gettid(), kSigno), SyscallSucceeds()); - - struct sigevent sev = {}; - sev.sigev_notify = SIGEV_THREAD_ID; - sev.sigev_signo = kSigno; - sev.sigev_value.sival_int = kSigvalue; - sev.sigev_notify_thread_id = gettid(); - auto timer = ASSERT_NO_ERRNO_AND_VALUE(TimerCreate(CLOCK_MONOTONIC, sev)); - - constexpr absl::Duration kPeriod = absl::Seconds(1); - constexpr int kCycles = 3; - struct itimerspec its = {}; - its.it_value = its.it_interval = absl::ToTimespec(kPeriod); - ASSERT_NO_ERRNO(timer.Set(0, its)); - - // End the sleep one cycle short; we will sleep for one more cycle below. - absl::SleepFor(kPeriod * (kCycles - 1)); - - // Dequeue the first signal, which we sent to ourselves with tgkill. - siginfo_t si; - struct timespec zero_ts = absl::ToTimespec(absl::ZeroDuration()); - ASSERT_THAT(sigtimedwait(&mask, &si, &zero_ts), - SyscallSucceedsWithValue(kSigno)); - EXPECT_EQ(si.si_signo, kSigno); - // glibc sigtimedwait silently replaces SI_TKILL with SI_USER: - // sysdeps/unix/sysv/linux/sigtimedwait.c:__sigtimedwait(). This isn't - // documented, so we don't depend on it. - EXPECT_THAT(si.si_code, AnyOf(SI_USER, SI_TKILL)); - - // Sleep for 1 more cycle to give the timer time to send a signal. - absl::SleepFor(kPeriod + kTimerSlack); - - // At least kCycles expirations should have occurred, resulting in kCycles-1 - // overruns (the last expiration sent the signal successfully). - ASSERT_THAT(sigtimedwait(&mask, &si, &zero_ts), - SyscallSucceedsWithValue(kSigno)); - EXPECT_EQ(si.si_signo, kSigno); - EXPECT_EQ(si.si_code, SI_TIMER); - EXPECT_EQ(si.si_timerid, timer.get()); - EXPECT_GE(si.si_overrun, kCycles - 1); - EXPECT_EQ(si.si_int, kSigvalue); - - // Kill the timer, then drain any additional signal it may have enqueued. We - // can't do this before the preceding sigtimedwait because stopping or - // deleting the timer resets si_overrun to 0. - timer.reset(); - sigtimedwait(&mask, &si, &zero_ts); -} - -TEST(IntervalTimerTest, IgnoredSignalCountsAsOverrun) { - constexpr int kSigno = SIGUSR1; - constexpr int kSigvalue = 42; - - // Ignore kSigno. - struct sigaction sa = {}; - sa.sa_handler = SIG_IGN; - const auto scoped_sigaction = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(kSigno, sa)); - - // Unblock kSigno so that ignored signals will be discarded. - sigset_t mask; - sigemptyset(&mask); - sigaddset(&mask, kSigno); - auto scoped_sigmask = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_UNBLOCK, mask)); - - struct sigevent sev = {}; - sev.sigev_notify = SIGEV_THREAD_ID; - sev.sigev_signo = kSigno; - sev.sigev_value.sival_int = kSigvalue; - sev.sigev_notify_thread_id = gettid(); - auto timer = ASSERT_NO_ERRNO_AND_VALUE(TimerCreate(CLOCK_MONOTONIC, sev)); - - constexpr absl::Duration kPeriod = absl::Seconds(1); - constexpr int kCycles = 3; - struct itimerspec its = {}; - its.it_value = its.it_interval = absl::ToTimespec(kPeriod); - ASSERT_NO_ERRNO(timer.Set(0, its)); - - // End the sleep one cycle short; we will sleep for one more cycle below. - absl::SleepFor(kPeriod * (kCycles - 1)); - - // Block kSigno so that ignored signals will be enqueued. - scoped_sigmask.Release()(); - scoped_sigmask = ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_BLOCK, mask)); - - // Sleep for 1 more cycle to give the timer time to send a signal. - absl::SleepFor(kPeriod + kTimerSlack); - - // At least kCycles expirations should have occurred, resulting in kCycles-1 - // overruns (the last expiration sent the signal successfully). - siginfo_t si; - struct timespec zero_ts = absl::ToTimespec(absl::ZeroDuration()); - ASSERT_THAT(sigtimedwait(&mask, &si, &zero_ts), - SyscallSucceedsWithValue(kSigno)); - EXPECT_EQ(si.si_signo, kSigno); - EXPECT_EQ(si.si_code, SI_TIMER); - EXPECT_EQ(si.si_timerid, timer.get()); - EXPECT_GE(si.si_overrun, kCycles - 1); - EXPECT_EQ(si.si_int, kSigvalue); - - // Kill the timer, then drain any additional signal it may have enqueued. We - // can't do this before the preceding sigtimedwait because stopping or - // deleting the timer resets si_overrun to 0. - timer.reset(); - sigtimedwait(&mask, &si, &zero_ts); -} - -} // namespace -} // namespace testing -} // namespace gvisor - -int main(int argc, char** argv) { - gvisor::testing::TestInit(&argc, &argv); - - if (absl::GetFlag(FLAGS_timers_test_sleep)) { - while (true) { - absl::SleepFor(absl::Seconds(10)); - } - } - - return RUN_ALL_TESTS(); -} |