1 // Copyright 2023 The Abseil Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "absl/synchronization/internal/stdcpp_waiter.h"
16
17 #ifdef ABSL_INTERNAL_HAVE_STDCPP_WAITER
18
19 #include <chrono> // NOLINT(build/c++11)
20 #include <condition_variable> // NOLINT(build/c++11)
21 #include <mutex> // NOLINT(build/c++11)
22
23 #include "absl/base/config.h"
24 #include "absl/base/internal/raw_logging.h"
25 #include "absl/base/internal/thread_identity.h"
26 #include "absl/base/optimization.h"
27 #include "absl/synchronization/internal/kernel_timeout.h"
28
29 namespace absl {
30 ABSL_NAMESPACE_BEGIN
31 namespace synchronization_internal {
32
33 #ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL
34 constexpr char StdcppWaiter::kName[];
35 #endif
36
StdcppWaiter()37 StdcppWaiter::StdcppWaiter() : waiter_count_(0), wakeup_count_(0) {}
38
Wait(KernelTimeout t)39 bool StdcppWaiter::Wait(KernelTimeout t) {
40 std::unique_lock<std::mutex> lock(mu_);
41 ++waiter_count_;
42
43 // Loop until we find a wakeup to consume or timeout.
44 // Note that, since the thread ticker is just reset, we don't need to check
45 // whether the thread is idle on the very first pass of the loop.
46 bool first_pass = true;
47 while (wakeup_count_ == 0) {
48 if (!first_pass) MaybeBecomeIdle();
49 // No wakeups available, time to wait.
50 if (!t.has_timeout()) {
51 cv_.wait(lock);
52 } else {
53 auto wait_result = t.SupportsSteadyClock() && t.is_relative_timeout()
54 ? cv_.wait_for(lock, t.ToChronoDuration())
55 : cv_.wait_until(lock, t.ToChronoTimePoint());
56 if (wait_result == std::cv_status::timeout) {
57 --waiter_count_;
58 return false;
59 }
60 }
61 first_pass = false;
62 }
63
64 // Consume a wakeup and we're done.
65 --wakeup_count_;
66 --waiter_count_;
67 return true;
68 }
69
Post()70 void StdcppWaiter::Post() {
71 std::lock_guard<std::mutex> lock(mu_);
72 ++wakeup_count_;
73 InternalCondVarPoke();
74 }
75
Poke()76 void StdcppWaiter::Poke() {
77 std::lock_guard<std::mutex> lock(mu_);
78 InternalCondVarPoke();
79 }
80
InternalCondVarPoke()81 void StdcppWaiter::InternalCondVarPoke() {
82 if (waiter_count_ != 0) {
83 cv_.notify_one();
84 }
85 }
86
87 } // namespace synchronization_internal
88 ABSL_NAMESPACE_END
89 } // namespace absl
90
91 #endif // ABSL_INTERNAL_HAVE_STDCPP_WAITER
92