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().get();
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
205 return BindRepeating(
206 &ProxyToTaskRunner, origin_task_runner_,
207 BindRepeating(&RunLoop::Quit, weak_factory_.GetWeakPtr()));
208 }
209
QuitWhenIdleClosure()210 RepeatingClosure RunLoop::QuitWhenIdleClosure() {
211 // Obtaining the QuitWhenIdleClosure() is not thread-safe; either obtain the
212 // QuitWhenIdleClosure() from the owning thread before Run() or invoke
213 // QuitWhenIdle() directly (which is thread-safe).
214 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
215
216 return BindRepeating(
217 &ProxyToTaskRunner, origin_task_runner_,
218 BindRepeating(&RunLoop::QuitWhenIdle, weak_factory_.GetWeakPtr()));
219 }
220
AnyQuitCalled()221 bool RunLoop::AnyQuitCalled() {
222 return quit_called_ || quit_when_idle_called_;
223 }
224
225 // static
IsRunningOnCurrentThread()226 bool RunLoop::IsRunningOnCurrentThread() {
227 return delegate && !delegate->active_run_loops_.empty();
228 }
229
230 // static
IsNestedOnCurrentThread()231 bool RunLoop::IsNestedOnCurrentThread() {
232 return delegate && delegate->active_run_loops_.size() > 1;
233 }
234
235 // static
AddNestingObserverOnCurrentThread(NestingObserver * observer)236 void RunLoop::AddNestingObserverOnCurrentThread(NestingObserver* observer) {
237 DCHECK(delegate);
238 delegate->nesting_observers_.AddObserver(observer);
239 }
240
241 // static
RemoveNestingObserverOnCurrentThread(NestingObserver * observer)242 void RunLoop::RemoveNestingObserverOnCurrentThread(NestingObserver* observer) {
243 DCHECK(delegate);
244 delegate->nesting_observers_.RemoveObserver(observer);
245 }
246
247
248 #if DCHECK_IS_ON()
ScopedDisallowRunningRunLoop()249 ScopedDisallowRunningRunLoop::ScopedDisallowRunningRunLoop()
250 : current_delegate_(delegate),
251 previous_run_allowance_(current_delegate_ &&
252 current_delegate_->allow_running_for_testing_) {
253 if (current_delegate_)
254 current_delegate_->allow_running_for_testing_ = false;
255 }
256
~ScopedDisallowRunningRunLoop()257 ScopedDisallowRunningRunLoop::~ScopedDisallowRunningRunLoop() {
258 DCHECK_EQ(current_delegate_, delegate);
259 if (current_delegate_)
260 current_delegate_->allow_running_for_testing_ = previous_run_allowance_;
261 }
262 #else // DCHECK_IS_ON()
263 // Defined out of line so that the compiler doesn't inline these and realize
264 // the scope has no effect and then throws an "unused variable" warning in
265 // non-dcheck builds.
266 ScopedDisallowRunningRunLoop::ScopedDisallowRunningRunLoop() = default;
267 ScopedDisallowRunningRunLoop::~ScopedDisallowRunningRunLoop() = default;
268 #endif // DCHECK_IS_ON()
269
270 RunLoop::RunLoopTimeout::RunLoopTimeout() = default;
271
272 RunLoop::RunLoopTimeout::~RunLoopTimeout() = default;
273
274 // static
SetTimeoutForCurrentThread(const RunLoopTimeout * timeout)275 void RunLoop::SetTimeoutForCurrentThread(const RunLoopTimeout* timeout) {
276 run_loop_timeout = timeout;
277 }
278
279 // static
GetTimeoutForCurrentThread()280 const RunLoop::RunLoopTimeout* RunLoop::GetTimeoutForCurrentThread() {
281 // Workaround false-positive MSAN use-of-uninitialized-value on
282 // thread_local storage for loaded libraries:
283 // https://github.com/google/sanitizers/issues/1265
284 MSAN_UNPOISON(&run_loop_timeout, sizeof(RunLoopTimeout*));
285
286 return run_loop_timeout;
287 }
288
BeforeRun()289 bool RunLoop::BeforeRun() {
290 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
291
292 #if DCHECK_IS_ON()
293 DCHECK(delegate_->allow_running_for_testing_)
294 << "RunLoop::Run() isn't allowed in the scope of a "
295 "ScopedDisallowRunningRunLoop. Hint: if mixing "
296 "TestMockTimeTaskRunners on same thread, use TestMockTimeTaskRunner's "
297 "API instead of RunLoop to drive individual task runners.";
298 DCHECK(run_allowed_);
299 run_allowed_ = false;
300 #endif // DCHECK_IS_ON()
301
302 // Allow Quit to be called before Run.
303 if (quit_called_) {
304 TRACE_EVENT_WITH_FLOW0("toplevel.flow", "RunLoop_ExitedEarly",
305 TRACE_ID_LOCAL(this), TRACE_EVENT_FLAG_FLOW_IN);
306 return false;
307 }
308
309 auto& active_run_loops = delegate_->active_run_loops_;
310 active_run_loops.push(this);
311
312 const bool is_nested = active_run_loops.size() > 1;
313
314 if (is_nested) {
315 for (auto& observer : delegate_->nesting_observers_)
316 observer.OnBeginNestedRunLoop();
317 if (type_ == Type::kNestableTasksAllowed)
318 delegate_->EnsureWorkScheduled();
319 }
320
321 running_ = true;
322 return true;
323 }
324
AfterRun()325 void RunLoop::AfterRun() {
326 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
327
328 running_ = false;
329
330 TRACE_EVENT_WITH_FLOW0("toplevel.flow", "RunLoop_Exited",
331 TRACE_ID_LOCAL(this), TRACE_EVENT_FLAG_FLOW_IN);
332
333 auto& active_run_loops = delegate_->active_run_loops_;
334 DCHECK_EQ(active_run_loops.top(), this);
335 active_run_loops.pop();
336
337 // Exiting a nested RunLoop?
338 if (!active_run_loops.empty()) {
339 for (auto& observer : delegate_->nesting_observers_)
340 observer.OnExitNestedRunLoop();
341
342 // Execute deferred Quit, if any:
343 if (active_run_loops.top()->quit_called_)
344 delegate_->Quit();
345 }
346 }
347
348 } // namespace base
349