Home | History | Annotate | Download | only in shared_impl
      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