 Chromium Code Reviews
 Chromium Code Reviews Issue 1082113004:
  BindToCurrentLoop should delete callback on original thread 
  Base URL: https://chromium.googlesource.com/chromium/src.git@master
    
  
    Issue 1082113004:
  BindToCurrentLoop should delete callback on original thread 
  Base URL: https://chromium.googlesource.com/chromium/src.git@master| 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 |