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 10 #include "base/callback.h" 11 #include "base/callback_internal.h" 12 #include "base/compiler_specific.h" 13 #include "base/logging.h" 14 #include "base/macros.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 // 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 71 namespace base { 72 73 namespace internal { 74 75 template <typename CallbackType> 76 class CallbackListBase { 77 public: 78 class Subscription { 79 public: Subscription(CallbackListBase<CallbackType> * list,typename std::list<CallbackType>::iterator iter)80 Subscription(CallbackListBase<CallbackType>* list, 81 typename std::list<CallbackType>::iterator iter) 82 : list_(list), 83 iter_(iter) { 84 } 85 ~Subscription()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. Add(const CallbackType & cb)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. set_removal_callback(const Closure & callback)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. empty()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: Iterator(CallbackListBase<CallbackType> * list)128 explicit Iterator(CallbackListBase<CallbackType>* list) 129 : list_(list), 130 list_iter_(list_->callbacks_.begin()) { 131 ++list_->active_iterator_count_; 132 } 133 Iterator(const Iterator & iter)134 Iterator(const Iterator& iter) 135 : list_(iter.list_), 136 list_iter_(iter.list_iter_) { 137 ++list_->active_iterator_count_; 138 } 139 ~Iterator()140 ~Iterator() { 141 if (list_ && --list_->active_iterator_count_ == 0) { 142 list_->Compact(); 143 } 144 } 145 GetNext()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 CallbackListBase()163 CallbackListBase() : active_iterator_count_(0) {} 164 ~CallbackListBase()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. GetIterator()172 Iterator GetIterator() { 173 return Iterator(this); 174 } 175 176 // Compact the list: remove any entries which were NULLed out during 177 // iteration. Compact()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 } 188 } 189 190 if (updated && !removal_callback_.is_null()) 191 removal_callback_.Run(); 192 } 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 204 template <typename Sig> class CallbackList; 205 206 template <typename... Args> 207 class CallbackList<void(Args...)> 208 : public internal::CallbackListBase<Callback<void(Args...)> > { 209 public: 210 typedef Callback<void(Args...)> CallbackType; 211 CallbackList()212 CallbackList() {} 213 Notify(typename internal::CallbackParamTraits<Args>::ForwardType...args)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_ 231