blob: 2bcc22151e50aa6bcb5bc9fc0097b30ecda1c1e5 [file] [log] [blame]
[email protected]8e937c1e2012-06-28 22:57:301// 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"
gabcf5e4ce2017-05-19 22:56:578#include "base/callback.h"
Wezd9e4cb772019-01-09 03:07:039#include "base/cancelable_callback.h"
gab7af9dc02017-05-05 13:38:5410#include "base/lazy_instance.h"
Alexander Timin4f9c35c2018-11-01 20:15:2011#include "base/message_loop/message_loop.h"
Wezd9e4cb772019-01-09 03:07:0312#include "base/no_destructor.h"
gabcf5e4ce2017-05-19 22:56:5713#include "base/single_thread_task_runner.h"
gab273551962017-05-18 06:01:1014#include "base/threading/thread_local.h"
gabcf5e4ce2017-05-19 22:56:5715#include "base/threading/thread_task_runner_handle.h"
avi9b6f42932015-12-26 22:15:1416#include "build/build_config.h"
[email protected]8e937c1e2012-06-28 22:57:3017
18namespace base {
19
gab7af9dc02017-05-05 13:38:5420namespace {
21
gab273551962017-05-18 06:01:1022LazyInstance<ThreadLocalPointer<RunLoop::Delegate>>::Leaky tls_delegate =
gab7af9dc02017-05-05 13:38:5423 LAZY_INSTANCE_INITIALIZER;
24
gabcf5e4ce2017-05-19 22:56:5725// Runs |closure| immediately if this is called on |task_runner|, otherwise
26// forwards |closure| to it.
27void 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
Wezd9e4cb772019-01-09 03:07:0336ThreadLocalPointer<RunLoop::ScopedRunTimeoutForTest>*
37ScopedRunTimeoutForTestTLS() {
38 static NoDestructor<ThreadLocalPointer<RunLoop::ScopedRunTimeoutForTest>> tls;
39 return tls.get();
40}
41
42void OnRunTimeout(RunLoop* run_loop, RepeatingClosure on_timeout) {
43 run_loop->Quit();
44 if (on_timeout)
45 on_timeout.Run();
46}
47
gab7af9dc02017-05-05 13:38:5448} // namespace
49
Wezd9e4cb772019-01-09 03:07:0350RunLoop::ScopedRunTimeoutForTest::ScopedRunTimeoutForTest(TimeDelta timeout)
51 : ScopedRunTimeoutForTest(timeout, RepeatingClosure()) {}
52
53RunLoop::ScopedRunTimeoutForTest::ScopedRunTimeoutForTest(
54 TimeDelta timeout,
55 RepeatingClosure on_timeout)
56 : timeout_(timeout),
57 on_timeout_(std::move(on_timeout)),
58 nested_timeout_(ScopedRunTimeoutForTestTLS()->Get()) {
59 ScopedRunTimeoutForTestTLS()->Set(this);
60}
61
62RunLoop::ScopedRunTimeoutForTest::~ScopedRunTimeoutForTest() {
63 ScopedRunTimeoutForTestTLS()->Set(nested_timeout_);
64}
65
66// static
67const RunLoop::ScopedRunTimeoutForTest*
68RunLoop::ScopedRunTimeoutForTest::Current() {
69 return ScopedRunTimeoutForTestTLS()->Get();
70}
71
Gabriel Charette50518fc2018-05-22 17:53:2272RunLoop::Delegate::Delegate() {
gab273551962017-05-18 06:01:1073 // The Delegate can be created on another thread. It is only bound in
74 // RegisterDelegateForCurrentThread().
75 DETACH_FROM_THREAD(bound_thread_checker_);
76}
77
78RunLoop::Delegate::~Delegate() {
79 DCHECK_CALLED_ON_VALID_THREAD(bound_thread_checker_);
80 // A RunLoop::Delegate may be destroyed before it is bound, if so it may still
81 // be on its creation thread (e.g. a Thread that fails to start) and
82 // shouldn't disrupt that thread's state.
83 if (bound_)
84 tls_delegate.Get().Set(nullptr);
85}
86
Gabriel Charettea3ec9612017-12-14 17:22:4087bool RunLoop::Delegate::ShouldQuitWhenIdle() {
Gabriel Charette50518fc2018-05-22 17:53:2288 return active_run_loops_.top()->quit_when_idle_received_;
gab273551962017-05-18 06:01:1089}
90
Gabriel Charette0592c3a2017-07-26 12:02:0491// static
Gabriel Charettea3ec9612017-12-14 17:22:4092void RunLoop::RegisterDelegateForCurrentThread(Delegate* delegate) {
gab273551962017-05-18 06:01:1093 // Bind |delegate| to this thread.
94 DCHECK(!delegate->bound_);
95 DCHECK_CALLED_ON_VALID_THREAD(delegate->bound_thread_checker_);
96
97 // There can only be one RunLoop::Delegate per thread.
Gabriel Charettefa5e7f0d2018-01-29 12:08:2198 DCHECK(!tls_delegate.Get().Get())
99 << "Error: Multiple RunLoop::Delegates registered on the same thread.\n\n"
100 "Hint: You perhaps instantiated a second "
101 "MessageLoop/ScopedTaskEnvironment on a thread that already had one?";
gab273551962017-05-18 06:01:10102 tls_delegate.Get().Set(delegate);
103 delegate->bound_ = true;
gab273551962017-05-18 06:01:10104}
105
Gabriel Charette3ff403e2017-08-07 04:22:48106RunLoop::RunLoop(Type type)
gabcf5e4ce2017-05-19 22:56:57107 : delegate_(tls_delegate.Get().Get()),
Gabriel Charette3ff403e2017-08-07 04:22:48108 type_(type),
gabcf5e4ce2017-05-19 22:56:57109 origin_task_runner_(ThreadTaskRunnerHandle::Get()),
110 weak_factory_(this) {
Gabriel Charettee2b632b2017-08-02 03:52:16111 DCHECK(delegate_) << "A RunLoop::Delegate must be bound to this thread prior "
112 "to using RunLoop.";
gabcf5e4ce2017-05-19 22:56:57113 DCHECK(origin_task_runner_);
[email protected]8e937c1e2012-06-28 22:57:30114}
115
[email protected]8e937c1e2012-06-28 22:57:30116RunLoop::~RunLoop() {
gab7af9dc02017-05-05 13:38:54117 // TODO(gab): Fix bad usage and enable this check, http://crbug.com/715235.
gab980a52712017-05-18 16:20:16118 // DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
[email protected]8e937c1e2012-06-28 22:57:30119}
120
121void RunLoop::Run() {
gab980a52712017-05-18 16:20:16122 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
gab7af9dc02017-05-05 13:38:54123
[email protected]8e937c1e2012-06-28 22:57:30124 if (!BeforeRun())
125 return;
vadimt12f0f7d2014-09-15 19:19:38126
Wezd9e4cb772019-01-09 03:07:03127 // If there is a ScopedRunTimeoutForTest active then set the timeout.
128 // TODO(crbug.com/905412): Use real-time for Run() timeouts so that they
129 // can be applied even in tests which mock TimeTicks::Now().
130 CancelableOnceClosure cancelable_timeout;
131 ScopedRunTimeoutForTest* run_timeout = ScopedRunTimeoutForTestTLS()->Get();
132 if (run_timeout && !run_timeout->timeout().is_zero()) {
133 cancelable_timeout.Reset(
134 BindOnce(&OnRunTimeout, Unretained(this), run_timeout->on_timeout()));
135 ThreadTaskRunnerHandle::Get()->PostDelayedTask(
136 FROM_HERE, cancelable_timeout.callback(), run_timeout->timeout());
137 }
138
gab980a52712017-05-18 16:20:16139 // It is okay to access this RunLoop from another sequence while Run() is
140 // active as this RunLoop won't touch its state until after that returns (if
141 // the RunLoop's state is accessed while processing Run(), it will be re-bound
142 // to the accessing sequence for the remainder of that Run() -- accessing from
143 // multiple sequences is still disallowed).
144 DETACH_FROM_SEQUENCE(sequence_checker_);
145
Gabriel Charetteb030a4a2017-10-26 01:04:40146 DCHECK_EQ(this, delegate_->active_run_loops_.top());
147 const bool application_tasks_allowed =
148 delegate_->active_run_loops_.size() == 1U ||
149 type_ == Type::kNestableTasksAllowed;
150 delegate_->Run(application_tasks_allowed);
vadimt12f0f7d2014-09-15 19:19:38151
gab980a52712017-05-18 16:20:16152 // Rebind this RunLoop to the current thread after Run().
153 DETACH_FROM_SEQUENCE(sequence_checker_);
154 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
155
[email protected]8e937c1e2012-06-28 22:57:30156 AfterRun();
157}
158
159void RunLoop::RunUntilIdle() {
gab980a52712017-05-18 16:20:16160 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
gab7af9dc02017-05-05 13:38:54161
[email protected]8e937c1e2012-06-28 22:57:30162 quit_when_idle_received_ = true;
163 Run();
164}
165
166void RunLoop::Quit() {
gabcf5e4ce2017-05-19 22:56:57167 // Thread-safe.
168
169 // This can only be hit if run_loop->Quit() is called directly (QuitClosure()
170 // proxies through ProxyToTaskRunner() as it can only deref its WeakPtr on
171 // |origin_task_runner_|).
172 if (!origin_task_runner_->RunsTasksInCurrentSequence()) {
Wezd9e4cb772019-01-09 03:07:03173 origin_task_runner_->PostTask(FROM_HERE,
174 BindOnce(&RunLoop::Quit, Unretained(this)));
gabcf5e4ce2017-05-19 22:56:57175 return;
176 }
gab7af9dc02017-05-05 13:38:54177
[email protected]8e937c1e2012-06-28 22:57:30178 quit_called_ = true;
gab273551962017-05-18 06:01:10179 if (running_ && delegate_->active_run_loops_.top() == this) {
[email protected]8e937c1e2012-06-28 22:57:30180 // This is the inner-most RunLoop, so quit now.
gab273551962017-05-18 06:01:10181 delegate_->Quit();
[email protected]8e937c1e2012-06-28 22:57:30182 }
183}
184
fdoraya4f28ec2016-06-10 00:08:58185void RunLoop::QuitWhenIdle() {
gabcf5e4ce2017-05-19 22:56:57186 // Thread-safe.
187
188 // This can only be hit if run_loop->QuitWhenIdle() is called directly
189 // (QuitWhenIdleClosure() proxies through ProxyToTaskRunner() as it can only
190 // deref its WeakPtr on |origin_task_runner_|).
191 if (!origin_task_runner_->RunsTasksInCurrentSequence()) {
192 origin_task_runner_->PostTask(
Wezd9e4cb772019-01-09 03:07:03193 FROM_HERE, BindOnce(&RunLoop::QuitWhenIdle, Unretained(this)));
gabcf5e4ce2017-05-19 22:56:57194 return;
195 }
196
fdoraya4f28ec2016-06-10 00:08:58197 quit_when_idle_received_ = true;
198}
199
Wezd9e4cb772019-01-09 03:07:03200Closure RunLoop::QuitClosure() {
gab7af9dc02017-05-05 13:38:54201 // TODO(gab): Fix bad usage and enable this check, http://crbug.com/715235.
gab980a52712017-05-18 16:20:16202 // DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Wez325eafc2018-07-17 17:01:49203 allow_quit_current_deprecated_ = false;
gabcf5e4ce2017-05-19 22:56:57204
205 // Need to use ProxyToTaskRunner() as WeakPtrs vended from
206 // |weak_factory_| may only be accessed on |origin_task_runner_|.
207 // TODO(gab): It feels wrong that QuitClosure() is bound to a WeakPtr.
Wezd9e4cb772019-01-09 03:07:03208 return Bind(&ProxyToTaskRunner, origin_task_runner_,
209 Bind(&RunLoop::Quit, weak_factory_.GetWeakPtr()));
[email protected]8e937c1e2012-06-28 22:57:30210}
211
Wezd9e4cb772019-01-09 03:07:03212Closure RunLoop::QuitWhenIdleClosure() {
gab7af9dc02017-05-05 13:38:54213 // TODO(gab): Fix bad usage and enable this check, http://crbug.com/715235.
gab980a52712017-05-18 16:20:16214 // DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Wez325eafc2018-07-17 17:01:49215 allow_quit_current_deprecated_ = false;
gabcf5e4ce2017-05-19 22:56:57216
217 // Need to use ProxyToTaskRunner() as WeakPtrs vended from
218 // |weak_factory_| may only be accessed on |origin_task_runner_|.
219 // TODO(gab): It feels wrong that QuitWhenIdleClosure() is bound to a WeakPtr.
Wezd9e4cb772019-01-09 03:07:03220 return Bind(&ProxyToTaskRunner, origin_task_runner_,
221 Bind(&RunLoop::QuitWhenIdle, weak_factory_.GetWeakPtr()));
fdoraya3658602016-06-10 18:23:15222}
223
gab7af9dc02017-05-05 13:38:54224// static
gab7af9dc02017-05-05 13:38:54225bool RunLoop::IsRunningOnCurrentThread() {
gab273551962017-05-18 06:01:10226 Delegate* delegate = tls_delegate.Get().Get();
227 return delegate && !delegate->active_run_loops_.empty();
gab7af9dc02017-05-05 13:38:54228}
229
230// static
231bool RunLoop::IsNestedOnCurrentThread() {
gab273551962017-05-18 06:01:10232 Delegate* delegate = tls_delegate.Get().Get();
233 return delegate && delegate->active_run_loops_.size() > 1;
gab7af9dc02017-05-05 13:38:54234}
235
236// static
237void RunLoop::AddNestingObserverOnCurrentThread(NestingObserver* observer) {
gab273551962017-05-18 06:01:10238 Delegate* delegate = tls_delegate.Get().Get();
239 DCHECK(delegate);
gab273551962017-05-18 06:01:10240 delegate->nesting_observers_.AddObserver(observer);
gab7af9dc02017-05-05 13:38:54241}
242
243// static
244void RunLoop::RemoveNestingObserverOnCurrentThread(NestingObserver* observer) {
gab273551962017-05-18 06:01:10245 Delegate* delegate = tls_delegate.Get().Get();
246 DCHECK(delegate);
gab273551962017-05-18 06:01:10247 delegate->nesting_observers_.RemoveObserver(observer);
gab7af9dc02017-05-05 13:38:54248}
249
250// static
Gabriel Charette0592c3a2017-07-26 12:02:04251void RunLoop::QuitCurrentDeprecated() {
252 DCHECK(IsRunningOnCurrentThread());
Wez325eafc2018-07-17 17:01:49253 Delegate* delegate = tls_delegate.Get().Get();
254 DCHECK(delegate->active_run_loops_.top()->allow_quit_current_deprecated_)
255 << "Please migrate off QuitCurrentDeprecated(), e.g. to QuitClosure().";
256 delegate->active_run_loops_.top()->Quit();
Gabriel Charette0592c3a2017-07-26 12:02:04257}
258
259// static
260void RunLoop::QuitCurrentWhenIdleDeprecated() {
261 DCHECK(IsRunningOnCurrentThread());
Wez325eafc2018-07-17 17:01:49262 Delegate* delegate = tls_delegate.Get().Get();
263 DCHECK(delegate->active_run_loops_.top()->allow_quit_current_deprecated_)
264 << "Please migrate off QuitCurrentWhenIdleDeprecated(), e.g. to "
265 "QuitWhenIdleClosure().";
266 delegate->active_run_loops_.top()->QuitWhenIdle();
Gabriel Charette0592c3a2017-07-26 12:02:04267}
268
Gabriel Charette5bc88412018-05-16 03:28:25269// static
270Closure RunLoop::QuitCurrentWhenIdleClosureDeprecated() {
Wez325eafc2018-07-17 17:01:49271 // TODO(844016): Fix callsites and enable this check, or remove the API.
272 // Delegate* delegate = tls_delegate.Get().Get();
273 // DCHECK(delegate->active_run_loops_.top()->allow_quit_current_deprecated_)
274 // << "Please migrate off QuitCurrentWhenIdleClosureDeprecated(), e.g to "
275 // "QuitWhenIdleClosure().";
Gabriel Charette5bc88412018-05-16 03:28:25276 return Bind(&RunLoop::QuitCurrentWhenIdleDeprecated);
277}
278
Gabriel Charettea44975052017-08-21 23:14:04279#if DCHECK_IS_ON()
280RunLoop::ScopedDisallowRunningForTesting::ScopedDisallowRunningForTesting()
281 : current_delegate_(tls_delegate.Get().Get()),
282 previous_run_allowance_(
283 current_delegate_ ? current_delegate_->allow_running_for_testing_
284 : false) {
285 if (current_delegate_)
286 current_delegate_->allow_running_for_testing_ = false;
287}
288
289RunLoop::ScopedDisallowRunningForTesting::~ScopedDisallowRunningForTesting() {
290 DCHECK_EQ(current_delegate_, tls_delegate.Get().Get());
291 if (current_delegate_)
292 current_delegate_->allow_running_for_testing_ = previous_run_allowance_;
293}
294#else // DCHECK_IS_ON()
295// Defined out of line so that the compiler doesn't inline these and realize
296// the scope has no effect and then throws an "unused variable" warning in
297// non-dcheck builds.
298RunLoop::ScopedDisallowRunningForTesting::ScopedDisallowRunningForTesting() =
299 default;
300RunLoop::ScopedDisallowRunningForTesting::~ScopedDisallowRunningForTesting() =
301 default;
302#endif // DCHECK_IS_ON()
303
[email protected]8e937c1e2012-06-28 22:57:30304bool RunLoop::BeforeRun() {
gab980a52712017-05-18 16:20:16305 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
gab7af9dc02017-05-05 13:38:54306
gabcf5e4ce2017-05-19 22:56:57307#if DCHECK_IS_ON()
Gabriel Charettea44975052017-08-21 23:14:04308 DCHECK(delegate_->allow_running_for_testing_)
309 << "RunLoop::Run() isn't allowed in the scope of a "
310 "ScopedDisallowRunningForTesting. Hint: if mixing "
311 "TestMockTimeTaskRunners on same thread, use TestMockTimeTaskRunner's "
312 "API instead of RunLoop to drive individual task runners.";
[email protected]8e937c1e2012-06-28 22:57:30313 DCHECK(!run_called_);
314 run_called_ = true;
gabcf5e4ce2017-05-19 22:56:57315#endif // DCHECK_IS_ON()
[email protected]8e937c1e2012-06-28 22:57:30316
317 // Allow Quit to be called before Run.
318 if (quit_called_)
319 return false;
320
gab273551962017-05-18 06:01:10321 auto& active_run_loops_ = delegate_->active_run_loops_;
322 active_run_loops_.push(this);
[email protected]8e937c1e2012-06-28 22:57:30323
gab273551962017-05-18 06:01:10324 const bool is_nested = active_run_loops_.size() > 1;
gab7af9dc02017-05-05 13:38:54325
326 if (is_nested) {
gab273551962017-05-18 06:01:10327 for (auto& observer : delegate_->nesting_observers_)
gab7af9dc02017-05-05 13:38:54328 observer.OnBeginNestedRunLoop();
Gabriel Charette3ff403e2017-08-07 04:22:48329 if (type_ == Type::kNestableTasksAllowed)
330 delegate_->EnsureWorkScheduled();
gab7af9dc02017-05-05 13:38:54331 }
jamescookaacdfd02016-04-28 00:50:03332
[email protected]8e937c1e2012-06-28 22:57:30333 running_ = true;
334 return true;
335}
336
337void RunLoop::AfterRun() {
gab980a52712017-05-18 16:20:16338 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
gab7af9dc02017-05-05 13:38:54339
[email protected]8e937c1e2012-06-28 22:57:30340 running_ = false;
341
gab273551962017-05-18 06:01:10342 auto& active_run_loops_ = delegate_->active_run_loops_;
343 DCHECK_EQ(active_run_loops_.top(), this);
344 active_run_loops_.pop();
gab7af9dc02017-05-05 13:38:54345
346 RunLoop* previous_run_loop =
gab273551962017-05-18 06:01:10347 active_run_loops_.empty() ? nullptr : active_run_loops_.top();
[email protected]8e937c1e2012-06-28 22:57:30348
Francois Doray80bdddf2018-01-04 16:17:32349 if (previous_run_loop) {
350 for (auto& observer : delegate_->nesting_observers_)
351 observer.OnExitNestedRunLoop();
352 }
353
Gabriel Charettee2b632b2017-08-02 03:52:16354 // Execute deferred Quit, if any:
gab7af9dc02017-05-05 13:38:54355 if (previous_run_loop && previous_run_loop->quit_called_)
gab273551962017-05-18 06:01:10356 delegate_->Quit();
[email protected]8e937c1e2012-06-28 22:57:30357}
358
359} // namespace base