|  | // 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; | 
|  |  | 
|  | }  // 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 in a narrow scope."; | 
|  | } | 
|  |  | 
|  | 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); | 
|  | } | 
|  |  | 
|  | }  // namespace internal | 
|  |  | 
|  | 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() { | 
|  | 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() |