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.
5 #include "base/run_loop.h"
8 #include "base/callback.h"
9 #include "base/cancelable_callback.h"
10 #include "base/check.h"
11 #include "base/no_destructor.h"
12 #include "base/observer_list.h"
13 #include "base/task/single_thread_task_runner.h"
14 #include "base/threading/thread_local.h"
15 #include "base/threading/thread_task_runner_handle.h"
16 #include "base/trace_event/base_tracing.h"
17 #include "build/build_config.h"
23 ThreadLocalPointer<RunLoop::Delegate>& GetTlsDelegate() {
24 static NoDestructor<ThreadLocalPointer<RunLoop::Delegate>> instance;
28 // Runs |closure| immediately if this is called on |task_runner|, otherwise
29 // forwards |closure| to it.
30 void ProxyToTaskRunner(scoped_refptr<SequencedTaskRunner> task_runner,
31 OnceClosure closure) {
32 if (task_runner->RunsTasksInCurrentSequence()) {
33 std::move(closure).Run();
36 task_runner->PostTask(FROM_HERE, std::move(closure));
39 ThreadLocalPointer<const RunLoop::RunLoopTimeout>& RunLoopTimeoutTLS() {
40 static NoDestructor<ThreadLocalPointer<const RunLoop::RunLoopTimeout>> tls;
44 void OnRunLoopTimeout(RunLoop* run_loop,
45 const Location& location,
46 OnceCallback<void(const Location&)> on_timeout) {
48 std::move(on_timeout).Run(location);
53 RunLoop::Delegate::Delegate() {
54 // The Delegate can be created on another thread. It is only bound in
55 // RegisterDelegateForCurrentThread().
56 DETACH_FROM_THREAD(bound_thread_checker_);
59 RunLoop::Delegate::~Delegate() {
60 DCHECK_CALLED_ON_VALID_THREAD(bound_thread_checker_);
61 DCHECK(active_run_loops_.empty());
62 // A RunLoop::Delegate may be destroyed before it is bound, if so it may still
63 // be on its creation thread (e.g. a Thread that fails to start) and
64 // shouldn't disrupt that thread's state.
66 DCHECK_EQ(this, GetTlsDelegate().Get());
67 GetTlsDelegate().Set(nullptr);
71 bool RunLoop::Delegate::ShouldQuitWhenIdle() {
72 const auto* top_loop = active_run_loops_.top();
73 if (top_loop->quit_when_idle_) {
74 TRACE_EVENT_WITH_FLOW0("toplevel.flow", "RunLoop_ExitedOnIdle",
75 TRACE_ID_LOCAL(top_loop), TRACE_EVENT_FLAG_FLOW_IN);
82 void RunLoop::RegisterDelegateForCurrentThread(Delegate* delegate) {
83 // Bind |delegate| to this thread.
84 DCHECK(!delegate->bound_);
85 DCHECK_CALLED_ON_VALID_THREAD(delegate->bound_thread_checker_);
87 // There can only be one RunLoop::Delegate per thread.
88 DCHECK(!GetTlsDelegate().Get())
89 << "Error: Multiple RunLoop::Delegates registered on the same thread.\n\n"
90 "Hint: You perhaps instantiated a second "
91 "MessageLoop/TaskEnvironment on a thread that already had one?";
92 GetTlsDelegate().Set(delegate);
93 delegate->bound_ = true;
96 RunLoop::RunLoop(Type type)
97 : delegate_(GetTlsDelegate().Get()),
99 origin_task_runner_(ThreadTaskRunnerHandle::Get()) {
100 DCHECK(delegate_) << "A RunLoop::Delegate must be bound to this thread prior "
102 DCHECK(origin_task_runner_);
105 RunLoop::~RunLoop() {
106 // ~RunLoop() must happen-after the RunLoop is done running but it doesn't
107 // have to be on |sequence_checker_| (it usually is but sometimes it can be a
108 // member of a RefCountedThreadSafe object and be destroyed on another thread
109 // after being quit).
113 void RunLoop::Run(const Location& location) {
114 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
115 // "test" tracing category is used here because in regular scenarios RunLoop
116 // trace events are not useful (each process normally has one RunLoop covering
117 // its entire lifetime) and might be confusing (they make idle processes look
118 // non-idle). In tests, however, creating a RunLoop is a frequent and an
119 // explicit action making this trace event very useful.
120 TRACE_EVENT("test", "RunLoop::Run", "location", location);
125 // If there is a RunLoopTimeout active then set the timeout.
126 // TODO(crbug.com/905412): Use real-time for Run() timeouts so that they
127 // can be applied even in tests which mock TimeTicks::Now().
128 CancelableOnceClosure cancelable_timeout;
129 const RunLoopTimeout* run_timeout = GetTimeoutForCurrentThread();
131 cancelable_timeout.Reset(BindOnce(&OnRunLoopTimeout, Unretained(this),
132 location, run_timeout->on_timeout));
133 origin_task_runner_->PostDelayedTask(
134 FROM_HERE, cancelable_timeout.callback(), run_timeout->timeout);
137 DCHECK_EQ(this, delegate_->active_run_loops_.top());
138 const bool application_tasks_allowed =
139 delegate_->active_run_loops_.size() == 1U ||
140 type_ == Type::kNestableTasksAllowed;
141 delegate_->Run(application_tasks_allowed, TimeDelta::Max());
146 void RunLoop::RunUntilIdle() {
147 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
149 quit_when_idle_ = true;
152 if (!AnyQuitCalled()) {
153 quit_when_idle_ = false;
160 void RunLoop::Quit() {
163 // This can only be hit if RunLoop::Quit() is called directly (QuitClosure()
164 // proxies through ProxyToTaskRunner() as it can only deref its WeakPtr on
165 // |origin_task_runner_|).
166 if (!origin_task_runner_->RunsTasksInCurrentSequence()) {
167 origin_task_runner_->PostTask(FROM_HERE,
168 BindOnce(&RunLoop::Quit, Unretained(this)));
172 // While Quit() is an "OUT" call to reach one of the quit-states ("IN"),
173 // OUT|IN is used to visually link multiple Quit*() together which can help
174 // when debugging flaky tests.
175 TRACE_EVENT_WITH_FLOW0("toplevel.flow", "RunLoop::Quit", TRACE_ID_LOCAL(this),
176 TRACE_EVENT_FLAG_FLOW_OUT | TRACE_EVENT_FLAG_FLOW_IN);
179 if (running_ && delegate_->active_run_loops_.top() == this) {
180 // This is the inner-most RunLoop, so quit now.
185 void RunLoop::QuitWhenIdle() {
188 // This can only be hit if RunLoop::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(
193 FROM_HERE, BindOnce(&RunLoop::QuitWhenIdle, Unretained(this)));
197 // OUT|IN as in Quit() to link all Quit*() together should there be multiple.
198 TRACE_EVENT_WITH_FLOW0("toplevel.flow", "RunLoop::QuitWhenIdle",
199 TRACE_ID_LOCAL(this),
200 TRACE_EVENT_FLAG_FLOW_OUT | TRACE_EVENT_FLAG_FLOW_IN);
202 quit_when_idle_ = true;
203 quit_when_idle_called_ = true;
206 RepeatingClosure RunLoop::QuitClosure() {
207 // Obtaining the QuitClosure() is not thread-safe; either obtain the
208 // QuitClosure() from the owning thread before Run() or invoke Quit() directly
209 // (which is thread-safe).
210 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
211 allow_quit_current_deprecated_ = false;
213 return BindRepeating(
214 &ProxyToTaskRunner, origin_task_runner_,
215 BindRepeating(&RunLoop::Quit, weak_factory_.GetWeakPtr()));
218 RepeatingClosure RunLoop::QuitWhenIdleClosure() {
219 // Obtaining the QuitWhenIdleClosure() is not thread-safe; either obtain the
220 // QuitWhenIdleClosure() from the owning thread before Run() or invoke
221 // QuitWhenIdle() directly (which is thread-safe).
222 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
223 allow_quit_current_deprecated_ = false;
225 return BindRepeating(
226 &ProxyToTaskRunner, origin_task_runner_,
227 BindRepeating(&RunLoop::QuitWhenIdle, weak_factory_.GetWeakPtr()));
230 bool RunLoop::AnyQuitCalled() {
231 return quit_called_ || quit_when_idle_called_;
235 bool RunLoop::IsRunningOnCurrentThread() {
236 Delegate* delegate = GetTlsDelegate().Get();
237 return delegate && !delegate->active_run_loops_.empty();
241 bool RunLoop::IsNestedOnCurrentThread() {
242 Delegate* delegate = GetTlsDelegate().Get();
243 return delegate && delegate->active_run_loops_.size() > 1;
247 void RunLoop::AddNestingObserverOnCurrentThread(NestingObserver* observer) {
248 Delegate* delegate = GetTlsDelegate().Get();
250 delegate->nesting_observers_.AddObserver(observer);
254 void RunLoop::RemoveNestingObserverOnCurrentThread(NestingObserver* observer) {
255 Delegate* delegate = GetTlsDelegate().Get();
257 delegate->nesting_observers_.RemoveObserver(observer);
261 void RunLoop::QuitCurrentDeprecated() {
262 DCHECK(IsRunningOnCurrentThread());
263 Delegate* delegate = GetTlsDelegate().Get();
264 DCHECK(delegate->active_run_loops_.top()->allow_quit_current_deprecated_)
265 << "Please migrate off QuitCurrentDeprecated(), e.g. to QuitClosure().";
266 delegate->active_run_loops_.top()->Quit();
270 void RunLoop::QuitCurrentWhenIdleDeprecated() {
271 DCHECK(IsRunningOnCurrentThread());
272 Delegate* delegate = GetTlsDelegate().Get();
273 DCHECK(delegate->active_run_loops_.top()->allow_quit_current_deprecated_)
274 << "Please migrate off QuitCurrentWhenIdleDeprecated(), e.g. to "
275 "QuitWhenIdleClosure().";
276 delegate->active_run_loops_.top()->QuitWhenIdle();
280 RepeatingClosure RunLoop::QuitCurrentWhenIdleClosureDeprecated() {
281 // TODO(844016): Fix callsites and enable this check, or remove the API.
282 // Delegate* delegate = GetTlsDelegate().Get();
283 // DCHECK(delegate->active_run_loops_.top()->allow_quit_current_deprecated_)
284 // << "Please migrate off QuitCurrentWhenIdleClosureDeprecated(), e.g to "
285 // "QuitWhenIdleClosure().";
286 return BindRepeating(&RunLoop::QuitCurrentWhenIdleDeprecated);
290 ScopedDisallowRunningRunLoop::ScopedDisallowRunningRunLoop()
291 : current_delegate_(GetTlsDelegate().Get()),
292 previous_run_allowance_(
293 current_delegate_ ? current_delegate_->allow_running_for_testing_
295 if (current_delegate_)
296 current_delegate_->allow_running_for_testing_ = false;
299 ScopedDisallowRunningRunLoop::~ScopedDisallowRunningRunLoop() {
300 DCHECK_EQ(current_delegate_, GetTlsDelegate().Get());
301 if (current_delegate_)
302 current_delegate_->allow_running_for_testing_ = previous_run_allowance_;
304 #else // DCHECK_IS_ON()
305 // Defined out of line so that the compiler doesn't inline these and realize
306 // the scope has no effect and then throws an "unused variable" warning in
307 // non-dcheck builds.
308 ScopedDisallowRunningRunLoop::ScopedDisallowRunningRunLoop() = default;
309 ScopedDisallowRunningRunLoop::~ScopedDisallowRunningRunLoop() = default;
310 #endif // DCHECK_IS_ON()
312 RunLoop::RunLoopTimeout::RunLoopTimeout() = default;
314 RunLoop::RunLoopTimeout::~RunLoopTimeout() = default;
317 void RunLoop::SetTimeoutForCurrentThread(const RunLoopTimeout* timeout) {
318 RunLoopTimeoutTLS().Set(timeout);
322 const RunLoop::RunLoopTimeout* RunLoop::GetTimeoutForCurrentThread() {
323 return RunLoopTimeoutTLS().Get();
326 bool RunLoop::BeforeRun() {
327 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
330 DCHECK(delegate_->allow_running_for_testing_)
331 << "RunLoop::Run() isn't allowed in the scope of a "
332 "ScopedDisallowRunningRunLoop. Hint: if mixing "
333 "TestMockTimeTaskRunners on same thread, use TestMockTimeTaskRunner's "
334 "API instead of RunLoop to drive individual task runners.";
335 DCHECK(run_allowed_);
336 run_allowed_ = false;
337 #endif // DCHECK_IS_ON()
339 // Allow Quit to be called before Run.
341 TRACE_EVENT_WITH_FLOW0("toplevel.flow", "RunLoop_ExitedEarly",
342 TRACE_ID_LOCAL(this), TRACE_EVENT_FLAG_FLOW_IN);
346 auto& active_run_loops = delegate_->active_run_loops_;
347 active_run_loops.push(this);
349 const bool is_nested = active_run_loops.size() > 1;
352 for (auto& observer : delegate_->nesting_observers_)
353 observer.OnBeginNestedRunLoop();
354 if (type_ == Type::kNestableTasksAllowed)
355 delegate_->EnsureWorkScheduled();
362 void RunLoop::AfterRun() {
363 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
367 TRACE_EVENT_WITH_FLOW0("toplevel.flow", "RunLoop_Exited",
368 TRACE_ID_LOCAL(this), TRACE_EVENT_FLAG_FLOW_IN);
370 auto& active_run_loops = delegate_->active_run_loops_;
371 DCHECK_EQ(active_run_loops.top(), this);
372 active_run_loops.pop();
374 // Exiting a nested RunLoop?
375 if (!active_run_loops.empty()) {
376 for (auto& observer : delegate_->nesting_observers_)
377 observer.OnExitNestedRunLoop();
379 // Execute deferred Quit, if any:
380 if (active_run_loops.top()->quit_called_)