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