blob: 7d6a478e8ce067338dfae8917a5cde7440cf9dae [file] [log] [blame]
Vitaly Bukacbed2062015-08-17 12:54:05 -07001// 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
Vitaly Bukacbed2062015-08-17 12:54:05 -070010#include "base/callback.h"
11#include "base/callback_internal.h"
12#include "base/compiler_specific.h"
13#include "base/logging.h"
Alex Vakulenko674f0eb2016-01-20 08:10:48 -080014#include "base/macros.h"
Vitaly Bukacbed2062015-08-17 12:54:05 -070015#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
71namespace base {
72
73namespace internal {
74
75template <typename CallbackType>
76class CallbackListBase {
77 public:
78 class Subscription {
79 public:
80 Subscription(CallbackListBase<CallbackType>* list,
81 typename std::list<CallbackType>::iterator iter)
82 : list_(list),
83 iter_(iter) {
84 }
85
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.
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.
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.
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:
128 explicit Iterator(CallbackListBase<CallbackType>* list)
129 : list_(list),
130 list_iter_(list_->callbacks_.begin()) {
131 ++list_->active_iterator_count_;
132 }
133
134 Iterator(const Iterator& iter)
135 : list_(iter.list_),
136 list_iter_(iter.list_iter_) {
137 ++list_->active_iterator_count_;
138 }
139
140 ~Iterator() {
141 if (list_ && --list_->active_iterator_count_ == 0) {
142 list_->Compact();
143 }
144 }
145
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
163 CallbackListBase() : active_iterator_count_(0) {}
164
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.
172 Iterator GetIterator() {
173 return Iterator(this);
174 }
175
176 // Compact the list: remove any entries which were NULLed out during
177 // iteration.
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 }
Vitaly Bukacbed2062015-08-17 12:54:05 -0700188 }
Alex Vakulenko674f0eb2016-01-20 08:10:48 -0800189
190 if (updated && !removal_callback_.is_null())
191 removal_callback_.Run();
Vitaly Bukacbed2062015-08-17 12:54:05 -0700192 }
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
204template <typename Sig> class CallbackList;
205
206template <typename... Args>
207class CallbackList<void(Args...)>
208 : public internal::CallbackListBase<Callback<void(Args...)> > {
209 public:
210 typedef Callback<void(Args...)> CallbackType;
211
212 CallbackList() {}
213
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_