xref: /aosp_15_r20/external/cronet/base/synchronization/condition_variable_posix.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2011 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/synchronization/condition_variable.h"
6 
7 #include <errno.h>
8 #include <stdint.h>
9 #include <sys/time.h>
10 
11 #include <optional>
12 
13 #include "base/synchronization/lock.h"
14 #include "base/threading/scoped_blocking_call.h"
15 #include "base/threading/thread_restrictions.h"
16 #include "base/time/time.h"
17 #include "build/build_config.h"
18 
19 #if BUILDFLAG(IS_APPLE)
20 #include <atomic>
21 
22 #include "base/feature_list.h"
23 #endif
24 
25 #if BUILDFLAG(IS_ANDROID) && __ANDROID_API__ < 21
26 #define HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC 1
27 #endif
28 
29 namespace {
30 #if BUILDFLAG(IS_APPLE)
31 // Under this feature a hack that was introduced to avoid crashes is skipped.
32 // Use to evaluate if the hack is still needed. See https://crbug.com/517681.
33 BASE_FEATURE(kSkipConditionVariableWakeupHack,
34              "SkipConditionVariableWakeupHack",
35              base::FEATURE_ENABLED_BY_DEFAULT);
36 std::atomic_bool g_skip_wakeup_hack = true;
37 #endif
38 }  // namespace
39 
40 namespace base {
41 
ConditionVariable(Lock * user_lock)42 ConditionVariable::ConditionVariable(Lock* user_lock)
43     : user_mutex_(user_lock->lock_.native_handle())
44 #if DCHECK_IS_ON()
45     , user_lock_(user_lock)
46 #endif
47 {
48   int rv = 0;
49   // http://crbug.com/293736
50   // NaCl doesn't support monotonic clock based absolute deadlines.
51   // On older Android platform versions, it's supported through the
52   // non-standard pthread_cond_timedwait_monotonic_np. Newer platform
53   // versions have pthread_condattr_setclock.
54   // Mac can use relative time deadlines.
55 #if !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_NACL) && \
56     !defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC)
57   pthread_condattr_t attrs;
58   rv = pthread_condattr_init(&attrs);
59   DCHECK_EQ(0, rv);
60   pthread_condattr_setclock(&attrs, CLOCK_MONOTONIC);
61   rv = pthread_cond_init(&condition_, &attrs);
62   pthread_condattr_destroy(&attrs);
63 #else
64   rv = pthread_cond_init(&condition_, NULL);
65 #endif
66   DCHECK_EQ(0, rv);
67 }
68 
~ConditionVariable()69 ConditionVariable::~ConditionVariable() {
70 #if BUILDFLAG(IS_APPLE)
71   // This hack is necessary to avoid a fatal pthreads subsystem bug in the
72   // Darwin kernel. http://crbug.com/517681.
73   if (!g_skip_wakeup_hack.load(std::memory_order_relaxed)) {
74     base::Lock lock;
75     base::AutoLock l(lock);
76     struct timespec ts;
77     ts.tv_sec = 0;
78     ts.tv_nsec = 1;
79     pthread_cond_timedwait_relative_np(&condition_, lock.lock_.native_handle(),
80                                        &ts);
81   }
82 #endif
83 
84   int rv = pthread_cond_destroy(&condition_);
85   DCHECK_EQ(0, rv);
86 }
87 
88 #if BUILDFLAG(IS_APPLE)
89 // static
InitializeFeatures()90 void ConditionVariable::InitializeFeatures() {
91   g_skip_wakeup_hack.store(
92       base::FeatureList::IsEnabled(kSkipConditionVariableWakeupHack),
93       std::memory_order_relaxed);
94 }
95 #endif
96 
Wait()97 void ConditionVariable::Wait() {
98   std::optional<internal::ScopedBlockingCallWithBaseSyncPrimitives>
99       scoped_blocking_call;
100   if (waiting_is_blocking_)
101     scoped_blocking_call.emplace(FROM_HERE, BlockingType::MAY_BLOCK);
102 
103 #if DCHECK_IS_ON()
104   user_lock_->CheckHeldAndUnmark();
105 #endif
106   int rv = pthread_cond_wait(&condition_, user_mutex_);
107   DCHECK_EQ(0, rv);
108 #if DCHECK_IS_ON()
109   user_lock_->CheckUnheldAndMark();
110 #endif
111 }
112 
TimedWait(const TimeDelta & max_time)113 void ConditionVariable::TimedWait(const TimeDelta& max_time) {
114   std::optional<internal::ScopedBlockingCallWithBaseSyncPrimitives>
115       scoped_blocking_call;
116   if (waiting_is_blocking_)
117     scoped_blocking_call.emplace(FROM_HERE, BlockingType::MAY_BLOCK);
118 
119   int64_t usecs = max_time.InMicroseconds();
120   struct timespec relative_time;
121   relative_time.tv_sec =
122       static_cast<time_t>(usecs / Time::kMicrosecondsPerSecond);
123   relative_time.tv_nsec =
124       (usecs % Time::kMicrosecondsPerSecond) * Time::kNanosecondsPerMicrosecond;
125 
126 #if DCHECK_IS_ON()
127   user_lock_->CheckHeldAndUnmark();
128 #endif
129 
130 #if BUILDFLAG(IS_APPLE)
131   int rv = pthread_cond_timedwait_relative_np(
132       &condition_, user_mutex_, &relative_time);
133 #else
134   // The timeout argument to pthread_cond_timedwait is in absolute time.
135   struct timespec absolute_time;
136 #if BUILDFLAG(IS_NACL)
137   // See comment in constructor for why this is different in NaCl.
138   struct timeval now;
139   gettimeofday(&now, NULL);
140   absolute_time.tv_sec = now.tv_sec;
141   absolute_time.tv_nsec = now.tv_usec * Time::kNanosecondsPerMicrosecond;
142 #else
143   struct timespec now;
144   clock_gettime(CLOCK_MONOTONIC, &now);
145   absolute_time.tv_sec = now.tv_sec;
146   absolute_time.tv_nsec = now.tv_nsec;
147 #endif
148 
149   absolute_time.tv_sec += relative_time.tv_sec;
150   absolute_time.tv_nsec += relative_time.tv_nsec;
151   absolute_time.tv_sec += absolute_time.tv_nsec / Time::kNanosecondsPerSecond;
152   absolute_time.tv_nsec %= Time::kNanosecondsPerSecond;
153   DCHECK_GE(absolute_time.tv_sec, now.tv_sec);  // Overflow paranoia
154 
155 #if defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC)
156   int rv = pthread_cond_timedwait_monotonic_np(
157       &condition_, user_mutex_, &absolute_time);
158 #else
159   int rv = pthread_cond_timedwait(&condition_, user_mutex_, &absolute_time);
160 #endif  // HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC
161 #endif  // BUILDFLAG(IS_APPLE)
162 
163   // On failure, we only expect the CV to timeout. Any other error value means
164   // that we've unexpectedly woken up.
165   DCHECK(rv == 0 || rv == ETIMEDOUT);
166 #if DCHECK_IS_ON()
167   user_lock_->CheckUnheldAndMark();
168 #endif
169 }
170 
Broadcast()171 void ConditionVariable::Broadcast() {
172   int rv = pthread_cond_broadcast(&condition_);
173   DCHECK_EQ(0, rv);
174 }
175 
Signal()176 void ConditionVariable::Signal() {
177   int rv = pthread_cond_signal(&condition_);
178   DCHECK_EQ(0, rv);
179 }
180 
181 }  // namespace base
182