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 |