• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <list>
9 #include <memory>
10 
11 #include "base/callback.h"
12 #include "base/compiler_specific.h"
13 #include "base/logging.h"
14 #include "base/macros.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 //   std::unique_ptr<base::CallbackList<void(const Foo&)>::Subscription>
32 //   RegisterCallback(const OnFooCallback& cb) {
33 //     return callback_list_.Add(cb);
34 //   }
35 //
36 //  private:
37 //   void NotifyFoo(const Foo& foo) {
38 //      callback_list_.Notify(foo);
39 //   }
40 //
41 //   base::CallbackList<void(const Foo&)> callback_list_;
42 //
43 //   DISALLOW_COPY_AND_ASSIGN(MyWidget);
44 // };
45 //
46 //
47 // class MyWidgetListener {
48 //  public:
49 //   MyWidgetListener::MyWidgetListener() {
50 //     foo_subscription_ = MyWidget::GetCurrent()->RegisterCallback(
51 //             base::Bind(&MyWidgetListener::OnFoo, this)));
52 //   }
53 //
54 //   MyWidgetListener::~MyWidgetListener() {
55 //      // Subscription gets deleted automatically and will deregister
56 //      // the callback in the process.
57 //   }
58 //
59 //  private:
60 //   void OnFoo(const Foo& foo) {
61 //     // Do something.
62 //   }
63 //
64 //   std::unique_ptr<base::CallbackList<void(const Foo&)>::Subscription>
65 //       foo_subscription_;
66 //
67 //   DISALLOW_COPY_AND_ASSIGN(MyWidgetListener);
68 // };
69 
70 namespace base {
71 
72 namespace internal {
73 
74 template <typename CallbackType>
75 class CallbackListBase {
76  public:
77   class Subscription {
78    public:
Subscription(CallbackListBase<CallbackType> * list,typename std::list<CallbackType>::iterator iter)79     Subscription(CallbackListBase<CallbackType>* list,
80                  typename std::list<CallbackType>::iterator iter)
81         : list_(list),
82           iter_(iter) {
83     }
84 
~Subscription()85     ~Subscription() {
86       if (list_->active_iterator_count_) {
87         iter_->Reset();
88       } else {
89         list_->callbacks_.erase(iter_);
90         if (!list_->removal_callback_.is_null())
91           list_->removal_callback_.Run();
92       }
93     }
94 
95    private:
96     CallbackListBase<CallbackType>* list_;
97     typename std::list<CallbackType>::iterator iter_;
98 
99     DISALLOW_COPY_AND_ASSIGN(Subscription);
100   };
101 
102   // Add a callback to the list. The callback will remain registered until the
103   // returned Subscription is destroyed, which must occur before the
104   // CallbackList is destroyed.
Add(const CallbackType & cb)105   std::unique_ptr<Subscription> Add(const CallbackType& cb) WARN_UNUSED_RESULT {
106     DCHECK(!cb.is_null());
107     return std::unique_ptr<Subscription>(
108         new Subscription(this, callbacks_.insert(callbacks_.end(), cb)));
109   }
110 
111   // Sets a callback which will be run when a subscription list is changed.
set_removal_callback(const Closure & callback)112   void set_removal_callback(const Closure& callback) {
113     removal_callback_ = callback;
114   }
115 
116   // Returns true if there are no subscriptions. This is only valid to call when
117   // not looping through the list.
empty()118   bool empty() {
119     DCHECK_EQ(0, active_iterator_count_);
120     return callbacks_.empty();
121   }
122 
123  protected:
124   // An iterator class that can be used to access the list of callbacks.
125   class Iterator {
126    public:
Iterator(CallbackListBase<CallbackType> * list)127     explicit Iterator(CallbackListBase<CallbackType>* list)
128         : list_(list),
129           list_iter_(list_->callbacks_.begin()) {
130       ++list_->active_iterator_count_;
131     }
132 
Iterator(const Iterator & iter)133     Iterator(const Iterator& iter)
134         : list_(iter.list_),
135           list_iter_(iter.list_iter_) {
136       ++list_->active_iterator_count_;
137     }
138 
~Iterator()139     ~Iterator() {
140       if (list_ && --list_->active_iterator_count_ == 0) {
141         list_->Compact();
142       }
143     }
144 
GetNext()145     CallbackType* GetNext() {
146       while ((list_iter_ != list_->callbacks_.end()) && list_iter_->is_null())
147         ++list_iter_;
148 
149       CallbackType* cb = NULL;
150       if (list_iter_ != list_->callbacks_.end()) {
151         cb = &(*list_iter_);
152         ++list_iter_;
153       }
154       return cb;
155     }
156 
157    private:
158     CallbackListBase<CallbackType>* list_;
159     typename std::list<CallbackType>::iterator list_iter_;
160   };
161 
CallbackListBase()162   CallbackListBase() : active_iterator_count_(0) {}
163 
~CallbackListBase()164   ~CallbackListBase() {
165     DCHECK_EQ(0, active_iterator_count_);
166     DCHECK_EQ(0U, callbacks_.size());
167   }
168 
169   // Returns an instance of a CallbackListBase::Iterator which can be used
170   // to run callbacks.
GetIterator()171   Iterator GetIterator() {
172     return Iterator(this);
173   }
174 
175   // Compact the list: remove any entries which were NULLed out during
176   // iteration.
Compact()177   void Compact() {
178     typename std::list<CallbackType>::iterator it = callbacks_.begin();
179     bool updated = false;
180     while (it != callbacks_.end()) {
181       if ((*it).is_null()) {
182         updated = true;
183         it = callbacks_.erase(it);
184       } else {
185         ++it;
186       }
187     }
188 
189     if (updated && !removal_callback_.is_null())
190       removal_callback_.Run();
191   }
192 
193  private:
194   std::list<CallbackType> callbacks_;
195   int active_iterator_count_;
196   Closure removal_callback_;
197 
198   DISALLOW_COPY_AND_ASSIGN(CallbackListBase);
199 };
200 
201 }  // namespace internal
202 
203 template <typename Sig> class CallbackList;
204 
205 template <typename... Args>
206 class CallbackList<void(Args...)>
207     : public internal::CallbackListBase<Callback<void(Args...)> > {
208  public:
209   typedef Callback<void(Args...)> CallbackType;
210 
CallbackList()211   CallbackList() {}
212 
213   template <typename... RunArgs>
Notify(RunArgs &&...args)214   void Notify(RunArgs&&... args) {
215     typename internal::CallbackListBase<CallbackType>::Iterator it =
216         this->GetIterator();
217     CallbackType* cb;
218     while ((cb = it.GetNext()) != NULL) {
219       cb->Run(args...);
220     }
221   }
222 
223  private:
224   DISALLOW_COPY_AND_ASSIGN(CallbackList);
225 };
226 
227 }  // namespace base
228 
229 #endif  // BASE_CALLBACK_LIST_H_
230