Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(221)

Side by Side Diff: base/run_loop.cc

Issue 2892993002: Make RunLoop::Quit() thread-safe. (Closed)
Patch Set: Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "base/run_loop.h" 5 #include "base/run_loop.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/callback.h"
8 #include "base/lazy_instance.h" 9 #include "base/lazy_instance.h"
10 #include "base/single_thread_task_runner.h"
9 #include "base/threading/thread_local.h" 11 #include "base/threading/thread_local.h"
12 #include "base/threading/thread_task_runner_handle.h"
10 #include "base/tracked_objects.h" 13 #include "base/tracked_objects.h"
11 #include "build/build_config.h" 14 #include "build/build_config.h"
12 15
13 namespace base { 16 namespace base {
14 17
15 namespace { 18 namespace {
16 19
17 LazyInstance<ThreadLocalPointer<RunLoop::Delegate>>::Leaky tls_delegate = 20 LazyInstance<ThreadLocalPointer<RunLoop::Delegate>>::Leaky tls_delegate =
18 LAZY_INSTANCE_INITIALIZER; 21 LAZY_INSTANCE_INITIALIZER;
19 22
23 // Runs |closure| immediately if this is called on |task_runner|, otherwise
24 // forwards |closure| to it.
25 void ProxyToTaskRunner(scoped_refptr<SequencedTaskRunner> task_runner,
26 OnceClosure closure) {
27 if (task_runner->RunsTasksInCurrentSequence()) {
danakj 2017/05/19 18:11:30 This if (on thread) A else post A pattern always i
gab 2017/05/19 19:38:16 Discussed offline, won't do that because the goal
28 std::move(closure).Run();
29 return;
30 }
31 task_runner->PostTask(FROM_HERE, std::move(closure));
32 }
33
20 } // namespace 34 } // namespace
21 35
22 RunLoop::Delegate::Delegate() { 36 RunLoop::Delegate::Delegate() {
23 // The Delegate can be created on another thread. It is only bound in 37 // The Delegate can be created on another thread. It is only bound in
24 // RegisterDelegateForCurrentThread(). 38 // RegisterDelegateForCurrentThread().
25 DETACH_FROM_THREAD(bound_thread_checker_); 39 DETACH_FROM_THREAD(bound_thread_checker_);
26 } 40 }
27 41
28 RunLoop::Delegate::~Delegate() { 42 RunLoop::Delegate::~Delegate() {
29 DCHECK_CALLED_ON_VALID_THREAD(bound_thread_checker_); 43 DCHECK_CALLED_ON_VALID_THREAD(bound_thread_checker_);
(...skipping 26 matching lines...) Expand all
56 DCHECK_CALLED_ON_VALID_THREAD(delegate->bound_thread_checker_); 70 DCHECK_CALLED_ON_VALID_THREAD(delegate->bound_thread_checker_);
57 71
58 // There can only be one RunLoop::Delegate per thread. 72 // There can only be one RunLoop::Delegate per thread.
59 DCHECK(!tls_delegate.Get().Get()); 73 DCHECK(!tls_delegate.Get().Get());
60 tls_delegate.Get().Set(delegate); 74 tls_delegate.Get().Set(delegate);
61 delegate->bound_ = true; 75 delegate->bound_ = true;
62 76
63 return &delegate->client_interface_; 77 return &delegate->client_interface_;
64 } 78 }
65 79
66 RunLoop::RunLoop() : delegate_(tls_delegate.Get().Get()), weak_factory_(this) { 80 RunLoop::RunLoop()
81 : delegate_(tls_delegate.Get().Get()),
82 origin_task_runner_(ThreadTaskRunnerHandle::Get()),
83 weak_factory_(this) {
67 // A RunLoop::Delegate must be bound to this thread prior to using RunLoop. 84 // A RunLoop::Delegate must be bound to this thread prior to using RunLoop.
68 DCHECK(delegate_); 85 DCHECK(delegate_);
86 DCHECK(origin_task_runner_);
69 } 87 }
70 88
71 RunLoop::~RunLoop() { 89 RunLoop::~RunLoop() {
72 // TODO(gab): Fix bad usage and enable this check, http://crbug.com/715235. 90 // TODO(gab): Fix bad usage and enable this check, http://crbug.com/715235.
73 // DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); 91 // DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
74 } 92 }
75 93
76 void RunLoop::Run() { 94 void RunLoop::Run() {
77 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); 95 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
78 96
(...skipping 22 matching lines...) Expand all
101 } 119 }
102 120
103 void RunLoop::RunUntilIdle() { 121 void RunLoop::RunUntilIdle() {
104 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); 122 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
105 123
106 quit_when_idle_received_ = true; 124 quit_when_idle_received_ = true;
107 Run(); 125 Run();
108 } 126 }
109 127
110 void RunLoop::Quit() { 128 void RunLoop::Quit() {
111 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); 129 // Thread-safe.
130
131 // This can only be hit if run_loop->Quit() is called directly (QuitClosure()
132 // proxies through ProxyToTaskRunner() as it can only deref its WeakPtr on
133 // |origin_task_runner_|).
134 if (!origin_task_runner_->RunsTasksInCurrentSequence()) {
135 origin_task_runner_->PostTask(FROM_HERE,
136 base::Bind(&RunLoop::Quit, Unretained(this)));
137 return;
138 }
112 139
113 quit_called_ = true; 140 quit_called_ = true;
114 if (running_ && delegate_->active_run_loops_.top() == this) { 141 if (running_ && delegate_->active_run_loops_.top() == this) {
115 // This is the inner-most RunLoop, so quit now. 142 // This is the inner-most RunLoop, so quit now.
116 delegate_->Quit(); 143 delegate_->Quit();
117 } 144 }
118 } 145 }
119 146
120 void RunLoop::QuitWhenIdle() { 147 void RunLoop::QuitWhenIdle() {
121 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); 148 // Thread-safe.
149
150 // This can only be hit if run_loop->QuitWhenIdle() is called directly
151 // (QuitWhenIdleClosure() proxies through ProxyToTaskRunner() as it can only
152 // deref its WeakPtr on |origin_task_runner_|).
153 if (!origin_task_runner_->RunsTasksInCurrentSequence()) {
154 origin_task_runner_->PostTask(
155 FROM_HERE, base::Bind(&RunLoop::QuitWhenIdle, Unretained(this)));
156 return;
157 }
158
122 quit_when_idle_received_ = true; 159 quit_when_idle_received_ = true;
123 } 160 }
124 161
125 base::Closure RunLoop::QuitClosure() { 162 base::Closure RunLoop::QuitClosure() {
126 // TODO(gab): Fix bad usage and enable this check, http://crbug.com/715235. 163 // TODO(gab): Fix bad usage and enable this check, http://crbug.com/715235.
127 // DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); 164 // DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
128 return base::Bind(&RunLoop::Quit, weak_factory_.GetWeakPtr()); 165
166 // Need to use ProxyToTaskRunner() as WeakPtrs vended from
167 // |weak_factory_| may only be accessed on |origin_task_runner_|.
168 // TODO(gab): It feels wrong that QuitClosure() is bound to a WeakPtr.
169 return base::Bind(&ProxyToTaskRunner, origin_task_runner_,
170 base::Bind(&RunLoop::Quit, weak_factory_.GetWeakPtr()));
129 } 171 }
130 172
131 base::Closure RunLoop::QuitWhenIdleClosure() { 173 base::Closure RunLoop::QuitWhenIdleClosure() {
132 // TODO(gab): Fix bad usage and enable this check, http://crbug.com/715235. 174 // TODO(gab): Fix bad usage and enable this check, http://crbug.com/715235.
133 // DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); 175 // DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
134 return base::Bind(&RunLoop::QuitWhenIdle, weak_factory_.GetWeakPtr()); 176
177 // Need to use ProxyToTaskRunner() as WeakPtrs vended from
178 // |weak_factory_| may only be accessed on |origin_task_runner_|.
179 // TODO(gab): It feels wrong that QuitWhenIdleClosure() is bound to a WeakPtr.
180 return base::Bind(
181 &ProxyToTaskRunner, origin_task_runner_,
182 base::Bind(&RunLoop::QuitWhenIdle, weak_factory_.GetWeakPtr()));
135 } 183 }
136 184
137 // static 185 // static
138 bool RunLoop::IsRunningOnCurrentThread() { 186 bool RunLoop::IsRunningOnCurrentThread() {
139 Delegate* delegate = tls_delegate.Get().Get(); 187 Delegate* delegate = tls_delegate.Get().Get();
140 return delegate && !delegate->active_run_loops_.empty(); 188 return delegate && !delegate->active_run_loops_.empty();
141 } 189 }
142 190
143 // static 191 // static
144 bool RunLoop::IsNestedOnCurrentThread() { 192 bool RunLoop::IsNestedOnCurrentThread() {
(...skipping 23 matching lines...) Expand all
168 } 216 }
169 217
170 // static 218 // static
171 void RunLoop::DisallowNestingOnCurrentThread() { 219 void RunLoop::DisallowNestingOnCurrentThread() {
172 tls_delegate.Get().Get()->allow_nesting_ = false; 220 tls_delegate.Get().Get()->allow_nesting_ = false;
173 } 221 }
174 222
175 bool RunLoop::BeforeRun() { 223 bool RunLoop::BeforeRun() {
176 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); 224 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
177 225
226 #if DCHECK_IS_ON()
178 DCHECK(!run_called_); 227 DCHECK(!run_called_);
179 run_called_ = true; 228 run_called_ = true;
229 #endif // DCHECK_IS_ON()
180 230
181 // Allow Quit to be called before Run. 231 // Allow Quit to be called before Run.
182 if (quit_called_) 232 if (quit_called_)
183 return false; 233 return false;
184 234
185 auto& active_run_loops_ = delegate_->active_run_loops_; 235 auto& active_run_loops_ = delegate_->active_run_loops_;
186 active_run_loops_.push(this); 236 active_run_loops_.push(this);
187 237
188 const bool is_nested = active_run_loops_.size() > 1; 238 const bool is_nested = active_run_loops_.size() > 1;
189 239
(...skipping 18 matching lines...) Expand all
208 258
209 RunLoop* previous_run_loop = 259 RunLoop* previous_run_loop =
210 active_run_loops_.empty() ? nullptr : active_run_loops_.top(); 260 active_run_loops_.empty() ? nullptr : active_run_loops_.top();
211 261
212 // Execute deferred QuitNow, if any: 262 // Execute deferred QuitNow, if any:
213 if (previous_run_loop && previous_run_loop->quit_called_) 263 if (previous_run_loop && previous_run_loop->quit_called_)
214 delegate_->Quit(); 264 delegate_->Quit();
215 } 265 }
216 266
217 } // namespace base 267 } // namespace base
OLDNEW
« no previous file with comments | « base/run_loop.h ('k') | base/run_loop_unittest.cc » ('j') | base/run_loop_unittest.cc » ('J')

Powered by Google App Engine
This is Rietveld 408576698