| Index: ppapi/shared_impl/proxy_lock.h
|
| diff --git a/ppapi/shared_impl/proxy_lock.h b/ppapi/shared_impl/proxy_lock.h
|
| index e0e97e4e38f4071ed592a4b673a4578265fadf17..935247189813b18552749c852770140fb080d512 100644
|
| --- a/ppapi/shared_impl/proxy_lock.h
|
| +++ b/ppapi/shared_impl/proxy_lock.h
|
| @@ -8,6 +8,7 @@
|
| #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"
|
|
|
| @@ -132,21 +133,153 @@ ReturnType CallWhileUnlocked(ReturnType (*function)(P1, P2, P3, P4, P5),
|
| }
|
| void PPAPI_SHARED_EXPORT CallWhileUnlocked(const base::Closure& closure);
|
|
|
| -// CallWhileLocked locks the ProxyLock and runs the given closure immediately.
|
| -// The lock is released when CallWhileLocked returns. This function assumes the
|
| -// lock is not held. This is mostly for use in RunWhileLocked; see below.
|
| -void PPAPI_SHARED_EXPORT CallWhileLocked(const base::Closure& closure);
|
| +namespace internal {
|
|
|
| -// RunWhileLocked binds the given closure with CallWhileLocked and returns the
|
| -// new Closure. This is for cases where you want to run a task, but you want to
|
| -// ensure that the ProxyLock is acquired for the duration of the task.
|
| -// Example usage:
|
| +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();
|
| + }
|
| + }
|
| +
|
| + 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);
|
| + }
|
| + }
|
| +
|
| + 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);
|
| + }
|
| + }
|
| +
|
| + 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);
|
| + }
|
| + }
|
| +
|
| + 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);
|
| -inline base::Closure RunWhileLocked(const base::Closure& closure) {
|
| - return base::Bind(CallWhileLocked, closure);
|
| +//
|
| +// 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.
|
| +// 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) {
|
| + internal::RunWhileLockedHelper<FunctionType>* helper =
|
| + new internal::RunWhileLockedHelper<FunctionType>(callback);
|
| + return base::Bind(
|
| + &internal::RunWhileLockedHelper<FunctionType>::CallWhileLocked,
|
| + base::Owned(helper));
|
| }
|
|
|
| } // namespace ppapi
|
|
|