Commit 5db1ab8d authored by Gabriel Charette's avatar Gabriel Charette Committed by Commit Bot

Add a trace event for the duration of ScopedBlockingCalls

This helps identify sections of a trace event that were blocked on external
dependencies and shed some light on the "descheduled" portion of an event.

This CL is non-trivial for 3 reasons:

 1) This new TRACE_EVENT kicks off tracing earlier than before in some
    configurations. This means that main() of basic utilities all of a sudden
    needed AtExitManagers (first patch set of this CL). But that then spread out
    to many main()'s and was painful. Since the Singleton is only required in
    tracing to know whether it's enabled : this CL specializes
    Singleton/TraceEventETWExport to avoid instantiating the instance only to
    notice it's not enabled (guaranteed default state).

 2) We do not want tracing events while a thread is idle (i.e. when it's
    sleeping on its own WaitableEvent/ConditionVariable while waiting for more
    tasks). Ideally we simply wouldn't record the trace event when it's not
    nested within another event but tracing doesn't have a notion of "current
    event depth". As such this CL opts for a declarative model where owners of
    the few WaitableEvents/ConditionVariables used to sleep-while-idle can flag
    those as irrelevant for tracing.

 3) Possibility of reentrancy if the trace event macros perform a blocking
    call themselves. Added DCHECKs to confirm this doesn't occur (was
    initial suspected cause of CrOS+amd64 only failures but it kept
    failing despite that and this feature had to be disabled there..)

