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