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/pthread_waiter.h"
16
17 #ifdef ABSL_INTERNAL_HAVE_PTHREAD_WAITER
18
19 #include <pthread.h>
20 #include <sys/time.h>
21 #include <unistd.h>
22
23 #include <cassert>
24 #include <cerrno>
25
26 #include "absl/base/config.h"
27 #include "absl/base/internal/raw_logging.h"
28 #include "absl/base/internal/thread_identity.h"
29 #include "absl/base/optimization.h"
30 #include "absl/synchronization/internal/kernel_timeout.h"
31
32 namespace absl {
33 ABSL_NAMESPACE_BEGIN
34 namespace synchronization_internal {
35
36 namespace {
37 class PthreadMutexHolder {
38 public:
PthreadMutexHolder(pthread_mutex_t * mu)39 explicit PthreadMutexHolder(pthread_mutex_t *mu) : mu_(mu) {
40 const int err = pthread_mutex_lock(mu_);
41 if (err != 0) {
42 ABSL_RAW_LOG(FATAL, "pthread_mutex_lock failed: %d", err);
43 }
44 }
45
46 PthreadMutexHolder(const PthreadMutexHolder &rhs) = delete;
47 PthreadMutexHolder &operator=(const PthreadMutexHolder &rhs) = delete;
48
~PthreadMutexHolder()49 ~PthreadMutexHolder() {
50 const int err = pthread_mutex_unlock(mu_);
51 if (err != 0) {
52 ABSL_RAW_LOG(FATAL, "pthread_mutex_unlock failed: %d", err);
53 }
54 }
55
56 private:
57 pthread_mutex_t *mu_;
58 };
59 } // namespace
60
61 #ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL
62 constexpr char PthreadWaiter::kName[];
63 #endif
64
PthreadWaiter()65 PthreadWaiter::PthreadWaiter() : waiter_count_(0), wakeup_count_(0) {
66 const int err = pthread_mutex_init(&mu_, 0);
67 if (err != 0) {
68 ABSL_RAW_LOG(FATAL, "pthread_mutex_init failed: %d", err);
69 }
70
71 const int err2 = pthread_cond_init(&cv_, 0);
72 if (err2 != 0) {
73 ABSL_RAW_LOG(FATAL, "pthread_cond_init failed: %d", err2);
74 }
75 }
76
77 #ifdef __APPLE__
78 #define ABSL_INTERNAL_HAS_PTHREAD_COND_TIMEDWAIT_RELATIVE_NP 1
79 #endif
80
81 #if defined(__GLIBC__) && \
82 (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 30))
83 #define ABSL_INTERNAL_HAVE_PTHREAD_COND_CLOCKWAIT 1
84 #elif defined(__ANDROID_API__) && __ANDROID_API__ >= 30
85 #define ABSL_INTERNAL_HAVE_PTHREAD_COND_CLOCKWAIT 1
86 #endif
87
88 // Calls pthread_cond_timedwait() or possibly something else like
89 // pthread_cond_timedwait_relative_np() depending on the platform and
90 // KernelTimeout requested. The return value is the same as the return
91 // value of pthread_cond_timedwait().
TimedWait(KernelTimeout t)92 int PthreadWaiter::TimedWait(KernelTimeout t) {
93 assert(t.has_timeout());
94 if (KernelTimeout::SupportsSteadyClock() && t.is_relative_timeout()) {
95 #ifdef ABSL_INTERNAL_HAS_PTHREAD_COND_TIMEDWAIT_RELATIVE_NP
96 const auto rel_timeout = t.MakeRelativeTimespec();
97 return pthread_cond_timedwait_relative_np(&cv_, &mu_, &rel_timeout);
98 #elif defined(ABSL_INTERNAL_HAVE_PTHREAD_COND_CLOCKWAIT) && \
99 defined(CLOCK_MONOTONIC)
100 const auto abs_clock_timeout = t.MakeClockAbsoluteTimespec(CLOCK_MONOTONIC);
101 return pthread_cond_clockwait(&cv_, &mu_, CLOCK_MONOTONIC,
102 &abs_clock_timeout);
103 #endif
104 }
105
106 const auto abs_timeout = t.MakeAbsTimespec();
107 return pthread_cond_timedwait(&cv_, &mu_, &abs_timeout);
108 }
109
Wait(KernelTimeout t)110 bool PthreadWaiter::Wait(KernelTimeout t) {
111 PthreadMutexHolder h(&mu_);
112 ++waiter_count_;
113 // Loop until we find a wakeup to consume or timeout.
114 // Note that, since the thread ticker is just reset, we don't need to check
115 // whether the thread is idle on the very first pass of the loop.
116 bool first_pass = true;
117 while (wakeup_count_ == 0) {
118 if (!first_pass) MaybeBecomeIdle();
119 // No wakeups available, time to wait.
120 if (!t.has_timeout()) {
121 const int err = pthread_cond_wait(&cv_, &mu_);
122 if (err != 0) {
123 ABSL_RAW_LOG(FATAL, "pthread_cond_wait failed: %d", err);
124 }
125 } else {
126 const int err = TimedWait(t);
127 if (err == ETIMEDOUT) {
128 --waiter_count_;
129 return false;
130 }
131 if (err != 0) {
132 ABSL_RAW_LOG(FATAL, "PthreadWaiter::TimedWait() failed: %d", err);
133 }
134 }
135 first_pass = false;
136 }
137 // Consume a wakeup and we're done.
138 --wakeup_count_;
139 --waiter_count_;
140 return true;
141 }
142
Post()143 void PthreadWaiter::Post() {
144 PthreadMutexHolder h(&mu_);
145 ++wakeup_count_;
146 InternalCondVarPoke();
147 }
148
Poke()149 void PthreadWaiter::Poke() {
150 PthreadMutexHolder h(&mu_);
151 InternalCondVarPoke();
152 }
153
InternalCondVarPoke()154 void PthreadWaiter::InternalCondVarPoke() {
155 if (waiter_count_ != 0) {
156 const int err = pthread_cond_signal(&cv_);
157 if (ABSL_PREDICT_FALSE(err != 0)) {
158 ABSL_RAW_LOG(FATAL, "pthread_cond_signal failed: %d", err);
159 }
160 }
161 }
162
163 } // namespace synchronization_internal
164 ABSL_NAMESPACE_END
165 } // namespace absl
166
167 #endif // ABSL_INTERNAL_HAVE_PTHREAD_WAITER
168