Bonus:
  * Singleton::GetIfExists() avoids instantiating TraceEventETWExport
    when switches::kTraceExportEventsToETW isn't present.
  * The declarative model brought forth by (2) also enables not instantiating
    debug::ScopedEventWaitActivity objects while a thread is merely sleeping.
    This will avoid polluting crash metadata with wait-acitvity for sleeping
    threads.
  * In a follow-up CL we will be able to remove
    base::internal::ScopedClearBlockingObserverForTesting which was previously
    necessary specifically to avoid a situation in TaskScheduler unit tests
    where WaitableEvents used for the test logic would instantiate
    ScopedBlockingCalls and interfere with the very logic under test.
    (not done in this one as it's large enough on its own)

Bug: 899897, 902514
Change-Id: I93b63e83ac6bd9e5e9d4e29a9b86c62c73738835
Reviewed-on: https://chromium-review.googlesource.com/c/1305891
Commit-Queue: Gabriel Charette <gab@chromium.org>
Reviewed-by: default avatardanakj <danakj@chromium.org>
Reviewed-by: default avatarFrançois Doray <fdoray@chromium.org>
Reviewed-by: default avatarPeter Kasting <pkasting@chromium.org>
Reviewed-by: default avatarCharlie Harrison <csharrison@chromium.org>
Reviewed-by: default avatarAvi Drissman <avi@chromium.org>
Reviewed-by: default avatarMisha Efimov <mef@chromium.org>
Cr-Commit-Position: refs/heads/master@{#605966}
parent c0061058
...@@ -222,11 +222,14 @@ class Singleton { ...@@ -222,11 +222,14 @@ class Singleton {
// Classes using the Singleton<T> pattern should declare a GetInstance() // Classes using the Singleton<T> pattern should declare a GetInstance()
// method and call Singleton::get() from within that. // method and call Singleton::get() from within that.
friend Type* Type::GetInstance(); friend Type* Type::GetInstance();
// Classes may also declare a GetInstanceIfExists() method to invoke
// Singleton::GetIfExists().
friend Type* Type::GetInstanceIfExists();
// This class is safe to be constructed and copy-constructed since it has no // This class is safe to be constructed and copy-constructed since it has no
// member. // member.
// Return a pointer to the one true instance of the class. // Returns a pointer to the one true instance of the class.
static Type* get() { static Type* get() {
#if DCHECK_IS_ON() #if DCHECK_IS_ON()
if (!Traits::kAllowedToAccessOnNonjoinableThread) if (!Traits::kAllowedToAccessOnNonjoinableThread)
...@@ -238,6 +241,22 @@ class Singleton { ...@@ -238,6 +241,22 @@ class Singleton {
Traits::kRegisterAtExit ? OnExit : nullptr, nullptr); Traits::kRegisterAtExit ? OnExit : nullptr, nullptr);
} }
// Returns the same result as get() if the instance exists but doesn't
// construct it (and returns null) if it doesn't.
static Type* GetIfExists() {
#if DCHECK_IS_ON()
if (!Traits::kAllowedToAccessOnNonjoinableThread)
ThreadRestrictions::AssertSingletonAllowed();
#endif
if (!subtle::NoBarrier_Load(&instance_))
return nullptr;
// Need to invoke get() nonetheless as some Traits return null after
// destruction (even though |instance_| still holds garbage).
return get();
}
// Internal method used as an adaptor for GetOrCreateLazyPointer(). Do not use // Internal method used as an adaptor for GetOrCreateLazyPointer(). Do not use
// outside of that use case. // outside of that use case.
static Type* CreatorFunc(void* /* creator_arg*/) { return Traits::New(); } static Type* CreatorFunc(void* /* creator_arg*/) { return Traits::New(); }
......
...@@ -22,7 +22,9 @@ namespace base { ...@@ -22,7 +22,9 @@ namespace base {
MessagePumpDefault::MessagePumpDefault() MessagePumpDefault::MessagePumpDefault()
: keep_running_(true), : keep_running_(true),
event_(WaitableEvent::ResetPolicy::AUTOMATIC, event_(WaitableEvent::ResetPolicy::AUTOMATIC,
WaitableEvent::InitialState::NOT_SIGNALED) {} WaitableEvent::InitialState::NOT_SIGNALED) {
event_.declare_only_used_while_idle();
}
MessagePumpDefault::~MessagePumpDefault() = default; MessagePumpDefault::~MessagePumpDefault() = default;
......
...@@ -102,6 +102,15 @@ class BASE_EXPORT ConditionVariable { ...@@ -102,6 +102,15 @@ class BASE_EXPORT ConditionVariable {
// Signal() revives one waiting thread. // Signal() revives one waiting thread.
void Signal(); void Signal();
// Declares that this ConditionVariable will only ever be used by a thread
// that is idle at the bottom of its stack and waiting for work (in
// particular, it is not synchronously waiting on this ConditionVariable
// before resuming ongoing work). This is useful to avoid telling
// base-internals that this thread is "blocked" when it's merely idle and
// ready to do work. As such, this is only expected to be used by thread and
// thread pool impls.
void declare_only_used_while_idle() { waiting_is_blocking_ = false; }
private: private:
#if defined(OS_WIN) #if defined(OS_WIN)
...@@ -116,6 +125,11 @@ class BASE_EXPORT ConditionVariable { ...@@ -116,6 +125,11 @@ class BASE_EXPORT ConditionVariable {
base::Lock* const user_lock_; // Needed to adjust shadow lock state on wait. base::Lock* const user_lock_; // Needed to adjust shadow lock state on wait.
#endif #endif
// Whether a thread invoking Wait() on this ConditionalVariable should be
// considered blocked as opposed to idle (and potentially replaced if part of
// a pool).
bool waiting_is_blocking_ = true;
DISALLOW_COPY_AND_ASSIGN(ConditionVariable); DISALLOW_COPY_AND_ASSIGN(ConditionVariable);
}; };
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <stdint.h> #include <stdint.h>
#include <sys/time.h> #include <sys/time.h>
#include "base/optional.h"
#include "base/synchronization/lock.h" #include "base/synchronization/lock.h"
#include "base/threading/scoped_blocking_call.h" #include "base/threading/scoped_blocking_call.h"
#include "base/threading/thread_restrictions.h" #include "base/threading/thread_restrictions.h"
...@@ -63,8 +64,11 @@ ConditionVariable::~ConditionVariable() { ...@@ -63,8 +64,11 @@ ConditionVariable::~ConditionVariable() {
} }
void ConditionVariable::Wait() { void ConditionVariable::Wait() {
internal::ScopedBlockingCallWithBaseSyncPrimitives scoped_blocking_call( Optional<internal::ScopedBlockingCallWithBaseSyncPrimitives>
BlockingType::MAY_BLOCK); scoped_blocking_call;
if (waiting_is_blocking_)
scoped_blocking_call.emplace(BlockingType::MAY_BLOCK);
#if DCHECK_IS_ON() #if DCHECK_IS_ON()
user_lock_->CheckHeldAndUnmark(); user_lock_->CheckHeldAndUnmark();
#endif #endif
...@@ -76,8 +80,11 @@ void ConditionVariable::Wait() { ...@@ -76,8 +80,11 @@ void ConditionVariable::Wait() {
} }
void ConditionVariable::TimedWait(const TimeDelta& max_time) { void ConditionVariable::TimedWait(const TimeDelta& max_time) {
internal::ScopedBlockingCallWithBaseSyncPrimitives scoped_blocking_call( Optional<internal::ScopedBlockingCallWithBaseSyncPrimitives>
BlockingType::MAY_BLOCK); scoped_blocking_call;
if (waiting_is_blocking_)
scoped_blocking_call.emplace(BlockingType::MAY_BLOCK);
int64_t usecs = max_time.InMicroseconds(); int64_t usecs = max_time.InMicroseconds();
struct timespec relative_time; struct timespec relative_time;
relative_time.tv_sec = usecs / Time::kMicrosecondsPerSecond; relative_time.tv_sec = usecs / Time::kMicrosecondsPerSecond;
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "base/synchronization/condition_variable.h" #include "base/synchronization/condition_variable.h"
#include "base/optional.h"
#include "base/synchronization/lock.h" #include "base/synchronization/lock.h"
#include "base/threading/scoped_blocking_call.h" #include "base/threading/scoped_blocking_call.h"
#include "base/threading/thread_restrictions.h" #include "base/threading/thread_restrictions.h"
...@@ -30,8 +31,11 @@ void ConditionVariable::Wait() { ...@@ -30,8 +31,11 @@ void ConditionVariable::Wait() {
} }
void ConditionVariable::TimedWait(const TimeDelta& max_time) { void ConditionVariable::TimedWait(const TimeDelta& max_time) {
internal::ScopedBlockingCallWithBaseSyncPrimitives scoped_blocking_call( Optional<internal::ScopedBlockingCallWithBaseSyncPrimitives>
BlockingType::MAY_BLOCK); scoped_blocking_call;
if (waiting_is_blocking_)
scoped_blocking_call.emplace(BlockingType::MAY_BLOCK);
DWORD timeout = static_cast<DWORD>(max_time.InMilliseconds()); DWORD timeout = static_cast<DWORD>(max_time.InMilliseconds());
#if DCHECK_IS_ON() #if DCHECK_IS_ON()
......
...@@ -113,6 +113,14 @@ class BASE_EXPORT WaitableEvent { ...@@ -113,6 +113,14 @@ class BASE_EXPORT WaitableEvent {
HANDLE handle() const { return handle_.Get(); } HANDLE handle() const { return handle_.Get(); }
#endif #endif
// Declares that this WaitableEvent will only ever be used by a thread that is
// idle at the bottom of its stack and waiting for work (in particular, it is
// not synchronously waiting on this event before resuming ongoing work). This
// is useful to avoid telling base-internals that this thread is "blocked"
// when it's merely idle and ready to do work. As such, this is only expected
// to be used by thread and thread pool impls.
void declare_only_used_while_idle() { waiting_is_blocking_ = false; }
// Wait, synchronously, on multiple events. // Wait, synchronously, on multiple events.
// waitables: an array of WaitableEvent pointers // waitables: an array of WaitableEvent pointers
// count: the number of elements in @waitables // count: the number of elements in @waitables
...@@ -276,6 +284,10 @@ class BASE_EXPORT WaitableEvent { ...@@ -276,6 +284,10 @@ class BASE_EXPORT WaitableEvent {
scoped_refptr<WaitableEventKernel> kernel_; scoped_refptr<WaitableEventKernel> kernel_;
#endif #endif
// Whether a thread invoking Wait() on this WaitableEvent should be considered
// blocked as opposed to idle (and potentially replaced if part of a pool).
bool waiting_is_blocking_ = true;
DISALLOW_COPY_AND_ASSIGN(WaitableEvent); DISALLOW_COPY_AND_ASSIGN(WaitableEvent);
}; };
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "base/debug/activity_tracker.h" #include "base/debug/activity_tracker.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/optional.h"
#include "base/synchronization/condition_variable.h" #include "base/synchronization/condition_variable.h"
#include "base/synchronization/lock.h" #include "base/synchronization/lock.h"
#include "base/synchronization/waitable_event.h" #include "base/synchronization/waitable_event.h"
...@@ -162,10 +163,13 @@ bool WaitableEvent::TimedWait(const TimeDelta& wait_delta) { ...@@ -162,10 +163,13 @@ bool WaitableEvent::TimedWait(const TimeDelta& wait_delta) {
} }
bool WaitableEvent::TimedWaitUntil(const TimeTicks& end_time) { bool WaitableEvent::TimedWaitUntil(const TimeTicks& end_time) {
internal::ScopedBlockingCallWithBaseSyncPrimitives scoped_blocking_call( Optional<internal::ScopedBlockingCallWithBaseSyncPrimitives>
BlockingType::MAY_BLOCK); scoped_blocking_call;
if (waiting_is_blocking_)
scoped_blocking_call.emplace(BlockingType::MAY_BLOCK);
// Record the event that this thread is blocking upon (for hang diagnosis). // Record the event that this thread is blocking upon (for hang diagnosis).
base::debug::ScopedEventWaitActivity event_activity(this); debug::ScopedEventWaitActivity event_activity(this);
const bool finite_time = !end_time.is_max(); const bool finite_time = !end_time.is_max();
...@@ -241,7 +245,7 @@ size_t WaitableEvent::WaitMany(WaitableEvent** raw_waitables, ...@@ -241,7 +245,7 @@ size_t WaitableEvent::WaitMany(WaitableEvent** raw_waitables,
internal::ScopedBlockingCallWithBaseSyncPrimitives scoped_blocking_call( internal::ScopedBlockingCallWithBaseSyncPrimitives scoped_blocking_call(
BlockingType::MAY_BLOCK); BlockingType::MAY_BLOCK);
// Record an event (the first) that this thread is blocking upon. // Record an event (the first) that this thread is blocking upon.
base::debug::ScopedEventWaitActivity event_activity(raw_waitables[0]); debug::ScopedEventWaitActivity event_activity(raw_waitables[0]);
// We need to acquire the locks in a globally consistent order. Thus we sort // We need to acquire the locks in a globally consistent order. Thus we sort
// the array of waitables by address. We actually sort a pairs so that we can // the array of waitables by address. We actually sort a pairs so that we can
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "base/debug/activity_tracker.h" #include "base/debug/activity_tracker.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/numerics/safe_conversions.h" #include "base/numerics/safe_conversions.h"
#include "base/optional.h"
#include "base/threading/scoped_blocking_call.h" #include "base/threading/scoped_blocking_call.h"
#include "base/threading/thread_restrictions.h" #include "base/threading/thread_restrictions.h"
#include "base/time/time.h" #include "base/time/time.h"
...@@ -53,10 +54,16 @@ bool WaitableEvent::IsSignaled() { ...@@ -53,10 +54,16 @@ bool WaitableEvent::IsSignaled() {
} }
void WaitableEvent::Wait() { void WaitableEvent::Wait() {
internal::ScopedBlockingCallWithBaseSyncPrimitives scoped_blocking_call( // Record the event that this thread is blocking upon (for hang diagnosis) and
BlockingType::MAY_BLOCK); // consider blocked for scheduling purposes. Ignore this for non-blocking
// Record the event that this thread is blocking upon (for hang diagnosis). // WaitableEvents.
base::debug::ScopedEventWaitActivity event_activity(this); Optional<debug::ScopedEventWaitActivity> event_activity;
Optional<internal::ScopedBlockingCallWithBaseSyncPrimitives>
scoped_blocking_call;
if (waiting_is_blocking_) {
event_activity.emplace(this);
scoped_blocking_call.emplace(BlockingType::MAY_BLOCK);
}
DWORD result = WaitForSingleObject(handle_.Get(), INFINITE); DWORD result = WaitForSingleObject(handle_.Get(), INFINITE);
// It is most unexpected that this should ever fail. Help consumers learn // It is most unexpected that this should ever fail. Help consumers learn
...@@ -69,9 +76,6 @@ namespace { ...@@ -69,9 +76,6 @@ namespace {
// Helper function called from TimedWait and TimedWaitUntil. // Helper function called from TimedWait and TimedWaitUntil.
bool WaitUntil(HANDLE handle, const TimeTicks& now, const TimeTicks& end_time) { bool WaitUntil(HANDLE handle, const TimeTicks& now, const TimeTicks& end_time) {
internal::ScopedBlockingCallWithBaseSyncPrimitives scoped_blocking_call(
BlockingType::MAY_BLOCK);
TimeDelta delta = end_time - now; TimeDelta delta = end_time - now;
DCHECK_GT(delta, TimeDelta()); DCHECK_GT(delta, TimeDelta());
...@@ -108,9 +112,16 @@ bool WaitableEvent::TimedWait(const TimeDelta& wait_delta) { ...@@ -108,9 +112,16 @@ bool WaitableEvent::TimedWait(const TimeDelta& wait_delta) {
if (wait_delta.is_zero()) if (wait_delta.is_zero())
return IsSignaled(); return IsSignaled();
internal::AssertBaseSyncPrimitivesAllowed(); // Record the event that this thread is blocking upon (for hang diagnosis) and
// Record the event that this thread is blocking upon (for hang diagnosis). // consider blocked for scheduling purposes. Ignore this for non-blocking
base::debug::ScopedEventWaitActivity event_activity(this); // WaitableEvents.
Optional<debug::ScopedEventWaitActivity> event_activity;
Optional<internal::ScopedBlockingCallWithBaseSyncPrimitives>
scoped_blocking_call;
if (waiting_is_blocking_) {
event_activity.emplace(this);
scoped_blocking_call.emplace(BlockingType::MAY_BLOCK);
}
TimeTicks now(TimeTicks::Now()); TimeTicks now(TimeTicks::Now());
// TimeTicks takes care of overflow including the cases when wait_delta // TimeTicks takes care of overflow including the cases when wait_delta
...@@ -122,9 +133,16 @@ bool WaitableEvent::TimedWaitUntil(const TimeTicks& end_time) { ...@@ -122,9 +133,16 @@ bool WaitableEvent::TimedWaitUntil(const TimeTicks& end_time) {
if (end_time.is_null()) if (end_time.is_null())
return IsSignaled(); return IsSignaled();
internal::AssertBaseSyncPrimitivesAllowed(); // Record the event that this thread is blocking upon (for hang diagnosis) and
// Record the event that this thread is blocking upon (for hang diagnosis). // consider blocked for scheduling purposes. Ignore this for non-blocking
base::debug::ScopedEventWaitActivity event_activity(this); // WaitableEvents.
Optional<debug::ScopedEventWaitActivity> event_activity;
Optional<internal::ScopedBlockingCallWithBaseSyncPrimitives>
scoped_blocking_call;
if (waiting_is_blocking_) {
event_activity.emplace(this);
scoped_blocking_call.emplace(BlockingType::MAY_BLOCK);
}
TimeTicks now(TimeTicks::Now()); TimeTicks now(TimeTicks::Now());
if (end_time <= now) if (end_time <= now)
...@@ -136,11 +154,10 @@ bool WaitableEvent::TimedWaitUntil(const TimeTicks& end_time) { ...@@ -136,11 +154,10 @@ bool WaitableEvent::TimedWaitUntil(const TimeTicks& end_time) {
// static // static
size_t WaitableEvent::WaitMany(WaitableEvent** events, size_t count) { size_t WaitableEvent::WaitMany(WaitableEvent** events, size_t count) {
DCHECK(count) << "Cannot wait on no events"; DCHECK(count) << "Cannot wait on no events";
internal::ScopedBlockingCallWithBaseSyncPrimitives scoped_blocking_call( internal::ScopedBlockingCallWithBaseSyncPrimitives scoped_blocking_call(
BlockingType::MAY_BLOCK); BlockingType::MAY_BLOCK);
// Record an event (the first) that this thread is blocking upon. // Record an event (the first) that this thread is blocking upon.
base::debug::ScopedEventWaitActivity event_activity(events[0]); debug::ScopedEventWaitActivity event_activity(events[0]);
HANDLE handles[MAXIMUM_WAIT_OBJECTS]; HANDLE handles[MAXIMUM_WAIT_OBJECTS];
CHECK_LE(count, static_cast<size_t>(MAXIMUM_WAIT_OBJECTS)) CHECK_LE(count, static_cast<size_t>(MAXIMUM_WAIT_OBJECTS))
......
...@@ -58,6 +58,7 @@ SchedulerWorker::SchedulerWorker( ...@@ -58,6 +58,7 @@ SchedulerWorker::SchedulerWorker(
DCHECK(task_tracker_); DCHECK(task_tracker_);
DCHECK(CanUseBackgroundPriorityForSchedulerWorker() || DCHECK(CanUseBackgroundPriorityForSchedulerWorker() ||
priority_hint_ != ThreadPriority::BACKGROUND); priority_hint_ != ThreadPriority::BACKGROUND);
wake_up_event_.declare_only_used_while_idle();
} }
bool SchedulerWorker::Start( bool SchedulerWorker::Start(
......
...@@ -8,11 +8,24 @@ ...@@ -8,11 +8,24 @@
#include "base/scoped_clear_last_error.h" #include "base/scoped_clear_last_error.h"
#include "base/threading/thread_local.h" #include "base/threading/thread_local.h"
#include "base/threading/thread_restrictions.h" #include "base/threading/thread_restrictions.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
namespace base { namespace base {
namespace { namespace {
#if defined(OS_CHROMEOS) && defined(ARCH_CPU_X86_64)
// For a mysterious reason, enabling tracing in ScopedBlockingCall makes
// telemetry_unittests's
// telemetry.core.tracing_controller_unittest.
// StartupTracingTest.testCloseBrowserBeforeTracingIsStopped
// timeout only on chromeos-amd64-generic-rel : https://crbug.com/902514.
const bool kTraceScopedBlockingCall = false;
#else
const bool kTraceScopedBlockingCall = true;
#endif
LazyInstance<ThreadLocalPointer<internal::BlockingObserver>>::Leaky LazyInstance<ThreadLocalPointer<internal::BlockingObserver>>::Leaky
tls_blocking_observer = LAZY_INSTANCE_INITIALIZER; tls_blocking_observer = LAZY_INSTANCE_INITIALIZER;
...@@ -20,6 +33,14 @@ LazyInstance<ThreadLocalPointer<internal::BlockingObserver>>::Leaky ...@@ -20,6 +33,14 @@ LazyInstance<ThreadLocalPointer<internal::BlockingObserver>>::Leaky
LazyInstance<ThreadLocalPointer<internal::UncheckedScopedBlockingCall>>::Leaky LazyInstance<ThreadLocalPointer<internal::UncheckedScopedBlockingCall>>::Leaky
tls_last_scoped_blocking_call = LAZY_INSTANCE_INITIALIZER; tls_last_scoped_blocking_call = LAZY_INSTANCE_INITIALIZER;
#if DCHECK_IS_ON()
// Used to verify that the trace events used in the constructor do not result in
// instantiating a ScopedBlockingCall themselves (which would cause an infinite
// reentrancy loop).
LazyInstance<ThreadLocalBoolean>::Leaky tls_construction_in_progress =
LAZY_INSTANCE_INITIALIZER;
#endif
} // namespace } // namespace
namespace internal { namespace internal {
...@@ -57,7 +78,25 @@ UncheckedScopedBlockingCall::~UncheckedScopedBlockingCall() { ...@@ -57,7 +78,25 @@ UncheckedScopedBlockingCall::~UncheckedScopedBlockingCall() {
ScopedBlockingCall::ScopedBlockingCall(BlockingType blocking_type) ScopedBlockingCall::ScopedBlockingCall(BlockingType blocking_type)
: UncheckedScopedBlockingCall(blocking_type) { : UncheckedScopedBlockingCall(blocking_type) {
#if DCHECK_IS_ON()
DCHECK(!tls_construction_in_progress.Get().Get());
tls_construction_in_progress.Get().Set(true);
#endif
internal::AssertBlockingAllowed(); internal::AssertBlockingAllowed();
if (kTraceScopedBlockingCall) {
TRACE_EVENT_BEGIN1("base", "ScopedBlockingCall", "blocking_type",
static_cast<int>(blocking_type));
}
#if DCHECK_IS_ON()
tls_construction_in_progress.Get().Set(false);
#endif
}
ScopedBlockingCall::~ScopedBlockingCall() {
if (kTraceScopedBlockingCall)
TRACE_EVENT_END0("base", "ScopedBlockingCall");
} }
namespace internal { namespace internal {
...@@ -65,7 +104,26 @@ namespace internal { ...@@ -65,7 +104,26 @@ namespace internal {
ScopedBlockingCallWithBaseSyncPrimitives:: ScopedBlockingCallWithBaseSyncPrimitives::
ScopedBlockingCallWithBaseSyncPrimitives(BlockingType blocking_type) ScopedBlockingCallWithBaseSyncPrimitives(BlockingType blocking_type)
: UncheckedScopedBlockingCall(blocking_type) { : UncheckedScopedBlockingCall(blocking_type) {
#if DCHECK_IS_ON()
DCHECK(!tls_construction_in_progress.Get().Get());
tls_construction_in_progress.Get().Set(true);
#endif
internal::AssertBaseSyncPrimitivesAllowed(); internal::AssertBaseSyncPrimitivesAllowed();
if (kTraceScopedBlockingCall) {
TRACE_EVENT_BEGIN1("base", "ScopedBlockingCallWithBaseSyncPrimitives",
"blocking_type", static_cast<int>(blocking_type));
}
#if DCHECK_IS_ON()
tls_construction_in_progress.Get().Set(false);
#endif
}
ScopedBlockingCallWithBaseSyncPrimitives::
~ScopedBlockingCallWithBaseSyncPrimitives() {
if (kTraceScopedBlockingCall)
TRACE_EVENT_END0("base", "ScopedBlockingCallWithBaseSyncPrimitives");
} }
void SetBlockingObserverForCurrentThread(BlockingObserver* blocking_observer) { void SetBlockingObserverForCurrentThread(BlockingObserver* blocking_observer) {
......
...@@ -110,7 +110,7 @@ class BASE_EXPORT ScopedBlockingCall ...@@ -110,7 +110,7 @@ class BASE_EXPORT ScopedBlockingCall
: public internal::UncheckedScopedBlockingCall { : public internal::UncheckedScopedBlockingCall {
public: public:
ScopedBlockingCall(BlockingType blocking_type); ScopedBlockingCall(BlockingType blocking_type);
~ScopedBlockingCall() = default; ~ScopedBlockingCall();
}; };
namespace internal { namespace internal {
...@@ -124,7 +124,7 @@ class BASE_EXPORT ScopedBlockingCallWithBaseSyncPrimitives ...@@ -124,7 +124,7 @@ class BASE_EXPORT ScopedBlockingCallWithBaseSyncPrimitives
: public UncheckedScopedBlockingCall { : public UncheckedScopedBlockingCall {
public: public:
ScopedBlockingCallWithBaseSyncPrimitives(BlockingType blocking_type); ScopedBlockingCallWithBaseSyncPrimitives(BlockingType blocking_type);
~ScopedBlockingCallWithBaseSyncPrimitives() = default; ~ScopedBlockingCallWithBaseSyncPrimitives();
}; };
// Interface for an observer to be informed when a thread enters or exits // Interface for an observer to be informed when a thread enters or exits
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include <stddef.h> #include <stddef.h>
#include "base/at_exit.h"
#include "base/command_line.h" #include "base/command_line.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/memory/singleton.h" #include "base/memory/singleton.h"
...@@ -32,6 +33,7 @@ ...@@ -32,6 +33,7 @@
#include "base/trace_event/etw_manifest/chrome_events_win.h" // NOLINT #include "base/trace_event/etw_manifest/chrome_events_win.h" // NOLINT
namespace { namespace {
// |kFilteredEventGroupNames| contains the event categories that can be // |kFilteredEventGroupNames| contains the event categories that can be
// exported individually. These categories can be enabled by passing the correct // exported individually. These categories can be enabled by passing the correct
// keyword when starting the trace. A keyword is a 64-bit flag and we attribute // keyword when starting the trace. A keyword is a 64-bit flag and we attribute
...@@ -134,12 +136,6 @@ TraceEventETWExport::~TraceEventETWExport() { ...@@ -134,12 +136,6 @@ TraceEventETWExport::~TraceEventETWExport() {
EventUnregisterChrome(); EventUnregisterChrome();
} }
// static
TraceEventETWExport* TraceEventETWExport::GetInstance() {
return Singleton<TraceEventETWExport,
StaticMemorySingletonTraits<TraceEventETWExport>>::get();
}
// static // static
void TraceEventETWExport::EnableETWExport() { void TraceEventETWExport::EnableETWExport() {
auto* instance = GetInstance(); auto* instance = GetInstance();
...@@ -167,7 +163,7 @@ void TraceEventETWExport::DisableETWExport() { ...@@ -167,7 +163,7 @@ void TraceEventETWExport::DisableETWExport() {
// static // static
bool TraceEventETWExport::IsETWExportEnabled() { bool TraceEventETWExport::IsETWExportEnabled() {
auto* instance = GetInstance(); auto* instance = GetInstanceIfExists();
return (instance && instance->etw_export_enabled_); return (instance && instance->etw_export_enabled_);
} }
...@@ -294,7 +290,7 @@ void TraceEventETWExport::AddCompleteEndEvent(const char* name) { ...@@ -294,7 +290,7 @@ void TraceEventETWExport::AddCompleteEndEvent(const char* name) {
bool TraceEventETWExport::IsCategoryGroupEnabled( bool TraceEventETWExport::IsCategoryGroupEnabled(
StringPiece category_group_name) { StringPiece category_group_name) {
DCHECK(!category_group_name.empty()); DCHECK(!category_group_name.empty());
auto* instance = GetInstance(); auto* instance = GetInstanceIfExists();
if (instance == nullptr) if (instance == nullptr)
return false; return false;
...@@ -377,5 +373,19 @@ void TraceEventETWExport::UpdateETWKeyword() { ...@@ -377,5 +373,19 @@ void TraceEventETWExport::UpdateETWKeyword() {
DCHECK(instance); DCHECK(instance);
instance->UpdateEnabledCategories(); instance->UpdateEnabledCategories();
} }
// static
TraceEventETWExport* TraceEventETWExport::GetInstance() {
return Singleton<TraceEventETWExport,
StaticMemorySingletonTraits<TraceEventETWExport>>::get();
}
// static
TraceEventETWExport* TraceEventETWExport::GetInstanceIfExists() {
return Singleton<
TraceEventETWExport,
StaticMemorySingletonTraits<TraceEventETWExport>>::GetIfExists();
}
} // namespace trace_event } // namespace trace_event
} // namespace base } // namespace base
...@@ -30,6 +30,12 @@ class BASE_EXPORT TraceEventETWExport { ...@@ -30,6 +30,12 @@ class BASE_EXPORT TraceEventETWExport {
// Note that this may return NULL post-AtExit processing. // Note that this may return NULL post-AtExit processing.
static TraceEventETWExport* GetInstance(); static TraceEventETWExport* GetInstance();
// Retrieves the singleton iff it was previously instantiated by a
// GetInstance() call. Avoids creating the instance only to check that it
// wasn't disabled. Note that, like GetInstance(), this may also return NULL
// post-AtExit processing.
static TraceEventETWExport* GetInstanceIfExists();
// Enables/disables exporting of events to ETW. If disabled, // Enables/disables exporting of events to ETW. If disabled,
// AddEvent and AddCustomEvent will simply return when called. // AddEvent and AddCustomEvent will simply return when called.
static void EnableETWExport(); static void EnableETWExport();
...@@ -61,6 +67,7 @@ class BASE_EXPORT TraceEventETWExport { ...@@ -61,6 +67,7 @@ class BASE_EXPORT TraceEventETWExport {
private: private:
// Ensure only the provider can construct us. // Ensure only the provider can construct us.
friend struct StaticMemorySingletonTraits<TraceEventETWExport>; friend struct StaticMemorySingletonTraits<TraceEventETWExport>;
// To have access to UpdateKeyword(). // To have access to UpdateKeyword().
class ETWKeywordUpdateThread; class ETWKeywordUpdateThread;
TraceEventETWExport(); TraceEventETWExport();
......
...@@ -18,7 +18,9 @@ SingleThreadTaskGraphRunner::SingleThreadTaskGraphRunner() ...@@ -18,7 +18,9 @@ SingleThreadTaskGraphRunner::SingleThreadTaskGraphRunner()
: lock_(), : lock_(),
has_ready_to_run_tasks_cv_(&lock_), has_ready_to_run_tasks_cv_(&lock_),
has_namespaces_with_finished_running_tasks_cv_(&lock_), has_namespaces_with_finished_running_tasks_cv_(&lock_),
shutdown_(false) {} shutdown_(false) {
has_ready_to_run_tasks_cv_.declare_only_used_while_idle();
}
SingleThreadTaskGraphRunner::~SingleThreadTaskGraphRunner() = default; SingleThreadTaskGraphRunner::~SingleThreadTaskGraphRunner() = default;
......
...@@ -29,7 +29,9 @@ struct NestedMessagePumpAndroid::RunState { ...@@ -29,7 +29,9 @@ struct NestedMessagePumpAndroid::RunState {
run_depth(run_depth), run_depth(run_depth),
should_quit(false), should_quit(false),
waitable_event(base::WaitableEvent::ResetPolicy::AUTOMATIC, waitable_event(base::WaitableEvent::ResetPolicy::AUTOMATIC,
base::WaitableEvent::InitialState::NOT_SIGNALED) {} base::WaitableEvent::InitialState::NOT_SIGNALED) {
waitable_event.declare_only_used_while_idle();
}
base::MessagePump::Delegate* delegate; base::MessagePump::Delegate* delegate;
......
...@@ -148,7 +148,12 @@ CategorizedWorkerPool::CategorizedWorkerPool() ...@@ -148,7 +148,12 @@ CategorizedWorkerPool::CategorizedWorkerPool()
has_ready_to_run_foreground_tasks_cv_(&lock_), has_ready_to_run_foreground_tasks_cv_(&lock_),
has_ready_to_run_background_tasks_cv_(&lock_), has_ready_to_run_background_tasks_cv_(&lock_),
has_namespaces_with_finished_running_tasks_cv_(&lock_), has_namespaces_with_finished_running_tasks_cv_(&lock_),
shutdown_(false) {} shutdown_(false) {
// Declare the two ConditionVariables which are used by worker threads to
// sleep-while-idle as such to avoid throwing off //base heuristics.
has_ready_to_run_foreground_tasks_cv_.declare_only_used_while_idle();
has_ready_to_run_background_tasks_cv_.declare_only_used_while_idle();
}
void CategorizedWorkerPool::Start(int num_threads) { void CategorizedWorkerPool::Start(int num_threads) {
DCHECK(threads_.empty()); DCHECK(threads_.empty());
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment