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