| // 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" |
| |
| #if DCHECK_IS_ON() |
| |
| #include "base/debug/stack_trace.h" |
| #include "base/lazy_instance.h" |
| #include "base/logging.h" |
| #include "base/threading/thread_local.h" |
| #include "build/build_config.h" |
| |
| namespace base { |
| |
| std::ostream& operator<<(std::ostream&out, const ThreadLocalBoolean& tl) { |
| out << "currently set to " << (tl.Get() ? "true" : "false"); |
| return out; |
| } |
| |
| namespace { |
| |
| #if defined(OS_NACL) || defined(OS_ANDROID) |
| // NaCL doesn't support stack sampling and Android is slow at stack |
| // sampling and this causes timeouts (crbug.com/959139). |
| using ThreadLocalBooleanWithStacks = ThreadLocalBoolean; |
| #else |
| class ThreadLocalBooleanWithStacks { |
| public: |
| ThreadLocalBooleanWithStacks() = default; |
| |
| bool Get() const { return bool_.Get(); } |
| |
| void Set(bool val) { |
| stack_.Set(std::make_unique<debug::StackTrace>()); |
| bool_.Set(val); |
| } |
| |
| friend std::ostream& operator<<(std::ostream& out, |
| const ThreadLocalBooleanWithStacks& tl) { |
| out << tl.bool_ << " by "; |
| |
| if (!tl.stack_.Get()) |
| return out << "default value\n"; |
| out << "\n"; |
| tl.stack_.Get()->OutputToStream(&out); |
| return out; |
| } |
| |
| private: |
| ThreadLocalBoolean bool_; |
| ThreadLocalOwnedPointer<debug::StackTrace> stack_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ThreadLocalBooleanWithStacks); |
| }; |
| #endif // defined(OS_NACL) |
| |
| LazyInstance<ThreadLocalBooleanWithStacks>::Leaky g_blocking_disallowed = |
| LAZY_INSTANCE_INITIALIZER; |
| |
| LazyInstance<ThreadLocalBooleanWithStacks>::Leaky g_singleton_disallowed = |
| LAZY_INSTANCE_INITIALIZER; |
| |
| LazyInstance<ThreadLocalBooleanWithStacks>::Leaky |
| g_base_sync_primitives_disallowed = LAZY_INSTANCE_INITIALIZER; |
| |
| LazyInstance<ThreadLocalBooleanWithStacks>::Leaky |
| g_cpu_intensive_work_disallowed = LAZY_INSTANCE_INITIALIZER; |
| |
| } // namespace |
| |
| namespace internal { |
| |
| void AssertBlockingAllowed() { |
| DCHECK(!g_blocking_disallowed.Get().Get()) |
| << "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 " << g_blocking_disallowed.Get(); |
| } |
| |
| } // namespace internal |
| |
| void DisallowBlocking() { |
| g_blocking_disallowed.Get().Set(true); |
| } |
| |
| ScopedDisallowBlocking::ScopedDisallowBlocking() |
| : was_disallowed_(g_blocking_disallowed.Get().Get()) { |
| g_blocking_disallowed.Get().Set(true); |
| } |
| |
| ScopedDisallowBlocking::~ScopedDisallowBlocking() { |
| DCHECK(g_blocking_disallowed.Get().Get()); |
| g_blocking_disallowed.Get().Set(was_disallowed_); |
| } |
| |
| ScopedAllowBlocking::ScopedAllowBlocking() |
| : was_disallowed_(g_blocking_disallowed.Get().Get()) { |
| g_blocking_disallowed.Get().Set(false); |
| } |
| |
| ScopedAllowBlocking::~ScopedAllowBlocking() { |
| DCHECK(!g_blocking_disallowed.Get().Get()); |
| g_blocking_disallowed.Get().Set(was_disallowed_); |
| } |
| |
| void DisallowBaseSyncPrimitives() { |
| g_base_sync_primitives_disallowed.Get().Set(true); |
| } |
| |
| ScopedAllowBaseSyncPrimitives::ScopedAllowBaseSyncPrimitives() |
| : was_disallowed_(g_base_sync_primitives_disallowed.Get().Get()) { |
| DCHECK(!g_blocking_disallowed.Get().Get()) |
| << "To allow //base sync primitives in a scope where blocking is " |
| "disallowed use ScopedAllowBaseSyncPrimitivesOutsideBlockingScope.\n" |
| << "g_blocking_disallowed " << g_blocking_disallowed.Get(); |
| g_base_sync_primitives_disallowed.Get().Set(false); |
| } |
| |
| ScopedAllowBaseSyncPrimitives::~ScopedAllowBaseSyncPrimitives() { |
| DCHECK(!g_base_sync_primitives_disallowed.Get().Get()); |
| g_base_sync_primitives_disallowed.Get().Set(was_disallowed_); |
| } |
| |
| ScopedAllowBaseSyncPrimitivesOutsideBlockingScope:: |
| ScopedAllowBaseSyncPrimitivesOutsideBlockingScope() |
| : was_disallowed_(g_base_sync_primitives_disallowed.Get().Get()) { |
| g_base_sync_primitives_disallowed.Get().Set(false); |
| } |
| |
| ScopedAllowBaseSyncPrimitivesOutsideBlockingScope:: |
| ~ScopedAllowBaseSyncPrimitivesOutsideBlockingScope() { |
| DCHECK(!g_base_sync_primitives_disallowed.Get().Get()); |
| g_base_sync_primitives_disallowed.Get().Set(was_disallowed_); |
| } |
| |
| ScopedAllowBaseSyncPrimitivesForTesting:: |
| ScopedAllowBaseSyncPrimitivesForTesting() |
| : was_disallowed_(g_base_sync_primitives_disallowed.Get().Get()) { |
| g_base_sync_primitives_disallowed.Get().Set(false); |
| } |
| |
| ScopedAllowBaseSyncPrimitivesForTesting:: |
| ~ScopedAllowBaseSyncPrimitivesForTesting() { |
| DCHECK(!g_base_sync_primitives_disallowed.Get().Get()); |
| g_base_sync_primitives_disallowed.Get().Set(was_disallowed_); |
| } |
| |
| ScopedAllowUnresponsiveTasksForTesting::ScopedAllowUnresponsiveTasksForTesting() |
| : was_disallowed_base_sync_(g_base_sync_primitives_disallowed.Get().Get()), |
| was_disallowed_blocking_(g_blocking_disallowed.Get().Get()), |
| was_disallowed_cpu_(g_cpu_intensive_work_disallowed.Get().Get()) { |
| g_base_sync_primitives_disallowed.Get().Set(false); |
| g_blocking_disallowed.Get().Set(false); |
| g_cpu_intensive_work_disallowed.Get().Set(false); |
| } |
| |
| ScopedAllowUnresponsiveTasksForTesting:: |
| ~ScopedAllowUnresponsiveTasksForTesting() { |
| DCHECK(!g_base_sync_primitives_disallowed.Get().Get()); |
| DCHECK(!g_blocking_disallowed.Get().Get()); |
| DCHECK(!g_cpu_intensive_work_disallowed.Get().Get()); |
| g_base_sync_primitives_disallowed.Get().Set(was_disallowed_base_sync_); |
| g_blocking_disallowed.Get().Set(was_disallowed_blocking_); |
| g_cpu_intensive_work_disallowed.Get().Set(was_disallowed_cpu_); |
| } |
| |
| namespace internal { |
| |
| void AssertBaseSyncPrimitivesAllowed() { |
| DCHECK(!g_base_sync_primitives_disallowed.Get().Get()) |
| << "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 " |
| << g_base_sync_primitives_disallowed.Get() |
| << "It can be useful to know that g_blocking_disallowed is " |
| << g_blocking_disallowed.Get(); |
| } |
| |
| void ResetThreadRestrictionsForTesting() { |
| g_blocking_disallowed.Get().Set(false); |
| g_singleton_disallowed.Get().Set(false); |
| g_base_sync_primitives_disallowed.Get().Set(false); |
| g_cpu_intensive_work_disallowed.Get().Set(false); |
| } |
| |
| } // namespace internal |
| |
| void AssertLongCPUWorkAllowed() { |
| DCHECK(!g_cpu_intensive_work_disallowed.Get().Get()) |
| << "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 " |
| << g_cpu_intensive_work_disallowed.Get(); |
| } |
| |
| void DisallowUnresponsiveTasks() { |
| DisallowBlocking(); |
| DisallowBaseSyncPrimitives(); |
| g_cpu_intensive_work_disallowed.Get().Set(true); |
| } |
| |
| ThreadRestrictions::ScopedAllowIO::ScopedAllowIO() |
| : was_allowed_(SetIOAllowed(true)) {} |
| |
| ThreadRestrictions::ScopedAllowIO::~ScopedAllowIO() { |
| SetIOAllowed(was_allowed_); |
| } |
| |
| // static |
| bool ThreadRestrictions::SetIOAllowed(bool allowed) { |
| bool previous_disallowed = g_blocking_disallowed.Get().Get(); |
| g_blocking_disallowed.Get().Set(!allowed); |
| return !previous_disallowed; |
| } |
| |
| // static |
| bool ThreadRestrictions::SetSingletonAllowed(bool allowed) { |
| bool previous_disallowed = g_singleton_disallowed.Get().Get(); |
| g_singleton_disallowed.Get().Set(!allowed); |
| return !previous_disallowed; |
| } |
| |
| // static |
| void ThreadRestrictions::AssertSingletonAllowed() { |
| DCHECK(!g_singleton_disallowed.Get().Get()) |
| << "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 " << g_singleton_disallowed.Get(); |
| } |
| |
| // static |
| void ThreadRestrictions::DisallowWaiting() { |
| DisallowBaseSyncPrimitives(); |
| } |
| |
| bool ThreadRestrictions::SetWaitAllowed(bool allowed) { |
| bool previous_disallowed = g_base_sync_primitives_disallowed.Get().Get(); |
| g_base_sync_primitives_disallowed.Get().Set(!allowed); |
| return !previous_disallowed; |
| } |
| |
| } // namespace base |
| |
| #endif // DCHECK_IS_ON() |