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