Index: ppapi/shared_impl/proxy_lock.h |
diff --git a/ppapi/shared_impl/proxy_lock.h b/ppapi/shared_impl/proxy_lock.h |
index 935247189813b18552749c852770140fb080d512..9a81a13baa1366c6003bfc9ebfa56e19a5b02675 100644 |
--- a/ppapi/shared_impl/proxy_lock.h |
+++ b/ppapi/shared_impl/proxy_lock.h |
@@ -16,6 +16,10 @@ 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 |
@@ -28,6 +32,12 @@ namespace ppapi { |
// 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; |
@@ -41,7 +51,23 @@ class PPAPI_SHARED_EXPORT ProxyLock { |
// process). Does nothing when running in-process (or in the host process). |
static void AssertAcquired(); |
+ // 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); |
}; |
@@ -164,6 +190,35 @@ class RunWhileLockedHelper<void ()> { |
} |
} |
+ ~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_; |
@@ -188,7 +243,13 @@ class RunWhileLockedHelper<void (P1)> { |
temp_callback->Run(p1); |
} |
} |
- |
+ ~RunWhileLockedHelper() { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ if (callback_) { |
+ ProxyAutoLock lock; |
+ callback_.reset(); |
+ } |
+ } |
private: |
scoped_ptr<CallbackType> callback_; |
base::ThreadChecker thread_checker_; |
@@ -211,7 +272,13 @@ class RunWhileLockedHelper<void (P1, P2)> { |
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_; |
@@ -234,7 +301,13 @@ class RunWhileLockedHelper<void (P1, P2, P3)> { |
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_; |
@@ -270,8 +343,6 @@ class RunWhileLockedHelper<void (P1, P2, P3)> { |
// 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. |
-// TODO(dmichael): This won't actually fail until |
-// https://codereview.chromium.org/19492014/ lands. |
template <class FunctionType> |
inline base::Callback<FunctionType> |
RunWhileLocked(const base::Callback<FunctionType>& callback) { |