// Copyright 2023 The Abseil 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 // // https://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 "absl/synchronization/internal/kernel_timeout.h" #include #include // NOLINT(build/c++11) #include #include "absl/base/config.h" #include "absl/random/random.h" #include "absl/time/clock.h" #include "absl/time/time.h" #include "gtest/gtest.h" // Test go/btm support by randomizing the value of clock_gettime() for // CLOCK_MONOTONIC. This works by overriding a weak symbol in glibc. // We should be resistant to this randomization when !SupportsSteadyClock(). #if defined(__GOOGLE_GRTE_VERSION__) && \ !defined(ABSL_HAVE_ADDRESS_SANITIZER) && \ !defined(ABSL_HAVE_MEMORY_SANITIZER) && \ !defined(ABSL_HAVE_THREAD_SANITIZER) extern "C" int __clock_gettime(clockid_t c, struct timespec* ts); extern "C" int clock_gettime(clockid_t c, struct timespec* ts) { if (c == CLOCK_MONOTONIC && !absl::synchronization_internal::KernelTimeout::SupportsSteadyClock()) { absl::SharedBitGen gen; ts->tv_sec = absl::Uniform(gen, 0, 1'000'000'000); ts->tv_nsec = absl::Uniform(gen, 0, 1'000'000'000); return 0; } return __clock_gettime(c, ts); } #endif namespace { #if defined(ABSL_HAVE_ADDRESS_SANITIZER) || \ defined(ABSL_HAVE_MEMORY_SANITIZER) || \ defined(ABSL_HAVE_THREAD_SANITIZER) || \ defined(__ANDROID__) || \ defined(_WIN32) || defined(_WIN64) constexpr absl::Duration kTimingBound = absl::Milliseconds(5); #else constexpr absl::Duration kTimingBound = absl::Microseconds(250); #endif using absl::synchronization_internal::KernelTimeout; TEST(KernelTimeout, FiniteTimes) { constexpr absl::Duration kDurationsToTest[] = { absl::ZeroDuration(), absl::Nanoseconds(1), absl::Microseconds(1), absl::Milliseconds(1), absl::Seconds(1), absl::Minutes(1), absl::Hours(1), absl::Hours(1000), -absl::Nanoseconds(1), -absl::Microseconds(1), -absl::Milliseconds(1), -absl::Seconds(1), -absl::Minutes(1), -absl::Hours(1), -absl::Hours(1000), }; for (auto duration : kDurationsToTest) { const absl::Time now = absl::Now(); const absl::Time when = now + duration; SCOPED_TRACE(duration); KernelTimeout t(when); EXPECT_TRUE(t.has_timeout()); EXPECT_TRUE(t.is_absolute_timeout()); EXPECT_FALSE(t.is_relative_timeout()); EXPECT_EQ(absl::TimeFromTimespec(t.MakeAbsTimespec()), when); #ifndef _WIN32 EXPECT_LE( absl::AbsDuration(absl::Now() + duration - absl::TimeFromTimespec( t.MakeClockAbsoluteTimespec(CLOCK_REALTIME))), absl::Milliseconds(10)); #endif EXPECT_LE( absl::AbsDuration(absl::DurationFromTimespec(t.MakeRelativeTimespec()) - std::max(duration, absl::ZeroDuration())), kTimingBound); EXPECT_EQ(absl::FromUnixNanos(t.MakeAbsNanos()), when); EXPECT_LE(absl::AbsDuration(absl::Milliseconds(t.InMillisecondsFromNow()) - std::max(duration, absl::ZeroDuration())), absl::Milliseconds(5)); EXPECT_LE(absl::AbsDuration(absl::FromChrono(t.ToChronoTimePoint()) - when), absl::Microseconds(1)); EXPECT_LE(absl::AbsDuration(absl::FromChrono(t.ToChronoDuration()) - std::max(duration, absl::ZeroDuration())), kTimingBound); } } TEST(KernelTimeout, InfiniteFuture) { KernelTimeout t(absl::InfiniteFuture()); EXPECT_FALSE(t.has_timeout()); // Callers are expected to check has_timeout() instead of using the methods // below, but we do try to do something reasonable if they don't. We may not // be able to round-trip back to absl::InfiniteDuration() or // absl::InfiniteFuture(), but we should return a very large value. EXPECT_GT(absl::TimeFromTimespec(t.MakeAbsTimespec()), absl::Now() + absl::Hours(100000)); #ifndef _WIN32 EXPECT_GT(absl::TimeFromTimespec(t.MakeClockAbsoluteTimespec(CLOCK_REALTIME)), absl::Now() + absl::Hours(100000)); #endif EXPECT_GT(absl::DurationFromTimespec(t.MakeRelativeTimespec()), absl::Hours(100000)); EXPECT_GT(absl::FromUnixNanos(t.MakeAbsNanos()), absl::Now() + absl::Hours(100000)); EXPECT_EQ(t.InMillisecondsFromNow(), std::numeric_limits::max()); EXPECT_EQ(t.ToChronoTimePoint(), std::chrono::time_point::max()); EXPECT_GE(t.ToChronoDuration(), std::chrono::nanoseconds::max()); } TEST(KernelTimeout, DefaultConstructor) { // The default constructor is equivalent to absl::InfiniteFuture(). KernelTimeout t; EXPECT_FALSE(t.has_timeout()); // Callers are expected to check has_timeout() instead of using the methods // below, but we do try to do something reasonable if they don't. We may not // be able to round-trip back to absl::InfiniteDuration() or // absl::InfiniteFuture(), but we should return a very large value. EXPECT_GT(absl::TimeFromTimespec(t.MakeAbsTimespec()), absl::Now() + absl::Hours(100000)); #ifndef _WIN32 EXPECT_GT(absl::TimeFromTimespec(t.MakeClockAbsoluteTimespec(CLOCK_REALTIME)), absl::Now() + absl::Hours(100000)); #endif EXPECT_GT(absl::DurationFromTimespec(t.MakeRelativeTimespec()), absl::Hours(100000)); EXPECT_GT(absl::FromUnixNanos(t.MakeAbsNanos()), absl::Now() + absl::Hours(100000)); EXPECT_EQ(t.InMillisecondsFromNow(), std::numeric_limits::max()); EXPECT_EQ(t.ToChronoTimePoint(), std::chrono::time_point::max()); EXPECT_GE(t.ToChronoDuration(), std::chrono::nanoseconds::max()); } TEST(KernelTimeout, TimeMaxNanos) { // Time >= kMaxNanos should behave as no timeout. KernelTimeout t(absl::FromUnixNanos(std::numeric_limits::max())); EXPECT_FALSE(t.has_timeout()); // Callers are expected to check has_timeout() instead of using the methods // below, but we do try to do something reasonable if they don't. We may not // be able to round-trip back to absl::InfiniteDuration() or // absl::InfiniteFuture(), but we should return a very large value. EXPECT_GT(absl::TimeFromTimespec(t.MakeAbsTimespec()), absl::Now() + absl::Hours(100000)); #ifndef _WIN32 EXPECT_GT(absl::TimeFromTimespec(t.MakeClockAbsoluteTimespec(CLOCK_REALTIME)), absl::Now() + absl::Hours(100000)); #endif EXPECT_GT(absl::DurationFromTimespec(t.MakeRelativeTimespec()), absl::Hours(100000)); EXPECT_GT(absl::FromUnixNanos(t.MakeAbsNanos()), absl::Now() + absl::Hours(100000)); EXPECT_EQ(t.InMillisecondsFromNow(), std::numeric_limits::max()); EXPECT_EQ(t.ToChronoTimePoint(), std::chrono::time_point::max()); EXPECT_GE(t.ToChronoDuration(), std::chrono::nanoseconds::max()); } TEST(KernelTimeout, Never) { // KernelTimeout::Never() is equivalent to absl::InfiniteFuture(). KernelTimeout t = KernelTimeout::Never(); EXPECT_FALSE(t.has_timeout()); // Callers are expected to check has_timeout() instead of using the methods // below, but we do try to do something reasonable if they don't. We may not // be able to round-trip back to absl::InfiniteDuration() or // absl::InfiniteFuture(), but we should return a very large value. EXPECT_GT(absl::TimeFromTimespec(t.MakeAbsTimespec()), absl::Now() + absl::Hours(100000)); #ifndef _WIN32 EXPECT_GT(absl::TimeFromTimespec(t.MakeClockAbsoluteTimespec(CLOCK_REALTIME)), absl::Now() + absl::Hours(100000)); #endif EXPECT_GT(absl::DurationFromTimespec(t.MakeRelativeTimespec()), absl::Hours(100000)); EXPECT_GT(absl::FromUnixNanos(t.MakeAbsNanos()), absl::Now() + absl::Hours(100000)); EXPECT_EQ(t.InMillisecondsFromNow(), std::numeric_limits::max()); EXPECT_EQ(t.ToChronoTimePoint(), std::chrono::time_point::max()); EXPECT_GE(t.ToChronoDuration(), std::chrono::nanoseconds::max()); } TEST(KernelTimeout, InfinitePast) { KernelTimeout t(absl::InfinitePast()); EXPECT_TRUE(t.has_timeout()); EXPECT_TRUE(t.is_absolute_timeout()); EXPECT_FALSE(t.is_relative_timeout()); EXPECT_LE(absl::TimeFromTimespec(t.MakeAbsTimespec()), absl::FromUnixNanos(1)); #ifndef _WIN32 EXPECT_LE(absl::TimeFromTimespec(t.MakeClockAbsoluteTimespec(CLOCK_REALTIME)), absl::FromUnixSeconds(1)); #endif EXPECT_EQ(absl::DurationFromTimespec(t.MakeRelativeTimespec()), absl::ZeroDuration()); EXPECT_LE(absl::FromUnixNanos(t.MakeAbsNanos()), absl::FromUnixNanos(1)); EXPECT_EQ(t.InMillisecondsFromNow(), KernelTimeout::DWord{0}); EXPECT_LT(t.ToChronoTimePoint(), std::chrono::system_clock::from_time_t(0) + std::chrono::seconds(1)); EXPECT_EQ(t.ToChronoDuration(), std::chrono::nanoseconds(0)); } TEST(KernelTimeout, FiniteDurations) { constexpr absl::Duration kDurationsToTest[] = { absl::ZeroDuration(), absl::Nanoseconds(1), absl::Microseconds(1), absl::Milliseconds(1), absl::Seconds(1), absl::Minutes(1), absl::Hours(1), absl::Hours(1000), }; for (auto duration : kDurationsToTest) { SCOPED_TRACE(duration); KernelTimeout t(duration); EXPECT_TRUE(t.has_timeout()); EXPECT_FALSE(t.is_absolute_timeout()); EXPECT_TRUE(t.is_relative_timeout()); EXPECT_LE(absl::AbsDuration(absl::Now() + duration - absl::TimeFromTimespec(t.MakeAbsTimespec())), absl::Milliseconds(5)); #ifndef _WIN32 EXPECT_LE( absl::AbsDuration(absl::Now() + duration - absl::TimeFromTimespec( t.MakeClockAbsoluteTimespec(CLOCK_REALTIME))), absl::Milliseconds(5)); #endif EXPECT_LE( absl::AbsDuration(absl::DurationFromTimespec(t.MakeRelativeTimespec()) - duration), kTimingBound); EXPECT_LE(absl::AbsDuration(absl::Now() + duration - absl::FromUnixNanos(t.MakeAbsNanos())), absl::Milliseconds(5)); EXPECT_LE(absl::Milliseconds(t.InMillisecondsFromNow()) - duration, absl::Milliseconds(5)); EXPECT_LE(absl::AbsDuration(absl::Now() + duration - absl::FromChrono(t.ToChronoTimePoint())), kTimingBound); EXPECT_LE( absl::AbsDuration(absl::FromChrono(t.ToChronoDuration()) - duration), kTimingBound); } } TEST(KernelTimeout, NegativeDurations) { constexpr absl::Duration kDurationsToTest[] = { -absl::ZeroDuration(), -absl::Nanoseconds(1), -absl::Microseconds(1), -absl::Milliseconds(1), -absl::Seconds(1), -absl::Minutes(1), -absl::Hours(1), -absl::Hours(1000), -absl::InfiniteDuration(), }; for (auto duration : kDurationsToTest) { // Negative durations should all be converted to zero durations or "now". SCOPED_TRACE(duration); KernelTimeout t(duration); EXPECT_TRUE(t.has_timeout()); EXPECT_FALSE(t.is_absolute_timeout()); EXPECT_TRUE(t.is_relative_timeout()); EXPECT_LE(absl::AbsDuration(absl::Now() - absl::TimeFromTimespec(t.MakeAbsTimespec())), absl::Milliseconds(5)); #ifndef _WIN32 EXPECT_LE(absl::AbsDuration(absl::Now() - absl::TimeFromTimespec( t.MakeClockAbsoluteTimespec( CLOCK_REALTIME))), absl::Milliseconds(5)); #endif EXPECT_EQ(absl::DurationFromTimespec(t.MakeRelativeTimespec()), absl::ZeroDuration()); EXPECT_LE( absl::AbsDuration(absl::Now() - absl::FromUnixNanos(t.MakeAbsNanos())), absl::Milliseconds(5)); EXPECT_EQ(t.InMillisecondsFromNow(), KernelTimeout::DWord{0}); EXPECT_LE(absl::AbsDuration(absl::Now() - absl::FromChrono(t.ToChronoTimePoint())), absl::Milliseconds(5)); EXPECT_EQ(t.ToChronoDuration(), std::chrono::nanoseconds(0)); } } TEST(KernelTimeout, InfiniteDuration) { KernelTimeout t(absl::InfiniteDuration()); EXPECT_FALSE(t.has_timeout()); // Callers are expected to check has_timeout() instead of using the methods // below, but we do try to do something reasonable if they don't. We may not // be able to round-trip back to absl::InfiniteDuration() or // absl::InfiniteFuture(), but we should return a very large value. EXPECT_GT(absl::TimeFromTimespec(t.MakeAbsTimespec()), absl::Now() + absl::Hours(100000)); #ifndef _WIN32 EXPECT_GT(absl::TimeFromTimespec(t.MakeClockAbsoluteTimespec(CLOCK_REALTIME)), absl::Now() + absl::Hours(100000)); #endif EXPECT_GT(absl::DurationFromTimespec(t.MakeRelativeTimespec()), absl::Hours(100000)); EXPECT_GT(absl::FromUnixNanos(t.MakeAbsNanos()), absl::Now() + absl::Hours(100000)); EXPECT_EQ(t.InMillisecondsFromNow(), std::numeric_limits::max()); EXPECT_EQ(t.ToChronoTimePoint(), std::chrono::time_point::max()); EXPECT_GE(t.ToChronoDuration(), std::chrono::nanoseconds::max()); } TEST(KernelTimeout, DurationMaxNanos) { // Duration >= kMaxNanos should behave as no timeout. KernelTimeout t(absl::Nanoseconds(std::numeric_limits::max())); EXPECT_FALSE(t.has_timeout()); // Callers are expected to check has_timeout() instead of using the methods // below, but we do try to do something reasonable if they don't. We may not // be able to round-trip back to absl::InfiniteDuration() or // absl::InfiniteFuture(), but we should return a very large value. EXPECT_GT(absl::TimeFromTimespec(t.MakeAbsTimespec()), absl::Now() + absl::Hours(100000)); #ifndef _WIN32 EXPECT_GT(absl::TimeFromTimespec(t.MakeClockAbsoluteTimespec(CLOCK_REALTIME)), absl::Now() + absl::Hours(100000)); #endif EXPECT_GT(absl::DurationFromTimespec(t.MakeRelativeTimespec()), absl::Hours(100000)); EXPECT_GT(absl::FromUnixNanos(t.MakeAbsNanos()), absl::Now() + absl::Hours(100000)); EXPECT_EQ(t.InMillisecondsFromNow(), std::numeric_limits::max()); EXPECT_EQ(t.ToChronoTimePoint(), std::chrono::time_point::max()); EXPECT_GE(t.ToChronoDuration(), std::chrono::nanoseconds::max()); } TEST(KernelTimeout, OverflowNanos) { // Test what happens when KernelTimeout is constructed with an absl::Duration // that would overflow now_nanos + duration. int64_t now_nanos = absl::ToUnixNanos(absl::Now()); int64_t limit = std::numeric_limits::max() - now_nanos; absl::Duration duration = absl::Nanoseconds(limit) + absl::Seconds(1); KernelTimeout t(duration); // Timeouts should still be far in the future. EXPECT_GT(absl::TimeFromTimespec(t.MakeAbsTimespec()), absl::Now() + absl::Hours(100000)); #ifndef _WIN32 EXPECT_GT(absl::TimeFromTimespec(t.MakeClockAbsoluteTimespec(CLOCK_REALTIME)), absl::Now() + absl::Hours(100000)); #endif EXPECT_GT(absl::DurationFromTimespec(t.MakeRelativeTimespec()), absl::Hours(100000)); EXPECT_GT(absl::FromUnixNanos(t.MakeAbsNanos()), absl::Now() + absl::Hours(100000)); EXPECT_LE(absl::Milliseconds(t.InMillisecondsFromNow()) - duration, absl::Milliseconds(5)); EXPECT_GT(t.ToChronoTimePoint(), std::chrono::system_clock::now() + std::chrono::hours(100000)); EXPECT_GT(t.ToChronoDuration(), std::chrono::hours(100000)); } } // namespace