xref: /aosp_15_r20/external/cronet/base/task/sequence_manager/thread_controller_impl.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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