2019-04-29 21:25:05 +00:00
|
|
|
// Copyright 2018 The gVisor Authors.
|
2018-12-10 22:41:40 +00:00
|
|
|
//
|
|
|
|
// 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"
|
2019-09-16 18:56:59 +00:00
|
|
|
#include "absl/flags/flag.h"
|
2018-12-10 22:41:40 +00:00
|
|
|
#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"
|
|
|
|
|
2019-09-16 18:56:59 +00:00
|
|
|
ABSL_FLAG(bool, timers_test_sleep, false,
|
|
|
|
"If true, sleep forever instead of running tests.");
|
2018-12-10 22:41:40 +00:00
|
|
|
|
|
|
|
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 (;;) {
|
|
|
|
}
|
|
|
|
}
|
2019-01-11 22:47:45 +00:00
|
|
|
ASSERT_THAT(pid, SyscallSucceeds());
|
2018-12-10 22:41:40 +00:00
|
|
|
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 (;;) {
|
|
|
|
}
|
|
|
|
}
|
2019-01-11 22:47:45 +00:00
|
|
|
ASSERT_THAT(pid, SyscallSucceeds());
|
2018-12-10 22:41:40 +00:00
|
|
|
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 (;;) {
|
|
|
|
}
|
|
|
|
}
|
2019-01-11 22:47:45 +00:00
|
|
|
ASSERT_THAT(pid, SyscallSucceeds());
|
2018-12-10 22:41:40 +00:00
|
|
|
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;
|
2020-02-04 21:05:30 +00:00
|
|
|
int ret = syscall(SYS_timer_create, clockid, &sev, &timerid);
|
|
|
|
if (ret < 0) {
|
2018-12-10 22:41:40 +00:00
|
|
|
return PosixError(errno, "timer_create");
|
|
|
|
}
|
2020-02-04 21:05:30 +00:00
|
|
|
if (ret > 0) {
|
|
|
|
return PosixError(EINVAL, "timer_create should never return positive");
|
|
|
|
}
|
2018-12-10 22:41:40 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2020-02-04 21:05:30 +00:00
|
|
|
// 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));
|
|
|
|
}
|
|
|
|
|
2018-12-10 22:41:40 +00:00
|
|
|
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);
|
|
|
|
|
2019-09-16 18:56:59 +00:00
|
|
|
if (absl::GetFlag(FLAGS_timers_test_sleep)) {
|
2018-12-10 22:41:40 +00:00
|
|
|
while (true) {
|
|
|
|
absl::SleepFor(absl::Seconds(10));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return RUN_ALL_TESTS();
|
|
|
|
}
|