tor-browser

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

thread_restrictions.cc (11638B)


      1 // Copyright 2012 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/threading/thread_restrictions.h"
      6 
      7 #include "base/threading/hang_watcher.h"
      8 #include "base/trace_event/base_tracing.h"
      9 #include "build/build_config.h"
     10 
     11 #if DCHECK_IS_ON()
     12 #include "base/check_op.h"
     13 #include "base/no_destructor.h"
     14 #include "base/threading/thread_local.h"
     15 
     16 // NaCL doesn't support stack sampling and Android is slow at stack sampling and
     17 // this causes timeouts (crbug.com/959139).
     18 #if BUILDFLAG(IS_NACL) || BUILDFLAG(IS_ANDROID)
     19 constexpr bool kCaptureStackTraces = false;
     20 #else
     21 // Always disabled when !EXPENSIVE_DCHECKS_ARE_ON() because user-facing builds
     22 // typically drop log strings anyways.
     23 constexpr bool kCaptureStackTraces = EXPENSIVE_DCHECKS_ARE_ON();
     24 #endif
     25 
     26 namespace base {
     27 
     28 BooleanWithStack::BooleanWithStack(bool value) : value_(value) {
     29  if (kCaptureStackTraces) {
     30    stack_.emplace();
     31  }
     32 }
     33 
     34 std::ostream& operator<<(std::ostream& out, const BooleanWithStack& bws) {
     35  out << bws.value_;
     36  if (kCaptureStackTraces) {
     37    if (bws.stack_.has_value()) {
     38      out << " set by\n" << bws.stack_.value();
     39    } else {
     40      out << " (value by default)";
     41    }
     42  }
     43  return out;
     44 }
     45 
     46 namespace {
     47 
     48 // TODO(crbug.com/1423437): Change these to directly-accessed, namespace-scope
     49 // `thread_local BooleanWithStack`s when doing so doesn't cause crashes.
     50 BooleanWithStack& GetBlockingDisallowedTls() {
     51  static NoDestructor<ThreadLocalOwnedPointer<BooleanWithStack>> instance;
     52  auto& tls = *instance;
     53  if (!tls.Get()) {
     54    tls.Set(std::make_unique<BooleanWithStack>());
     55  }
     56  return *tls;
     57 }
     58 BooleanWithStack& GetSingletonDisallowedTls() {
     59  static NoDestructor<ThreadLocalOwnedPointer<BooleanWithStack>> instance;
     60  auto& tls = *instance;
     61  if (!tls.Get()) {
     62    tls.Set(std::make_unique<BooleanWithStack>());
     63  }
     64  return *tls;
     65 }
     66 BooleanWithStack& GetBaseSyncPrimitivesDisallowedTls() {
     67  static NoDestructor<ThreadLocalOwnedPointer<BooleanWithStack>> instance;
     68  auto& tls = *instance;
     69  if (!tls.Get()) {
     70    tls.Set(std::make_unique<BooleanWithStack>());
     71  }
     72  return *tls;
     73 }
     74 BooleanWithStack& GetCPUIntensiveWorkDisallowedTls() {
     75  static NoDestructor<ThreadLocalOwnedPointer<BooleanWithStack>> instance;
     76  auto& tls = *instance;
     77  if (!tls.Get()) {
     78    tls.Set(std::make_unique<BooleanWithStack>());
     79  }
     80  return *tls;
     81 }
     82 
     83 }  // namespace
     84 
     85 namespace internal {
     86 
     87 void AssertBlockingAllowed() {
     88  DCHECK(!GetBlockingDisallowedTls())
     89      << "Function marked as blocking was called from a scope that disallows "
     90         "blocking! If this task is running inside the ThreadPool, it needs "
     91         "to have MayBlock() in its TaskTraits. Otherwise, consider making "
     92         "this blocking work asynchronous or, as a last resort, you may use "
     93         "ScopedAllowBlocking (see its documentation for best practices).\n"
     94      << "blocking_disallowed " << GetBlockingDisallowedTls();
     95 }
     96 
     97 void AssertBlockingDisallowedForTesting() {
     98  DCHECK(GetBlockingDisallowedTls())
     99      << "blocking_disallowed " << GetBlockingDisallowedTls();
    100 }
    101 
    102 }  // namespace internal
    103 
    104 void DisallowBlocking() {
    105  GetBlockingDisallowedTls() = BooleanWithStack(true);
    106 }
    107 
    108 ScopedDisallowBlocking::ScopedDisallowBlocking()
    109    : resetter_(&GetBlockingDisallowedTls(), BooleanWithStack(true)) {}
    110 
    111 ScopedDisallowBlocking::~ScopedDisallowBlocking() {
    112  DCHECK(GetBlockingDisallowedTls())
    113      << "~ScopedDisallowBlocking() running while surprisingly already no "
    114         "longer disallowed.\n"
    115      << "blocking_disallowed " << GetBlockingDisallowedTls();
    116 }
    117 
    118 void DisallowBaseSyncPrimitives() {
    119  GetBaseSyncPrimitivesDisallowedTls() = BooleanWithStack(true);
    120 }
    121 
    122 ScopedDisallowBaseSyncPrimitives::ScopedDisallowBaseSyncPrimitives()
    123    : resetter_(&GetBaseSyncPrimitivesDisallowedTls(), BooleanWithStack(true)) {
    124 }
    125 
    126 ScopedDisallowBaseSyncPrimitives::~ScopedDisallowBaseSyncPrimitives() {
    127  DCHECK(GetBaseSyncPrimitivesDisallowedTls())
    128      << "~ScopedDisallowBaseSyncPrimitives() running while surprisingly "
    129         "already no longer disallowed.\n"
    130      << "base_sync_primitives_disallowed "
    131      << GetBaseSyncPrimitivesDisallowedTls();
    132 }
    133 
    134 ScopedAllowBaseSyncPrimitives::ScopedAllowBaseSyncPrimitives()
    135    : resetter_(&GetBaseSyncPrimitivesDisallowedTls(),
    136                BooleanWithStack(false)) {
    137  DCHECK(!GetBlockingDisallowedTls())
    138      << "To allow //base sync primitives in a scope where blocking is "
    139         "disallowed use ScopedAllowBaseSyncPrimitivesOutsideBlockingScope.\n"
    140      << "blocking_disallowed " << GetBlockingDisallowedTls();
    141 }
    142 
    143 ScopedAllowBaseSyncPrimitives::~ScopedAllowBaseSyncPrimitives() {
    144  DCHECK(!GetBaseSyncPrimitivesDisallowedTls())
    145      << "~ScopedAllowBaseSyncPrimitives() running while surprisingly already "
    146         "no longer allowed.\n"
    147      << "base_sync_primitives_disallowed "
    148      << GetBaseSyncPrimitivesDisallowedTls();
    149 }
    150 
    151 ScopedAllowBaseSyncPrimitivesForTesting::
    152    ScopedAllowBaseSyncPrimitivesForTesting()
    153    : resetter_(&GetBaseSyncPrimitivesDisallowedTls(),
    154                BooleanWithStack(false)) {}
    155 
    156 ScopedAllowBaseSyncPrimitivesForTesting::
    157    ~ScopedAllowBaseSyncPrimitivesForTesting() {
    158  DCHECK(!GetBaseSyncPrimitivesDisallowedTls())
    159      << "~ScopedAllowBaseSyncPrimitivesForTesting() running while "  // IN-TEST
    160         "surprisingly already no longer allowed.\n"
    161      << "base_sync_primitives_disallowed "
    162      << GetBaseSyncPrimitivesDisallowedTls();
    163 }
    164 
    165 ScopedAllowUnresponsiveTasksForTesting::ScopedAllowUnresponsiveTasksForTesting()
    166    : base_sync_resetter_(&GetBaseSyncPrimitivesDisallowedTls(),
    167                          BooleanWithStack(false)),
    168      blocking_resetter_(&GetBlockingDisallowedTls(), BooleanWithStack(false)),
    169      cpu_resetter_(&GetCPUIntensiveWorkDisallowedTls(),
    170                    BooleanWithStack(false)) {}
    171 
    172 ScopedAllowUnresponsiveTasksForTesting::
    173    ~ScopedAllowUnresponsiveTasksForTesting() {
    174  DCHECK(!GetBaseSyncPrimitivesDisallowedTls())
    175      << "~ScopedAllowUnresponsiveTasksForTesting() running while "  // IN-TEST
    176         "surprisingly already no longer allowed.\n"
    177      << "base_sync_primitives_disallowed "
    178      << GetBaseSyncPrimitivesDisallowedTls();
    179  DCHECK(!GetBlockingDisallowedTls())
    180      << "~ScopedAllowUnresponsiveTasksForTesting() running while "  // IN-TEST
    181         "surprisingly already no longer allowed.\n"
    182      << "blocking_disallowed " << GetBlockingDisallowedTls();
    183  DCHECK(!GetCPUIntensiveWorkDisallowedTls())
    184      << "~ScopedAllowUnresponsiveTasksForTesting() running while "  // IN-TEST
    185         "surprisingly already no longer allowed.\n"
    186      << "cpu_intensive_work_disallowed " << GetCPUIntensiveWorkDisallowedTls();
    187 }
    188 
    189 namespace internal {
    190 
    191 void AssertBaseSyncPrimitivesAllowed() {
    192  DCHECK(!GetBaseSyncPrimitivesDisallowedTls())
    193      << "Waiting on a //base sync primitive is not allowed on this thread to "
    194         "prevent jank and deadlock. If waiting on a //base sync primitive is "
    195         "unavoidable, do it within the scope of a "
    196         "ScopedAllowBaseSyncPrimitives. If in a test, use "
    197         "ScopedAllowBaseSyncPrimitivesForTesting.\n"
    198      << "base_sync_primitives_disallowed "
    199      << GetBaseSyncPrimitivesDisallowedTls()
    200      << "It can be useful to know that blocking_disallowed is "
    201      << GetBlockingDisallowedTls();
    202 }
    203 
    204 void ResetThreadRestrictionsForTesting() {
    205  GetBlockingDisallowedTls() = BooleanWithStack(false);
    206  GetSingletonDisallowedTls() = BooleanWithStack(false);
    207  GetBaseSyncPrimitivesDisallowedTls() = BooleanWithStack(false);
    208  GetCPUIntensiveWorkDisallowedTls() = BooleanWithStack(false);
    209 }
    210 
    211 void AssertSingletonAllowed() {
    212  DCHECK(!GetSingletonDisallowedTls())
    213      << "LazyInstance/Singleton is not allowed to be used on this thread. "
    214         "Most likely it's because this thread is not joinable (or the current "
    215         "task is running with TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN "
    216         "semantics), so AtExitManager may have deleted the object on "
    217         "shutdown, leading to a potential shutdown crash. If you need to use "
    218         "the object from this context, it'll have to be updated to use Leaky "
    219         "traits.\n"
    220      << "singleton_disallowed " << GetSingletonDisallowedTls();
    221 }
    222 
    223 }  // namespace internal
    224 
    225 void DisallowSingleton() {
    226  GetSingletonDisallowedTls() = BooleanWithStack(true);
    227 }
    228 
    229 ScopedDisallowSingleton::ScopedDisallowSingleton()
    230    : resetter_(&GetSingletonDisallowedTls(), BooleanWithStack(true)) {}
    231 
    232 ScopedDisallowSingleton::~ScopedDisallowSingleton() {
    233  DCHECK(GetSingletonDisallowedTls())
    234      << "~ScopedDisallowSingleton() running while surprisingly already no "
    235         "longer disallowed.\n"
    236      << "singleton_disallowed " << GetSingletonDisallowedTls();
    237 }
    238 
    239 void AssertLongCPUWorkAllowed() {
    240  DCHECK(!GetCPUIntensiveWorkDisallowedTls())
    241      << "Function marked as CPU intensive was called from a scope that "
    242         "disallows this kind of work! Consider making this work "
    243         "asynchronous.\n"
    244      << "cpu_intensive_work_disallowed " << GetCPUIntensiveWorkDisallowedTls();
    245 }
    246 
    247 void DisallowUnresponsiveTasks() {
    248  DisallowBlocking();
    249  DisallowBaseSyncPrimitives();
    250  GetCPUIntensiveWorkDisallowedTls() = BooleanWithStack(true);
    251 }
    252 
    253 // static
    254 void PermanentThreadAllowance::AllowBlocking() {
    255  GetBlockingDisallowedTls() = BooleanWithStack(false);
    256 }
    257 
    258 // static
    259 void PermanentThreadAllowance::AllowBaseSyncPrimitives() {
    260  GetBaseSyncPrimitivesDisallowedTls() = BooleanWithStack(false);
    261 }
    262 
    263 }  // namespace base
    264 
    265 #endif  // DCHECK_IS_ON()
    266 
    267 namespace base {
    268 
    269 ScopedAllowBlocking::ScopedAllowBlocking(const Location& from_here)
    270 #if DCHECK_IS_ON()
    271    : resetter_(&GetBlockingDisallowedTls(), BooleanWithStack(false))
    272 #endif
    273 {
    274  TRACE_EVENT_BEGIN(
    275      "base", "ScopedAllowBlocking", [&](perfetto::EventContext ctx) {
    276        ctx.event()->set_source_location_iid(
    277            base::trace_event::InternedSourceLocation::Get(&ctx, from_here));
    278      });
    279 }
    280 
    281 ScopedAllowBlocking::~ScopedAllowBlocking() {
    282  TRACE_EVENT_END0("base", "ScopedAllowBlocking");
    283 
    284 #if DCHECK_IS_ON()
    285  DCHECK(!GetBlockingDisallowedTls())
    286      << "~ScopedAllowBlocking() running while surprisingly already no longer "
    287         "allowed.\n"
    288      << "blocking_disallowed " << GetBlockingDisallowedTls();
    289 #endif
    290 }
    291 
    292 #if !defined(MOZ_SANDBOX)
    293 ScopedAllowBaseSyncPrimitivesOutsideBlockingScope::
    294    ScopedAllowBaseSyncPrimitivesOutsideBlockingScope(const Location& from_here)
    295 #if DCHECK_IS_ON()
    296    : resetter_(&GetBaseSyncPrimitivesDisallowedTls(), BooleanWithStack(false))
    297 #endif
    298 {
    299  TRACE_EVENT_BEGIN(
    300      "base", "ScopedAllowBaseSyncPrimitivesOutsideBlockingScope",
    301      [&](perfetto::EventContext ctx) {
    302        ctx.event()->set_source_location_iid(
    303            base::trace_event::InternedSourceLocation::Get(&ctx, from_here));
    304      });
    305 
    306  // Since this object is used to indicate that sync primitives will be used to
    307  // wait for an event ignore the current operation for hang watching purposes
    308  // since the wait time duration is unknown.
    309  base::HangWatcher::InvalidateActiveExpectations();
    310 }
    311 #endif
    312 
    313 ScopedAllowBaseSyncPrimitivesOutsideBlockingScope::
    314    ~ScopedAllowBaseSyncPrimitivesOutsideBlockingScope() {
    315  TRACE_EVENT_END0("base", "ScopedAllowBaseSyncPrimitivesOutsideBlockingScope");
    316 
    317 #if DCHECK_IS_ON()
    318  DCHECK(!GetBaseSyncPrimitivesDisallowedTls())
    319      << "~ScopedAllowBaseSyncPrimitivesOutsideBlockingScope() running while "
    320         "surprisingly already no longer allowed.\n"
    321      << "base_sync_primitives_disallowed "
    322      << GetBaseSyncPrimitivesDisallowedTls();
    323 #endif
    324 }
    325 
    326 }  // namespace base