| // 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/lazy_instance.h" | |
| #include "base/logging.h" | |
| #include "base/threading/thread_local.h" | |
| namespace base { | |
| namespace { | |
| LazyInstance<ThreadLocalBoolean>::Leaky g_blocking_disallowed = | |
| LAZY_INSTANCE_INITIALIZER; | |
| LazyInstance<ThreadLocalBoolean>::Leaky | |
| g_singleton_disallowed = LAZY_INSTANCE_INITIALIZER; | |
| LazyInstance<ThreadLocalBoolean>::Leaky g_base_sync_primitives_disallowed = | |
| LAZY_INSTANCE_INITIALIZER; | |
| LazyInstance<ThreadLocalBoolean>::Leaky g_cpu_intensive_work_disallowed = | |
| LAZY_INSTANCE_INITIALIZER; | |
| } // namespace | |
| 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 TaskScheduler, 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)."; | |
| } | |
| 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."; | |
| 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_); | |
| } | |
| 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."; | |
| } | |
| 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."; | |
| } | |
| 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; | |
| } | |
| #if defined(STARBOARD) | |
| // static | |
| bool ThreadRestrictions::GetSingletonAllowed() { | |
| return !g_singleton_disallowed.Get().Get(); | |
| } | |
| #endif // defined(STARBOARD) | |
| // 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() { | |
| if (g_singleton_disallowed.Get().Get()) { | |
| NOTREACHED() << "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."; | |
| } | |
| } | |
| // 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; | |
| } | |
| ThreadRestrictions::ScopedAllowWait::ScopedAllowWait() | |
| : was_allowed_(SetWaitAllowed(true)) {} | |
| ThreadRestrictions::ScopedAllowWait::~ScopedAllowWait() { | |
| SetWaitAllowed(was_allowed_); | |
| } | |
| } // namespace base | |
| #endif // DCHECK_IS_ON() |