1*9356374aSAndroid Build Coastguard Worker // Copyright 2023 The Abseil Authors.
2*9356374aSAndroid Build Coastguard Worker //
3*9356374aSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License");
4*9356374aSAndroid Build Coastguard Worker // you may not use this file except in compliance with the License.
5*9356374aSAndroid Build Coastguard Worker // You may obtain a copy of the License at
6*9356374aSAndroid Build Coastguard Worker //
7*9356374aSAndroid Build Coastguard Worker // https://www.apache.org/licenses/LICENSE-2.0
8*9356374aSAndroid Build Coastguard Worker //
9*9356374aSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*9356374aSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
11*9356374aSAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*9356374aSAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
13*9356374aSAndroid Build Coastguard Worker // limitations under the License.
14*9356374aSAndroid Build Coastguard Worker
15*9356374aSAndroid Build Coastguard Worker #include "absl/synchronization/internal/sem_waiter.h"
16*9356374aSAndroid Build Coastguard Worker
17*9356374aSAndroid Build Coastguard Worker #ifdef ABSL_INTERNAL_HAVE_SEM_WAITER
18*9356374aSAndroid Build Coastguard Worker
19*9356374aSAndroid Build Coastguard Worker #include <semaphore.h>
20*9356374aSAndroid Build Coastguard Worker
21*9356374aSAndroid Build Coastguard Worker #include <atomic>
22*9356374aSAndroid Build Coastguard Worker #include <cassert>
23*9356374aSAndroid Build Coastguard Worker #include <cstdint>
24*9356374aSAndroid Build Coastguard Worker #include <cerrno>
25*9356374aSAndroid Build Coastguard Worker
26*9356374aSAndroid Build Coastguard Worker #include "absl/base/config.h"
27*9356374aSAndroid Build Coastguard Worker #include "absl/base/internal/raw_logging.h"
28*9356374aSAndroid Build Coastguard Worker #include "absl/base/internal/thread_identity.h"
29*9356374aSAndroid Build Coastguard Worker #include "absl/base/optimization.h"
30*9356374aSAndroid Build Coastguard Worker #include "absl/synchronization/internal/kernel_timeout.h"
31*9356374aSAndroid Build Coastguard Worker
32*9356374aSAndroid Build Coastguard Worker namespace absl {
33*9356374aSAndroid Build Coastguard Worker ABSL_NAMESPACE_BEGIN
34*9356374aSAndroid Build Coastguard Worker namespace synchronization_internal {
35*9356374aSAndroid Build Coastguard Worker
36*9356374aSAndroid Build Coastguard Worker #ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL
37*9356374aSAndroid Build Coastguard Worker constexpr char SemWaiter::kName[];
38*9356374aSAndroid Build Coastguard Worker #endif
39*9356374aSAndroid Build Coastguard Worker
SemWaiter()40*9356374aSAndroid Build Coastguard Worker SemWaiter::SemWaiter() : wakeups_(0) {
41*9356374aSAndroid Build Coastguard Worker if (sem_init(&sem_, 0, 0) != 0) {
42*9356374aSAndroid Build Coastguard Worker ABSL_RAW_LOG(FATAL, "sem_init failed with errno %d\n", errno);
43*9356374aSAndroid Build Coastguard Worker }
44*9356374aSAndroid Build Coastguard Worker }
45*9356374aSAndroid Build Coastguard Worker
46*9356374aSAndroid Build Coastguard Worker #if defined(__GLIBC__) && \
47*9356374aSAndroid Build Coastguard Worker (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 30))
48*9356374aSAndroid Build Coastguard Worker #define ABSL_INTERNAL_HAVE_SEM_CLOCKWAIT 1
49*9356374aSAndroid Build Coastguard Worker #elif defined(__ANDROID_API__) && __ANDROID_API__ >= 30
50*9356374aSAndroid Build Coastguard Worker #define ABSL_INTERNAL_HAVE_SEM_CLOCKWAIT 1
51*9356374aSAndroid Build Coastguard Worker #endif
52*9356374aSAndroid Build Coastguard Worker
53*9356374aSAndroid Build Coastguard Worker // Calls sem_timedwait() or possibly something else like
54*9356374aSAndroid Build Coastguard Worker // sem_clockwait() depending on the platform and
55*9356374aSAndroid Build Coastguard Worker // KernelTimeout requested. The return value is the same as a call to the return
56*9356374aSAndroid Build Coastguard Worker // value to a call to sem_timedwait().
TimedWait(KernelTimeout t)57*9356374aSAndroid Build Coastguard Worker int SemWaiter::TimedWait(KernelTimeout t) {
58*9356374aSAndroid Build Coastguard Worker if (KernelTimeout::SupportsSteadyClock() && t.is_relative_timeout()) {
59*9356374aSAndroid Build Coastguard Worker #if defined(ABSL_INTERNAL_HAVE_SEM_CLOCKWAIT) && defined(CLOCK_MONOTONIC)
60*9356374aSAndroid Build Coastguard Worker const auto abs_clock_timeout = t.MakeClockAbsoluteTimespec(CLOCK_MONOTONIC);
61*9356374aSAndroid Build Coastguard Worker return sem_clockwait(&sem_, CLOCK_MONOTONIC, &abs_clock_timeout);
62*9356374aSAndroid Build Coastguard Worker #endif
63*9356374aSAndroid Build Coastguard Worker }
64*9356374aSAndroid Build Coastguard Worker
65*9356374aSAndroid Build Coastguard Worker const auto abs_timeout = t.MakeAbsTimespec();
66*9356374aSAndroid Build Coastguard Worker return sem_timedwait(&sem_, &abs_timeout);
67*9356374aSAndroid Build Coastguard Worker }
68*9356374aSAndroid Build Coastguard Worker
Wait(KernelTimeout t)69*9356374aSAndroid Build Coastguard Worker bool SemWaiter::Wait(KernelTimeout t) {
70*9356374aSAndroid Build Coastguard Worker // Loop until we timeout or consume a wakeup.
71*9356374aSAndroid Build Coastguard Worker // Note that, since the thread ticker is just reset, we don't need to check
72*9356374aSAndroid Build Coastguard Worker // whether the thread is idle on the very first pass of the loop.
73*9356374aSAndroid Build Coastguard Worker bool first_pass = true;
74*9356374aSAndroid Build Coastguard Worker while (true) {
75*9356374aSAndroid Build Coastguard Worker int x = wakeups_.load(std::memory_order_relaxed);
76*9356374aSAndroid Build Coastguard Worker while (x != 0) {
77*9356374aSAndroid Build Coastguard Worker if (!wakeups_.compare_exchange_weak(x, x - 1,
78*9356374aSAndroid Build Coastguard Worker std::memory_order_acquire,
79*9356374aSAndroid Build Coastguard Worker std::memory_order_relaxed)) {
80*9356374aSAndroid Build Coastguard Worker continue; // Raced with someone, retry.
81*9356374aSAndroid Build Coastguard Worker }
82*9356374aSAndroid Build Coastguard Worker // Successfully consumed a wakeup, we're done.
83*9356374aSAndroid Build Coastguard Worker return true;
84*9356374aSAndroid Build Coastguard Worker }
85*9356374aSAndroid Build Coastguard Worker
86*9356374aSAndroid Build Coastguard Worker if (!first_pass) MaybeBecomeIdle();
87*9356374aSAndroid Build Coastguard Worker // Nothing to consume, wait (looping on EINTR).
88*9356374aSAndroid Build Coastguard Worker while (true) {
89*9356374aSAndroid Build Coastguard Worker if (!t.has_timeout()) {
90*9356374aSAndroid Build Coastguard Worker if (sem_wait(&sem_) == 0) break;
91*9356374aSAndroid Build Coastguard Worker if (errno == EINTR) continue;
92*9356374aSAndroid Build Coastguard Worker ABSL_RAW_LOG(FATAL, "sem_wait failed: %d", errno);
93*9356374aSAndroid Build Coastguard Worker } else {
94*9356374aSAndroid Build Coastguard Worker if (TimedWait(t) == 0) break;
95*9356374aSAndroid Build Coastguard Worker if (errno == EINTR) continue;
96*9356374aSAndroid Build Coastguard Worker if (errno == ETIMEDOUT) return false;
97*9356374aSAndroid Build Coastguard Worker ABSL_RAW_LOG(FATAL, "SemWaiter::TimedWait() failed: %d", errno);
98*9356374aSAndroid Build Coastguard Worker }
99*9356374aSAndroid Build Coastguard Worker }
100*9356374aSAndroid Build Coastguard Worker first_pass = false;
101*9356374aSAndroid Build Coastguard Worker }
102*9356374aSAndroid Build Coastguard Worker }
103*9356374aSAndroid Build Coastguard Worker
Post()104*9356374aSAndroid Build Coastguard Worker void SemWaiter::Post() {
105*9356374aSAndroid Build Coastguard Worker // Post a wakeup.
106*9356374aSAndroid Build Coastguard Worker if (wakeups_.fetch_add(1, std::memory_order_release) == 0) {
107*9356374aSAndroid Build Coastguard Worker // We incremented from 0, need to wake a potential waiter.
108*9356374aSAndroid Build Coastguard Worker Poke();
109*9356374aSAndroid Build Coastguard Worker }
110*9356374aSAndroid Build Coastguard Worker }
111*9356374aSAndroid Build Coastguard Worker
Poke()112*9356374aSAndroid Build Coastguard Worker void SemWaiter::Poke() {
113*9356374aSAndroid Build Coastguard Worker if (sem_post(&sem_) != 0) { // Wake any semaphore waiter.
114*9356374aSAndroid Build Coastguard Worker ABSL_RAW_LOG(FATAL, "sem_post failed with errno %d\n", errno);
115*9356374aSAndroid Build Coastguard Worker }
116*9356374aSAndroid Build Coastguard Worker }
117*9356374aSAndroid Build Coastguard Worker
118*9356374aSAndroid Build Coastguard Worker } // namespace synchronization_internal
119*9356374aSAndroid Build Coastguard Worker ABSL_NAMESPACE_END
120*9356374aSAndroid Build Coastguard Worker } // namespace absl
121*9356374aSAndroid Build Coastguard Worker
122*9356374aSAndroid Build Coastguard Worker #endif // ABSL_INTERNAL_HAVE_SEM_WAITER
123