summaryrefslogtreecommitdiffhomepage
path: root/test/syscalls/linux/clock_nanosleep.cc
diff options
context:
space:
mode:
Diffstat (limited to 'test/syscalls/linux/clock_nanosleep.cc')
-rw-r--r--test/syscalls/linux/clock_nanosleep.cc153
1 files changed, 153 insertions, 0 deletions
diff --git a/test/syscalls/linux/clock_nanosleep.cc b/test/syscalls/linux/clock_nanosleep.cc
new file mode 100644
index 000000000..96bb961b4
--- /dev/null
+++ b/test/syscalls/linux/clock_nanosleep.cc
@@ -0,0 +1,153 @@
+// Copyright 2018 Google LLC
+//
+// 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 <time.h>
+
+#include <atomic>
+#include <utility>
+
+#include "gtest/gtest.h"
+#include "absl/time/time.h"
+#include "test/util/cleanup.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"
+#include "test/util/timer_util.h"
+
+namespace gvisor {
+namespace testing {
+
+namespace {
+
+// sys_clock_nanosleep is defined because the glibc clock_nanosleep returns
+// error numbers directly and does not set errno. This makes our Syscall
+// matchers look a little weird when expecting failure:
+// "SyscallSucceedsWithValue(ERRNO)".
+int sys_clock_nanosleep(clockid_t clkid, int flags,
+ const struct timespec* request,
+ struct timespec* remain) {
+ return syscall(SYS_clock_nanosleep, clkid, flags, request, remain);
+}
+
+PosixErrorOr<absl::Time> GetTime(clockid_t clk) {
+ struct timespec ts = {};
+ int rc = clock_gettime(clk, &ts);
+ MaybeSave();
+ if (rc < 0) {
+ return PosixError(errno, "clock_gettime");
+ }
+ return absl::TimeFromTimespec(ts);
+}
+
+class WallClockNanosleepTest : public ::testing::TestWithParam<clockid_t> {};
+
+TEST_P(WallClockNanosleepTest, InvalidValues) {
+ const struct timespec invalid[] = {
+ {.tv_sec = -1, .tv_nsec = -1}, {.tv_sec = 0, .tv_nsec = INT32_MIN},
+ {.tv_sec = 0, .tv_nsec = INT32_MAX}, {.tv_sec = 0, .tv_nsec = -1},
+ {.tv_sec = -1, .tv_nsec = 0},
+ };
+
+ for (auto const ts : invalid) {
+ EXPECT_THAT(sys_clock_nanosleep(GetParam(), 0, &ts, nullptr),
+ SyscallFailsWithErrno(EINVAL));
+ }
+}
+
+TEST_P(WallClockNanosleepTest, SleepOneSecond) {
+ absl::Duration const duration = absl::Seconds(1);
+ struct timespec dur = absl::ToTimespec(duration);
+
+ absl::Time const before = ASSERT_NO_ERRNO_AND_VALUE(GetTime(GetParam()));
+ EXPECT_THAT(RetryEINTR(sys_clock_nanosleep)(GetParam(), 0, &dur, &dur),
+ SyscallSucceeds());
+ absl::Time const after = ASSERT_NO_ERRNO_AND_VALUE(GetTime(GetParam()));
+
+ EXPECT_GE(after - before, duration);
+}
+
+TEST_P(WallClockNanosleepTest, InterruptedNanosleep) {
+ absl::Duration const duration = absl::Seconds(60);
+ struct timespec dur = absl::ToTimespec(duration);
+
+ // Install no-op signal handler for SIGALRM.
+ struct sigaction sa = {};
+ sigfillset(&sa.sa_mask);
+ sa.sa_handler = +[](int signo) {};
+ auto const cleanup_sa =
+ ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGALRM, sa));
+
+ // Measure time since setting the alarm, since the alarm will interrupt the
+ // sleep and hence determine how long we sleep.
+ absl::Time const before = ASSERT_NO_ERRNO_AND_VALUE(GetTime(GetParam()));
+
+ // Set an alarm to go off while sleeping.
+ struct itimerval timer = {};
+ timer.it_value.tv_sec = 1;
+ timer.it_value.tv_usec = 0;
+ timer.it_interval.tv_sec = 1;
+ timer.it_interval.tv_usec = 0;
+ auto const cleanup =
+ ASSERT_NO_ERRNO_AND_VALUE(ScopedItimer(ITIMER_REAL, timer));
+
+ EXPECT_THAT(sys_clock_nanosleep(GetParam(), 0, &dur, &dur),
+ SyscallFailsWithErrno(EINTR));
+ absl::Time const after = ASSERT_NO_ERRNO_AND_VALUE(GetTime(GetParam()));
+
+ absl::Duration const remaining = absl::DurationFromTimespec(dur);
+ EXPECT_GE(after - before + remaining, duration);
+}
+
+TEST_P(WallClockNanosleepTest, SleepUntil) {
+ absl::Time const now = ASSERT_NO_ERRNO_AND_VALUE(GetTime(GetParam()));
+ absl::Time const until = now + absl::Seconds(2);
+ struct timespec ts = absl::ToTimespec(until);
+
+ EXPECT_THAT(
+ RetryEINTR(sys_clock_nanosleep)(GetParam(), TIMER_ABSTIME, &ts, nullptr),
+ SyscallSucceeds());
+ absl::Time const after = ASSERT_NO_ERRNO_AND_VALUE(GetTime(GetParam()));
+
+ EXPECT_GE(after, until);
+}
+
+INSTANTIATE_TEST_CASE_P(Sleepers, WallClockNanosleepTest,
+ ::testing::Values(CLOCK_REALTIME, CLOCK_MONOTONIC));
+
+TEST(ClockNanosleepProcessTest, SleepFiveSeconds) {
+ absl::Duration const kDuration = absl::Seconds(5);
+ struct timespec dur = absl::ToTimespec(kDuration);
+
+ // Ensure that CLOCK_PROCESS_CPUTIME_ID advances.
+ std::atomic<bool> done(false);
+ ScopedThread t([&] {
+ while (!done.load()) {
+ }
+ });
+ auto const cleanup_done = Cleanup([&] { done.store(true); });
+
+ absl::Time const before =
+ ASSERT_NO_ERRNO_AND_VALUE(GetTime(CLOCK_PROCESS_CPUTIME_ID));
+ EXPECT_THAT(
+ RetryEINTR(sys_clock_nanosleep)(CLOCK_PROCESS_CPUTIME_ID, 0, &dur, &dur),
+ SyscallSucceeds());
+ absl::Time const after =
+ ASSERT_NO_ERRNO_AND_VALUE(GetTime(CLOCK_PROCESS_CPUTIME_ID));
+ EXPECT_GE(after - before, kDuration);
+}
+} // namespace
+
+} // namespace testing
+} // namespace gvisor