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 ppapi {
     20 
     21 // This is the one lock to rule them all for the ppapi proxy. All PPB interface
     22 // functions that need to be synchronized should lock this lock on entry. This
     23 // is normally accomplished by using an appropriate Enter RAII object at the
     24 // beginning of each thunk function.
     25 //
     26 // TODO(dmichael): If this turns out to be too slow and contentious, we'll want
     27 // to use multiple locks. E.g., one for the var tracker, one for the resource
     28 // tracker, etc.
     29 class PPAPI_SHARED_EXPORT ProxyLock {
     30  public:
     31   // Acquire the proxy lock. If it is currently held by another thread, block
     32   // until it is available. If the lock has not been set using the 'Set' method,
     33   // this operation does nothing. That is the normal case for the host side;
     34   // see PluginResourceTracker for where the lock gets set for the out-of-
     35   // process plugin case.
     36   static void Acquire();
     37   // Relinquish the proxy lock. If the lock has not been set, this does nothing.
     38   static void Release();
     39 
     40   // Assert that the lock is owned by the current thread (in the plugin
     41   // process). Does nothing when running in-process (or in the host process).
     42   static void AssertAcquired();
     43 
     44  private:
     45   DISALLOW_IMPLICIT_CONSTRUCTORS(ProxyLock);
     46 };
     47 
     48 // A simple RAII class for locking the PPAPI proxy lock on entry and releasing
     49 // on exit. This is for simple interfaces that don't use the 'thunk' system,
     50 // such as PPB_Var and PPB_Core.
     51 class ProxyAutoLock {
     52  public:
     53   ProxyAutoLock() {
     54     ProxyLock::Acquire();
     55   }
     56   ~ProxyAutoLock() {
     57     ProxyLock::Release();
     58   }
     59  private:
     60   DISALLOW_COPY_AND_ASSIGN(ProxyAutoLock);
     61 };
     62 
     63 // The inverse of the above; unlock on construction, lock on destruction. This
     64 // is useful for calling out to the plugin, when we need to unlock but ensure
     65 // that we re-acquire the lock when the plugin is returns or raises an
     66 // exception.
     67 class ProxyAutoUnlock {
     68  public:
     69   ProxyAutoUnlock() {
     70     ProxyLock::Release();
     71   }
     72   ~ProxyAutoUnlock() {
     73     ProxyLock::Acquire();
     74   }
     75  private:
     76   DISALLOW_COPY_AND_ASSIGN(ProxyAutoUnlock);
     77 };
     78 
     79 // A set of function template overloads for invoking a function pointer while
     80 // the ProxyLock is unlocked. This assumes that the luck is held.
     81 // CallWhileUnlocked unlocks the ProxyLock just before invoking the given
     82 // function. The lock is immediately re-acquired when the invoked function
     83 // function returns. CallWhileUnlocked returns whatever the given function
     84 // returned.
     85 //
     86 // Example usage:
     87 //   *result = CallWhileUnlocked(ppp_input_event_impl_->HandleInputEvent,
     88 //                               instance,
     89 //                               resource->pp_resource());
     90 template <class ReturnType>
     91 ReturnType CallWhileUnlocked(ReturnType (*function)()) {
     92   ProxyAutoUnlock unlock;
     93   return function();
     94 }
     95 template <class ReturnType, class P1>
     96 ReturnType CallWhileUnlocked(ReturnType (*function)(P1), const P1& p1) {
     97   ProxyAutoUnlock unlock;
     98   return function(p1);
     99 }
    100 template <class ReturnType, class P1, class P2>
    101 ReturnType CallWhileUnlocked(ReturnType (*function)(P1, P2),
    102                              const P1& p1,
    103                              const P2& p2) {
    104   ProxyAutoUnlock unlock;
    105   return function(p1, p2);
    106 }
    107 template <class ReturnType, class P1, class P2, class P3>
    108 ReturnType CallWhileUnlocked(ReturnType (*function)(P1, P2, P3),
    109                              const P1& p1,
    110                              const P2& p2,
    111                              const P3& p3) {
    112   ProxyAutoUnlock unlock;
    113   return function(p1, p2, p3);
    114 }
    115 template <class ReturnType, class P1, class P2, class P3, class P4>
    116 ReturnType CallWhileUnlocked(ReturnType (*function)(P1, P2, P3, P4),
    117                              const P1& p1,
    118                              const P2& p2,
    119                              const P3& p3,
    120                              const P4& p4) {
    121   ProxyAutoUnlock unlock;
    122   return function(p1, p2, p3, p4);
    123 }
    124 template <class ReturnType, class P1, class P2, class P3, class P4, class P5>
    125 ReturnType CallWhileUnlocked(ReturnType (*function)(P1, P2, P3, P4, P5),
    126                              const P1& p1,
    127                              const P2& p2,
    128                              const P3& p3,
    129                              const P4& p4,
    130                              const P5& p5) {
    131   ProxyAutoUnlock unlock;
    132   return function(p1, p2, p3, p4, p5);
    133 }
    134 void PPAPI_SHARED_EXPORT CallWhileUnlocked(const base::Closure& closure);
    135 
    136 namespace internal {
    137 
    138 template <typename RunType>
    139 class RunWhileLockedHelper;
    140 
    141 template <>
    142 class RunWhileLockedHelper<void ()> {
    143  public:
    144   typedef base::Callback<void ()> CallbackType;
    145   explicit RunWhileLockedHelper(const CallbackType& callback)
    146       : callback_(new CallbackType(callback)) {
    147     // Copying |callback| may adjust reference counts for bound Vars or
    148     // Resources; we should have the lock already.
    149     ProxyLock::AssertAcquired();
    150     // CallWhileLocked and destruction might happen on a different thread from
    151     // creation.
    152     thread_checker_.DetachFromThread();
    153   }
    154   void CallWhileLocked() {
    155     // Bind thread_checker_ to this thread so we can check in the destructor.
    156     DCHECK(thread_checker_.CalledOnValidThread());
    157     ProxyAutoLock lock;
    158     {
    159       // Use a scope and local Callback to ensure that the callback is cleared
    160       // before the lock is released, even in the unlikely event that Run()
    161       // throws an exception.
    162       scoped_ptr<CallbackType> temp_callback(callback_.Pass());
    163       temp_callback->Run();
    164     }
    165   }
    166 
    167  private:
    168   scoped_ptr<CallbackType> callback_;
    169 
    170   // Used to ensure that the Callback is run and deleted on the same thread.
    171   base::ThreadChecker thread_checker_;
    172 };
    173 
    174 template <typename P1>
    175 class RunWhileLockedHelper<void (P1)> {
    176  public:
    177   typedef base::Callback<void (P1)> CallbackType;
    178   explicit RunWhileLockedHelper(const CallbackType& callback)
    179       : callback_(new CallbackType(callback)) {
    180     ProxyLock::AssertAcquired();
    181     thread_checker_.DetachFromThread();
    182   }
    183   void CallWhileLocked(P1 p1) {
    184     DCHECK(thread_checker_.CalledOnValidThread());
    185     ProxyAutoLock lock;
    186     {
    187       scoped_ptr<CallbackType> temp_callback(callback_.Pass());
    188       temp_callback->Run(p1);
    189     }
    190   }
    191 
    192  private:
    193   scoped_ptr<CallbackType> callback_;
    194   base::ThreadChecker thread_checker_;
    195 };
    196 
    197 template <typename P1, typename P2>
    198 class RunWhileLockedHelper<void (P1, P2)> {
    199  public:
    200   typedef base::Callback<void (P1, P2)> CallbackType;
    201   explicit RunWhileLockedHelper(const CallbackType& callback)
    202       : callback_(new CallbackType(callback)) {
    203     ProxyLock::AssertAcquired();
    204     thread_checker_.DetachFromThread();
    205   }
    206   void CallWhileLocked(P1 p1, P2 p2) {
    207     DCHECK(thread_checker_.CalledOnValidThread());
    208     ProxyAutoLock lock;
    209     {
    210       scoped_ptr<CallbackType> temp_callback(callback_.Pass());
    211       temp_callback->Run(p1, p2);
    212     }
    213   }
    214 
    215  private:
    216   scoped_ptr<CallbackType> callback_;
    217   base::ThreadChecker thread_checker_;
    218 };
    219 
    220 template <typename P1, typename P2, typename P3>
    221 class RunWhileLockedHelper<void (P1, P2, P3)> {
    222  public:
    223   typedef base::Callback<void (P1, P2, P3)> CallbackType;
    224   explicit RunWhileLockedHelper(const CallbackType& callback)
    225       : callback_(new CallbackType(callback)) {
    226     ProxyLock::AssertAcquired();
    227     thread_checker_.DetachFromThread();
    228   }
    229   void CallWhileLocked(P1 p1, P2 p2, P3 p3) {
    230     DCHECK(thread_checker_.CalledOnValidThread());
    231     ProxyAutoLock lock;
    232     {
    233       scoped_ptr<CallbackType> temp_callback(callback_.Pass());
    234       temp_callback->Run(p1, p2, p3);
    235     }
    236   }
    237 
    238  private:
    239   scoped_ptr<CallbackType> callback_;
    240   base::ThreadChecker thread_checker_;
    241 };
    242 
    243 }  // namespace internal
    244 
    245 // RunWhileLocked wraps the given Callback in a new Callback that, when invoked:
    246 //  1) Locks the ProxyLock.
    247 //  2) Runs the original Callback (forwarding arguments, if any).
    248 //  3) Clears the original Callback (while the lock is held).
    249 //  4) Unlocks the ProxyLock.
    250 // Note that it's important that the callback is cleared in step (3), in case
    251 // clearing the Callback causes a destructor (e.g., for a Resource) to run,
    252 // which should hold the ProxyLock to avoid data races.
    253 //
    254 // This is for cases where you want to run a task or store a Callback, but you
    255 // want to ensure that the ProxyLock is acquired for the duration of the task
    256 // that the Callback runs.
    257 // EXAMPLE USAGE:
    258 //   GetMainThreadMessageLoop()->PostDelayedTask(
    259 //     FROM_HERE,
    260 //     RunWhileLocked(base::Bind(&CallbackWrapper, callback, result)),
    261 //     delay_in_ms);
    262 //
    263 // In normal usage like the above, this all should "just work". However, if you
    264 // do something unusual, you may get a runtime crash due to deadlock. Here are
    265 // the ways that the returned Callback must be used to avoid a deadlock:
    266 // (1) copied to another Callback. After that, the original callback can be
    267 // destroyed with or without the proxy lock acquired, while the newly assigned
    268 // callback has to conform to these same restrictions. Or
    269 // (2) run without proxy lock acquired (e.g., being posted to a MessageLoop
    270 // and run there). The callback must be destroyed on the same thread where it
    271 // was run (but can be destroyed with or without the proxy lock acquired). Or
    272 // (3) destroyed without the proxy lock acquired.
    273 // TODO(dmichael): This won't actually fail until
    274 //                 https://codereview.chromium.org/19492014/ lands.
    275 template <class FunctionType>
    276 inline base::Callback<FunctionType>
    277 RunWhileLocked(const base::Callback<FunctionType>& callback) {
    278   internal::RunWhileLockedHelper<FunctionType>* helper =
    279       new internal::RunWhileLockedHelper<FunctionType>(callback);
    280   return base::Bind(
    281       &internal::RunWhileLockedHelper<FunctionType>::CallWhileLocked,
    282       base::Owned(helper));
    283 }
    284 
    285 }  // namespace ppapi
    286 
    287 #endif  // PPAPI_SHARED_IMPL_PROXY_LOCK_H_
    288