summaryrefslogtreecommitdiffstats
path: root/security/sandbox/chromium/base/threading/thread_restrictions.cc
diff options
context:
space:
mode:
Diffstat (limited to 'security/sandbox/chromium/base/threading/thread_restrictions.cc')
-rw-r--r--security/sandbox/chromium/base/threading/thread_restrictions.cc258
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()