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();
AssertAcquiredDebugOnly()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:
ProxyAutoLock()84 ProxyAutoLock() {
85 ProxyLock::Acquire();
86 }
~ProxyAutoLock()87 ~ProxyAutoLock() {
88 ProxyLock::Release();
89 }
90 private:
91 DISALLOW_COPY_AND_ASSIGN(ProxyAutoLock);
92 };
93
94 // The inverse of the above; unlock on construction, lock on destruction. This
95 // is useful for calling out to the plugin, when we need to unlock but ensure
96 // that we re-acquire the lock when the plugin is returns or raises an
97 // exception.
98 class ProxyAutoUnlock {
99 public:
ProxyAutoUnlock()100 ProxyAutoUnlock() {
101 ProxyLock::Release();
102 }
~ProxyAutoUnlock()103 ~ProxyAutoUnlock() {
104 ProxyLock::Acquire();
105 }
106 private:
107 DISALLOW_COPY_AND_ASSIGN(ProxyAutoUnlock);
108 };
109
110 // A set of function template overloads for invoking a function pointer while
111 // the ProxyLock is unlocked. This assumes that the luck is held.
112 // CallWhileUnlocked unlocks the ProxyLock just before invoking the given
113 // function. The lock is immediately re-acquired when the invoked function
114 // function returns. CallWhileUnlocked returns whatever the given function
115 // returned.
116 //
117 // Example usage:
118 // *result = CallWhileUnlocked(ppp_input_event_impl_->HandleInputEvent,
119 // instance,
120 // resource->pp_resource());
121 template <class ReturnType>
CallWhileUnlocked(ReturnType (* function)())122 ReturnType CallWhileUnlocked(ReturnType (*function)()) {
123 ProxyAutoUnlock unlock;
124 return function();
125 }
126 template <class ReturnType, class P1>
CallWhileUnlocked(ReturnType (* function)(P1),const P1 & p1)127 ReturnType CallWhileUnlocked(ReturnType (*function)(P1), const P1& p1) {
128 ProxyAutoUnlock unlock;
129 return function(p1);
130 }
131 template <class ReturnType, class P1, class P2>
CallWhileUnlocked(ReturnType (* function)(P1,P2),const P1 & p1,const P2 & p2)132 ReturnType CallWhileUnlocked(ReturnType (*function)(P1, P2),
133 const P1& p1,
134 const P2& p2) {
135 ProxyAutoUnlock unlock;
136 return function(p1, p2);
137 }
138 template <class ReturnType, class P1, class P2, class P3>
CallWhileUnlocked(ReturnType (* function)(P1,P2,P3),const P1 & p1,const P2 & p2,const P3 & p3)139 ReturnType CallWhileUnlocked(ReturnType (*function)(P1, P2, P3),
140 const P1& p1,
141 const P2& p2,
142 const P3& p3) {
143 ProxyAutoUnlock unlock;
144 return function(p1, p2, p3);
145 }
146 template <class ReturnType, class P1, class P2, class P3, class P4>
CallWhileUnlocked(ReturnType (* function)(P1,P2,P3,P4),const P1 & p1,const P2 & p2,const P3 & p3,const P4 & p4)147 ReturnType CallWhileUnlocked(ReturnType (*function)(P1, P2, P3, P4),
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 P1, class P2, class P3, class P4, class P5>
CallWhileUnlocked(ReturnType (* function)(P1,P2,P3,P4,P5),const P1 & p1,const P2 & p2,const P3 & p3,const P4 & p4,const P5 & p5)156 ReturnType CallWhileUnlocked(ReturnType (*function)(P1, P2, P3, P4, P5),
157 const P1& p1,
158 const P2& p2,
159 const P3& p3,
160 const P4& p4,
161 const P5& p5) {
162 ProxyAutoUnlock unlock;
163 return function(p1, p2, p3, p4, p5);
164 }
165 void PPAPI_SHARED_EXPORT CallWhileUnlocked(const base::Closure& closure);
166
167 namespace internal {
168
169 template <typename RunType>
170 class RunWhileLockedHelper;
171
172 template <>
173 class RunWhileLockedHelper<void ()> {
174 public:
175 typedef base::Callback<void ()> CallbackType;
RunWhileLockedHelper(const CallbackType & callback)176 explicit RunWhileLockedHelper(const CallbackType& callback)
177 : callback_(new CallbackType(callback)) {
178 // Copying |callback| may adjust reference counts for bound Vars or
179 // Resources; we should have the lock already.
180 ProxyLock::AssertAcquired();
181 // CallWhileLocked and destruction might happen on a different thread from
182 // creation.
183 thread_checker_.DetachFromThread();
184 }
CallWhileLocked()185 void CallWhileLocked() {
186 // Bind thread_checker_ to this thread so we can check in the destructor.
187 DCHECK(thread_checker_.CalledOnValidThread());
188 ProxyAutoLock lock;
189 {
190 // Use a scope and local Callback to ensure that the callback is cleared
191 // before the lock is released, even in the unlikely event that Run()
192 // throws an exception.
193 scoped_ptr<CallbackType> temp_callback(callback_.Pass());
194 temp_callback->Run();
195 }
196 }
197
~RunWhileLockedHelper()198 ~RunWhileLockedHelper() {
199 // Check that the Callback is destroyed on the same thread as where
200 // CallWhileLocked happened (if CallWhileLocked happened).
201 DCHECK(thread_checker_.CalledOnValidThread());
202 // Here we read callback_ without the lock. This is why the callback must be
203 // destroyed on the same thread where it runs. There are 2 cases where
204 // callback_ will be NULL:
205 // 1) This is the original RunWhileLockedHelper that RunWhileLocked
206 // created. When it was copied somewhere else (e.g., to a MessageLoop
207 // queue), callback_ was passed to the new copy, and the original
208 // RunWhileLockedHelper's callback_ was set to NULL (since scoped_ptrs
209 // only ever have 1 owner). In this case, we don't want to acquire the
210 // lock, because we already have it.
211 // 2) callback_ has already been run via CallWhileLocked. In this case,
212 // there's no need to acquire the lock, because we don't touch any
213 // shared data.
214 if (callback_) {
215 // If the callback was not run, we still need to have the lock when we
216 // destroy the callback in case it had a Resource bound to it. This
217 // ensures that the Resource's destructor is invoked only with the lock
218 // held.
219 //
220 // Also: Resource and Var inherit RefCounted (not ThreadSafeRefCounted),
221 // and these callbacks need to be usable on any thread. So we need to lock
222 // when releasing the callback to avoid ref counting races.
223 ProxyAutoLock lock;
224 callback_.reset();
225 }
226 }
227 private:
228 scoped_ptr<CallbackType> callback_;
229
230 // Used to ensure that the Callback is run and deleted on the same thread.
231 base::ThreadChecker thread_checker_;
232 };
233
234 template <typename P1>
235 class RunWhileLockedHelper<void (P1)> {
236 public:
237 typedef base::Callback<void (P1)> CallbackType;
RunWhileLockedHelper(const CallbackType & callback)238 explicit RunWhileLockedHelper(const CallbackType& callback)
239 : callback_(new CallbackType(callback)) {
240 ProxyLock::AssertAcquired();
241 thread_checker_.DetachFromThread();
242 }
CallWhileLocked(P1 p1)243 void CallWhileLocked(P1 p1) {
244 DCHECK(thread_checker_.CalledOnValidThread());
245 ProxyAutoLock lock;
246 {
247 scoped_ptr<CallbackType> temp_callback(callback_.Pass());
248 temp_callback->Run(p1);
249 }
250 }
~RunWhileLockedHelper()251 ~RunWhileLockedHelper() {
252 DCHECK(thread_checker_.CalledOnValidThread());
253 if (callback_) {
254 ProxyAutoLock lock;
255 callback_.reset();
256 }
257 }
258 private:
259 scoped_ptr<CallbackType> callback_;
260 base::ThreadChecker thread_checker_;
261 };
262
263 template <typename P1, typename P2>
264 class RunWhileLockedHelper<void (P1, P2)> {
265 public:
266 typedef base::Callback<void (P1, P2)> CallbackType;
RunWhileLockedHelper(const CallbackType & callback)267 explicit RunWhileLockedHelper(const CallbackType& callback)
268 : callback_(new CallbackType(callback)) {
269 ProxyLock::AssertAcquired();
270 thread_checker_.DetachFromThread();
271 }
CallWhileLocked(P1 p1,P2 p2)272 void CallWhileLocked(P1 p1, P2 p2) {
273 DCHECK(thread_checker_.CalledOnValidThread());
274 ProxyAutoLock lock;
275 {
276 scoped_ptr<CallbackType> temp_callback(callback_.Pass());
277 temp_callback->Run(p1, p2);
278 }
279 }
~RunWhileLockedHelper()280 ~RunWhileLockedHelper() {
281 DCHECK(thread_checker_.CalledOnValidThread());
282 if (callback_) {
283 ProxyAutoLock lock;
284 callback_.reset();
285 }
286 }
287 private:
288 scoped_ptr<CallbackType> callback_;
289 base::ThreadChecker thread_checker_;
290 };
291
292 template <typename P1, typename P2, typename P3>
293 class RunWhileLockedHelper<void (P1, P2, P3)> {
294 public:
295 typedef base::Callback<void (P1, P2, P3)> CallbackType;
RunWhileLockedHelper(const CallbackType & callback)296 explicit RunWhileLockedHelper(const CallbackType& callback)
297 : callback_(new CallbackType(callback)) {
298 ProxyLock::AssertAcquired();
299 thread_checker_.DetachFromThread();
300 }
CallWhileLocked(P1 p1,P2 p2,P3 p3)301 void CallWhileLocked(P1 p1, P2 p2, P3 p3) {
302 DCHECK(thread_checker_.CalledOnValidThread());
303 ProxyAutoLock lock;
304 {
305 scoped_ptr<CallbackType> temp_callback(callback_.Pass());
306 temp_callback->Run(p1, p2, p3);
307 }
308 }
~RunWhileLockedHelper()309 ~RunWhileLockedHelper() {
310 DCHECK(thread_checker_.CalledOnValidThread());
311 if (callback_) {
312 ProxyAutoLock lock;
313 callback_.reset();
314 }
315 }
316 private:
317 scoped_ptr<CallbackType> callback_;
318 base::ThreadChecker thread_checker_;
319 };
320
321 } // namespace internal
322
323 // RunWhileLocked wraps the given Callback in a new Callback that, when invoked:
324 // 1) Locks the ProxyLock.
325 // 2) Runs the original Callback (forwarding arguments, if any).
326 // 3) Clears the original Callback (while the lock is held).
327 // 4) Unlocks the ProxyLock.
328 // Note that it's important that the callback is cleared in step (3), in case
329 // clearing the Callback causes a destructor (e.g., for a Resource) to run,
330 // which should hold the ProxyLock to avoid data races.
331 //
332 // This is for cases where you want to run a task or store a Callback, but you
333 // want to ensure that the ProxyLock is acquired for the duration of the task
334 // that the Callback runs.
335 // EXAMPLE USAGE:
336 // GetMainThreadMessageLoop()->PostDelayedTask(
337 // FROM_HERE,
338 // RunWhileLocked(base::Bind(&CallbackWrapper, callback, result)),
339 // delay_in_ms);
340 //
341 // In normal usage like the above, this all should "just work". However, if you
342 // do something unusual, you may get a runtime crash due to deadlock. Here are
343 // the ways that the returned Callback must be used to avoid a deadlock:
344 // (1) copied to another Callback. After that, the original callback can be
345 // destroyed with or without the proxy lock acquired, while the newly assigned
346 // callback has to conform to these same restrictions. Or
347 // (2) run without proxy lock acquired (e.g., being posted to a MessageLoop
348 // and run there). The callback must be destroyed on the same thread where it
349 // was run (but can be destroyed with or without the proxy lock acquired). Or
350 // (3) destroyed without the proxy lock acquired.
351 template <class FunctionType>
352 inline base::Callback<FunctionType>
RunWhileLocked(const base::Callback<FunctionType> & callback)353 RunWhileLocked(const base::Callback<FunctionType>& callback) {
354 internal::RunWhileLockedHelper<FunctionType>* helper =
355 new internal::RunWhileLockedHelper<FunctionType>(callback);
356 return base::Bind(
357 &internal::RunWhileLockedHelper<FunctionType>::CallWhileLocked,
358 base::Owned(helper));
359 }
360
361 } // namespace ppapi
362
363 #endif // PPAPI_SHARED_IMPL_PROXY_LOCK_H_
364