sem_waiter.cc (3840B)
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/sem_waiter.h" 16 17 #ifdef ABSL_INTERNAL_HAVE_SEM_WAITER 18 19 #include <semaphore.h> 20 21 #include <atomic> 22 #include <cassert> 23 #include <cstdint> 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 SemWaiter::SemWaiter() : wakeups_(0) { 37 if (sem_init(&sem_, 0, 0) != 0) { 38 ABSL_RAW_LOG(FATAL, "sem_init failed with errno %d\n", errno); 39 } 40 } 41 42 #if defined(__GLIBC__) && \ 43 (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 30)) 44 #define ABSL_INTERNAL_HAVE_SEM_CLOCKWAIT 1 45 #elif defined(__ANDROID_API__) && __ANDROID_API__ >= 30 46 #define ABSL_INTERNAL_HAVE_SEM_CLOCKWAIT 1 47 #endif 48 49 // Calls sem_timedwait() or possibly something else like 50 // sem_clockwait() depending on the platform and 51 // KernelTimeout requested. The return value is the same as a call to the return 52 // value to a call to sem_timedwait(). 53 int SemWaiter::TimedWait(KernelTimeout t) { 54 if (KernelTimeout::SupportsSteadyClock() && t.is_relative_timeout()) { 55 #if defined(ABSL_INTERNAL_HAVE_SEM_CLOCKWAIT) && defined(CLOCK_MONOTONIC) 56 const auto abs_clock_timeout = t.MakeClockAbsoluteTimespec(CLOCK_MONOTONIC); 57 return sem_clockwait(&sem_, CLOCK_MONOTONIC, &abs_clock_timeout); 58 #endif 59 } 60 61 const auto abs_timeout = t.MakeAbsTimespec(); 62 return sem_timedwait(&sem_, &abs_timeout); 63 } 64 65 bool SemWaiter::Wait(KernelTimeout t) { 66 // Loop until we timeout or consume a wakeup. 67 // Note that, since the thread ticker is just reset, we don't need to check 68 // whether the thread is idle on the very first pass of the loop. 69 bool first_pass = true; 70 while (true) { 71 int x = wakeups_.load(std::memory_order_relaxed); 72 while (x != 0) { 73 if (!wakeups_.compare_exchange_weak(x, x - 1, 74 std::memory_order_acquire, 75 std::memory_order_relaxed)) { 76 continue; // Raced with someone, retry. 77 } 78 // Successfully consumed a wakeup, we're done. 79 return true; 80 } 81 82 if (!first_pass) MaybeBecomeIdle(); 83 // Nothing to consume, wait (looping on EINTR). 84 while (true) { 85 if (!t.has_timeout()) { 86 if (sem_wait(&sem_) == 0) break; 87 if (errno == EINTR) continue; 88 ABSL_RAW_LOG(FATAL, "sem_wait failed: %d", errno); 89 } else { 90 if (TimedWait(t) == 0) break; 91 if (errno == EINTR) continue; 92 if (errno == ETIMEDOUT) return false; 93 ABSL_RAW_LOG(FATAL, "SemWaiter::TimedWait() failed: %d", errno); 94 } 95 } 96 first_pass = false; 97 } 98 } 99 100 void SemWaiter::Post() { 101 // Post a wakeup. 102 if (wakeups_.fetch_add(1, std::memory_order_release) == 0) { 103 // We incremented from 0, need to wake a potential waiter. 104 Poke(); 105 } 106 } 107 108 void SemWaiter::Poke() { 109 if (sem_post(&sem_) != 0) { // Wake any semaphore waiter. 110 ABSL_RAW_LOG(FATAL, "sem_post failed with errno %d\n", errno); 111 } 112 } 113 114 } // namespace synchronization_internal 115 ABSL_NAMESPACE_END 116 } // namespace absl 117 118 #endif // ABSL_INTERNAL_HAVE_SEM_WAITER