// 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 <poll.h>
#include <sys/timerfd.h>
#include <time.h>

#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "test/util/file_descriptor.h"
#include "test/util/posix_error.h"
#include "test/util/test_util.h"

namespace gvisor {
namespace testing {

namespace {

// Wrapper around timerfd_create(2) that returns a FileDescriptor.
PosixErrorOr<FileDescriptor> TimerfdCreate(int clockid, int flags) {
  int fd = timerfd_create(clockid, flags);
  MaybeSave();
  if (fd < 0) {
    return PosixError(errno, "timerfd_create failed");
  }
  return FileDescriptor(fd);
}

// In tests that race a timerfd with a sleep, some slack is required because:
//
// - Timerfd expirations are asynchronous with respect to nanosleeps.
//
// - Because clock_gettime(CLOCK_MONOTONIC) is implemented through the VDSO,
// it technically uses a closely-related, but distinct, time domain from the
// CLOCK_MONOTONIC used to trigger timerfd expirations.
absl::Duration TimerSlack() { return absl::Milliseconds(500); }

TEST(TimerfdTest, IsInitiallyStopped) {
  auto const tfd = ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(CLOCK_MONOTONIC, 0));
  struct itimerspec its = {};
  ASSERT_THAT(timerfd_gettime(tfd.get(), &its), SyscallSucceeds());
  EXPECT_EQ(0, its.it_value.tv_sec);
  EXPECT_EQ(0, its.it_value.tv_nsec);
}

TEST(TimerfdTest, SingleShot) {
  constexpr absl::Duration kDelay = absl::Seconds(1);

  auto const tfd = ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(CLOCK_MONOTONIC, 0));
  struct itimerspec its = {};
  its.it_value = absl::ToTimespec(kDelay);
  ASSERT_THAT(timerfd_settime(tfd.get(), /* flags = */ 0, &its, nullptr),
              SyscallSucceeds());

  // The timer should fire exactly once since the interval is zero.
  absl::SleepFor(kDelay + TimerSlack());
  uint64_t val = 0;
  ASSERT_THAT(ReadFd(tfd.get(), &val, sizeof(uint64_t)),
              SyscallSucceedsWithValue(sizeof(uint64_t)));
  EXPECT_EQ(1, val);
}

TEST(TimerfdTest, Periodic) {
  constexpr absl::Duration kDelay = absl::Seconds(1);
  constexpr int kPeriods = 3;

  auto const tfd = ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(CLOCK_MONOTONIC, 0));
  struct itimerspec its = {};
  its.it_value = absl::ToTimespec(kDelay);
  its.it_interval = absl::ToTimespec(kDelay);
  ASSERT_THAT(timerfd_settime(tfd.get(), /* flags = */ 0, &its, nullptr),
              SyscallSucceeds());

  // Expect to see at least kPeriods expirations. More may occur due to the
  // timer slack, or due to delays from scheduling or save/restore.
  absl::SleepFor(kPeriods * kDelay + TimerSlack());
  uint64_t val = 0;
  ASSERT_THAT(ReadFd(tfd.get(), &val, sizeof(uint64_t)),
              SyscallSucceedsWithValue(sizeof(uint64_t)));
  EXPECT_GE(val, kPeriods);
}

TEST(TimerfdTest, BlockingRead) {
  constexpr absl::Duration kDelay = absl::Seconds(3);

  auto const tfd = ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(CLOCK_MONOTONIC, 0));
  struct itimerspec its = {};
  its.it_value.tv_sec = absl::ToInt64Seconds(kDelay);
  auto const start_time = absl::Now();
  ASSERT_THAT(timerfd_settime(tfd.get(), /* flags = */ 0, &its, nullptr),
              SyscallSucceeds());

  // read should block until the timer fires.
  uint64_t val = 0;
  ASSERT_THAT(ReadFd(tfd.get(), &val, sizeof(uint64_t)),
              SyscallSucceedsWithValue(sizeof(uint64_t)));
  auto const end_time = absl::Now();
  EXPECT_EQ(1, val);
  EXPECT_GE((end_time - start_time) + TimerSlack(), kDelay);
}

TEST(TimerfdTest, NonblockingRead_NoRandomSave) {
  constexpr absl::Duration kDelay = absl::Seconds(5);

  auto const tfd =
      ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(CLOCK_MONOTONIC, TFD_NONBLOCK));

  // Since the timer is initially disabled and has never fired, read should
  // return EAGAIN.
  uint64_t val = 0;
  ASSERT_THAT(ReadFd(tfd.get(), &val, sizeof(uint64_t)),
              SyscallFailsWithErrno(EAGAIN));

  DisableSave ds;  // Timing-sensitive.

  // Arm the timer.
  struct itimerspec its = {};
  its.it_value.tv_sec = absl::ToInt64Seconds(kDelay);
  ASSERT_THAT(timerfd_settime(tfd.get(), /* flags = */ 0, &its, nullptr),
              SyscallSucceeds());

  // Since the timer has not yet fired, read should return EAGAIN.
  ASSERT_THAT(ReadFd(tfd.get(), &val, sizeof(uint64_t)),
              SyscallFailsWithErrno(EAGAIN));

  ds.reset();  // No longer timing-sensitive.

  // After the timer fires, read should indicate 1 expiration.
  absl::SleepFor(kDelay + TimerSlack());
  ASSERT_THAT(ReadFd(tfd.get(), &val, sizeof(uint64_t)),
              SyscallSucceedsWithValue(sizeof(uint64_t)));
  EXPECT_EQ(1, val);

  // The successful read should have reset the number of expirations.
  ASSERT_THAT(ReadFd(tfd.get(), &val, sizeof(uint64_t)),
              SyscallFailsWithErrno(EAGAIN));
}

