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