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

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
« no previous file with comments | « base/base.gypi ('k') | base/callback_registry_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_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 // private:
58 // void OnFoo(const Foo& foo) {
erikwright (departed) 2013/09/10 14:07:09 Is it actually correct that this is 'const Foo&' w
awong 2013/09/10 17:06:38 Yep...the specialization of CallbackRegistryBase i
Cait (Slow) 2013/09/10 18:24:39 This is by design. CallbackRegistry<T> expects cal
59 // // Do something.
60 // }
61 //
62 // scoped_ptr<base::CallbackRegistry<Foo>::Subscription> foo_subscription_;
63 // };
64
65 namespace base {
66
67 namespace internal {
68
69 template <typename CallbackType>
70 class CallbackRegistryBase {
71 public:
72 class Subscription {
73 public:
74 Subscription(CallbackRegistryBase<CallbackType>* list,
75 typename std::list<CallbackType>::iterator iter)
76 : list_(list),
77 iter_(iter) {}
78
79 ~Subscription() {
80 if (list_->active_iterator_count_)
81 (*iter_).Reset();
82 else
83 list_->callbacks_.erase(iter_);
84 }
85
86 private:
87 CallbackRegistryBase<CallbackType>* list_;
88 typename std::list<CallbackType>::iterator iter_;
89
90 DISALLOW_COPY_AND_ASSIGN(Subscription);
91 };
92
93 // Add a callback to the list. The callback will remain registered until the
94 // returned Subscription is destroyed, which must occur before the
95 // CallbackRegistry is destroyed.
96 scoped_ptr<Subscription> Add(const CallbackType& cb) {
97 DCHECK(!cb.is_null());
98 typename std::list<CallbackType>::iterator it =
99 callbacks_.insert(callbacks_.end(), cb);
100 return scoped_ptr<Subscription>(new Subscription(this, it));
erikwright (departed) 2013/09/10 14:07:09 nit: I'm all for inlining 'it'.
Cait (Slow) 2013/09/10 18:24:39 Done.
101 }
102
103 protected:
104 // An iterator class that can be used to access the list of callbacks.
105 class Iterator {
106 public:
107 explicit Iterator(CallbackRegistryBase<CallbackType>* list)
108 : list_(list),
109 list_iter_(list_->callbacks_.begin()) {
110 ++list_->active_iterator_count_;
111 }
112
113 Iterator(const Iterator& iter)
114 : list_(iter.list_),
115 list_iter_(iter.list_iter_) {
116 ++list_->active_iterator_count_;
117 }
118
119 ~Iterator() {
120 if (list_ && --list_->active_iterator_count_ == 0) {
121 list_->Compact();
122 }
123 }
124
125 CallbackType* GetNext() {
126 while ((list_iter_ != list_->callbacks_.end()) && list_iter_->is_null())
127 ++list_iter_;
128
129 CallbackType* cb =
130 list_iter_ != list_->callbacks_.end() ? &(*list_iter_) : NULL;
131 ++list_iter_;
132 return cb;
133 }
134
135 private:
136 CallbackRegistryBase<CallbackType>* list_;
137 typename std::list<CallbackType>::iterator list_iter_;
138 };
139
140 CallbackRegistryBase()
141 : active_iterator_count_(0) {}
142
143 ~CallbackRegistryBase() {
144 DCHECK_EQ(0, active_iterator_count_);
145 DCHECK_EQ(0U, callbacks_.size());
146 }
147
148 // Returns an instance of a CallbackRegistryBase::Iterator which can be used
149 // to run callbacks.
150 Iterator GetIterator() {
151 return Iterator(this);
152 }
153
154 // Compact the list: remove any entries which were NULLed out during
155 // iteration.
156 void Compact() {
157 typename std::list<CallbackType>::iterator it = callbacks_.begin();
158 while (it != callbacks_.end()) {
159 if ((*it).is_null())
160 it = callbacks_.erase(it);
161 else
162 ++it;
163 }
164 }
165
166 private:
167 std::list<CallbackType> callbacks_;
168 int active_iterator_count_;
169
170 DISALLOW_COPY_AND_ASSIGN(CallbackRegistryBase);
171 };
172
173 } // namespace internal
174
175 template <typename Details>
176 class CallbackRegistry
177 : public internal::CallbackRegistryBase<Callback<void(const Details&)> > {
178 public:
179 CallbackRegistry() {}
180
181 // Execute all active callbacks with |details| parameter.
182 void Notify(const Details& details) {
183 typename internal::CallbackRegistryBase<
184 Callback<void(const Details&)> >::Iterator it = this->GetIterator();
185 Callback<void(const Details&)>* cb;
186 while((cb = it.GetNext()) != NULL) {
187 cb->Run(details);
188 }
189 }
190
191 private:
192 DISALLOW_COPY_AND_ASSIGN(CallbackRegistry);
193 };
194
195 template <> class CallbackRegistry<void>
196 : public internal::CallbackRegistryBase<Closure> {
197 public:
198 CallbackRegistry() {}
199
200 // Execute all active callbacks.
201 void Notify() {
202 Iterator it = this->GetIterator();
203 Closure* cb;
204 while((cb = it.GetNext()) != NULL) {
205 cb->Run();
206 }
207 }
208
209 private:
210 DISALLOW_COPY_AND_ASSIGN(CallbackRegistry);
211 };
212
213 } // namespace base
214
215 #endif // BASE_CALLBACK_REGISTRY_H_
OLDNEW
« no previous file with comments | « base/base.gypi ('k') | base/callback_registry_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698