• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 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_TASK_SEQUENCE_MANAGER_THREAD_CONTROLLER_H_
6 #define BASE_TASK_SEQUENCE_MANAGER_THREAD_CONTROLLER_H_
7 
8 #include <stack>
9 #include <vector>
10 
11 #include "base/base_export.h"
12 #include "base/check.h"
13 #include "base/memory/raw_ptr.h"
14 #include "base/memory/raw_ref.h"
15 #include "base/memory/scoped_refptr.h"
16 #include "base/message_loop/message_pump.h"
17 #include "base/profiler/sample_metadata.h"
18 #include "base/run_loop.h"
19 #include "base/task/common/lazy_now.h"
20 #include "base/task/sequence_manager/associated_thread_id.h"
21 #include "base/task/sequence_manager/tasks.h"
22 #include "base/task/single_thread_task_runner.h"
23 #include "base/thread_annotations.h"
24 #include "base/time/time.h"
25 #include "base/trace_event/base_tracing.h"
26 #include "base/tracing_buildflags.h"
27 #include "build/build_config.h"
28 #include "third_party/abseil-cpp/absl/types/optional.h"
29 
30 namespace base {
31 
32 class HistogramBase;
33 class MessageLoopBase;
34 class TickClock;
35 struct PendingTask;
36 
37 namespace sequence_manager {
38 namespace internal {
39 
40 class SequencedTaskSource;
41 
42 // Implementation of this interface is used by SequenceManager to schedule
43 // actual work to be run. Hopefully we can stop using MessageLoop and this
44 // interface will become more concise.
45 class BASE_EXPORT ThreadController {
46  public:
47   // Phases the top-RunLevel can go through. While these are more precise than
48   // RunLevelTracker::State, unlike it: phases are determined retrospectively
49   // as we often only find out the type of work that was just performed at the
50   // end of a phase. Or even find out about past phases later in the timeline
51   // (i.e. kScheduled is only known after the first kSelectingApplicationTask
52   // phase out-of-idle).
53   // Public for unit tests.
54   // These values are logged to UMA. Entries should not be renumbered and
55   // numeric values should never be reused. Please keep in sync
56   // with "MessagePumpPhases" in src/tools/metrics/histograms/enums.xml.
57   enum Phase {
58     kScheduled = 1,
59     kPumpOverhead = 2,
60     // Any work item, in practice application tasks are mapped to
61     // kApplicationTask so this only accounts for native work.
62     kWorkItem = 3,
63     kNativeWork = kWorkItem,
64     kSelectingApplicationTask = 4,
65     kApplicationTask = 5,
66     kIdleWork = 6,
67     kNested = 7,
68     kLastPhase = kNested,
69     // Reported as a kWorkItem but doesn't clear state relevant to the ongoing
70     // work item as it isn't finished (will resume after nesting).
71     kWorkItemSuspendedOnNested,
72   };
73 
74   explicit ThreadController(const TickClock* time_source);
75   virtual ~ThreadController();
76 
77   // Sets the number of tasks executed in a single invocation of DoWork.
78   // Increasing the batch size can reduce the overhead of yielding back to the
79   // main message loop.
80   virtual void SetWorkBatchSize(int work_batch_size = 1) = 0;
81 
82   // Notifies that |pending_task| is about to be enqueued. Needed for tracing
83   // purposes. The impl may use this opportunity add metadata to |pending_task|
84   // before it is moved into the queue.
85   virtual void WillQueueTask(PendingTask* pending_task) = 0;
86 
87   // Notify the controller that its associated sequence has immediate work
88   // to run. Shortly after this is called, the thread associated with this
89   // controller will run a task returned by sequence->TakeTask(). Can be called
90   // from any sequence.
91   //
92   // TODO(altimin): Change this to "the thread associated with this
93   // controller will run tasks returned by sequence->TakeTask() until it
94   // returns null or sequence->DidRunTask() returns false" once the
95   // code is changed to work that way.
96   virtual void ScheduleWork() = 0;
97 
98   // Notify the controller that SequencedTaskSource will have a delayed work
99   // ready to be run at |wake_up|. This call cancels any previously
100   // scheduled delayed work. Can only be called from the main sequence.
101   // NOTE: GetPendingWakeUp might return a different value as it also takes
102   // immediate work into account.
103   // TODO(kraynov): Remove |lazy_now| parameter.
104   virtual void SetNextDelayedDoWork(LazyNow* lazy_now,
105                                     absl::optional<WakeUp> wake_up) = 0;
106 
107   // Sets the sequenced task source from which to take tasks after
108   // a Schedule*Work() call is made.
109   // Must be called before the first call to Schedule*Work().
110   virtual void SetSequencedTaskSource(SequencedTaskSource*) = 0;
111 
112   // Requests desired timer precision from the OS.
113   // Has no effect on some platforms.
114   virtual void SetTimerSlack(TimerSlack timer_slack) = 0;
115 
116   // Completes delayed initialization of unbound ThreadControllers.
117   // BindToCurrentThread(MessageLoopBase*) or BindToCurrentThread(MessagePump*)
118   // may only be called once.
119   virtual void BindToCurrentThread(
120       std::unique_ptr<MessagePump> message_pump) = 0;
121 
122   // Explicitly allow or disallow task execution. Implicitly disallowed when
123   // entering a nested runloop.
124   virtual void SetTaskExecutionAllowed(bool allowed) = 0;
125 
126   // Whether task execution is allowed or not.
127   virtual bool IsTaskExecutionAllowed() const = 0;
128 
129   // Returns the MessagePump we're bound to if any.
130   virtual MessagePump* GetBoundMessagePump() const = 0;
131 
132   // Returns true if the current run loop should quit when idle.
133   virtual bool ShouldQuitRunLoopWhenIdle() = 0;
134 
135 #if BUILDFLAG(IS_IOS) || BUILDFLAG(IS_ANDROID)
136   // On iOS, the main message loop cannot be Run().  Instead call
137   // AttachToMessagePump(), which connects this ThreadController to the
138   // UI thread's CFRunLoop and allows PostTask() to work.
139   virtual void AttachToMessagePump() = 0;
140 #endif
141 
142 #if BUILDFLAG(IS_IOS)
143   // Detaches this ThreadController from the message pump, allowing the
144   // controller to be shut down cleanly.
145   virtual void DetachFromMessagePump() = 0;
146 #endif
147 
148   // Enables TimeKeeper metrics. `thread_name` will be used as a suffix.
149   void EnableMessagePumpTimeKeeperMetrics(const char* thread_name);
150 
151   // Currently only overridden on ThreadControllerWithMessagePumpImpl.
152   //
153   // While Now() is less than |prioritize_until| we will alternate between
154   // |work_batch_size| tasks before setting |yield_to_native| on the
155   // NextWorkInfo and yielding to the underlying sequence (e.g. the message
156   // pump).
157   virtual void PrioritizeYieldingToNative(base::TimeTicks prioritize_until) = 0;
158 
159   // Sets the SingleThreadTaskRunner that will be returned by
160   // SingleThreadTaskRunner::GetCurrentDefault on the thread controlled by this
161   // ThreadController.
162   virtual void SetDefaultTaskRunner(scoped_refptr<SingleThreadTaskRunner>) = 0;
163 
164   // TODO(altimin): Get rid of the methods below.
165   // These methods exist due to current integration of SequenceManager
166   // with MessageLoop.
167 
168   virtual bool RunsTasksInCurrentSequence() = 0;
169   void SetTickClock(const TickClock* clock);
170   virtual scoped_refptr<SingleThreadTaskRunner> GetDefaultTaskRunner() = 0;
171   virtual void RestoreDefaultTaskRunner() = 0;
172   virtual void AddNestingObserver(RunLoop::NestingObserver* observer) = 0;
173   virtual void RemoveNestingObserver(RunLoop::NestingObserver* observer) = 0;
174 
GetAssociatedThread()175   const scoped_refptr<AssociatedThreadId>& GetAssociatedThread() const {
176     return associated_thread_;
177   }
178 
179  protected:
180   const scoped_refptr<AssociatedThreadId> associated_thread_;
181 
182   // The source of TimeTicks for this ThreadController.
183   // Must only be accessed from the `associated_thread_`.
184   // TODO(scheduler-dev): This could be made
185   // `GUARDED_BY_CONTEXT(associated_thread_->thread_checker)` when
186   // switching MainThreadOnly to thread annotations and annotating all
187   // thread-affine ThreadController methods. Without that, this lone annotation
188   // would result in an inconsistent set of DCHECKs...
189   raw_ptr<const TickClock> time_source_;  // Not owned.
190 
191   // Tracks the state of each run-level (main and nested ones) in its associated
192   // ThreadController. It does so using two high-level principles:
193   //  1) #work-in-work-implies-nested :
194   //     If the |state_| is kRunningWorkItem and another work item starts
195   //     (OnWorkStarted()), it implies this inner-work-item is running from a
196   //  2) #done-work-at-lower-runlevel-implies-done-nested
197   //     WorkItems are required to pass in the nesting depth at which they were
198   //     created in OnWorkEnded(). Then, if |rundepth| is lower than the current
199   //     RunDepth(), we know the top RunLevel was an (already exited) nested
200   //     loop and will be popped off |run_levels_|.
201   // We need this logic because native nested loops can run from any work item
202   // without a RunLoop being involved, see
203   // ThreadControllerWithMessagePumpTest.ThreadControllerActive* tests for
204   // examples. Using these two heuristics is the simplest way, trying to
205   // capture all the ways in which work items can nest is harder than reacting
206   // as it happens.
207   //
208   // Note 1: "native work" is only captured if the MessagePump is
209   // instrumented to see them and shares them with ThreadController (via
210   // MessagePump::Delegate::OnBeginWorkItem). As such it is still possible to
211   // view trace events emanating from native work without "ThreadController
212   // active" being active.
213   // Note 2: Non-instrumented native work does not break the two high-level
214   // principles above because:
215   //  A) If a non-instrumented work item enters a nested loop, either:
216   //     i) No instrumented work run within the loop so it's invisible.
217   //     ii) Instrumented work runs *and* current state is kRunningWorkItem
218   //         ((A) is a work item within an instrumented work item):
219   //         #work-in-work-implies-nested triggers and the nested loop is
220   //         visible.
221   //     iii) Instrumented work runs *and* current state is kIdle or
222   //          kInBetweenWorkItems ((A) is a work item run by a native loop):
223   //          #work-in-work-implies-nested doesn't trigger and this instrumented
224   //          work (iii) looks like a non-nested continuation of work at the
225   //          current RunLevel.
226   //  B) When work item (A) exits its nested loop and completes, respectively:
227   //     i) The loop was invisible so no RunLevel was created for it and
228   //        #done-work-at-lower-runlevel-implies-done-nested doesn't trigger so
229   //        it balances out.
230   //     ii) Instrumented work did run, and so RunLevels() increased. However,
231   //         since instrumented work (the work which called the nested loop)
232   //         keeps track of its own run depth, on its exit, we know to pop the
233   //         RunLevel corresponding to the nested work.
234   //     iii) Nested instrumented work was visible but didn't appear nested,
235   //          state is now back to kInBetweenWorkItems or kIdle as before (A).
236   class BASE_EXPORT RunLevelTracker {
237    public:
238     // States each RunLevel can be in.
239     enum State {
240       // Waiting for work (pending wakeup).
241       kIdle,
242       // Between two work items but not idle.
243       kInBetweenWorkItems,
244       // Running and currently processing a work items (includes selecting the
245       // next work item, i.e. either peeking the native work queue or selecting
246       // the next application task).
247       kRunningWorkItem,
248     };
249 
250     explicit RunLevelTracker(const ThreadController& outer);
251     ~RunLevelTracker();
252 
253     void OnRunLoopStarted(State initial_state, LazyNow& lazy_now);
254     void OnRunLoopEnded();
255     void OnWorkStarted(LazyNow& lazy_now);
256     void OnApplicationTaskSelected(TimeTicks queue_time, LazyNow& lazy_now);
257     void OnWorkEnded(LazyNow& lazy_now, int run_level_depth);
258     void OnIdle(LazyNow& lazy_now);
259 
num_run_levels()260     size_t num_run_levels() const {
261       DCHECK_CALLED_ON_VALID_THREAD(outer_->associated_thread_->thread_checker);
262       return run_levels_.size();
263     }
264 
265     // Emits a perfetto::Flow (wakeup.flow) event associated with this
266     // RunLevelTracker.
267     void RecordScheduleWork();
268 
269     void EnableTimeKeeperMetrics(const char* thread_name);
270 
271     // Observes changes of state sent as trace-events so they can be tested.
272     class TraceObserverForTesting {
273      public:
274       virtual ~TraceObserverForTesting() = default;
275 
276       virtual void OnThreadControllerActiveBegin() = 0;
277       virtual void OnThreadControllerActiveEnd() = 0;
278       virtual void OnPhaseRecorded(Phase phase) = 0;
279     };
280 
281     static void SetTraceObserverForTesting(
282         TraceObserverForTesting* trace_observer_for_testing);
283 
284    private:
285 #if BUILDFLAG(ENABLE_BASE_TRACING)
286     using TerminatingFlowLambda =
287         std::invoke_result<decltype(perfetto::TerminatingFlow::FromPointer),
288                            void*>::type;
289 #endif
290 
291     // Keeps track of the time spent in various Phases (ignores idle), reports
292     // via UMA to the corresponding phase every time one reaches >= 100ms of
293     // cumulative time, resulting in a metric of relative time spent in each
294     // non-idle phase. Also emits each phase as a trace event on its own
295     // MessagePumpPhases track when the disabled-by-default-base tracing
296     // category is enabled.
297     class TimeKeeper {
298      public:
299       explicit TimeKeeper(const RunLevelTracker& outer);
300 
301       void EnableRecording(const char* thread_name);
302 
303       // Records the start time of the first phase out-of-idle. The kScheduled
304       // phase will be attributed the time before this point once its
305       // `queue_time` is known.
306       void RecordWakeUp(LazyNow& lazy_now);
307 
308       // Accounts the time since OnWorkStarted() towards
309       // kSelectingApplicationTask. Accounts `queue_time - last_wakeup_` towards
310       // kScheduled (iff `queue_time` is not null nor later than
311       // `last_wakeup_`). And flags the current kWorkItem as a kApplicationTask,
312       // to be accounted from OnWorkEnded(). Emits a trace event for the
313       // kScheduled phase if applicable.
314       void OnApplicationTaskSelected(TimeTicks queue_time, LazyNow& lazy_now);
315 
316       // If recording is enabled: Records the end of a phase, attributing it the
317       // delta between `lazy_now` and `last_phase_end` and emit a trace event
318       // for it.
319       void RecordEndOfPhase(Phase phase, LazyNow& lazy_now);
320 
321      private:
322       enum class ShouldRecordReqs {
323         // Regular should-record requirements.
324         kRegular,
325         // On wakeup there's an exception to the requirement that `last_wakeup_`
326         // be set.
327         kOnWakeUp,
328         // On end-nested there's an exception to the requirement that there's no
329         // ongoing nesting (as the kNested phase ends from ~RunLevel, before
330         // run_levels.pop() completes).
331         kOnEndNested,
332       };
333       bool ShouldRecordNow(ShouldRecordReqs reqs = ShouldRecordReqs::kRegular);
334 
335       // Common helper to actually record time in a phase and emitt histograms
336       // as needed.
337       void RecordTimeInPhase(Phase phase,
338                              TimeTicks phase_begin,
339                              TimeTicks phase_end);
340 
341       static const char* PhaseToEventName(Phase phase);
342 
343       // Cumulative time deltas for each phase, reported and reset when >=100ms.
344       std::array<TimeDelta, Phase::kLastPhase + 1> deltas_ = {};
345       // Set at the start of the first work item out-of-idle. Consumed from the
346       // first application task found in that work cycle
347       // (in OnApplicationTaskSelected).
348       TimeTicks last_wakeup_;
349       // The end of the last phase (used as the beginning of the next one).
350       TimeTicks last_phase_end_;
351       // The end of the last kIdleWork phase. Used as a minimum for the next
352       // kScheduled phase's begin (as it's possible that the next wake-up is
353       // scheduled during DoIdleWork adn we don't want overlapping phases).
354       TimeTicks last_sleep_;
355       // Assumes each kWorkItem is native unless OnApplicationTaskSelected() is
356       // invoked in a given [OnWorkStarted, OnWorkEnded].
357       bool current_work_item_is_native_ = true;
358 
359       // non-null when recording is enabled.
360       raw_ptr<HistogramBase> histogram_ = nullptr;
361 #if BUILDFLAG(ENABLE_BASE_TRACING)
362       absl::optional<perfetto::Track> perfetto_track_;
363 
364       // True if tracing was enabled during the last pass of RecordTimeInPhase.
365       bool was_tracing_enabled_ = false;
366 #endif
367       const raw_ref<const RunLevelTracker> outer_;
368     } time_keeper_{*this};
369 
370     class RunLevel {
371      public:
372       RunLevel(State initial_state,
373                bool is_nested,
374                TimeKeeper& time_keeper,
375                LazyNow& lazy_now
376 #if BUILDFLAG(ENABLE_BASE_TRACING)
377                ,
378                TerminatingFlowLambda& terminating_flow_lambda
379 #endif
380       );
381       ~RunLevel();
382 
383       // Move-constructible for STL compat. Flags `other.was_moved_` so it noops
384       // on destruction after handing off its responsibility. Move-assignment
385       // is not necessary nor possible as not all members are assignable.
386       RunLevel(RunLevel&& other);
387       RunLevel& operator=(RunLevel&&) = delete;
388 
389       void UpdateState(State new_state);
390 
state()391       State state() const { return state_; }
392 
set_exit_lazy_now(LazyNow * exit_lazy_now)393       void set_exit_lazy_now(LazyNow* exit_lazy_now) {
394         DCHECK(exit_lazy_now);
395         DCHECK(!exit_lazy_now_);
396         exit_lazy_now_ = exit_lazy_now;
397       }
398 
399      private:
400       State state_ = kIdle;
401       bool is_nested_;
402 
403       const raw_ref<TimeKeeper> time_keeper_;
404       // Must be set shortly before ~RunLevel.
405       raw_ptr<LazyNow> exit_lazy_now_ = nullptr;
406 
407       SampleMetadata thread_controller_sample_metadata_;
408       size_t thread_controller_active_id_ = 0;
409 #if BUILDFLAG(ENABLE_BASE_TRACING)
410       const raw_ref<TerminatingFlowLambda> terminating_wakeup_flow_lambda_;
411 #endif
412 
413       // Toggles to true when used as RunLevel&& input to construct another
414       // RunLevel. This RunLevel's destructor will then no-op.
415       class TruePostMove {
416        public:
417         TruePostMove() = default;
TruePostMove(TruePostMove && other)418         TruePostMove(TruePostMove&& other) { other.was_moved_ = true; }
419         // Not necessary for now.
420         TruePostMove& operator=(TruePostMove&&) = delete;
421 
422         explicit operator bool() { return was_moved_; }
423 
424        private:
425         bool was_moved_ = false;
426       };
427       TruePostMove was_moved_;
428     };
429 
430     [[maybe_unused]] const raw_ref<const ThreadController> outer_;
431 
432 #if BUILDFLAG(ENABLE_BASE_TRACING)
433     TerminatingFlowLambda terminating_wakeup_lambda_{
434         perfetto::TerminatingFlow::FromPointer(this)};
435 #endif
436 
437     std::stack<RunLevel, std::vector<RunLevel>> run_levels_
438         GUARDED_BY_CONTEXT(outer_->associated_thread_->thread_checker);
439 
440     static TraceObserverForTesting* trace_observer_for_testing_;
441   } run_level_tracker_{*this};
442 };
443 
444 }  // namespace internal
445 }  // namespace sequence_manager
446 }  // namespace base
447 
448 #endif  // BASE_TASK_SEQUENCE_MANAGER_THREAD_CONTROLLER_H_
449