| // 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 "base/basictypes.h" |
| #include "base/bind.h" |
| #include "base/callback.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. |
| static void DisableLockingOnThreadForTest(); |
| |
| // Enables locking on the current thread. Although locking is enabled by |
| // default, unit tests that rely on the lock being enabled should *still* |
| // call this, since a previous test may have disabled locking. |
| static void EnableLockingOnThreadForTest(); |
| |
| 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(); |
| } |
| template <class ReturnType, class P1> |
| ReturnType CallWhileUnlocked(ReturnType (*function)(P1), const P1& p1) { |
| ProxyAutoUnlock unlock; |
| return function(p1); |
| } |
| template <class ReturnType, class P1, class P2> |
| ReturnType CallWhileUnlocked(ReturnType (*function)(P1, P2), |
| const P1& p1, |
| const P2& p2) { |
| ProxyAutoUnlock unlock; |
| return function(p1, p2); |
| } |
| template <class ReturnType, class P1, class P2, class P3> |
| ReturnType CallWhileUnlocked(ReturnType (*function)(P1, P2, P3), |
| const P1& p1, |
| const P2& p2, |
| const P3& p3) { |
| ProxyAutoUnlock unlock; |
| return function(p1, p2, p3); |
| } |
| template <class ReturnType, class P1, class P2, class P3, class P4> |
| ReturnType CallWhileUnlocked(ReturnType (*function)(P1, P2, P3, P4), |
| const P1& p1, |
| const P2& p2, |
| const P3& p3, |
| const P4& p4) { |
| ProxyAutoUnlock unlock; |
| return function(p1, p2, p3, p4); |
| } |
| template <class ReturnType, class P1, class P2, class P3, class P4, class P5> |
| ReturnType CallWhileUnlocked(ReturnType (*function)(P1, P2, P3, P4, P5), |
| 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; |
| |
| template <> |
| class RunWhileLockedHelper<void()> { |
| public: |
| typedef base::Callback<void()> CallbackType; |
| explicit RunWhileLockedHelper(const CallbackType& callback) |
| : callback_(new CallbackType(callback)) { |
| // Copying |callback| may adjust reference counts for bound Vars or |
| // Resources; we should have the lock already. |
| ProxyLock::AssertAcquired(); |
| // CallWhileLocked and destruction might happen on a different thread from |
| // creation. |
| thread_checker_.DetachFromThread(); |
| } |
| void CallWhileLocked() { |
| // Bind thread_checker_ to this thread so we can check in the destructor. |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| ProxyAutoLock lock; |
| { |
| // 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. |
| scoped_ptr<CallbackType> temp_callback(callback_.Pass()); |
| temp_callback->Run(); |
| } |
| } |
| |
| ~RunWhileLockedHelper() { |
| // Check that the Callback is destroyed on the same thread as where |
| // CallWhileLocked happened (if CallWhileLocked happened). |
| 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. There are 2 cases where |
| // callback_ will be NULL: |
| // 1) This is the original RunWhileLockedHelper that RunWhileLocked |
| // created. When it was copied somewhere else (e.g., to a MessageLoop |
| // queue), callback_ was passed to the new copy, and the original |
| // RunWhileLockedHelper's callback_ was set to NULL (since scoped_ptrs |
| // only ever have 1 owner). In this case, we don't want to acquire the |
| // lock, because we already have it. |
| // 2) callback_ 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 still need to have the lock when we |
| // destroy the callback in case it had a Resource bound to it. This |
| // ensures that the Resource's destructor is invoked only with the lock |
| // held. |
| // |
| // Also: Resource and Var inherit RefCounted (not ThreadSafeRefCounted), |
| // and these callbacks need to be usable on any thread. So we need to lock |
| // when releasing the callback to avoid ref counting races. |
| ProxyAutoLock lock; |
| callback_.reset(); |
| } |
| } |
| |
| private: |
| scoped_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)) { |
| ProxyLock::AssertAcquired(); |
| thread_checker_.DetachFromThread(); |
| } |
| void CallWhileLocked(P1 p1) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| ProxyAutoLock lock; |
| { |
| scoped_ptr<CallbackType> temp_callback(callback_.Pass()); |
| temp_callback->Run(p1); |
| } |
| } |
| ~RunWhileLockedHelper() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| if (callback_) { |
| ProxyAutoLock lock; |
| callback_.reset(); |
| } |
| } |
| |
| private: |
| scoped_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)) { |
| ProxyLock::AssertAcquired(); |
| thread_checker_.DetachFromThread(); |
| } |
| void CallWhileLocked(P1 p1, P2 p2) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| ProxyAutoLock lock; |
| { |
| scoped_ptr<CallbackType> temp_callback(callback_.Pass()); |
| temp_callback->Run(p1, p2); |
| } |
| } |
| ~RunWhileLockedHelper() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| if (callback_) { |
| ProxyAutoLock lock; |
| callback_.reset(); |
| } |
| } |
| |
| private: |
| scoped_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)) { |
| ProxyLock::AssertAcquired(); |
| thread_checker_.DetachFromThread(); |
| } |
| void CallWhileLocked(P1 p1, P2 p2, P3 p3) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| ProxyAutoLock lock; |
| { |
| scoped_ptr<CallbackType> temp_callback(callback_.Pass()); |
| temp_callback->Run(p1, p2, p3); |
| } |
| } |
| ~RunWhileLockedHelper() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| if (callback_) { |
| ProxyAutoLock lock; |
| callback_.reset(); |
| } |
| } |
| |
| private: |
| scoped_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) { |
| internal::RunWhileLockedHelper<FunctionType>* helper = |
| new internal::RunWhileLockedHelper<FunctionType>(callback); |
| return base::Bind( |
| &internal::RunWhileLockedHelper<FunctionType>::CallWhileLocked, |
| base::Owned(helper)); |
| } |
| |
| } // namespace ppapi |
| |
| #endif // PPAPI_SHARED_IMPL_PROXY_LOCK_H_ |