xref: /aosp_15_r20/external/cronet/base/callback_list.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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