1 // Copyright 2020 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef BASE_THREADING_SCOPED_BLOCKING_CALL_INTERNAL_H_ 6 #define BASE_THREADING_SCOPED_BLOCKING_CALL_INTERNAL_H_ 7 8 #include "base/auto_reset.h" 9 #include "base/base_export.h" 10 #include "base/functional/callback_forward.h" 11 #include "base/memory/raw_ptr.h" 12 #include "base/memory/ref_counted.h" 13 #include "base/synchronization/lock.h" 14 #include "base/thread_annotations.h" 15 #include "base/time/time.h" 16 #include "base/types/strong_alias.h" 17 #include "third_party/abseil-cpp/absl/types/optional.h" 18 19 namespace base { 20 21 // Forward-declare types from scoped_blocking_call.h to break cyclic dependency. 22 enum class BlockingType; 23 using IOJankReportingCallback = RepeatingCallback<void(int, int)>; 24 using OnlyObservedThreadsForTest = 25 StrongAlias<class OnlyObservedThreadsTag, bool>; 26 void BASE_EXPORT EnableIOJankMonitoringForProcess(IOJankReportingCallback, 27 OnlyObservedThreadsForTest); 28 29 // Implementation details of types in scoped_blocking_call.h and classes for a 30 // few key //base types to observe and react to blocking calls. 31 namespace internal { 32 33 // Interface for an observer to be informed when a thread enters or exits 34 // the scope of ScopedBlockingCall objects. 35 class BASE_EXPORT BlockingObserver { 36 public: 37 virtual ~BlockingObserver() = default; 38 39 // Invoked when a ScopedBlockingCall is instantiated on the observed thread 40 // where there wasn't an existing ScopedBlockingCall. 41 virtual void BlockingStarted(BlockingType blocking_type) = 0; 42 43 // Invoked when a WILL_BLOCK ScopedBlockingCall is instantiated on the 44 // observed thread where there was a MAY_BLOCK ScopedBlockingCall but not a 45 // WILL_BLOCK ScopedBlockingCall. 46 virtual void BlockingTypeUpgraded() = 0; 47 48 // Invoked when the last ScopedBlockingCall on the observed thread is 49 // destroyed. 50 virtual void BlockingEnded() = 0; 51 }; 52 53 // Registers |new_blocking_observer| on the current thread. It is invalid to 54 // call this on a thread where there is an active ScopedBlockingCall. 55 BASE_EXPORT void SetBlockingObserverForCurrentThread( 56 BlockingObserver* new_blocking_observer); 57 58 BASE_EXPORT void ClearBlockingObserverForCurrentThread(); 59 60 // An IOJankMonitoringWindow instruments 1-minute of runtime. Any I/O jank > 1 61 // second happening during that period will be reported to it. It will then 62 // report via the IOJankReportingCallback in |reporting_callback_storage()| if 63 // it's non-null. https://bit.ly/chrome-io-jank-metric. 64 class BASE_EXPORT [[maybe_unused, nodiscard]] IOJankMonitoringWindow 65 : public RefCountedThreadSafe<IOJankMonitoringWindow> { 66 public: 67 explicit IOJankMonitoringWindow(TimeTicks start_time); 68 69 IOJankMonitoringWindow(const IOJankMonitoringWindow&) = delete; 70 IOJankMonitoringWindow& operator=(const IOJankMonitoringWindow&) = delete; 71 72 // Cancels monitoring and clears this class' static state. 73 static void CancelMonitoringForTesting(); 74 75 class [[maybe_unused, nodiscard]] ScopedMonitoredCall { 76 public: 77 // Stores a ref to the current IOJankMonitoringWindow if monitoring is 78 // active, keeping it alive at least until the monitored call completes or 79 // Cancel() is invoked. 80 ScopedMonitoredCall(); 81 82 // Reports to |assigned_jank_window_| if it's non-null. 83 ~ScopedMonitoredCall(); 84 85 ScopedMonitoredCall(const ScopedMonitoredCall&) = delete; 86 ScopedMonitoredCall& operator=(const ScopedMonitoredCall&) = delete; 87 88 // Cancels monitoring of this call. 89 void Cancel(); 90 91 private: 92 TimeTicks call_start_; 93 scoped_refptr<IOJankMonitoringWindow> assigned_jank_window_; 94 }; 95 96 static constexpr TimeDelta kIOJankInterval = Seconds(1); 97 static constexpr TimeDelta kMonitoringWindow = Minutes(1); 98 static constexpr TimeDelta kTimeDiscrepancyTimeout = kIOJankInterval * 10; 99 static constexpr int kNumIntervals = kMonitoringWindow / kIOJankInterval; 100 101 // kIOJankIntervals must integrally fill kMonitoringWindow 102 static_assert((kMonitoringWindow % kIOJankInterval).is_zero(), ""); 103 104 // Cancelation is simple because it can only affect the current window. 105 static_assert(kTimeDiscrepancyTimeout < kMonitoringWindow, ""); 106 107 private: 108 friend class base::RefCountedThreadSafe<IOJankMonitoringWindow>; 109 friend void base::EnableIOJankMonitoringForProcess( 110 IOJankReportingCallback, 111 OnlyObservedThreadsForTest); 112 113 // No-op if reporting_callback_storage() is null (i.e. unless 114 // EnableIOJankMonitoringForProcess() was called). 115 // When reporting_callback_storage() is non-null : Ensures that there's an 116 // active IOJankMonitoringWindow for Now(), connects it via |next_| to the 117 // previous IOJankMonitoringWindow to let ScopedMonitoredCalls that span 118 // multiple windows report to each window they cover. In the event that Now() 119 // is farther ahead than expected (> 10s), the previous window is |canceled_| 120 // as it was likely interrupted by a system sleep and a new 121 // IOJankMonitoringWindow chain is started from Now(). In all cases, returns a 122 // live reference to the current (old or new) IOJankMonitoringWindow as a 123 // helper so callers that need it don't need to re-acquire 124 // current_jank_window_lock() after calling this. 125 // |recent_now| is a recent sampling of TimeTicks::Now(), avoids 126 // double-sampling Now() from most callers. 127 static scoped_refptr<IOJankMonitoringWindow> MonitorNextJankWindowIfNecessary( 128 TimeTicks recent_now); 129 130 // An IOJankMonitoringWindow is destroyed when all refs to it are gone, i.e.: 131 // 1) The window it covers has elapsed and MonitorNextJankWindowIfNecessary() 132 // has replaced it. 133 // 2) All pending ScopedMonitoredCall's in their range have completed 134 // (including the ones that transitively have it in their |next_| chain). 135 ~IOJankMonitoringWindow(); 136 137 // Called from ~ScopedMonitoredCall(). 138 void OnBlockingCallCompleted(TimeTicks call_start, TimeTicks call_end); 139 140 // Helper for OnBlockingCallCompleted(). Records |num_janky_intervals| 141 // starting at |local_jank_start_index|. Having this logic separately helps 142 // sane management of |intervals_lock_| when recursive calls through |next_| 143 // pointers are necessary. 144 void AddJank(int local_jank_start_index, int num_janky_intervals); 145 146 static Lock& current_jank_window_lock(); 147 static scoped_refptr<IOJankMonitoringWindow>& current_jank_window_storage() 148 EXCLUSIVE_LOCKS_REQUIRED(current_jank_window_lock()); 149 150 // Storage for callback used to report monitoring results. 151 // NullCallback if monitoring was not enabled for this process. 152 static IOJankReportingCallback& reporting_callback_storage() 153 EXCLUSIVE_LOCKS_REQUIRED(current_jank_window_lock()); 154 155 Lock intervals_lock_; 156 size_t intervals_jank_count_[kNumIntervals] GUARDED_BY(intervals_lock_) = {}; 157 158 const TimeTicks start_time_; 159 160 // Set only once per window, in MonitorNextJankWindowIfNecessary(). Any read 161 // of this value must be ordered after that call in memory and in time. 162 scoped_refptr<IOJankMonitoringWindow> next_; 163 164 // Set to true if ~IOJankMonitoringWindow() shouldn't record metrics. 165 // Modifications of this variable must be synchronized with each other and 166 // happen-before ~IOJankMonitoringWindow(). 167 bool canceled_ = false; 168 }; 169 170 // Common implementation class for both ScopedBlockingCall and 171 // ScopedBlockingCallWithBaseSyncPrimitives without assertions. 172 class BASE_EXPORT [[maybe_unused, nodiscard]] UncheckedScopedBlockingCall { 173 public: 174 enum class BlockingCallType { 175 kRegular, 176 kBaseSyncPrimitives, 177 }; 178 179 UncheckedScopedBlockingCall(BlockingType blocking_type, 180 BlockingCallType blocking_call_type); 181 182 UncheckedScopedBlockingCall(const UncheckedScopedBlockingCall&) = delete; 183 UncheckedScopedBlockingCall& operator=(const UncheckedScopedBlockingCall&) = 184 delete; 185 186 ~UncheckedScopedBlockingCall(); 187 188 private: 189 const raw_ptr<BlockingObserver> blocking_observer_; 190 191 // Previous ScopedBlockingCall instantiated on this thread. 192 const raw_ptr<UncheckedScopedBlockingCall> previous_scoped_blocking_call_; 193 194 const base::AutoReset<UncheckedScopedBlockingCall*> resetter_; 195 196 // Whether the BlockingType of the current thread was WILL_BLOCK after this 197 // ScopedBlockingCall was instantiated. 198 const bool is_will_block_; 199 200 // Non-nullopt for non-nested blocking calls of type MAY_BLOCK on foreground 201 // threads which we monitor for I/O jank. 202 absl::optional<IOJankMonitoringWindow::ScopedMonitoredCall> monitored_call_; 203 }; 204 205 } // namespace internal 206 } // namespace base 207 208 #endif // BASE_THREADING_SCOPED_BLOCKING_CALL_INTERNAL_H_ 209