tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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