// 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 "gtest/gtest.h" #include "absl/base/config.h" #include "absl/time/clock.h" #include "absl/time/time.h" namespace { #if defined(ABSL_HAVE_ADDRESS_SANITIZER) || \ defined(ABSL_HAVE_MEMORY_SANITIZER) || \ defined(ABSL_HAVE_THREAD_SANITIZER) || defined(__ANDROID__) 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); // MakeRelativeTimespec() doesn't quite round trip when using an absolute // time, but it should get pretty close. Past times are converted to zero // durations. 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)); } } 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)); 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()); } 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)); 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()); } 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)); 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()); } 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)); 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()); } 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)); EXPECT_EQ(absl::DurationFromTimespec(t.MakeRelativeTimespec()), absl::ZeroDuration()); EXPECT_LE(absl::FromUnixNanos(t.MakeAbsNanos()), absl::FromUnixNanos(1)); EXPECT_EQ(t.InMillisecondsFromNow(), KernelTimeout::DWord{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)); EXPECT_EQ(absl::DurationFromTimespec(t.MakeRelativeTimespec()), duration); EXPECT_LE(absl::AbsDuration(absl::Now() + duration - absl::FromUnixNanos(t.MakeAbsNanos())), absl::Milliseconds(5)); EXPECT_LE(absl::Milliseconds(t.InMillisecondsFromNow()) - duration, absl::Milliseconds(5)); } } 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)); 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}); } } 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)); 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()); } 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)); 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()); } 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); EXPECT_TRUE(t.has_timeout()); // Timeouts should still be far in the future. EXPECT_GT(absl::TimeFromTimespec(t.MakeAbsTimespec()), absl::Now() + absl::Hours(100000)); 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)); } } // namespace