1 // Copyright 2017 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_impl.h"
6
7 #include <algorithm>
8
9 #include "base/functional/bind.h"
10 #include "base/memory/ptr_util.h"
11 #include "base/message_loop/message_pump.h"
12 #include "base/notreached.h"
13 #include "base/run_loop.h"
14 #include "base/task/common/lazy_now.h"
15 #include "base/task/sequence_manager/sequence_manager_impl.h"
16 #include "base/task/sequence_manager/sequenced_task_source.h"
17 #include "base/trace_event/base_tracing.h"
18 #include "build/build_config.h"
19
20 namespace base {
21 namespace sequence_manager {
22 namespace internal {
23
24 using ShouldScheduleWork = WorkDeduplicator::ShouldScheduleWork;
25
ThreadControllerImpl(SequenceManagerImpl * funneled_sequence_manager,scoped_refptr<SingleThreadTaskRunner> task_runner,const TickClock * time_source)26 ThreadControllerImpl::ThreadControllerImpl(
27 SequenceManagerImpl* funneled_sequence_manager,
28 scoped_refptr<SingleThreadTaskRunner> task_runner,
29 const TickClock* time_source)
30 : ThreadController(time_source),
31 funneled_sequence_manager_(funneled_sequence_manager),
32 task_runner_(task_runner),
33 message_loop_task_runner_(funneled_sequence_manager
34 ? funneled_sequence_manager->GetTaskRunner()
35 : nullptr),
36 work_deduplicator_(associated_thread_) {
37 if (task_runner_ || funneled_sequence_manager_)
38 work_deduplicator_.BindToCurrentThread();
39 immediate_do_work_closure_ =
40 BindRepeating(&ThreadControllerImpl::DoWork, weak_factory_.GetWeakPtr(),
41 WorkType::kImmediate);
42 delayed_do_work_closure_ =
43 BindRepeating(&ThreadControllerImpl::DoWork, weak_factory_.GetWeakPtr(),
44 WorkType::kDelayed);
45
46 // Unlike ThreadControllerWithMessagePumpImpl, ThreadControllerImpl isn't
47 // explicitly Run(). Rather, DoWork() will be invoked at some point in the
48 // future when the associated thread begins pumping messages.
49 LazyNow lazy_now(time_source_);
50 run_level_tracker_.OnRunLoopStarted(RunLevelTracker::kIdle, lazy_now);
51 }
52
~ThreadControllerImpl()53 ThreadControllerImpl::~ThreadControllerImpl() {
54 // Balances OnRunLoopStarted() in the constructor to satisfy the exit criteria
55 // of ~RunLevelTracker().
56 run_level_tracker_.OnRunLoopEnded();
57 }
58
59 ThreadControllerImpl::MainSequenceOnly::MainSequenceOnly() = default;
60
61 ThreadControllerImpl::MainSequenceOnly::~MainSequenceOnly() = default;
62
Create(SequenceManagerImpl * funneled_sequence_manager,const TickClock * time_source)63 std::unique_ptr<ThreadControllerImpl> ThreadControllerImpl::Create(
64 SequenceManagerImpl* funneled_sequence_manager,
65 const TickClock* time_source) {
66 return WrapUnique(new ThreadControllerImpl(
67 funneled_sequence_manager,
68 funneled_sequence_manager ? funneled_sequence_manager->GetTaskRunner()
69 : nullptr,
70 time_source));
71 }
72
SetSequencedTaskSource(SequencedTaskSource * sequence)73 void ThreadControllerImpl::SetSequencedTaskSource(
74 SequencedTaskSource* sequence) {
75 DCHECK_CALLED_ON_VALID_SEQUENCE(associated_thread_->sequence_checker);
76 DCHECK(sequence);
77 DCHECK(!sequence_);
78 sequence_ = sequence;
79 }
80
ScheduleWork()81 void ThreadControllerImpl::ScheduleWork() {
82 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("sequence_manager"),
83 "ThreadControllerImpl::ScheduleWork::PostTask");
84
85 if (work_deduplicator_.OnWorkRequested() ==
86 ShouldScheduleWork::kScheduleImmediate) {
87 task_runner_->PostTask(FROM_HERE, immediate_do_work_closure_);
88 }
89 }
90
SetNextDelayedDoWork(LazyNow * lazy_now,absl::optional<WakeUp> wake_up)91 void ThreadControllerImpl::SetNextDelayedDoWork(
92 LazyNow* lazy_now,
93 absl::optional<WakeUp> wake_up) {
94 DCHECK_CALLED_ON_VALID_SEQUENCE(associated_thread_->sequence_checker);
95 DCHECK(sequence_);
96 DCHECK(!wake_up || !wake_up->is_immediate());
97
98 // Cancel DoWork if it was scheduled and we set an "infinite" delay now.
99 if (!wake_up) {
100 if (!main_sequence_only().next_delayed_do_work.is_max()) {
101 cancelable_delayed_do_work_closure_.Cancel();
102 main_sequence_only().next_delayed_do_work = TimeTicks::Max();
103 }
104 return;
105 }
106
107 if (work_deduplicator_.OnDelayedWorkRequested() ==
108 ShouldScheduleWork::kNotNeeded) {
109 return;
110 }
111
112 if (main_sequence_only().next_delayed_do_work == wake_up->time)
113 return;
114
115 base::TimeDelta delay =
116 std::max(TimeDelta(), wake_up->time - lazy_now->Now());
117 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("sequence_manager"),
118 "ThreadControllerImpl::SetNextDelayedDoWork::PostDelayedTask",
119 "delay_ms", delay.InMillisecondsF());
120
121 main_sequence_only().next_delayed_do_work = wake_up->time;
122 // Reset also causes cancellation of the previous DoWork task.
123 cancelable_delayed_do_work_closure_.Reset(delayed_do_work_closure_);
124 task_runner_->PostDelayedTask(
125 FROM_HERE, cancelable_delayed_do_work_closure_.callback(), delay);
126 }
127
RunsTasksInCurrentSequence()128 bool ThreadControllerImpl::RunsTasksInCurrentSequence() {
129 return task_runner_->RunsTasksInCurrentSequence();
130 }
131
SetDefaultTaskRunner(scoped_refptr<SingleThreadTaskRunner> task_runner)132 void ThreadControllerImpl::SetDefaultTaskRunner(
133 scoped_refptr<SingleThreadTaskRunner> task_runner) {
134 #if DCHECK_IS_ON()
135 default_task_runner_set_ = true;
136 #endif
137 if (!funneled_sequence_manager_)
138 return;
139 funneled_sequence_manager_->SetTaskRunner(task_runner);
140 }
141
142 scoped_refptr<SingleThreadTaskRunner>
GetDefaultTaskRunner()143 ThreadControllerImpl::GetDefaultTaskRunner() {
144 return funneled_sequence_manager_->GetTaskRunner();
145 }
146
RestoreDefaultTaskRunner()147 void ThreadControllerImpl::RestoreDefaultTaskRunner() {
148 if (!funneled_sequence_manager_)
149 return;
150 funneled_sequence_manager_->SetTaskRunner(message_loop_task_runner_);
151 }
152
BindToCurrentThread(std::unique_ptr<MessagePump> message_pump)153 void ThreadControllerImpl::BindToCurrentThread(
154 std::unique_ptr<MessagePump> message_pump) {
155 NOTREACHED();
156 }
157
WillQueueTask(PendingTask * pending_task)158 void ThreadControllerImpl::WillQueueTask(PendingTask* pending_task) {
159 task_annotator_.WillQueueTask("SequenceManager PostTask", pending_task);
160 }
161
DoWork(WorkType work_type)162 void ThreadControllerImpl::DoWork(WorkType work_type) {
163 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("sequence_manager"),
164 "ThreadControllerImpl::DoWork");
165
166 DCHECK_CALLED_ON_VALID_SEQUENCE(associated_thread_->sequence_checker);
167 DCHECK(sequence_);
168
169 work_deduplicator_.OnWorkStarted();
170 absl::optional<base::TimeTicks> recent_time;
171
172 WeakPtr<ThreadControllerImpl> weak_ptr = weak_factory_.GetWeakPtr();
173 for (int i = 0; i < main_sequence_only().work_batch_size_; i++) {
174 LazyNow lazy_now_select_task(recent_time, time_source_);
175
176 // Include SelectNextTask() in the scope of the work item. This ensures
177 // it's covered in tracing and hang reports. This is particularly
178 // important when SelectNextTask() finds no work immediately after a
179 // wakeup, otherwise the power-inefficient wakeup is invisible in
180 // tracing. OnApplicationTaskSelected() assumes this ordering as well.
181 DCHECK_GT(run_level_tracker_.num_run_levels(), 0U);
182 run_level_tracker_.OnWorkStarted(lazy_now_select_task);
183 int run_depth = static_cast<int>(run_level_tracker_.num_run_levels());
184
185 absl::optional<SequencedTaskSource::SelectedTask> selected_task =
186 sequence_->SelectNextTask(lazy_now_select_task);
187 LazyNow lazy_now_task_selected(time_source_);
188 run_level_tracker_.OnApplicationTaskSelected(
189 (selected_task && selected_task->task.delayed_run_time.is_null())
190 ? selected_task->task.queue_time
191 : TimeTicks(),
192 lazy_now_task_selected);
193 if (!selected_task) {
194 run_level_tracker_.OnWorkEnded(lazy_now_task_selected, run_depth);
195 break;
196 }
197
198 {
199 // Trace-parsing tools (DevTools, Lighthouse, etc) consume this event
200 // to determine long tasks.
201 // See https://crbug.com/681863 and https://crbug.com/874982
202 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "RunTask");
203
204 // Note: all arguments after task are just passed to a TRACE_EVENT for
205 // logging so lambda captures are safe as lambda is executed inline.
206 SequencedTaskSource* source = sequence_;
207 task_annotator_.RunTask(
208 "ThreadControllerImpl::RunTask", selected_task->task,
209 [&selected_task, &source](perfetto::EventContext& ctx) {
210 if (selected_task->task_execution_trace_logger)
211 selected_task->task_execution_trace_logger.Run(
212 ctx, selected_task->task);
213 source->MaybeEmitTaskDetails(ctx, *selected_task);
214 });
215 if (!weak_ptr)
216 return;
217
218 // This processes microtasks, hence all scoped operations above must end
219 // after it.
220 LazyNow lazy_now_after_run_task(time_source_);
221 sequence_->DidRunTask(lazy_now_after_run_task);
222 run_level_tracker_.OnWorkEnded(lazy_now_after_run_task, run_depth);
223
224 // If DidRunTask() read the clock (lazy_now_after_run_task.has_value()),
225 // store it in `recent_time` so it can be reused by SelectNextTask() at
226 // the next loop iteration.
227 if (lazy_now_after_run_task.has_value()) {
228 recent_time =
229 absl::optional<base::TimeTicks>(lazy_now_after_run_task.Now());
230 } else {
231 recent_time.reset();
232 }
233 }
234
235 // NOTE: https://crbug.com/828835.
236 // When we're running inside a nested RunLoop it may quit anytime, so any
237 // outstanding pending tasks must run in the outer RunLoop
238 // (see SequenceManagerTestWithMessageLoop.QuitWhileNested test).
239 // Unfortunately, it's MessageLoop who's receiving that signal and we can't
240 // know it before we return from DoWork, hence, OnExitNestedRunLoop
241 // will be called later. Since we must implement ThreadController and
242 // SequenceManager in conformance with MessageLoop task runners, we need
243 // to disable this batching optimization while nested.
244 // Implementing MessagePump::Delegate ourselves will help to resolve this
245 // issue.
246 if (run_level_tracker_.num_run_levels() > 1)
247 break;
248 }
249
250 work_deduplicator_.WillCheckForMoreWork();
251
252 LazyNow lazy_now_after_work(time_source_);
253 absl::optional<WakeUp> next_wake_up =
254 sequence_->GetPendingWakeUp(&lazy_now_after_work);
255 // The OnSystemIdle callback allows the TimeDomains to advance virtual time
256 // in which case we now have immediate work to do.
257 if ((next_wake_up && next_wake_up->is_immediate()) ||
258 sequence_->OnSystemIdle()) {
259 // The next task needs to run immediately, post a continuation if
260 // another thread didn't get there first.
261 if (work_deduplicator_.DidCheckForMoreWork(
262 WorkDeduplicator::NextTask::kIsImmediate) ==
263 ShouldScheduleWork::kScheduleImmediate) {
264 task_runner_->PostTask(FROM_HERE, immediate_do_work_closure_);
265 }
266 return;
267 }
268
269 // It looks like we have a non-zero delay, however another thread may have
270 // posted an immediate task while we computed the delay.
271 if (work_deduplicator_.DidCheckForMoreWork(
272 WorkDeduplicator::NextTask::kIsDelayed) ==
273 ShouldScheduleWork::kScheduleImmediate) {
274 task_runner_->PostTask(FROM_HERE, immediate_do_work_closure_);
275 return;
276 }
277
278 // No more immediate work.
279 run_level_tracker_.OnIdle(lazy_now_after_work);
280
281 // Any future work?
282 if (!next_wake_up) {
283 main_sequence_only().next_delayed_do_work = TimeTicks::Max();
284 cancelable_delayed_do_work_closure_.Cancel();
285 return;
286 }
287
288 TimeTicks next_wake_up_time = next_wake_up->time;
289 // Already requested next delay?
290 if (next_wake_up_time == main_sequence_only().next_delayed_do_work)
291 return;
292
293 // Schedule a callback after |delay_till_next_task| and cancel any previous
294 // callback.
295 main_sequence_only().next_delayed_do_work = next_wake_up_time;
296 cancelable_delayed_do_work_closure_.Reset(delayed_do_work_closure_);
297 // TODO(1153139): Use PostDelayedTaskAt().
298 task_runner_->PostDelayedTask(FROM_HERE,
299 cancelable_delayed_do_work_closure_.callback(),
300 next_wake_up_time - lazy_now_after_work.Now());
301 }
302
AddNestingObserver(RunLoop::NestingObserver * observer)303 void ThreadControllerImpl::AddNestingObserver(
304 RunLoop::NestingObserver* observer) {
305 DCHECK_CALLED_ON_VALID_SEQUENCE(associated_thread_->sequence_checker);
306 nesting_observer_ = observer;
307 RunLoop::AddNestingObserverOnCurrentThread(this);
308 }
309
RemoveNestingObserver(RunLoop::NestingObserver * observer)310 void ThreadControllerImpl::RemoveNestingObserver(
311 RunLoop::NestingObserver* observer) {
312 DCHECK_CALLED_ON_VALID_SEQUENCE(associated_thread_->sequence_checker);
313 DCHECK_EQ(observer, nesting_observer_);
314 nesting_observer_ = nullptr;
315 RunLoop::RemoveNestingObserverOnCurrentThread(this);
316 }
317
OnBeginNestedRunLoop()318 void ThreadControllerImpl::OnBeginNestedRunLoop() {
319 LazyNow lazy_now(time_source_);
320 run_level_tracker_.OnRunLoopStarted(RunLevelTracker::kInBetweenWorkItems,
321 lazy_now);
322
323 // Just assume we have a pending task and post a DoWork to make sure we don't
324 // grind to a halt while nested.
325 work_deduplicator_.OnWorkRequested(); // Set the pending DoWork flag.
326 task_runner_->PostTask(FROM_HERE, immediate_do_work_closure_);
327
328 if (nesting_observer_)
329 nesting_observer_->OnBeginNestedRunLoop();
330 }
331
OnExitNestedRunLoop()332 void ThreadControllerImpl::OnExitNestedRunLoop() {
333 if (nesting_observer_)
334 nesting_observer_->OnExitNestedRunLoop();
335 run_level_tracker_.OnRunLoopEnded();
336 }
337
SetWorkBatchSize(int work_batch_size)338 void ThreadControllerImpl::SetWorkBatchSize(int work_batch_size) {
339 main_sequence_only().work_batch_size_ = work_batch_size;
340 }
341
SetTaskExecutionAllowed(bool allowed)342 void ThreadControllerImpl::SetTaskExecutionAllowed(bool allowed) {
343 NOTREACHED();
344 }
345
IsTaskExecutionAllowed() const346 bool ThreadControllerImpl::IsTaskExecutionAllowed() const {
347 return true;
348 }
349
ShouldQuitRunLoopWhenIdle()350 bool ThreadControllerImpl::ShouldQuitRunLoopWhenIdle() {
351 // The MessageLoop does not expose the API needed to support this query.
352 return false;
353 }
354
GetBoundMessagePump() const355 MessagePump* ThreadControllerImpl::GetBoundMessagePump() const {
356 return nullptr;
357 }
358
359 #if BUILDFLAG(IS_IOS) || BUILDFLAG(IS_ANDROID)
AttachToMessagePump()360 void ThreadControllerImpl::AttachToMessagePump() {
361 NOTREACHED();
362 }
363 #endif // BUILDFLAG(IS_IOS) || BUILDFLAG(IS_ANDROID)
364
365 #if BUILDFLAG(IS_IOS)
DetachFromMessagePump()366 void ThreadControllerImpl::DetachFromMessagePump() {
367 NOTREACHED();
368 }
369 #endif // BUILDFLAG(IS_IOS)
370
PrioritizeYieldingToNative(base::TimeTicks)371 void ThreadControllerImpl::PrioritizeYieldingToNative(base::TimeTicks) {
372 NOTREACHED();
373 }
374
375 } // namespace internal
376 } // namespace sequence_manager
377 } // namespace base
378