[go: nahoru, domu]

blob: 7ab79dd8e1fcf79de5b76bc6e2586f49c7e3d974 [file] [log] [blame]
caitkp@chromium.org893c8162013-09-11 15:16:331// 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.org2a7cac02013-09-26 19:20:185#ifndef BASE_CALLBACK_LIST_H_
6#define BASE_CALLBACK_LIST_H_
caitkp@chromium.org893c8162013-09-11 15:16:337
8#include <list>
dcheng093de9b2016-04-04 21:25:519#include <memory>
caitkp@chromium.org893c8162013-09-11 15:16:3310
caitkp@chromium.org893c8162013-09-11 15:16:3311#include "base/callback.h"
12#include "base/compiler_specific.h"
13#include "base/logging.h"
avi9b6f42932015-12-26 22:15:1414#include "base/macros.h"
caitkp@chromium.org893c8162013-09-11 15:16:3315
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//
dcheng093de9b2016-04-04 21:25:5131// std::unique_ptr<base::CallbackList<void(const Foo&)>::Subscription>
caitkp@google.com243576f2013-09-25 13:56:2332// RegisterCallback(const OnFooCallback& cb) {
caitkp@chromium.org2a7cac02013-09-26 19:20:1833// return callback_list_.Add(cb);
caitkp@chromium.org893c8162013-09-11 15:16:3334// }
35//
36// private:
37// void NotifyFoo(const Foo& foo) {
caitkp@chromium.org2a7cac02013-09-26 19:20:1838// callback_list_.Notify(foo);
caitkp@chromium.org893c8162013-09-11 15:16:3339// }
40//
caitkp@chromium.org2a7cac02013-09-26 19:20:1841// base::CallbackList<void(const Foo&)> callback_list_;
caitkp@chromium.orgabdd66e2013-10-09 23:28:2142//
43// DISALLOW_COPY_AND_ASSIGN(MyWidget);
caitkp@chromium.org893c8162013-09-11 15:16:3344// };
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//
dcheng093de9b2016-04-04 21:25:5164// std::unique_ptr<base::CallbackList<void(const Foo&)>::Subscription>
caitkp@google.com243576f2013-09-25 13:56:2365// foo_subscription_;
caitkp@chromium.orgabdd66e2013-10-09 23:28:2166//
67// DISALLOW_COPY_AND_ASSIGN(MyWidgetListener);
caitkp@chromium.org893c8162013-09-11 15:16:3368// };
69
70namespace base {
71
72namespace internal {
73
74template <typename CallbackType>
caitkp@chromium.org2a7cac02013-09-26 19:20:1875class CallbackListBase {
caitkp@chromium.org893c8162013-09-11 15:16:3376 public:
77 class Subscription {
78 public:
caitkp@chromium.org2a7cac02013-09-26 19:20:1879 Subscription(CallbackListBase<CallbackType>* list,
caitkp@chromium.org893c8162013-09-11 15:16:3380 typename std::list<CallbackType>::iterator iter)
81 : list_(list),
caitkp@chromium.orgabdd66e2013-10-09 23:28:2182 iter_(iter) {
83 }
caitkp@chromium.org893c8162013-09-11 15:16:3384
85 ~Subscription() {
jam@chromium.orgf28ef9a32014-05-12 16:36:1086 if (list_->active_iterator_count_) {
caitkp@chromium.orgabdd66e2013-10-09 23:28:2187 iter_->Reset();
jam@chromium.orgf28ef9a32014-05-12 16:36:1088 } else {
caitkp@chromium.org893c8162013-09-11 15:16:3389 list_->callbacks_.erase(iter_);
jam@chromium.orgf28ef9a32014-05-12 16:36:1090 if (!list_->removal_callback_.is_null())
91 list_->removal_callback_.Run();
92 }
caitkp@chromium.org893c8162013-09-11 15:16:3393 }
94
95 private:
caitkp@chromium.org2a7cac02013-09-26 19:20:1896 CallbackListBase<CallbackType>* list_;
caitkp@chromium.org893c8162013-09-11 15:16:3397 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
caitkp@chromium.org2a7cac02013-09-26 19:20:18104 // CallbackList is destroyed.
dcheng093de9b2016-04-04 21:25:51105 std::unique_ptr<Subscription> Add(const CallbackType& cb) WARN_UNUSED_RESULT {
caitkp@chromium.org893c8162013-09-11 15:16:33106 DCHECK(!cb.is_null());
dcheng093de9b2016-04-04 21:25:51107 return std::unique_ptr<Subscription>(
caitkp@chromium.org893c8162013-09-11 15:16:33108 new Subscription(this, callbacks_.insert(callbacks_.end(), cb)));
jam@chromium.orgf28ef9a32014-05-12 16:36:10109 }
110
111 // Sets a callback which will be run when a subscription list is changed.
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.
118 bool empty() {
119 DCHECK_EQ(0, active_iterator_count_);
120 return callbacks_.empty();
caitkp@chromium.org893c8162013-09-11 15:16:33121 }
122
123 protected:
124 // An iterator class that can be used to access the list of callbacks.
125 class Iterator {
126 public:
caitkp@chromium.org2a7cac02013-09-26 19:20:18127 explicit Iterator(CallbackListBase<CallbackType>* list)
caitkp@chromium.org893c8162013-09-11 15:16:33128 : list_(list),
129 list_iter_(list_->callbacks_.begin()) {
130 ++list_->active_iterator_count_;
131 }
132
133 Iterator(const Iterator& iter)
134 : list_(iter.list_),
135 list_iter_(iter.list_iter_) {
136 ++list_->active_iterator_count_;
137 }
138
139 ~Iterator() {
140 if (list_ && --list_->active_iterator_count_ == 0) {
141 list_->Compact();
142 }
143 }
144
145 CallbackType* GetNext() {
146 while ((list_iter_ != list_->callbacks_.end()) && list_iter_->is_null())
147 ++list_iter_;
148
caitkp@google.comda04fbf2013-09-11 16:44:56149 CallbackType* cb = NULL;
150 if (list_iter_ != list_->callbacks_.end()) {
151 cb = &(*list_iter_);
152 ++list_iter_;
153 }
caitkp@chromium.org893c8162013-09-11 15:16:33154 return cb;
155 }
156
157 private:
caitkp@chromium.org2a7cac02013-09-26 19:20:18158 CallbackListBase<CallbackType>* list_;
caitkp@chromium.org893c8162013-09-11 15:16:33159 typename std::list<CallbackType>::iterator list_iter_;
160 };
161
caitkp@chromium.orgabdd66e2013-10-09 23:28:21162 CallbackListBase() : active_iterator_count_(0) {}
caitkp@chromium.org893c8162013-09-11 15:16:33163
caitkp@chromium.org2a7cac02013-09-26 19:20:18164 ~CallbackListBase() {
caitkp@chromium.org893c8162013-09-11 15:16:33165 DCHECK_EQ(0, active_iterator_count_);
166 DCHECK_EQ(0U, callbacks_.size());
167 }
168
caitkp@chromium.org2a7cac02013-09-26 19:20:18169 // Returns an instance of a CallbackListBase::Iterator which can be used
caitkp@chromium.org893c8162013-09-11 15:16:33170 // to run callbacks.
171 Iterator GetIterator() {
172 return Iterator(this);
173 }
174
175 // Compact the list: remove any entries which were NULLed out during
176 // iteration.
177 void Compact() {
178 typename std::list<CallbackType>::iterator it = callbacks_.begin();
jam@chromium.orgf28ef9a32014-05-12 16:36:10179 bool updated = false;
caitkp@chromium.org893c8162013-09-11 15:16:33180 while (it != callbacks_.end()) {
jam@chromium.orgf28ef9a32014-05-12 16:36:10181 if ((*it).is_null()) {
182 updated = true;
caitkp@chromium.org893c8162013-09-11 15:16:33183 it = callbacks_.erase(it);
jam@chromium.orgf28ef9a32014-05-12 16:36:10184 } else {
caitkp@chromium.org893c8162013-09-11 15:16:33185 ++it;
jam@chromium.orgf28ef9a32014-05-12 16:36:10186 }
caitkp@chromium.org893c8162013-09-11 15:16:33187 }
davidbenf126cb22015-10-30 20:42:07188
189 if (updated && !removal_callback_.is_null())
190 removal_callback_.Run();
caitkp@chromium.org893c8162013-09-11 15:16:33191 }
192
193 private:
194 std::list<CallbackType> callbacks_;
195 int active_iterator_count_;
jam@chromium.orgf28ef9a32014-05-12 16:36:10196 Closure removal_callback_;
caitkp@chromium.org893c8162013-09-11 15:16:33197
caitkp@chromium.org2a7cac02013-09-26 19:20:18198 DISALLOW_COPY_AND_ASSIGN(CallbackListBase);
caitkp@chromium.org893c8162013-09-11 15:16:33199};
200
201} // namespace internal
202
caitkp@chromium.org2a7cac02013-09-26 19:20:18203template <typename Sig> class CallbackList;
caitkp@google.com243576f2013-09-25 13:56:23204
thakis6117a472014-11-20 01:15:14205template <typename... Args>
206class CallbackList<void(Args...)>
207 : public internal::CallbackListBase<Callback<void(Args...)> > {
caitkp@chromium.org893c8162013-09-11 15:16:33208 public:
thakis6117a472014-11-20 01:15:14209 typedef Callback<void(Args...)> CallbackType;
caitkp@google.com243576f2013-09-25 13:56:23210
caitkp@chromium.org2a7cac02013-09-26 19:20:18211 CallbackList() {}
caitkp@chromium.org893c8162013-09-11 15:16:33212
tzikd2046ce32016-04-14 21:44:39213 template <typename... RunArgs>
214 void Notify(RunArgs&&... args) {
caitkp@chromium.org2a7cac02013-09-26 19:20:18215 typename internal::CallbackListBase<CallbackType>::Iterator it =
caitkp@google.com243576f2013-09-25 13:56:23216 this->GetIterator();
217 CallbackType* cb;
caitkp@chromium.orgabdd66e2013-10-09 23:28:21218 while ((cb = it.GetNext()) != NULL) {
thakis6117a472014-11-20 01:15:14219 cb->Run(args...);
caitkp@google.com243576f2013-09-25 13:56:23220 }
221 }
222
223 private:
caitkp@chromium.org2a7cac02013-09-26 19:20:18224 DISALLOW_COPY_AND_ASSIGN(CallbackList);
caitkp@google.com243576f2013-09-25 13:56:23225};
226
caitkp@chromium.org893c8162013-09-11 15:16:33227} // namespace base
228
caitkp@chromium.org2a7cac02013-09-26 19:20:18229#endif // BASE_CALLBACK_LIST_H_