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