diff options
Diffstat (limited to 'security/sandbox/chromium/base/threading/thread_restrictions.cc')
-rw-r--r-- | security/sandbox/chromium/base/threading/thread_restrictions.cc | 258 |
1 files changed, 258 insertions, 0 deletions
diff --git a/security/sandbox/chromium/base/threading/thread_restrictions.cc b/security/sandbox/chromium/base/threading/thread_restrictions.cc new file mode 100644 index 0000000000..75c37eab4f --- /dev/null +++ b/security/sandbox/chromium/base/threading/thread_restrictions.cc @@ -0,0 +1,258 @@ +// 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() |