Chromium Code Reviews| Index: media/base/bind_to_current_loop.h |
| diff --git a/media/base/bind_to_current_loop.h b/media/base/bind_to_current_loop.h |
| index cb3e28d4839641f3745684fc7757f87c3aa4cba9..e8320fc0ba86b031a6b21117529f4ec6ac231e72 100644 |
| --- a/media/base/bind_to_current_loop.h |
| +++ b/media/base/bind_to_current_loop.h |
| @@ -7,8 +7,10 @@ |
| #include "base/bind.h" |
| #include "base/location.h" |
| +#include "base/memory/scoped_ptr.h" |
| #include "base/message_loop/message_loop_proxy.h" |
| #include "base/single_thread_task_runner.h" |
| +#include "base/thread_task_runner_handle.h" |
| // This is a helper utility for base::Bind()ing callbacks to the current |
| // MessageLoop. The typical use is when |a| (of class |A|) wants to hand a |
| @@ -22,12 +24,19 @@ |
| // Note that like base::Bind(), BindToCurrentLoop() can't bind non-constant |
| // references, and that *unlike* base::Bind(), BindToCurrentLoop() makes copies |
| // of its arguments, and thus can't be used with arrays. |
| +// |
| +// The callback passed in to BindToCurrentLoop is guaranteed to be deleted on |
| +// the thread from which BindToCurrentLoop was invoked. This allows objects that |
| +// must be deleted on the originating thread to be bound into it. In particular, |
| +// it can be useful to use WeakPtr<> in the callback so that the reply operation |
| +// can be canceled. |
| namespace media { |
| +namespace internal { |
| + |
| // Mimic base::internal::CallbackForward, replacing p.Pass() with |
| // base::Passed(&p) to account for the extra layer of indirection. |
| -namespace internal { |
| template <typename T> |
| T& TrampolineForward(T& t) { return t; } |
| @@ -39,29 +48,56 @@ template <typename T> |
| base::internal::PassedWrapper<ScopedVector<T> > TrampolineForward( |
| ScopedVector<T>& p) { return base::Passed(&p); } |
| -// First, tell the compiler TrampolineHelper is a struct template with one |
| -// type parameter. Then define specializations where the type is a function |
| -// returning void and taking zero or more arguments. |
| -template <typename Sig> struct TrampolineHelper; |
| +} // namespace internal |
| + |
| +namespace { |
| template <typename... Args> |
| -struct TrampolineHelper<void(Args...)> { |
| - static void Run( |
| - const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, |
| - const base::Callback<void(Args...)>& cb, |
| - Args... args) { |
| - task_runner->PostTask(FROM_HERE, |
| - base::Bind(cb, TrampolineForward(args)...)); |
| +class BindToCurrentLoopRelay { |
| + public: |
| + BindToCurrentLoopRelay(const base::Callback<void(Args...)>& cb) |
| + // TODO(johnme): Caller of BindToCurrentLoop should pass in FROM_HERE. |
| + : from_here_(FROM_HERE), |
| + origin_loop_(base::ThreadTaskRunnerHandle::Get()), |
| + cb_ptr_(new base::Callback<void(Args...)>(cb)) {} |
| + |
| + ~BindToCurrentLoopRelay() { |
| + if (origin_loop_->BelongsToCurrentThread()) { |
| + delete cb_ptr_; |
| + } else if (!origin_loop_->DeleteSoon(from_here_, cb_ptr_)) { |
|
danakj
2015/04/24 19:00:03
I think you want FROM_HERE here? Or why do you use
johnme
2015/04/27 18:32:33
Ok, I've changed both the |from_here_|s to FROM_HE
|
| + // If DeleteSoon is not possible because the originating MessageLoop is no |
| + // longer available, the Callback is leaked. Leaking is considered |
| + // preferable to having thread-safety violations caused by invoking the |
| + // Callback destructor on the wrong thread. |
| +#if defined(UNIT_TEST) |
| + // Only logged under unit testing because leaks at shutdown |
| + // are acceptable under normal circumstances. |
| + LOG(ERROR) << "DeleteSoon failed"; |
|
danakj
2015/04/24 19:00:03
FATAL?
johnme
2015/04/27 18:32:33
I copied this from https://codereview.chromium.org
|
| +#endif // UNIT_TEST |
| + } |
| + } |
| + |
| + void Run(Args... args) { |
| + // Ignore return value; nothing we can do if PostTask fails. |
| + origin_loop_->PostTask( |
| + from_here_, base::Bind(*cb_ptr_, internal::TrampolineForward(args)...)); |
|
danakj
2015/04/24 19:00:03
Would it be simpler to do this as follows?
templa
johnme
2015/04/27 18:32:33
If you delete the callback returned from BindToCur
|
| } |
| + |
| + private: |
| + tracked_objects::Location from_here_; |
| + scoped_refptr<base::SingleThreadTaskRunner> origin_loop_; |
| + base::Callback<void(Args...)>* cb_ptr_; // Owned. |
| }; |
| -} // namespace internal |
| +} // namespace |
| -template<typename T> |
| -static base::Callback<T> BindToCurrentLoop( |
| - const base::Callback<T>& cb) { |
| - return base::Bind(&internal::TrampolineHelper<T>::Run, |
| - base::MessageLoopProxy::current(), cb); |
| +template<typename... Args> |
| +static base::Callback<void(Args...)> BindToCurrentLoop( |
| + const base::Callback<void(Args...)>& cb) { |
| + BindToCurrentLoopRelay<Args...>* relay = |
| + new BindToCurrentLoopRelay<Args...>(cb); |
| + return base::Bind(&BindToCurrentLoopRelay<Args...>::Run, |
| + base::Owned(relay)); |
| } |
| } // namespace media |