• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 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/run_loop.h"
6 
7 #include "base/cancelable_callback.h"
8 #include "base/check.h"
9 #include "base/compiler_specific.h"
10 #include "base/functional/bind.h"
11 #include "base/functional/callback.h"
12 #include "base/observer_list.h"
13 #include "base/task/single_thread_task_runner.h"
14 #include "base/trace_event/base_tracing.h"
15 #include "build/build_config.h"
16 
17 namespace base {
18 
19 namespace {
20 
21 constinit thread_local RunLoop::Delegate* delegate = nullptr;
22 constinit thread_local const RunLoop::RunLoopTimeout* run_loop_timeout =
23     nullptr;
24 
25 // Runs |closure| immediately if this is called on |task_runner|, otherwise
26 // forwards |closure| to it.
ProxyToTaskRunner(scoped_refptr<SequencedTaskRunner> task_runner,OnceClosure closure)27 void ProxyToTaskRunner(scoped_refptr<SequencedTaskRunner> task_runner,
28                        OnceClosure closure) {
29   if (task_runner->RunsTasksInCurrentSequence()) {
30     std::move(closure).Run();
31     return;
32   }
33   task_runner->PostTask(FROM_HERE, std::move(closure));
34 }
35 
OnRunLoopTimeout(RunLoop * run_loop,const Location & location,OnceCallback<void (const Location &)> on_timeout)36 void OnRunLoopTimeout(RunLoop* run_loop,
37                       const Location& location,
38                       OnceCallback<void(const Location&)> on_timeout) {
39   run_loop->Quit();
40   std::move(on_timeout).Run(location);
41 }
42 
43 }  // namespace
44 
Delegate()45 RunLoop::Delegate::Delegate() {
46   // The Delegate can be created on another thread. It is only bound in
47   // RegisterDelegateForCurrentThread().
48   DETACH_FROM_THREAD(bound_thread_checker_);
49 }
50 
~Delegate()51 RunLoop::Delegate::~Delegate() {
52   DCHECK_CALLED_ON_VALID_THREAD(bound_thread_checker_);
53   DCHECK(active_run_loops_.empty());
54   // A RunLoop::Delegate may be destroyed before it is bound, if so it may still
55   // be on its creation thread (e.g. a Thread that fails to start) and
56   // shouldn't disrupt that thread's state.
57   if (bound_) {
58     DCHECK_EQ(this, delegate);
59     delegate = nullptr;
60   }
61 }
62 
ShouldQuitWhenIdle()63 bool RunLoop::Delegate::ShouldQuitWhenIdle() {
64   const auto* top_loop = active_run_loops_.top().get();
65   if (top_loop->quit_when_idle_) {
66     TRACE_EVENT_WITH_FLOW0("toplevel.flow", "RunLoop_ExitedOnIdle",
67                            TRACE_ID_LOCAL(top_loop), TRACE_EVENT_FLAG_FLOW_IN);
68     return true;
69   }
70   return false;
71 }
72 
73 // static
RegisterDelegateForCurrentThread(Delegate * new_delegate)74 void RunLoop::RegisterDelegateForCurrentThread(Delegate* new_delegate) {
75   // Bind |delegate| to this thread.
76   DCHECK(!new_delegate->bound_);
77   DCHECK_CALLED_ON_VALID_THREAD(new_delegate->bound_thread_checker_);
78 
79   // There can only be one RunLoop::Delegate per thread.
80   DCHECK(!delegate)
81       << "Error: Multiple RunLoop::Delegates registered on the same thread.\n\n"
82          "Hint: You perhaps instantiated a second "
83          "MessageLoop/TaskEnvironment on a thread that already had one?";
84   delegate = new_delegate;
85   delegate->bound_ = true;
86 }
87 
RunLoop(Type type)88 RunLoop::RunLoop(Type type)
89     : delegate_(delegate),
90       type_(type),
91       origin_task_runner_(SingleThreadTaskRunner::GetCurrentDefault()) {
92   DCHECK(delegate_) << "A RunLoop::Delegate must be bound to this thread prior "
93                        "to using RunLoop.";
94   DCHECK(origin_task_runner_);
95 }
96 
~RunLoop()97 RunLoop::~RunLoop() {
98   // ~RunLoop() must happen-after the RunLoop is done running but it doesn't
99   // have to be on |sequence_checker_| (it usually is but sometimes it can be a
100   // member of a RefCountedThreadSafe object and be destroyed on another thread
101   // after being quit).
102   DCHECK(!running_);
103 }
104 
Run(const Location & location)105 void RunLoop::Run(const Location& location) {
106   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
107   // "test" tracing category is used here because in regular scenarios RunLoop
108   // trace events are not useful (each process normally has one RunLoop covering
109   // its entire lifetime) and might be confusing (they make idle processes look
110   // non-idle). In tests, however, creating a RunLoop is a frequent and an
111   // explicit action making this trace event very useful.
112   TRACE_EVENT("test", "RunLoop::Run", "location", location);
113 
114   if (!BeforeRun())
115     return;
116 
117   // If there is a RunLoopTimeout active then set the timeout.
118   // TODO(crbug.com/40602467): Use real-time for Run() timeouts so that they
119   // can be applied even in tests which mock TimeTicks::Now().
120   CancelableOnceClosure cancelable_timeout;
121   const RunLoopTimeout* run_timeout = GetTimeoutForCurrentThread();
122   if (run_timeout) {
123     cancelable_timeout.Reset(BindOnce(&OnRunLoopTimeout, Unretained(this),
124                                       location, run_timeout->on_timeout));
125     origin_task_runner_->PostDelayedTask(
126         FROM_HERE, cancelable_timeout.callback(), run_timeout->timeout);
127   }
128 
129   DCHECK_EQ(this, delegate_->active_run_loops_.top());
130   const bool application_tasks_allowed =
131       delegate_->active_run_loops_.size() == 1U ||
132       type_ == Type::kNestableTasksAllowed;
133   delegate_->Run(application_tasks_allowed, TimeDelta::Max());
134 
135   AfterRun();
136 }
137 
RunUntilIdle()138 void RunLoop::RunUntilIdle() {
139   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
140 
141   quit_when_idle_ = true;
142   Run();
143 
144   if (!AnyQuitCalled()) {
145     quit_when_idle_ = false;
146 #if DCHECK_IS_ON()
147     run_allowed_ = true;
148 #endif
149   }
150 }
151 
Quit()152 void RunLoop::Quit() {
153   // Thread-safe.
154 
155   // This can only be hit if RunLoop::Quit() is called directly (QuitClosure()
156   // proxies through ProxyToTaskRunner() as it can only deref its WeakPtr on
157   // |origin_task_runner_|).
158   if (!origin_task_runner_->RunsTasksInCurrentSequence()) {
159     origin_task_runner_->PostTask(FROM_HERE,
160                                   BindOnce(&RunLoop::Quit, Unretained(this)));
161     return;
162   }
163 
164   // While Quit() is an "OUT" call to reach one of the quit-states ("IN"),
165   // OUT|IN is used to visually link multiple Quit*() together which can help
166   // when debugging flaky tests.
167   TRACE_EVENT_WITH_FLOW0("toplevel.flow", "RunLoop::Quit", TRACE_ID_LOCAL(this),
168                          TRACE_EVENT_FLAG_FLOW_OUT | TRACE_EVENT_FLAG_FLOW_IN);
169 
170   quit_called_ = true;
171   if (running_ && delegate_->active_run_loops_.top() == this) {
172     // This is the inner-most RunLoop, so quit now.
173     delegate_->Quit();
174   }
175 }
176 
QuitWhenIdle()177 void RunLoop::QuitWhenIdle() {
178   // Thread-safe.
179 
180   // This can only be hit if RunLoop::QuitWhenIdle() is called directly
181   // (QuitWhenIdleClosure() proxies through ProxyToTaskRunner() as it can only
182   // deref its WeakPtr on |origin_task_runner_|).
183   if (!origin_task_runner_->RunsTasksInCurrentSequence()) {
184     origin_task_runner_->PostTask(
185         FROM_HERE, BindOnce(&RunLoop::QuitWhenIdle, Unretained(this)));
186     return;
187   }
188 
189   // OUT|IN as in Quit() to link all Quit*() together should there be multiple.
190   TRACE_EVENT_WITH_FLOW0("toplevel.flow", "RunLoop::QuitWhenIdle",
191                          TRACE_ID_LOCAL(this),
192                          TRACE_EVENT_FLAG_FLOW_OUT | TRACE_EVENT_FLAG_FLOW_IN);
193 
194   quit_when_idle_ = true;
195   quit_when_idle_called_ = true;
196 }
197 
QuitClosure()198 RepeatingClosure RunLoop::QuitClosure() & {
199   // Obtaining the QuitClosure() is not thread-safe; either obtain the
200   // QuitClosure() from the owning thread before Run() or invoke Quit() directly
201   // (which is thread-safe).
202   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
203 
204   return BindRepeating(
205       &ProxyToTaskRunner, origin_task_runner_,
206       BindRepeating(&RunLoop::Quit, weak_factory_.GetWeakPtr()));
207 }
208 
QuitWhenIdleClosure()209 RepeatingClosure RunLoop::QuitWhenIdleClosure() & {
210   // Obtaining the QuitWhenIdleClosure() is not thread-safe; either obtain the
211   // QuitWhenIdleClosure() from the owning thread before Run() or invoke
212   // QuitWhenIdle() directly (which is thread-safe).
213   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
214 
215   return BindRepeating(
216       &ProxyToTaskRunner, origin_task_runner_,
217       BindRepeating(&RunLoop::QuitWhenIdle, weak_factory_.GetWeakPtr()));
218 }
219 
AnyQuitCalled()220 bool RunLoop::AnyQuitCalled() {
221   return quit_called_ || quit_when_idle_called_;
222 }
223 
224 // static
IsRunningOnCurrentThread()225 bool RunLoop::IsRunningOnCurrentThread() {
226   return delegate && !delegate->active_run_loops_.empty();
227 }
228 
229 // static
IsNestedOnCurrentThread()230 bool RunLoop::IsNestedOnCurrentThread() {
231   return delegate && delegate->active_run_loops_.size() > 1;
232 }
233 
234 // static
AddNestingObserverOnCurrentThread(NestingObserver * observer)235 void RunLoop::AddNestingObserverOnCurrentThread(NestingObserver* observer) {
236   DCHECK(delegate);
237   delegate->nesting_observers_.AddObserver(observer);
238 }
239 
240 // static
RemoveNestingObserverOnCurrentThread(NestingObserver * observer)241 void RunLoop::RemoveNestingObserverOnCurrentThread(NestingObserver* observer) {
242   DCHECK(delegate);
243   delegate->nesting_observers_.RemoveObserver(observer);
244 }
245 
246 
247 #if DCHECK_IS_ON()
ScopedDisallowRunningRunLoop()248 ScopedDisallowRunningRunLoop::ScopedDisallowRunningRunLoop()
249     : current_delegate_(delegate),
250       previous_run_allowance_(current_delegate_ &&
251                               current_delegate_->allow_running_for_testing_) {
252   if (current_delegate_)
253     current_delegate_->allow_running_for_testing_ = false;
254 }
255 
~ScopedDisallowRunningRunLoop()256 ScopedDisallowRunningRunLoop::~ScopedDisallowRunningRunLoop() {
257   DCHECK_EQ(current_delegate_, delegate);
258   if (current_delegate_)
259     current_delegate_->allow_running_for_testing_ = previous_run_allowance_;
260 }
261 #else   // DCHECK_IS_ON()
262 // Defined out of line so that the compiler doesn't inline these and realize
263 // the scope has no effect and then throws an "unused variable" warning in
264 // non-dcheck builds.
265 ScopedDisallowRunningRunLoop::ScopedDisallowRunningRunLoop() = default;
266 ScopedDisallowRunningRunLoop::~ScopedDisallowRunningRunLoop() = default;
267 #endif  // DCHECK_IS_ON()
268 
269 RunLoop::RunLoopTimeout::RunLoopTimeout() = default;
270 
271 RunLoop::RunLoopTimeout::~RunLoopTimeout() = default;
272 
273 // static
SetTimeoutForCurrentThread(const RunLoopTimeout * timeout)274 void RunLoop::SetTimeoutForCurrentThread(const RunLoopTimeout* timeout) {
275   run_loop_timeout = timeout;
276 }
277 
278 // static
GetTimeoutForCurrentThread()279 const RunLoop::RunLoopTimeout* RunLoop::GetTimeoutForCurrentThread() {
280   // Workaround false-positive MSAN use-of-uninitialized-value on
281   // thread_local storage for loaded libraries:
282   // https://github.com/google/sanitizers/issues/1265
283   MSAN_UNPOISON(&run_loop_timeout, sizeof(RunLoopTimeout*));
284 
285   return run_loop_timeout;
286 }
287 
BeforeRun()288 bool RunLoop::BeforeRun() {
289   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
290 
291 #if DCHECK_IS_ON()
292   DCHECK(delegate_->allow_running_for_testing_)
293       << "RunLoop::Run() isn't allowed in the scope of a "
294          "ScopedDisallowRunningRunLoop. Hint: if mixing "
295          "TestMockTimeTaskRunners on same thread, use TestMockTimeTaskRunner's "
296          "API instead of RunLoop to drive individual task runners.";
297   DCHECK(run_allowed_);
298   run_allowed_ = false;
299 #endif  // DCHECK_IS_ON()
300 
301   // Allow Quit to be called before Run.
302   if (quit_called_) {
303     TRACE_EVENT_WITH_FLOW0("toplevel.flow", "RunLoop_ExitedEarly",
304                            TRACE_ID_LOCAL(this), TRACE_EVENT_FLAG_FLOW_IN);
305     return false;
306   }
307 
308   auto& active_run_loops = delegate_->active_run_loops_;
309   active_run_loops.push(this);
310 
311   const bool is_nested = active_run_loops.size() > 1;
312 
313   if (is_nested) {
314     for (auto& observer : delegate_->nesting_observers_)
315       observer.OnBeginNestedRunLoop();
316     if (type_ == Type::kNestableTasksAllowed)
317       delegate_->EnsureWorkScheduled();
318   }
319 
320   running_ = true;
321   return true;
322 }
323 
AfterRun()324 void RunLoop::AfterRun() {
325   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
326 
327   running_ = false;
328 
329   TRACE_EVENT_WITH_FLOW0("toplevel.flow", "RunLoop_Exited",
330                          TRACE_ID_LOCAL(this), TRACE_EVENT_FLAG_FLOW_IN);
331 
332   auto& active_run_loops = delegate_->active_run_loops_;
333   DCHECK_EQ(active_run_loops.top(), this);
334   active_run_loops.pop();
335 
336   // Exiting a nested RunLoop?
337   if (!active_run_loops.empty()) {
338     for (auto& observer : delegate_->nesting_observers_)
339       observer.OnExitNestedRunLoop();
340 
341     // Execute deferred Quit, if any:
342     if (active_run_loops.top()->quit_called_)
343       delegate_->Quit();
344   }
345 }
346 
347 }  // namespace base
348