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

Side by Side Diff: base/callback_registry.h

Issue 22877038: Add a CallbackRegistry class to base/ to manage callbacks (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: 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
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_REGISTRY_H_
6 #define BASE_CALLBACK_REGISTRY_H_
7
8 #include <list>
9
10 #include "base/basictypes.h"
11 #include "base/callback.h"
12 #include "base/compiler_specific.h"
13 #include "base/logging.h"
14 #include "base/memory/scoped_ptr.h"
15
16 // OVERVIEW:
17 //
18 // A container for a list of callbacks. Unlike a normal STL vector or list,
19 // this container can be modified during iteration without invalidating the
20 // iterator. It safely handles the case of a callback removing itself
21 // or another callback from the list while callbacks are being run.
22 //
23 // TYPICAL USAGE:
24 //
25 // class MyWidget {
26 // public:
27 // ...
28 //
29 // typedef base::Callback<void(const Foo&)> OnFooCallback;
30 //
31 // scoped_ptr<base::CallbackRegistry<Foo>::Subscription> RegisterCallback(
32 // const OnFooCallback& cb) {
33 // return callback_registry_.Add(cb);
34 // }
35 //
36 // private:
37 // void NotifyFoo(const Foo& foo) {
38 // callback_registry_.Notify(foo);
39 // }
40 //
41 // base::CallbackRegistry<Foo> callback_registry_;
42 // };
43 //
44 //
45 // class MyWidgetListener {
46 // public:
47 // MyWidgetListener::MyWidgetListener() {
48 // foo_subscription_ = MyWidget::GetCurrent()->RegisterCallback(
49 // base::Bind(&MyWidgetListener::OnFoo, this)));
50 // }
51 //
52 // MyWidgetListener::~MyWidgetListener() {
53 // // Subscription gets deleted automatically and will deregister
54 // // the callback in the process.
55 // }
56 //
57 // void OnFoo(const Foo& foo) {
58 // // Do something.
59 // }
60 //
61 // private:
62 // scoped_ptr<base::CallbackRegistry<Foo>::Subscription> foo_subscription_;
63 // };
64
65 namespace base {
66
67 namespace internal {
68 // Holds the methods shared by all specializations of CallbackRegistry to avoid
69 // code duplication.
70 template <typename CallbackType>
71 class CallbackRegistryBase {
72 public:
73 class Subscription {
74 public:
75 Subscription(CallbackRegistryBase<CallbackType>* list,
76 typename std::list<CallbackType>::iterator iter)
77 : list_(list),
78 iter_(iter) {}
79
80 ~Subscription() {
81 if (list_->active_iterator_count_)
82 (*iter_).Reset();
83 else
84 list_->callbacks_.erase(iter_);
85 }
86
87 private:
88 CallbackRegistryBase<CallbackType>* list_;
89 typename std::list<CallbackType>::iterator iter_;
90
91 DISALLOW_COPY_AND_ASSIGN(Subscription);
92 };
93
94 // Add a callback to the list. The callback will remain registered until the
95 // returned Subscription is destroyed, which must occur before the
96 // CallbackRegistry is destroyed.
97 scoped_ptr<Subscription> Add(const CallbackType& cb) WARN_UNUSED_RESULT {
awong 2013/09/09 20:04:55 Let's whack this WARN_UNUSED_RESULT as well. The s
Cait (Slow) 2013/09/09 20:33:03 Done.
98 DCHECK(!cb.is_null());
99 typename std::list<CallbackType>::iterator it =
100 callbacks_.insert(callbacks_.end(), cb);
101 return scoped_ptr<Subscription>(new Subscription(this, it));
awong 2013/09/09 20:04:55 FYI, you could also do return make_scoped_ptr(new
102 }
103
104 protected:
105 // An iterator class that can be used to access the list of callbacks.
106 class Iterator {
107 public:
108 explicit Iterator(CallbackRegistryBase<CallbackType>* list)
109 : list_(list),
110 list_iter_(list_->callbacks_.begin()) {
111 ++list_->active_iterator_count_;
112 }
113
114 Iterator(const Iterator& iter)
115 : list_(iter.list_),
116 list_iter_(iter.list_iter_) {
117 ++list_->active_iterator_count_;
118 }
119
120 ~Iterator() {
121 if (list_ && --list_->active_iterator_count_ == 0) {
122 list_->Compact();
123 }
124 }
125
126 CallbackType* GetNext() {
127 while ((list_iter_ != list_->callbacks_.end()) && list_iter_->is_null())
128 list_iter_++;
129
130 CallbackType* cb =
131 list_iter_ != list_->callbacks_.end() ? &(*list_iter_) : NULL;
132 list_iter_++;
133 return cb;
134 }
135
136 private:
137 CallbackRegistryBase<CallbackType>* list_;
138 typename std::list<CallbackType>::iterator list_iter_;
139 };
140
141 CallbackRegistryBase()
142 : active_iterator_count_(0) {}
143
144 ~CallbackRegistryBase() {
145 DCHECK(active_iterator_count_ == 0);
awong 2013/09/09 20:04:55 DCHECK_EQ?
Cait (Slow) 2013/09/09 20:33:03 Done.
146 DCHECK(callbacks_.size() == 0);
147 }
148
149 // Returns an instance of a CallbackRegistryBase::Iterator which can be used
150 // to run callbacks.
151 Iterator GetIterator() {
152 return Iterator(this);
153 }
154
155 // Compact the list: remove any entries which were NULLed out during
156 // iteration.
157 void Compact() {
158 typename std::list<CallbackType>::iterator it = callbacks_.begin();
159 while (it != callbacks_.end()) {
160 if ((*it).is_null())
161 it = callbacks_.erase(it);
162 else
163 ++it;
164 }
165 }
166
167 private:
168
169 std::list<CallbackType> callbacks_;
170 int active_iterator_count_;
171
172 DISALLOW_COPY_AND_ASSIGN(CallbackRegistryBase);
173 };
174
175 } // namespace internal
176
177 template <typename Details>
178 class CallbackRegistry
179 : public internal::CallbackRegistryBase<Callback<void(const Details&)> > {
180 public:
181 typedef typename internal::CallbackRegistryBase<
182 Callback<void(const Details&)> >::Subscription Subscription;
awong 2013/09/09 20:04:55 Is this needed? I just did a small test case, and
Cait (Slow) 2013/09/09 20:33:03 Done.
183 CallbackRegistry() {}
184
185 // Execute all active callbacks with |details| parameter.
186 void Notify(const Details& details) {
187 typename internal::CallbackRegistryBase<
188 Callback<void(const Details&)> >::Iterator it = this->GetIterator();
189 Callback<void(const Details&)>* cb;
190 while((cb = it.GetNext()) != NULL) {
191 cb->Run(details);
192 }
193 }
194
195 private:
196 DISALLOW_COPY_AND_ASSIGN(CallbackRegistry);
197 };
198
199 template <> class CallbackRegistry<void>
200 : public internal::CallbackRegistryBase<Closure> {
201 public:
202 CallbackRegistry() {}
203
204 // Execute all active callbacks.
205 void Notify() {
206 Iterator it = this->GetIterator();
207 Closure* cb;
208 while((cb = it.GetNext()) != NULL) {
209 cb->Run();
210 }
211 }
212
213 private:
214 DISALLOW_COPY_AND_ASSIGN(CallbackRegistry);
215 };
216
217 } // namespace base
218
219 #endif // BASE_CALLBACK_REGISTRY_H_
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698