From 276f88cb77dd543ae9cc4ed55c08fb5f74f405ea Mon Sep 17 00:00:00 2001 From: Derek Mauro Date: Tue, 21 Mar 2023 07:30:38 -0700 Subject: Add an implementation of Waiter that uses std::mutex/std::condition_variable This implementation may at some point become the default on some platforms. Currently not all platforms have widespread support for both real absolute timeouts or real relative timeouts (here "real" means without converting to the other timeout type which is the only one supported by the underlying APIs). In this case we can defer to their standard library to implement correct support. This is not currently the default on any platform Note: The size of WaiterState had to increase to fit the new implementation PiperOrigin-RevId: 518266646 Change-Id: I7f246646a960d6e1b155f9de0bf2f681c5d3d245 --- CMake/AbseilDll.cmake | 2 + absl/base/internal/thread_identity.h | 2 +- absl/synchronization/BUILD.bazel | 2 + absl/synchronization/CMakeLists.txt | 2 + absl/synchronization/internal/stdcpp_waiter.cc | 91 ++++++++++++++++++++++++++ absl/synchronization/internal/stdcpp_waiter.h | 56 ++++++++++++++++ absl/synchronization/internal/waiter.h | 4 ++ absl/synchronization/internal/waiter_test.cc | 5 ++ 8 files changed, 163 insertions(+), 1 deletion(-) create mode 100644 absl/synchronization/internal/stdcpp_waiter.cc create mode 100644 absl/synchronization/internal/stdcpp_waiter.h diff --git a/CMake/AbseilDll.cmake b/CMake/AbseilDll.cmake index 3de1b235..2a331435 100644 --- a/CMake/AbseilDll.cmake +++ b/CMake/AbseilDll.cmake @@ -374,6 +374,8 @@ set(ABSL_INTERNAL_DLL_FILES "synchronization/internal/pthread_waiter.cc" "synchronization/internal/sem_waiter.h" "synchronization/internal/sem_waiter.cc" + "synchronization/internal/stdcpp_waiter.h" + "synchronization/internal/stdcpp_waiter.cc" "synchronization/internal/thread_pool.h" "synchronization/internal/waiter.h" "synchronization/internal/waiter_base.h" diff --git a/absl/base/internal/thread_identity.h b/absl/base/internal/thread_identity.h index b99c9575..496ec214 100644 --- a/absl/base/internal/thread_identity.h +++ b/absl/base/internal/thread_identity.h @@ -147,7 +147,7 @@ struct ThreadIdentity { // Private: Reserved for absl::synchronization_internal::Waiter. struct WaiterState { - alignas(void*) char data[128]; + alignas(void*) char data[256]; } waiter_state; // Used by PerThreadSem::{Get,Set}ThreadBlockedCounter(). diff --git a/absl/synchronization/BUILD.bazel b/absl/synchronization/BUILD.bazel index 40843b35..adf6ae2f 100644 --- a/absl/synchronization/BUILD.bazel +++ b/absl/synchronization/BUILD.bazel @@ -90,6 +90,7 @@ cc_library( "internal/per_thread_sem.cc", "internal/pthread_waiter.cc", "internal/sem_waiter.cc", + "internal/stdcpp_waiter.cc", "internal/waiter_base.cc", "internal/win32_waiter.cc", "mutex.cc", @@ -104,6 +105,7 @@ cc_library( "internal/per_thread_sem.h", "internal/pthread_waiter.h", "internal/sem_waiter.h", + "internal/stdcpp_waiter.h", "internal/waiter.h", "internal/waiter_base.h", "internal/win32_waiter.h", diff --git a/absl/synchronization/CMakeLists.txt b/absl/synchronization/CMakeLists.txt index d95e327f..9926fb72 100644 --- a/absl/synchronization/CMakeLists.txt +++ b/absl/synchronization/CMakeLists.txt @@ -75,6 +75,7 @@ absl_cc_library( "internal/per_thread_sem.h" "internal/pthread_waiter.h" "internal/sem_waiter.h" + "internal/stdcpp_waiter.h" "internal/waiter.h" "internal/waiter_base.h" "internal/win32_waiter.h" @@ -88,6 +89,7 @@ absl_cc_library( "internal/per_thread_sem.cc" "internal/pthread_waiter.cc" "internal/sem_waiter.cc" + "internal/stdcpp_waiter.cc" "internal/waiter_base.cc" "internal/win32_waiter.cc" "notification.cc" diff --git a/absl/synchronization/internal/stdcpp_waiter.cc b/absl/synchronization/internal/stdcpp_waiter.cc new file mode 100644 index 00000000..8b5d1df4 --- /dev/null +++ b/absl/synchronization/internal/stdcpp_waiter.cc @@ -0,0 +1,91 @@ +// 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/stdcpp_waiter.h" + +#ifdef ABSL_INTERNAL_HAVE_STDCPP_WAITER + +#include // NOLINT(build/c++11) +#include // NOLINT(build/c++11) +#include // NOLINT(build/c++11) + +#include "absl/base/config.h" +#include "absl/base/internal/raw_logging.h" +#include "absl/base/internal/thread_identity.h" +#include "absl/base/optimization.h" +#include "absl/synchronization/internal/kernel_timeout.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace synchronization_internal { + +#ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL +constexpr char StdcppWaiter::kName[]; +#endif + +StdcppWaiter::StdcppWaiter() : waiter_count_(0), wakeup_count_(0) {} + +bool StdcppWaiter::Wait(KernelTimeout t) { + std::unique_lock lock(mu_); + ++waiter_count_; + + // Loop until we find a wakeup to consume or timeout. + // 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. + bool first_pass = true; + while (wakeup_count_ == 0) { + if (!first_pass) MaybeBecomeIdle(); + // No wakeups available, time to wait. + if (!t.has_timeout()) { + cv_.wait(lock); + } else { + auto wait_result = t.is_relative_timeout() + ? cv_.wait_for(lock, t.ToChronoDuration()) + : cv_.wait_until(lock, t.ToChronoTimePoint()); + if (wait_result == std::cv_status::timeout) { + --waiter_count_; + return false; + } + } + first_pass = false; + } + + // Consume a wakeup and we're done. + --wakeup_count_; + --waiter_count_; + return true; +} + +void StdcppWaiter::Post() { + std::lock_guard lock(mu_); + ++wakeup_count_; + InternalCondVarPoke(); +} + +void StdcppWaiter::Poke() { + std::lock_guard lock(mu_); + InternalCondVarPoke(); +} + +void StdcppWaiter::InternalCondVarPoke() { + if (waiter_count_ != 0) { + cv_.notify_one(); + } +} + +} // namespace synchronization_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_INTERNAL_HAVE_STDCPP_WAITER diff --git a/absl/synchronization/internal/stdcpp_waiter.h b/absl/synchronization/internal/stdcpp_waiter.h new file mode 100644 index 00000000..e592a27b --- /dev/null +++ b/absl/synchronization/internal/stdcpp_waiter.h @@ -0,0 +1,56 @@ +// 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. +// + +#ifndef ABSL_SYNCHRONIZATION_INTERNAL_STDCPP_WAITER_H_ +#define ABSL_SYNCHRONIZATION_INTERNAL_STDCPP_WAITER_H_ + +#include // NOLINT(build/c++11) +#include // NOLINT(build/c++11) + +#include "absl/base/config.h" +#include "absl/synchronization/internal/kernel_timeout.h" +#include "absl/synchronization/internal/waiter_base.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace synchronization_internal { + +#define ABSL_INTERNAL_HAVE_STDCPP_WAITER 1 + +class StdcppWaiter : public WaiterCrtp { + public: + StdcppWaiter(); + + bool Wait(KernelTimeout t); + void Post(); + void Poke(); + + static constexpr char kName[] = "StdcppWaiter"; + + private: + // REQUIRES: mu_ must be held. + void InternalCondVarPoke(); + + std::mutex mu_; + std::condition_variable cv_; + int waiter_count_; + int wakeup_count_; // Unclaimed wakeups. +}; + +} // namespace synchronization_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_SYNCHRONIZATION_INTERNAL_STDCPP_WAITER_H_ diff --git a/absl/synchronization/internal/waiter.h b/absl/synchronization/internal/waiter.h index 07bba10a..1a8b0b83 100644 --- a/absl/synchronization/internal/waiter.h +++ b/absl/synchronization/internal/waiter.h @@ -20,6 +20,7 @@ #include "absl/synchronization/internal/futex_waiter.h" #include "absl/synchronization/internal/pthread_waiter.h" #include "absl/synchronization/internal/sem_waiter.h" +#include "absl/synchronization/internal/stdcpp_waiter.h" #include "absl/synchronization/internal/win32_waiter.h" // May be chosen at compile time via -DABSL_FORCE_WAITER_MODE= @@ -27,6 +28,7 @@ #define ABSL_WAITER_MODE_SEM 1 #define ABSL_WAITER_MODE_CONDVAR 2 #define ABSL_WAITER_MODE_WIN32 3 +#define ABSL_WAITER_MODE_STDCPP 4 #if defined(ABSL_FORCE_WAITER_MODE) #define ABSL_WAITER_MODE ABSL_FORCE_WAITER_MODE @@ -54,6 +56,8 @@ using Waiter = SemWaiter; using Waiter = PthreadWaiter; #elif ABSL_WAITER_MODE == ABSL_WAITER_MODE_WIN32 using Waiter = Win32Waiter; +#elif ABSL_WAITER_MODE == ABSL_WAITER_MODE_STDCPP +using Waiter = StdcppWaiter; #endif } // namespace synchronization_internal diff --git a/absl/synchronization/internal/waiter_test.cc b/absl/synchronization/internal/waiter_test.cc index 92812965..5de11d26 100644 --- a/absl/synchronization/internal/waiter_test.cc +++ b/absl/synchronization/internal/waiter_test.cc @@ -22,6 +22,7 @@ #include "absl/synchronization/internal/kernel_timeout.h" #include "absl/synchronization/internal/pthread_waiter.h" #include "absl/synchronization/internal/sem_waiter.h" +#include "absl/synchronization/internal/stdcpp_waiter.h" #include "absl/synchronization/internal/thread_pool.h" #include "absl/synchronization/internal/win32_waiter.h" #include "absl/time/clock.h" @@ -147,5 +148,9 @@ INSTANTIATE_TYPED_TEST_SUITE_P(Sem, WaiterTest, INSTANTIATE_TYPED_TEST_SUITE_P(Win32, WaiterTest, absl::synchronization_internal::Win32Waiter); #endif +#ifdef ABSL_INTERNAL_HAVE_STDCPP_WAITER +INSTANTIATE_TYPED_TEST_SUITE_P(Stdcpp, WaiterTest, + absl::synchronization_internal::StdcppWaiter); +#endif } // namespace -- cgit v1.2.3