123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363 |
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "base/threading/thread_restrictions.h"
- #include "base/threading/hang_watcher.h"
- #include "base/trace_event/base_tracing.h"
- #include "build/build_config.h"
- #if DCHECK_IS_ON()
- #include <utility>
- #include "base/check_op.h"
- #include "base/debug/stack_trace.h"
- #include "base/no_destructor.h"
- #include "base/threading/thread_local.h"
- #include "base/trace_event/base_tracing.h"
- #include "build/build_config.h"
- #include "third_party/abseil-cpp/absl/types/optional.h"
- // NaCL doesn't support stack sampling and Android is slow at stack sampling and
- // this causes timeouts (crbug.com/959139).
- #if BUILDFLAG(IS_NACL) || BUILDFLAG(IS_ANDROID)
- constexpr bool kCaptureStackTraces = false;
- #else
- // Always disabled when !EXPENSIVE_DCHECKS_ARE_ON() because user-facing builds
- // typically drop log strings anyways.
- constexpr bool kCaptureStackTraces = EXPENSIVE_DCHECKS_ARE_ON();
- #endif
- namespace base {
- class BooleanWithStack {
- public:
- // Default value.
- BooleanWithStack() = default;
- // Value when explicitly set.
- explicit BooleanWithStack(bool value) : value_(value) {
- if (kCaptureStackTraces)
- stack_.emplace();
- }
- BooleanWithStack(const BooleanWithStack&) = delete;
- BooleanWithStack& operator=(const BooleanWithStack&) = delete;
- explicit operator bool() const { return value_; }
- friend std::ostream& operator<<(std::ostream& out,
- const BooleanWithStack& bws) {
- out << bws.value_;
- if (kCaptureStackTraces) {
- if (bws.stack_.has_value())
- out << " set by\n" << bws.stack_.value();
- else
- out << " (value by default)";
- }
- return out;
- }
- private:
- const bool value_ = false;
- absl::optional<debug::StackTrace> stack_;
- };
- namespace {
- ThreadLocalOwnedPointer<BooleanWithStack>& GetBlockingDisallowedTls() {
- static NoDestructor<ThreadLocalOwnedPointer<BooleanWithStack>> instance;
- auto& tls = *instance;
- if (!tls.Get())
- tls.Set(std::make_unique<BooleanWithStack>());
- return tls;
- }
- ThreadLocalOwnedPointer<BooleanWithStack>& GetSingletonDisallowedTls() {
- static NoDestructor<ThreadLocalOwnedPointer<BooleanWithStack>> instance;
- auto& tls = *instance;
- if (!tls.Get())
- tls.Set(std::make_unique<BooleanWithStack>());
- return tls;
- }
- ThreadLocalOwnedPointer<BooleanWithStack>&
- GetBaseSyncPrimitivesDisallowedTls() {
- static NoDestructor<ThreadLocalOwnedPointer<BooleanWithStack>> instance;
- auto& tls = *instance;
- if (!tls.Get())
- tls.Set(std::make_unique<BooleanWithStack>());
- return tls;
- }
- ThreadLocalOwnedPointer<BooleanWithStack>& GetCPUIntensiveWorkDisallowedTls() {
- static NoDestructor<ThreadLocalOwnedPointer<BooleanWithStack>> instance;
- auto& tls = *instance;
- if (!tls.Get())
- tls.Set(std::make_unique<BooleanWithStack>());
- return tls;
- }
- } // namespace
- namespace internal {
- void AssertBlockingAllowed() {
- DCHECK(!*GetBlockingDisallowedTls())
- << "Function marked as blocking was called from a scope that disallows "
- "blocking! If this task is running inside the ThreadPool, it needs "
- "to have MayBlock() in its TaskTraits. Otherwise, consider making "
- "this blocking work asynchronous or, as a last resort, you may use "
- "ScopedAllowBlocking (see its documentation for best practices).\n"
- << "g_blocking_disallowed " << *GetBlockingDisallowedTls();
- }
- void AssertBlockingDisallowedForTesting() {
- DCHECK(*GetBlockingDisallowedTls())
- << "g_blocking_disallowed " << *GetBlockingDisallowedTls();
- }
- } // namespace internal
- void DisallowBlocking() {
- GetBlockingDisallowedTls().Set(std::make_unique<BooleanWithStack>(true));
- }
- ScopedDisallowBlocking::ScopedDisallowBlocking()
- : was_disallowed_(GetBlockingDisallowedTls().Set(
- std::make_unique<BooleanWithStack>(true))) {}
- ScopedDisallowBlocking::~ScopedDisallowBlocking() {
- DCHECK(*GetBlockingDisallowedTls())
- << "~ScopedDisallowBlocking() running while surprisingly already no "
- "longer disallowed.\n"
- << "g_blocking_disallowed " << *GetBlockingDisallowedTls();
- GetBlockingDisallowedTls().Set(std::move(was_disallowed_));
- }
- void DisallowBaseSyncPrimitives() {
- GetBaseSyncPrimitivesDisallowedTls().Set(
- std::make_unique<BooleanWithStack>(true));
- }
- ScopedDisallowBaseSyncPrimitives::ScopedDisallowBaseSyncPrimitives()
- : was_disallowed_(GetBaseSyncPrimitivesDisallowedTls().Set(
- std::make_unique<BooleanWithStack>(true))) {}
- ScopedDisallowBaseSyncPrimitives::~ScopedDisallowBaseSyncPrimitives() {
- DCHECK(*GetBaseSyncPrimitivesDisallowedTls())
- << "~ScopedDisallowBaseSyncPrimitives() running while surprisingly "
- "already no longer disallowed.\n"
- << "g_base_sync_primitives_disallowed "
- << *GetBaseSyncPrimitivesDisallowedTls();
- GetBaseSyncPrimitivesDisallowedTls().Set(std::move(was_disallowed_));
- }
- ScopedAllowBaseSyncPrimitives::ScopedAllowBaseSyncPrimitives()
- : was_disallowed_(GetBaseSyncPrimitivesDisallowedTls().Set(
- std::make_unique<BooleanWithStack>(false))) {
- DCHECK(!*GetBlockingDisallowedTls())
- << "To allow //base sync primitives in a scope where blocking is "
- "disallowed use ScopedAllowBaseSyncPrimitivesOutsideBlockingScope.\n"
- << "g_blocking_disallowed " << *GetBlockingDisallowedTls();
- }
- ScopedAllowBaseSyncPrimitives::~ScopedAllowBaseSyncPrimitives() {
- DCHECK(!*GetBaseSyncPrimitivesDisallowedTls());
- GetBaseSyncPrimitivesDisallowedTls().Set(std::move(was_disallowed_));
- }
- ScopedAllowBaseSyncPrimitivesForTesting::
- ScopedAllowBaseSyncPrimitivesForTesting()
- : was_disallowed_(GetBaseSyncPrimitivesDisallowedTls().Set(
- std::make_unique<BooleanWithStack>(false))) {}
- ScopedAllowBaseSyncPrimitivesForTesting::
- ~ScopedAllowBaseSyncPrimitivesForTesting() {
- DCHECK(!*GetBaseSyncPrimitivesDisallowedTls());
- GetBaseSyncPrimitivesDisallowedTls().Set(std::move(was_disallowed_));
- }
- ScopedAllowUnresponsiveTasksForTesting::ScopedAllowUnresponsiveTasksForTesting()
- : was_disallowed_base_sync_(GetBaseSyncPrimitivesDisallowedTls().Set(
- std::make_unique<BooleanWithStack>(false))),
- was_disallowed_blocking_(GetBlockingDisallowedTls().Set(
- std::make_unique<BooleanWithStack>(false))),
- was_disallowed_cpu_(GetCPUIntensiveWorkDisallowedTls().Set(
- std::make_unique<BooleanWithStack>(false))) {}
- ScopedAllowUnresponsiveTasksForTesting::
- ~ScopedAllowUnresponsiveTasksForTesting() {
- DCHECK(!*GetBaseSyncPrimitivesDisallowedTls());
- DCHECK(!*GetBlockingDisallowedTls());
- DCHECK(!*GetCPUIntensiveWorkDisallowedTls());
- GetBaseSyncPrimitivesDisallowedTls().Set(
- std::move(was_disallowed_base_sync_));
- GetBlockingDisallowedTls().Set(std::move(was_disallowed_blocking_));
- GetCPUIntensiveWorkDisallowedTls().Set(std::move(was_disallowed_cpu_));
- }
- namespace internal {
- void AssertBaseSyncPrimitivesAllowed() {
- DCHECK(!*GetBaseSyncPrimitivesDisallowedTls())
- << "Waiting on a //base sync primitive is not allowed on this thread to "
- "prevent jank and deadlock. If waiting on a //base sync primitive is "
- "unavoidable, do it within the scope of a "
- "ScopedAllowBaseSyncPrimitives. If in a test, "
- "use ScopedAllowBaseSyncPrimitivesForTesting.\n"
- << "g_base_sync_primitives_disallowed "
- << *GetBaseSyncPrimitivesDisallowedTls()
- << "It can be useful to know that g_blocking_disallowed is "
- << *GetBlockingDisallowedTls();
- }
- void ResetThreadRestrictionsForTesting() {
- GetBlockingDisallowedTls().Set(std::make_unique<BooleanWithStack>(false));
- GetSingletonDisallowedTls().Set(std::make_unique<BooleanWithStack>(false));
- GetBaseSyncPrimitivesDisallowedTls().Set(
- std::make_unique<BooleanWithStack>(false));
- GetCPUIntensiveWorkDisallowedTls().Set(
- std::make_unique<BooleanWithStack>(false));
- }
- void AssertSingletonAllowed() {
- DCHECK(!*GetSingletonDisallowedTls())
- << "LazyInstance/Singleton is not allowed to be used on this thread. "
- "Most likely it's because this thread is not joinable (or the current "
- "task is running with TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN "
- "semantics), so AtExitManager may have deleted the object on "
- "shutdown, leading to a potential shutdown crash. If you need to use "
- "the object from this context, it'll have to be updated to use Leaky "
- "traits.\n"
- << "g_singleton_disallowed " << *GetSingletonDisallowedTls();
- }
- } // namespace internal
- void DisallowSingleton() {
- GetSingletonDisallowedTls().Set(std::make_unique<BooleanWithStack>(true));
- }
- ScopedDisallowSingleton::ScopedDisallowSingleton()
- : was_disallowed_(GetSingletonDisallowedTls().Set(
- std::make_unique<BooleanWithStack>(true))) {}
- ScopedDisallowSingleton::~ScopedDisallowSingleton() {
- DCHECK(*GetSingletonDisallowedTls())
- << "~ScopedDisallowSingleton() running while surprisingly already no "
- "longer disallowed.\n"
- << "g_singleton_disallowed " << *GetSingletonDisallowedTls();
- GetSingletonDisallowedTls().Set(std::move(was_disallowed_));
- }
- void AssertLongCPUWorkAllowed() {
- DCHECK(!*GetCPUIntensiveWorkDisallowedTls())
- << "Function marked as CPU intensive was called from a scope that "
- "disallows this kind of work! Consider making this work "
- "asynchronous.\n"
- << "g_cpu_intensive_work_disallowed "
- << *GetCPUIntensiveWorkDisallowedTls();
- }
- void DisallowUnresponsiveTasks() {
- DisallowBlocking();
- DisallowBaseSyncPrimitives();
- GetCPUIntensiveWorkDisallowedTls().Set(
- std::make_unique<BooleanWithStack>(true));
- }
- // static
- void PermanentThreadAllowance::AllowBlocking() {
- GetBlockingDisallowedTls().Set(std::make_unique<BooleanWithStack>(false));
- }
- // static
- void PermanentThreadAllowance::AllowBaseSyncPrimitives() {
- GetBaseSyncPrimitivesDisallowedTls().Set(
- std::make_unique<BooleanWithStack>(false));
- }
- // static
- void PermanentSingletonAllowance::AllowSingleton() {
- GetSingletonDisallowedTls().Set(std::make_unique<BooleanWithStack>(false));
- }
- } // namespace base
- #endif // DCHECK_IS_ON()
- namespace base {
- ScopedAllowBlocking::ScopedAllowBlocking(const Location& from_here)
- #if DCHECK_IS_ON()
- : was_disallowed_(GetBlockingDisallowedTls().Set(
- std::make_unique<BooleanWithStack>(false)))
- #endif
- {
- TRACE_EVENT_BEGIN(
- "base", "ScopedAllowBlocking", [&](perfetto::EventContext ctx) {
- ctx.event()->set_source_location_iid(
- base::trace_event::InternedSourceLocation::Get(&ctx, from_here));
- });
- }
- ScopedAllowBlocking::~ScopedAllowBlocking() {
- TRACE_EVENT_END0("base", "ScopedAllowBlocking");
- #if DCHECK_IS_ON()
- DCHECK(!*GetBlockingDisallowedTls());
- GetBlockingDisallowedTls().Set(std::move(was_disallowed_));
- #endif
- }
- ScopedAllowBaseSyncPrimitivesOutsideBlockingScope::
- ScopedAllowBaseSyncPrimitivesOutsideBlockingScope(const Location& from_here)
- #if DCHECK_IS_ON()
- : was_disallowed_(GetBaseSyncPrimitivesDisallowedTls().Set(
- std::make_unique<BooleanWithStack>(false)))
- #endif
- {
- TRACE_EVENT_BEGIN(
- "base", "ScopedAllowBaseSyncPrimitivesOutsideBlockingScope",
- [&](perfetto::EventContext ctx) {
- ctx.event()->set_source_location_iid(
- base::trace_event::InternedSourceLocation::Get(&ctx, from_here));
- });
- // Since this object is used to indicate that sync primitives will be used to
- // wait for an event ignore the current operation for hang watching purposes
- // since the wait time duration is unknown.
- base::HangWatcher::InvalidateActiveExpectations();
- }
- ScopedAllowBaseSyncPrimitivesOutsideBlockingScope::
- ~ScopedAllowBaseSyncPrimitivesOutsideBlockingScope() {
- TRACE_EVENT_END0("base", "ScopedAllowBaseSyncPrimitivesOutsideBlockingScope");
- #if DCHECK_IS_ON()
- DCHECK(!*GetBaseSyncPrimitivesDisallowedTls());
- GetBaseSyncPrimitivesDisallowedTls().Set(std::move(was_disallowed_));
- #endif
- }
- ThreadRestrictions::ScopedAllowIO::ScopedAllowIO(const Location& from_here)
- #if DCHECK_IS_ON()
- : was_disallowed_(GetBlockingDisallowedTls().Set(
- std::make_unique<BooleanWithStack>(false)))
- #endif
- {
- TRACE_EVENT_BEGIN("base", "ScopedAllowIO", [&](perfetto::EventContext ctx) {
- ctx.event()->set_source_location_iid(
- base::trace_event::InternedSourceLocation::Get(&ctx, from_here));
- });
- }
- ThreadRestrictions::ScopedAllowIO::~ScopedAllowIO() {
- TRACE_EVENT_END0("base", "ScopedAllowIO");
- #if DCHECK_IS_ON()
- GetBlockingDisallowedTls().Set(std::move(was_disallowed_));
- #endif
- }
- } // namespace base
|