• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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