xref: /aosp_15_r20/external/cronet/base/threading/scoped_blocking_call_internal.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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