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