• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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() { ProxyLock::Acquire(); }
~ProxyAutoLock()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:
ProxyAutoUnlock()97   ProxyAutoUnlock() { ProxyLock::Release(); }
~ProxyAutoUnlock()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>
CallWhileUnlocked(ReturnType (* function)())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>
CallWhileUnlocked(ReturnType (* function)(A1),const P1 & 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>
CallWhileUnlocked(ReturnType (* function)(A1,A2),const P1 & p1,const P2 & 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>
CallWhileUnlocked(ReturnType (* function)(A1,A2,A3),const P1 & p1,const P2 & p2,const P3 & 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>
CallWhileUnlocked(ReturnType (* function)(A1,A2,A3,A4),const P1 & p1,const P2 & p2,const P3 & p3,const P4 & 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>
CallWhileUnlocked(ReturnType (* function)(A1,A2,A3,A4,A5),const P1 & p1,const P2 & p2,const P3 & p3,const P4 & p4,const P5 & 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;
RunWhileLockedHelper(const CallbackType & callback)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   }
CallWhileLocked()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 
~RunWhileLockedHelper()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;
RunWhileLockedHelper(const CallbackType & callback)240   explicit RunWhileLockedHelper(const CallbackType& callback)
241       : callback_(new CallbackType(callback)) {
242     ProxyLock::AssertAcquired();
243     thread_checker_.DetachFromThread();
244   }
CallWhileLocked(P1 p1)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   }
~RunWhileLockedHelper()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;
RunWhileLockedHelper(const CallbackType & callback)270   explicit RunWhileLockedHelper(const CallbackType& callback)
271       : callback_(new CallbackType(callback)) {
272     ProxyLock::AssertAcquired();
273     thread_checker_.DetachFromThread();
274   }
CallWhileLocked(P1 p1,P2 p2)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   }
~RunWhileLockedHelper()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;
RunWhileLockedHelper(const CallbackType & callback)300   explicit RunWhileLockedHelper(const CallbackType& callback)
301       : callback_(new CallbackType(callback)) {
302     ProxyLock::AssertAcquired();
303     thread_checker_.DetachFromThread();
304   }
CallWhileLocked(P1 p1,P2 p2,P3 p3)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   }
~RunWhileLockedHelper()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>
RunWhileLocked(const base::Callback<FunctionType> & callback)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