TEST(TimerfdTest, BlockingPoll_SetTimeResetsExpirations) {
  constexpr absl::Duration kDelay = absl::Seconds(3);

  auto const tfd =
      ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(CLOCK_MONOTONIC, TFD_NONBLOCK));
  struct itimerspec its = {};
  its.it_value.tv_sec = absl::ToInt64Seconds(kDelay);
  auto const start_time = absl::Now();
  ASSERT_THAT(timerfd_settime(tfd.get(), /* flags = */ 0, &its, nullptr),
              SyscallSucceeds());

  // poll should block until the timer fires.
  struct pollfd pfd = {};
  pfd.fd = tfd.get();
  pfd.events = POLLIN;
  ASSERT_THAT(poll(&pfd, /* nfds = */ 1,
                   /* timeout = */ 2 * absl::ToInt64Seconds(kDelay) * 1000),
              SyscallSucceedsWithValue(1));
  auto const end_time = absl::Now();
  EXPECT_EQ(POLLIN, pfd.revents);
  EXPECT_GE((end_time - start_time) + TimerSlack(), kDelay);

  // Call timerfd_settime again with a value of 0. This should reset the number
  // of expirations to 0, causing read to return EAGAIN since the timerfd is
  // non-blocking.
  its.it_value.tv_sec = 0;
  ASSERT_THAT(timerfd_settime(tfd.get(), /* flags = */ 0, &its, nullptr),
              SyscallSucceeds());
  uint64_t val = 0;
  ASSERT_THAT(ReadFd(tfd.get(), &val, sizeof(uint64_t)),
              SyscallFailsWithErrno(EAGAIN));
}

TEST(TimerfdTest, SetAbsoluteTime) {
  constexpr absl::Duration kDelay = absl::Seconds(3);

  // Use a non-blocking timerfd so that if TFD_TIMER_ABSTIME is incorrectly
  // non-functional, we get EAGAIN rather than a test timeout.
  auto const tfd =
      ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(CLOCK_MONOTONIC, TFD_NONBLOCK));
  struct itimerspec its = {};
  ASSERT_THAT(clock_gettime(CLOCK_MONOTONIC, &its.it_value), SyscallSucceeds());
  its.it_value.tv_sec += absl::ToInt64Seconds(kDelay);
  ASSERT_THAT(timerfd_settime(tfd.get(), TFD_TIMER_ABSTIME, &its, nullptr),
              SyscallSucceeds());

  absl::SleepFor(kDelay + TimerSlack());
  uint64_t val = 0;
  ASSERT_THAT(ReadFd(tfd.get(), &val, sizeof(uint64_t)),
              SyscallSucceedsWithValue(sizeof(uint64_t)));
  EXPECT_EQ(1, val);
}

TEST(TimerfdTest, ClockRealtime) {
  // Since CLOCK_REALTIME can, by definition, change, we can't make any
  // non-flaky assertions about the amount of time it takes for a
  // CLOCK_REALTIME-based timer to expire. Just check that it expires at all,
  // and hope it happens before the test times out.
  constexpr int kDelaySecs = 1;

  auto const tfd = ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(CLOCK_REALTIME, 0));
  struct itimerspec its = {};
  its.it_value.tv_sec = kDelaySecs;
  ASSERT_THAT(timerfd_settime(tfd.get(), /* flags = */ 0, &its, nullptr),
              SyscallSucceeds());

  uint64_t val = 0;
  ASSERT_THAT(ReadFd(tfd.get(), &val, sizeof(uint64_t)),
              SyscallSucceedsWithValue(sizeof(uint64_t)));
  EXPECT_EQ(1, val);
}

TEST(TimerfdTest, IllegalReadWrite) {
  auto const tfd =
      ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(CLOCK_MONOTONIC, TFD_NONBLOCK));
  uint64_t val = 0;
  EXPECT_THAT(PreadFd(tfd.get(), &val, sizeof(val), 0),
              SyscallFailsWithErrno(ESPIPE));
  EXPECT_THAT(WriteFd(tfd.get(), &val, sizeof(val)),
              SyscallFailsWithErrno(EINVAL));
  EXPECT_THAT(PwriteFd(tfd.get(), &val, sizeof(val), 0),
              SyscallFailsWithErrno(ESPIPE));
}

}  // namespace

}  // namespace testing
}  // namespace gvisor