| // Copyright 2023 The Abseil Authors. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // https://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| #include "absl/synchronization/internal/pthread_waiter.h" | |
| #ifdef ABSL_INTERNAL_HAVE_PTHREAD_WAITER | |
| #include <pthread.h> | |
| #include <sys/time.h> | |
| #include <unistd.h> | |
| #include <cassert> | |
| #include <cerrno> | |
| #include "absl/base/config.h" | |
| #include "absl/base/internal/raw_logging.h" | |
| #include "absl/base/optimization.h" | |
| #include "absl/synchronization/internal/kernel_timeout.h" | |
| namespace absl { | |
| ABSL_NAMESPACE_BEGIN | |
| namespace synchronization_internal { | |
| namespace { | |
| class PthreadMutexHolder { | |
| public: | |
| explicit PthreadMutexHolder(pthread_mutex_t *mu) : mu_(mu) { | |
| const int err = pthread_mutex_lock(mu_); | |
| if (err != 0) { | |
| ABSL_RAW_LOG(FATAL, "pthread_mutex_lock failed: %d", err); | |
| } | |
| } | |
| PthreadMutexHolder(const PthreadMutexHolder &rhs) = delete; | |
| PthreadMutexHolder &operator=(const PthreadMutexHolder &rhs) = delete; | |
| ~PthreadMutexHolder() { | |
| const int err = pthread_mutex_unlock(mu_); | |
| if (err != 0) { | |
| ABSL_RAW_LOG(FATAL, "pthread_mutex_unlock failed: %d", err); | |
| } | |
| } | |
| private: | |
| pthread_mutex_t *mu_; | |
| }; | |
| } // namespace | |
| PthreadWaiter::PthreadWaiter() : waiter_count_(0), wakeup_count_(0) { | |
| const int err = pthread_mutex_init(&mu_, nullptr); | |
| if (err != 0) { | |
| ABSL_RAW_LOG(FATAL, "pthread_mutex_init failed: %d", err); | |
| } | |
| const int err2 = pthread_cond_init(&cv_, nullptr); | |
| if (err2 != 0) { | |
| ABSL_RAW_LOG(FATAL, "pthread_cond_init failed: %d", err2); | |
| } | |
| } | |
| #ifdef __APPLE__ | |
| #define ABSL_INTERNAL_HAS_PTHREAD_COND_TIMEDWAIT_RELATIVE_NP 1 | |
| #endif | |
| #if defined(__GLIBC__) && \ | |
| (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 30)) | |
| #define ABSL_INTERNAL_HAVE_PTHREAD_COND_CLOCKWAIT 1 | |
| #elif defined(__ANDROID_API__) && __ANDROID_API__ >= 30 | |
| #define ABSL_INTERNAL_HAVE_PTHREAD_COND_CLOCKWAIT 1 | |
| #endif | |
| // Calls pthread_cond_timedwait() or possibly something else like | |
| // pthread_cond_timedwait_relative_np() depending on the platform and | |
| // KernelTimeout requested. The return value is the same as the return | |
| // value of pthread_cond_timedwait(). | |
| int PthreadWaiter::TimedWait(KernelTimeout t) { | |
| assert(t.has_timeout()); | |
| if (KernelTimeout::SupportsSteadyClock() && t.is_relative_timeout()) { | |
| #ifdef ABSL_INTERNAL_HAS_PTHREAD_COND_TIMEDWAIT_RELATIVE_NP | |
| const auto rel_timeout = t.MakeRelativeTimespec(); | |
| return pthread_cond_timedwait_relative_np(&cv_, &mu_, &rel_timeout); | |
| #elif defined(ABSL_INTERNAL_HAVE_PTHREAD_COND_CLOCKWAIT) && \ | |
| defined(CLOCK_MONOTONIC) | |
| const auto abs_clock_timeout = t.MakeClockAbsoluteTimespec(CLOCK_MONOTONIC); | |
| return pthread_cond_clockwait(&cv_, &mu_, CLOCK_MONOTONIC, | |
| &abs_clock_timeout); | |
| #endif | |
| } | |
| const auto abs_timeout = t.MakeAbsTimespec(); | |
| return pthread_cond_timedwait(&cv_, &mu_, &abs_timeout); | |
| } | |
| bool PthreadWaiter::Wait(KernelTimeout t) { | |
| PthreadMutexHolder h(&mu_); | |
| ++waiter_count_; | |
| // Loop until we find a wakeup to consume or timeout. | |
| // Note that, since the thread ticker is just reset, we don't need to check | |
| // whether the thread is idle on the very first pass of the loop. | |
| bool first_pass = true; | |
| while (wakeup_count_ == 0) { | |
| if (!first_pass) MaybeBecomeIdle(); | |
| // No wakeups available, time to wait. | |
| if (!t.has_timeout()) { | |
| const int err = pthread_cond_wait(&cv_, &mu_); | |
| if (err != 0) { | |
| ABSL_RAW_LOG(FATAL, "pthread_cond_wait failed: %d", err); | |
| } | |
| } else { | |
| const int err = TimedWait(t); | |
| if (err == ETIMEDOUT) { | |
| --waiter_count_; | |
| return false; | |
| } | |
| if (err != 0) { | |
| ABSL_RAW_LOG(FATAL, "PthreadWaiter::TimedWait() failed: %d", err); | |
| } | |
| } | |
| first_pass = false; | |
| } | |
| // Consume a wakeup and we're done. | |
| --wakeup_count_; | |
| --waiter_count_; | |
| return true; | |
| } | |
| void PthreadWaiter::Post() { | |
| PthreadMutexHolder h(&mu_); | |
| ++wakeup_count_; | |
| InternalCondVarPoke(); | |
| } | |
| void PthreadWaiter::Poke() { | |
| PthreadMutexHolder h(&mu_); | |
| InternalCondVarPoke(); | |
| } | |
| void PthreadWaiter::InternalCondVarPoke() { | |
| if (waiter_count_ != 0) { | |
| const int err = pthread_cond_signal(&cv_); | |
| if (ABSL_PREDICT_FALSE(err != 0)) { | |
| ABSL_RAW_LOG(FATAL, "pthread_cond_signal failed: %d", err); | |
| } | |
| } | |
| } | |
| } // namespace synchronization_internal | |
| ABSL_NAMESPACE_END | |
| } // namespace absl | |
| #endif // ABSL_INTERNAL_HAVE_PTHREAD_WAITER |