• 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 #include "base/task/sequence_manager/thread_controller_with_message_pump_impl.h"
6 
7 #include <algorithm>
8 #include <atomic>
9 #include <utility>
10 
11 #include "base/auto_reset.h"
12 #include "base/feature_list.h"
13 #include "base/logging.h"
14 #include "base/memory/ptr_util.h"
15 #include "base/memory/raw_ref.h"
16 #include "base/message_loop/message_pump.h"
17 #include "base/metrics/histogram.h"
18 #include "base/metrics/histogram_macros.h"
19 #include "base/task/sequence_manager/tasks.h"
20 #include "base/task/task_features.h"
21 #include "base/threading/hang_watcher.h"
22 #include "base/time/tick_clock.h"
23 #include "base/time/time.h"
24 #include "base/trace_event/base_tracing.h"
25 #include "build/build_config.h"
26 #include "third_party/abseil-cpp/absl/types/optional.h"
27 
28 #if BUILDFLAG(IS_IOS)
29 #include "base/message_loop/message_pump_mac.h"
30 #elif BUILDFLAG(IS_ANDROID)
31 #include "base/message_loop/message_pump_android.h"
32 #endif
33 
34 namespace base {
35 namespace sequence_manager {
36 namespace internal {
37 namespace {
38 
39 // Returns |next_run_time| capped at 1 day from |lazy_now|. This is used to
40 // mitigate https://crbug.com/850450 where some platforms are unhappy with
41 // delays > 100,000,000 seconds. In practice, a diagnosis metric showed that no
42 // sleep > 1 hour ever completes (always interrupted by an earlier MessageLoop
43 // event) and 99% of completed sleeps are the ones scheduled for <= 1 second.
44 // Details @ https://crrev.com/c/1142589.
CapAtOneDay(TimeTicks next_run_time,LazyNow * lazy_now)45 TimeTicks CapAtOneDay(TimeTicks next_run_time, LazyNow* lazy_now) {
46   return std::min(next_run_time, lazy_now->Now() + Days(1));
47 }
48 
49 // Feature to run tasks by batches before pumping out messages.
50 BASE_FEATURE(kRunTasksByBatches,
51              "RunTasksByBatches",
52              base::FEATURE_DISABLED_BY_DEFAULT);
53 
54 #if BUILDFLAG(IS_WIN)
55 // If enabled, deactivate the high resolution timer immediately in DoWork(),
56 // instead of waiting for next DoIdleWork.
57 BASE_FEATURE(kUseLessHighResTimers,
58              "UseLessHighResTimers",
59              base::FEATURE_DISABLED_BY_DEFAULT);
60 std::atomic_bool g_use_less_high_res_timers = false;
61 
62 // If enabled, high resolution timer will be used all the time on Windows. This
63 // is for test only.
64 BASE_FEATURE(kAlwaysUseHighResTimers,
65              "AlwaysUseHighResTimers",
66              base::FEATURE_DISABLED_BY_DEFAULT);
67 #endif
68 
69 std::atomic_bool g_align_wake_ups = false;
70 std::atomic_bool g_run_tasks_by_batches = false;
71 #if BUILDFLAG(IS_WIN)
72 bool g_explicit_high_resolution_timer_win = false;
73 #endif  // BUILDFLAG(IS_WIN)
74 
WakeUpRunTime(const WakeUp & wake_up)75 TimeTicks WakeUpRunTime(const WakeUp& wake_up) {
76   // Windows relies on the low resolution timer rather than manual wake up
77   // alignment.
78 #if BUILDFLAG(IS_WIN)
79   if (g_explicit_high_resolution_timer_win)
80     return wake_up.earliest_time();
81 #else  // BUILDFLAG(IS_WIN)
82   if (g_align_wake_ups.load(std::memory_order_relaxed)) {
83     TimeTicks aligned_run_time = wake_up.earliest_time().SnappedToNextTick(
84         TimeTicks(), GetTaskLeewayForCurrentThread());
85     return std::min(aligned_run_time, wake_up.latest_time());
86   }
87 #endif
88   return wake_up.time;
89 }
90 
91 }  // namespace
92 
93 // static
InitializeFeatures()94 void ThreadControllerWithMessagePumpImpl::InitializeFeatures() {
95   g_align_wake_ups = FeatureList::IsEnabled(kAlignWakeUps);
96   g_run_tasks_by_batches.store(FeatureList::IsEnabled(kRunTasksByBatches),
97                                std::memory_order_relaxed);
98 #if BUILDFLAG(IS_WIN)
99   g_explicit_high_resolution_timer_win =
100       FeatureList::IsEnabled(kExplicitHighResolutionTimerWin);
101   g_use_less_high_res_timers.store(
102       FeatureList::IsEnabled(kUseLessHighResTimers), std::memory_order_relaxed);
103   if (FeatureList::IsEnabled(kAlwaysUseHighResTimers)) {
104     Time::ActivateHighResolutionTimer(true);
105   }
106 #endif
107 }
108 
109 // static
ResetFeatures()110 void ThreadControllerWithMessagePumpImpl::ResetFeatures() {
111   g_align_wake_ups.store(
112       kAlignWakeUps.default_state == FEATURE_ENABLED_BY_DEFAULT,
113       std::memory_order_relaxed);
114   g_run_tasks_by_batches.store(
115       kRunTasksByBatches.default_state == FEATURE_ENABLED_BY_DEFAULT,
116       std::memory_order_relaxed);
117 }
118 
ThreadControllerWithMessagePumpImpl(const SequenceManager::Settings & settings)119 ThreadControllerWithMessagePumpImpl::ThreadControllerWithMessagePumpImpl(
120     const SequenceManager::Settings& settings)
121     : ThreadController(settings.clock),
122       work_deduplicator_(associated_thread_),
123       can_run_tasks_by_batches_(settings.can_run_tasks_by_batches) {}
124 
ThreadControllerWithMessagePumpImpl(std::unique_ptr<MessagePump> message_pump,const SequenceManager::Settings & settings)125 ThreadControllerWithMessagePumpImpl::ThreadControllerWithMessagePumpImpl(
126     std::unique_ptr<MessagePump> message_pump,
127     const SequenceManager::Settings& settings)
128     : ThreadControllerWithMessagePumpImpl(settings) {
129   BindToCurrentThread(std::move(message_pump));
130 }
131 
~ThreadControllerWithMessagePumpImpl()132 ThreadControllerWithMessagePumpImpl::~ThreadControllerWithMessagePumpImpl() {
133   // Destructors of MessagePump::Delegate and
134   // SingleThreadTaskRunner::CurrentDefaultHandle will do all the clean-up.
135   // ScopedSetSequenceLocalStorageMapForCurrentThread destructor will
136   // de-register the current thread as a sequence.
137 
138 #if BUILDFLAG(IS_WIN)
139   if (main_thread_only().in_high_res_mode) {
140     main_thread_only().in_high_res_mode = false;
141     Time::ActivateHighResolutionTimer(false);
142   }
143 #endif
144 }
145 
146 // static
147 std::unique_ptr<ThreadControllerWithMessagePumpImpl>
CreateUnbound(const SequenceManager::Settings & settings)148 ThreadControllerWithMessagePumpImpl::CreateUnbound(
149     const SequenceManager::Settings& settings) {
150   return base::WrapUnique(new ThreadControllerWithMessagePumpImpl(settings));
151 }
152 
153 ThreadControllerWithMessagePumpImpl::MainThreadOnly::MainThreadOnly() = default;
154 
155 ThreadControllerWithMessagePumpImpl::MainThreadOnly::~MainThreadOnly() =
156     default;
157 
SetSequencedTaskSource(SequencedTaskSource * task_source)158 void ThreadControllerWithMessagePumpImpl::SetSequencedTaskSource(
159     SequencedTaskSource* task_source) {
160   DCHECK(task_source);
161   DCHECK(!main_thread_only().task_source);
162   main_thread_only().task_source = task_source;
163 }
164 
BindToCurrentThread(std::unique_ptr<MessagePump> message_pump)165 void ThreadControllerWithMessagePumpImpl::BindToCurrentThread(
166     std::unique_ptr<MessagePump> message_pump) {
167   associated_thread_->BindToCurrentThread();
168   pump_ = std::move(message_pump);
169   work_id_provider_ = WorkIdProvider::GetForCurrentThread();
170   RunLoop::RegisterDelegateForCurrentThread(this);
171   scoped_set_sequence_local_storage_map_for_current_thread_ = std::make_unique<
172       base::internal::ScopedSetSequenceLocalStorageMapForCurrentThread>(
173       &sequence_local_storage_map_);
174   {
175     base::internal::CheckedAutoLock task_runner_lock(task_runner_lock_);
176     if (task_runner_)
177       InitializeSingleThreadTaskRunnerCurrentDefaultHandle();
178   }
179   if (work_deduplicator_.BindToCurrentThread() ==
180       ShouldScheduleWork::kScheduleImmediate) {
181     pump_->ScheduleWork();
182   }
183 }
184 
SetWorkBatchSize(int work_batch_size)185 void ThreadControllerWithMessagePumpImpl::SetWorkBatchSize(
186     int work_batch_size) {
187   DCHECK_GE(work_batch_size, 1);
188   CHECK(main_thread_only().can_change_batch_size);
189   main_thread_only().work_batch_size = work_batch_size;
190 }
191 
SetTimerSlack(TimerSlack timer_slack)192 void ThreadControllerWithMessagePumpImpl::SetTimerSlack(
193     TimerSlack timer_slack) {
194   DCHECK(RunsTasksInCurrentSequence());
195   pump_->SetTimerSlack(timer_slack);
196 }
197 
WillQueueTask(PendingTask * pending_task)198 void ThreadControllerWithMessagePumpImpl::WillQueueTask(
199     PendingTask* pending_task) {
200   task_annotator_.WillQueueTask("SequenceManager PostTask", pending_task);
201 }
202 
ScheduleWork()203 void ThreadControllerWithMessagePumpImpl::ScheduleWork() {
204   base::internal::CheckedLock::AssertNoLockHeldOnCurrentThread();
205   if (work_deduplicator_.OnWorkRequested() ==
206       ShouldScheduleWork::kScheduleImmediate) {
207     if (!associated_thread_->IsBoundToCurrentThread()) {
208       run_level_tracker_.RecordScheduleWork();
209     } else {
210       TRACE_EVENT_INSTANT("wakeup.flow", "ScheduleWorkToSelf");
211     }
212     pump_->ScheduleWork();
213   }
214 }
215 
SetNextDelayedDoWork(LazyNow * lazy_now,absl::optional<WakeUp> wake_up)216 void ThreadControllerWithMessagePumpImpl::SetNextDelayedDoWork(
217     LazyNow* lazy_now,
218     absl::optional<WakeUp> wake_up) {
219   DCHECK(!wake_up || !wake_up->is_immediate());
220   TimeTicks run_time =
221       wake_up.has_value() ? WakeUpRunTime(*wake_up) : TimeTicks::Max();
222   DCHECK_LT(lazy_now->Now(), run_time);
223 
224   if (main_thread_only().next_delayed_do_work == run_time)
225     return;
226   main_thread_only().next_delayed_do_work = run_time;
227 
228   // It's very rare for PostDelayedTask to be called outside of a DoWork in
229   // production, so most of the time this does nothing.
230   if (work_deduplicator_.OnDelayedWorkRequested() ==
231       ShouldScheduleWork::kScheduleImmediate) {
232     // Cap at one day but remember the exact time for the above equality check
233     // on the next round.
234     if (!run_time.is_max())
235       run_time = CapAtOneDay(run_time, lazy_now);
236     // |pump_| can't be null as all postTasks are cross-thread before binding,
237     // and delayed cross-thread postTasks do the thread hop through an immediate
238     // task.
239     pump_->ScheduleDelayedWork({run_time, lazy_now->Now()});
240   }
241 }
242 
RunsTasksInCurrentSequence()243 bool ThreadControllerWithMessagePumpImpl::RunsTasksInCurrentSequence() {
244   return associated_thread_->IsBoundToCurrentThread();
245 }
246 
SetDefaultTaskRunner(scoped_refptr<SingleThreadTaskRunner> task_runner)247 void ThreadControllerWithMessagePumpImpl::SetDefaultTaskRunner(
248     scoped_refptr<SingleThreadTaskRunner> task_runner) {
249   base::internal::CheckedAutoLock lock(task_runner_lock_);
250   task_runner_ = task_runner;
251   if (associated_thread_->IsBound()) {
252     DCHECK(associated_thread_->IsBoundToCurrentThread());
253     // Thread task runner handle will be created in BindToCurrentThread().
254     InitializeSingleThreadTaskRunnerCurrentDefaultHandle();
255   }
256 }
257 
258 void ThreadControllerWithMessagePumpImpl::
InitializeSingleThreadTaskRunnerCurrentDefaultHandle()259     InitializeSingleThreadTaskRunnerCurrentDefaultHandle() {
260   // Only one SingleThreadTaskRunner::CurrentDefaultHandle can exist at any
261   // time, so reset the old one.
262   main_thread_only().thread_task_runner_handle.reset();
263   main_thread_only().thread_task_runner_handle =
264       std::make_unique<SingleThreadTaskRunner::CurrentDefaultHandle>(
265           task_runner_);
266   // When the task runner is known, bind the power manager. Power notifications
267   // are received through that sequence.
268   power_monitor_.BindToCurrentThread();
269 }
270 
271 scoped_refptr<SingleThreadTaskRunner>
GetDefaultTaskRunner()272 ThreadControllerWithMessagePumpImpl::GetDefaultTaskRunner() {
273   base::internal::CheckedAutoLock lock(task_runner_lock_);
274   return task_runner_;
275 }
276 
RestoreDefaultTaskRunner()277 void ThreadControllerWithMessagePumpImpl::RestoreDefaultTaskRunner() {
278   // There is no default task runner (as opposed to ThreadControllerImpl).
279 }
280 
AddNestingObserver(RunLoop::NestingObserver * observer)281 void ThreadControllerWithMessagePumpImpl::AddNestingObserver(
282     RunLoop::NestingObserver* observer) {
283   DCHECK(!main_thread_only().nesting_observer);
284   DCHECK(observer);
285   main_thread_only().nesting_observer = observer;
286   RunLoop::AddNestingObserverOnCurrentThread(this);
287 }
288 
RemoveNestingObserver(RunLoop::NestingObserver * observer)289 void ThreadControllerWithMessagePumpImpl::RemoveNestingObserver(
290     RunLoop::NestingObserver* observer) {
291   DCHECK_EQ(main_thread_only().nesting_observer, observer);
292   main_thread_only().nesting_observer = nullptr;
293   RunLoop::RemoveNestingObserverOnCurrentThread(this);
294 }
295 
OnBeginWorkItem()296 void ThreadControllerWithMessagePumpImpl::OnBeginWorkItem() {
297   LazyNow lazy_now(time_source_);
298   OnBeginWorkItemImpl(lazy_now);
299 }
300 
OnBeginWorkItemImpl(LazyNow & lazy_now)301 void ThreadControllerWithMessagePumpImpl::OnBeginWorkItemImpl(
302     LazyNow& lazy_now) {
303   hang_watch_scope_.emplace();
304   work_id_provider_->IncrementWorkId();
305   run_level_tracker_.OnWorkStarted(lazy_now);
306 }
307 
OnEndWorkItem(int run_level_depth)308 void ThreadControllerWithMessagePumpImpl::OnEndWorkItem(int run_level_depth) {
309   LazyNow lazy_now(time_source_);
310   OnEndWorkItemImpl(lazy_now, run_level_depth);
311 }
312 
OnEndWorkItemImpl(LazyNow & lazy_now,int run_level_depth)313 void ThreadControllerWithMessagePumpImpl::OnEndWorkItemImpl(
314     LazyNow& lazy_now,
315     int run_level_depth) {
316   // Work completed, begin a new hang watch until the next task (watching the
317   // pump's overhead).
318   hang_watch_scope_.emplace();
319   work_id_provider_->IncrementWorkId();
320   run_level_tracker_.OnWorkEnded(lazy_now, run_level_depth);
321 }
322 
BeforeWait()323 void ThreadControllerWithMessagePumpImpl::BeforeWait() {
324   // In most cases, DoIdleWork() will already have cleared the
325   // `hang_watch_scope_` but in some cases where the native side of the
326   // MessagePump impl is instrumented, it's possible to get a BeforeWait()
327   // outside of a DoWork cycle (e.g. message_pump_win.cc :
328   // MessagePumpForUI::HandleWorkMessage).
329   hang_watch_scope_.reset();
330 
331   work_id_provider_->IncrementWorkId();
332   LazyNow lazy_now(time_source_);
333   run_level_tracker_.OnIdle(lazy_now);
334 }
335 
336 MessagePump::Delegate::NextWorkInfo
DoWork()337 ThreadControllerWithMessagePumpImpl::DoWork() {
338 #if BUILDFLAG(IS_WIN)
339   // We've been already in a wakeup here. Deactivate the high res timer of OS
340   // immediately instead of waiting for next DoIdleWork().
341   if (g_use_less_high_res_timers.load(std::memory_order_relaxed) &&
342       main_thread_only().in_high_res_mode) {
343     main_thread_only().in_high_res_mode = false;
344     Time::ActivateHighResolutionTimer(false);
345   }
346 #endif
347   MessagePump::Delegate::NextWorkInfo next_work_info{};
348 
349   work_deduplicator_.OnWorkStarted();
350   LazyNow continuation_lazy_now(time_source_);
351   absl::optional<WakeUp> next_wake_up = DoWorkImpl(&continuation_lazy_now);
352 
353   // If we are yielding after DoWorkImpl (a work batch) set the flag boolean.
354   // This will inform the MessagePump to schedule a new continuation based on
355   // the information below, but even if its immediate let the native sequence
356   // have a chance to run.
357   // When we have |g_run_tasks_by_batches| active we want to always set the flag
358   // to true to have a similar behavior on Android as on the desktop platforms
359   // for this experiment.
360   if (RunsTasksByBatches() ||
361       (!main_thread_only().yield_to_native_after_batch.is_null() &&
362        continuation_lazy_now.Now() <
363            main_thread_only().yield_to_native_after_batch)) {
364     next_work_info.yield_to_native = true;
365   }
366   // Schedule a continuation.
367   WorkDeduplicator::NextTask next_task =
368       (next_wake_up && next_wake_up->is_immediate())
369           ? WorkDeduplicator::NextTask::kIsImmediate
370           : WorkDeduplicator::NextTask::kIsDelayed;
371   if (work_deduplicator_.DidCheckForMoreWork(next_task) ==
372       ShouldScheduleWork::kScheduleImmediate) {
373     // Need to run new work immediately, but due to the contract of DoWork
374     // we only need to return a null TimeTicks to ensure that happens.
375     return next_work_info;
376   }
377 
378   // Special-casing here avoids unnecessarily sampling Now() when out of work.
379   if (!next_wake_up) {
380     main_thread_only().next_delayed_do_work = TimeTicks::Max();
381     next_work_info.delayed_run_time = TimeTicks::Max();
382     return next_work_info;
383   }
384 
385   // The MessagePump will schedule the wake up on our behalf, so we need to
386   // update |main_thread_only().next_delayed_do_work|.
387   main_thread_only().next_delayed_do_work = WakeUpRunTime(*next_wake_up);
388 
389   // Don't request a run time past |main_thread_only().quit_runloop_after|.
390   if (main_thread_only().next_delayed_do_work >
391       main_thread_only().quit_runloop_after) {
392     main_thread_only().next_delayed_do_work =
393         main_thread_only().quit_runloop_after;
394     // If we've passed |quit_runloop_after| there's no more work to do.
395     if (continuation_lazy_now.Now() >= main_thread_only().quit_runloop_after) {
396       next_work_info.delayed_run_time = TimeTicks::Max();
397       return next_work_info;
398     }
399   }
400 
401   next_work_info.delayed_run_time = CapAtOneDay(
402       main_thread_only().next_delayed_do_work, &continuation_lazy_now);
403   next_work_info.recent_now = continuation_lazy_now.Now();
404   return next_work_info;
405 }
406 
DoWorkImpl(LazyNow * continuation_lazy_now)407 absl::optional<WakeUp> ThreadControllerWithMessagePumpImpl::DoWorkImpl(
408     LazyNow* continuation_lazy_now) {
409   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("sequence_manager"),
410                "ThreadControllerImpl::DoWork");
411 
412   if (!main_thread_only().task_execution_allowed) {
413     // Broadcast in a trace event that application tasks were disallowed. This
414     // helps spot nested loops that intentionally starve application tasks.
415     TRACE_EVENT0("base", "ThreadController: application tasks disallowed");
416     if (main_thread_only().quit_runloop_after == TimeTicks::Max())
417       return absl::nullopt;
418     return WakeUp{main_thread_only().quit_runloop_after};
419   }
420 
421   DCHECK(main_thread_only().task_source);
422 
423   // Keep running tasks for up to 8ms before yielding to the pump when tasks are
424   // run by batches.
425   const base::TimeDelta batch_duration =
426       RunsTasksByBatches() ? base::Milliseconds(8) : base::Milliseconds(0);
427 
428   const absl::optional<base::TimeTicks> start_time =
429       batch_duration.is_zero()
430           ? absl::nullopt
431           : absl::optional<base::TimeTicks>(time_source_->NowTicks());
432   absl::optional<base::TimeTicks> recent_time = start_time;
433 
434   // Loops for |batch_duration|, or |work_batch_size| times if |batch_duration|
435   // is zero.
436   for (int num_tasks_executed = 0;
437        (!batch_duration.is_zero() &&
438         (recent_time.value() - start_time.value()) < batch_duration) ||
439        (batch_duration.is_zero() &&
440         num_tasks_executed < main_thread_only().work_batch_size);
441        ++num_tasks_executed) {
442     LazyNow lazy_now_select_task(recent_time, time_source_);
443     // Include SelectNextTask() in the scope of the work item. This ensures
444     // it's covered in tracing and hang reports. This is particularly
445     // important when SelectNextTask() finds no work immediately after a
446     // wakeup, otherwise the power-inefficient wakeup is invisible in
447     // tracing. OnApplicationTaskSelected() assumes this ordering as well.
448     OnBeginWorkItemImpl(lazy_now_select_task);
449     int run_depth = static_cast<int>(run_level_tracker_.num_run_levels());
450 
451     const SequencedTaskSource::SelectTaskOption select_task_option =
452         power_monitor_.IsProcessInPowerSuspendState()
453             ? SequencedTaskSource::SelectTaskOption::kSkipDelayedTask
454             : SequencedTaskSource::SelectTaskOption::kDefault;
455     absl::optional<SequencedTaskSource::SelectedTask> selected_task =
456         main_thread_only().task_source->SelectNextTask(lazy_now_select_task,
457                                                        select_task_option);
458     LazyNow lazy_now_task_selected(time_source_);
459     run_level_tracker_.OnApplicationTaskSelected(
460         (selected_task && selected_task->task.delayed_run_time.is_null())
461             ? selected_task->task.queue_time
462             : TimeTicks(),
463         lazy_now_task_selected);
464     if (!selected_task) {
465       OnEndWorkItemImpl(lazy_now_task_selected, run_depth);
466       break;
467     }
468 
469     // Execute the task and assume the worst: it is probably not reentrant.
470     AutoReset<bool> ban_nested_application_tasks(
471         &main_thread_only().task_execution_allowed, false);
472 
473     // Trace-parsing tools (DevTools, Lighthouse, etc) consume this event to
474     // determine long tasks.
475     // See https://crbug.com/681863 and https://crbug.com/874982
476     TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "RunTask");
477 
478     {
479       // Always track the start of the task, as this is low-overhead.
480       TaskAnnotator::LongTaskTracker long_task_tracker(
481           time_source_, selected_task->task, &task_annotator_);
482 
483       // Note: all arguments after task are just passed to a TRACE_EVENT for
484       // logging so lambda captures are safe as lambda is executed inline.
485       SequencedTaskSource* source = main_thread_only().task_source;
486       task_annotator_.RunTask(
487           "ThreadControllerImpl::RunTask", selected_task->task,
488           [&selected_task, &source](perfetto::EventContext& ctx) {
489             if (selected_task->task_execution_trace_logger) {
490               selected_task->task_execution_trace_logger.Run(
491                   ctx, selected_task->task);
492             }
493             source->MaybeEmitTaskDetails(ctx, selected_task.value());
494           });
495     }
496 
497     // Reset `selected_task` before the call to `DidRunTask()` below makes its
498     // `PendingTask` reference dangling.
499     selected_task.reset();
500 
501     LazyNow lazy_now_after_run_task(time_source_);
502     main_thread_only().task_source->DidRunTask(lazy_now_after_run_task);
503     // End the work item scope after DidRunTask() as it can process microtasks
504     // (which are extensions of the RunTask).
505     OnEndWorkItemImpl(lazy_now_after_run_task, run_depth);
506 
507     // If DidRunTask() read the clock (lazy_now_after_run_task.has_value()) or
508     // if |batch_duration| > 0, store the clock value in `recent_time` so it can
509     // be reused by SelectNextTask() at the next loop iteration.
510     if (lazy_now_after_run_task.has_value() || !batch_duration.is_zero()) {
511       recent_time = lazy_now_after_run_task.Now();
512     } else {
513       recent_time.reset();
514     }
515 
516     // When Quit() is called we must stop running the batch because the
517     // caller expects per-task granularity.
518     if (main_thread_only().quit_pending)
519       break;
520   }
521 
522   if (main_thread_only().quit_pending)
523     return absl::nullopt;
524 
525   work_deduplicator_.WillCheckForMoreWork();
526 
527   // Re-check the state of the power after running tasks. An executed task may
528   // have been a power change notification.
529   const SequencedTaskSource::SelectTaskOption select_task_option =
530       power_monitor_.IsProcessInPowerSuspendState()
531           ? SequencedTaskSource::SelectTaskOption::kSkipDelayedTask
532           : SequencedTaskSource::SelectTaskOption::kDefault;
533   main_thread_only().task_source->RemoveAllCanceledDelayedTasksFromFront(
534       continuation_lazy_now);
535   return main_thread_only().task_source->GetPendingWakeUp(continuation_lazy_now,
536                                                           select_task_option);
537 }
538 
RunsTasksByBatches() const539 bool ThreadControllerWithMessagePumpImpl::RunsTasksByBatches() const {
540   return can_run_tasks_by_batches_ &&
541          g_run_tasks_by_batches.load(std::memory_order_relaxed);
542 }
543 
DoIdleWork()544 bool ThreadControllerWithMessagePumpImpl::DoIdleWork() {
545   struct OnIdle {
546     OnIdle(const TickClock* time_source, RunLevelTracker& run_level_tracker_ref)
547         : lazy_now(time_source), run_level_tracker(run_level_tracker_ref) {}
548 
549     // Very last step before going idle, must be fast as this is hidden from the
550     // DoIdleWork trace event below.
551     ~OnIdle() { run_level_tracker->OnIdle(lazy_now); }
552 
553     LazyNow lazy_now;
554 
555    private:
556     const raw_ref<RunLevelTracker> run_level_tracker;
557   };
558   absl::optional<OnIdle> on_idle;
559 
560   // Must be after `on_idle` as this trace event's scope must end before the END
561   // of the "ThreadController active" trace event emitted from
562   // `run_level_tracker_.OnIdle()`.
563   TRACE_EVENT0("sequence_manager", "SequenceManager::DoIdleWork");
564 
565 #if BUILDFLAG(IS_WIN)
566   if (!power_monitor_.IsProcessInPowerSuspendState()) {
567     // Avoid calling Time::ActivateHighResolutionTimer() between
568     // suspend/resume as the system hangs if we do (crbug.com/1074028).
569     // OnResume() will generate a task on this thread per the
570     // ThreadControllerPowerMonitor observer and DoIdleWork() will thus get
571     // another chance to set the right high-resolution-timer-state before
572     // going to sleep after resume.
573 
574     const bool need_high_res_mode =
575         main_thread_only().task_source->HasPendingHighResolutionTasks();
576     if (main_thread_only().in_high_res_mode != need_high_res_mode) {
577       // On Windows we activate the high resolution timer so that the wait
578       // _if_ triggered by the timer happens with good resolution. If we don't
579       // do this the default resolution is 15ms which might not be acceptable
580       // for some tasks.
581       main_thread_only().in_high_res_mode = need_high_res_mode;
582       Time::ActivateHighResolutionTimer(need_high_res_mode);
583     }
584   }
585 #endif  // BUILDFLAG(IS_WIN)
586 
587   if (main_thread_only().task_source->OnSystemIdle()) {
588     // The OnSystemIdle() callback resulted in more immediate work, so schedule
589     // a DoWork callback. For some message pumps returning true from here is
590     // sufficient to do that but not on mac.
591     pump_->ScheduleWork();
592     return false;
593   }
594 
595   // This is mostly redundant with the identical call in BeforeWait (upcoming)
596   // but some uninstrumented MessagePump impls don't call BeforeWait so it must
597   // also be done here.
598   hang_watch_scope_.reset();
599 
600   // All return paths below are truly idle.
601   on_idle.emplace(time_source_, run_level_tracker_);
602 
603   // Check if any runloop timeout has expired.
604   if (main_thread_only().quit_runloop_after != TimeTicks::Max() &&
605       main_thread_only().quit_runloop_after <= on_idle->lazy_now.Now()) {
606     Quit();
607     return false;
608   }
609 
610   // RunLoop::Delegate knows whether we called Run() or RunUntilIdle().
611   if (ShouldQuitWhenIdle())
612     Quit();
613 
614   return false;
615 }
616 
RunDepth()617 int ThreadControllerWithMessagePumpImpl::RunDepth() {
618   return static_cast<int>(run_level_tracker_.num_run_levels());
619 }
620 
Run(bool application_tasks_allowed,TimeDelta timeout)621 void ThreadControllerWithMessagePumpImpl::Run(bool application_tasks_allowed,
622                                               TimeDelta timeout) {
623   DCHECK(RunsTasksInCurrentSequence());
624 
625   LazyNow lazy_now_run_loop_start(time_source_);
626 
627   // RunLoops can be nested so we need to restore the previous value of
628   // |quit_runloop_after| upon exit. NB we could use saturated arithmetic here
629   // but don't because we have some tests which assert the number of calls to
630   // Now.
631   AutoReset<TimeTicks> quit_runloop_after(
632       &main_thread_only().quit_runloop_after,
633       (timeout == TimeDelta::Max()) ? TimeTicks::Max()
634                                     : lazy_now_run_loop_start.Now() + timeout);
635 
636   run_level_tracker_.OnRunLoopStarted(RunLevelTracker::kInBetweenWorkItems,
637                                       lazy_now_run_loop_start);
638 
639   // Quit may have been called outside of a Run(), so |quit_pending| might be
640   // true here. We can't use InTopLevelDoWork() in Quit() as this call may be
641   // outside top-level DoWork but still in Run().
642   main_thread_only().quit_pending = false;
643   hang_watch_scope_.emplace();
644   if (application_tasks_allowed && !main_thread_only().task_execution_allowed) {
645     // Allow nested task execution as explicitly requested.
646     DCHECK(RunLoop::IsNestedOnCurrentThread());
647     main_thread_only().task_execution_allowed = true;
648     pump_->Run(this);
649     main_thread_only().task_execution_allowed = false;
650   } else {
651     pump_->Run(this);
652   }
653 
654   run_level_tracker_.OnRunLoopEnded();
655   main_thread_only().quit_pending = false;
656 
657   // If this was a nested loop, hang watch the remainder of the task which
658   // caused it. Otherwise, stop watching as we're no longer running.
659   if (RunLoop::IsNestedOnCurrentThread()) {
660     hang_watch_scope_.emplace();
661   } else {
662     hang_watch_scope_.reset();
663   }
664   work_id_provider_->IncrementWorkId();
665 }
666 
OnBeginNestedRunLoop()667 void ThreadControllerWithMessagePumpImpl::OnBeginNestedRunLoop() {
668   // We don't need to ScheduleWork here! That's because the call to pump_->Run()
669   // above, which is always called for RunLoop().Run(), guarantees a call to
670   // DoWork on all platforms.
671   if (main_thread_only().nesting_observer)
672     main_thread_only().nesting_observer->OnBeginNestedRunLoop();
673 }
674 
OnExitNestedRunLoop()675 void ThreadControllerWithMessagePumpImpl::OnExitNestedRunLoop() {
676   if (main_thread_only().nesting_observer)
677     main_thread_only().nesting_observer->OnExitNestedRunLoop();
678 }
679 
Quit()680 void ThreadControllerWithMessagePumpImpl::Quit() {
681   DCHECK(RunsTasksInCurrentSequence());
682   // Interrupt a batch of work.
683   main_thread_only().quit_pending = true;
684 
685   // If we're in a nested RunLoop, continuation will be posted if necessary.
686   pump_->Quit();
687 }
688 
EnsureWorkScheduled()689 void ThreadControllerWithMessagePumpImpl::EnsureWorkScheduled() {
690   if (work_deduplicator_.OnWorkRequested() ==
691       ShouldScheduleWork::kScheduleImmediate) {
692     pump_->ScheduleWork();
693   }
694 }
695 
SetTaskExecutionAllowed(bool allowed)696 void ThreadControllerWithMessagePumpImpl::SetTaskExecutionAllowed(
697     bool allowed) {
698   if (allowed) {
699     // We need to schedule work unconditionally because we might be about to
700     // enter an OS level nested message loop. Unlike a RunLoop().Run() we don't
701     // get a call to DoWork on entering for free.
702     work_deduplicator_.OnWorkRequested();  // Set the pending DoWork flag.
703     pump_->ScheduleWork();
704   } else {
705     // We've (probably) just left an OS level nested message loop. Make sure a
706     // subsequent PostTask within the same Task doesn't ScheduleWork with the
707     // pump (this will be done anyway when the task exits).
708     work_deduplicator_.OnWorkStarted();
709   }
710   main_thread_only().task_execution_allowed = allowed;
711 }
712 
IsTaskExecutionAllowed() const713 bool ThreadControllerWithMessagePumpImpl::IsTaskExecutionAllowed() const {
714   return main_thread_only().task_execution_allowed;
715 }
716 
GetBoundMessagePump() const717 MessagePump* ThreadControllerWithMessagePumpImpl::GetBoundMessagePump() const {
718   return pump_.get();
719 }
720 
PrioritizeYieldingToNative(base::TimeTicks prioritize_until)721 void ThreadControllerWithMessagePumpImpl::PrioritizeYieldingToNative(
722     base::TimeTicks prioritize_until) {
723   main_thread_only().yield_to_native_after_batch = prioritize_until;
724 }
725 
726 #if BUILDFLAG(IS_IOS)
AttachToMessagePump()727 void ThreadControllerWithMessagePumpImpl::AttachToMessagePump() {
728   static_cast<MessagePumpCFRunLoopBase*>(pump_.get())->Attach(this);
729 }
730 
DetachFromMessagePump()731 void ThreadControllerWithMessagePumpImpl::DetachFromMessagePump() {
732   static_cast<MessagePumpCFRunLoopBase*>(pump_.get())->Detach();
733 }
734 #elif BUILDFLAG(IS_ANDROID)
AttachToMessagePump()735 void ThreadControllerWithMessagePumpImpl::AttachToMessagePump() {
736   CHECK(main_thread_only().work_batch_size == 1);
737   // Aborting the message pump currently relies on the batch size being 1.
738   main_thread_only().can_change_batch_size = false;
739   static_cast<MessagePumpForUI*>(pump_.get())->Attach(this);
740 }
741 #endif
742 
ShouldQuitRunLoopWhenIdle()743 bool ThreadControllerWithMessagePumpImpl::ShouldQuitRunLoopWhenIdle() {
744   if (run_level_tracker_.num_run_levels() == 0)
745     return false;
746   // It's only safe to call ShouldQuitWhenIdle() when in a RunLoop.
747   return ShouldQuitWhenIdle();
748 }
749 
750 }  // namespace internal
751 }  // namespace sequence_manager
752 }  // namespace base
753