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