• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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