• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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