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