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() {
     85     ProxyLock::Acquire();
     86   }
     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:
    100   ProxyAutoUnlock() {
    101     ProxyLock::Release();
    102   }
    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>
    122 ReturnType CallWhileUnlocked(ReturnType (*function)()) {
    123   ProxyAutoUnlock unlock;
    124   return function();
    125 }
    126 template <class ReturnType, class 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>
    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>
    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>
    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>
    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;
    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   }
    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 
    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;
    238   explicit RunWhileLockedHelper(const CallbackType& callback)
    239       : callback_(new CallbackType(callback)) {
    240     ProxyLock::AssertAcquired();
    241     thread_checker_.DetachFromThread();
    242   }
    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   }
    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;
    267   explicit RunWhileLockedHelper(const CallbackType& callback)
    268       : callback_(new CallbackType(callback)) {
    269     ProxyLock::AssertAcquired();
    270     thread_checker_.DetachFromThread();
    271   }
    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   }
    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;
    296   explicit RunWhileLockedHelper(const CallbackType& callback)
    297       : callback_(new CallbackType(callback)) {
    298     ProxyLock::AssertAcquired();
    299     thread_checker_.DetachFromThread();
    300   }
    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   }
    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>
    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