pthread_waiter.cc (4926B)
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: 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 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 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(). 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 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 139 void PthreadWaiter::Post() { 140 PthreadMutexHolder h(&mu_); 141 ++wakeup_count_; 142 InternalCondVarPoke(); 143 } 144 145 void PthreadWaiter::Poke() { 146 PthreadMutexHolder h(&mu_); 147 InternalCondVarPoke(); 148 } 149 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