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