blob: b87688d547b348b6fd2fc4aa8583153b7a123aa8 [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"
gab7af9dc02017-05-05 13:38:548#include "base/lazy_instance.h"
gab273551962017-05-18 06:01:109#include "base/threading/thread_local.h"
vadimt12f0f7d2014-09-15 19:19:3810#include "base/tracked_objects.h"
avi9b6f42932015-12-26 22:15:1411#include "build/build_config.h"
[email protected]8e937c1e2012-06-28 22:57:3012
13namespace base {
14
gab7af9dc02017-05-05 13:38:5415namespace {
16
gab273551962017-05-18 06:01:1017LazyInstance<ThreadLocalPointer<RunLoop::Delegate>>::Leaky tls_delegate =
gab7af9dc02017-05-05 13:38:5418 LAZY_INSTANCE_INITIALIZER;
19
20} // namespace
21
gab273551962017-05-18 06:01:1022RunLoop::Delegate::Delegate() {
23 // The Delegate can be created on another thread. It is only bound in
24 // RegisterDelegateForCurrentThread().
25 DETACH_FROM_THREAD(bound_thread_checker_);
26}
27
28RunLoop::Delegate::~Delegate() {
29 DCHECK_CALLED_ON_VALID_THREAD(bound_thread_checker_);
30 // A RunLoop::Delegate may be destroyed before it is bound, if so it may still
31 // be on its creation thread (e.g. a Thread that fails to start) and
32 // shouldn't disrupt that thread's state.
33 if (bound_)
34 tls_delegate.Get().Set(nullptr);
35}
36
37RunLoop* RunLoop::Delegate::Client::GetTopMostRunLoop() const {
38 DCHECK_CALLED_ON_VALID_THREAD(outer_->bound_thread_checker_);
39 DCHECK(outer_->bound_);
40 return outer_->active_run_loops_.empty() ? nullptr
41 : outer_->active_run_loops_.top();
42}
43
44bool RunLoop::Delegate::Client::IsNested() const {
45 DCHECK_CALLED_ON_VALID_THREAD(outer_->bound_thread_checker_);
46 DCHECK(outer_->bound_);
47 return outer_->active_run_loops_.size() > 1;
48}
49
50RunLoop::Delegate::Client::Client(Delegate* outer) : outer_(outer) {}
51
52RunLoop::Delegate::Client* RunLoop::RegisterDelegateForCurrentThread(
53 Delegate* delegate) {
54 // Bind |delegate| to this thread.
55 DCHECK(!delegate->bound_);
56 DCHECK_CALLED_ON_VALID_THREAD(delegate->bound_thread_checker_);
57
58 // There can only be one RunLoop::Delegate per thread.
59 DCHECK(!tls_delegate.Get().Get());
60 tls_delegate.Get().Set(delegate);
61 delegate->bound_ = true;
62
63 return &delegate->client_interface_;
64}
65
66RunLoop::RunLoop() : delegate_(tls_delegate.Get().Get()), weak_factory_(this) {
67 // A RunLoop::Delegate must be bound to this thread prior to using RunLoop.
68 DCHECK(delegate_);
[email protected]8e937c1e2012-06-28 22:57:3069}
70
[email protected]8e937c1e2012-06-28 22:57:3071RunLoop::~RunLoop() {
gab7af9dc02017-05-05 13:38:5472 // TODO(gab): Fix bad usage and enable this check, http://crbug.com/715235.
73 // DCHECK(thread_checker_.CalledOnValidThread());
[email protected]8e937c1e2012-06-28 22:57:3074}
75
76void RunLoop::Run() {
ahest72c1b442016-12-09 20:40:3877 DCHECK(thread_checker_.CalledOnValidThread());
gab7af9dc02017-05-05 13:38:5478
[email protected]8e937c1e2012-06-28 22:57:3079 if (!BeforeRun())
80 return;
vadimt12f0f7d2014-09-15 19:19:3881
82 // Use task stopwatch to exclude the loop run time from the current task, if
83 // any.
84 tracked_objects::TaskStopwatch stopwatch;
vadimt20175532014-10-28 20:14:2085 stopwatch.Start();
gab273551962017-05-18 06:01:1086 delegate_->Run();
vadimt12f0f7d2014-09-15 19:19:3887 stopwatch.Stop();
88
[email protected]8e937c1e2012-06-28 22:57:3089 AfterRun();
90}
91
92void RunLoop::RunUntilIdle() {
gab7af9dc02017-05-05 13:38:5493 DCHECK(thread_checker_.CalledOnValidThread());
94
[email protected]8e937c1e2012-06-28 22:57:3095 quit_when_idle_received_ = true;
96 Run();
97}
98
99void RunLoop::Quit() {
ahest72c1b442016-12-09 20:40:38100 DCHECK(thread_checker_.CalledOnValidThread());
gab7af9dc02017-05-05 13:38:54101
[email protected]8e937c1e2012-06-28 22:57:30102 quit_called_ = true;
gab273551962017-05-18 06:01:10103 if (running_ && delegate_->active_run_loops_.top() == this) {
[email protected]8e937c1e2012-06-28 22:57:30104 // This is the inner-most RunLoop, so quit now.
gab273551962017-05-18 06:01:10105 delegate_->Quit();
[email protected]8e937c1e2012-06-28 22:57:30106 }
107}
108
fdoraya4f28ec2016-06-10 00:08:58109void RunLoop::QuitWhenIdle() {
ahest72c1b442016-12-09 20:40:38110 DCHECK(thread_checker_.CalledOnValidThread());
fdoraya4f28ec2016-06-10 00:08:58111 quit_when_idle_received_ = true;
112}
113
[email protected]8e937c1e2012-06-28 22:57:30114base::Closure RunLoop::QuitClosure() {
gab7af9dc02017-05-05 13:38:54115 // TODO(gab): Fix bad usage and enable this check, http://crbug.com/715235.
116 // DCHECK(thread_checker_.CalledOnValidThread());
[email protected]8e937c1e2012-06-28 22:57:30117 return base::Bind(&RunLoop::Quit, weak_factory_.GetWeakPtr());
118}
119
fdoraya3658602016-06-10 18:23:15120base::Closure RunLoop::QuitWhenIdleClosure() {
gab7af9dc02017-05-05 13:38:54121 // TODO(gab): Fix bad usage and enable this check, http://crbug.com/715235.
122 // DCHECK(thread_checker_.CalledOnValidThread());
fdoraya3658602016-06-10 18:23:15123 return base::Bind(&RunLoop::QuitWhenIdle, weak_factory_.GetWeakPtr());
124}
125
gab7af9dc02017-05-05 13:38:54126// static
gab7af9dc02017-05-05 13:38:54127bool RunLoop::IsRunningOnCurrentThread() {
gab273551962017-05-18 06:01:10128 Delegate* delegate = tls_delegate.Get().Get();
129 return delegate && !delegate->active_run_loops_.empty();
gab7af9dc02017-05-05 13:38:54130}
131
132// static
133bool RunLoop::IsNestedOnCurrentThread() {
gab273551962017-05-18 06:01:10134 Delegate* delegate = tls_delegate.Get().Get();
135 return delegate && delegate->active_run_loops_.size() > 1;
gab7af9dc02017-05-05 13:38:54136}
137
138// static
139void RunLoop::AddNestingObserverOnCurrentThread(NestingObserver* observer) {
gab273551962017-05-18 06:01:10140 Delegate* delegate = tls_delegate.Get().Get();
141 DCHECK(delegate);
142 CHECK(delegate->allow_nesting_);
143 delegate->nesting_observers_.AddObserver(observer);
gab7af9dc02017-05-05 13:38:54144}
145
146// static
147void RunLoop::RemoveNestingObserverOnCurrentThread(NestingObserver* observer) {
gab273551962017-05-18 06:01:10148 Delegate* delegate = tls_delegate.Get().Get();
149 DCHECK(delegate);
150 CHECK(delegate->allow_nesting_);
151 delegate->nesting_observers_.RemoveObserver(observer);
gab7af9dc02017-05-05 13:38:54152}
153
154// static
155bool RunLoop::IsNestingAllowedOnCurrentThread() {
gab273551962017-05-18 06:01:10156 return tls_delegate.Get().Get()->allow_nesting_;
gab7af9dc02017-05-05 13:38:54157}
158
159// static
160void RunLoop::DisallowNestingOnCurrentThread() {
gab273551962017-05-18 06:01:10161 tls_delegate.Get().Get()->allow_nesting_ = false;
gab7af9dc02017-05-05 13:38:54162}
163
[email protected]8e937c1e2012-06-28 22:57:30164bool RunLoop::BeforeRun() {
gab7af9dc02017-05-05 13:38:54165 DCHECK(thread_checker_.CalledOnValidThread());
166
[email protected]8e937c1e2012-06-28 22:57:30167 DCHECK(!run_called_);
168 run_called_ = true;
169
170 // Allow Quit to be called before Run.
171 if (quit_called_)
172 return false;
173
gab273551962017-05-18 06:01:10174 auto& active_run_loops_ = delegate_->active_run_loops_;
175 active_run_loops_.push(this);
[email protected]8e937c1e2012-06-28 22:57:30176
gab273551962017-05-18 06:01:10177 const bool is_nested = active_run_loops_.size() > 1;
gab7af9dc02017-05-05 13:38:54178
179 if (is_nested) {
gab273551962017-05-18 06:01:10180 CHECK(delegate_->allow_nesting_);
181 for (auto& observer : delegate_->nesting_observers_)
gab7af9dc02017-05-05 13:38:54182 observer.OnBeginNestedRunLoop();
183 }
jamescookaacdfd02016-04-28 00:50:03184
[email protected]8e937c1e2012-06-28 22:57:30185 running_ = true;
186 return true;
187}
188
189void RunLoop::AfterRun() {
gab7af9dc02017-05-05 13:38:54190 DCHECK(thread_checker_.CalledOnValidThread());
191
[email protected]8e937c1e2012-06-28 22:57:30192 running_ = false;
193
gab273551962017-05-18 06:01:10194 auto& active_run_loops_ = delegate_->active_run_loops_;
195 DCHECK_EQ(active_run_loops_.top(), this);
196 active_run_loops_.pop();
gab7af9dc02017-05-05 13:38:54197
198 RunLoop* previous_run_loop =
gab273551962017-05-18 06:01:10199 active_run_loops_.empty() ? nullptr : active_run_loops_.top();
[email protected]8e937c1e2012-06-28 22:57:30200
201 // Execute deferred QuitNow, if any:
gab7af9dc02017-05-05 13:38:54202 if (previous_run_loop && previous_run_loop->quit_called_)
gab273551962017-05-18 06:01:10203 delegate_->Quit();
[email protected]8e937c1e2012-06-28 22:57:30204}
205
206} // namespace base