• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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