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 #include <utility> 11 12 #include "base/auto_reset.h" 13 #include "base/bind.h" 14 #include "base/callback.h" 15 #include "base/callback_helpers.h" 16 #include "base/check.h" 17 #include "base/compiler_specific.h" 18 #include "base/memory/weak_ptr.h" 19 #include "base/ranges/algorithm.h" 20 #include "base/stl_util.h" 21 22 // OVERVIEW: 23 // 24 // A container for a list of callbacks. Provides callers the ability to manually 25 // or automatically unregister callbacks at any time, including during callback 26 // notification. 27 // 28 // TYPICAL USAGE: 29 // 30 // class MyWidget { 31 // public: 32 // using CallbackList = base::RepeatingCallbackList<void(const Foo&)>; 33 // 34 // // Registers |cb| to be called whenever NotifyFoo() is executed. 35 // std::unique_ptr<CallbackList::Subscription> 36 // RegisterCallback(CallbackList::CallbackType cb) { 37 // return callback_list_.Add(std::move(cb)); 38 // } 39 // 40 // private: 41 // // Calls all registered callbacks, with |foo| as the supplied arg. 42 // void NotifyFoo(const Foo& foo) { 43 // callback_list_.Notify(foo); 44 // } 45 // 46 // CallbackList callback_list_; 47 // }; 48 // 49 // 50 // class MyWidgetListener { 51 // private: 52 // void OnFoo(const Foo& foo) { 53 // // Called whenever MyWidget::NotifyFoo() is executed, unless 54 // // |foo_subscription_| has been reset(). 55 // } 56 // 57 // // Automatically deregisters the callback when deleted (e.g. in 58 // // ~MyWidgetListener()). Unretained(this) is safe here since the 59 // // Subscription does not outlive |this|. 60 // std::unique_ptr<MyWidget::CallbackList::Subscription> foo_subscription_ = 61 // MyWidget::Get()->RegisterCallback( 62 // base::BindRepeating(&MyWidgetListener::OnFoo, 63 // base::Unretained(this))); 64 // }; 65 // 66 // UNSUPPORTED: 67 // 68 // * Destroying the CallbackList during callback notification. 69 // 70 // This is possible to support, but not currently necessary. 71 72 namespace base { 73 74 template <typename Signature> 75 class OnceCallbackList; 76 77 template <typename Signature> 78 class RepeatingCallbackList; 79 80 namespace internal { 81 82 // A traits class to break circular type dependencies between CallbackListBase 83 // and its subclasses. 84 template <typename CallbackList> 85 struct CallbackListTraits; 86 87 // NOTE: It's important that Callbacks provide iterator stability when items are 88 // added to the end, so e.g. a std::vector<> is not suitable here. 89 template <typename Signature> 90 struct CallbackListTraits<OnceCallbackList<Signature>> { 91 using CallbackType = OnceCallback<Signature>; 92 using Callbacks = std::list<CallbackType>; 93 }; 94 template <typename Signature> 95 struct CallbackListTraits<RepeatingCallbackList<Signature>> { 96 using CallbackType = RepeatingCallback<Signature>; 97 using Callbacks = std::list<CallbackType>; 98 }; 99 100 template <typename CallbackListImpl> 101 class CallbackListBase { 102 public: 103 using CallbackType = 104 typename CallbackListTraits<CallbackListImpl>::CallbackType; 105 static_assert(IsBaseCallback<CallbackType>::value, ""); 106 107 // A cancellation handle for callers who register callbacks. Subscription 108 // destruction cancels the associated callback and is legal any time, 109 // including after the destruction of the CallbackList that vends it. 110 class Subscription { 111 public: 112 explicit Subscription(base::OnceClosure destruction_closure) 113 : destruction_closure_(std::move(destruction_closure)) {} 114 115 Subscription(Subscription&&) = default; 116 Subscription& operator=(Subscription&&) = default; 117 118 ~Subscription() { std::move(destruction_closure_).Run(); } 119 120 private: 121 // Run when |this| is destroyed to notify the CallbackList the associated 122 // callback should be canceled. Since this is bound using a WeakPtr to the 123 // CallbackList, it will automatically no-op if the CallbackList no longer 124 // exists. 125 base::OnceClosure destruction_closure_; 126 }; 127 128 CallbackListBase() = default; 129 CallbackListBase(const CallbackListBase&) = delete; 130 CallbackListBase& operator=(const CallbackListBase&) = delete; 131 132 ~CallbackListBase() { 133 // Destroying the list during iteration is unsupported and will cause a UAF. 134 CHECK(!iterating_); 135 } 136 137 // Registers |cb| for future notifications. Returns a Subscription that can be 138 // used to cancel |cb|. 139 std::unique_ptr<Subscription> Add(CallbackType cb) WARN_UNUSED_RESULT { 140 DCHECK(!cb.is_null()); 141 return std::make_unique<Subscription>(base::BindOnce( 142 &CallbackListBase::CancelCallback, weak_ptr_factory_.GetWeakPtr(), 143 callbacks_.insert(callbacks_.end(), std::move(cb)))); 144 } 145 146 // Registers |cb| for future notifications. Provides no way for the caller to 147 // cancel, so this is only safe for cases where the callback is guaranteed to 148 // live at least as long as this list (e.g. if it's bound on the same object 149 // that owns the list). 150 // TODO(pkasting): Attempt to use Add() instead and see if callers can relax 151 // other lifetime/ordering mechanisms as a result. 152 void AddUnsafe(CallbackType cb) { 153 DCHECK(!cb.is_null()); 154 callbacks_.push_back(std::move(cb)); 155 } 156 157 // Registers |removal_callback| to be run after elements are removed from the 158 // list of registered callbacks. 159 void set_removal_callback(const RepeatingClosure& removal_callback) { 160 removal_callback_ = removal_callback; 161 } 162 163 // Returns whether the list of registered callbacks is empty (from an external 164 // perspective -- meaning no remaining callbacks are live). 165 bool empty() const { 166 return ranges::all_of( 167 callbacks_, [](const auto& callback) { return callback.is_null(); }); 168 } 169 170 // Calls all registered callbacks that are not canceled beforehand. If any 171 // callbacks are unregistered, notifies any registered removal callback at the 172 // end. 173 // 174 // Arguments must be copyable, since they must be supplied to all callbacks. 175 // Move-only types would be destructively modified by passing them to the 176 // first callback and not reach subsequent callbacks as intended. 177 // 178 // Notify() may be called re-entrantly, in which case the nested call 179 // completes before the outer one continues. Callbacks are only ever added at 180 // the end and canceled callbacks are not pruned from the list until the 181 // outermost iteration completes, so existing iterators should never be 182 // invalidated. However, this does mean that a callback added during a nested 183 // call can be notified by outer calls -- meaning it will be notified about 184 // things that happened before it was added -- if its subscription outlives 185 // the reentrant Notify() call. 186 template <typename... RunArgs> 187 void Notify(RunArgs&&... args) { 188 if (empty()) 189 return; // Nothing to do. 190 191 { 192 AutoReset<bool> iterating(&iterating_, true); 193 194 // Skip any callbacks that are canceled during iteration. 195 // NOTE: Since RunCallback() may call Add(), it's not safe to cache the 196 // value of callbacks_.end() across loop iterations. 197 const auto next_valid = [this](const auto it) { 198 return std::find_if_not(it, callbacks_.end(), [](const auto& callback) { 199 return callback.is_null(); 200 }); 201 }; 202 for (auto it = next_valid(callbacks_.begin()); it != callbacks_.end(); 203 it = next_valid(it)) 204 // NOTE: Intentionally does not call std::forward<RunArgs>(args)..., 205 // since that would allow move-only arguments. 206 static_cast<CallbackListImpl*>(this)->RunCallback(it++, args...); 207 } 208 209 // Re-entrant invocations shouldn't prune anything from the list. This can 210 // invalidate iterators from underneath higher call frames. It's safe to 211 // simply do nothing, since the outermost frame will continue through here 212 // and prune all null callbacks below. 213 if (iterating_) 214 return; 215 216 // Any null callbacks remaining in the list were canceled due to 217 // Subscription destruction during iteration, and can safely be erased now. 218 const size_t erased_callbacks = 219 EraseIf(callbacks_, [](const auto& cb) { return cb.is_null(); }); 220 221 // Run |removal_callback_| if any callbacks were canceled. Note that we 222 // cannot simply compare list sizes before and after iterating, since 223 // notification may result in Add()ing new callbacks as well as canceling 224 // them. Also note that if this is a OnceCallbackList, the OnceCallbacks 225 // that were executed above have all been removed regardless of whether 226 // they're counted in |erased_callbacks_|. 227 if (removal_callback_ && 228 (erased_callbacks || IsOnceCallback<CallbackType>::value)) 229 removal_callback_.Run(); // May delete |this|! 230 } 231 232 protected: 233 using Callbacks = typename CallbackListTraits<CallbackListImpl>::Callbacks; 234 235 // Holds non-null callbacks, which will be called during Notify(). 236 Callbacks callbacks_; 237 238 private: 239 // Cancels the callback pointed to by |it|, which is guaranteed to be valid. 240 void CancelCallback(const typename Callbacks::iterator& it) { 241 if (static_cast<CallbackListImpl*>(this)->CancelNullCallback(it)) 242 return; 243 244 if (iterating_) { 245 // Calling erase() here is unsafe, since the loop in Notify() may be 246 // referencing this same iterator, e.g. if adjacent callbacks' 247 // Subscriptions are both destroyed when the first one is Run(). Just 248 // reset the callback and let Notify() clean it up at the end. 249 it->Reset(); 250 } else { 251 callbacks_.erase(it); 252 if (removal_callback_) 253 removal_callback_.Run(); // May delete |this|! 254 } 255 } 256 257 // Set while Notify() is traversing |callbacks_|. Used primarily to avoid 258 // invalidating iterators that may be in use. 259 bool iterating_ = false; 260 261 // Called after elements are removed from |callbacks_|. 262 RepeatingClosure removal_callback_; 263 264 WeakPtrFactory<CallbackListBase> weak_ptr_factory_{this}; 265 }; 266 267 } // namespace internal 268 269 template <typename Signature> 270 class OnceCallbackList 271 : public internal::CallbackListBase<OnceCallbackList<Signature>> { 272 private: 273 friend internal::CallbackListBase<OnceCallbackList>; 274 using Traits = internal::CallbackListTraits<OnceCallbackList>; 275 276 // Runs the current callback, which may cancel it or any other callbacks. 277 template <typename... RunArgs> 278 void RunCallback(typename Traits::Callbacks::iterator it, RunArgs&&... args) { 279 // OnceCallbacks still have Subscriptions with outstanding iterators; 280 // splice() removes them from |callbacks_| without invalidating those. 281 null_callbacks_.splice(null_callbacks_.end(), this->callbacks_, it); 282 283 // NOTE: Intentionally does not call std::forward<RunArgs>(args)...; see 284 // comments in Notify(). 285 std::move(*it).Run(args...); 286 } 287 288 // If |it| refers to an already-canceled callback, does any necessary cleanup 289 // and returns true. Otherwise returns false. 290 bool CancelNullCallback(const typename Traits::Callbacks::iterator& it) { 291 if (it->is_null()) { 292 null_callbacks_.erase(it); 293 return true; 294 } 295 return false; 296 } 297 298 // Holds null callbacks whose Subscriptions are still alive, so the 299 // Subscriptions will still contain valid iterators. Only needed for 300 // OnceCallbacks, since RepeatingCallbacks are not canceled except by 301 // Subscription destruction. 302 typename Traits::Callbacks null_callbacks_; 303 }; 304 305 template <typename Signature> 306 class RepeatingCallbackList 307 : public internal::CallbackListBase<RepeatingCallbackList<Signature>> { 308 private: 309 friend internal::CallbackListBase<RepeatingCallbackList>; 310 using Traits = internal::CallbackListTraits<RepeatingCallbackList>; 311 // Runs the current callback, which may cancel it or any other callbacks. 312 template <typename... RunArgs> 313 void RunCallback(typename Traits::Callbacks::iterator it, RunArgs&&... args) { 314 // NOTE: Intentionally does not call std::forward<RunArgs>(args)...; see 315 // comments in Notify(). 316 it->Run(args...); 317 } 318 319 // If |it| refers to an already-canceled callback, does any necessary cleanup 320 // and returns true. Otherwise returns false. 321 bool CancelNullCallback(const typename Traits::Callbacks::iterator& it) { 322 // Because at most one Subscription can point to a given callback, and 323 // RepeatingCallbacks are only reset by CancelCallback(), no one should be 324 // able to request cancellation of a canceled RepeatingCallback. 325 DCHECK(!it->is_null()); 326 return false; 327 } 328 }; 329 330 template <typename Signature> 331 using CallbackList = RepeatingCallbackList<Signature>; 332 333 // Syntactic sugar to parallel that used for Callbacks. 334 using OnceClosureList = OnceCallbackList<void()>; 335 using RepeatingClosureList = RepeatingCallbackList<void()>; 336 using ClosureList = CallbackList<void()>; 337 338 } // namespace base 339 340 #endif // BASE_CALLBACK_LIST_H_ 341