1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
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/bind.h"
8 #include "base/callback.h"
9 #include "base/lazy_instance.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/single_thread_task_runner.h"
12 #include "base/threading/thread_local.h"
13 #include "base/threading/thread_task_runner_handle.h"
14 #include "build/build_config.h"
15
16 namespace base {
17
18 namespace {
19
20 LazyInstance<ThreadLocalPointer<RunLoop::Delegate>>::Leaky tls_delegate =
21 LAZY_INSTANCE_INITIALIZER;
22
23 // Runs |closure| immediately if this is called on |task_runner|, otherwise
24 // forwards |closure| to it.
ProxyToTaskRunner(scoped_refptr<SequencedTaskRunner> task_runner,OnceClosure closure)25 void ProxyToTaskRunner(scoped_refptr<SequencedTaskRunner> task_runner,
26 OnceClosure closure) {
27 if (task_runner->RunsTasksInCurrentSequence()) {
28 std::move(closure).Run();
29 return;
30 }
31 task_runner->PostTask(FROM_HERE, std::move(closure));
32 }
33
34 } // namespace
35
Delegate()36 RunLoop::Delegate::Delegate() {
37 // The Delegate can be created on another thread. It is only bound in
38 // RegisterDelegateForCurrentThread().
39 DETACH_FROM_THREAD(bound_thread_checker_);
40 }
41
~Delegate()42 RunLoop::Delegate::~Delegate() {
43 DCHECK_CALLED_ON_VALID_THREAD(bound_thread_checker_);
44 // A RunLoop::Delegate may be destroyed before it is bound, if so it may still
45 // be on its creation thread (e.g. a Thread that fails to start) and
46 // shouldn't disrupt that thread's state.
47 if (bound_)
48 tls_delegate.Get().Set(nullptr);
49 }
50
ShouldQuitWhenIdle()51 bool RunLoop::Delegate::ShouldQuitWhenIdle() {
52 return active_run_loops_.top()->quit_when_idle_received_;
53 }
54
55 // static
RegisterDelegateForCurrentThread(Delegate * delegate)56 void RunLoop::RegisterDelegateForCurrentThread(Delegate* delegate) {
57 // Bind |delegate| to this thread.
58 DCHECK(!delegate->bound_);
59 DCHECK_CALLED_ON_VALID_THREAD(delegate->bound_thread_checker_);
60
61 // There can only be one RunLoop::Delegate per thread.
62 DCHECK(!tls_delegate.Get().Get())
63 << "Error: Multiple RunLoop::Delegates registered on the same thread.\n\n"
64 "Hint: You perhaps instantiated a second "
65 "MessageLoop/ScopedTaskEnvironment on a thread that already had one?";
66 tls_delegate.Get().Set(delegate);
67 delegate->bound_ = true;
68 }
69
RunLoop(Type type)70 RunLoop::RunLoop(Type type)
71 : delegate_(tls_delegate.Get().Get()),
72 type_(type),
73 origin_task_runner_(ThreadTaskRunnerHandle::Get()),
74 weak_factory_(this) {
75 DCHECK(delegate_) << "A RunLoop::Delegate must be bound to this thread prior "
76 "to using RunLoop.";
77 DCHECK(origin_task_runner_);
78 }
79
~RunLoop()80 RunLoop::~RunLoop() {
81 // TODO(gab): Fix bad usage and enable this check, http://crbug.com/715235.
82 // DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
83 }
84
Run()85 void RunLoop::Run() {
86 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
87
88 if (!BeforeRun())
89 return;
90
91 // It is okay to access this RunLoop from another sequence while Run() is
92 // active as this RunLoop won't touch its state until after that returns (if
93 // the RunLoop's state is accessed while processing Run(), it will be re-bound
94 // to the accessing sequence for the remainder of that Run() -- accessing from
95 // multiple sequences is still disallowed).
96 DETACH_FROM_SEQUENCE(sequence_checker_);
97
98 DCHECK_EQ(this, delegate_->active_run_loops_.top());
99 const bool application_tasks_allowed =
100 delegate_->active_run_loops_.size() == 1U ||
101 type_ == Type::kNestableTasksAllowed;
102 delegate_->Run(application_tasks_allowed);
103
104 // Rebind this RunLoop to the current thread after Run().
105 DETACH_FROM_SEQUENCE(sequence_checker_);
106 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
107
108 AfterRun();
109 }
110
RunUntilIdle()111 void RunLoop::RunUntilIdle() {
112 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
113
114 quit_when_idle_received_ = true;
115 Run();
116 }
117
Quit()118 void RunLoop::Quit() {
119 // Thread-safe.
120
121 // This can only be hit if run_loop->Quit() is called directly (QuitClosure()
122 // proxies through ProxyToTaskRunner() as it can only deref its WeakPtr on
123 // |origin_task_runner_|).
124 if (!origin_task_runner_->RunsTasksInCurrentSequence()) {
125 origin_task_runner_->PostTask(
126 FROM_HERE, base::BindOnce(&RunLoop::Quit, Unretained(this)));
127 return;
128 }
129
130 quit_called_ = true;
131 if (running_ && delegate_->active_run_loops_.top() == this) {
132 // This is the inner-most RunLoop, so quit now.
133 delegate_->Quit();
134 }
135 }
136
QuitWhenIdle()137 void RunLoop::QuitWhenIdle() {
138 // Thread-safe.
139
140 // This can only be hit if run_loop->QuitWhenIdle() is called directly
141 // (QuitWhenIdleClosure() proxies through ProxyToTaskRunner() as it can only
142 // deref its WeakPtr on |origin_task_runner_|).
143 if (!origin_task_runner_->RunsTasksInCurrentSequence()) {
144 origin_task_runner_->PostTask(
145 FROM_HERE, base::BindOnce(&RunLoop::QuitWhenIdle, Unretained(this)));
146 return;
147 }
148
149 quit_when_idle_received_ = true;
150 }
151
QuitClosure()152 base::Closure RunLoop::QuitClosure() {
153 // TODO(gab): Fix bad usage and enable this check, http://crbug.com/715235.
154 // DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
155 allow_quit_current_deprecated_ = false;
156
157 // Need to use ProxyToTaskRunner() as WeakPtrs vended from
158 // |weak_factory_| may only be accessed on |origin_task_runner_|.
159 // TODO(gab): It feels wrong that QuitClosure() is bound to a WeakPtr.
160 return base::Bind(&ProxyToTaskRunner, origin_task_runner_,
161 base::Bind(&RunLoop::Quit, weak_factory_.GetWeakPtr()));
162 }
163
QuitWhenIdleClosure()164 base::Closure RunLoop::QuitWhenIdleClosure() {
165 // TODO(gab): Fix bad usage and enable this check, http://crbug.com/715235.
166 // DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
167 allow_quit_current_deprecated_ = false;
168
169 // Need to use ProxyToTaskRunner() as WeakPtrs vended from
170 // |weak_factory_| may only be accessed on |origin_task_runner_|.
171 // TODO(gab): It feels wrong that QuitWhenIdleClosure() is bound to a WeakPtr.
172 return base::Bind(
173 &ProxyToTaskRunner, origin_task_runner_,
174 base::Bind(&RunLoop::QuitWhenIdle, weak_factory_.GetWeakPtr()));
175 }
176
177 // static
IsRunningOnCurrentThread()178 bool RunLoop::IsRunningOnCurrentThread() {
179 Delegate* delegate = tls_delegate.Get().Get();
180 return delegate && !delegate->active_run_loops_.empty();
181 }
182
183 // static
IsNestedOnCurrentThread()184 bool RunLoop::IsNestedOnCurrentThread() {
185 Delegate* delegate = tls_delegate.Get().Get();
186 return delegate && delegate->active_run_loops_.size() > 1;
187 }
188
189 // static
AddNestingObserverOnCurrentThread(NestingObserver * observer)190 void RunLoop::AddNestingObserverOnCurrentThread(NestingObserver* observer) {
191 Delegate* delegate = tls_delegate.Get().Get();
192 DCHECK(delegate);
193 delegate->nesting_observers_.AddObserver(observer);
194 }
195
196 // static
RemoveNestingObserverOnCurrentThread(NestingObserver * observer)197 void RunLoop::RemoveNestingObserverOnCurrentThread(NestingObserver* observer) {
198 Delegate* delegate = tls_delegate.Get().Get();
199 DCHECK(delegate);
200 delegate->nesting_observers_.RemoveObserver(observer);
201 }
202
203 // static
QuitCurrentDeprecated()204 void RunLoop::QuitCurrentDeprecated() {
205 DCHECK(IsRunningOnCurrentThread());
206 Delegate* delegate = tls_delegate.Get().Get();
207 DCHECK(delegate->active_run_loops_.top()->allow_quit_current_deprecated_)
208 << "Please migrate off QuitCurrentDeprecated(), e.g. to QuitClosure().";
209 delegate->active_run_loops_.top()->Quit();
210 }
211
212 // static
QuitCurrentWhenIdleDeprecated()213 void RunLoop::QuitCurrentWhenIdleDeprecated() {
214 DCHECK(IsRunningOnCurrentThread());
215 Delegate* delegate = tls_delegate.Get().Get();
216 DCHECK(delegate->active_run_loops_.top()->allow_quit_current_deprecated_)
217 << "Please migrate off QuitCurrentWhenIdleDeprecated(), e.g. to "
218 "QuitWhenIdleClosure().";
219 delegate->active_run_loops_.top()->QuitWhenIdle();
220 }
221
222 // static
QuitCurrentWhenIdleClosureDeprecated()223 Closure RunLoop::QuitCurrentWhenIdleClosureDeprecated() {
224 // TODO(844016): Fix callsites and enable this check, or remove the API.
225 // Delegate* delegate = tls_delegate.Get().Get();
226 // DCHECK(delegate->active_run_loops_.top()->allow_quit_current_deprecated_)
227 // << "Please migrate off QuitCurrentWhenIdleClosureDeprecated(), e.g to "
228 // "QuitWhenIdleClosure().";
229 return Bind(&RunLoop::QuitCurrentWhenIdleDeprecated);
230 }
231
232 #if DCHECK_IS_ON()
ScopedDisallowRunningForTesting()233 RunLoop::ScopedDisallowRunningForTesting::ScopedDisallowRunningForTesting()
234 : current_delegate_(tls_delegate.Get().Get()),
235 previous_run_allowance_(
236 current_delegate_ ? current_delegate_->allow_running_for_testing_
237 : false) {
238 if (current_delegate_)
239 current_delegate_->allow_running_for_testing_ = false;
240 }
241
~ScopedDisallowRunningForTesting()242 RunLoop::ScopedDisallowRunningForTesting::~ScopedDisallowRunningForTesting() {
243 DCHECK_EQ(current_delegate_, tls_delegate.Get().Get());
244 if (current_delegate_)
245 current_delegate_->allow_running_for_testing_ = previous_run_allowance_;
246 }
247 #else // DCHECK_IS_ON()
248 // Defined out of line so that the compiler doesn't inline these and realize
249 // the scope has no effect and then throws an "unused variable" warning in
250 // non-dcheck builds.
251 RunLoop::ScopedDisallowRunningForTesting::ScopedDisallowRunningForTesting() =
252 default;
253 RunLoop::ScopedDisallowRunningForTesting::~ScopedDisallowRunningForTesting() =
254 default;
255 #endif // DCHECK_IS_ON()
256
BeforeRun()257 bool RunLoop::BeforeRun() {
258 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
259
260 #if DCHECK_IS_ON()
261 DCHECK(delegate_->allow_running_for_testing_)
262 << "RunLoop::Run() isn't allowed in the scope of a "
263 "ScopedDisallowRunningForTesting. Hint: if mixing "
264 "TestMockTimeTaskRunners on same thread, use TestMockTimeTaskRunner's "
265 "API instead of RunLoop to drive individual task runners.";
266 DCHECK(!run_called_);
267 run_called_ = true;
268 #endif // DCHECK_IS_ON()
269
270 // Allow Quit to be called before Run.
271 if (quit_called_)
272 return false;
273
274 auto& active_run_loops_ = delegate_->active_run_loops_;
275 active_run_loops_.push(this);
276
277 const bool is_nested = active_run_loops_.size() > 1;
278
279 if (is_nested) {
280 for (auto& observer : delegate_->nesting_observers_)
281 observer.OnBeginNestedRunLoop();
282 if (type_ == Type::kNestableTasksAllowed)
283 delegate_->EnsureWorkScheduled();
284 }
285
286 running_ = true;
287 return true;
288 }
289
AfterRun()290 void RunLoop::AfterRun() {
291 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
292
293 running_ = false;
294
295 auto& active_run_loops_ = delegate_->active_run_loops_;
296 DCHECK_EQ(active_run_loops_.top(), this);
297 active_run_loops_.pop();
298
299 RunLoop* previous_run_loop =
300 active_run_loops_.empty() ? nullptr : active_run_loops_.top();
301
302 if (previous_run_loop) {
303 for (auto& observer : delegate_->nesting_observers_)
304 observer.OnExitNestedRunLoop();
305 }
306
307 // Execute deferred Quit, if any:
308 if (previous_run_loop && previous_run_loop->quit_called_)
309 delegate_->Quit();
310 }
311
312 } // namespace base
313