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