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 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 // 31 // std::unique_ptr<base::CallbackList<void(const Foo&)>::Subscription> 32 // RegisterCallback(const OnFooCallback& cb) { 33 // return callback_list_.Add(cb); 34 // } 35 // 36 // private: 37 // void NotifyFoo(const Foo& foo) { 38 // callback_list_.Notify(foo); 39 // } 40 // 41 // base::CallbackList<void(const Foo&)> callback_list_; 42 // 43 // DISALLOW_COPY_AND_ASSIGN(MyWidget); 44 // }; 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 // 64 // std::unique_ptr<base::CallbackList<void(const Foo&)>::Subscription> 65 // foo_subscription_; 66 // 67 // DISALLOW_COPY_AND_ASSIGN(MyWidgetListener); 68 // }; 69 70 namespace base { 71 72 namespace internal { 73 74 template <typename CallbackType> 75 class CallbackListBase { 76 public: 77 class Subscription { 78 public: Subscription(CallbackListBase<CallbackType> * list,typename std::list<CallbackType>::iterator iter)79 Subscription(CallbackListBase<CallbackType>* list, 80 typename std::list<CallbackType>::iterator iter) 81 : list_(list), 82 iter_(iter) { 83 } 84 ~Subscription()85 ~Subscription() { 86 if (list_->active_iterator_count_) { 87 iter_->Reset(); 88 } else { 89 list_->callbacks_.erase(iter_); 90 if (!list_->removal_callback_.is_null()) 91 list_->removal_callback_.Run(); 92 } 93 } 94 95 private: 96 CallbackListBase<CallbackType>* list_; 97 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 104 // CallbackList is destroyed. Add(const CallbackType & cb)105 std::unique_ptr<Subscription> Add(const CallbackType& cb) WARN_UNUSED_RESULT { 106 DCHECK(!cb.is_null()); 107 return std::unique_ptr<Subscription>( 108 new Subscription(this, callbacks_.insert(callbacks_.end(), cb))); 109 } 110 111 // Sets a callback which will be run when a subscription list is changed. set_removal_callback(const Closure & callback)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. empty()118 bool empty() { 119 DCHECK_EQ(0, active_iterator_count_); 120 return callbacks_.empty(); 121 } 122 123 protected: 124 // An iterator class that can be used to access the list of callbacks. 125 class Iterator { 126 public: Iterator(CallbackListBase<CallbackType> * list)127 explicit Iterator(CallbackListBase<CallbackType>* list) 128 : list_(list), 129 list_iter_(list_->callbacks_.begin()) { 130 ++list_->active_iterator_count_; 131 } 132 Iterator(const Iterator & iter)133 Iterator(const Iterator& iter) 134 : list_(iter.list_), 135 list_iter_(iter.list_iter_) { 136 ++list_->active_iterator_count_; 137 } 138 ~Iterator()139 ~Iterator() { 140 if (list_ && --list_->active_iterator_count_ == 0) { 141 list_->Compact(); 142 } 143 } 144 GetNext()145 CallbackType* GetNext() { 146 while ((list_iter_ != list_->callbacks_.end()) && list_iter_->is_null()) 147 ++list_iter_; 148 149 CallbackType* cb = NULL; 150 if (list_iter_ != list_->callbacks_.end()) { 151 cb = &(*list_iter_); 152 ++list_iter_; 153 } 154 return cb; 155 } 156 157 private: 158 CallbackListBase<CallbackType>* list_; 159 typename std::list<CallbackType>::iterator list_iter_; 160 }; 161 CallbackListBase()162 CallbackListBase() : active_iterator_count_(0) {} 163 ~CallbackListBase()164 ~CallbackListBase() { 165 DCHECK_EQ(0, active_iterator_count_); 166 DCHECK_EQ(0U, callbacks_.size()); 167 } 168 169 // Returns an instance of a CallbackListBase::Iterator which can be used 170 // to run callbacks. GetIterator()171 Iterator GetIterator() { 172 return Iterator(this); 173 } 174 175 // Compact the list: remove any entries which were NULLed out during 176 // iteration. Compact()177 void Compact() { 178 typename std::list<CallbackType>::iterator it = callbacks_.begin(); 179 bool updated = false; 180 while (it != callbacks_.end()) { 181 if ((*it).is_null()) { 182 updated = true; 183 it = callbacks_.erase(it); 184 } else { 185 ++it; 186 } 187 } 188 189 if (updated && !removal_callback_.is_null()) 190 removal_callback_.Run(); 191 } 192 193 private: 194 std::list<CallbackType> callbacks_; 195 int active_iterator_count_; 196 Closure removal_callback_; 197 198 DISALLOW_COPY_AND_ASSIGN(CallbackListBase); 199 }; 200 201 } // namespace internal 202 203 template <typename Sig> class CallbackList; 204 205 template <typename... Args> 206 class CallbackList<void(Args...)> 207 : public internal::CallbackListBase<Callback<void(Args...)> > { 208 public: 209 typedef Callback<void(Args...)> CallbackType; 210 CallbackList()211 CallbackList() {} 212 213 template <typename... RunArgs> Notify(RunArgs &&...args)214 void Notify(RunArgs&&... args) { 215 typename internal::CallbackListBase<CallbackType>::Iterator it = 216 this->GetIterator(); 217 CallbackType* cb; 218 while ((cb = it.GetNext()) != NULL) { 219 cb->Run(args...); 220 } 221 } 222 223 private: 224 DISALLOW_COPY_AND_ASSIGN(CallbackList); 225 }; 226 227 } // namespace base 228 229 #endif // BASE_CALLBACK_LIST_H_ 230