text: // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #ifndef PPAPI_SHARED_IMPL_PROXY_LOCK_H_
  #define PPAPI_SHARED_IMPL_PROXY_LOCK_H_
  
  #include <memory>
  #include <utility>
  
  #include "base/bind.h"
  #include "base/callback.h"
  #include "base/macros.h"
  #include "base/threading/thread_checker.h"
  #include "ppapi/shared_impl/ppapi_shared_export.h"
  
  namespace base {
  class Lock;
  }
  
  namespace content {
  class HostGlobals;
  }
  
  namespace ppapi {
  
  // This is the one lock to rule them all for the ppapi proxy. All PPB interface
  // functions that need to be synchronized should lock this lock on entry. This
  // is normally accomplished by using an appropriate Enter RAII object at the
  // beginning of each thunk function.
  //
  // TODO(dmichael): If this turns out to be too slow and contentious, we'll want
  // to use multiple locks. E.g., one for the var tracker, one for the resource
  // tracker, etc.
  class PPAPI_SHARED_EXPORT ProxyLock {
   public:
    // Return the global ProxyLock. Normally, you should not access this
    // directly but instead use ProxyAutoLock or ProxyAutoUnlock. But sometimes
    // you need access to the ProxyLock, for example to create a condition
    // variable.
    static base::Lock* Get();
  
    // Acquire the proxy lock. If it is currently held by another thread, block
    // until it is available. If the lock has not been set using the 'Set' method,
    // this operation does nothing. That is the normal case for the host side;
    // see PluginResourceTracker for where the lock gets set for the out-of-
    // process plugin case.
    static void Acquire();
    // Relinquish the proxy lock. If the lock has not been set, this does nothing.
    static void Release();
  
    // Assert that the lock is owned by the current thread (in the plugin
    // process). Does nothing when running in-process (or in the host process).
    static void AssertAcquired();
    static void AssertAcquiredDebugOnly() {
  #ifndef NDEBUG
      AssertAcquired();
  #endif
    }
  
    // We have some unit tests where one thread pretends to be the host and one
    // pretends to be the plugin. This allows the lock to do nothing on only one
    // thread to support these tests. See TwoWayTest for more information.
    class PPAPI_SHARED_EXPORT LockingDisablerForTest {
     public:
      LockingDisablerForTest();
      ~LockingDisablerForTest();
    };
  
   private:
    friend class content::HostGlobals;
    // On the host side, we do not lock. This must be called at most once at
    // startup, before other threads that may access the ProxyLock have had a
    // chance to run.
    static void DisableLocking();
  
    DISALLOW_IMPLICIT_CONSTRUCTORS(ProxyLock);
  };
  
  // A simple RAII class for locking the PPAPI proxy lock on entry and releasing
  // on exit. This is for simple interfaces that don't use the 'thunk' system,
  // such as PPB_Var and PPB_Core.
  class ProxyAutoLock {
   public:
    ProxyAutoLock() { ProxyLock::Acquire(); }
    ~ProxyAutoLock() { ProxyLock::Release(); }
  
   private:
    DISALLOW_COPY_AND_ASSIGN(ProxyAutoLock);
  };
  
  // The inverse of the above; unlock on construction, lock on destruction. This
  // is useful for calling out to the plugin, when we need to unlock but ensure
  // that we re-acquire the lock when the plugin is returns or raises an
  // exception.
  class ProxyAutoUnlock {
   public:
    ProxyAutoUnlock() { ProxyLock::Release(); }
    ~ProxyAutoUnlock() { ProxyLock::Acquire(); }
  
   private:
    DISALLOW_COPY_AND_ASSIGN(ProxyAutoUnlock);
  };
  
  // A set of function template overloads for invoking a function pointer while
  // the ProxyLock is unlocked. This assumes that the luck is held.
  // CallWhileUnlocked unlocks the ProxyLock just before invoking the given
  // function. The lock is immediately re-acquired when the invoked function
  // function returns. CallWhileUnlocked returns whatever the given function
  // returned.
  //
  // Example usage:
  //   *result = CallWhileUnlocked(ppp_input_event_impl_->HandleInputEvent,
  //                               instance,
  //                               resource->pp_resource());
  template <class ReturnType>
  ReturnType CallWhileUnlocked(ReturnType (*function)()) {
    ProxyAutoUnlock unlock;
    return function();
  }
  // Note we use 2 types for the params, even though for the most part we expect
  // A1 to match P1. We let the compiler determine if P1 can convert safely to
  // A1. This allows callers to avoid having to do things like
  // const_cast to add const.
  template <class ReturnType, class A1, class P1>
  ReturnType CallWhileUnlocked(ReturnType (*function)(A1), const P1& p1) {
    ProxyAutoUnlock unlock;
    return function(p1);
  }
  template <class ReturnType, class A1, class A2, class P1, class P2>
  ReturnType CallWhileUnlocked(ReturnType (*function)(A1, A2),
                               const P1& p1,
                               const P2& p2) {
    ProxyAutoUnlock unlock;
    return function(p1, p2);
  }
  template <class ReturnType, class A1, class A2, class A3, class P1, class P2,
            class P3>
  ReturnType CallWhileUnlocked(ReturnType (*function)(A1, A2, A3),
                               const P1& p1,
                               const P2& p2,
                               const P3& p3) {
    ProxyAutoUnlock unlock;
    return function(p1, p2, p3);
  }
  template <class ReturnType, class A1, class A2, class A3, class A4, class P1,
            class P2, class P3, class P4>
  ReturnType CallWhileUnlocked(ReturnType (*function)(A1, A2, A3, A4),
                               const P1& p1,
                               const P2& p2,
                               const P3& p3,
                               const P4& p4) {
    ProxyAutoUnlock unlock;
    return function(p1, p2, p3, p4);
  }
  template <class ReturnType, class A1, class A2, class A3, class A4, class A5,
            class P1, class P2, class P3, class P4, class P5>
  ReturnType CallWhileUnlocked(ReturnType (*function)(A1, A2, A3, A4, A5),
                               const P1& p1,
                               const P2& p2,
                               const P3& p3,
                               const P4& p4,
                               const P5& p5) {
    ProxyAutoUnlock unlock;
    return function(p1, p2, p3, p4, p5);
  }
  void PPAPI_SHARED_EXPORT CallWhileUnlocked(const base::Closure& closure);
  
  namespace internal {
  
  template <typename RunType>
  class RunWhileLockedHelper;
  
  // A helper class to ensure that a callback is always run and destroyed while
  // the ProxyLock is held. A callback that is bound with ref-counted Var or
  // Resource parameters may invoke methods on the VarTracker or the
  // ResourceTracker in its destructor, and these require the ProxyLock.
  template <>
  class RunWhileLockedHelper<void()> {
   public:
    typedef base::Callback<void()> CallbackType;
    explicit RunWhileLockedHelper(const CallbackType& callback)
        : callback_(new CallbackType(callback)) {
      // CallWhileLocked and destruction might happen on a different thread from
      // creation.
      thread_checker_.DetachFromThread();
    }
    static void CallWhileLocked(std::unique_ptr<RunWhileLockedHelper> ptr) {
      // Bind thread_checker_ to this thread so we can check in the destructor.
      // *If* the callback gets invoked, it's important that RunWhileLockedHelper
      // is destroyed on the same thread (see the comments in the destructor).
      DCHECK(ptr->thread_checker_.CalledOnValidThread());
      ProxyAutoLock lock;
  
      // Relax the cross-thread access restriction to non-thread-safe RefCount.
      // |lock| above protects the access to Resource instances.
      base::ScopedAllowCrossThreadRefCountAccess
          allow_cross_thread_ref_count_access;
  
      {
        // Use a scope and local Callback to ensure that the callback is cleared
        // before the lock is released, even in the unlikely event that Run()
        // throws an exception.
        std::unique_ptr<CallbackType> temp_callback(std::move(ptr->callback_));
        temp_callback->Run();
      }
    }
  
    ~RunWhileLockedHelper() {
      // Check that the Callback is destroyed on the same thread as where
      // CallWhileLocked happened if CallWhileLocked happened. If we weren't
      // invoked, thread_checked_ isn't bound to a thread.
      DCHECK(thread_checker_.CalledOnValidThread());
      // Here we read callback_ without the lock. This is why the callback must be
      // destroyed on the same thread where it runs. Note that callback_ will be
      // NULL if it has already been run via CallWhileLocked. In this case,
      // there's no need to acquire the lock, because we don't touch any shared
      // data.
      if (callback_) {
        // If the callback was *not* run, we're in a case where the task queue
        // we got pushed to has been destroyed (e.g., the thread is shut down and
        // its MessageLoop destroyed before all tasks have run.)
        //
        // We still need to have the lock when we destroy the callback:
        // - Because Resource and Var inherit RefCounted (not
        //   ThreadSafeRefCounted).
        // - Because if the callback owns the last ref to a Resource, it will
        //   call the ResourceTracker and also the Resource's destructor, which
        //   both require the ProxyLock.
        ProxyAutoLock lock;
  
        // Relax the cross-thread access restriction to non-thread-safe RefCount.
        // |lock| above protects the access to Resource instances.
        base::ScopedAllowCrossThreadRefCountAccess
            allow_cross_thread_ref_count_access;
  
        callback_.reset();
      }
    }
  
   private:
    DISALLOW_COPY_AND_ASSIGN(RunWhileLockedHelper);
    std::unique_ptr<CallbackType> callback_;
  
    // Used to ensure that the Callback is run and deleted on the same thread.
    base::ThreadChecker thread_checker_;
  };
  
  template <typename P1>
  class RunWhileLockedHelper<void(P1)> {
   public:
    typedef base::Callback<void(P1)> CallbackType;
    explicit RunWhileLockedHelper(const CallbackType& callback)
        : callback_(new CallbackType(callback)) {
      thread_checker_.DetachFromThread();
    }
    static void CallWhileLocked(std::unique_ptr<RunWhileLockedHelper> ptr,
                                P1 p1) {
      DCHECK(ptr->thread_checker_.CalledOnValidThread());
      ProxyAutoLock lock;
      // Relax the cross-thread access restriction to non-thread-safe RefCount.
      // |lock| above protects the access to Resource instances.
      base::ScopedAllowCrossThreadRefCountAccess
          allow_cross_thread_ref_count_access;
      {
        std::unique_ptr<CallbackType> temp_callback(std::move(ptr->callback_));
        temp_callback->Run(p1);
      }
    }
    ~RunWhileLockedHelper() {
      DCHECK(thread_checker_.CalledOnValidThread());
      if (callback_) {
        ProxyAutoLock lock;
        // Relax the cross-thread access restriction to non-thread-safe RefCount.
        // |lock| above protects the access to Resource instances.
        base::ScopedAllowCrossThreadRefCountAccess
            allow_cross_thread_ref_count_access;
        callback_.reset();
      }
    }
  
   private:
    DISALLOW_COPY_AND_ASSIGN(RunWhileLockedHelper);
    std::unique_ptr<CallbackType> callback_;
    base::ThreadChecker thread_checker_;
  };
  
  template <typename P1, typename P2>
  class RunWhileLockedHelper<void(P1, P2)> {
   public:
    typedef base::Callback<void(P1, P2)> CallbackType;
    explicit RunWhileLockedHelper(const CallbackType& callback)
        : callback_(new CallbackType(callback)) {
      thread_checker_.DetachFromThread();
    }
    static void CallWhileLocked(std::unique_ptr<RunWhileLockedHelper> ptr,
                                P1 p1,
                                P2 p2) {
      DCHECK(ptr->thread_checker_.CalledOnValidThread());
      ProxyAutoLock lock;
      // Relax the cross-thread access restriction to non-thread-safe RefCount.
      // |lock| above protects the access to Resource instances.
      base::ScopedAllowCrossThreadRefCountAccess
          allow_cross_thread_ref_count_access;
      {
        std::unique_ptr<CallbackType> temp_callback(std::move(ptr->callback_));
        temp_callback->Run(p1, p2);
      }
    }
    ~RunWhileLockedHelper() {
      DCHECK(thread_checker_.CalledOnValidThread());
      if (callback_) {
        ProxyAutoLock lock;
        // Relax the cross-thread access restriction to non-thread-safe RefCount.
        // |lock| above protects the access to Resource instances.
        base::ScopedAllowCrossThreadRefCountAccess
            allow_cross_thread_ref_count_access;
        callback_.reset();
      }
    }
  
   private:
    DISALLOW_COPY_AND_ASSIGN(RunWhileLockedHelper);
    std::unique_ptr<CallbackType> callback_;
    base::ThreadChecker thread_checker_;
  };
  
  template <typename P1, typename P2, typename P3>
  class RunWhileLockedHelper<void(P1, P2, P3)> {
   public:
    typedef base::Callback<void(P1, P2, P3)> CallbackType;
    explicit RunWhileLockedHelper(const CallbackType& callback)
        : callback_(new CallbackType(callback)) {
      thread_checker_.DetachFromThread();
    }
    static void CallWhileLocked(std::unique_ptr<RunWhileLockedHelper> ptr,
                                P1 p1,
                                P2 p2,
                                P3 p3) {
      DCHECK(ptr->thread_checker_.CalledOnValidThread());
      ProxyAutoLock lock;
      // Relax the cross-thread access restriction to non-thread-safe RefCount.
      // |lock| above protects the access to Resource instances.
      base::ScopedAllowCrossThreadRefCountAccess
          allow_cross_thread_ref_count_access;
      {
        std::unique_ptr<CallbackType> temp_callback(std::move(ptr->callback_));
        temp_callback->Run(p1, p2, p3);
      }
    }
    ~RunWhileLockedHelper() {
      DCHECK(thread_checker_.CalledOnValidThread());
      if (callback_) {
        ProxyAutoLock lock;
        // Relax the cross-thread access restriction to non-thread-safe RefCount.
        // |lock| above protects the access to Resource instances.
        base::ScopedAllowCrossThreadRefCountAccess
            allow_cross_thread_ref_count_access;
        callback_.reset();
      }
    }
  
   private:
    DISALLOW_COPY_AND_ASSIGN(RunWhileLockedHelper);
    std::unique_ptr<CallbackType> callback_;
    base::ThreadChecker thread_checker_;
  };
  
  }  // namespace internal
  
  // RunWhileLocked wraps the given Callback in a new Callback that, when invoked:
  //  1) Locks the ProxyLock.
  //  2) Runs the original Callback (forwarding arguments, if any).
  //  3) Clears the original Callback (while the lock is held).
  //  4) Unlocks the ProxyLock.
  // Note that it's important that the callback is cleared in step (3), in case
  // clearing the Callback causes a destructor (e.g., for a Resource) to run,
  // which should hold the ProxyLock to avoid data races.
  //
  // This is for cases where you want to run a task or store a Callback, but you
  // want to ensure that the ProxyLock is acquired for the duration of the task
  // that the Callback runs.
  // EXAMPLE USAGE:
  //   GetMainThreadMessageLoop()->PostDelayedTask(
  //     FROM_HERE,
  //     RunWhileLocked(base::Bind(&CallbackWrapper, callback, result)),
  //     delay_in_ms);
  //
  // In normal usage like the above, this all should "just work". However, if you
  // do something unusual, you may get a runtime crash due to deadlock. Here are
  // the ways that the returned Callback must be used to avoid a deadlock:
  // (1) copied to another Callback. After that, the original callback can be
  // destroyed with or without the proxy lock acquired, while the newly assigned
  // callback has to conform to these same restrictions. Or
  // (2) run without proxy lock acquired (e.g., being posted to a MessageLoop
  // and run there). The callback must be destroyed on the same thread where it
  // was run (but can be destroyed with or without the proxy lock acquired). Or
  // (3) destroyed without the proxy lock acquired.
  template <class FunctionType>
  inline base::Callback<FunctionType> RunWhileLocked(
      const base::Callback<FunctionType>& callback) {
    // NOTE: the reason we use "scoped_ptr" here instead of letting the callback
    // own it via base::Owned is kind of subtle. Imagine for the moment that we
    // call RunWhileLocked without the ProxyLock:
    // {
    //   base::Callback<void ()> local_callback = base::Bind(&Foo);
    //   some_task_runner.PostTask(FROM_HERE, RunWhileLocked(local_callback));
    // }
    // In this case, since we don't have a lock synchronizing us, it's possible
    // for the callback to run on the other thread before we return and destroy
    // |local_callback|. The important thing here is that even though the other
    // thread gets a copy of the callback, the internal "BindState" of the
    // callback is refcounted and shared between all copies of the callback. So
    // in that case, if we used base::Owned, we might delete RunWhileLockedHelper
    // on this thread, which will violate the RunWhileLockedHelper's assumption
    // that it is destroyed on the same thread where it is run.
    std::unique_ptr<internal::RunWhileLockedHelper<FunctionType>> helper(
        new internal::RunWhileLockedHelper<FunctionType>(callback));
    return base::Bind(
        &internal::RunWhileLockedHelper<FunctionType>::CallWhileLocked,
        base::Passed(std::move(helper)));
  }
  
  }  // namespace ppapi
  
  #endif  // PPAPI_SHARED_IMPL_PROXY_LOCK_H_
  