Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2015 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #ifndef MEDIA_BASE_BIND_TO_CURRENT_LOOP_H_ | 5 #ifndef MEDIA_BASE_BIND_TO_CURRENT_LOOP_H_ |
| 6 #define MEDIA_BASE_BIND_TO_CURRENT_LOOP_H_ | 6 #define MEDIA_BASE_BIND_TO_CURRENT_LOOP_H_ |
| 7 | 7 |
| 8 #include "base/bind.h" | 8 #include "base/bind.h" |
| 9 #include "base/location.h" | 9 #include "base/location.h" |
| 10 #include "base/memory/scoped_ptr.h" | |
| 11 #include "base/message_loop/message_loop.h" | |
| 10 #include "base/message_loop/message_loop_proxy.h" | 12 #include "base/message_loop/message_loop_proxy.h" |
| 11 #include "base/single_thread_task_runner.h" | 13 #include "base/single_thread_task_runner.h" |
| 14 #include "media/base/media_export.h" | |
| 12 | 15 |
| 13 // This is a helper utility for base::Bind()ing callbacks to the current | 16 // This is a helper utility for base::Bind()ing callbacks to the current |
| 14 // MessageLoop. The typical use is when |a| (of class |A|) wants to hand a | 17 // MessageLoop. The typical use is when |a| (of class |A|) wants to hand a |
| 15 // callback such as base::Bind(&A::AMethod, a) to |b|, but needs to ensure that | 18 // callback such as base::Bind(&A::AMethod, a) to |b|, but needs to ensure that |
| 16 // when |b| executes the callback, it does so on |a|'s current MessageLoop. | 19 // when |b| executes the callback, it does so on |a|'s current MessageLoop. |
| 17 // | 20 // |
| 18 // Typical usage: request to be called back on the current thread: | 21 // Typical usage: request to be called back on the current thread: |
| 19 // other->StartAsyncProcessAndCallMeBack( | 22 // other->StartAsyncProcessAndCallMeBack( |
| 20 // media::BindToCurrentLoop(base::Bind(&MyClass::MyMethod, this))); | 23 // 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.
| |
| 21 // | 24 // |
| 22 // Note that like base::Bind(), BindToCurrentLoop() can't bind non-constant | 25 // Note that like base::Bind(), BindToCurrentLoop() can't bind non-constant |
| 23 // references, and that *unlike* base::Bind(), BindToCurrentLoop() makes copies | 26 // references, and that *unlike* base::Bind(), BindToCurrentLoop() makes copies |
| 24 // of its arguments, and thus can't be used with arrays. | 27 // of its arguments, and thus can't be used with arrays. |
| 28 // | |
| 29 // The callback passed in to BindToCurrentLoop is guaranteed to be deleted on | |
| 30 // the thread from which BindToCurrentLoop was invoked. This allows objects that | |
| 31 // must be deleted on the originating thread to be bound into it. In particular, | |
| 32 // it can be useful to use WeakPtr<> in the callback so that the reply operation | |
| 33 // can be canceled. | |
| 25 | 34 |
| 26 namespace media { | 35 namespace media { |
| 27 | 36 |
| 37 namespace internal { | |
| 38 | |
| 28 // Mimic base::internal::CallbackForward, replacing p.Pass() with | 39 // Mimic base::internal::CallbackForward, replacing p.Pass() with |
| 29 // base::Passed(&p) to account for the extra layer of indirection. | 40 // base::Passed(&p) to account for the extra layer of indirection. |
| 30 namespace internal { | |
| 31 template <typename T> | 41 template <typename T> |
| 32 T& TrampolineForward(T& t) { return t; } | 42 T& TrampolineForward(T& t) { return t; } |
| 33 | 43 |
| 34 template <typename T, typename R> | 44 template <typename T, typename R> |
| 35 base::internal::PassedWrapper<scoped_ptr<T, R> > TrampolineForward( | 45 base::internal::PassedWrapper<scoped_ptr<T, R> > TrampolineForward( |
| 36 scoped_ptr<T, R>& p) { return base::Passed(&p); } | 46 scoped_ptr<T, R>& p) { return base::Passed(&p); } |
| 37 | 47 |
| 38 template <typename T> | 48 template <typename T> |
| 39 base::internal::PassedWrapper<ScopedVector<T> > TrampolineForward( | 49 base::internal::PassedWrapper<ScopedVector<T> > TrampolineForward( |
| 40 ScopedVector<T>& p) { return base::Passed(&p); } | 50 ScopedVector<T>& p) { return base::Passed(&p); } |
| 41 | 51 |
| 42 // First, tell the compiler TrampolineHelper is a struct template with one | 52 } // namespace internal |
| 43 // type parameter. Then define specializations where the type is a function | |
| 44 // returning void and taking zero or more arguments. | |
| 45 template <typename Sig> struct TrampolineHelper; | |
| 46 | 53 |
| 47 template <typename... Args> | 54 // Function object which deletes its parameter, which must be a pointer, |
| 48 struct TrampolineHelper<void(Args...)> { | 55 // on the task runner on which this object was constructed. |
| 49 static void Run( | 56 // |
| 50 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, | 57 // This is more expensive than content::BrowserThread::DeleteOnUIThread etc; |
| 51 const base::Callback<void(Args...)>& cb, | 58 // use those instead when possible. |
| 52 Args... args) { | 59 // |
| 53 task_runner->PostTask(FROM_HERE, | 60 // Sample usage with RefCountedThreadSafe: |
| 54 base::Bind(cb, TrampolineForward(args)...)); | 61 // class Foo : public base::RefCountedThreadSafe< |
| 62 // Foo, media::base::DeleteOnCurrentLoop> { | |
| 63 // ... | |
| 64 // private: | |
| 65 // friend struct media::base::DeleteOnCurrentLoop; | |
| 66 // friend class base::DeleteHelper<Foo>; | |
| 67 // ~Foo(); | |
| 68 // } | |
| 69 // | |
| 70 // Sample usage with scoped_ptr: | |
| 71 // 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).
| |
| 72 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
| |
| 73 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.
| |
| 74 ~DeleteOnCurrentLoop(); | |
| 75 | |
| 76 template <typename T> | |
| 77 void operator()(T* ptr) const { | |
| 78 enum { type_must_be_complete = sizeof(T) }; | |
| 79 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
| |
| 80 delete ptr; | |
| 81 } else { | |
| 82 if (!origin_loop_->DeleteSoon(FROM_HERE, ptr)) { | |
| 83 #if defined(UNIT_TEST) | |
| 84 // Only logged under unit testing because leaks at shutdown | |
| 85 // are acceptable under normal circumstances. | |
| 86 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
| |
| 87 #endif // UNIT_TEST | |
| 88 } | |
| 89 } | |
| 55 } | 90 } |
| 91 | |
| 92 private: | |
| 93 scoped_refptr<base::SingleThreadTaskRunner> origin_loop_; | |
| 56 }; | 94 }; |
| 57 | 95 |
| 58 } // namespace internal | 96 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
| |
| 59 | 97 |
| 60 template<typename T> | 98 template<typename... A> |
| 61 static base::Callback<T> BindToCurrentLoop( | 99 void PostBackToOriginLoop( |
| 62 const base::Callback<T>& cb) { | 100 const scoped_refptr<base::SingleThreadTaskRunner>& origin_task_runner, |
| 63 return base::Bind(&internal::TrampolineHelper<T>::Run, | 101 scoped_ptr<base::Callback<void(A...)>, |
| 64 base::MessageLoopProxy::current(), cb); | 102 DeleteOnCurrentLoop>* origin_cb_ptr_ptr, |
| 103 A... args) { | |
| 104 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
| |
| 105 FROM_HERE, base::Bind(**origin_cb_ptr_ptr, | |
| 106 internal::TrampolineForward(args)...)); | |
| 107 } | |
| 108 | |
| 109 } // namespace | |
| 110 | |
| 111 template<typename... A> | |
| 112 static base::Callback<void(A...)> BindToCurrentLoop( | |
| 113 const base::Callback<void(A...)>& origin_cb) { | |
| 114 auto cb_copy_ptr = new base::Callback<void(A...)>(origin_cb); | |
| 115 // Normally it's pointless to create a scoped_ptr on the heap; here we're just | |
| 116 // (ab)using it to take advantage of its custom deleter support. | |
| 117 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
| |
| 118 DeleteOnCurrentLoop>(cb_copy_ptr); | |
| 119 return base::Bind(&PostBackToOriginLoop<A...>, | |
| 120 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).
| |
| 121 base::Owned(cb_copy_ptr_ptr)); | |
| 65 } | 122 } |
| 66 | 123 |
| 67 } // namespace media | 124 } // namespace media |
| 68 | 125 |
| 69 #endif // MEDIA_BASE_BIND_TO_CURRENT_LOOP_H_ | 126 #endif // MEDIA_BASE_BIND_TO_CURRENT_LOOP_H_ |
| OLD | NEW |