condition_variable_posix.cc (5554B)
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 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 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 89 void ConditionVariable::InitializeFeatures() { 90 g_skip_wakeup_hack.store( 91 base::FeatureList::IsEnabled(kSkipConditionVariableWakeupHack), 92 std::memory_order_relaxed); 93 } 94 #endif 95 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 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 170 void ConditionVariable::Broadcast() { 171 int rv = pthread_cond_broadcast(&condition_); 172 DCHECK_EQ(0, rv); 173 } 174 175 void ConditionVariable::Signal() { 176 int rv = pthread_cond_signal(&condition_); 177 DCHECK_EQ(0, rv); 178 } 179 180 } // namespace base