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