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