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 3 // found in the LICENSE file. 4 5 #ifndef PPAPI_SHARED_IMPL_PROXY_LOCK_H_ 6 #define PPAPI_SHARED_IMPL_PROXY_LOCK_H_ 7 8 #include "base/basictypes.h" 9 #include "base/bind.h" 10 #include "base/callback.h" 11 #include "base/threading/thread_checker.h" 12 13 #include "ppapi/shared_impl/ppapi_shared_export.h" 14 15 namespace base { 16 class Lock; 17 } 18 19 namespace content { 20 class HostGlobals; 21 } 22 23 namespace ppapi { 24 25 // This is the one lock to rule them all for the ppapi proxy. All PPB interface 26 // functions that need to be synchronized should lock this lock on entry. This 27 // is normally accomplished by using an appropriate Enter RAII object at the 28 // beginning of each thunk function. 29 // 30 // TODO(dmichael): If this turns out to be too slow and contentious, we'll want 31 // to use multiple locks. E.g., one for the var tracker, one for the resource 32 // tracker, etc. 33 class PPAPI_SHARED_EXPORT ProxyLock { 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 41 // Acquire the proxy lock. If it is currently held by another thread, block 42 // until it is available. If the lock has not been set using the 'Set' method, 43 // this operation does nothing. That is the normal case for the host side; 44 // see PluginResourceTracker for where the lock gets set for the out-of- 45 // process plugin case. 46 static void Acquire(); 47 // Relinquish the proxy lock. If the lock has not been set, this does nothing. 48 static void Release(); 49 50 // Assert that the lock is owned by the current thread (in the plugin 51 // process). Does nothing when running in-process (or in the host process). 52 static void AssertAcquired(); 53 static void AssertAcquiredDebugOnly() { 54 #ifndef NDEBUG 55 AssertAcquired(); 56 #endif 57 } 58 59 // We have some unit tests where one thread pretends to be the host and one 60 // pretends to be the plugin. This allows the lock to do nothing on only one 61 // thread to support these tests. See TwoWayTest for more information. 62 static void DisableLockingOnThreadForTest(); 63 64 // Enables locking on the current thread. Although locking is enabled by 65 // default, unit tests that rely on the lock being enabled should *still* 66 // call this, since a previous test may have disabled locking. 67 static void EnableLockingOnThreadForTest(); 68 69 private: 70 friend class content::HostGlobals; 71 // On the host side, we do not lock. This must be called at most once at 72 // startup, before other threads that may access the ProxyLock have had a 73 // chance to run. 74 static void DisableLocking(); 75 76 DISALLOW_IMPLICIT_CONSTRUCTORS(ProxyLock); 77 }; 78 79 // A simple RAII class for locking the PPAPI proxy lock on entry and releasing 80 // on exit. This is for simple interfaces that don't use the 'thunk' system, 81 // such as PPB_Var and PPB_Core. 82 class ProxyAutoLock { 83 public: 84 ProxyAutoLock() { ProxyLock::Acquire(); } 85 ~ProxyAutoLock() { ProxyLock::Release(); } 86 87 private: 88 DISALLOW_COPY_AND_ASSIGN(ProxyAutoLock); 89 }; 90 91 // The inverse of the above; unlock on construction, lock on destruction. This 92 // is useful for calling out to the plugin, when we need to unlock but ensure 93 // that we re-acquire the lock when the plugin is returns or raises an 94 // exception. 95 class ProxyAutoUnlock { 96 public: 97 ProxyAutoUnlock() { ProxyLock::Release(); } 98 ~ProxyAutoUnlock() { ProxyLock::Acquire(); } 99 100 private: 101 DISALLOW_COPY_AND_ASSIGN(ProxyAutoUnlock); 102 }; 103 104 // A set of function template overloads for invoking a function pointer while 105 // the ProxyLock is unlocked. This assumes that the luck is held. 106 // CallWhileUnlocked unlocks the ProxyLock just before invoking the given 107 // function. The lock is immediately re-acquired when the invoked function 108 // function returns. CallWhileUnlocked returns whatever the given function 109 // returned. 110 // 111 // Example usage: 112 // *result = CallWhileUnlocked(ppp_input_event_impl_->HandleInputEvent, 113 // instance, 114 // resource->pp_resource()); 115 template <class ReturnType> 116 ReturnType CallWhileUnlocked(ReturnType (*function)()) { 117 ProxyAutoUnlock unlock; 118 return function(); 119 } 120 // Note we use 2 types for the params, even though for the most part we expect 121 // A1 to match P1. We let the compiler determine if P1 can convert safely to 122 // A1. This allows callers to avoid having to do things like 123 // const_cast to add const. 124 template <class ReturnType, class A1, class P1> 125 ReturnType CallWhileUnlocked(ReturnType (*function)(A1), const P1& p1) { 126 ProxyAutoUnlock unlock; 127 return function(p1); 128 } 129 template <class ReturnType, class A1, class A2, class P1, class P2> 130 ReturnType CallWhileUnlocked(ReturnType (*function)(A1, A2), 131 const P1& p1, 132 const P2& p2) { 133 ProxyAutoUnlock unlock; 134 return function(p1, p2); 135 } 136 template <class ReturnType, class A1, class A2, class A3, class P1, class P2, 137 class P3> 138 ReturnType CallWhileUnlocked(ReturnType (*function)(A1, A2, A3), 139 const P1& p1, 140 const P2& p2, 141 const P3& p3) { 142 ProxyAutoUnlock unlock; 143 return function(p1, p2, p3); 144 } 145 template <class ReturnType, class A1, class A2, class A3, class A4, class P1, 146 class P2, class P3, class P4> 147 ReturnType CallWhileUnlocked(ReturnType (*function)(A1, A2, A3, A4), 148 const P1& p1, 149 const P2& p2, 150 const P3& p3, 151 const P4& p4) { 152 ProxyAutoUnlock unlock; 153 return function(p1, p2, p3, p4); 154 } 155 template <class ReturnType, class A1, class A2, class A3, class A4, class A5, 156 class P1, class P2, class P3, class P4, class P5> 157 ReturnType CallWhileUnlocked(ReturnType (*function)(A1, A2, A3, A4, A5), 158 const P1& p1, 159 const P2& p2, 160 const P3& p3, 161 const P4& p4, 162 const P5& p5) { 163 ProxyAutoUnlock unlock; 164 return function(p1, p2, p3, p4, p5); 165 } 166 void PPAPI_SHARED_EXPORT CallWhileUnlocked(const base::Closure& closure); 167 168 namespace internal { 169 170 template <typename RunType> 171 class RunWhileLockedHelper; 172 173 template <> 174 class RunWhileLockedHelper<void()> { 175 public: 176 typedef base::Callback<void()> CallbackType; 177 explicit RunWhileLockedHelper(const CallbackType& callback) 178 : callback_(new CallbackType(callback)) { 179 // Copying |callback| may adjust reference counts for bound Vars or 180 // Resources; we should have the lock already. 181 ProxyLock::AssertAcquired(); 182 // CallWhileLocked and destruction might happen on a different thread from 183 // creation. 184 thread_checker_.DetachFromThread(); 185 } 186 void CallWhileLocked() { 187 // Bind thread_checker_ to this thread so we can check in the destructor. 188 DCHECK(thread_checker_.CalledOnValidThread()); 189 ProxyAutoLock lock; 190 { 191 // Use a scope and local Callback to ensure that the callback is cleared 192 // before the lock is released, even in the unlikely event that Run() 193 // throws an exception. 194 scoped_ptr<CallbackType> temp_callback(callback_.Pass()); 195 temp_callback->Run(); 196 } 197 } 198 199 ~RunWhileLockedHelper() { 200 // Check that the Callback is destroyed on the same thread as where 201 // CallWhileLocked happened (if CallWhileLocked happened). 202 DCHECK(thread_checker_.CalledOnValidThread()); 203 // Here we read callback_ without the lock. This is why the callback must be 204 // destroyed on the same thread where it runs. There are 2 cases where 205 // callback_ will be NULL: 206 // 1) This is the original RunWhileLockedHelper that RunWhileLocked 207 // created. When it was copied somewhere else (e.g., to a MessageLoop 208 // queue), callback_ was passed to the new copy, and the original 209 // RunWhileLockedHelper's callback_ was set to NULL (since scoped_ptrs 210 // only ever have 1 owner). In this case, we don't want to acquire the 211 // lock, because we already have it. 212 // 2) callback_ has already been run via CallWhileLocked. In this case, 213 // there's no need to acquire the lock, because we don't touch any 214 // shared data. 215 if (callback_) { 216 // If the callback was not run, we still need to have the lock when we 217 // destroy the callback in case it had a Resource bound to it. This 218 // ensures that the Resource's destructor is invoked only with the lock 219 // held. 220 // 221 // Also: Resource and Var inherit RefCounted (not ThreadSafeRefCounted), 222 // and these callbacks need to be usable on any thread. So we need to lock 223 // when releasing the callback to avoid ref counting races. 224 ProxyAutoLock lock; 225 callback_.reset(); 226 } 227 } 228 229 private: 230 scoped_ptr<CallbackType> callback_; 231 232 // Used to ensure that the Callback is run and deleted on the same thread. 233 base::ThreadChecker thread_checker_; 234 }; 235 236 template <typename P1> 237 class RunWhileLockedHelper<void(P1)> { 238 public: 239 typedef base::Callback<void(P1)> CallbackType; 240 explicit RunWhileLockedHelper(const CallbackType& callback) 241 : callback_(new CallbackType(callback)) { 242 ProxyLock::AssertAcquired(); 243 thread_checker_.DetachFromThread(); 244 } 245 void CallWhileLocked(P1 p1) { 246 DCHECK(thread_checker_.CalledOnValidThread()); 247 ProxyAutoLock lock; 248 { 249 scoped_ptr<CallbackType> temp_callback(callback_.Pass()); 250 temp_callback->Run(p1); 251 } 252 } 253 ~RunWhileLockedHelper() { 254 DCHECK(thread_checker_.CalledOnValidThread()); 255 if (callback_) { 256 ProxyAutoLock lock; 257 callback_.reset(); 258 } 259 } 260 261 private: 262 scoped_ptr<CallbackType> callback_; 263 base::ThreadChecker thread_checker_; 264 }; 265 266 template <typename P1, typename P2> 267 class RunWhileLockedHelper<void(P1, P2)> { 268 public: 269 typedef base::Callback<void(P1, P2)> CallbackType; 270 explicit RunWhileLockedHelper(const CallbackType& callback) 271 : callback_(new CallbackType(callback)) { 272 ProxyLock::AssertAcquired(); 273 thread_checker_.DetachFromThread(); 274 } 275 void CallWhileLocked(P1 p1, P2 p2) { 276 DCHECK(thread_checker_.CalledOnValidThread()); 277 ProxyAutoLock lock; 278 { 279 scoped_ptr<CallbackType> temp_callback(callback_.Pass()); 280 temp_callback->Run(p1, p2); 281 } 282 } 283 ~RunWhileLockedHelper() { 284 DCHECK(thread_checker_.CalledOnValidThread()); 285 if (callback_) { 286 ProxyAutoLock lock; 287 callback_.reset(); 288 } 289 } 290 291 private: 292 scoped_ptr<CallbackType> callback_; 293 base::ThreadChecker thread_checker_; 294 }; 295 296 template <typename P1, typename P2, typename P3> 297 class RunWhileLockedHelper<void(P1, P2, P3)> { 298 public: 299 typedef base::Callback<void(P1, P2, P3)> CallbackType; 300 explicit RunWhileLockedHelper(const CallbackType& callback) 301 : callback_(new CallbackType(callback)) { 302 ProxyLock::AssertAcquired(); 303 thread_checker_.DetachFromThread(); 304 } 305 void CallWhileLocked(P1 p1, P2 p2, P3 p3) { 306 DCHECK(thread_checker_.CalledOnValidThread()); 307 ProxyAutoLock lock; 308 { 309 scoped_ptr<CallbackType> temp_callback(callback_.Pass()); 310 temp_callback->Run(p1, p2, p3); 311 } 312 } 313 ~RunWhileLockedHelper() { 314 DCHECK(thread_checker_.CalledOnValidThread()); 315 if (callback_) { 316 ProxyAutoLock lock; 317 callback_.reset(); 318 } 319 } 320 321 private: 322 scoped_ptr<CallbackType> callback_; 323 base::ThreadChecker thread_checker_; 324 }; 325 326 } // namespace internal 327 328 // RunWhileLocked wraps the given Callback in a new Callback that, when invoked: 329 // 1) Locks the ProxyLock. 330 // 2) Runs the original Callback (forwarding arguments, if any). 331 // 3) Clears the original Callback (while the lock is held). 332 // 4) Unlocks the ProxyLock. 333 // Note that it's important that the callback is cleared in step (3), in case 334 // clearing the Callback causes a destructor (e.g., for a Resource) to run, 335 // which should hold the ProxyLock to avoid data races. 336 // 337 // This is for cases where you want to run a task or store a Callback, but you 338 // want to ensure that the ProxyLock is acquired for the duration of the task 339 // that the Callback runs. 340 // EXAMPLE USAGE: 341 // GetMainThreadMessageLoop()->PostDelayedTask( 342 // FROM_HERE, 343 // RunWhileLocked(base::Bind(&CallbackWrapper, callback, result)), 344 // delay_in_ms); 345 // 346 // In normal usage like the above, this all should "just work". However, if you 347 // do something unusual, you may get a runtime crash due to deadlock. Here are 348 // the ways that the returned Callback must be used to avoid a deadlock: 349 // (1) copied to another Callback. After that, the original callback can be 350 // destroyed with or without the proxy lock acquired, while the newly assigned 351 // callback has to conform to these same restrictions. Or 352 // (2) run without proxy lock acquired (e.g., being posted to a MessageLoop 353 // and run there). The callback must be destroyed on the same thread where it 354 // was run (but can be destroyed with or without the proxy lock acquired). Or 355 // (3) destroyed without the proxy lock acquired. 356 template <class FunctionType> 357 inline base::Callback<FunctionType> RunWhileLocked( 358 const base::Callback<FunctionType>& callback) { 359 internal::RunWhileLockedHelper<FunctionType>* helper = 360 new internal::RunWhileLockedHelper<FunctionType>(callback); 361 return base::Bind( 362 &internal::RunWhileLockedHelper<FunctionType>::CallWhileLocked, 363 base::Owned(helper)); 364 } 365 366 } // namespace ppapi 367 368 #endif // PPAPI_SHARED_IMPL_PROXY_LOCK_H_ 369