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 ppapi { 20 21 // 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 23 // is normally accomplished by using an appropriate Enter RAII object at the 24 // beginning of each thunk function. 25 // 26 // 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 28 // tracker, etc. 29 class PPAPI_SHARED_EXPORT ProxyLock { 30 public: 31 // 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, 33 // 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- 35 // process plugin case. 36 static void Acquire(); 37 // Relinquish the proxy lock. If the lock has not been set, this does nothing. 38 static void Release(); 39 40 // 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). 42 static void AssertAcquired(); 43 44 private: 45 DISALLOW_IMPLICIT_CONSTRUCTORS(ProxyLock); 46 }; 47 48 // 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, 50 // such as PPB_Var and PPB_Core. 51 class ProxyAutoLock { 52 public: 53 ProxyAutoLock() { 54 ProxyLock::Acquire(); 55 } 56 ~ProxyAutoLock() { 57 ProxyLock::Release(); 58 } 59 private: 60 DISALLOW_COPY_AND_ASSIGN(ProxyAutoLock); 61 }; 62 63 // The inverse of the above; unlock on construction, lock on destruction. This 64 // is useful for calling out to the plugin, when we need to unlock but ensure 65 // that we re-acquire the lock when the plugin is returns or raises an 66 // exception. 67 class ProxyAutoUnlock { 68 public: 69 ProxyAutoUnlock() { 70 ProxyLock::Release(); 71 } 72 ~ProxyAutoUnlock() { 73 ProxyLock::Acquire(); 74 } 75 private: 76 DISALLOW_COPY_AND_ASSIGN(ProxyAutoUnlock); 77 }; 78 79 // A set of function template overloads for invoking a function pointer while 80 // the ProxyLock is unlocked. This assumes that the luck is held. 81 // CallWhileUnlocked unlocks the ProxyLock just before invoking the given 82 // function. The lock is immediately re-acquired when the invoked function 83 // function returns. CallWhileUnlocked returns whatever the given function 84 // returned. 85 // 86 // Example usage: 87 // *result = CallWhileUnlocked(ppp_input_event_impl_->HandleInputEvent, 88 // instance, 89 // resource->pp_resource()); 90 template <class ReturnType> 91 ReturnType CallWhileUnlocked(ReturnType (*function)()) { 92 ProxyAutoUnlock unlock; 93 return function(); 94 } 95 template <class ReturnType, class P1> 96 ReturnType CallWhileUnlocked(ReturnType (*function)(P1), const P1& p1) { 97 ProxyAutoUnlock unlock; 98 return function(p1); 99 } 100 template <class ReturnType, class P1, class P2> 101 ReturnType CallWhileUnlocked(ReturnType (*function)(P1, P2), 102 const P1& p1, 103 const P2& p2) { 104 ProxyAutoUnlock unlock; 105 return function(p1, p2); 106 } 107 template <class ReturnType, class P1, class P2, class P3> 108 ReturnType CallWhileUnlocked(ReturnType (*function)(P1, P2, P3), 109 const P1& p1, 110 const P2& p2, 111 const P3& p3) { 112 ProxyAutoUnlock unlock; 113 return function(p1, p2, p3); 114 } 115 template <class ReturnType, class P1, class P2, class P3, class P4> 116 ReturnType CallWhileUnlocked(ReturnType (*function)(P1, P2, P3, P4), 117 const P1& p1, 118 const P2& p2, 119 const P3& p3, 120 const P4& p4) { 121 ProxyAutoUnlock unlock; 122 return function(p1, p2, p3, p4); 123 } 124 template <class ReturnType, class P1, class P2, class P3, class P4, class P5> 125 ReturnType CallWhileUnlocked(ReturnType (*function)(P1, P2, P3, P4, P5), 126 const P1& p1, 127 const P2& p2, 128 const P3& p3, 129 const P4& p4, 130 const P5& p5) { 131 ProxyAutoUnlock unlock; 132 return function(p1, p2, p3, p4, p5); 133 } 134 void PPAPI_SHARED_EXPORT CallWhileUnlocked(const base::Closure& closure); 135 136 namespace internal { 137 138 template <typename RunType> 139 class RunWhileLockedHelper; 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 private: 168 scoped_ptr<CallbackType> callback_; 169 170 // Used to ensure that the Callback is run and deleted on the same thread. 171 base::ThreadChecker thread_checker_; 172 }; 173 174 template <typename P1> 175 class RunWhileLockedHelper<void (P1)> { 176 public: 177 typedef base::Callback<void (P1)> CallbackType; 178 explicit RunWhileLockedHelper(const CallbackType& callback) 179 : callback_(new CallbackType(callback)) { 180 ProxyLock::AssertAcquired(); 181 thread_checker_.DetachFromThread(); 182 } 183 void CallWhileLocked(P1 p1) { 184 DCHECK(thread_checker_.CalledOnValidThread()); 185 ProxyAutoLock lock; 186 { 187 scoped_ptr<CallbackType> temp_callback(callback_.Pass()); 188 temp_callback->Run(p1); 189 } 190 } 191 192 private: 193 scoped_ptr<CallbackType> callback_; 194 base::ThreadChecker thread_checker_; 195 }; 196 197 template <typename P1, typename P2> 198 class RunWhileLockedHelper<void (P1, P2)> { 199 public: 200 typedef base::Callback<void (P1, P2)> CallbackType; 201 explicit RunWhileLockedHelper(const CallbackType& callback) 202 : callback_(new CallbackType(callback)) { 203 ProxyLock::AssertAcquired(); 204 thread_checker_.DetachFromThread(); 205 } 206 void CallWhileLocked(P1 p1, P2 p2) { 207 DCHECK(thread_checker_.CalledOnValidThread()); 208 ProxyAutoLock lock; 209 { 210 scoped_ptr<CallbackType> temp_callback(callback_.Pass()); 211 temp_callback->Run(p1, p2); 212 } 213 } 214 215 private: 216 scoped_ptr<CallbackType> callback_; 217 base::ThreadChecker thread_checker_; 218 }; 219 220 template <typename P1, typename P2, typename P3> 221 class RunWhileLockedHelper<void (P1, P2, P3)> { 222 public: 223 typedef base::Callback<void (P1, P2, P3)> CallbackType; 224 explicit RunWhileLockedHelper(const CallbackType& callback) 225 : callback_(new CallbackType(callback)) { 226 ProxyLock::AssertAcquired(); 227 thread_checker_.DetachFromThread(); 228 } 229 void CallWhileLocked(P1 p1, P2 p2, P3 p3) { 230 DCHECK(thread_checker_.CalledOnValidThread()); 231 ProxyAutoLock lock; 232 { 233 scoped_ptr<CallbackType> temp_callback(callback_.Pass()); 234 temp_callback->Run(p1, p2, p3); 235 } 236 } 237 238 private: 239 scoped_ptr<CallbackType> callback_; 240 base::ThreadChecker thread_checker_; 241 }; 242 243 } // namespace internal 244 245 // RunWhileLocked wraps the given Callback in a new Callback that, when invoked: 246 // 1) Locks the ProxyLock. 247 // 2) Runs the original Callback (forwarding arguments, if any). 248 // 3) Clears the original Callback (while the lock is held). 249 // 4) Unlocks the ProxyLock. 250 // Note that it's important that the callback is cleared in step (3), in case 251 // clearing the Callback causes a destructor (e.g., for a Resource) to run, 252 // which should hold the ProxyLock to avoid data races. 253 // 254 // This is for cases where you want to run a task or store a Callback, but you 255 // want to ensure that the ProxyLock is acquired for the duration of the task 256 // that the Callback runs. 257 // EXAMPLE USAGE: 258 // GetMainThreadMessageLoop()->PostDelayedTask( 259 // FROM_HERE, 260 // RunWhileLocked(base::Bind(&CallbackWrapper, callback, result)), 261 // delay_in_ms); 262 // 263 // 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 265 // 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 267 // destroyed with or without the proxy lock acquired, while the newly assigned 268 // callback has to conform to these same restrictions. Or 269 // (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 271 // was run (but can be destroyed with or without the proxy lock acquired). Or 272 // (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> 276 inline base::Callback<FunctionType> 277 RunWhileLocked(const base::Callback<FunctionType>& callback) { 278 internal::RunWhileLockedHelper<FunctionType>* helper = 279 new internal::RunWhileLockedHelper<FunctionType>(callback); 280 return base::Bind( 281 &internal::RunWhileLockedHelper<FunctionType>::CallWhileLocked, 282 base::Owned(helper)); 283 } 284 285 } // namespace ppapi 286 287 #endif // PPAPI_SHARED_IMPL_PROXY_LOCK_H_ 288