Commit b295f20a authored by OlivierLi's avatar OlivierLi Committed by Commit Bot

Create HangWatchScopeDisabled.

This scope object can be used to disable hang watching when it is known
an operation can run in unbounded time. HangWatchScope is renamed to
HangWatchScopeEnabled as part of this change also for symmetry.

This is done through the introduction of a new persistent flag
kHasActiveHangWatchScopeDisabled that can ensure that new
HangWatchScopeEnabled instances do not count towards hang watching.

Bug: 1034046
Change-Id: I9d8d8b2812a31fe29ba7980d64567798ada3b432
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2288195Reviewed-by: default avatarFrançois Doray <fdoray@chromium.org>
Commit-Queue: François Doray <fdoray@chromium.org>
Auto-Submit: Oliver Li <olivierli@chromium.org>
Cr-Commit-Position: refs/heads/master@{#796459}
parent 11c4d568
......@@ -184,12 +184,13 @@ void ThreadControllerWithMessagePumpImpl::InitializeThreadTaskRunnerHandle() {
power_monitor_.BindToCurrentThread();
}
void ThreadControllerWithMessagePumpImpl::MaybeStartHangWatchScope() {
void ThreadControllerWithMessagePumpImpl::MaybeStartHangWatchScopeEnabled() {
// Nested runloops are covered by the parent loop hang watch scope.
// TODO(crbug/1034046): Provide more granular scoping that reuses the parent
// scope deadline.
if (main_thread_only().runloop_count == 1 && base::HangWatcher::IsEnabled()) {
hang_watch_scope_.emplace(base::HangWatchScope::kDefaultHangWatchTime);
hang_watch_scope_.emplace(
base::HangWatchScopeEnabled::kDefaultHangWatchTime);
}
}
......@@ -225,7 +226,7 @@ ThreadControllerWithMessagePumpImpl::GetAssociatedThread() const {
}
void ThreadControllerWithMessagePumpImpl::BeforeDoInternalWork() {
MaybeStartHangWatchScope();
MaybeStartHangWatchScopeEnabled();
work_id_provider_->IncrementWorkId();
}
......@@ -244,7 +245,7 @@ void ThreadControllerWithMessagePumpImpl::BeforeWait() {
MessagePump::Delegate::NextWorkInfo
ThreadControllerWithMessagePumpImpl::DoWork() {
MaybeStartHangWatchScope();
MaybeStartHangWatchScopeEnabled();
work_deduplicator_.OnWorkStarted();
LazyNow continuation_lazy_now(time_source_);
......@@ -367,7 +368,7 @@ TimeDelta ThreadControllerWithMessagePumpImpl::DoWorkImpl(
bool ThreadControllerWithMessagePumpImpl::DoIdleWork() {
TRACE_EVENT0("sequence_manager", "SequenceManager::DoIdleWork");
MaybeStartHangWatchScope();
MaybeStartHangWatchScopeEnabled();
work_id_provider_->IncrementWorkId();
#if defined(OS_WIN)
......
......@@ -153,9 +153,9 @@ class BASE_EXPORT ThreadControllerWithMessagePumpImpl
return main_thread_only_;
}
// Instantiate a HangWatchScope to cover the current work if hang
// Instantiate a HangWatchScopeEnabled to cover the current work if hang
// watching is activated via finch and the current loop is not nested.
void MaybeStartHangWatchScope();
void MaybeStartHangWatchScopeEnabled();
// TODO(altimin): Merge with the one in SequenceManager.
scoped_refptr<AssociatedThreadId> associated_thread_;
......@@ -196,7 +196,7 @@ class BASE_EXPORT ThreadControllerWithMessagePumpImpl
// Reset at the start of each unit of work to cover the work itself and then
// transition to the next one.
base::Optional<HangWatchScope> hang_watch_scope_;
base::Optional<HangWatchScopeEnabled> hang_watch_scope_;
DISALLOW_COPY_AND_ASSIGN(ThreadControllerWithMessagePumpImpl);
};
......
......@@ -325,9 +325,10 @@ void WorkerThread::RunWorker() {
#if defined(OS_APPLE)
mac::ScopedNSAutoreleasePool autorelease_pool;
#endif
base::Optional<HangWatchScope> hang_watch_scope;
base::Optional<HangWatchScopeEnabled> hang_watch_scope;
if (watch_for_hangs)
hang_watch_scope.emplace(base::HangWatchScope::kDefaultHangWatchTime);
hang_watch_scope.emplace(
base::HangWatchScopeEnabled::kDefaultHangWatchTime);
UpdateThreadPriority(GetDesiredThreadPriority());
......
......@@ -36,7 +36,7 @@ constexpr base::FeatureParam<bool> kHangWatchIOThread{
constexpr base::FeatureParam<bool> kHangWatchThreadPool{
&kEnableHangWatcher, "hang_watch_threadpool", false};
constexpr base::TimeDelta HangWatchScope::kDefaultHangWatchTime =
constexpr base::TimeDelta HangWatchScopeEnabled::kDefaultHangWatchTime =
base::TimeDelta::FromSeconds(10);
namespace {
......@@ -56,44 +56,70 @@ constexpr const char* kThreadName = "HangWatcher";
// hangs but present unacceptable overhead.
const base::TimeDelta kMonitoringPeriod = base::TimeDelta::FromSeconds(10);
HangWatchScope::HangWatchScope(TimeDelta timeout) {
HangWatchScopeEnabled::HangWatchScopeEnabled(TimeDelta timeout) {
internal::HangWatchState* current_hang_watch_state =
internal::HangWatchState::GetHangWatchStateForCurrentThread()->Get();
DCHECK(timeout >= base::TimeDelta()) << "Negative timeouts are invalid.";
// TODO(crbug.com/1034046): Remove when all threads using HangWatchScope are
// monitored. Thread is not monitored, noop.
if (!current_hang_watch_state) {
// TODO(crbug.com/1034046): Remove when all threads using
// HangWatchScopeEnabled are monitored. Thread is not monitored, noop.
if (!current_hang_watch_state)
return;
}
DCHECK(current_hang_watch_state)
<< "A scope can only be used on a thread that "
<< "HangWatchScopeEnabled can only be used on a thread that "
"registered for hang watching with HangWatcher::RegisterThread.";
#if DCHECK_IS_ON()
previous_scope_ = current_hang_watch_state->GetCurrentHangWatchScope();
current_hang_watch_state->SetCurrentHangWatchScope(this);
previous_hang_watch_scope_enable_ =
current_hang_watch_state->GetCurrentHangWatchScopeEnabled();
current_hang_watch_state->SetCurrentHangWatchScopeEnabled(this);
#endif
uint64_t old_flags;
base::TimeTicks old_deadline;
std::tie(old_flags, old_deadline) =
current_hang_watch_state->GetFlagsAndDeadline();
const bool hangs_ignored_for_current_scope =
internal::HangWatchDeadline::IsFlagSet(
internal::HangWatchDeadline::Flag::
kIgnoreCurrentHangWatchScopeEnabled,
old_flags);
const bool has_active_hang_watch_disabled =
internal::HangWatchDeadline::IsFlagSet(
internal::HangWatchDeadline::Flag::kHasActiveHangWatchScopeDisabled,
old_flags);
// If the current HangWatchScopeEnabled is ignored but there are no active
// HangWatchScopeDisabled instances, temporarely reactivate hang watching for
// this newly created HangWatchScopeEnabled. On exiting hang watching is
// suspended again to return to the original state.
if (hangs_ignored_for_current_scope && !has_active_hang_watch_disabled) {
current_hang_watch_state->UnsetIgnoreCurrentHangWatchScopeEnabled();
set_hangs_ignored_on_exit_ = true;
}
// TODO(crbug.com/1034046): Check whether we are over deadline already for the
// previous scope here by issuing only one TimeTicks::Now() and resuing the
// value.
// previous HangWatchScopeEnabled here by issuing only one TimeTicks::Now()
// and resuing the value.
previous_deadline_ = current_hang_watch_state->GetDeadline();
previous_deadline_ = old_deadline;
TimeTicks deadline = TimeTicks::Now() + timeout;
current_hang_watch_state->SetDeadline(deadline);
current_hang_watch_state->IncrementNestingLevel();
}
HangWatchScope::~HangWatchScope() {
HangWatchScopeEnabled::~HangWatchScopeEnabled() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
internal::HangWatchState* current_hang_watch_state =
internal::HangWatchState::GetHangWatchStateForCurrentThread()->Get();
// TODO(crbug.com/1034046): Remove when all threads using HangWatchScope are
// monitored. Thread is not monitored, noop.
// TODO(crbug.com/1034046): Remove when all threads using
// HangWatchScopeEnabled are monitored. Thread is not monitored, noop.
if (!current_hang_watch_state) {
return;
}
......@@ -106,16 +132,62 @@ HangWatchScope::~HangWatchScope() {
#if DCHECK_IS_ON()
// Verify that no Scope was destructed out of order.
DCHECK_EQ(this, current_hang_watch_state->GetCurrentHangWatchScope());
current_hang_watch_state->SetCurrentHangWatchScope(previous_scope_);
DCHECK_EQ(this, current_hang_watch_state->GetCurrentHangWatchScopeEnabled());
current_hang_watch_state->SetCurrentHangWatchScopeEnabled(
previous_hang_watch_scope_enable_);
#endif
// Reset the deadline to the value it had before entering this scope.
// If a HangWatchScopeDisabled suspended hang watching during the
// lifetime of this or any nested HangWatchScopeEnabled it can now safely be
// reactivated by clearing the ignore bit since this is the outer-most scope.
// See HangWatchScopeDisabled class comments where this represents the
// destruction of |scope_1|.
if (current_hang_watch_state->nesting_level() == 1)
current_hang_watch_state->UnsetIgnoreCurrentHangWatchScopeEnabled();
// Return to ignoring hangs since this was the previous state before hang
// watching was temporarily enabled for this HangWatchScopeEnabled only in the
// constructor. See HangWatchScopeDisabled class comments where the next line
// of code is part of the destruction of |scope_4|.
else if (set_hangs_ignored_on_exit_)
current_hang_watch_state->SetIgnoreCurrentHangWatchScopeEnabled();
// Reset the deadline to the value it had before entering this
// HangWatchScopeEnabled.
current_hang_watch_state->SetDeadline(previous_deadline_);
// TODO(crbug.com/1034046): Log when a HangWatchScope exits after its deadline
// and that went undetected by the HangWatcher.
// TODO(crbug.com/1034046): Log when a HangWatchScopeEnabled exits after its
// deadline and that went undetected by the HangWatcher.
current_hang_watch_state->DecrementNestingLevel();
}
HangWatchScopeDisabled::HangWatchScopeDisabled() {
internal::HangWatchState* current_hang_watch_state =
internal::HangWatchState::GetHangWatchStateForCurrentThread()->Get();
if (!current_hang_watch_state) {
return;
}
current_hang_watch_state->SetIgnoreCurrentHangWatchScopeEnabled();
current_hang_watch_state->SetHasActiveHangWatchScopeDisabled();
}
HangWatchScopeDisabled::~HangWatchScopeDisabled() {
internal::HangWatchState* current_hang_watch_state =
internal::HangWatchState::GetHangWatchStateForCurrentThread()->Get();
if (!current_hang_watch_state)
return;
// If this instance outlived all HangWatchScopeEnabled instances watching
// needs to be reactivated.
if (current_hang_watch_state->nesting_level() == 0)
current_hang_watch_state->UnsetIgnoreCurrentHangWatchScopeEnabled();
current_hang_watch_state->UnsetHasActiveHangWatchScopeDisabled();
}
// static
void HangWatcher::InitializeOnMainThread() {
DCHECK(!g_use_hang_watcher);
DCHECK(!g_hang_watch_workers);
......@@ -207,15 +279,15 @@ void HangWatcher::Wait() {
if (!wait_was_normal) {
// If the time spent waiting was too high it might indicate the machine is
// very slow or that that it went to sleep. In any case we can't trust the
// hang watch scopes that are currently live. Update the ignore threshold
// to make sure they don't trigger a hang on subsequent monitors then keep
// waiting.
// HangWatchScopeEnableds that are currently live. Update the ignore
// threshold to make sure they don't trigger a hang on subsequent monitors
// then keep waiting.
base::AutoLock auto_lock(watch_state_lock_);
// Find the latest deadline among the live watch states. They might change
// atomically while iterating but that's fine because if they do that
// means the new HangWatchScope was constructed very soon after the
// means the new HangWatchScopeEnabled was constructed very soon after the
// abnormal sleep happened and might be affected by the root cause still.
// Ignoring it is cautious and harmless.
base::TimeTicks latest_deadline;
......@@ -303,7 +375,7 @@ HangWatcher::WatchStateSnapShot::WatchStateSnapShot(
// Only copy hung threads.
if (deadline <= snapshot_time) {
// Attempt to mark the thread as needing to stay within its current
// HangWatchScope until capture is complete.
// HangWatchScopeEnabled until capture is complete.
bool thread_marked = watch_state->SetShouldBlockOnHang(flags, deadline);
// If marking some threads already failed the snapshot won't be kept so
......@@ -397,8 +469,14 @@ void HangWatcher::Monitor() {
bool any_thread_hung = std::any_of(
watch_states_.cbegin(), watch_states_.cend(),
[this, now](const std::unique_ptr<internal::HangWatchState>& state) {
base::TimeTicks deadline = state->GetDeadline();
return deadline > deadline_ignore_threshold_ && deadline < now;
uint64_t flags;
base::TimeTicks deadline;
std::tie(flags, deadline) = state->GetFlagsAndDeadline();
return !internal::HangWatchDeadline::IsFlagSet(
internal::HangWatchDeadline::Flag::
kIgnoreCurrentHangWatchScopeEnabled,
flags) &&
deadline > deadline_ignore_threshold_ && deadline < now;
});
// If at least a thread is hung we need to capture.
......@@ -530,7 +608,10 @@ constexpr uint64_t kMaximumFlag = 0x8000000000000000u;
// Use as a mask to keep persistent flags and the deadline.
constexpr uint64_t kPersistentFlagsAndDeadlineMask =
kOnlyDeadlineMask |
static_cast<uint64_t>(HangWatchDeadline::Flag::kIgnoreHangs);
static_cast<uint64_t>(
HangWatchDeadline::Flag::kIgnoreCurrentHangWatchScopeEnabled) |
static_cast<uint64_t>(
HangWatchDeadline::Flag::kHasActiveHangWatchScopeDisabled);
} // namespace
// Flag binary representation assertions.
......@@ -617,12 +698,20 @@ bool HangWatchDeadline::SetShouldBlockOnHang(uint64_t old_flags,
std::memory_order_relaxed);
}
void HangWatchDeadline::SetIgnoreHangs() {
SetPersistentFlag(Flag::kIgnoreHangs);
void HangWatchDeadline::SetHasActiveHangWatchScopeDisabled() {
SetPersistentFlag(Flag::kHasActiveHangWatchScopeDisabled);
}
void HangWatchDeadline::UnsetHasActiveHangWatchScopeDisabled() {
ClearPersistentFlag(Flag::kHasActiveHangWatchScopeDisabled);
}
void HangWatchDeadline::SetIgnoreCurrentHangWatchScopeEnabled() {
SetPersistentFlag(Flag::kIgnoreCurrentHangWatchScopeEnabled);
}
void HangWatchDeadline::UnsetIgnoreHangs() {
ClearPersistentFlag(Flag::kIgnoreHangs);
void HangWatchDeadline::UnsetIgnoreCurrentHangWatchScopeEnabled() {
ClearPersistentFlag(Flag::kIgnoreCurrentHangWatchScopeEnabled);
}
void HangWatchDeadline::SetPersistentFlag(Flag flag) {
......@@ -700,8 +789,8 @@ HangWatchState::~HangWatchState() {
#if DCHECK_IS_ON()
// Destroying the HangWatchState should not be done if there are live
// HangWatchScopes.
DCHECK(!current_hang_watch_scope_);
// HangWatchScopeEnableds.
DCHECK(!current_hang_watch_scope_enable_);
#endif
}
......@@ -737,6 +826,22 @@ bool HangWatchState::IsOverDeadline() const {
return TimeTicks::Now() > deadline_.GetDeadline();
}
void HangWatchState::SetHasActiveHangWatchScopeDisabled() {
deadline_.SetHasActiveHangWatchScopeDisabled();
}
void HangWatchState::UnsetHasActiveHangWatchScopeDisabled() {
deadline_.UnsetHasActiveHangWatchScopeDisabled();
}
void HangWatchState::SetIgnoreCurrentHangWatchScopeEnabled() {
deadline_.SetIgnoreCurrentHangWatchScopeEnabled();
}
void HangWatchState::UnsetIgnoreCurrentHangWatchScopeEnabled() {
deadline_.UnsetIgnoreCurrentHangWatchScopeEnabled();
}
bool HangWatchState::SetShouldBlockOnHang(uint64_t old_flags,
TimeTicks old_deadline) {
return deadline_.SetShouldBlockOnHang(old_flags, old_deadline);
......@@ -747,14 +852,15 @@ bool HangWatchState::IsFlagSet(HangWatchDeadline::Flag flag) {
}
#if DCHECK_IS_ON()
void HangWatchState::SetCurrentHangWatchScope(HangWatchScope* scope) {
void HangWatchState::SetCurrentHangWatchScopeEnabled(
HangWatchScopeEnabled* current_hang_watch_scope_enable) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
current_hang_watch_scope_ = scope;
current_hang_watch_scope_enable_ = current_hang_watch_scope_enable;
}
HangWatchScope* HangWatchState::GetCurrentHangWatchScope() {
HangWatchScopeEnabled* HangWatchState::GetCurrentHangWatchScopeEnabled() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
return current_hang_watch_scope_;
return current_hang_watch_scope_enable_;
}
#endif
......@@ -762,6 +868,14 @@ HangWatchDeadline* HangWatchState::GetHangWatchDeadlineForTesting() {
return &deadline_;
}
void HangWatchState::IncrementNestingLevel() {
++nesting_level_;
}
void HangWatchState::DecrementNestingLevel() {
--nesting_level_;
}
// static
ThreadLocalPointer<HangWatchState>*
HangWatchState::GetHangWatchStateForCurrentThread() {
......
......@@ -27,7 +27,7 @@
#include "base/time/time.h"
namespace base {
class HangWatchScope;
class HangWatchScopeEnabled;
namespace internal {
class HangWatchState;
} // namespace internal
......@@ -35,13 +35,13 @@ class HangWatchState;
namespace base {
// Instantiate a HangWatchScope in a scope to register to be
// Instantiate a HangWatchScopeEnabled in a code scope to register to be
// watched for hangs of more than |timeout| by the HangWatcher.
//
// Example usage:
//
// void FooBar(){
// HangWatchScope scope(base::TimeDelta::FromSeconds(5));
// HangWatchScopeEnabled scope(base::TimeDelta::FromSeconds(5));
// DoWork();
// }
//
......@@ -49,11 +49,12 @@ namespace base {
// inspects the thread state before Foobar returns a hang will be
// reported.
//
// HangWatchScopes are typically meant to live on the stack. In some cases it's
// necessary to keep a HangWatchScope instance as a class member but special
// care is required when doing so as a HangWatchScope that stays alive longer
// than intended will generate non-actionable hang reports.
class BASE_EXPORT HangWatchScope {
// HangWatchScopeEnableds are typically meant to live on the stack. In some
// cases it's necessary to keep a HangWatchScopeEnabled instance as a class
// member but special care is required when doing so as a HangWatchScopeEnabled
// that stays alive longer than intended will generate non-actionable hang
// reports.
class BASE_EXPORT HangWatchScopeEnabled {
public:
// A good default value needs to be large enough to represent a significant
// hang and avoid noise while being small enough to not exclude too many
......@@ -63,26 +64,69 @@ class BASE_EXPORT HangWatchScope {
static const base::TimeDelta kDefaultHangWatchTime;
// Constructing/destructing thread must be the same thread.
explicit HangWatchScope(TimeDelta timeout);
~HangWatchScope();
explicit HangWatchScopeEnabled(TimeDelta timeout);
~HangWatchScopeEnabled();
HangWatchScope(const HangWatchScope&) = delete;
HangWatchScope& operator=(const HangWatchScope&) = delete;
HangWatchScopeEnabled(const HangWatchScopeEnabled&) = delete;
HangWatchScopeEnabled& operator=(const HangWatchScopeEnabled&) = delete;
private:
// This object should always be constructed and destructed on the same thread.
THREAD_CHECKER(thread_checker_);
// The deadline set by the previous HangWatchScope created on this thread.
// Stored so it can be restored when this HangWatchScope is destroyed.
// The deadline set by the previous HangWatchScopeEnabled created on this
// thread. Stored so it can be restored when this HangWatchScopeEnabled is
// destroyed.
TimeTicks previous_deadline_;
// Indicates whether the kIgnoreCurrentHang flag must be set upon exiting this
// HangWatchScopeEnabled. This is true if the
// kIgnoreCurrentHangWatchScopeEnabled flag was set upon entering this scope,
// but was cleared for this HangWatchScopeEnabled because there was no active
// HangWatchScopeDisabled.
bool set_hangs_ignored_on_exit_ = false;
#if DCHECK_IS_ON()
// The previous HangWatchScope created on this thread.
HangWatchScope* previous_scope_;
// The previous HangWatchScopeEnabled created on this thread.
HangWatchScopeEnabled* previous_hang_watch_scope_enable_;
#endif
};
// Scoped object that disables hang watching on the thread. The object nullifies
// the effect of all live HangWatchScopeEnabled instances and also that of new
// HangWatchScopeEnabled instances created during its lifetime. Use to avoid
// capturing hangs for operations known to take unbounded time like waiting for
// user input. This does not unregister the thread so when this object is
// destroyed hang watching resumes for new HangWatchScopeEnableds.
//
// Example usage:
// {
// HangWatchScopeEnabled scope_1;
// {
// HangWatchScopeEnabled scope_2;
// HangWatchScopeDisabled disabler;
// WaitForUserInput();
// HangWatchScopeEnabled scope_3;
// }
//
// HangWatchScopeEnabled scope_4;
// }
//
// HangWatchScopeEnabled scope_5;
//
// In this example hang watching is disabled for HangWatchScopeEnableds 1, 2 and
// 3 since they were either active at the time of the |disabler|'s creation or
// created while the disabler was still active. HangWatchScopeEnableds 4 and 5
// are unaffected since they were created after the disabler was destroyed.
//
class BASE_EXPORT HangWatchScopeDisabled {
public:
HangWatchScopeDisabled();
~HangWatchScopeDisabled();
HangWatchScopeDisabled(const HangWatchScopeDisabled&) = delete;
HangWatchScopeDisabled& operator=(const HangWatchScopeDisabled&) = delete;
};
// Monitors registered threads for hangs by inspecting their associated
// HangWatchStates for deadline overruns. This happens at a regular interval on
// a separate thread. Only one instance of HangWatcher can exist at a time
......@@ -298,10 +342,13 @@ class BASE_EXPORT HangWatchDeadline {
enum class Flag : uint64_t {
// Minimum value for validation purposes. Not currently used.
kMinValue = bits::LeftmostBit<uint64_t>() >> 7,
// Persistent because control by the lifetime of HangWatchScopeDisabled.
kHasActiveHangWatchScopeDisabled = bits::LeftmostBit<uint64_t>() >> 2,
// Persistent because if hang detection is disabled on a thread it should
// be re-enabled manually.
kIgnoreHangs = bits::LeftmostBit<uint64_t>() >> 1,
// Non-persistent because a new value means a new hang watch scope started
kIgnoreCurrentHangWatchScopeEnabled = bits::LeftmostBit<uint64_t>() >> 1,
// Non-persistent because a new value means a new HangWatchScopeEnabled
// started
// after the beginning of capture. It can't be implicated in the hang so we
// don't want it to block.
kShouldBlockOnHang = bits::LeftmostBit<uint64_t>() >> 0,
......@@ -346,11 +393,17 @@ class BASE_EXPORT HangWatchDeadline {
// not set the flag and returns false.
bool SetShouldBlockOnHang(uint64_t old_flags, TimeTicks old_deadline);
// Sets the kIgnoreHangs flag.
void SetIgnoreHangs();
// Sets the kHasActiveHangWatchScopeDisabled flag.
void SetHasActiveHangWatchScopeDisabled();
// Clears the kIgnoreHangs flag.
void UnsetIgnoreHangs();
// Clears the kHasActiveHangWatchScopeDisabled flag.
void UnsetHasActiveHangWatchScopeDisabled();
// Sets the kIgnoreCurrentHangWatchScopeEnabled flag.
void SetIgnoreCurrentHangWatchScopeEnabled();
// Clears the kIgnoreCurrentHangWatchScopeEnabled flag.
void UnsetIgnoreCurrentHangWatchScopeEnabled();
// Use to simulate the value of |bits_| changing between the calling a
// Set* function and the moment of atomically switching the values. The
......@@ -466,6 +519,22 @@ class BASE_EXPORT HangWatchState {
// Sets the deadline to a new value.
void SetDeadline(TimeTicks deadline);
// Mark this thread as ignored for hang watching. This means existing hang
// watch will not trigger hangs.
void SetIgnoreCurrentHangWatchScopeEnabled();
// Reactivate hang watching on this thread. Should be called when all
// HangWatchScopeEnabled instances that were ignored have completed.
void UnsetIgnoreCurrentHangWatchScopeEnabled();
// Mark hang watching as disabled on this thread. This means new
// HangWatchScopeEnabled instances will not trigger hangs.
void SetHasActiveHangWatchScopeDisabled();
// Reactivate hang watching on this thread. New HangWatchScopeEnabled
// instances will trigger hangs.
void UnsetHasActiveHangWatchScopeDisabled();
// Mark the current state as having to block in its destruction until hang
// capture completes.
bool SetShouldBlockOnHang(uint64_t old_flags, TimeTicks old_deadline);
......@@ -479,11 +548,12 @@ class BASE_EXPORT HangWatchState {
bool IsOverDeadline() const;
#if DCHECK_IS_ON()
// Saves the supplied HangWatchScope as the currently active scope.
void SetCurrentHangWatchScope(HangWatchScope* scope);
// Saves the supplied HangWatchScopeEnabled as the currently active
// HangWatchScopeEnabled.
void SetCurrentHangWatchScopeEnabled(HangWatchScopeEnabled* scope);
// Retrieve the currently active scope.
HangWatchScope* GetCurrentHangWatchScope();
HangWatchScopeEnabled* GetCurrentHangWatchScopeEnabled();
#endif
PlatformThreadId GetThreadID() const;
......@@ -491,6 +561,15 @@ class BASE_EXPORT HangWatchState {
// Retrieve the current hang watch deadline directly. For testing only.
HangWatchDeadline* GetHangWatchDeadlineForTesting();
// Returns the current nesting level.
int nesting_level() { return nesting_level_; }
// Increase the nesting level by 1;
void IncrementNestingLevel();
// Reduce the nesting level by 1;
void DecrementNestingLevel();
private:
// The thread that creates the instance should be the class that updates
// the deadline.
......@@ -502,10 +581,13 @@ class BASE_EXPORT HangWatchState {
const PlatformThreadId thread_id_;
// Number of active HangWatchScopeEnables on this thread.
int nesting_level_ = 0;
#if DCHECK_IS_ON()
// Used to keep track of the current HangWatchScope and detect improper usage.
// Scopes should always be destructed in reverse order from the one they were
// constructed in. Example of improper use:
// Used to keep track of the current HangWatchScopeEnabled and detect improper
// usage. Scopes should always be destructed in reverse order from the one
// they were constructed in. Example of improper use:
//
// {
// std::unique_ptr<Scope> scope = std::make_unique<Scope>(...);
......@@ -513,7 +595,7 @@ class BASE_EXPORT HangWatchState {
// |scope| gets deallocated first, violating reverse destruction order.
// scope.reset();
// }
HangWatchScope* current_hang_watch_scope_{nullptr};
HangWatchScopeEnabled* current_hang_watch_scope_enable_{nullptr};
#endif
};
......
......@@ -10,6 +10,7 @@
#include "base/bind.h"
#include "base/callback.h"
#include "base/callback_helpers.h"
#include "base/optional.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/synchronization/lock.h"
......@@ -56,7 +57,7 @@ class BlockingThread : public DelegateSimpleThread::Delegate {
base::ScopedClosureRunner unregister_closure =
base::HangWatcher::GetInstance()->RegisterThread();
HangWatchScope scope(timeout_);
HangWatchScopeEnabled scope(timeout_);
wait_until_entered_scope_.Signal();
unblock_thread_->Wait();
......@@ -68,7 +69,7 @@ class BlockingThread : public DelegateSimpleThread::Delegate {
void StartAndWaitForScopeEntered() {
thread_.Start();
// Block until this thread registered itself for hang watching and has
// entered a HangWatchScope.
// entered a HangWatchScopeEnabled.
wait_until_entered_scope_.Wait();
}
......@@ -80,7 +81,7 @@ class BlockingThread : public DelegateSimpleThread::Delegate {
base::DelegateSimpleThread thread_;
// Will be signaled once the thread is properly registered for watching and
// scope has been entered.
// the HangWatchScopeEnabled has been entered.
WaitableEvent wait_until_entered_scope_;
// Will be signaled once ThreadMain has run.
......@@ -183,6 +184,218 @@ class HangWatcherBlockingThreadTest : public HangWatcherTest {
};
} // namespace
TEST_F(
HangWatcherTest,
ScopeDisabledCreateScopeDisabledDestroyScopeEnabledCreateScopeEnabledDestroy) {
// Register the main test thread for hang watching.
auto unregister_thread_closure = hang_watcher_.RegisterThread();
{
HangWatchScopeEnabled expires_instantly(base::TimeDelta{});
task_environment_.FastForwardBy(kHangTime);
// De-activate hang watching,
HangWatchScopeDisabled disabler;
}
HangWatchScopeEnabled also_expires_instantly(base::TimeDelta{});
task_environment_.FastForwardBy(kHangTime);
// Triger a monitoring on HangWatcher thread and verify results.
hang_watcher_.SignalMonitorEventForTesting();
monitor_event_.Wait();
// Hang is detected since the new HangWatchScopeEnabled was not covered by the
// disabler.
monitor_event_.Wait();
ASSERT_TRUE(hang_event_.IsSignaled());
}
TEST_F(
HangWatcherTest,
ScopeEnabledCreateScopeDisabledCreateScopeEnabledDestroyScopeDisabledDestroy) {
// Register the main test thread for hang watching.
auto unregister_thread_closure = hang_watcher_.RegisterThread();
base::Optional<HangWatchScopeDisabled> disabler;
// De-activate hang watching,
{
// Start a HangWatchScopeEnabled that expires right away. Then advance
// time to make sure a hang is detected.
HangWatchScopeEnabled expires_instantly(base::TimeDelta{});
task_environment_.FastForwardBy(kHangTime);
disabler.emplace();
// Triger a monitoring on HangWatcher thread and verify results.
hang_watcher_.SignalMonitorEventForTesting();
monitor_event_.Wait();
}
disabler.reset();
// Hang is ignored since a disabler was live during the lifetime of the hung
// HangWatchScopeEnabled.
ASSERT_FALSE(hang_event_.IsSignaled());
}
TEST_F(
HangWatcherTest,
ScopeDisabledCreateScopeEnabledCreateScopeDisabledDestroyScopeEnabledDestroy) {
// Register the main test thread for hang watching.
auto unregister_thread_closure = hang_watcher_.RegisterThread();
base::Optional<HangWatchScopeDisabled> disabler;
// De-activate hang watching,
{
disabler.emplace();
// Start a HangWatchScopeEnabled that expires right away. Then advance
// time to make sure a hang is detected.
HangWatchScopeEnabled expires_instantly(base::TimeDelta{});
task_environment_.FastForwardBy(kHangTime);
disabler.reset();
// Triger a monitoring on HangWatcher thread and verify results.
hang_watcher_.SignalMonitorEventForTesting();
monitor_event_.Wait();
}
// Hang is ignored since a disabler was live during the lifetime of the hung
// HangWatchScopeEnabled.
ASSERT_FALSE(hang_event_.IsSignaled());
}
TEST_F(
HangWatcherTest,
ScopeDisabledCreateScopeEnabledCreateScopeEnabledDestroyScopeDisabledDestroy) {
// Register the main test thread for hang watching.
auto unregister_thread_closure = hang_watcher_.RegisterThread();
// De-activate hang watching,
HangWatchScopeDisabled disabler;
{
// Start a HangWatchScopeEnabled that expires right away. Then advance
// time to make sure a hang is detected.
HangWatchScopeEnabled expires_instantly(base::TimeDelta{});
task_environment_.FastForwardBy(kHangTime);
// Triger a monitoring on HangWatcher thread and verify results.
hang_watcher_.SignalMonitorEventForTesting();
monitor_event_.Wait();
}
// Hang is ignored.
ASSERT_FALSE(hang_event_.IsSignaled());
}
TEST_F(HangWatcherTest, ScopeCreateTempCreateTempDestroyScopeDestroy) {
// Register the main test thread for hang watching.
auto unregister_thread_closure = hang_watcher_.RegisterThread();
{
// Start a HangWatchScopeEnabled that expires right away. Then advance
// time to make sure a hang is detected.
HangWatchScopeEnabled expires_instantly(base::TimeDelta{});
task_environment_.FastForwardBy(kHangTime);
{
// De-activate hang watching,
HangWatchScopeDisabled disabler;
}
// Triger a monitoring on HangWatcher thread and verify results.
hang_watcher_.SignalMonitorEventForTesting();
monitor_event_.Wait();
}
// Hang is ignored.
ASSERT_FALSE(hang_event_.IsSignaled());
}
TEST_F(
HangWatcherTest,
ScopeEnabledCreateScopeDisabledCreateScopeDisabledDestroyScopeEnabledDestroy) {
// Register the main test thread for hang watching.
auto unregister_thread_closure = hang_watcher_.RegisterThread();
{
// Start a HangWatchScopeEnabled that expires right away. Then advance
// time to make sure a hang is detected.
HangWatchScopeEnabled expires_instantly(base::TimeDelta{});
task_environment_.FastForwardBy(kHangTime);
// De-activate hang watching,
HangWatchScopeDisabled disabler;
// Triger a monitoring on HangWatcher thread and verify results.
hang_watcher_.SignalMonitorEventForTesting();
monitor_event_.Wait();
}
// Hang is ignored.
ASSERT_FALSE(hang_event_.IsSignaled());
}
// Test that disabling an inner HangWatchScopeEnabled will also prevent hang
// detection in outer scopes.
TEST_F(HangWatcherTest, ScopeDisabledObjectInnerScope) {
// Register the main test thread for hang watching.
auto unregister_thread_closure = hang_watcher_.RegisterThread();
// Start a HangWatchScopeEnabled that expires right away. Then advance
// time to make sure a hang is detected.
HangWatchScopeEnabled expires_instantly(base::TimeDelta{});
task_environment_.FastForwardBy(kHangTime);
{
// De-activate hang watching,
HangWatchScopeDisabled disabler;
// Start a HangWatchScopeEnabled under the disabler that expires right away.
// Then advance time to make sure a hang is detected.
HangWatchScopeEnabled also_expires_instantly(base::TimeDelta{});
task_environment_.FastForwardBy(kHangTime);
}
// Triger a monitoring on HangWatcher thread and verify results.
hang_watcher_.SignalMonitorEventForTesting();
monitor_event_.Wait();
// Hang is ignored since it concerns a scope for which one of the inner scope
// was ignored.
ASSERT_FALSE(hang_event_.IsSignaled());
}
TEST_F(HangWatcherTest, NewScopeAfterDisabling) {
// Register the main test thread for hang watching.
auto unregister_thread_closure = hang_watcher_.RegisterThread();
// Start a HangWatchScopeEnabled that expires right away. Then advance
// time to make sure a hang is detected.
HangWatchScopeEnabled expires_instantly(base::TimeDelta{});
task_environment_.FastForwardBy(kHangTime);
{
// De-activate hang watching,
HangWatchScopeDisabled disabler;
// Start a HangWatchScopeEnabled under the disabler that expires right away.
// Then advance time to make sure a hang is detected.
HangWatchScopeEnabled also_expires_instantly(base::TimeDelta{});
task_environment_.FastForwardBy(kHangTime);
}
HangWatchScopeEnabled also_expires_instantly(base::TimeDelta{});
task_environment_.FastForwardBy(kHangTime);
// Triger a monitoring on HangWatcher thread and verify results.
hang_watcher_.SignalMonitorEventForTesting();
monitor_event_.Wait();
// Hang is detected because it's unrelated to the hangs that were disabled.
ASSERT_TRUE(hang_event_.IsSignaled());
}
TEST_F(HangWatcherTest, NestedScopes) {
// Create a state object for the test thread since this test is single
// threaded.
......@@ -203,14 +416,14 @@ TEST_F(HangWatcherTest, NestedScopes) {
// At this point we have not set any timeouts.
{
// Create a first timeout which is more restrictive than the default.
HangWatchScope first_scope(kFirstTimeout);
HangWatchScopeEnabled first_scope(kFirstTimeout);
// We are on mock time. There is no time advancement and as such no hangs.
ASSERT_FALSE(current_hang_watch_state->IsOverDeadline());
ASSERT_EQ(current_hang_watch_state->GetDeadline(), first_deadline);
{
// Set a yet more restrictive deadline. Still no hang.
HangWatchScope second_scope(kSecondTimeout);
HangWatchScopeEnabled second_scope(kSecondTimeout);
ASSERT_FALSE(current_hang_watch_state->IsOverDeadline());
ASSERT_EQ(current_hang_watch_state->GetDeadline(), second_deadline);
}
......@@ -314,8 +527,8 @@ class HangWatcherSnapshotTest : public testing::Test {
} // namespace
// Verify that the hang capture fails when marking a thread for blocking fails.
// This simulates a HangWatchScope completing between the time the hang was
// dected and the time it is recorded which would create a non-actionable
// This simulates a HangWatchScopeEnabled completing between the time the hang
// was dected and the time it is recorded which would create a non-actionable
// report.
TEST_F(HangWatcherSnapshotTest, NonActionableReport) {
hang_watcher_.SetOnHangClosureForTesting(
......@@ -326,11 +539,11 @@ TEST_F(HangWatcherSnapshotTest, NonActionableReport) {
hang_watcher_.Start();
// Register the main test thread for hang watching.
auto unregister_thread_closure_ = hang_watcher_.RegisterThread();
auto unregister_thread_closure = hang_watcher_.RegisterThread();
{
// Start a hang watch scope that expires right away. Ensures that
// Start a HangWatchScopeEnabled that expires right away. Ensures that
// the first monitor will detect a hang.
HangWatchScope expires_instantly(base::TimeDelta{});
HangWatchScopeEnabled expires_instantly(base::TimeDelta{});
internal::HangWatchState* current_hang_watch_state =
internal::HangWatchState::GetHangWatchStateForCurrentThread()->Get();
......@@ -372,16 +585,16 @@ TEST_F(HangWatcherSnapshotTest, HungThreadIDs) {
hang_watcher_.Start();
// Register the main test thread for hang watching.
auto unregister_thread_closure_ = hang_watcher_.RegisterThread();
auto unregister_thread_closure = hang_watcher_.RegisterThread();
BlockingThread blocking_thread(&monitor_event_, base::TimeDelta{});
blocking_thread.StartAndWaitForScopeEntered();
{
// Start a hang watch scope that expires right away. Ensures that
// Start a HangWatchScopeEnabled that expires right away. Ensures that
// the first monitor will detect a hang. This scope will naturally have a
// later deadline than the one in |blocking_thread_| since it was created
// after.
HangWatchScope expires_instantly(base::TimeDelta{});
HangWatchScopeEnabled expires_instantly(base::TimeDelta{});
// Hung thread list should contain the id the blocking thread and then the
// id of the test main thread since that is the order of increasing
......@@ -402,12 +615,12 @@ TEST_F(HangWatcherSnapshotTest, HungThreadIDs) {
ExpectNoCapture();
}
// All hang watch scopes are over. There should be no capture.
// All HangWatchScopeEnables are over. There should be no capture.
ExpectNoCapture();
// Once all recorded scopes are over creating a new one and monitoring will
// trigger a hang detection.
HangWatchScope expires_instantly(base::TimeDelta{});
HangWatchScopeEnabled expires_instantly(base::TimeDelta{});
TestIDList(ConcatenateThreadIds({test_thread_id_}));
}
......@@ -569,9 +782,9 @@ TEST_F(HangWatcherPeriodicMonitoringTest, NoMonitorOnOverSleep) {
}
namespace {
class HangWatchScopeBlockingTest : public testing::Test {
class HangWatchScopeEnabledBlockingTest : public testing::Test {
public:
HangWatchScopeBlockingTest() {
HangWatchScopeEnabledBlockingTest() {
hang_watcher_.SetOnHangClosureForTesting(base::BindLambdaForTesting([&] {
capture_started_.Signal();
// Simulate capturing that takes a long time.
......@@ -597,15 +810,16 @@ class HangWatchScopeBlockingTest : public testing::Test {
unregister_thread_closure_ = hang_watcher_.RegisterThread();
}
HangWatchScopeBlockingTest(const HangWatchScopeBlockingTest& other) = delete;
HangWatchScopeBlockingTest& operator=(
const HangWatchScopeBlockingTest& other) = delete;
HangWatchScopeEnabledBlockingTest(
const HangWatchScopeEnabledBlockingTest& other) = delete;
HangWatchScopeEnabledBlockingTest& operator=(
const HangWatchScopeEnabledBlockingTest& other) = delete;
void VerifyScopesDontBlock() {
// Start a hang watch scope that cannot possibly cause a hang to be
// Start a HangWatchScopeEnabled that cannot possibly cause a hang to be
// detected.
{
HangWatchScope long_scope(kVeryLongDelta);
HangWatchScopeEnabled long_scope(kVeryLongDelta);
// Manually trigger a monitoring.
hang_watcher_.SignalMonitorEventForTesting();
......@@ -639,27 +853,27 @@ class HangWatchScopeBlockingTest : public testing::Test {
};
} // namespace
// Tests that execution is unimpeded by ~HangWatchScope() when no capture ever
// takes place.
TEST_F(HangWatchScopeBlockingTest, ScopeDoesNotBlocksWithoutCapture) {
// Tests that execution is unimpeded by ~HangWatchScopeEnabled() when no capture
// ever takes place.
TEST_F(HangWatchScopeEnabledBlockingTest, ScopeDoesNotBlocksWithoutCapture) {
// No capture should take place so |continue_capture_| is not signaled to
// create a test hang if one ever does.
VerifyScopesDontBlock();
}
// Test that execution blocks in ~HangWatchScope() for a thread under watch
// during the capturing of a hang.
TEST_F(HangWatchScopeBlockingTest, ScopeBlocksDuringCapture) {
// Test that execution blocks in ~HangWatchScopeEnabled() for a thread under
// watch during the capturing of a hang.
TEST_F(HangWatchScopeEnabledBlockingTest, ScopeBlocksDuringCapture) {
// The capture completing is not dependent on any test event. Signal to make
// sure the test is not blocked.
continue_capture_.Signal();
// Start a hang watch scope that expires in the past already. Ensures that the
// first monitor will detect a hang.
// Start a HangWatchScopeEnabled that expires in the past already. Ensures
// that the first monitor will detect a hang.
{
// Start a hang watch scope that expires right away. Ensures that the
// Start a HangWatchScopeEnabled that expires right away. Ensures that the
// first monitor will detect a hang.
HangWatchScope expires_right_away(base::TimeDelta{});
HangWatchScopeEnabled expires_right_away(base::TimeDelta{});
// Manually trigger a monitoring.
hang_watcher_.SignalMonitorEventForTesting();
......@@ -668,7 +882,7 @@ TEST_F(HangWatchScopeBlockingTest, ScopeBlocksDuringCapture) {
capture_started_.Wait();
// Execution will get stuck in the outer scope because it can't escape
// ~HangWatchScope() if a hang capture is under way.
// ~HangWatchScopeEnabled() if a hang capture is under way.
}
// A hang was in progress so execution should have been blocked in
......@@ -685,12 +899,12 @@ TEST_F(HangWatchScopeBlockingTest, ScopeBlocksDuringCapture) {
VerifyScopesDontBlock();
}
// Test that execution does not block in ~HangWatchScope() when the scope was
// created after the start of a capture.
TEST_F(HangWatchScopeBlockingTest, NewScopeDoesNotBlockDuringCapture) {
// Start a hang watch scope that expires right away. Ensures that the
// Test that execution does not block in ~HangWatchScopeEnabled() when the scope
// was created after the start of a capture.
TEST_F(HangWatchScopeEnabledBlockingTest, NewScopeDoesNotBlockDuringCapture) {
// Start a HangWatchScopeEnabled that expires right away. Ensures that the
// first monitor will detect a hang.
HangWatchScope expires_right_away(base::TimeDelta{});
HangWatchScopeEnabled expires_right_away(base::TimeDelta{});
// Manually trigger a monitoring.
hang_watcher_.SignalMonitorEventForTesting();
......@@ -700,19 +914,21 @@ TEST_F(HangWatchScopeBlockingTest, NewScopeDoesNotBlockDuringCapture) {
// A scope started once a capture is already under way should not block
// execution.
{ HangWatchScope also_expires_right_away(base::TimeDelta{}); }
{ HangWatchScopeEnabled also_expires_right_away(base::TimeDelta{}); }
// Wait for the new HangWatchScope to be destroyed to let the capture finish.
// If the new scope block waiting for the capture to finish this would create
// a deadlock and the test would hang.
// Wait for the new HangWatchScopeEnabled to be destroyed to let the capture
// finish. If the new scope block waiting for the capture to finish this would
// create a deadlock and the test would hang.
continue_capture_.Signal();
}
namespace internal {
namespace {
constexpr std::array<HangWatchDeadline::Flag, 3> kAllFlags{
{HangWatchDeadline::Flag::kMinValue, HangWatchDeadline::Flag::kIgnoreHangs,
constexpr std::array<HangWatchDeadline::Flag, 4> kAllFlags{
{HangWatchDeadline::Flag::kMinValue,
HangWatchDeadline::Flag::kIgnoreCurrentHangWatchScopeEnabled,
HangWatchDeadline::Flag::kHasActiveHangWatchScopeDisabled,
HangWatchDeadline::Flag::kShouldBlockOnHang}};
} // namespace
......@@ -753,7 +969,7 @@ TEST_F(HangWatchDeadlineTest, SetAndClearPersistentFlag) {
std::tie(old_flags, old_deadline) = deadline_.GetFlagsAndDeadline();
// Set the flag. Operation cannot fail.
deadline_.SetIgnoreHangs();
deadline_.SetIgnoreCurrentHangWatchScopeEnabled();
// Get new flags and deadline.
uint64_t new_flags;
......@@ -762,16 +978,19 @@ TEST_F(HangWatchDeadlineTest, SetAndClearPersistentFlag) {
// Flag was set properly.
ASSERT_TRUE(HangWatchDeadline::IsFlagSet(
HangWatchDeadline::Flag::kIgnoreHangs, new_flags));
HangWatchDeadline::Flag::kIgnoreCurrentHangWatchScopeEnabled, new_flags));
// No side-effect on deadline.
ASSERT_EQ(new_deadline, old_deadline);
// No side-effect on other flags.
ASSERT_EQ(FlagsMinus(new_flags, HangWatchDeadline::Flag::kIgnoreHangs),
old_flags);
ASSERT_EQ(
FlagsMinus(new_flags,
HangWatchDeadline::Flag::kIgnoreCurrentHangWatchScopeEnabled),
old_flags);
// Clear the flag, operation cannot fail.
deadline_.UnsetIgnoreHangs();
deadline_.UnsetIgnoreCurrentHangWatchScopeEnabled();
// Update new values.
std::tie(new_flags, new_deadline) = deadline_.GetFlagsAndDeadline();
......@@ -826,8 +1045,8 @@ TEST_F(HangWatchDeadlineTest, SetShouldBlockOnHangDeadlineChanged) {
ASSERT_EQ(deadline_.GetDeadline(), new_deadline);
}
// Verify that clearing a persistent (kIgnoreHangs) when the value changed
// succeeds and has non side-effects.
// Verify that clearing a persistent (kIgnoreCurrentHangWatchScopeEnabled) when
// the value changed succeeds and has non side-effects.
TEST_F(HangWatchDeadlineTest, ClearIgnoreHangsDeadlineChanged) {
AssertNoFlagsSet();
......@@ -835,10 +1054,10 @@ TEST_F(HangWatchDeadlineTest, ClearIgnoreHangsDeadlineChanged) {
base::TimeTicks deadline;
std::tie(flags, deadline) = deadline_.GetFlagsAndDeadline();
deadline_.SetIgnoreHangs();
deadline_.SetIgnoreCurrentHangWatchScopeEnabled();
std::tie(flags, deadline) = deadline_.GetFlagsAndDeadline();
ASSERT_TRUE(HangWatchDeadline::IsFlagSet(
HangWatchDeadline::Flag::kIgnoreHangs, flags));
HangWatchDeadline::Flag::kIgnoreCurrentHangWatchScopeEnabled, flags));
// Simulate deadline change. Flags are constant.
const base::TimeTicks new_deadline =
......@@ -850,17 +1069,19 @@ TEST_F(HangWatchDeadlineTest, ClearIgnoreHangsDeadlineChanged) {
}));
// Clearing kIgnoreHang is unafected by deadline or flags change.
deadline_.UnsetIgnoreHangs();
ASSERT_FALSE(deadline_.IsFlagSet(HangWatchDeadline::Flag::kIgnoreHangs));
deadline_.UnsetIgnoreCurrentHangWatchScopeEnabled();
ASSERT_FALSE(deadline_.IsFlagSet(
HangWatchDeadline::Flag::kIgnoreCurrentHangWatchScopeEnabled));
// New deadline that was changed concurrently is preserved.
ASSERT_TRUE(deadline_.IsFlagSet(HangWatchDeadline::Flag::kShouldBlockOnHang));
ASSERT_EQ(deadline_.GetDeadline(), new_deadline);
}
// Verify that setting a persistent (kIgnoreHangs) when the deadline or flags
// changed succeeds and has non side-effects.
TEST_F(HangWatchDeadlineTest, SetIgnoreHangsDeadlineChanged) {
// Verify that setting a persistent (kIgnoreCurrentHangWatchScopeEnabled) when
// the deadline or flags changed succeeds and has non side-effects.
TEST_F(HangWatchDeadlineTest,
SetIgnoreCurrentHangWatchScopeEnableDeadlineChangedd) {
AssertNoFlagsSet();
uint64_t flags;
......@@ -878,8 +1099,9 @@ TEST_F(HangWatchDeadlineTest, SetIgnoreHangsDeadlineChanged) {
}));
// kIgnoreHang persists through value change.
deadline_.SetIgnoreHangs();
ASSERT_TRUE(deadline_.IsFlagSet(HangWatchDeadline::Flag::kIgnoreHangs));
deadline_.SetIgnoreCurrentHangWatchScopeEnabled();
ASSERT_TRUE(deadline_.IsFlagSet(
HangWatchDeadline::Flag::kIgnoreCurrentHangWatchScopeEnabled));
// New deadline and flags that changed concurrently are preserved.
ASSERT_TRUE(deadline_.IsFlagSet(HangWatchDeadline::Flag::kShouldBlockOnHang));
......@@ -898,8 +1120,9 @@ TEST_F(HangWatchDeadlineTest, SetDeadlineWipesFlags) {
std::tie(flags, deadline) = deadline_.GetFlagsAndDeadline();
deadline_.SetIgnoreHangs();
ASSERT_TRUE(deadline_.IsFlagSet(HangWatchDeadline::Flag::kIgnoreHangs));
deadline_.SetIgnoreCurrentHangWatchScopeEnabled();
ASSERT_TRUE(deadline_.IsFlagSet(
HangWatchDeadline::Flag::kIgnoreCurrentHangWatchScopeEnabled));
// Change the deadline.
deadline_.SetDeadline(TimeTicks{});
......@@ -908,7 +1131,8 @@ TEST_F(HangWatchDeadlineTest, SetDeadlineWipesFlags) {
// Verify the persistent flag stuck and the non-persistent one was unset.
ASSERT_FALSE(
deadline_.IsFlagSet(HangWatchDeadline::Flag::kShouldBlockOnHang));
ASSERT_TRUE(deadline_.IsFlagSet(HangWatchDeadline::Flag::kIgnoreHangs));
ASSERT_TRUE(deadline_.IsFlagSet(
HangWatchDeadline::Flag::kIgnoreCurrentHangWatchScopeEnabled));
}
} // namespace internal
......
......@@ -10,6 +10,7 @@
#include "base/gtest_prod_util.h"
#include "base/location.h"
#include "base/macros.h"
#include "base/threading/hang_watcher.h"
// -----------------------------------------------------------------------------
// Usage documentation
......@@ -570,6 +571,11 @@ class BASE_EXPORT ScopedAllowBaseSyncPrimitivesOutsideBlockingScope {
const bool was_disallowed_;
#endif
// 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::HangWatchScopeDisabled hang_watch_scope_disabled_;
DISALLOW_COPY_AND_ASSIGN(ScopedAllowBaseSyncPrimitivesOutsideBlockingScope);
};
......
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