 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 | 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 |