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