OLD | NEW |
---|---|
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 #ifndef PPAPI_SHARED_IMPL_PROXY_LOCK_H_ | 5 #ifndef PPAPI_SHARED_IMPL_PROXY_LOCK_H_ |
6 #define PPAPI_SHARED_IMPL_PROXY_LOCK_H_ | 6 #define PPAPI_SHARED_IMPL_PROXY_LOCK_H_ |
7 | 7 |
8 #include "base/basictypes.h" | 8 #include "base/basictypes.h" |
9 #include "base/bind.h" | 9 #include "base/bind.h" |
10 #include "base/callback.h" | 10 #include "base/callback.h" |
11 #include "base/threading/thread_checker.h" | |
11 | 12 |
12 #include "ppapi/shared_impl/ppapi_shared_export.h" | 13 #include "ppapi/shared_impl/ppapi_shared_export.h" |
13 | 14 |
14 namespace base { | 15 namespace base { |
15 class Lock; | 16 class Lock; |
16 } | 17 } |
17 | 18 |
18 namespace ppapi { | 19 namespace ppapi { |
19 | 20 |
20 // This is the one lock to rule them all for the ppapi proxy. All PPB interface | 21 // This is the one lock to rule them all for the ppapi proxy. All PPB interface |
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
125 const P1& p1, | 126 const P1& p1, |
126 const P2& p2, | 127 const P2& p2, |
127 const P3& p3, | 128 const P3& p3, |
128 const P4& p4, | 129 const P4& p4, |
129 const P5& p5) { | 130 const P5& p5) { |
130 ProxyAutoUnlock unlock; | 131 ProxyAutoUnlock unlock; |
131 return function(p1, p2, p3, p4, p5); | 132 return function(p1, p2, p3, p4, p5); |
132 } | 133 } |
133 void PPAPI_SHARED_EXPORT CallWhileUnlocked(const base::Closure& closure); | 134 void PPAPI_SHARED_EXPORT CallWhileUnlocked(const base::Closure& closure); |
134 | 135 |
135 // CallWhileLocked locks the ProxyLock and runs the given closure immediately. | 136 namespace internal { |
136 // The lock is released when CallWhileLocked returns. This function assumes the | |
137 // lock is not held. This is mostly for use in RunWhileLocked; see below. | |
138 void PPAPI_SHARED_EXPORT CallWhileLocked(const base::Closure& closure); | |
139 | 137 |
140 // RunWhileLocked binds the given closure with CallWhileLocked and returns the | 138 template <typename RunType> |
141 // new Closure. This is for cases where you want to run a task, but you want to | 139 class RunWhileLockedHelper; |
142 // ensure that the ProxyLock is acquired for the duration of the task. | 140 |
141 template <> | |
142 class RunWhileLockedHelper<void ()> { | |
143 public: | |
144 typedef base::Callback<void ()> CallbackType; | |
145 explicit RunWhileLockedHelper(const CallbackType& callback) | |
146 : callback_(new CallbackType(callback)) { | |
147 // Copying |callback| may adjust reference counts for bound Vars or | |
148 // Resources; we should have the lock already. | |
149 ProxyLock::AssertAcquired(); | |
150 // CallWhileLocked and destruction might happen on a different thread from | |
151 // creation. | |
152 thread_checker_.DetachFromThread(); | |
153 } | |
154 void CallWhileLocked() { | |
155 // Bind thread_checker_ to this thread so we can check in the destructor. | |
156 DCHECK(thread_checker_.CalledOnValidThread()); | |
157 ProxyAutoLock lock; | |
158 { | |
159 // Use a scope and local Callback to ensure that the callback is cleared | |
160 // before the lock is released, even in the unlikely event that Run() | |
161 // throws an exception. | |
162 scoped_ptr<CallbackType> temp_callback(callback_.Pass()); | |
163 temp_callback->Run(); | |
164 } | |
165 } | |
166 | |
167 ~RunWhileLockedHelper() { | |
168 // Check that the Callback is destroyed on the same thread as where | |
169 // CallWhileLocked happened (if CallWhileLocked happened). | |
170 DCHECK(thread_checker_.CalledOnValidThread()); | |
171 // Here we read callback_ without the lock. This is why the callback must be | |
172 // destroyed on the same thread where it runs. There are 2 cases where | |
173 // callback_ will be NULL: | |
174 // 1) This is the original RunWhileLockedHelper that RunWhileLocked | |
175 // created. When it was copied somewhere else (e.g., to a MessageLoop | |
176 // queue), callback_ was passed to the new copy, and the original | |
177 // RunWhileLockedHelper's callback_ was set to NULL (since scoped_ptrs | |
178 // only ever have 1 owner). | |
teravest
2013/07/22 20:36:00
You should mention here that the proxy lock is sti
| |
179 // 2) callback_ has already been run via CallWhileLocked. | |
180 if (callback_) { | |
181 // If the callback was not run, we still need to have the lock when we | |
182 // destroy the callback in case it had a Resource bound to it. This | |
183 // ensures that the Resource's destructor is invoked only with the lock | |
184 // held. | |
185 // | |
186 // Also: Resource and Var inherit RefCounted (not ThreadSafeRefCounted), | |
187 // and these callbacks need to be usable on any thread. So we need to lock | |
188 // when releasing the callback to avoid ref counting races. | |
189 ProxyAutoLock lock; | |
190 callback_.reset(); | |
191 } | |
192 } | |
193 private: | |
194 scoped_ptr<CallbackType> callback_; | |
195 | |
196 // Used to ensure that the Callback is run and deleted on the same thread. | |
197 base::ThreadCheckerImpl thread_checker_; | |
198 }; | |
199 | |
200 template <typename P1> | |
201 class RunWhileLockedHelper<void (P1)> { | |
202 public: | |
203 typedef base::Callback<void (P1)> CallbackType; | |
204 explicit RunWhileLockedHelper(const CallbackType& callback) | |
205 : callback_(new CallbackType(callback)) { | |
206 ProxyLock::AssertAcquired(); | |
207 thread_checker_.DetachFromThread(); | |
208 } | |
209 void CallWhileLocked(P1 p1) { | |
210 DCHECK(thread_checker_.CalledOnValidThread()); | |
211 ProxyAutoLock lock; | |
212 { | |
213 scoped_ptr<CallbackType> temp_callback(callback_.Pass()); | |
214 temp_callback->Run(p1); | |
215 } | |
216 } | |
217 | |
218 ~RunWhileLockedHelper() { | |
219 DCHECK(thread_checker_.CalledOnValidThread()); | |
220 if (callback_) { | |
221 ProxyAutoLock lock; | |
222 callback_.reset(); | |
223 } | |
224 } | |
225 private: | |
226 scoped_ptr<CallbackType> callback_; | |
227 base::ThreadCheckerImpl thread_checker_; | |
228 }; | |
229 | |
230 template <typename P1, typename P2> | |
231 class RunWhileLockedHelper<void (P1, P2)> { | |
232 public: | |
233 typedef base::Callback<void (P1, P2)> CallbackType; | |
234 explicit RunWhileLockedHelper(const CallbackType& callback) | |
235 : callback_(new CallbackType(callback)) { | |
236 ProxyLock::AssertAcquired(); | |
237 thread_checker_.DetachFromThread(); | |
238 } | |
239 void CallWhileLocked(P1 p1, P2 p2) { | |
240 DCHECK(thread_checker_.CalledOnValidThread()); | |
241 ProxyAutoLock lock; | |
242 { | |
243 scoped_ptr<CallbackType> temp_callback(callback_.Pass()); | |
244 temp_callback->Run(p1, p2); | |
245 } | |
246 } | |
247 | |
248 ~RunWhileLockedHelper() { | |
249 DCHECK(thread_checker_.CalledOnValidThread()); | |
250 if (callback_) { | |
251 ProxyAutoLock lock; | |
252 callback_.reset(); | |
253 } | |
254 } | |
255 private: | |
256 scoped_ptr<CallbackType> callback_; | |
257 base::ThreadCheckerImpl thread_checker_; | |
258 }; | |
259 | |
260 template <typename P1, typename P2, typename P3> | |
261 class RunWhileLockedHelper<void (P1, P2, P3)> { | |
262 public: | |
263 typedef base::Callback<void (P1, P2, P3)> CallbackType; | |
264 explicit RunWhileLockedHelper(const CallbackType& callback) | |
265 : callback_(new CallbackType(callback)) { | |
266 ProxyLock::AssertAcquired(); | |
267 thread_checker_.DetachFromThread(); | |
268 } | |
269 void CallWhileLocked(P1 p1, P2 p2, P3 p3) { | |
270 DCHECK(thread_checker_.CalledOnValidThread()); | |
271 ProxyAutoLock lock; | |
272 { | |
273 scoped_ptr<CallbackType> temp_callback(callback_.Pass()); | |
274 temp_callback->Run(p1, p2, p3); | |
275 } | |
276 } | |
277 | |
278 ~RunWhileLockedHelper() { | |
279 DCHECK(thread_checker_.CalledOnValidThread()); | |
280 if (callback_) { | |
281 ProxyAutoLock lock; | |
282 callback_.reset(); | |
283 } | |
284 } | |
285 private: | |
286 scoped_ptr<CallbackType> callback_; | |
287 base::ThreadCheckerImpl thread_checker_; | |
288 }; | |
289 | |
290 } // namespace internal | |
291 | |
292 // RunWhileLocked wraps the given Callback in a new Callback that, when invoked: | |
293 // 1) Locks the ProxyLock. | |
294 // 2) Runs the original Callback (forwarding arguments, if any). | |
295 // 3) Clears the original Callback (while the lock is held). | |
296 // 4) Unlocks the ProxyLock. | |
297 // Note that it's important that the callback is cleared in step (3), in case | |
298 // clearing the Callback causes a destructor (e.g., for a Resource) to run, | |
299 // which should hold the ProxyLock to avoid data races. | |
300 // | |
301 // This is for cases where you want to run a task or store a Callback, but you | |
302 // want to ensure that the ProxyLock is acquired for the duration of the task. | |
143 // Example usage: | 303 // Example usage: |
144 // GetMainThreadMessageLoop()->PostDelayedTask( | 304 // GetMainThreadMessageLoop()->PostDelayedTask( |
145 // FROM_HERE, | 305 // FROM_HERE, |
146 // RunWhileLocked(base::Bind(&CallbackWrapper, callback, result)), | 306 // RunWhileLocked(base::Bind(&CallbackWrapper, callback, result)), |
147 // delay_in_ms); | 307 // delay_in_ms); |
148 inline base::Closure RunWhileLocked(const base::Closure& closure) { | 308 template <class FunctionType> |
149 return base::Bind(CallWhileLocked, closure); | 309 inline base::Callback<FunctionType> |
310 RunWhileLocked(const base::Callback<FunctionType>& callback) { | |
311 internal::RunWhileLockedHelper<FunctionType>* helper = | |
312 new internal::RunWhileLockedHelper<FunctionType>(callback); | |
313 return base::Bind( | |
314 &internal::RunWhileLockedHelper<FunctionType>::CallWhileLocked, | |
315 base::Owned(helper)); | |
150 } | 316 } |
151 | 317 |
152 } // namespace ppapi | 318 } // namespace ppapi |
153 | 319 |
154 #endif // PPAPI_SHARED_IMPL_PROXY_LOCK_H_ | 320 #endif // PPAPI_SHARED_IMPL_PROXY_LOCK_H_ |
OLD | NEW |