Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(466)

Side by Side Diff: base/callback_list.h

Issue 22877038: Add a CallbackRegistry class to base/ to manage callbacks (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Use CallbackListBase Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « base/base.gypi ('k') | base/callback_list_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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_
OLDNEW
« no previous file with comments | « base/base.gypi ('k') | base/callback_list_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698