209 lines
8.5 KiB
C++
209 lines
8.5 KiB
C++
// Copyright 2020 The Chromium Authors
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#ifndef BASE_THREADING_SCOPED_BLOCKING_CALL_INTERNAL_H_
|
|
#define BASE_THREADING_SCOPED_BLOCKING_CALL_INTERNAL_H_
|
|
|
|
#include "base/auto_reset.h"
|
|
#include "base/base_export.h"
|
|
#include "base/functional/callback_forward.h"
|
|
#include "base/memory/raw_ptr.h"
|
|
#include "base/memory/ref_counted.h"
|
|
#include "base/synchronization/lock.h"
|
|
#include "base/thread_annotations.h"
|
|
#include "base/time/time.h"
|
|
#include "base/types/strong_alias.h"
|
|
#include "third_party/abseil-cpp/absl/types/optional.h"
|
|
|
|
namespace base {
|
|
|
|
// Forward-declare types from scoped_blocking_call.h to break cyclic dependency.
|
|
enum class BlockingType;
|
|
using IOJankReportingCallback = RepeatingCallback<void(int, int)>;
|
|
using OnlyObservedThreadsForTest =
|
|
StrongAlias<class OnlyObservedThreadsTag, bool>;
|
|
void BASE_EXPORT EnableIOJankMonitoringForProcess(IOJankReportingCallback,
|
|
OnlyObservedThreadsForTest);
|
|
|
|
// Implementation details of types in scoped_blocking_call.h and classes for a
|
|
// few key //base types to observe and react to blocking calls.
|
|
namespace internal {
|
|
|
|
// Interface for an observer to be informed when a thread enters or exits
|
|
// the scope of ScopedBlockingCall objects.
|
|
class BASE_EXPORT BlockingObserver {
|
|
public:
|
|
virtual ~BlockingObserver() = default;
|
|
|
|
// Invoked when a ScopedBlockingCall is instantiated on the observed thread
|
|
// where there wasn't an existing ScopedBlockingCall.
|
|
virtual void BlockingStarted(BlockingType blocking_type) = 0;
|
|
|
|
// Invoked when a WILL_BLOCK ScopedBlockingCall is instantiated on the
|
|
// observed thread where there was a MAY_BLOCK ScopedBlockingCall but not a
|
|
// WILL_BLOCK ScopedBlockingCall.
|
|
virtual void BlockingTypeUpgraded() = 0;
|
|
|
|
// Invoked when the last ScopedBlockingCall on the observed thread is
|
|
// destroyed.
|
|
virtual void BlockingEnded() = 0;
|
|
};
|
|
|
|
// Registers |new_blocking_observer| on the current thread. It is invalid to
|
|
// call this on a thread where there is an active ScopedBlockingCall.
|
|
BASE_EXPORT void SetBlockingObserverForCurrentThread(
|
|
BlockingObserver* new_blocking_observer);
|
|
|
|
BASE_EXPORT void ClearBlockingObserverForCurrentThread();
|
|
|
|
// An IOJankMonitoringWindow instruments 1-minute of runtime. Any I/O jank > 1
|
|
// second happening during that period will be reported to it. It will then
|
|
// report via the IOJankReportingCallback in |reporting_callback_storage()| if
|
|
// it's non-null. https://bit.ly/chrome-io-jank-metric.
|
|
class BASE_EXPORT [[maybe_unused, nodiscard]] IOJankMonitoringWindow
|
|
: public RefCountedThreadSafe<IOJankMonitoringWindow> {
|
|
public:
|
|
explicit IOJankMonitoringWindow(TimeTicks start_time);
|
|
|
|
IOJankMonitoringWindow(const IOJankMonitoringWindow&) = delete;
|
|
IOJankMonitoringWindow& operator=(const IOJankMonitoringWindow&) = delete;
|
|
|
|
// Cancels monitoring and clears this class' static state.
|
|
static void CancelMonitoringForTesting();
|
|
|
|
class [[maybe_unused, nodiscard]] ScopedMonitoredCall {
|
|
public:
|
|
// Stores a ref to the current IOJankMonitoringWindow if monitoring is
|
|
// active, keeping it alive at least until the monitored call completes or
|
|
// Cancel() is invoked.
|
|
ScopedMonitoredCall();
|
|
|
|
// Reports to |assigned_jank_window_| if it's non-null.
|
|
~ScopedMonitoredCall();
|
|
|
|
ScopedMonitoredCall(const ScopedMonitoredCall&) = delete;
|
|
ScopedMonitoredCall& operator=(const ScopedMonitoredCall&) = delete;
|
|
|
|
// Cancels monitoring of this call.
|
|
void Cancel();
|
|
|
|
private:
|
|
TimeTicks call_start_;
|
|
scoped_refptr<IOJankMonitoringWindow> assigned_jank_window_;
|
|
};
|
|
|
|
static constexpr TimeDelta kIOJankInterval = Seconds(1);
|
|
static constexpr TimeDelta kMonitoringWindow = Minutes(1);
|
|
static constexpr TimeDelta kTimeDiscrepancyTimeout = kIOJankInterval * 10;
|
|
static constexpr int kNumIntervals = kMonitoringWindow / kIOJankInterval;
|
|
|
|
// kIOJankIntervals must integrally fill kMonitoringWindow
|
|
static_assert((kMonitoringWindow % kIOJankInterval).is_zero(), "");
|
|
|
|
// Cancelation is simple because it can only affect the current window.
|
|
static_assert(kTimeDiscrepancyTimeout < kMonitoringWindow, "");
|
|
|
|
private:
|
|
friend class base::RefCountedThreadSafe<IOJankMonitoringWindow>;
|
|
friend void base::EnableIOJankMonitoringForProcess(
|
|
IOJankReportingCallback,
|
|
OnlyObservedThreadsForTest);
|
|
|
|
// No-op if reporting_callback_storage() is null (i.e. unless
|
|
// EnableIOJankMonitoringForProcess() was called).
|
|
// When reporting_callback_storage() is non-null : Ensures that there's an
|
|
// active IOJankMonitoringWindow for Now(), connects it via |next_| to the
|
|
// previous IOJankMonitoringWindow to let ScopedMonitoredCalls that span
|
|
// multiple windows report to each window they cover. In the event that Now()
|
|
// is farther ahead than expected (> 10s), the previous window is |canceled_|
|
|
// as it was likely interrupted by a system sleep and a new
|
|
// IOJankMonitoringWindow chain is started from Now(). In all cases, returns a
|
|
// live reference to the current (old or new) IOJankMonitoringWindow as a
|
|
// helper so callers that need it don't need to re-acquire
|
|
// current_jank_window_lock() after calling this.
|
|
// |recent_now| is a recent sampling of TimeTicks::Now(), avoids
|
|
// double-sampling Now() from most callers.
|
|
static scoped_refptr<IOJankMonitoringWindow> MonitorNextJankWindowIfNecessary(
|
|
TimeTicks recent_now);
|
|
|
|
// An IOJankMonitoringWindow is destroyed when all refs to it are gone, i.e.:
|
|
// 1) The window it covers has elapsed and MonitorNextJankWindowIfNecessary()
|
|
// has replaced it.
|
|
// 2) All pending ScopedMonitoredCall's in their range have completed
|
|
// (including the ones that transitively have it in their |next_| chain).
|
|
~IOJankMonitoringWindow();
|
|
|
|
// Called from ~ScopedMonitoredCall().
|
|
void OnBlockingCallCompleted(TimeTicks call_start, TimeTicks call_end);
|
|
|
|
// Helper for OnBlockingCallCompleted(). Records |num_janky_intervals|
|
|
// starting at |local_jank_start_index|. Having this logic separately helps
|
|
// sane management of |intervals_lock_| when recursive calls through |next_|
|
|
// pointers are necessary.
|
|
void AddJank(int local_jank_start_index, int num_janky_intervals);
|
|
|
|
static Lock& current_jank_window_lock();
|
|
static scoped_refptr<IOJankMonitoringWindow>& current_jank_window_storage()
|
|
EXCLUSIVE_LOCKS_REQUIRED(current_jank_window_lock());
|
|
|
|
// Storage for callback used to report monitoring results.
|
|
// NullCallback if monitoring was not enabled for this process.
|
|
static IOJankReportingCallback& reporting_callback_storage()
|
|
EXCLUSIVE_LOCKS_REQUIRED(current_jank_window_lock());
|
|
|
|
Lock intervals_lock_;
|
|
size_t intervals_jank_count_[kNumIntervals] GUARDED_BY(intervals_lock_) = {};
|
|
|
|
const TimeTicks start_time_;
|
|
|
|
// Set only once per window, in MonitorNextJankWindowIfNecessary(). Any read
|
|
// of this value must be ordered after that call in memory and in time.
|
|
scoped_refptr<IOJankMonitoringWindow> next_;
|
|
|
|
// Set to true if ~IOJankMonitoringWindow() shouldn't record metrics.
|
|
// Modifications of this variable must be synchronized with each other and
|
|
// happen-before ~IOJankMonitoringWindow().
|
|
bool canceled_ = false;
|
|
};
|
|
|
|
// Common implementation class for both ScopedBlockingCall and
|
|
// ScopedBlockingCallWithBaseSyncPrimitives without assertions.
|
|
class BASE_EXPORT [[maybe_unused, nodiscard]] UncheckedScopedBlockingCall {
|
|
public:
|
|
enum class BlockingCallType {
|
|
kRegular,
|
|
kBaseSyncPrimitives,
|
|
};
|
|
|
|
UncheckedScopedBlockingCall(BlockingType blocking_type,
|
|
BlockingCallType blocking_call_type);
|
|
|
|
UncheckedScopedBlockingCall(const UncheckedScopedBlockingCall&) = delete;
|
|
UncheckedScopedBlockingCall& operator=(const UncheckedScopedBlockingCall&) =
|
|
delete;
|
|
|
|
~UncheckedScopedBlockingCall();
|
|
|
|
private:
|
|
const raw_ptr<BlockingObserver> blocking_observer_;
|
|
|
|
// Previous ScopedBlockingCall instantiated on this thread.
|
|
const raw_ptr<UncheckedScopedBlockingCall> previous_scoped_blocking_call_;
|
|
|
|
const base::AutoReset<UncheckedScopedBlockingCall*> resetter_;
|
|
|
|
// Whether the BlockingType of the current thread was WILL_BLOCK after this
|
|
// ScopedBlockingCall was instantiated.
|
|
const bool is_will_block_;
|
|
|
|
// Non-nullopt for non-nested blocking calls of type MAY_BLOCK on foreground
|
|
// threads which we monitor for I/O jank.
|
|
absl::optional<IOJankMonitoringWindow::ScopedMonitoredCall> monitored_call_;
|
|
};
|
|
|
|
} // namespace internal
|
|
} // namespace base
|
|
|
|
#endif // BASE_THREADING_SCOPED_BLOCKING_CALL_INTERNAL_H_
|