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