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
PthreadWaiter()61 PthreadWaiter::PthreadWaiter() : waiter_count_(0), wakeup_count_(0) {
62 const int err = pthread_mutex_init(&mu_, 0);
63 if (err != 0) {
64 ABSL_RAW_LOG(FATAL, "pthread_mutex_init failed: %d", err);
65 }
66
67 const int err2 = pthread_cond_init(&cv_, 0);
68 if (err2 != 0) {
69 ABSL_RAW_LOG(FATAL, "pthread_cond_init failed: %d", err2);
70 }
71 }
72
73 #ifdef __APPLE__
74 #define ABSL_INTERNAL_HAS_PTHREAD_COND_TIMEDWAIT_RELATIVE_NP 1
75 #endif
76
77 #if defined(__GLIBC__) && \
78 (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 30))
79 #define ABSL_INTERNAL_HAVE_PTHREAD_COND_CLOCKWAIT 1
80 #elif defined(__ANDROID_API__) && __ANDROID_API__ >= 30
81 #define ABSL_INTERNAL_HAVE_PTHREAD_COND_CLOCKWAIT 1
82 #endif
83
84 // Calls pthread_cond_timedwait() or possibly something else like
85 // pthread_cond_timedwait_relative_np() depending on the platform and
86 // KernelTimeout requested. The return value is the same as the return
87 // value of pthread_cond_timedwait().
TimedWait(KernelTimeout t)88 int PthreadWaiter::TimedWait(KernelTimeout t) {
89 assert(t.has_timeout());
90 if (KernelTimeout::SupportsSteadyClock() && t.is_relative_timeout()) {
91 #ifdef ABSL_INTERNAL_HAS_PTHREAD_COND_TIMEDWAIT_RELATIVE_NP
92 const auto rel_timeout = t.MakeRelativeTimespec();
93 return pthread_cond_timedwait_relative_np(&cv_, &mu_, &rel_timeout);
94 #elif defined(ABSL_INTERNAL_HAVE_PTHREAD_COND_CLOCKWAIT) && \
95 defined(CLOCK_MONOTONIC)
96 const auto abs_clock_timeout = t.MakeClockAbsoluteTimespec(CLOCK_MONOTONIC);
97 return pthread_cond_clockwait(&cv_, &mu_, CLOCK_MONOTONIC,
98 &abs_clock_timeout);
99 #endif
100 }
101
102 const auto abs_timeout = t.MakeAbsTimespec();
103 return pthread_cond_timedwait(&cv_, &mu_, &abs_timeout);
104 }
105
Wait(KernelTimeout t)106 bool PthreadWaiter::Wait(KernelTimeout t) {
107 PthreadMutexHolder h(&mu_);
108 ++waiter_count_;
109 // Loop until we find a wakeup to consume or timeout.
110 // Note that, since the thread ticker is just reset, we don't need to check
111 // whether the thread is idle on the very first pass of the loop.
112 bool first_pass = true;
113 while (wakeup_count_ == 0) {
114 if (!first_pass) MaybeBecomeIdle();
115 // No wakeups available, time to wait.
116 if (!t.has_timeout()) {
117 const int err = pthread_cond_wait(&cv_, &mu_);
118 if (err != 0) {
119 ABSL_RAW_LOG(FATAL, "pthread_cond_wait failed: %d", err);
120 }
121 } else {
122 const int err = TimedWait(t);
123 if (err == ETIMEDOUT) {
124 --waiter_count_;
125 return false;
126 }
127 if (err != 0) {
128 ABSL_RAW_LOG(FATAL, "PthreadWaiter::TimedWait() failed: %d", err);
129 }
130 }
131 first_pass = false;
132 }
133 // Consume a wakeup and we're done.
134 --wakeup_count_;
135 --waiter_count_;
136 return true;
137 }
138
Post()139 void PthreadWaiter::Post() {
140 PthreadMutexHolder h(&mu_);
141 ++wakeup_count_;
142 InternalCondVarPoke();
143 }
144
Poke()145 void PthreadWaiter::Poke() {
146 PthreadMutexHolder h(&mu_);
147 InternalCondVarPoke();
148 }
149
InternalCondVarPoke()150 void PthreadWaiter::InternalCondVarPoke() {
151 if (waiter_count_ != 0) {
152 const int err = pthread_cond_signal(&cv_);
153 if (ABSL_PREDICT_FALSE(err != 0)) {
154 ABSL_RAW_LOG(FATAL, "pthread_cond_signal failed: %d", err);
155 }
156 }
157 }
158
159 } // namespace synchronization_internal
160 ABSL_NAMESPACE_END
161 } // namespace absl
162
163 #endif // ABSL_INTERNAL_HAVE_PTHREAD_WAITER
164