blob: e6b0b0483ca1734c4d4290bfa071469f0ddf2562 [file] [log] [blame]
Avi Drissmane4622aa2022-09-08 20:36:061// Copyright 2012 The Chromium Authors
[email protected]8e937c1e2012-06-28 22:57:302// 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
Wezd9e4cb772019-01-09 03:07:037#include "base/cancelable_callback.h"
Gabriel Charette2a53350172021-05-06 20:22:558#include "base/check.h"
Peter Kasting960e2d32023-03-14 17:18:419#include "base/compiler_specific.h"
Avi Drissman63e1f992023-01-13 18:54:4310#include "base/functional/bind.h"
11#include "base/functional/callback.h"
David Sanders97aac0de2022-02-07 14:33:5312#include "base/observer_list.h"
Patrick Monette643cdf62021-10-15 19:13:4213#include "base/task/single_thread_task_runner.h"
Gabriel Charettefbf3c622020-11-19 14:51:1314#include "base/trace_event/base_tracing.h"
avi9b6f42932015-12-26 22:15:1415#include "build/build_config.h"
Peter Kasting960e2d32023-03-14 17:18:4116#include "third_party/abseil-cpp/absl/base/attributes.h"
[email protected]8e937c1e2012-06-28 22:57:3017
18namespace base {
19
gab7af9dc02017-05-05 13:38:5420namespace {
21
Peter Kasting960e2d32023-03-14 17:18:4122ABSL_CONST_INIT thread_local RunLoop::Delegate* delegate = nullptr;
23ABSL_CONST_INIT thread_local const RunLoop::RunLoopTimeout* run_loop_timeout =
24 nullptr;
gab7af9dc02017-05-05 13:38:5425
gabcf5e4ce2017-05-19 22:56:5726// Runs |closure| immediately if this is called on |task_runner|, otherwise
27// forwards |closure| to it.
28void ProxyToTaskRunner(scoped_refptr<SequencedTaskRunner> task_runner,
29 OnceClosure closure) {
30 if (task_runner->RunsTasksInCurrentSequence()) {
31 std::move(closure).Run();
32 return;
33 }
34 task_runner->PostTask(FROM_HERE, std::move(closure));
35}
36
danakje125e8d62021-01-21 22:06:3137void OnRunLoopTimeout(RunLoop* run_loop,
38 const Location& location,
39 OnceCallback<void(const Location&)> on_timeout) {
Wezd9e4cb772019-01-09 03:07:0340 run_loop->Quit();
danakje125e8d62021-01-21 22:06:3141 std::move(on_timeout).Run(location);
Wezd9e4cb772019-01-09 03:07:0342}
43
gab7af9dc02017-05-05 13:38:5444} // namespace
45
Gabriel Charette50518fc2018-05-22 17:53:2246RunLoop::Delegate::Delegate() {
gab273551962017-05-18 06:01:1047 // The Delegate can be created on another thread. It is only bound in
48 // RegisterDelegateForCurrentThread().
49 DETACH_FROM_THREAD(bound_thread_checker_);
50}
51
52RunLoop::Delegate::~Delegate() {
53 DCHECK_CALLED_ON_VALID_THREAD(bound_thread_checker_);
Gabriel Charetteb87279d2019-06-27 03:21:0454 DCHECK(active_run_loops_.empty());
gab273551962017-05-18 06:01:1055 // A RunLoop::Delegate may be destroyed before it is bound, if so it may still
56 // be on its creation thread (e.g. a Thread that fails to start) and
57 // shouldn't disrupt that thread's state.
Yannic Bonenberger3dcd7fe2019-06-08 11:01:4558 if (bound_) {
Peter Kasting960e2d32023-03-14 17:18:4159 DCHECK_EQ(this, delegate);
60 delegate = nullptr;
Yannic Bonenberger3dcd7fe2019-06-08 11:01:4561 }
gab273551962017-05-18 06:01:1062}
63
Gabriel Charettea3ec9612017-12-14 17:22:4064bool RunLoop::Delegate::ShouldQuitWhenIdle() {
Gabriel Charettefbf3c622020-11-19 14:51:1365 const auto* top_loop = active_run_loops_.top();
Gabriel Charette2a53350172021-05-06 20:22:5566 if (top_loop->quit_when_idle_) {
Bruce Dawson9066bfa2021-10-14 06:17:2767 TRACE_EVENT_WITH_FLOW0("toplevel.flow", "RunLoop_ExitedOnIdle",
68 TRACE_ID_LOCAL(top_loop), TRACE_EVENT_FLAG_FLOW_IN);
Gabriel Charettefbf3c622020-11-19 14:51:1369 return true;
70 }
71 return false;
gab273551962017-05-18 06:01:1072}
73
Gabriel Charette0592c3a2017-07-26 12:02:0474// static
Peter Kasting960e2d32023-03-14 17:18:4175void RunLoop::RegisterDelegateForCurrentThread(Delegate* new_delegate) {
gab273551962017-05-18 06:01:1076 // Bind |delegate| to this thread.
Peter Kasting960e2d32023-03-14 17:18:4177 DCHECK(!new_delegate->bound_);
78 DCHECK_CALLED_ON_VALID_THREAD(new_delegate->bound_thread_checker_);
gab273551962017-05-18 06:01:1079
80 // There can only be one RunLoop::Delegate per thread.
Peter Kasting960e2d32023-03-14 17:18:4181 DCHECK(!delegate)
Gabriel Charettefa5e7f0d2018-01-29 12:08:2182 << "Error: Multiple RunLoop::Delegates registered on the same thread.\n\n"
83 "Hint: You perhaps instantiated a second "
Gabriel Charette694c3c332019-08-19 14:53:0584 "MessageLoop/TaskEnvironment on a thread that already had one?";
Peter Kasting960e2d32023-03-14 17:18:4185 delegate = new_delegate;
gab273551962017-05-18 06:01:1086 delegate->bound_ = true;
gab273551962017-05-18 06:01:1087}
88
Gabriel Charette3ff403e2017-08-07 04:22:4889RunLoop::RunLoop(Type type)
Peter Kasting960e2d32023-03-14 17:18:4190 : delegate_(delegate),
Gabriel Charette3ff403e2017-08-07 04:22:4891 type_(type),
Sean Maher7d0e8052022-12-09 01:46:3292 origin_task_runner_(SingleThreadTaskRunner::GetCurrentDefault()) {
Gabriel Charettee2b632b2017-08-02 03:52:1693 DCHECK(delegate_) << "A RunLoop::Delegate must be bound to this thread prior "
94 "to using RunLoop.";
gabcf5e4ce2017-05-19 22:56:5795 DCHECK(origin_task_runner_);
[email protected]8e937c1e2012-06-28 22:57:3096}
97
[email protected]8e937c1e2012-06-28 22:57:3098RunLoop::~RunLoop() {
Gabriel Charette78fd3352019-08-01 23:24:1599 // ~RunLoop() must happen-after the RunLoop is done running but it doesn't
100 // have to be on |sequence_checker_| (it usually is but sometimes it can be a
101 // member of a RefCountedThreadSafe object and be destroyed on another thread
102 // after being quit).
103 DCHECK(!running_);
[email protected]8e937c1e2012-06-28 22:57:30104}
105
danakje125e8d62021-01-21 22:06:31106void RunLoop::Run(const Location& location) {
gab980a52712017-05-18 16:20:16107 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Alexander Timin26ac38f2021-10-05 00:50:36108 // "test" tracing category is used here because in regular scenarios RunLoop
109 // trace events are not useful (each process normally has one RunLoop covering
110 // its entire lifetime) and might be confusing (they make idle processes look
111 // non-idle). In tests, however, creating a RunLoop is a frequent and an
112 // explicit action making this trace event very useful.
113 TRACE_EVENT("test", "RunLoop::Run", "location", location);
gab7af9dc02017-05-05 13:38:54114
[email protected]8e937c1e2012-06-28 22:57:30115 if (!BeforeRun())
116 return;
vadimt12f0f7d2014-09-15 19:19:38117
Wez9d5dd282020-02-10 17:21:22118 // If there is a RunLoopTimeout active then set the timeout.
Wezd9e4cb772019-01-09 03:07:03119 // TODO(crbug.com/905412): Use real-time for Run() timeouts so that they
120 // can be applied even in tests which mock TimeTicks::Now().
121 CancelableOnceClosure cancelable_timeout;
Wez9d5dd282020-02-10 17:21:22122 const RunLoopTimeout* run_timeout = GetTimeoutForCurrentThread();
Wezbbffcc52019-02-21 02:01:20123 if (run_timeout) {
danakje125e8d62021-01-21 22:06:31124 cancelable_timeout.Reset(BindOnce(&OnRunLoopTimeout, Unretained(this),
125 location, run_timeout->on_timeout));
Wez9d5dd282020-02-10 17:21:22126 origin_task_runner_->PostDelayedTask(
127 FROM_HERE, cancelable_timeout.callback(), run_timeout->timeout);
Wezd9e4cb772019-01-09 03:07:03128 }
129
Gabriel Charetteb030a4a2017-10-26 01:04:40130 DCHECK_EQ(this, delegate_->active_run_loops_.top());
131 const bool application_tasks_allowed =
132 delegate_->active_run_loops_.size() == 1U ||
133 type_ == Type::kNestableTasksAllowed;
Alex Clarkecfde7b32019-08-15 17:11:47134 delegate_->Run(application_tasks_allowed, TimeDelta::Max());
vadimt12f0f7d2014-09-15 19:19:38135
[email protected]8e937c1e2012-06-28 22:57:30136 AfterRun();
137}
138
139void RunLoop::RunUntilIdle() {
gab980a52712017-05-18 16:20:16140 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
gab7af9dc02017-05-05 13:38:54141
Gabriel Charette2a53350172021-05-06 20:22:55142 quit_when_idle_ = true;
[email protected]8e937c1e2012-06-28 22:57:30143 Run();
Gabriel Charette2a53350172021-05-06 20:22:55144
145 if (!AnyQuitCalled()) {
146 quit_when_idle_ = false;
147#if DCHECK_IS_ON()
148 run_allowed_ = true;
149#endif
150 }
[email protected]8e937c1e2012-06-28 22:57:30151}
152
153void RunLoop::Quit() {
gabcf5e4ce2017-05-19 22:56:57154 // Thread-safe.
155
Gabriel Charette3a2ce022020-05-28 19:44:35156 // This can only be hit if RunLoop::Quit() is called directly (QuitClosure()
gabcf5e4ce2017-05-19 22:56:57157 // proxies through ProxyToTaskRunner() as it can only deref its WeakPtr on
158 // |origin_task_runner_|).
159 if (!origin_task_runner_->RunsTasksInCurrentSequence()) {
Wezd9e4cb772019-01-09 03:07:03160 origin_task_runner_->PostTask(FROM_HERE,
161 BindOnce(&RunLoop::Quit, Unretained(this)));
gabcf5e4ce2017-05-19 22:56:57162 return;
163 }
gab7af9dc02017-05-05 13:38:54164
Bruce Dawson9066bfa2021-10-14 06:17:27165 // While Quit() is an "OUT" call to reach one of the quit-states ("IN"),
166 // OUT|IN is used to visually link multiple Quit*() together which can help
167 // when debugging flaky tests.
168 TRACE_EVENT_WITH_FLOW0("toplevel.flow", "RunLoop::Quit", TRACE_ID_LOCAL(this),
169 TRACE_EVENT_FLAG_FLOW_OUT | TRACE_EVENT_FLAG_FLOW_IN);
Gabriel Charettefbf3c622020-11-19 14:51:13170
[email protected]8e937c1e2012-06-28 22:57:30171 quit_called_ = true;
gab273551962017-05-18 06:01:10172 if (running_ && delegate_->active_run_loops_.top() == this) {
[email protected]8e937c1e2012-06-28 22:57:30173 // This is the inner-most RunLoop, so quit now.
gab273551962017-05-18 06:01:10174 delegate_->Quit();
[email protected]8e937c1e2012-06-28 22:57:30175 }
176}
177
fdoraya4f28ec2016-06-10 00:08:58178void RunLoop::QuitWhenIdle() {
gabcf5e4ce2017-05-19 22:56:57179 // Thread-safe.
180
Gabriel Charette3a2ce022020-05-28 19:44:35181 // This can only be hit if RunLoop::QuitWhenIdle() is called directly
gabcf5e4ce2017-05-19 22:56:57182 // (QuitWhenIdleClosure() proxies through ProxyToTaskRunner() as it can only
183 // deref its WeakPtr on |origin_task_runner_|).
184 if (!origin_task_runner_->RunsTasksInCurrentSequence()) {
185 origin_task_runner_->PostTask(
Wezd9e4cb772019-01-09 03:07:03186 FROM_HERE, BindOnce(&RunLoop::QuitWhenIdle, Unretained(this)));
gabcf5e4ce2017-05-19 22:56:57187 return;
188 }
189
Bruce Dawson9066bfa2021-10-14 06:17:27190 // OUT|IN as in Quit() to link all Quit*() together should there be multiple.
191 TRACE_EVENT_WITH_FLOW0("toplevel.flow", "RunLoop::QuitWhenIdle",
192 TRACE_ID_LOCAL(this),
193 TRACE_EVENT_FLAG_FLOW_OUT | TRACE_EVENT_FLAG_FLOW_IN);
Gabriel Charettefbf3c622020-11-19 14:51:13194
Gabriel Charette2a53350172021-05-06 20:22:55195 quit_when_idle_ = true;
196 quit_when_idle_called_ = true;
fdoraya4f28ec2016-06-10 00:08:58197}
198
kylechar650caf02019-07-17 03:25:41199RepeatingClosure RunLoop::QuitClosure() {
Gabriel Charette3a2ce022020-05-28 19:44:35200 // Obtaining the QuitClosure() is not thread-safe; either obtain the
201 // QuitClosure() from the owning thread before Run() or invoke Quit() directly
202 // (which is thread-safe).
Gabriel Charetted913e5b2019-06-27 05:41:59203 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Wez325eafc2018-07-17 17:01:49204 allow_quit_current_deprecated_ = false;
gabcf5e4ce2017-05-19 22:56:57205
kylechar650caf02019-07-17 03:25:41206 return BindRepeating(
207 &ProxyToTaskRunner, origin_task_runner_,
208 BindRepeating(&RunLoop::Quit, weak_factory_.GetWeakPtr()));
[email protected]8e937c1e2012-06-28 22:57:30209}
210
kylechar650caf02019-07-17 03:25:41211RepeatingClosure RunLoop::QuitWhenIdleClosure() {
Gabriel Charette3a2ce022020-05-28 19:44:35212 // Obtaining the QuitWhenIdleClosure() is not thread-safe; either obtain the
213 // QuitWhenIdleClosure() from the owning thread before Run() or invoke
214 // QuitWhenIdle() directly (which is thread-safe).
Gabriel Charetted913e5b2019-06-27 05:41:59215 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Wez325eafc2018-07-17 17:01:49216 allow_quit_current_deprecated_ = false;
gabcf5e4ce2017-05-19 22:56:57217
kylechar650caf02019-07-17 03:25:41218 return BindRepeating(
219 &ProxyToTaskRunner, origin_task_runner_,
220 BindRepeating(&RunLoop::QuitWhenIdle, weak_factory_.GetWeakPtr()));
fdoraya3658602016-06-10 18:23:15221}
222
Gabriel Charette2a53350172021-05-06 20:22:55223bool RunLoop::AnyQuitCalled() {
224 return quit_called_ || quit_when_idle_called_;
225}
226
gab7af9dc02017-05-05 13:38:54227// static
gab7af9dc02017-05-05 13:38:54228bool RunLoop::IsRunningOnCurrentThread() {
gab273551962017-05-18 06:01:10229 return delegate && !delegate->active_run_loops_.empty();
gab7af9dc02017-05-05 13:38:54230}
231
232// static
233bool RunLoop::IsNestedOnCurrentThread() {
gab273551962017-05-18 06:01:10234 return delegate && delegate->active_run_loops_.size() > 1;
gab7af9dc02017-05-05 13:38:54235}
236
237// static
238void RunLoop::AddNestingObserverOnCurrentThread(NestingObserver* observer) {
gab273551962017-05-18 06:01:10239 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 DCHECK(delegate);
gab273551962017-05-18 06:01:10246 delegate->nesting_observers_.RemoveObserver(observer);
gab7af9dc02017-05-05 13:38:54247}
248
249// static
Gabriel Charette0592c3a2017-07-26 12:02:04250void RunLoop::QuitCurrentDeprecated() {
251 DCHECK(IsRunningOnCurrentThread());
Wez325eafc2018-07-17 17:01:49252 DCHECK(delegate->active_run_loops_.top()->allow_quit_current_deprecated_)
253 << "Please migrate off QuitCurrentDeprecated(), e.g. to QuitClosure().";
254 delegate->active_run_loops_.top()->Quit();
Gabriel Charette0592c3a2017-07-26 12:02:04255}
256
257// static
258void RunLoop::QuitCurrentWhenIdleDeprecated() {
259 DCHECK(IsRunningOnCurrentThread());
Wez325eafc2018-07-17 17:01:49260 DCHECK(delegate->active_run_loops_.top()->allow_quit_current_deprecated_)
261 << "Please migrate off QuitCurrentWhenIdleDeprecated(), e.g. to "
262 "QuitWhenIdleClosure().";
263 delegate->active_run_loops_.top()->QuitWhenIdle();
Gabriel Charette0592c3a2017-07-26 12:02:04264}
265
Gabriel Charette5bc88412018-05-16 03:28:25266// static
kylechar650caf02019-07-17 03:25:41267RepeatingClosure RunLoop::QuitCurrentWhenIdleClosureDeprecated() {
Wez325eafc2018-07-17 17:01:49268 // TODO(844016): Fix callsites and enable this check, or remove the API.
Wez325eafc2018-07-17 17:01:49269 // DCHECK(delegate->active_run_loops_.top()->allow_quit_current_deprecated_)
270 // << "Please migrate off QuitCurrentWhenIdleClosureDeprecated(), e.g to "
271 // "QuitWhenIdleClosure().";
kylechar650caf02019-07-17 03:25:41272 return BindRepeating(&RunLoop::QuitCurrentWhenIdleDeprecated);
Gabriel Charette5bc88412018-05-16 03:28:25273}
274
Gabriel Charettea44975052017-08-21 23:14:04275#if DCHECK_IS_ON()
Hans Wennborg9b292ba2022-02-15 16:01:29276ScopedDisallowRunningRunLoop::ScopedDisallowRunningRunLoop()
Peter Kasting960e2d32023-03-14 17:18:41277 : current_delegate_(delegate),
278 previous_run_allowance_(current_delegate_ &&
279 current_delegate_->allow_running_for_testing_) {
Gabriel Charettea44975052017-08-21 23:14:04280 if (current_delegate_)
281 current_delegate_->allow_running_for_testing_ = false;
282}
283
Hans Wennborg9b292ba2022-02-15 16:01:29284ScopedDisallowRunningRunLoop::~ScopedDisallowRunningRunLoop() {
Peter Kasting960e2d32023-03-14 17:18:41285 DCHECK_EQ(current_delegate_, delegate);
Gabriel Charettea44975052017-08-21 23:14:04286 if (current_delegate_)
287 current_delegate_->allow_running_for_testing_ = previous_run_allowance_;
288}
289#else // DCHECK_IS_ON()
290// Defined out of line so that the compiler doesn't inline these and realize
291// the scope has no effect and then throws an "unused variable" warning in
292// non-dcheck builds.
Hans Wennborg9b292ba2022-02-15 16:01:29293ScopedDisallowRunningRunLoop::ScopedDisallowRunningRunLoop() = default;
294ScopedDisallowRunningRunLoop::~ScopedDisallowRunningRunLoop() = default;
Gabriel Charettea44975052017-08-21 23:14:04295#endif // DCHECK_IS_ON()
296
Wez9d5dd282020-02-10 17:21:22297RunLoop::RunLoopTimeout::RunLoopTimeout() = default;
298
299RunLoop::RunLoopTimeout::~RunLoopTimeout() = default;
300
301// static
302void RunLoop::SetTimeoutForCurrentThread(const RunLoopTimeout* timeout) {
Peter Kasting960e2d32023-03-14 17:18:41303 run_loop_timeout = timeout;
Wez9d5dd282020-02-10 17:21:22304}
305
306// static
307const RunLoop::RunLoopTimeout* RunLoop::GetTimeoutForCurrentThread() {
Peter Kasting960e2d32023-03-14 17:18:41308 // Workaround false-positive MSAN use-of-uninitialized-value on
309 // thread_local storage for loaded libraries:
310 // https://github.com/google/sanitizers/issues/1265
311 MSAN_UNPOISON(&run_loop_timeout, sizeof(RunLoopTimeout*));
312
313 return run_loop_timeout;
Wez212eeb72020-02-04 17:54:54314}
315
[email protected]8e937c1e2012-06-28 22:57:30316bool RunLoop::BeforeRun() {
gab980a52712017-05-18 16:20:16317 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
gab7af9dc02017-05-05 13:38:54318
gabcf5e4ce2017-05-19 22:56:57319#if DCHECK_IS_ON()
Gabriel Charettea44975052017-08-21 23:14:04320 DCHECK(delegate_->allow_running_for_testing_)
321 << "RunLoop::Run() isn't allowed in the scope of a "
Hans Wennborg9b292ba2022-02-15 16:01:29322 "ScopedDisallowRunningRunLoop. Hint: if mixing "
Gabriel Charettea44975052017-08-21 23:14:04323 "TestMockTimeTaskRunners on same thread, use TestMockTimeTaskRunner's "
324 "API instead of RunLoop to drive individual task runners.";
Gabriel Charette2a53350172021-05-06 20:22:55325 DCHECK(run_allowed_);
326 run_allowed_ = false;
gabcf5e4ce2017-05-19 22:56:57327#endif // DCHECK_IS_ON()
[email protected]8e937c1e2012-06-28 22:57:30328
329 // Allow Quit to be called before Run.
Gabriel Charettefbf3c622020-11-19 14:51:13330 if (quit_called_) {
Bruce Dawson9066bfa2021-10-14 06:17:27331 TRACE_EVENT_WITH_FLOW0("toplevel.flow", "RunLoop_ExitedEarly",
332 TRACE_ID_LOCAL(this), TRACE_EVENT_FLAG_FLOW_IN);
[email protected]8e937c1e2012-06-28 22:57:30333 return false;
Gabriel Charettefbf3c622020-11-19 14:51:13334 }
[email protected]8e937c1e2012-06-28 22:57:30335
Gabriel Charetteb87279d2019-06-27 03:21:04336 auto& active_run_loops = delegate_->active_run_loops_;
337 active_run_loops.push(this);
[email protected]8e937c1e2012-06-28 22:57:30338
Gabriel Charetteb87279d2019-06-27 03:21:04339 const bool is_nested = active_run_loops.size() > 1;
gab7af9dc02017-05-05 13:38:54340
341 if (is_nested) {
gab273551962017-05-18 06:01:10342 for (auto& observer : delegate_->nesting_observers_)
gab7af9dc02017-05-05 13:38:54343 observer.OnBeginNestedRunLoop();
Gabriel Charette3ff403e2017-08-07 04:22:48344 if (type_ == Type::kNestableTasksAllowed)
345 delegate_->EnsureWorkScheduled();
gab7af9dc02017-05-05 13:38:54346 }
jamescookaacdfd02016-04-28 00:50:03347
[email protected]8e937c1e2012-06-28 22:57:30348 running_ = true;
349 return true;
350}
351
352void RunLoop::AfterRun() {
gab980a52712017-05-18 16:20:16353 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
gab7af9dc02017-05-05 13:38:54354
[email protected]8e937c1e2012-06-28 22:57:30355 running_ = false;
356
Bruce Dawson9066bfa2021-10-14 06:17:27357 TRACE_EVENT_WITH_FLOW0("toplevel.flow", "RunLoop_Exited",
358 TRACE_ID_LOCAL(this), TRACE_EVENT_FLAG_FLOW_IN);
Gabriel Charettefbf3c622020-11-19 14:51:13359
Gabriel Charetteb87279d2019-06-27 03:21:04360 auto& active_run_loops = delegate_->active_run_loops_;
361 DCHECK_EQ(active_run_loops.top(), this);
362 active_run_loops.pop();
gab7af9dc02017-05-05 13:38:54363
Gabriel Charetteb87279d2019-06-27 03:21:04364 // Exiting a nested RunLoop?
365 if (!active_run_loops.empty()) {
Francois Doray80bdddf2018-01-04 16:17:32366 for (auto& observer : delegate_->nesting_observers_)
367 observer.OnExitNestedRunLoop();
Francois Doray80bdddf2018-01-04 16:17:32368
Gabriel Charetteb87279d2019-06-27 03:21:04369 // Execute deferred Quit, if any:
370 if (active_run_loops.top()->quit_called_)
371 delegate_->Quit();
372 }
[email protected]8e937c1e2012-06-28 22:57:30373}
374
375} // namespace base