OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #ifndef BASE_CALLBACK_LIST_H_ | |
6 #define BASE_CALLBACK_LIST_H_ | |
7 | |
8 #include <algorithm> | |
9 #include <limits> | |
10 #include <vector> | |
11 | |
12 #include "base/basictypes.h" | |
13 #include "base/bind.h" | |
14 #include "base/callback.h" | |
15 #include "base/memory/weak_ptr.h" | |
16 | |
17 /////////////////////////////////////////////////////////////////////////////// | |
18 // | |
19 // OVERVIEW: | |
20 // | |
21 // A container for a list of callbacks. Unlike a normal STL vector or list, | |
22 // this container can be modified during iteration without invalidating the | |
23 // iterator. So, it safely handles the case of an callback removing itself | |
24 // or other callback from the list while callbacks are being run. | |
25 // | |
26 // TYPICAL USAGE: | |
27 // | |
28 // class MyWidget { | |
29 // public: | |
30 // ... | |
31 // | |
32 // typedef base::Callback<void(const Foo&)> OnFooCallback; | |
33 // | |
34 // base::Closure RegisterCallback(const OnFooCallback& cb) { | |
35 // return callback_list_.RegisterCallback(cb); | |
36 // } | |
37 // | |
38 // private: | |
39 // void NotifyFoo(const Foo& foo) { | |
40 // callback_list_.Run(foo); | |
41 // } | |
42 // | |
43 // CallbackList<Foo> callback_list_; | |
44 // }; | |
45 // | |
46 // | |
47 // class MyWidgetListener { | |
48 // public: | |
49 // | |
50 // MyWidgetListener::MyWidgetListener() { | |
51 // remove_foo_callback_ = MyWidget::GetCurrent()->RegisterCallback( | |
52 // base::Bind(&MyWidgetListener::OnFoo, this)); | |
53 // } | |
54 // | |
55 // MyWidgetListener::~MyWidgetListener() { | |
56 // remove_foo_callback_.Run(); | |
57 // } | |
58 // | |
59 // void OnFoo(const Foo& foo) { | |
60 // // Do something. | |
61 // } | |
62 // | |
63 // private: | |
64 // base::Closure remove_foo_callback_; | |
65 // }; | |
66 // | |
67 /////////////////////////////////////////////////////////////////////////////// | |
68 | |
69 // TODO(caitkp): Move to .cc | |
70 class CallbackListBase { | |
awong
2013/08/27 21:23:29
The declaration will have to be public because Cal
Cait (Slow)
2013/08/28 21:50:21
Done.
| |
71 public: | |
72 // Enumeration of which callbacks are called. | |
73 enum NotificationType { | |
74 // Specifies that any callbacks added during notification are called. | |
75 // This is the default type if no type is provided to the constructor. | |
76 NOTIFY_ALL, | |
77 | |
78 // Specifies that callbacks added while sending out notifications are not | |
79 // called. | |
80 NOTIFY_EXISTING_ONLY | |
81 }; | |
82 | |
83 CallbackListBase() | |
84 : active_iterator_count_(0), | |
85 type_(NOTIFY_ALL), | |
86 weak_factory_(this) {} | |
87 | |
88 explicit CallbackListBase(NotificationType type) | |
89 : active_iterator_count_(0), | |
90 type_(type), | |
91 weak_factory_(this) {} | |
92 | |
93 virtual ~CallbackListBase() { | |
94 Compact(); | |
95 DCHECK_EQ(0U, size()); | |
96 Clear(); | |
97 } | |
98 | |
99 void Clear() { | |
100 if (active_iterator_count_) { | |
101 for (size_t i = 0; i < callbacks_.size(); ++i) { | |
102 callbacks_[i]->Reset(); | |
awong
2013/08/27 21:23:29
Call delete before setting to NULL?
BTW, do we ne
| |
103 callbacks_[i] = NULL; | |
104 } | |
105 } else { | |
106 callbacks_.clear(); | |
107 } | |
108 } | |
109 | |
110 bool might_have_callbacks() const { | |
111 return size() != 0; | |
112 } | |
113 | |
114 protected: | |
awong
2013/08/27 21:23:29
If we put this in the base::internal namespace, it
Cait (Slow)
2013/08/28 21:50:21
Seems like we'd want to keep Clear() and might_hav
| |
115 typedef std::vector<base::internal::CallbackBase*> ListType; | |
Avi (use Gerrit)
2013/08/28 15:38:58
Why CallbackBase and not Callback? It's not obviou
| |
116 | |
117 // An iterator class that can be used to access the list of callbacks. | |
118 class Iterator { | |
119 public: | |
120 Iterator(CallbackListBase* list) | |
121 : list_(list->GetWeakPtr()), | |
122 index_(0), | |
123 max_index_(list->type_ == NOTIFY_ALL ? | |
124 std::numeric_limits<size_t>::max() : | |
125 list->callbacks_.size()) { | |
126 ++list_->active_iterator_count_; | |
127 } | |
128 | |
129 ~Iterator() { | |
130 if (list_.get() && --list_->active_iterator_count_ == 0) | |
awong
2013/08/27 21:23:29
You should be able to do
if (list_)
here and else
Cait (Slow)
2013/08/28 21:50:21
Done.
| |
131 list_->Compact(); | |
132 } | |
133 | |
134 base::internal::CallbackBase* GetNext() { | |
135 if (!list_.get()) | |
136 return NULL; | |
137 ListType& callbacks = list_->callbacks_; | |
138 size_t max_index = std::min(max_index_, callbacks.size()); | |
139 while (index_ < max_index && !callbacks[index_]) | |
140 ++index_; | |
141 return index_ < max_index ? callbacks[index_++] : | |
awong
2013/08/27 21:23:29
This should fit on one line.
Cait (Slow)
2013/08/28 21:50:21
Done.
| |
142 NULL; | |
143 } | |
144 | |
145 private: | |
146 base::WeakPtr<CallbackListBase> list_; | |
147 size_t index_; | |
148 size_t max_index_; | |
149 }; | |
150 | |
151 // Remove an callback from the list if it is in the list. | |
152 void RemoveCallback(base::internal::CallbackBase* cb) { | |
153 for (size_t i = 0; i < callbacks_.size(); i++) { | |
154 if (callbacks_[i] == cb) { | |
155 if (active_iterator_count_) { | |
156 callbacks_[i]->Reset(); | |
157 callbacks_[i] = NULL; | |
158 } else { | |
159 callbacks_.erase(callbacks_.begin() + i); | |
awong
2013/08/27 21:23:29
How frequently are lists going to be modified in p
Cait (Slow)
2013/08/28 21:50:21
I was wondering that too...
| |
160 } | |
161 return; | |
162 } | |
163 } | |
164 } | |
165 | |
166 base::Closure AddCallback(base::internal::CallbackBase* cb) { | |
awong
2013/08/27 21:23:29
Document that this takes ownership of |cb|
Cait (Slow)
2013/08/28 21:50:21
Done.
| |
167 for (size_t i = 0; i < callbacks_.size(); i++) { | |
168 if (callbacks_[i] == cb) { | |
169 NOTREACHED() << "Callbacks can only be added once!"; | |
170 return base::Bind(&base::DoNothing);; | |
171 } | |
172 } | |
173 callbacks_.push_back(cb); | |
174 return base::Bind(&CallbackListBase::RemoveCallback, GetWeakPtr(), cb); | |
175 } | |
176 | |
177 bool HasCallback(base::internal::CallbackBase* cb) const { | |
awong
2013/08/27 21:23:29
I don't think this is used.
Cait (Slow)
2013/08/28 21:50:21
Done.
| |
178 for (size_t i = 0; i < callbacks_.size(); ++i) { | |
179 if (callbacks_[i] == cb) | |
180 return true; | |
181 } | |
182 return false; | |
183 } | |
184 | |
185 base::WeakPtr<CallbackListBase> GetWeakPtr() { | |
186 return weak_factory_.GetWeakPtr(); | |
187 } | |
188 | |
189 void Compact() { | |
190 callbacks_.erase( | |
191 std::remove(callbacks_.begin(), callbacks_.end(), | |
192 static_cast<base::internal::CallbackBase*>(NULL)), | |
193 callbacks_.end()); | |
194 } | |
195 | |
196 size_t size() const { return callbacks_.size(); } | |
197 | |
198 private: | |
199 | |
awong
2013/08/27 21:23:29
spurious newline.
Cait (Slow)
2013/08/28 21:50:21
Done.
| |
200 ListType callbacks_; | |
201 int active_iterator_count_; | |
202 NotificationType type_; | |
203 base::WeakPtrFactory<CallbackListBase> weak_factory_; | |
204 | |
205 DISALLOW_COPY_AND_ASSIGN(CallbackListBase); | |
206 }; | |
207 | |
208 | |
209 template <typename Detail> | |
210 class CallbackListWithDetails : public CallbackListBase { | |
211 public: | |
212 typedef base::Callback<void(const Detail&)> CallbackType; | |
213 typedef CallbackListBase::NotificationType NotificationType; | |
214 | |
awong
2013/08/27 21:23:29
spurious newline.
Cait (Slow)
2013/08/28 21:50:21
Done.
| |
215 | |
216 CallbackListWithDetails() {} | |
awong
2013/08/27 21:23:29
Do we need a default constructor?
Few APIs are ni
Cait (Slow)
2013/08/28 21:50:21
Done.
| |
217 | |
218 explicit CallbackListWithDetails(NotificationType type) | |
219 : CallbackListBase(type) {} | |
220 | |
221 virtual ~CallbackListWithDetails() { | |
222 } | |
223 | |
224 // Add a callback to the list. A callback should not be added to | |
225 // the same list more than once. The returned closure (used to remove the | |
226 // callback from the list) is guaranteed to be safe to run. | |
227 base::Closure RegisterCallback( | |
228 const base::Callback<void(const Detail&)>& cb) { | |
awong
2013/08/27 21:23:29
Make this "const CallbackType&" as well? Alternat
Cait (Slow)
2013/08/28 21:50:21
Done.
| |
229 DCHECK(!cb.is_null()); | |
230 // TODO(caitkp): This is a leak. | |
awong
2013/08/27 21:23:29
Can't the CallbackListBase<> just delete the point
Cait (Slow)
2013/08/27 22:14:20
In the current impl, CallbackListBase keeps a vect
awong
2013/08/28 02:02:55
Geez. What paranoid idiot made that protected...oh
| |
231 return AddCallback(new CallbackType(cb)); | |
232 } | |
233 | |
234 void Run(const Detail& details) { | |
235 if (might_have_callbacks()) { | |
236 CallbackListBase::Iterator it(this); | |
237 CallbackType* cb; | |
238 while((cb = static_cast<CallbackType*>(it.GetNext())) != NULL) { | |
239 cb->Run(details); | |
240 } | |
241 } | |
242 } | |
243 | |
244 DISALLOW_COPY_AND_ASSIGN(CallbackListWithDetails); | |
awong
2013/08/27 21:23:29
needs a private:
Cait (Slow)
2013/08/28 21:50:21
Done.
| |
245 }; | |
246 | |
247 class CallbackList : public CallbackListBase { | |
248 public: | |
249 typedef base::Closure CallbackType; | |
250 typedef CallbackListBase::NotificationType NotificationType; | |
251 | |
252 | |
awong
2013/08/27 21:23:29
spurious newline
Cait (Slow)
2013/08/28 21:50:21
Done.
| |
253 CallbackList() {} | |
254 | |
255 explicit CallbackList(NotificationType type) | |
256 : CallbackListBase(type) {} | |
257 | |
258 virtual ~CallbackList() { | |
259 } | |
260 | |
261 // Add a callback to the list. A callback should not be added to | |
262 // the same list more than once. The returned closure (used to remove the | |
263 // callback from the list) is guaranteed to be safe to run. | |
264 base::Closure RegisterCallback(const base::Closure& cb) { | |
awong
2013/08/27 21:23:29
One more nit: maybe just call this Add()? That be
Cait (Slow)
2013/08/27 22:14:20
My thinking for the "remove callback" approach was
awong
2013/08/28 02:02:55
My main concerns are:
(1) Binding a WeakPtr<> all
| |
265 DCHECK(!cb.is_null()); | |
266 // TODO(caitkp): This is a leak. | |
267 return AddCallback(new CallbackType(cb)); | |
268 } | |
269 | |
270 void Run() { | |
271 if (might_have_callbacks()) { | |
272 CallbackListBase::Iterator it(this); | |
273 CallbackType* cb; | |
274 while((cb = static_cast<CallbackType*>(it.GetNext())) != NULL) { | |
275 cb->Run(); | |
276 } | |
277 } | |
278 } | |
279 | |
280 DISALLOW_COPY_AND_ASSIGN(CallbackList); | |
281 }; | |
282 | |
283 #endif // BASE_CALLBACK_LIST_H_ | |
OLD | NEW |