futex_waiter.cc (3545B)
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/futex_waiter.h" 16 17 #ifdef ABSL_INTERNAL_HAVE_FUTEX_WAITER 18 19 #include <atomic> 20 #include <cstdint> 21 #include <cerrno> 22 23 #include "absl/base/config.h" 24 #include "absl/base/internal/raw_logging.h" 25 #include "absl/base/internal/thread_identity.h" 26 #include "absl/base/optimization.h" 27 #include "absl/synchronization/internal/kernel_timeout.h" 28 #include "absl/synchronization/internal/futex.h" 29 30 namespace absl { 31 ABSL_NAMESPACE_BEGIN 32 namespace synchronization_internal { 33 34 int FutexWaiter::WaitUntil(std::atomic<int32_t>* v, int32_t val, 35 KernelTimeout t) { 36 #ifdef CLOCK_MONOTONIC 37 constexpr bool kHasClockMonotonic = true; 38 #else 39 constexpr bool kHasClockMonotonic = false; 40 #endif 41 42 // We can't call Futex::WaitUntil() here because the prodkernel implementation 43 // does not know about KernelTimeout::SupportsSteadyClock(). 44 if (!t.has_timeout()) { 45 return Futex::Wait(v, val); 46 } else if (kHasClockMonotonic && KernelTimeout::SupportsSteadyClock() && 47 t.is_relative_timeout()) { 48 auto rel_timespec = t.MakeRelativeTimespec(); 49 return Futex::WaitRelativeTimeout(v, val, &rel_timespec); 50 } else { 51 auto abs_timespec = t.MakeAbsTimespec(); 52 return Futex::WaitAbsoluteTimeout(v, val, &abs_timespec); 53 } 54 } 55 56 bool FutexWaiter::Wait(KernelTimeout t) { 57 // Loop until we can atomically decrement futex from a positive 58 // value, waiting on a futex while we believe it is zero. 59 // Note that, since the thread ticker is just reset, we don't need to check 60 // whether the thread is idle on the very first pass of the loop. 61 bool first_pass = true; 62 while (true) { 63 int32_t x = futex_.load(std::memory_order_relaxed); 64 while (x != 0) { 65 if (!futex_.compare_exchange_weak(x, x - 1, 66 std::memory_order_acquire, 67 std::memory_order_relaxed)) { 68 continue; // Raced with someone, retry. 69 } 70 return true; // Consumed a wakeup, we are done. 71 } 72 73 if (!first_pass) MaybeBecomeIdle(); 74 const int err = WaitUntil(&futex_, 0, t); 75 if (err != 0) { 76 if (err == -EINTR || err == -EWOULDBLOCK) { 77 // Do nothing, the loop will retry. 78 } else if (err == -ETIMEDOUT) { 79 return false; 80 } else { 81 ABSL_RAW_LOG(FATAL, "Futex operation failed with error %d\n", err); 82 } 83 } 84 first_pass = false; 85 } 86 } 87 88 void FutexWaiter::Post() { 89 if (futex_.fetch_add(1, std::memory_order_release) == 0) { 90 // We incremented from 0, need to wake a potential waiter. 91 Poke(); 92 } 93 } 94 95 void FutexWaiter::Poke() { 96 // Wake one thread waiting on the futex. 97 const int err = Futex::Wake(&futex_, 1); 98 if (ABSL_PREDICT_FALSE(err < 0)) { 99 ABSL_RAW_LOG(FATAL, "Futex operation failed with error %d\n", err); 100 } 101 } 102 103 } // namespace synchronization_internal 104 ABSL_NAMESPACE_END 105 } // namespace absl 106 107 #endif // ABSL_INTERNAL_HAVE_FUTEX_WAITER