1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
|
// 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 <optional>
#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"
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.
std::optional<IOJankMonitoringWindow::ScopedMonitoredCall> monitored_call_;
};
} // namespace internal
} // namespace base
#endif // BASE_THREADING_SCOPED_BLOCKING_CALL_INTERNAL_H_
|