From fa4855403cdfb91b9c16f792041258ed819594c1 Mon Sep 17 00:00:00 2001 From: Derek Mauro Date: Thu, 9 Feb 2023 19:45:05 -0800 Subject: Rewrite KernelTimeout to support both absolute and relative timeouts APIs that take KernelTimeout as a parameter can now query if an absolute or relative timeout was requested. If the underlying API can only use one type of timeout, the code will do a reasonable conversion. The goal is to eventually enable the possibility of using wait times that are based on monotonic clocks that are safe against system clock steps. PiperOrigin-RevId: 508541507 Change-Id: Id08bf13515f3e1bfd78d88393cde98a6fd3ef72c --- .../internal/kernel_timeout_test.cc | 278 +++++++++++++++++++++ 1 file changed, 278 insertions(+) create mode 100644 absl/synchronization/internal/kernel_timeout_test.cc (limited to 'absl/synchronization/internal/kernel_timeout_test.cc') diff --git a/absl/synchronization/internal/kernel_timeout_test.cc b/absl/synchronization/internal/kernel_timeout_test.cc new file mode 100644 index 00000000..a89ae220 --- /dev/null +++ b/absl/synchronization/internal/kernel_timeout_test.cc @@ -0,0 +1,278 @@ +// 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 -- cgit v1.2.3 From 0372af19f2f4f588351ca8f71b3f528cbe467a4d Mon Sep 17 00:00:00 2001 From: Derek Mauro Date: Thu, 16 Feb 2023 11:52:26 -0800 Subject: Add KernelTimeout methods that convert the timeout to the std::chrono methods used by std::condition_variable. A followup change will add an implemention of synchronization_internal::Waiter that can use std:mutex/std::condition_variable to implement the per-thread semaphore that absl::Mutex waits on. This implementation may at some point become the default on platforms such as Windows where there doesn't seem to be an easy way of supporting real absolute timeouts. In this case we can defer to their standard library to implement correct support. PiperOrigin-RevId: 510204786 Change-Id: Icf4d695013fd060abbd53dae23e71ea36f731565 --- absl/synchronization/internal/kernel_timeout.cc | 41 ++++++++++++++++++++++ absl/synchronization/internal/kernel_timeout.h | 15 ++++++++ .../internal/kernel_timeout_test.cc | 38 ++++++++++++++++++++ 3 files changed, 94 insertions(+) (limited to 'absl/synchronization/internal/kernel_timeout_test.cc') diff --git a/absl/synchronization/internal/kernel_timeout.cc b/absl/synchronization/internal/kernel_timeout.cc index 8d9e7d74..548a8fc6 100644 --- a/absl/synchronization/internal/kernel_timeout.cc +++ b/absl/synchronization/internal/kernel_timeout.cc @@ -15,6 +15,7 @@ #include "absl/synchronization/internal/kernel_timeout.h" #include +#include // NOLINT(build/c++11) #include #include #include @@ -163,6 +164,46 @@ KernelTimeout::DWord KernelTimeout::InMillisecondsFromNow() const { return DWord{0}; } +std::chrono::time_point +KernelTimeout::ToChronoTimePoint() const { + if (!has_timeout()) { + return std::chrono::time_point::max(); + } + + // The cast to std::microseconds is because (on some platforms) the + // std::ratio used by std::chrono::steady_clock doesn't convert to + // std::nanoseconds, so it doesn't compile. + auto micros = std::chrono::duration_cast( + std::chrono::nanoseconds(RawNanos())); + if (is_relative_timeout()) { + auto now = std::chrono::system_clock::now(); + if (micros > + std::chrono::time_point::max() - now) { + // Overflow. + return std::chrono::time_point::max(); + } + return now + micros; + } + return std::chrono::system_clock::from_time_t(0) + micros; +} + +std::chrono::nanoseconds KernelTimeout::ToChronoDuration() const { + if (!has_timeout()) { + return std::chrono::nanoseconds::max(); + } + if (is_absolute_timeout()) { + auto d = std::chrono::duration_cast( + std::chrono::nanoseconds(RawNanos()) - + (std::chrono::system_clock::now() - + std::chrono::system_clock::from_time_t(0))); + if (d < std::chrono::nanoseconds(0)) { + d = std::chrono::nanoseconds(0); + } + return d; + } + return std::chrono::nanoseconds(RawNanos()); +} + } // namespace synchronization_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/synchronization/internal/kernel_timeout.h b/absl/synchronization/internal/kernel_timeout.h index 1f4d82cd..f7c40337 100644 --- a/absl/synchronization/internal/kernel_timeout.h +++ b/absl/synchronization/internal/kernel_timeout.h @@ -16,6 +16,7 @@ #define ABSL_SYNCHRONIZATION_INTERNAL_KERNEL_TIMEOUT_H_ #include +#include // NOLINT(build/c++11) #include #include #include @@ -95,6 +96,20 @@ class KernelTimeout { typedef unsigned long DWord; // NOLINT DWord InMillisecondsFromNow() const; + // Convert to std::chrono::time_point for interfaces that expect an absolute + // timeout, like std::condition_variable::wait_until(). If !has_timeout() or + // is_relative_timeout(), attempts to convert to a reasonable absolute + // timeout, but callers should test has_timeout() and is_relative_timeout() + // and prefer to use a more appropriate interface. + std::chrono::time_point ToChronoTimePoint() const; + + // Convert to std::chrono::time_point for interfaces that expect a relative + // timeout, like std::condition_variable::wait_for(). If !has_timeout() or + // is_absolute_timeout(), attempts to convert to a reasonable relative + // timeout, but callers should test has_timeout() and is_absolute_timeout() + // and prefer to use a more appropriate interface. + std::chrono::nanoseconds ToChronoDuration() const; + private: // Internal representation. // - If the value is kNoTimeout, then the timeout is infinite, and diff --git a/absl/synchronization/internal/kernel_timeout_test.cc b/absl/synchronization/internal/kernel_timeout_test.cc index a89ae220..431ffcf4 100644 --- a/absl/synchronization/internal/kernel_timeout_test.cc +++ b/absl/synchronization/internal/kernel_timeout_test.cc @@ -14,6 +14,7 @@ #include "absl/synchronization/internal/kernel_timeout.h" +#include // NOLINT(build/c++11) #include #include "gtest/gtest.h" @@ -72,6 +73,11 @@ TEST(KernelTimeout, FiniteTimes) { 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); } } @@ -90,6 +96,9 @@ TEST(KernelTimeout, InfiniteFuture) { 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) { @@ -108,6 +117,9 @@ TEST(KernelTimeout, DefaultConstructor) { 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) { @@ -126,6 +138,9 @@ TEST(KernelTimeout, TimeMaxNanos) { 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) { @@ -144,6 +159,9 @@ TEST(KernelTimeout, Never) { 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) { @@ -157,6 +175,9 @@ TEST(KernelTimeout, InfinitePast) { 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) { @@ -186,6 +207,10 @@ TEST(KernelTimeout, FiniteDurations) { 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_EQ(absl::FromChrono(t.ToChronoDuration()), duration); } } @@ -218,6 +243,10 @@ TEST(KernelTimeout, NegativeDurations) { 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)); } } @@ -236,6 +265,9 @@ TEST(KernelTimeout, InfiniteDuration) { 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) { @@ -254,6 +286,9 @@ TEST(KernelTimeout, DurationMaxNanos) { 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) { @@ -273,6 +308,9 @@ TEST(KernelTimeout, OverflowNanos) { 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 -- cgit v1.2.3 From 7f47b00fca75bee477c8a2e3e8fc74a1cf7c743f Mon Sep 17 00:00:00 2001 From: Derek Mauro Date: Mon, 13 Mar 2023 13:24:00 -0700 Subject: Synchronization: Change KernelTimeout to always store absolute timeouts, but when a relative timeout is provided, the timeout is an absolute timeout against a steady clock (when possible). This allows methods that return relative timeouts to automatically recompute the remaining duration, for instance, on suprious wakeups. PiperOrigin-RevId: 516304139 Change-Id: I7d739cb50dd749eba5dba7ac6c34d18dc53703ed --- absl/synchronization/internal/kernel_timeout.cc | 108 +++++++++------------ absl/synchronization/internal/kernel_timeout.h | 24 ++++- .../internal/kernel_timeout_test.cc | 13 +-- 3 files changed, 72 insertions(+), 73 deletions(-) (limited to 'absl/synchronization/internal/kernel_timeout_test.cc') diff --git a/absl/synchronization/internal/kernel_timeout.cc b/absl/synchronization/internal/kernel_timeout.cc index 548a8fc6..2e485097 100644 --- a/absl/synchronization/internal/kernel_timeout.cc +++ b/absl/synchronization/internal/kernel_timeout.cc @@ -32,6 +32,18 @@ constexpr uint64_t KernelTimeout::kNoTimeout; constexpr int64_t KernelTimeout::kMaxNanos; #endif +int64_t KernelTimeout::SteadyClockNow() { +#ifdef __GOOGLE_GRTE_VERSION__ + // go/btm requires synchronized clocks, so we have to use the system + // clock. + return absl::GetCurrentTimeNanos(); +#else + return std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()) + .count(); +#endif +} + KernelTimeout::KernelTimeout(absl::Time t) { // `absl::InfiniteFuture()` is a common "no timeout" value and cheaper to // compare than convert. @@ -73,12 +85,14 @@ KernelTimeout::KernelTimeout(absl::Duration d) { nanos = 0; } - // Values greater than or equal to kMaxNanos are converted to infinite. - if (nanos >= kMaxNanos) { + int64_t now = SteadyClockNow(); + if (nanos > kMaxNanos - now) { + // Durations that would be greater than kMaxNanos are converted to infinite. rep_ = kNoTimeout; return; } + nanos += now; rep_ = (static_cast(nanos) << 1) | uint64_t{1}; } @@ -90,6 +104,9 @@ int64_t KernelTimeout::MakeAbsNanos() const { int64_t nanos = RawNanos(); if (is_relative_timeout()) { + // We need to change epochs, because the relative timeout might be + // represented by an absolute timestamp from another clock. + nanos = std::max(nanos - SteadyClockNow(), 0); int64_t now = absl::GetCurrentTimeNanos(); if (nanos > kMaxNanos - now) { // Overflow. @@ -106,27 +123,24 @@ int64_t KernelTimeout::MakeAbsNanos() const { return nanos; } -struct timespec KernelTimeout::MakeAbsTimespec() const { - return absl::ToTimespec(absl::Nanoseconds(MakeAbsNanos())); -} - -struct timespec KernelTimeout::MakeRelativeTimespec() const { +int64_t KernelTimeout::InNanosecondsFromNow() const { if (!has_timeout()) { - return absl::ToTimespec(absl::Nanoseconds(kMaxNanos)); - } - if (is_relative_timeout()) { - return absl::ToTimespec(absl::Nanoseconds(RawNanos())); + return kMaxNanos; } int64_t nanos = RawNanos(); - int64_t now = absl::GetCurrentTimeNanos(); - if (now > nanos) { - // Convert past values to 0 to be safe. - nanos = 0; - } else { - nanos -= now; + if (is_absolute_timeout()) { + return std::max(nanos - absl::GetCurrentTimeNanos(), 0); } - return absl::ToTimespec(absl::Nanoseconds(nanos)); + return std::max(nanos - SteadyClockNow(), 0); +} + +struct timespec KernelTimeout::MakeAbsTimespec() const { + return absl::ToTimespec(absl::Nanoseconds(MakeAbsNanos())); +} + +struct timespec KernelTimeout::MakeRelativeTimespec() const { + return absl::ToTimespec(absl::Nanoseconds(InNanosecondsFromNow())); } KernelTimeout::DWord KernelTimeout::InMillisecondsFromNow() const { @@ -136,32 +150,21 @@ KernelTimeout::DWord KernelTimeout::InMillisecondsFromNow() const { return kInfinite; } - const int64_t nanos = RawNanos(); - constexpr uint64_t kNanosInMillis = uint64_t{1000000}; + constexpr uint64_t kNanosInMillis = uint64_t{1'000'000}; + constexpr uint64_t kMaxValueNanos = + std::numeric_limits::max() - kNanosInMillis + 1; - if (is_relative_timeout()) { - uint64_t ms = static_cast(nanos) / kNanosInMillis; - if (ms > static_cast(kInfinite)) { - ms = static_cast(kInfinite); - } - return static_cast(ms); + uint64_t ns_from_now = static_cast(InNanosecondsFromNow()); + if (ns_from_now >= kMaxValueNanos) { + // Rounding up would overflow. + return kInfinite; } - - int64_t now = absl::GetCurrentTimeNanos(); - if (nanos >= now) { - // Round up so that now + ms_from_now >= nanos. - constexpr uint64_t kMaxValueNanos = - std::numeric_limits::max() - kNanosInMillis + 1; - uint64_t ms_from_now = - (std::min(kMaxValueNanos, static_cast(nanos - now)) + - kNanosInMillis - 1) / - kNanosInMillis; - if (ms_from_now > kInfinite) { - return kInfinite; - } - return static_cast(ms_from_now); + // Convert to milliseconds, always rounding up. + uint64_t ms_from_now = (ns_from_now + kNanosInMillis - 1) / kNanosInMillis; + if (ms_from_now > kInfinite) { + return kInfinite; } - return DWord{0}; + return static_cast(ms_from_now); } std::chrono::time_point @@ -174,16 +177,7 @@ KernelTimeout::ToChronoTimePoint() const { // std::ratio used by std::chrono::steady_clock doesn't convert to // std::nanoseconds, so it doesn't compile. auto micros = std::chrono::duration_cast( - std::chrono::nanoseconds(RawNanos())); - if (is_relative_timeout()) { - auto now = std::chrono::system_clock::now(); - if (micros > - std::chrono::time_point::max() - now) { - // Overflow. - return std::chrono::time_point::max(); - } - return now + micros; - } + std::chrono::nanoseconds(MakeAbsNanos())); return std::chrono::system_clock::from_time_t(0) + micros; } @@ -191,17 +185,7 @@ std::chrono::nanoseconds KernelTimeout::ToChronoDuration() const { if (!has_timeout()) { return std::chrono::nanoseconds::max(); } - if (is_absolute_timeout()) { - auto d = std::chrono::duration_cast( - std::chrono::nanoseconds(RawNanos()) - - (std::chrono::system_clock::now() - - std::chrono::system_clock::from_time_t(0))); - if (d < std::chrono::nanoseconds(0)) { - d = std::chrono::nanoseconds(0); - } - return d; - } - return std::chrono::nanoseconds(RawNanos()); + return std::chrono::nanoseconds(InNanosecondsFromNow()); } } // namespace synchronization_internal diff --git a/absl/synchronization/internal/kernel_timeout.h b/absl/synchronization/internal/kernel_timeout.h index f7c40337..e2cf3c2a 100644 --- a/absl/synchronization/internal/kernel_timeout.h +++ b/absl/synchronization/internal/kernel_timeout.h @@ -75,7 +75,9 @@ class KernelTimeout { // Convert to `struct timespec` for interfaces that expect a relative // timeout. If !has_timeout() or is_absolute_timeout(), attempts to convert to // a reasonable relative timeout, but callers should to test has_timeout() and - // is_absolute_timeout() and prefer to use a more appropriate interface. + // is_absolute_timeout() and prefer to use a more appropriate interface. Since + // the return value is a relative duration, it should be recomputed by calling + // this method in the case of a spurious wakeup. struct timespec MakeRelativeTimespec() const; // Convert to unix epoch nanos for interfaces that expect an absolute timeout @@ -107,17 +109,24 @@ class KernelTimeout { // timeout, like std::condition_variable::wait_for(). If !has_timeout() or // is_absolute_timeout(), attempts to convert to a reasonable relative // timeout, but callers should test has_timeout() and is_absolute_timeout() - // and prefer to use a more appropriate interface. + // and prefer to use a more appropriate interface. Since the return value is a + // relative duration, it should be recomputed by calling this method in the + // case of a spurious wakeup. std::chrono::nanoseconds ToChronoDuration() const; private: + // Returns the current time, expressed as a count of nanoseconds since the + // epoch used by an arbitrary clock. The implementation tries to use a steady + // (monotonic) clock if one is available. + static int64_t SteadyClockNow(); + // Internal representation. // - If the value is kNoTimeout, then the timeout is infinite, and // has_timeout() will return true. - // - If the low bit is 0, then the high 63 bits is number of nanoseconds + // - If the low bit is 0, then the high 63 bits is the number of nanoseconds // after the unix epoch. - // - If the low bit is 1, then the high 63 bits is a relative duration in - // nanoseconds. + // - If the low bit is 1, then the high 63 bits is the number of nanoseconds + // after the epoch used by SteadyClockNow(). uint64_t rep_; // Returns the number of nanoseconds stored in the internal representation. @@ -125,6 +134,11 @@ class KernelTimeout { // value is used to compute when the timeout should occur. int64_t RawNanos() const { return static_cast(rep_ >> 1); } + // Converts to nanoseconds from now. Since the return value is a relative + // duration, it should be recomputed by calling this method in the case of a + // spurious wakeup. + int64_t InNanosecondsFromNow() const; + // A value that represents no timeout (or an infinite timeout). static constexpr uint64_t kNoTimeout = (std::numeric_limits::max)(); diff --git a/absl/synchronization/internal/kernel_timeout_test.cc b/absl/synchronization/internal/kernel_timeout_test.cc index 431ffcf4..a96f806f 100644 --- a/absl/synchronization/internal/kernel_timeout_test.cc +++ b/absl/synchronization/internal/kernel_timeout_test.cc @@ -62,9 +62,6 @@ TEST(KernelTimeout, FiniteTimes) { 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())), @@ -201,7 +198,10 @@ TEST(KernelTimeout, FiniteDurations) { 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::DurationFromTimespec(t.MakeRelativeTimespec()) - + duration), + kTimingBound); EXPECT_LE(absl::AbsDuration(absl::Now() + duration - absl::FromUnixNanos(t.MakeAbsNanos())), absl::Milliseconds(5)); @@ -210,7 +210,9 @@ TEST(KernelTimeout, FiniteDurations) { EXPECT_LE(absl::AbsDuration(absl::Now() + duration - absl::FromChrono(t.ToChronoTimePoint())), kTimingBound); - EXPECT_EQ(absl::FromChrono(t.ToChronoDuration()), duration); + EXPECT_LE( + absl::AbsDuration(absl::FromChrono(t.ToChronoDuration()) - duration), + kTimingBound); } } @@ -298,7 +300,6 @@ TEST(KernelTimeout, OverflowNanos) { 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)); -- cgit v1.2.3 From 0697762c62cdb51ead8d9c2f0d299c5d4a4ff9db Mon Sep 17 00:00:00 2001 From: Tom Rybka Date: Thu, 16 Mar 2023 12:20:11 -0700 Subject: Increase the timeout of Windows for the kernel timeout tests. Windows tests often run in Emulation, and even with KVM we can still timeout. PiperOrigin-RevId: 517192968 Change-Id: I3b4e435f8ac8ad1e7eab6f043c051fa75efed64b --- absl/synchronization/internal/kernel_timeout_test.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'absl/synchronization/internal/kernel_timeout_test.cc') diff --git a/absl/synchronization/internal/kernel_timeout_test.cc b/absl/synchronization/internal/kernel_timeout_test.cc index a96f806f..26ee34a2 100644 --- a/absl/synchronization/internal/kernel_timeout_test.cc +++ b/absl/synchronization/internal/kernel_timeout_test.cc @@ -26,7 +26,9 @@ namespace { #if defined(ABSL_HAVE_ADDRESS_SANITIZER) || \ defined(ABSL_HAVE_MEMORY_SANITIZER) || \ - defined(ABSL_HAVE_THREAD_SANITIZER) || defined(__ANDROID__) + 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); -- cgit v1.2.3 From 1a72ea7bb8ba27679dd8b179a4671d7d9b099d4b Mon Sep 17 00:00:00 2001 From: Derek Mauro Date: Sat, 8 Apr 2023 09:52:09 -0700 Subject: Synchronization: Support true relative timeouts using the POSIX proposed standard pthread_cond_clockwait() and sem_clockwait(). These are currently implemented in glibc >= 2.30. These methods take a clock and use an absolute time with reference to that clock, so KernelTimeout now can produce these values. PiperOrigin-RevId: 522824226 Change-Id: Ife98713f6f95d800b1f8e52d5364a3dbebc4f8a6 --- absl/synchronization/internal/kernel_timeout.cc | 35 ++++++++++++++- absl/synchronization/internal/kernel_timeout.h | 28 ++++++++++-- .../internal/kernel_timeout_test.cc | 52 ++++++++++++++++++++++ absl/synchronization/internal/pthread_waiter.cc | 10 +++++ absl/synchronization/internal/sem_waiter.cc | 34 +++++++++++--- absl/synchronization/internal/sem_waiter.h | 2 + 6 files changed, 150 insertions(+), 11 deletions(-) (limited to 'absl/synchronization/internal/kernel_timeout_test.cc') diff --git a/absl/synchronization/internal/kernel_timeout.cc b/absl/synchronization/internal/kernel_timeout.cc index 2e485097..bfbf282f 100644 --- a/absl/synchronization/internal/kernel_timeout.cc +++ b/absl/synchronization/internal/kernel_timeout.cc @@ -14,6 +14,10 @@ #include "absl/synchronization/internal/kernel_timeout.h" +#ifndef _WIN32 +#include +#endif + #include #include // NOLINT(build/c++11) #include @@ -101,7 +105,7 @@ int64_t KernelTimeout::MakeAbsNanos() const { return kMaxNanos; } - int64_t nanos = RawNanos(); + int64_t nanos = RawAbsNanos(); if (is_relative_timeout()) { // We need to change epochs, because the relative timeout might be @@ -128,7 +132,7 @@ int64_t KernelTimeout::InNanosecondsFromNow() const { return kMaxNanos; } - int64_t nanos = RawNanos(); + int64_t nanos = RawAbsNanos(); if (is_absolute_timeout()) { return std::max(nanos - absl::GetCurrentTimeNanos(), 0); } @@ -143,6 +147,33 @@ struct timespec KernelTimeout::MakeRelativeTimespec() const { return absl::ToTimespec(absl::Nanoseconds(InNanosecondsFromNow())); } +#ifndef _WIN32 +struct timespec KernelTimeout::MakeClockAbsoluteTimespec(clockid_t c) const { + if (!has_timeout()) { + return absl::ToTimespec(absl::Nanoseconds(kMaxNanos)); + } + + int64_t nanos = RawAbsNanos(); + if (is_absolute_timeout()) { + nanos -= absl::GetCurrentTimeNanos(); + } else { + nanos -= SteadyClockNow(); + } + + struct timespec now; + ABSL_RAW_CHECK(clock_gettime(c, &now) == 0, "clock_gettime() failed"); + absl::Duration from_clock_epoch = + absl::DurationFromTimespec(now) + absl::Nanoseconds(nanos); + if (from_clock_epoch <= absl::ZeroDuration()) { + // Some callers have assumed that 0 means no timeout, so instead we return a + // time of 1 nanosecond after the epoch. For safety we also do not return + // negative values. + return absl::ToTimespec(absl::Nanoseconds(1)); + } + return absl::ToTimespec(from_clock_epoch); +} +#endif + KernelTimeout::DWord KernelTimeout::InMillisecondsFromNow() const { constexpr DWord kInfinite = std::numeric_limits::max(); diff --git a/absl/synchronization/internal/kernel_timeout.h b/absl/synchronization/internal/kernel_timeout.h index 952bd2c6..4e361a6a 100644 --- a/absl/synchronization/internal/kernel_timeout.h +++ b/absl/synchronization/internal/kernel_timeout.h @@ -15,6 +15,10 @@ #ifndef ABSL_SYNCHRONIZATION_INTERNAL_KERNEL_TIMEOUT_H_ #define ABSL_SYNCHRONIZATION_INTERNAL_KERNEL_TIMEOUT_H_ +#ifndef _WIN32 +#include +#endif + #include #include // NOLINT(build/c++11) #include @@ -78,6 +82,18 @@ class KernelTimeout { // this method in the case of a spurious wakeup. struct timespec MakeRelativeTimespec() const; +#ifndef _WIN32 + // Convert to `struct timespec` for interfaces that expect an absolute timeout + // on a specific clock `c`. This is similar to `MakeAbsTimespec()`, but + // callers usually want to use this method with `CLOCK_MONOTONIC` when + // relative timeouts are requested, and when the appropriate interface expects + // an absolute timeout relative to a specific clock (for example, + // pthread_cond_clockwait() or sem_clockwait()). If !has_timeout(), attempts + // to convert to a reasonable absolute timeout, but callers should to test + // has_timeout() prefer to use a more appropriate interface. + struct timespec MakeClockAbsoluteTimespec(clockid_t c) const; +#endif + // Convert to unix epoch nanos for interfaces that expect an absolute timeout // in nanoseconds. If !has_timeout() or is_relative_timeout(), attempts to // convert to a reasonable absolute timeout, but callers should to test @@ -125,12 +141,18 @@ class KernelTimeout { // after the unix epoch. // - If the low bit is 1, then the high 63 bits is the number of nanoseconds // after the epoch used by SteadyClockNow(). + // + // In all cases the time is stored as an absolute time, the only difference is + // the clock epoch. The use of absolute times is important since in the case + // of a relative timeout with a spurious wakeup, the program would have to + // restart the wait, and thus needs a way of recomputing the remaining time. uint64_t rep_; // Returns the number of nanoseconds stored in the internal representation. - // Together with is_absolute_timeout() and is_relative_timeout(), the return - // value is used to compute when the timeout should occur. - int64_t RawNanos() const { return static_cast(rep_ >> 1); } + // When combined with the clock epoch indicated by the low bit (which is + // accessed through is_absolute_timeout() and is_relative_timeout()), the + // return value is used to compute when the timeout should occur. + int64_t RawAbsNanos() const { return static_cast(rep_ >> 1); } // Converts to nanoseconds from now. Since the return value is a relative // duration, it should be recomputed by calling this method in the case of a diff --git a/absl/synchronization/internal/kernel_timeout_test.cc b/absl/synchronization/internal/kernel_timeout_test.cc index 26ee34a2..c853c4bc 100644 --- a/absl/synchronization/internal/kernel_timeout_test.cc +++ b/absl/synchronization/internal/kernel_timeout_test.cc @@ -64,6 +64,13 @@ TEST(KernelTimeout, FiniteTimes) { 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())), @@ -89,6 +96,10 @@ TEST(KernelTimeout, InfiniteFuture) { // 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()), @@ -110,6 +121,10 @@ TEST(KernelTimeout, DefaultConstructor) { // 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()), @@ -131,6 +146,10 @@ TEST(KernelTimeout, TimeMaxNanos) { // 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()), @@ -152,6 +171,10 @@ TEST(KernelTimeout, Never) { // 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()), @@ -170,6 +193,10 @@ TEST(KernelTimeout, InfinitePast) { 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)); @@ -200,6 +227,13 @@ TEST(KernelTimeout, FiniteDurations) { 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), @@ -241,6 +275,12 @@ TEST(KernelTimeout, NegativeDurations) { 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( @@ -263,6 +303,10 @@ TEST(KernelTimeout, InfiniteDuration) { // 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()), @@ -284,6 +328,10 @@ TEST(KernelTimeout, DurationMaxNanos) { // 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()), @@ -305,6 +353,10 @@ TEST(KernelTimeout, OverflowNanos) { // 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()), diff --git a/absl/synchronization/internal/pthread_waiter.cc b/absl/synchronization/internal/pthread_waiter.cc index a8dafd94..8d90cc45 100644 --- a/absl/synchronization/internal/pthread_waiter.cc +++ b/absl/synchronization/internal/pthread_waiter.cc @@ -78,6 +78,11 @@ PthreadWaiter::PthreadWaiter() : waiter_count_(0), wakeup_count_(0) { #define ABSL_INTERNAL_HAS_PTHREAD_COND_TIMEDWAIT_RELATIVE_NP 1 #endif +#if defined(__GLIBC__) && \ + (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 30)) +#define ABSL_INTERNAL_HAVE_PTHREAD_COND_CLOCKWAIT 1 +#endif + // Calls pthread_cond_timedwait() or possibly something else like // pthread_cond_timedwait_relative_np() depending on the platform and // KernelTimeout requested. The return value is the same as the return @@ -94,6 +99,11 @@ int PthreadWaiter::TimedWait(KernelTimeout t) { #ifdef ABSL_INTERNAL_HAS_PTHREAD_COND_TIMEDWAIT_RELATIVE_NP const auto rel_timeout = t.MakeRelativeTimespec(); return pthread_cond_timedwait_relative_np(&cv_, &mu_, &rel_timeout); +#elif defined(ABSL_INTERNAL_HAVE_PTHREAD_COND_CLOCKWAIT) && \ + defined(CLOCK_MONOTONIC) + const auto abs_clock_timeout = t.MakeClockAbsoluteTimespec(CLOCK_MONOTONIC); + return pthread_cond_clockwait(&cv_, &mu_, CLOCK_MONOTONIC, + &abs_clock_timeout); #endif } diff --git a/absl/synchronization/internal/sem_waiter.cc b/absl/synchronization/internal/sem_waiter.cc index 89af5de2..7dd27fb7 100644 --- a/absl/synchronization/internal/sem_waiter.cc +++ b/absl/synchronization/internal/sem_waiter.cc @@ -43,12 +43,34 @@ SemWaiter::SemWaiter() : wakeups_(0) { } } -bool SemWaiter::Wait(KernelTimeout t) { - struct timespec abs_timeout; - if (t.has_timeout()) { - abs_timeout = t.MakeAbsTimespec(); +#if defined(__GLIBC__) && \ + (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 30)) +#define ABSL_INTERNAL_HAVE_SEM_CLOCKWAIT 1 +#endif + +// Calls sem_timedwait() or possibly something else like +// sem_clockwait() depending on the platform and +// KernelTimeout requested. The return value is the same as a call to the return +// value to a call to sem_timedwait(). +int SemWaiter::TimedWait(KernelTimeout t) { +#ifndef __GOOGLE_GRTE_VERSION__ + constexpr bool kRelativeTimeoutSupported = true; +#else + constexpr bool kRelativeTimeoutSupported = false; +#endif + + if (kRelativeTimeoutSupported && t.is_relative_timeout()) { +#if defined(ABSL_INTERNAL_HAVE_SEM_CLOCKWAIT) && defined(CLOCK_MONOTONIC) + const auto abs_clock_timeout = t.MakeClockAbsoluteTimespec(CLOCK_MONOTONIC); + return sem_clockwait(&sem_, CLOCK_MONOTONIC, &abs_clock_timeout); +#endif } + const auto abs_timeout = t.MakeAbsTimespec(); + return sem_timedwait(&sem_, &abs_timeout); +} + +bool SemWaiter::Wait(KernelTimeout t) { // Loop until we timeout or consume a wakeup. // Note that, since the thread ticker is just reset, we don't need to check // whether the thread is idle on the very first pass of the loop. @@ -73,10 +95,10 @@ bool SemWaiter::Wait(KernelTimeout t) { if (errno == EINTR) continue; ABSL_RAW_LOG(FATAL, "sem_wait failed: %d", errno); } else { - if (sem_timedwait(&sem_, &abs_timeout) == 0) break; + if (TimedWait(t) == 0) break; if (errno == EINTR) continue; if (errno == ETIMEDOUT) return false; - ABSL_RAW_LOG(FATAL, "sem_timedwait failed: %d", errno); + ABSL_RAW_LOG(FATAL, "SemWaiter::TimedWait() failed: %d", errno); } } first_pass = false; diff --git a/absl/synchronization/internal/sem_waiter.h b/absl/synchronization/internal/sem_waiter.h index 47d5bf3d..c22746f9 100644 --- a/absl/synchronization/internal/sem_waiter.h +++ b/absl/synchronization/internal/sem_waiter.h @@ -46,6 +46,8 @@ class SemWaiter : public WaiterCrtp { static constexpr char kName[] = "SemWaiter"; private: + int TimedWait(KernelTimeout t); + sem_t sem_; // This seems superfluous, but for Poke() we need to cause spurious -- cgit v1.2.3 From c23acb9b5636e7b908fba03d6b3584d8f80dba6d Mon Sep 17 00:00:00 2001 From: Derek Mauro Date: Wed, 12 Apr 2023 13:26:48 -0700 Subject: Synchronization: Consolidate the logic for whether steady clocks are supported for relative timeouts PiperOrigin-RevId: 523789416 Change-Id: Ide4cfdcae9ea7bffca3355c80ea9c8833a9536e6 --- absl/synchronization/BUILD.bazel | 4 ++++ absl/synchronization/CMakeLists.txt | 4 ++++ absl/synchronization/internal/futex.h | 25 ---------------------- absl/synchronization/internal/futex_waiter.cc | 24 ++++++++++++++++++++- absl/synchronization/internal/futex_waiter.h | 5 +++++ absl/synchronization/internal/kernel_timeout.cc | 13 +++++------ absl/synchronization/internal/kernel_timeout.h | 5 +++++ .../internal/kernel_timeout_test.cc | 20 +++++++++++++++++ absl/synchronization/internal/pthread_waiter.cc | 8 +------ absl/synchronization/internal/sem_waiter.cc | 8 +------ absl/synchronization/internal/stdcpp_waiter.cc | 2 +- absl/synchronization/internal/waiter_test.cc | 20 +++++++++++++++++ 12 files changed, 91 insertions(+), 47 deletions(-) (limited to 'absl/synchronization/internal/kernel_timeout_test.cc') diff --git a/absl/synchronization/BUILD.bazel b/absl/synchronization/BUILD.bazel index b1c4e9a3..b6721e14 100644 --- a/absl/synchronization/BUILD.bazel +++ b/absl/synchronization/BUILD.bazel @@ -61,7 +61,9 @@ cc_library( "//absl/synchronization:__pkg__", ], deps = [ + "//absl/base", "//absl/base:config", + "//absl/base:core_headers", "//absl/base:raw_logging_internal", "//absl/time", ], @@ -75,6 +77,7 @@ cc_test( deps = [ ":kernel_timeout_internal", "//absl/base:config", + "//absl/random", "//absl/time", "@com_google_googletest//:gtest_main", ], @@ -348,6 +351,7 @@ cc_test( ":kernel_timeout_internal", ":synchronization", ":thread_pool", + "//absl/random", "//absl/time", "@com_google_googletest//:gtest_main", ], diff --git a/absl/synchronization/CMakeLists.txt b/absl/synchronization/CMakeLists.txt index 9926fb72..b6b3b27c 100644 --- a/absl/synchronization/CMakeLists.txt +++ b/absl/synchronization/CMakeLists.txt @@ -44,7 +44,9 @@ absl_cc_library( COPTS ${ABSL_DEFAULT_COPTS} DEPS + absl::base absl::config + absl::core_headers absl::raw_logging_internal absl::time ) @@ -59,6 +61,7 @@ absl_cc_test( DEPS absl::kernel_timeout_internal absl::config + absl::random_random absl::time GTest::gmock_main ) @@ -257,6 +260,7 @@ absl_cc_test( ${ABSL_TEST_COPTS} DEPS absl::kernel_timeout_internal + absl::random_random absl::synchronization absl::thread_pool absl::time diff --git a/absl/synchronization/internal/futex.h b/absl/synchronization/internal/futex.h index 8d973263..55078f14 100644 --- a/absl/synchronization/internal/futex.h +++ b/absl/synchronization/internal/futex.h @@ -83,31 +83,6 @@ namespace synchronization_internal { class FutexImpl { public: - // Atomically check that `*v == val`, and if it is, then sleep until the - // timeout `t` has been reached, or until woken by `Wake()`. - static int WaitUntil(std::atomic* v, int32_t val, - KernelTimeout t) { - // Monotonic waits are disabled for production builds because go/btm - // requires synchronized clocks. - // TODO(b/160682823): Find a way to enable this when BTM is not enabled - // since production builds don't always run on Borg. -#if defined(CLOCK_MONOTONIC) && !defined(__GOOGLE_GRTE_VERSION__) - constexpr bool kRelativeTimeoutSupported = true; -#else - constexpr bool kRelativeTimeoutSupported = false; -#endif - - if (!t.has_timeout()) { - return Wait(v, val); - } else if (kRelativeTimeoutSupported && t.is_relative_timeout()) { - auto rel_timespec = t.MakeRelativeTimespec(); - return WaitRelativeTimeout(v, val, &rel_timespec); - } else { - auto abs_timespec = t.MakeAbsTimespec(); - return WaitAbsoluteTimeout(v, val, &abs_timespec); - } - } - // Atomically check that `*v == val`, and if it is, then sleep until the until // woken by `Wake()`. static int Wait(std::atomic* v, int32_t val) { diff --git a/absl/synchronization/internal/futex_waiter.cc b/absl/synchronization/internal/futex_waiter.cc index 0a76bc9a..7c07fbe2 100644 --- a/absl/synchronization/internal/futex_waiter.cc +++ b/absl/synchronization/internal/futex_waiter.cc @@ -35,6 +35,28 @@ namespace synchronization_internal { constexpr char FutexWaiter::kName[]; #endif +int FutexWaiter::WaitUntil(std::atomic* v, int32_t val, + KernelTimeout t) { +#ifdef CLOCK_MONOTONIC + constexpr bool kHasClockMonotonic = true; +#else + constexpr bool kHasClockMonotonic = false; +#endif + + // We can't call Futex::WaitUntil() here because the prodkernel implementation + // does not know about KernelTimeout::SupportsSteadyClock(). + if (!t.has_timeout()) { + return Futex::Wait(v, val); + } else if (kHasClockMonotonic && KernelTimeout::SupportsSteadyClock() && + t.is_relative_timeout()) { + auto rel_timespec = t.MakeRelativeTimespec(); + return Futex::WaitRelativeTimeout(v, val, &rel_timespec); + } else { + auto abs_timespec = t.MakeAbsTimespec(); + return Futex::WaitAbsoluteTimeout(v, val, &abs_timespec); + } +} + bool FutexWaiter::Wait(KernelTimeout t) { // Loop until we can atomically decrement futex from a positive // value, waiting on a futex while we believe it is zero. @@ -54,7 +76,7 @@ bool FutexWaiter::Wait(KernelTimeout t) { } if (!first_pass) MaybeBecomeIdle(); - const int err = Futex::WaitUntil(&futex_, 0, t); + const int err = WaitUntil(&futex_, 0, t); if (err != 0) { if (err == -EINTR || err == -EWOULDBLOCK) { // Do nothing, the loop will retry. diff --git a/absl/synchronization/internal/futex_waiter.h b/absl/synchronization/internal/futex_waiter.h index a6a72095..11dfa93b 100644 --- a/absl/synchronization/internal/futex_waiter.h +++ b/absl/synchronization/internal/futex_waiter.h @@ -43,6 +43,11 @@ class FutexWaiter : public WaiterCrtp { static constexpr char kName[] = "FutexWaiter"; private: + // Atomically check that `*v == val`, and if it is, then sleep until the + // timeout `t` has been reached, or until woken by `Wake()`. + static int WaitUntil(std::atomic* v, int32_t val, + KernelTimeout t); + // Futexes are defined by specification to be 32-bits. // Thus std::atomic must be just an int32_t with lockfree methods. std::atomic futex_; diff --git a/absl/synchronization/internal/kernel_timeout.cc b/absl/synchronization/internal/kernel_timeout.cc index bfbf282f..48ea6287 100644 --- a/absl/synchronization/internal/kernel_timeout.cc +++ b/absl/synchronization/internal/kernel_timeout.cc @@ -21,9 +21,13 @@ #include #include // NOLINT(build/c++11) #include +#include +#include #include #include +#include "absl/base/attributes.h" +#include "absl/base/call_once.h" #include "absl/base/config.h" #include "absl/time/time.h" @@ -37,15 +41,12 @@ constexpr int64_t KernelTimeout::kMaxNanos; #endif int64_t KernelTimeout::SteadyClockNow() { -#ifdef __GOOGLE_GRTE_VERSION__ - // go/btm requires synchronized clocks, so we have to use the system - // clock. - return absl::GetCurrentTimeNanos(); -#else + if (!SupportsSteadyClock()) { + return absl::GetCurrentTimeNanos(); + } return std::chrono::duration_cast( std::chrono::steady_clock::now().time_since_epoch()) .count(); -#endif } KernelTimeout::KernelTimeout(absl::Time t) { diff --git a/absl/synchronization/internal/kernel_timeout.h b/absl/synchronization/internal/kernel_timeout.h index 4e361a6a..06404a75 100644 --- a/absl/synchronization/internal/kernel_timeout.h +++ b/absl/synchronization/internal/kernel_timeout.h @@ -128,6 +128,11 @@ class KernelTimeout { // case of a spurious wakeup. std::chrono::nanoseconds ToChronoDuration() const; + // Returns true if steady (aka monotonic) clocks are supported by the system. + // This method exists because go/btm requires synchronized clocks, and + // thus requires we use the system (aka walltime) clock. + static constexpr bool SupportsSteadyClock() { return true; } + private: // Returns the current time, expressed as a count of nanoseconds since the // epoch used by an arbitrary clock. The implementation tries to use a steady diff --git a/absl/synchronization/internal/kernel_timeout_test.cc b/absl/synchronization/internal/kernel_timeout_test.cc index c853c4bc..e3286f7f 100644 --- a/absl/synchronization/internal/kernel_timeout_test.cc +++ b/absl/synchronization/internal/kernel_timeout_test.cc @@ -14,14 +14,34 @@ #include "absl/synchronization/internal/kernel_timeout.h" +#include #include // NOLINT(build/c++11) #include +#include "absl/random/random.h" #include "gtest/gtest.h" #include "absl/base/config.h" #include "absl/time/clock.h" #include "absl/time/time.h" +// Test go/btm support by randomizing the value clock_gettime() for +// CLOCK_MONOTONIC. This works by overriding a weak symbol in glibc. +// We should be resistant to this randomization when !SupportsSteadyClock(). +#ifdef __GOOGLE_GRTE_VERSION__ +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 // __GOOGLE_GRTE_VERSION__ + namespace { #if defined(ABSL_HAVE_ADDRESS_SANITIZER) || \ diff --git a/absl/synchronization/internal/pthread_waiter.cc b/absl/synchronization/internal/pthread_waiter.cc index 8d90cc45..366aaea2 100644 --- a/absl/synchronization/internal/pthread_waiter.cc +++ b/absl/synchronization/internal/pthread_waiter.cc @@ -88,14 +88,8 @@ PthreadWaiter::PthreadWaiter() : waiter_count_(0), wakeup_count_(0) { // KernelTimeout requested. The return value is the same as the return // value of pthread_cond_timedwait(). int PthreadWaiter::TimedWait(KernelTimeout t) { -#ifndef __GOOGLE_GRTE_VERSION__ - constexpr bool kRelativeTimeoutSupported = true; -#else - constexpr bool kRelativeTimeoutSupported = false; -#endif - assert(t.has_timeout()); - if (kRelativeTimeoutSupported && t.is_relative_timeout()) { + if (KernelTimeout::SupportsSteadyClock() && t.is_relative_timeout()) { #ifdef ABSL_INTERNAL_HAS_PTHREAD_COND_TIMEDWAIT_RELATIVE_NP const auto rel_timeout = t.MakeRelativeTimespec(); return pthread_cond_timedwait_relative_np(&cv_, &mu_, &rel_timeout); diff --git a/absl/synchronization/internal/sem_waiter.cc b/absl/synchronization/internal/sem_waiter.cc index 7dd27fb7..3b3849bf 100644 --- a/absl/synchronization/internal/sem_waiter.cc +++ b/absl/synchronization/internal/sem_waiter.cc @@ -53,13 +53,7 @@ SemWaiter::SemWaiter() : wakeups_(0) { // KernelTimeout requested. The return value is the same as a call to the return // value to a call to sem_timedwait(). int SemWaiter::TimedWait(KernelTimeout t) { -#ifndef __GOOGLE_GRTE_VERSION__ - constexpr bool kRelativeTimeoutSupported = true; -#else - constexpr bool kRelativeTimeoutSupported = false; -#endif - - if (kRelativeTimeoutSupported && t.is_relative_timeout()) { + if (KernelTimeout::SupportsSteadyClock() && t.is_relative_timeout()) { #if defined(ABSL_INTERNAL_HAVE_SEM_CLOCKWAIT) && defined(CLOCK_MONOTONIC) const auto abs_clock_timeout = t.MakeClockAbsoluteTimespec(CLOCK_MONOTONIC); return sem_clockwait(&sem_, CLOCK_MONOTONIC, &abs_clock_timeout); diff --git a/absl/synchronization/internal/stdcpp_waiter.cc b/absl/synchronization/internal/stdcpp_waiter.cc index 8b5d1df4..355718a7 100644 --- a/absl/synchronization/internal/stdcpp_waiter.cc +++ b/absl/synchronization/internal/stdcpp_waiter.cc @@ -50,7 +50,7 @@ bool StdcppWaiter::Wait(KernelTimeout t) { if (!t.has_timeout()) { cv_.wait(lock); } else { - auto wait_result = t.is_relative_timeout() + auto wait_result = t.SupportsSteadyClock() && t.is_relative_timeout() ? cv_.wait_for(lock, t.ToChronoDuration()) : cv_.wait_until(lock, t.ToChronoTimePoint()); if (wait_result == std::cv_status::timeout) { diff --git a/absl/synchronization/internal/waiter_test.cc b/absl/synchronization/internal/waiter_test.cc index 66b255de..57a1a3da 100644 --- a/absl/synchronization/internal/waiter_test.cc +++ b/absl/synchronization/internal/waiter_test.cc @@ -14,9 +14,11 @@ #include "absl/synchronization/internal/waiter.h" +#include #include #include +#include "absl/random/random.h" #include "absl/synchronization/internal/create_thread_identity.h" #include "absl/synchronization/internal/futex_waiter.h" #include "absl/synchronization/internal/kernel_timeout.h" @@ -29,6 +31,24 @@ #include "absl/time/time.h" #include "gtest/gtest.h" +// Test go/btm support by randomizing the value clock_gettime() for +// CLOCK_MONOTONIC. This works by overriding a weak symbol in glibc. +// We should be resistant to this randomization when !SupportsSteadyClock(). +#ifdef __GOOGLE_GRTE_VERSION__ +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 // __GOOGLE_GRTE_VERSION__ + namespace { TEST(Waiter, PrintPlatformImplementation) { -- cgit v1.2.3 From c5f310de3ba06b7ad24da48145a099485322d2ac Mon Sep 17 00:00:00 2001 From: Derek Mauro Date: Thu, 13 Apr 2023 10:25:13 -0700 Subject: Don't override clock_gettime in the KernelTimeout and Waiter tests under sanitizers. The overrides break the sanitizers. PiperOrigin-RevId: 524037272 Change-Id: I85b87d3870c488cb316505e94b394c6f98e9f60f --- absl/synchronization/BUILD.bazel | 1 + absl/synchronization/CMakeLists.txt | 1 + absl/synchronization/internal/kernel_timeout_test.cc | 13 ++++++++----- absl/synchronization/internal/waiter_test.cc | 10 +++++++--- 4 files changed, 17 insertions(+), 8 deletions(-) (limited to 'absl/synchronization/internal/kernel_timeout_test.cc') diff --git a/absl/synchronization/BUILD.bazel b/absl/synchronization/BUILD.bazel index b6721e14..406c5b85 100644 --- a/absl/synchronization/BUILD.bazel +++ b/absl/synchronization/BUILD.bazel @@ -351,6 +351,7 @@ cc_test( ":kernel_timeout_internal", ":synchronization", ":thread_pool", + "//absl/base:config", "//absl/random", "//absl/time", "@com_google_googletest//:gtest_main", diff --git a/absl/synchronization/CMakeLists.txt b/absl/synchronization/CMakeLists.txt index b6b3b27c..86278349 100644 --- a/absl/synchronization/CMakeLists.txt +++ b/absl/synchronization/CMakeLists.txt @@ -259,6 +259,7 @@ absl_cc_test( COPTS ${ABSL_TEST_COPTS} DEPS + absl::config absl::kernel_timeout_internal absl::random_random absl::synchronization diff --git a/absl/synchronization/internal/kernel_timeout_test.cc b/absl/synchronization/internal/kernel_timeout_test.cc index e3286f7f..92ed2691 100644 --- a/absl/synchronization/internal/kernel_timeout_test.cc +++ b/absl/synchronization/internal/kernel_timeout_test.cc @@ -18,16 +18,19 @@ #include // NOLINT(build/c++11) #include -#include "absl/random/random.h" -#include "gtest/gtest.h" #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 clock_gettime() for +// 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(). -#ifdef __GOOGLE_GRTE_VERSION__ +#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) { @@ -40,7 +43,7 @@ extern "C" int clock_gettime(clockid_t c, struct timespec* ts) { } return __clock_gettime(c, ts); } -#endif // __GOOGLE_GRTE_VERSION__ +#endif namespace { diff --git a/absl/synchronization/internal/waiter_test.cc b/absl/synchronization/internal/waiter_test.cc index 57a1a3da..9a933940 100644 --- a/absl/synchronization/internal/waiter_test.cc +++ b/absl/synchronization/internal/waiter_test.cc @@ -18,6 +18,7 @@ #include #include +#include "absl/base/config.h" #include "absl/random/random.h" #include "absl/synchronization/internal/create_thread_identity.h" #include "absl/synchronization/internal/futex_waiter.h" @@ -31,10 +32,13 @@ #include "absl/time/time.h" #include "gtest/gtest.h" -// Test go/btm support by randomizing the value clock_gettime() for +// 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(). -#ifdef __GOOGLE_GRTE_VERSION__ +#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) { @@ -47,7 +51,7 @@ extern "C" int clock_gettime(clockid_t c, struct timespec* ts) { } return __clock_gettime(c, ts); } -#endif // __GOOGLE_GRTE_VERSION__ +#endif namespace { -- cgit v1.2.3