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..5a57776b1b25ade80e0cab5531dc5e2805e92f0c 100644 |
| --- a/media/base/bind_to_current_loop.h |
| +++ b/media/base/bind_to_current_loop.h |
| @@ -7,8 +7,11 @@ |
| #include "base/bind.h" |
| #include "base/location.h" |
| +#include "base/memory/scoped_ptr.h" |
| +#include "base/message_loop/message_loop.h" |
| #include "base/message_loop/message_loop_proxy.h" |
| #include "base/single_thread_task_runner.h" |
| +#include "media/base/media_export.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 |
| @@ -17,17 +20,24 @@ |
| // |
| // Typical usage: request to be called back on the current thread: |
| // other->StartAsyncProcessAndCallMeBack( |
| -// media::BindToCurrentLoop(base::Bind(&MyClass::MyMethod, this))); |
| +// media::base::BindToCurrentLoop(base::Bind(&MyClass::MyMethod, this))); |
|
danakj
2015/04/29 23:35:01
no base::?
johnme
2015/04/30 16:15:53
Done.
|
| // |
| // 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 +49,76 @@ 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; |
| - |
| -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)...)); |
| +} // namespace internal |
| + |
| +// Function object which deletes its parameter, which must be a pointer, |
| +// on the task runner on which this object was constructed. |
| +// |
| +// This is more expensive than content::BrowserThread::DeleteOnUIThread etc; |
| +// use those instead when possible. |
| +// |
| +// Sample usage with RefCountedThreadSafe: |
| +// class Foo : public base::RefCountedThreadSafe< |
| +// Foo, media::base::DeleteOnCurrentLoop> { |
| +// ... |
| +// private: |
| +// friend struct media::base::DeleteOnCurrentLoop; |
| +// friend class base::DeleteHelper<Foo>; |
| +// ~Foo(); |
| +// } |
| +// |
| +// Sample usage with scoped_ptr: |
| +// scoped_ptr<Foo, media::base::DeleteOnCurrentLoop> ptr; |
|
danakj
2015/04/29 23:35:01
no base::?
johnme
2015/04/30 16:15:53
Done (ditto above).
|
| +struct MEDIA_EXPORT DeleteOnCurrentLoop { |
|
danakj
2015/04/29 23:35:01
should this be inside anon/internal?
johnme
2015/04/30 16:15:53
I've moved DeleteOnLoop into internal (a subclass
|
| + DeleteOnCurrentLoop(); |
|
danakj
2015/04/29 23:35:01
i prefer this take a STTR* as an argument instead
johnme
2015/04/30 16:15:53
Done.
|
| + ~DeleteOnCurrentLoop(); |
| + |
| + template <typename T> |
| + void operator()(T* ptr) const { |
| + enum { type_must_be_complete = sizeof(T) }; |
| + if (origin_loop_->BelongsToCurrentThread()) { |
|
danakj
2015/04/29 23:37:34
oh and no conditional posting please
johnme
2015/04/30 16:15:53
I modeled this on BrowserThread::DeleteOnThread, w
|
| + delete ptr; |
| + } else { |
| + if (!origin_loop_->DeleteSoon(FROM_HERE, ptr)) { |
| +#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/29 23:35:01
I'd still prefer LOG(FATAL) and maybe a way to tur
johnme
2015/04/30 16:15:53
Done (hmm ok, I re-read https://codereview.chromiu
|
| +#endif // UNIT_TEST |
| + } |
| + } |
| } |
| + |
| + private: |
| + scoped_refptr<base::SingleThreadTaskRunner> origin_loop_; |
| }; |
| -} // namespace internal |
| +namespace { |
|
danakj
2015/04/29 23:35:01
why is this anon but the above is internal?
johnme
2015/04/30 16:15:53
Done (oops, shouldn't use anonymous namespaces in
|
| + |
| +template<typename... A> |
| +void PostBackToOriginLoop( |
| + const scoped_refptr<base::SingleThreadTaskRunner>& origin_task_runner, |
| + scoped_ptr<base::Callback<void(A...)>, |
| + DeleteOnCurrentLoop>* origin_cb_ptr_ptr, |
| + A... args) { |
| + origin_task_runner->PostTask( |
|
danakj
2015/04/29 23:35:01
I think that I prefer posting to our own method on
johnme
2015/04/30 16:15:53
We'd still need the custom deleter in case it does
|
| + FROM_HERE, base::Bind(**origin_cb_ptr_ptr, |
| + internal::TrampolineForward(args)...)); |
| +} |
| + |
| +} // 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... A> |
| +static base::Callback<void(A...)> BindToCurrentLoop( |
| + const base::Callback<void(A...)>& origin_cb) { |
| + auto cb_copy_ptr = new base::Callback<void(A...)>(origin_cb); |
| + // Normally it's pointless to create a scoped_ptr on the heap; here we're just |
| + // (ab)using it to take advantage of its custom deleter support. |
| + auto cb_copy_ptr_ptr = new scoped_ptr<base::Callback<void(A...)>, |
|
danakj
2015/04/29 23:35:01
can we do this with a scoped_ptr Passed instead of
johnme
2015/04/30 16:15:53
Only allowing the callback to be called once break
|
| + DeleteOnCurrentLoop>(cb_copy_ptr); |
| + return base::Bind(&PostBackToOriginLoop<A...>, |
| + base::MessageLoop::current()->task_runner(), |
|
danakj
2015/04/29 23:35:01
ThreadTaskRunnerHandler::Get()?
johnme
2015/04/30 16:15:53
Done (and in .cc).
|
| + base::Owned(cb_copy_ptr_ptr)); |
| } |
| } // namespace media |