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 8c14e9e88cd40ad8cec0098f71f2cb53a58b56c2..84a2d7019fb23136636b577661926733fb729929 100644 |
--- a/media/base/bind_to_current_loop.h |
+++ b/media/base/bind_to_current_loop.h |
@@ -7,10 +7,12 @@ |
#include "base/bind.h" |
#include "base/location.h" |
+#include "base/memory/ref_counted.h" |
#include "base/memory/scoped_ptr.h" |
#include "base/memory/scoped_vector.h" |
#include "base/single_thread_task_runner.h" |
#include "base/thread_task_runner_handle.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 |
@@ -24,12 +26,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 std::move(p) with |
// base::Passed(&p) to account for the extra layer of indirection. |
-namespace internal { |
template <typename T> |
T& TrampolineForward(T& t) { return t; } |
@@ -41,29 +50,86 @@ 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)...)); |
+// Deleter suitable for use with scoped_ptr (or as traits for |
+// RefCountedThreadSafe, if a subclass has a no-arguments constructor). |
+struct MEDIA_EXPORT DeleteOnLoop { |
+ DeleteOnLoop(scoped_refptr<base::SingleThreadTaskRunner> loop); |
+ ~DeleteOnLoop(); |
+ |
+ template <typename T> |
+ void Destruct(const T* ptr) const { |
+ if (loop_->BelongsToCurrentThread()) { |
+ delete ptr; |
+ } else { |
+ if (!loop_->DeleteSoon(FROM_HERE, ptr)) { |
+#if defined(UNIT_TEST) |
+ // Only logged under unit testing because leaks at shutdown |
+ // are acceptable under normal circumstances. |
+ LOG(FATAL) << "DeleteSoon failed"; |
+#endif // UNIT_TEST |
+ } |
+ } |
+ } |
+ |
+ template <typename T> |
+ inline void operator()(T* ptr) const { |
+ enum { type_must_be_complete = sizeof(T) }; |
+ Destruct(ptr); |
} |
+ |
+ private: |
+ scoped_refptr<base::SingleThreadTaskRunner> loop_; |
}; |
+template<typename... A> |
+void PostBackToOriginLoop( |
+ const scoped_refptr<base::SingleThreadTaskRunner>& origin_task_runner, |
+ scoped_ptr<base::Callback<void(A...)>, DeleteOnLoop>* origin_cb_ptr_ptr, |
+ A... args) { |
+ origin_task_runner->PostTask( |
+ FROM_HERE, base::Bind(**origin_cb_ptr_ptr, |
+ TrampolineForward(args)...)); |
+} |
+ |
} // namespace internal |
-template<typename T> |
-static base::Callback<T> BindToCurrentLoop( |
- const base::Callback<T>& cb) { |
- return base::Bind(&internal::TrampolineHelper<T>::Run, |
- base::ThreadTaskRunnerHandle::Get(), cb); |
+// 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::DeleteOnCurrentLoop> { |
+// ... |
+// private: |
+// friend struct media::DeleteOnCurrentLoop; |
+// friend class base::DeleteHelper<Foo>; |
+// ~Foo(); |
+// } |
+// |
+// Sample usage with scoped_ptr: |
+// scoped_ptr<Foo, media::DeleteOnCurrentLoop> ptr; |
+struct MEDIA_EXPORT DeleteOnCurrentLoop : internal::DeleteOnLoop { |
+ DeleteOnCurrentLoop(); |
+ ~DeleteOnCurrentLoop(); |
+}; |
+ |
+template<typename... A> |
+static base::Callback<void(A...)> BindToCurrentLoop( |
+ const base::Callback<void(A...)>& origin_cb) { |
+ scoped_refptr<base::SingleThreadTaskRunner> loop = |
+ base::ThreadTaskRunnerHandle::Get(); |
+ 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...)>, internal::DeleteOnLoop>( |
+ cb_copy_ptr, internal::DeleteOnLoop(loop)); |
+ return base::Bind(&internal::PostBackToOriginLoop<A...>, |
+ loop, |
+ base::Owned(cb_copy_ptr_ptr)); |
} |
} // namespace media |