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