• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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