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 #include "base/threading/thread_checker.h" |
12 | 12 |
13 #include "ppapi/shared_impl/ppapi_shared_export.h" | 13 #include "ppapi/shared_impl/ppapi_shared_export.h" |
14 | 14 |
15 namespace base { | 15 namespace base { |
16 class Lock; | 16 class Lock; |
17 } | 17 } |
18 | 18 |
| 19 namespace content { |
| 20 class HostGlobals; |
| 21 } |
| 22 |
19 namespace ppapi { | 23 namespace ppapi { |
20 | 24 |
21 // This is the one lock to rule them all for the ppapi proxy. All PPB interface | 25 // This is the one lock to rule them all for the ppapi proxy. All PPB interface |
22 // functions that need to be synchronized should lock this lock on entry. This | 26 // functions that need to be synchronized should lock this lock on entry. This |
23 // is normally accomplished by using an appropriate Enter RAII object at the | 27 // is normally accomplished by using an appropriate Enter RAII object at the |
24 // beginning of each thunk function. | 28 // beginning of each thunk function. |
25 // | 29 // |
26 // TODO(dmichael): If this turns out to be too slow and contentious, we'll want | 30 // TODO(dmichael): If this turns out to be too slow and contentious, we'll want |
27 // to use multiple locks. E.g., one for the var tracker, one for the resource | 31 // to use multiple locks. E.g., one for the var tracker, one for the resource |
28 // tracker, etc. | 32 // tracker, etc. |
29 class PPAPI_SHARED_EXPORT ProxyLock { | 33 class PPAPI_SHARED_EXPORT ProxyLock { |
30 public: | 34 public: |
| 35 // Return the global ProxyLock. Normally, you should not access this |
| 36 // directly but instead use ProxyAutoLock or ProxyAutoUnlock. But sometimes |
| 37 // you need access to the ProxyLock, for example to create a condition |
| 38 // variable. |
| 39 static base::Lock* Get(); |
| 40 |
31 // Acquire the proxy lock. If it is currently held by another thread, block | 41 // Acquire the proxy lock. If it is currently held by another thread, block |
32 // until it is available. If the lock has not been set using the 'Set' method, | 42 // until it is available. If the lock has not been set using the 'Set' method, |
33 // this operation does nothing. That is the normal case for the host side; | 43 // this operation does nothing. That is the normal case for the host side; |
34 // see PluginResourceTracker for where the lock gets set for the out-of- | 44 // see PluginResourceTracker for where the lock gets set for the out-of- |
35 // process plugin case. | 45 // process plugin case. |
36 static void Acquire(); | 46 static void Acquire(); |
37 // Relinquish the proxy lock. If the lock has not been set, this does nothing. | 47 // Relinquish the proxy lock. If the lock has not been set, this does nothing. |
38 static void Release(); | 48 static void Release(); |
39 | 49 |
40 // Assert that the lock is owned by the current thread (in the plugin | 50 // Assert that the lock is owned by the current thread (in the plugin |
41 // process). Does nothing when running in-process (or in the host process). | 51 // process). Does nothing when running in-process (or in the host process). |
42 static void AssertAcquired(); | 52 static void AssertAcquired(); |
43 | 53 |
| 54 // We have some unit tests where one thread pretends to be the host and one |
| 55 // pretends to be the plugin. This allows the lock to do nothing on only one |
| 56 // thread to support these tests. See TwoWayTest for more information. |
| 57 static void DisableLockingOnThreadForTest(); |
| 58 |
| 59 // Enables locking on the current thread. Although locking is enabled by |
| 60 // default, unit tests that rely on the lock being enabled should *still* |
| 61 // call this, since a previous test may have disabled locking. |
| 62 static void EnableLockingOnThreadForTest(); |
| 63 |
44 private: | 64 private: |
| 65 friend class content::HostGlobals; |
| 66 // On the host side, we do not lock. This must be called at most once at |
| 67 // startup, before other threads that may access the ProxyLock have had a |
| 68 // chance to run. |
| 69 static void DisableLocking(); |
| 70 |
45 DISALLOW_IMPLICIT_CONSTRUCTORS(ProxyLock); | 71 DISALLOW_IMPLICIT_CONSTRUCTORS(ProxyLock); |
46 }; | 72 }; |
47 | 73 |
48 // A simple RAII class for locking the PPAPI proxy lock on entry and releasing | 74 // A simple RAII class for locking the PPAPI proxy lock on entry and releasing |
49 // on exit. This is for simple interfaces that don't use the 'thunk' system, | 75 // on exit. This is for simple interfaces that don't use the 'thunk' system, |
50 // such as PPB_Var and PPB_Core. | 76 // such as PPB_Var and PPB_Core. |
51 class ProxyAutoLock { | 77 class ProxyAutoLock { |
52 public: | 78 public: |
53 ProxyAutoLock() { | 79 ProxyAutoLock() { |
54 ProxyLock::Acquire(); | 80 ProxyLock::Acquire(); |
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
157 ProxyAutoLock lock; | 183 ProxyAutoLock lock; |
158 { | 184 { |
159 // Use a scope and local Callback to ensure that the callback is cleared | 185 // 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() | 186 // before the lock is released, even in the unlikely event that Run() |
161 // throws an exception. | 187 // throws an exception. |
162 scoped_ptr<CallbackType> temp_callback(callback_.Pass()); | 188 scoped_ptr<CallbackType> temp_callback(callback_.Pass()); |
163 temp_callback->Run(); | 189 temp_callback->Run(); |
164 } | 190 } |
165 } | 191 } |
166 | 192 |
| 193 ~RunWhileLockedHelper() { |
| 194 // Check that the Callback is destroyed on the same thread as where |
| 195 // CallWhileLocked happened (if CallWhileLocked happened). |
| 196 DCHECK(thread_checker_.CalledOnValidThread()); |
| 197 // Here we read callback_ without the lock. This is why the callback must be |
| 198 // destroyed on the same thread where it runs. There are 2 cases where |
| 199 // callback_ will be NULL: |
| 200 // 1) This is the original RunWhileLockedHelper that RunWhileLocked |
| 201 // created. When it was copied somewhere else (e.g., to a MessageLoop |
| 202 // queue), callback_ was passed to the new copy, and the original |
| 203 // RunWhileLockedHelper's callback_ was set to NULL (since scoped_ptrs |
| 204 // only ever have 1 owner). In this case, we don't want to acquire the |
| 205 // lock, because we already have it. |
| 206 // 2) callback_ has already been run via CallWhileLocked. In this case, |
| 207 // there's no need to acquire the lock, because we don't touch any |
| 208 // shared data. |
| 209 if (callback_) { |
| 210 // If the callback was not run, we still need to have the lock when we |
| 211 // destroy the callback in case it had a Resource bound to it. This |
| 212 // ensures that the Resource's destructor is invoked only with the lock |
| 213 // held. |
| 214 // |
| 215 // Also: Resource and Var inherit RefCounted (not ThreadSafeRefCounted), |
| 216 // and these callbacks need to be usable on any thread. So we need to lock |
| 217 // when releasing the callback to avoid ref counting races. |
| 218 ProxyAutoLock lock; |
| 219 callback_.reset(); |
| 220 } |
| 221 } |
167 private: | 222 private: |
168 scoped_ptr<CallbackType> callback_; | 223 scoped_ptr<CallbackType> callback_; |
169 | 224 |
170 // Used to ensure that the Callback is run and deleted on the same thread. | 225 // Used to ensure that the Callback is run and deleted on the same thread. |
171 base::ThreadChecker thread_checker_; | 226 base::ThreadChecker thread_checker_; |
172 }; | 227 }; |
173 | 228 |
174 template <typename P1> | 229 template <typename P1> |
175 class RunWhileLockedHelper<void (P1)> { | 230 class RunWhileLockedHelper<void (P1)> { |
176 public: | 231 public: |
177 typedef base::Callback<void (P1)> CallbackType; | 232 typedef base::Callback<void (P1)> CallbackType; |
178 explicit RunWhileLockedHelper(const CallbackType& callback) | 233 explicit RunWhileLockedHelper(const CallbackType& callback) |
179 : callback_(new CallbackType(callback)) { | 234 : callback_(new CallbackType(callback)) { |
180 ProxyLock::AssertAcquired(); | 235 ProxyLock::AssertAcquired(); |
181 thread_checker_.DetachFromThread(); | 236 thread_checker_.DetachFromThread(); |
182 } | 237 } |
183 void CallWhileLocked(P1 p1) { | 238 void CallWhileLocked(P1 p1) { |
184 DCHECK(thread_checker_.CalledOnValidThread()); | 239 DCHECK(thread_checker_.CalledOnValidThread()); |
185 ProxyAutoLock lock; | 240 ProxyAutoLock lock; |
186 { | 241 { |
187 scoped_ptr<CallbackType> temp_callback(callback_.Pass()); | 242 scoped_ptr<CallbackType> temp_callback(callback_.Pass()); |
188 temp_callback->Run(p1); | 243 temp_callback->Run(p1); |
189 } | 244 } |
190 } | 245 } |
191 | 246 ~RunWhileLockedHelper() { |
| 247 DCHECK(thread_checker_.CalledOnValidThread()); |
| 248 if (callback_) { |
| 249 ProxyAutoLock lock; |
| 250 callback_.reset(); |
| 251 } |
| 252 } |
192 private: | 253 private: |
193 scoped_ptr<CallbackType> callback_; | 254 scoped_ptr<CallbackType> callback_; |
194 base::ThreadChecker thread_checker_; | 255 base::ThreadChecker thread_checker_; |
195 }; | 256 }; |
196 | 257 |
197 template <typename P1, typename P2> | 258 template <typename P1, typename P2> |
198 class RunWhileLockedHelper<void (P1, P2)> { | 259 class RunWhileLockedHelper<void (P1, P2)> { |
199 public: | 260 public: |
200 typedef base::Callback<void (P1, P2)> CallbackType; | 261 typedef base::Callback<void (P1, P2)> CallbackType; |
201 explicit RunWhileLockedHelper(const CallbackType& callback) | 262 explicit RunWhileLockedHelper(const CallbackType& callback) |
202 : callback_(new CallbackType(callback)) { | 263 : callback_(new CallbackType(callback)) { |
203 ProxyLock::AssertAcquired(); | 264 ProxyLock::AssertAcquired(); |
204 thread_checker_.DetachFromThread(); | 265 thread_checker_.DetachFromThread(); |
205 } | 266 } |
206 void CallWhileLocked(P1 p1, P2 p2) { | 267 void CallWhileLocked(P1 p1, P2 p2) { |
207 DCHECK(thread_checker_.CalledOnValidThread()); | 268 DCHECK(thread_checker_.CalledOnValidThread()); |
208 ProxyAutoLock lock; | 269 ProxyAutoLock lock; |
209 { | 270 { |
210 scoped_ptr<CallbackType> temp_callback(callback_.Pass()); | 271 scoped_ptr<CallbackType> temp_callback(callback_.Pass()); |
211 temp_callback->Run(p1, p2); | 272 temp_callback->Run(p1, p2); |
212 } | 273 } |
213 } | 274 } |
214 | 275 ~RunWhileLockedHelper() { |
| 276 DCHECK(thread_checker_.CalledOnValidThread()); |
| 277 if (callback_) { |
| 278 ProxyAutoLock lock; |
| 279 callback_.reset(); |
| 280 } |
| 281 } |
215 private: | 282 private: |
216 scoped_ptr<CallbackType> callback_; | 283 scoped_ptr<CallbackType> callback_; |
217 base::ThreadChecker thread_checker_; | 284 base::ThreadChecker thread_checker_; |
218 }; | 285 }; |
219 | 286 |
220 template <typename P1, typename P2, typename P3> | 287 template <typename P1, typename P2, typename P3> |
221 class RunWhileLockedHelper<void (P1, P2, P3)> { | 288 class RunWhileLockedHelper<void (P1, P2, P3)> { |
222 public: | 289 public: |
223 typedef base::Callback<void (P1, P2, P3)> CallbackType; | 290 typedef base::Callback<void (P1, P2, P3)> CallbackType; |
224 explicit RunWhileLockedHelper(const CallbackType& callback) | 291 explicit RunWhileLockedHelper(const CallbackType& callback) |
225 : callback_(new CallbackType(callback)) { | 292 : callback_(new CallbackType(callback)) { |
226 ProxyLock::AssertAcquired(); | 293 ProxyLock::AssertAcquired(); |
227 thread_checker_.DetachFromThread(); | 294 thread_checker_.DetachFromThread(); |
228 } | 295 } |
229 void CallWhileLocked(P1 p1, P2 p2, P3 p3) { | 296 void CallWhileLocked(P1 p1, P2 p2, P3 p3) { |
230 DCHECK(thread_checker_.CalledOnValidThread()); | 297 DCHECK(thread_checker_.CalledOnValidThread()); |
231 ProxyAutoLock lock; | 298 ProxyAutoLock lock; |
232 { | 299 { |
233 scoped_ptr<CallbackType> temp_callback(callback_.Pass()); | 300 scoped_ptr<CallbackType> temp_callback(callback_.Pass()); |
234 temp_callback->Run(p1, p2, p3); | 301 temp_callback->Run(p1, p2, p3); |
235 } | 302 } |
236 } | 303 } |
237 | 304 ~RunWhileLockedHelper() { |
| 305 DCHECK(thread_checker_.CalledOnValidThread()); |
| 306 if (callback_) { |
| 307 ProxyAutoLock lock; |
| 308 callback_.reset(); |
| 309 } |
| 310 } |
238 private: | 311 private: |
239 scoped_ptr<CallbackType> callback_; | 312 scoped_ptr<CallbackType> callback_; |
240 base::ThreadChecker thread_checker_; | 313 base::ThreadChecker thread_checker_; |
241 }; | 314 }; |
242 | 315 |
243 } // namespace internal | 316 } // namespace internal |
244 | 317 |
245 // RunWhileLocked wraps the given Callback in a new Callback that, when invoked: | 318 // RunWhileLocked wraps the given Callback in a new Callback that, when invoked: |
246 // 1) Locks the ProxyLock. | 319 // 1) Locks the ProxyLock. |
247 // 2) Runs the original Callback (forwarding arguments, if any). | 320 // 2) Runs the original Callback (forwarding arguments, if any). |
(...skipping 15 matching lines...) Expand all Loading... |
263 // In normal usage like the above, this all should "just work". However, if you | 336 // In normal usage like the above, this all should "just work". However, if you |
264 // do something unusual, you may get a runtime crash due to deadlock. Here are | 337 // do something unusual, you may get a runtime crash due to deadlock. Here are |
265 // the ways that the returned Callback must be used to avoid a deadlock: | 338 // the ways that the returned Callback must be used to avoid a deadlock: |
266 // (1) copied to another Callback. After that, the original callback can be | 339 // (1) copied to another Callback. After that, the original callback can be |
267 // destroyed with or without the proxy lock acquired, while the newly assigned | 340 // destroyed with or without the proxy lock acquired, while the newly assigned |
268 // callback has to conform to these same restrictions. Or | 341 // callback has to conform to these same restrictions. Or |
269 // (2) run without proxy lock acquired (e.g., being posted to a MessageLoop | 342 // (2) run without proxy lock acquired (e.g., being posted to a MessageLoop |
270 // and run there). The callback must be destroyed on the same thread where it | 343 // and run there). The callback must be destroyed on the same thread where it |
271 // was run (but can be destroyed with or without the proxy lock acquired). Or | 344 // was run (but can be destroyed with or without the proxy lock acquired). Or |
272 // (3) destroyed without the proxy lock acquired. | 345 // (3) destroyed without the proxy lock acquired. |
273 // TODO(dmichael): This won't actually fail until | |
274 // https://codereview.chromium.org/19492014/ lands. | |
275 template <class FunctionType> | 346 template <class FunctionType> |
276 inline base::Callback<FunctionType> | 347 inline base::Callback<FunctionType> |
277 RunWhileLocked(const base::Callback<FunctionType>& callback) { | 348 RunWhileLocked(const base::Callback<FunctionType>& callback) { |
278 internal::RunWhileLockedHelper<FunctionType>* helper = | 349 internal::RunWhileLockedHelper<FunctionType>* helper = |
279 new internal::RunWhileLockedHelper<FunctionType>(callback); | 350 new internal::RunWhileLockedHelper<FunctionType>(callback); |
280 return base::Bind( | 351 return base::Bind( |
281 &internal::RunWhileLockedHelper<FunctionType>::CallWhileLocked, | 352 &internal::RunWhileLockedHelper<FunctionType>::CallWhileLocked, |
282 base::Owned(helper)); | 353 base::Owned(helper)); |
283 } | 354 } |
284 | 355 |
285 } // namespace ppapi | 356 } // namespace ppapi |
286 | 357 |
287 #endif // PPAPI_SHARED_IMPL_PROXY_LOCK_H_ | 358 #endif // PPAPI_SHARED_IMPL_PROXY_LOCK_H_ |
OLD | NEW |