blob: 5f263f899a37a730a543fdc9d1e6f8a8b82fb57d [file] [log] [blame]
caitkp@chromium.org1268af12013-09-12 00:16:33 +09001// 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
caitkp@chromium.org49647322013-09-27 04:20:18 +09005#ifndef BASE_CALLBACK_LIST_H_
6#define BASE_CALLBACK_LIST_H_
caitkp@chromium.org1268af12013-09-12 00:16:33 +09007
8#include <list>
9
10#include "base/basictypes.h"
11#include "base/callback.h"
caitkp@google.com2b10b742013-09-25 22:56:23 +090012#include "base/callback_internal.h"
caitkp@chromium.org1268af12013-09-12 00:16:33 +090013#include "base/compiler_specific.h"
14#include "base/logging.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//
caitkp@chromium.org49647322013-09-27 04:20:18 +090032// scoped_ptr<base::CallbackList<void(const Foo&)>::Subscription>
caitkp@google.com2b10b742013-09-25 22:56:23 +090033// RegisterCallback(const OnFooCallback& cb) {
caitkp@chromium.org49647322013-09-27 04:20:18 +090034// return callback_list_.Add(cb);
caitkp@chromium.org1268af12013-09-12 00:16:33 +090035// }
36//
37// private:
38// void NotifyFoo(const Foo& foo) {
caitkp@chromium.org49647322013-09-27 04:20:18 +090039// callback_list_.Notify(foo);
caitkp@chromium.org1268af12013-09-12 00:16:33 +090040// }
41//
caitkp@chromium.org49647322013-09-27 04:20:18 +090042// base::CallbackList<void(const Foo&)> callback_list_;
caitkp@chromium.org0b4414d2013-10-10 08:28:21 +090043//
44// DISALLOW_COPY_AND_ASSIGN(MyWidget);
caitkp@chromium.org1268af12013-09-12 00:16:33 +090045// };
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//
caitkp@chromium.org49647322013-09-27 04:20:18 +090065// scoped_ptr<base::CallbackList<void(const Foo&)>::Subscription>
caitkp@google.com2b10b742013-09-25 22:56:23 +090066// foo_subscription_;
caitkp@chromium.org0b4414d2013-10-10 08:28:21 +090067//
68// DISALLOW_COPY_AND_ASSIGN(MyWidgetListener);
caitkp@chromium.org1268af12013-09-12 00:16:33 +090069// };
70
71namespace base {
72
73namespace internal {
74
75template <typename CallbackType>
caitkp@chromium.org49647322013-09-27 04:20:18 +090076class CallbackListBase {
caitkp@chromium.org1268af12013-09-12 00:16:33 +090077 public:
78 class Subscription {
79 public:
caitkp@chromium.org49647322013-09-27 04:20:18 +090080 Subscription(CallbackListBase<CallbackType>* list,
caitkp@chromium.org1268af12013-09-12 00:16:33 +090081 typename std::list<CallbackType>::iterator iter)
82 : list_(list),
caitkp@chromium.org0b4414d2013-10-10 08:28:21 +090083 iter_(iter) {
84 }
caitkp@chromium.org1268af12013-09-12 00:16:33 +090085
86 ~Subscription() {
jam@chromium.org63a01af2014-05-13 01:36:10 +090087 if (list_->active_iterator_count_) {
caitkp@chromium.org0b4414d2013-10-10 08:28:21 +090088 iter_->Reset();
jam@chromium.org63a01af2014-05-13 01:36:10 +090089 } else {
caitkp@chromium.org1268af12013-09-12 00:16:33 +090090 list_->callbacks_.erase(iter_);
jam@chromium.org63a01af2014-05-13 01:36:10 +090091 if (!list_->removal_callback_.is_null())
92 list_->removal_callback_.Run();
93 }
caitkp@chromium.org1268af12013-09-12 00:16:33 +090094 }
95
96 private:
caitkp@chromium.org49647322013-09-27 04:20:18 +090097 CallbackListBase<CallbackType>* list_;
caitkp@chromium.org1268af12013-09-12 00:16:33 +090098 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
caitkp@chromium.org49647322013-09-27 04:20:18 +0900105 // CallbackList is destroyed.
bauerb@chromium.org69cfb182013-11-16 00:54:36 +0900106 scoped_ptr<Subscription> Add(const CallbackType& cb) WARN_UNUSED_RESULT {
caitkp@chromium.org1268af12013-09-12 00:16:33 +0900107 DCHECK(!cb.is_null());
108 return scoped_ptr<Subscription>(
109 new Subscription(this, callbacks_.insert(callbacks_.end(), cb)));
110 }
111
jam@chromium.org63a01af2014-05-13 01:36:10 +0900112 // 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
caitkp@chromium.org1268af12013-09-12 00:16:33 +0900124 protected:
125 // An iterator class that can be used to access the list of callbacks.
126 class Iterator {
127 public:
caitkp@chromium.org49647322013-09-27 04:20:18 +0900128 explicit Iterator(CallbackListBase<CallbackType>* list)
caitkp@chromium.org1268af12013-09-12 00:16:33 +0900129 : 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
caitkp@google.com146349f2013-09-12 01:44:56 +0900150 CallbackType* cb = NULL;
151 if (list_iter_ != list_->callbacks_.end()) {
152 cb = &(*list_iter_);
153 ++list_iter_;
154 }
caitkp@chromium.org1268af12013-09-12 00:16:33 +0900155 return cb;
156 }
157
158 private:
caitkp@chromium.org49647322013-09-27 04:20:18 +0900159 CallbackListBase<CallbackType>* list_;
caitkp@chromium.org1268af12013-09-12 00:16:33 +0900160 typename std::list<CallbackType>::iterator list_iter_;
161 };
162
caitkp@chromium.org0b4414d2013-10-10 08:28:21 +0900163 CallbackListBase() : active_iterator_count_(0) {}
caitkp@chromium.org1268af12013-09-12 00:16:33 +0900164
caitkp@chromium.org49647322013-09-27 04:20:18 +0900165 ~CallbackListBase() {
caitkp@chromium.org1268af12013-09-12 00:16:33 +0900166 DCHECK_EQ(0, active_iterator_count_);
167 DCHECK_EQ(0U, callbacks_.size());
168 }
169
caitkp@chromium.org49647322013-09-27 04:20:18 +0900170 // Returns an instance of a CallbackListBase::Iterator which can be used
caitkp@chromium.org1268af12013-09-12 00:16:33 +0900171 // 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();
jam@chromium.org63a01af2014-05-13 01:36:10 +0900180 bool updated = false;
caitkp@chromium.org1268af12013-09-12 00:16:33 +0900181 while (it != callbacks_.end()) {
jam@chromium.org63a01af2014-05-13 01:36:10 +0900182 if ((*it).is_null()) {
183 updated = true;
caitkp@chromium.org1268af12013-09-12 00:16:33 +0900184 it = callbacks_.erase(it);
jam@chromium.org63a01af2014-05-13 01:36:10 +0900185 } else {
caitkp@chromium.org1268af12013-09-12 00:16:33 +0900186 ++it;
jam@chromium.org63a01af2014-05-13 01:36:10 +0900187 }
caitkp@chromium.org1268af12013-09-12 00:16:33 +0900188 }
davidben4f5f0432015-10-31 05:42:07 +0900189
190 if (updated && !removal_callback_.is_null())
191 removal_callback_.Run();
caitkp@chromium.org1268af12013-09-12 00:16:33 +0900192 }
193
194 private:
195 std::list<CallbackType> callbacks_;
196 int active_iterator_count_;
jam@chromium.org63a01af2014-05-13 01:36:10 +0900197 Closure removal_callback_;
caitkp@chromium.org1268af12013-09-12 00:16:33 +0900198
caitkp@chromium.org49647322013-09-27 04:20:18 +0900199 DISALLOW_COPY_AND_ASSIGN(CallbackListBase);
caitkp@chromium.org1268af12013-09-12 00:16:33 +0900200};
201
202} // namespace internal
203
caitkp@chromium.org49647322013-09-27 04:20:18 +0900204template <typename Sig> class CallbackList;
caitkp@google.com2b10b742013-09-25 22:56:23 +0900205
thakis260669d2014-11-20 10:15:14 +0900206template <typename... Args>
207class CallbackList<void(Args...)>
208 : public internal::CallbackListBase<Callback<void(Args...)> > {
caitkp@chromium.org1268af12013-09-12 00:16:33 +0900209 public:
thakis260669d2014-11-20 10:15:14 +0900210 typedef Callback<void(Args...)> CallbackType;
caitkp@google.com2b10b742013-09-25 22:56:23 +0900211
caitkp@chromium.org49647322013-09-27 04:20:18 +0900212 CallbackList() {}
caitkp@chromium.org1268af12013-09-12 00:16:33 +0900213
thakis260669d2014-11-20 10:15:14 +0900214 void Notify(
215 typename internal::CallbackParamTraits<Args>::ForwardType... args) {
caitkp@chromium.org49647322013-09-27 04:20:18 +0900216 typename internal::CallbackListBase<CallbackType>::Iterator it =
caitkp@google.com2b10b742013-09-25 22:56:23 +0900217 this->GetIterator();
218 CallbackType* cb;
caitkp@chromium.org0b4414d2013-10-10 08:28:21 +0900219 while ((cb = it.GetNext()) != NULL) {
thakis260669d2014-11-20 10:15:14 +0900220 cb->Run(args...);
caitkp@google.com2b10b742013-09-25 22:56:23 +0900221 }
222 }
223
224 private:
caitkp@chromium.org49647322013-09-27 04:20:18 +0900225 DISALLOW_COPY_AND_ASSIGN(CallbackList);
caitkp@google.com2b10b742013-09-25 22:56:23 +0900226};
227
caitkp@chromium.org1268af12013-09-12 00:16:33 +0900228} // namespace base
229
caitkp@chromium.org49647322013-09-27 04:20:18 +0900230#endif // BASE_CALLBACK_LIST_H_