blob: b4cf3c9ba781ea74a30ac6bd68870c0aacf2fa10 [file] [log] [blame]
// 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"
#include "base/trace_event/base_tracing.h"
#if DCHECK_IS_ON()
#include <utility>
#include "base/check_op.h"
#include "base/debug/stack_trace.h"
#include "base/no_destructor.h"
#include "base/threading/thread_local.h"
#include "base/trace_event/base_tracing.h"
#include "build/build_config.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace base {
struct BooleanWithStack {
// Default value.
BooleanWithStack() = default;
// Value when explicitly set.
explicit BooleanWithStack(bool value)
: value_(value)
#if !defined(OS_NACL) && !defined(OS_ANDROID)
,
stack_(absl::in_place)
#endif
{
}
explicit operator bool() const { return value_; }
friend std::ostream& operator<<(std::ostream& out,
const BooleanWithStack& bws) {
out << bws.value_;
#if !defined(OS_NACL) && !defined(OS_ANDROID)
if (bws.stack_.has_value()) {
out << " set by\n" << bws.stack_.value();
} else {
out << " (value by default)";
}
#endif
return out;
}
const bool value_ = false;
// NaCL doesn't support stack sampling and Android is slow at stack
// sampling and this causes timeouts (crbug.com/959139).
#if !defined(OS_NACL) && !defined(OS_ANDROID)
const absl::optional<debug::StackTrace> stack_;
#endif
};
namespace {
ThreadLocalOwnedPointer<BooleanWithStack>& GetBlockingDisallowedTls() {
static NoDestructor<ThreadLocalOwnedPointer<BooleanWithStack>> instance;
auto& tls = *instance;
if (!tls.Get())
tls.Set(std::make_unique<BooleanWithStack>());
return tls;
}
ThreadLocalOwnedPointer<BooleanWithStack>& GetSingletonDisallowedTls() {
static NoDestructor<ThreadLocalOwnedPointer<BooleanWithStack>> instance;
auto& tls = *instance;
if (!tls.Get())
tls.Set(std::make_unique<BooleanWithStack>());
return tls;
}
ThreadLocalOwnedPointer<BooleanWithStack>&
GetBaseSyncPrimitivesDisallowedTls() {
static NoDestructor<ThreadLocalOwnedPointer<BooleanWithStack>> instance;
auto& tls = *instance;
if (!tls.Get())
tls.Set(std::make_unique<BooleanWithStack>());
return tls;
}
ThreadLocalOwnedPointer<BooleanWithStack>& GetCPUIntensiveWorkDisallowedTls() {
static NoDestructor<ThreadLocalOwnedPointer<BooleanWithStack>> instance;
auto& tls = *instance;
if (!tls.Get())
tls.Set(std::make_unique<BooleanWithStack>());
return tls;
}
} // namespace
namespace internal {
void AssertBlockingAllowed() {
DCHECK(!*GetBlockingDisallowedTls())
<< "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 " << *GetBlockingDisallowedTls();
}
} // namespace internal
void DisallowBlocking() {
GetBlockingDisallowedTls().Set(std::make_unique<BooleanWithStack>(true));
}
ScopedDisallowBlocking::ScopedDisallowBlocking()
: was_disallowed_(GetBlockingDisallowedTls().Set(
std::make_unique<BooleanWithStack>(true))) {}
ScopedDisallowBlocking::~ScopedDisallowBlocking() {
DCHECK(*GetBlockingDisallowedTls())
<< "~ScopedDisallowBlocking() running while surprisingly already no "
"longer disallowed.\n"
<< "g_blocking_disallowed " << *GetBlockingDisallowedTls();
GetBlockingDisallowedTls().Set(std::move(was_disallowed_));
}
void DisallowBaseSyncPrimitives() {
GetBaseSyncPrimitivesDisallowedTls().Set(
std::make_unique<BooleanWithStack>(true));
}
ScopedAllowBaseSyncPrimitives::ScopedAllowBaseSyncPrimitives()
: was_disallowed_(GetBaseSyncPrimitivesDisallowedTls().Set(
std::make_unique<BooleanWithStack>(false))) {
DCHECK(!*GetBlockingDisallowedTls())
<< "To allow //base sync primitives in a scope where blocking is "
"disallowed use ScopedAllowBaseSyncPrimitivesOutsideBlockingScope.\n"
<< "g_blocking_disallowed " << *GetBlockingDisallowedTls();
}
ScopedAllowBaseSyncPrimitives::~ScopedAllowBaseSyncPrimitives() {
DCHECK(!*GetBaseSyncPrimitivesDisallowedTls());
GetBaseSyncPrimitivesDisallowedTls().Set(std::move(was_disallowed_));
}
ScopedAllowBaseSyncPrimitivesForTesting::
ScopedAllowBaseSyncPrimitivesForTesting()
: was_disallowed_(GetBaseSyncPrimitivesDisallowedTls().Set(
std::make_unique<BooleanWithStack>(false))) {}
ScopedAllowBaseSyncPrimitivesForTesting::
~ScopedAllowBaseSyncPrimitivesForTesting() {
DCHECK(!*GetBaseSyncPrimitivesDisallowedTls());
GetBaseSyncPrimitivesDisallowedTls().Set(std::move(was_disallowed_));
}
ScopedAllowUnresponsiveTasksForTesting::ScopedAllowUnresponsiveTasksForTesting()
: was_disallowed_base_sync_(GetBaseSyncPrimitivesDisallowedTls().Set(
std::make_unique<BooleanWithStack>(false))),
was_disallowed_blocking_(GetBlockingDisallowedTls().Set(
std::make_unique<BooleanWithStack>(false))),
was_disallowed_cpu_(GetCPUIntensiveWorkDisallowedTls().Set(
std::make_unique<BooleanWithStack>(false))) {}
ScopedAllowUnresponsiveTasksForTesting::
~ScopedAllowUnresponsiveTasksForTesting() {
DCHECK(!*GetBaseSyncPrimitivesDisallowedTls());
DCHECK(!*GetBlockingDisallowedTls());
DCHECK(!*GetCPUIntensiveWorkDisallowedTls());
GetBaseSyncPrimitivesDisallowedTls().Set(
std::move(was_disallowed_base_sync_));
GetBlockingDisallowedTls().Set(std::move(was_disallowed_blocking_));
GetCPUIntensiveWorkDisallowedTls().Set(std::move(was_disallowed_cpu_));
}
namespace internal {
void AssertBaseSyncPrimitivesAllowed() {
DCHECK(!*GetBaseSyncPrimitivesDisallowedTls())
<< "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 "
<< *GetBaseSyncPrimitivesDisallowedTls()
<< "It can be useful to know that g_blocking_disallowed is "
<< *GetBlockingDisallowedTls();
}
void ResetThreadRestrictionsForTesting() {
GetBlockingDisallowedTls().Set(std::make_unique<BooleanWithStack>(false));
GetSingletonDisallowedTls().Set(std::make_unique<BooleanWithStack>(false));
GetBaseSyncPrimitivesDisallowedTls().Set(
std::make_unique<BooleanWithStack>(false));
GetCPUIntensiveWorkDisallowedTls().Set(
std::make_unique<BooleanWithStack>(false));
}
} // namespace internal
void AssertLongCPUWorkAllowed() {
DCHECK(!*GetCPUIntensiveWorkDisallowedTls())
<< "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 "
<< *GetCPUIntensiveWorkDisallowedTls();
}
void DisallowUnresponsiveTasks() {
DisallowBlocking();
DisallowBaseSyncPrimitives();
GetCPUIntensiveWorkDisallowedTls().Set(
std::make_unique<BooleanWithStack>(true));
}
// static
bool ThreadRestrictions::SetIOAllowed(bool allowed) {
const bool previously_allowed = !*GetBlockingDisallowedTls().Set(
std::make_unique<BooleanWithStack>(!allowed));
return previously_allowed;
}
// static
bool ThreadRestrictions::SetSingletonAllowed(bool allowed) {
const bool previously_allowed = !*GetSingletonDisallowedTls().Set(
std::make_unique<BooleanWithStack>(!allowed));
return previously_allowed;
}
// static
void ThreadRestrictions::AssertSingletonAllowed() {
DCHECK(!*GetSingletonDisallowedTls())
<< "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 " << *GetSingletonDisallowedTls();
}
// static
void ThreadRestrictions::DisallowWaiting() {
DisallowBaseSyncPrimitives();
}
bool ThreadRestrictions::SetWaitAllowed(bool allowed) {
const bool previously_allowed = !*GetBaseSyncPrimitivesDisallowedTls().Set(
std::make_unique<BooleanWithStack>(!allowed));
return previously_allowed;
}
} // namespace base
#endif // DCHECK_IS_ON()
namespace base {
ScopedAllowBlocking::ScopedAllowBlocking(const Location& from_here)
#if DCHECK_IS_ON()
: was_disallowed_(GetBlockingDisallowedTls().Set(
std::make_unique<BooleanWithStack>(false)))
#endif
{
TRACE_EVENT_BEGIN(
"base", "ScopedAllowBlocking", [&](perfetto::EventContext ctx) {
ctx.event()->set_source_location_iid(
base::trace_event::InternedSourceLocation::Get(
&ctx, base::trace_event::TraceSourceLocation(from_here)));
});
}
ScopedAllowBlocking::~ScopedAllowBlocking() {
TRACE_EVENT_END0("base", "ScopedAllowBlocking");
#if DCHECK_IS_ON()
DCHECK(!*GetBlockingDisallowedTls());
GetBlockingDisallowedTls().Set(std::move(was_disallowed_));
#endif
}
ScopedAllowBaseSyncPrimitivesOutsideBlockingScope::
ScopedAllowBaseSyncPrimitivesOutsideBlockingScope(const Location& from_here)
#if DCHECK_IS_ON()
: was_disallowed_(GetBaseSyncPrimitivesDisallowedTls().Set(
std::make_unique<BooleanWithStack>(false)))
#endif
{
TRACE_EVENT_BEGIN(
"base", "ScopedAllowBaseSyncPrimitivesOutsideBlockingScope",
[&](perfetto::EventContext ctx) {
ctx.event()->set_source_location_iid(
base::trace_event::InternedSourceLocation::Get(
&ctx, base::trace_event::TraceSourceLocation(from_here)));
});
// Since this object is used to indicate that sync primitives will be used to
// wait for an event ignore the current operation for hang watching purposes
// since the wait time duration is unknown.
base::HangWatcher::InvalidateActiveExpectations();
}
ScopedAllowBaseSyncPrimitivesOutsideBlockingScope::
~ScopedAllowBaseSyncPrimitivesOutsideBlockingScope() {
TRACE_EVENT_END0("base", "ScopedAllowBaseSyncPrimitivesOutsideBlockingScope");
#if DCHECK_IS_ON()
DCHECK(!*GetBaseSyncPrimitivesDisallowedTls());
GetBaseSyncPrimitivesDisallowedTls().Set(std::move(was_disallowed_));
#endif
}
ThreadRestrictions::ScopedAllowIO::ScopedAllowIO(const Location& from_here)
#if DCHECK_IS_ON()
: was_disallowed_(GetBlockingDisallowedTls().Set(
std::make_unique<BooleanWithStack>(false)))
#endif
{
TRACE_EVENT_BEGIN("base", "ScopedAllowIO", [&](perfetto::EventContext ctx) {
ctx.event()->set_source_location_iid(
base::trace_event::InternedSourceLocation::Get(
&ctx, base::trace_event::TraceSourceLocation(from_here)));
});
}
ThreadRestrictions::ScopedAllowIO::~ScopedAllowIO() {
TRACE_EVENT_END0("base", "ScopedAllowIO");
#if DCHECK_IS_ON()
GetBlockingDisallowedTls().Set(std::move(was_disallowed_));
#endif
}
} // namespace base