| // Copyright 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef BASE_CALLBACK_LIST_H_ |
| #define BASE_CALLBACK_LIST_H_ |
| |
| #include <list> |
| #include <memory> |
| #include <utility> |
| |
| #include "base/auto_reset.h" |
| #include "base/base_export.h" |
| #include "base/bind.h" |
| #include "base/callback.h" |
| #include "base/callback_helpers.h" |
| #include "base/check.h" |
| #include "base/compiler_specific.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/ranges/algorithm.h" |
| #include "base/stl_util.h" |
| |
| // OVERVIEW: |
| // |
| // A container for a list of callbacks. Provides callers the ability to manually |
| // or automatically unregister callbacks at any time, including during callback |
| // notification. |
| // |
| // TYPICAL USAGE: |
| // |
| // class MyWidget { |
| // public: |
| // using CallbackList = base::RepeatingCallbackList<void(const Foo&)>; |
| // |
| // // Registers |cb| to be called whenever NotifyFoo() is executed. |
| // CallbackListSubscription RegisterCallback(CallbackList::CallbackType cb) { |
| // return callback_list_.Add(std::move(cb)); |
| // } |
| // |
| // private: |
| // // Calls all registered callbacks, with |foo| as the supplied arg. |
| // void NotifyFoo(const Foo& foo) { |
| // callback_list_.Notify(foo); |
| // } |
| // |
| // CallbackList callback_list_; |
| // }; |
| // |
| // |
| // class MyWidgetListener { |
| // private: |
| // void OnFoo(const Foo& foo) { |
| // // Called whenever MyWidget::NotifyFoo() is executed, unless |
| // // |foo_subscription_| has been destroyed. |
| // } |
| // |
| // // Automatically deregisters the callback when deleted (e.g. in |
| // // ~MyWidgetListener()). Unretained(this) is safe here since the |
| // // ScopedClosureRunner does not outlive |this|. |
| // CallbackListSubscription foo_subscription_ = |
| // MyWidget::Get()->RegisterCallback( |
| // base::BindRepeating(&MyWidgetListener::OnFoo, |
| // base::Unretained(this))); |
| // }; |
| // |
| // UNSUPPORTED: |
| // |
| // * Destroying the CallbackList during callback notification. |
| // |
| // This is possible to support, but not currently necessary. |
| |
| namespace base { |
| namespace internal { |
| template <typename CallbackListImpl> |
| class CallbackListBase; |
| } // namespace internal |
| |
| template <typename Signature> |
| class OnceCallbackList; |
| |
| template <typename Signature> |
| class RepeatingCallbackList; |
| |
| // A trimmed-down version of ScopedClosureRunner that can be used to guarantee a |
| // closure is run on destruction. This is designed to be used by |
| // CallbackListBase to run CancelCallback() when this subscription dies; |
| // consumers can avoid callbacks on dead objects by ensuring the subscription |
| // returned by CallbackListBase::Add() does not outlive the bound object in the |
| // callback. A typical way to do this is to bind a callback to a member function |
| // on `this` and store the returned subscription as a member variable. |
| class BASE_EXPORT CallbackListSubscription { |
| public: |
| CallbackListSubscription(); |
| CallbackListSubscription(CallbackListSubscription&& subscription); |
| CallbackListSubscription& operator=(CallbackListSubscription&& subscription); |
| ~CallbackListSubscription(); |
| |
| explicit operator bool() const { return !!closure_; } |
| |
| private: |
| template <typename T> |
| friend class internal::CallbackListBase; |
| |
| explicit CallbackListSubscription(base::OnceClosure closure); |
| |
| void Run(); |
| |
| OnceClosure closure_; |
| }; |
| |
| namespace internal { |
| |
| // A traits class to break circular type dependencies between CallbackListBase |
| // and its subclasses. |
| template <typename CallbackList> |
| struct CallbackListTraits; |
| |
| // NOTE: It's important that Callbacks provide iterator stability when items are |
| // added to the end, so e.g. a std::vector<> is not suitable here. |
| template <typename Signature> |
| struct CallbackListTraits<OnceCallbackList<Signature>> { |
| using CallbackType = OnceCallback<Signature>; |
| using Callbacks = std::list<CallbackType>; |
| }; |
| template <typename Signature> |
| struct CallbackListTraits<RepeatingCallbackList<Signature>> { |
| using CallbackType = RepeatingCallback<Signature>; |
| using Callbacks = std::list<CallbackType>; |
| }; |
| |
| template <typename CallbackListImpl> |
| class CallbackListBase { |
| public: |
| using CallbackType = |
| typename CallbackListTraits<CallbackListImpl>::CallbackType; |
| static_assert(IsBaseCallback<CallbackType>::value, ""); |
| |
| // TODO(crbug.com/1103086): Update references to use this directly and by |
| // value, then remove. |
| using Subscription = CallbackListSubscription; |
| |
| CallbackListBase() = default; |
| CallbackListBase(const CallbackListBase&) = delete; |
| CallbackListBase& operator=(const CallbackListBase&) = delete; |
| |
| ~CallbackListBase() { |
| // Destroying the list during iteration is unsupported and will cause a UAF. |
| CHECK(!iterating_); |
| } |
| |
| // Registers |cb| for future notifications. Returns a CallbackListSubscription |
| // whose destruction will cancel |cb|. |
| CallbackListSubscription Add(CallbackType cb) WARN_UNUSED_RESULT { |
| DCHECK(!cb.is_null()); |
| return CallbackListSubscription(base::BindOnce( |
| &CallbackListBase::CancelCallback, weak_ptr_factory_.GetWeakPtr(), |
| callbacks_.insert(callbacks_.end(), std::move(cb)))); |
| } |
| |
| // Registers |cb| for future notifications. Provides no way for the caller to |
| // cancel, so this is only safe for cases where the callback is guaranteed to |
| // live at least as long as this list (e.g. if it's bound on the same object |
| // that owns the list). |
| // TODO(pkasting): Attempt to use Add() instead and see if callers can relax |
| // other lifetime/ordering mechanisms as a result. |
| void AddUnsafe(CallbackType cb) { |
| DCHECK(!cb.is_null()); |
| callbacks_.push_back(std::move(cb)); |
| } |
| |
| // Registers |removal_callback| to be run after elements are removed from the |
| // list of registered callbacks. |
| void set_removal_callback(const RepeatingClosure& removal_callback) { |
| removal_callback_ = removal_callback; |
| } |
| |
| // Returns whether the list of registered callbacks is empty (from an external |
| // perspective -- meaning no remaining callbacks are live). |
| bool empty() const { |
| return ranges::all_of( |
| callbacks_, [](const auto& callback) { return callback.is_null(); }); |
| } |
| |
| // Calls all registered callbacks that are not canceled beforehand. If any |
| // callbacks are unregistered, notifies any registered removal callback at the |
| // end. |
| // |
| // Arguments must be copyable, since they must be supplied to all callbacks. |
| // Move-only types would be destructively modified by passing them to the |
| // first callback and not reach subsequent callbacks as intended. |
| // |
| // Notify() may be called re-entrantly, in which case the nested call |
| // completes before the outer one continues. Callbacks are only ever added at |
| // the end and canceled callbacks are not pruned from the list until the |
| // outermost iteration completes, so existing iterators should never be |
| // invalidated. However, this does mean that a callback added during a nested |
| // call can be notified by outer calls -- meaning it will be notified about |
| // things that happened before it was added -- if its subscription outlives |
| // the reentrant Notify() call. |
| template <typename... RunArgs> |
| void Notify(RunArgs&&... args) { |
| if (empty()) |
| return; // Nothing to do. |
| |
| { |
| AutoReset<bool> iterating(&iterating_, true); |
| |
| // Skip any callbacks that are canceled during iteration. |
| // NOTE: Since RunCallback() may call Add(), it's not safe to cache the |
| // value of callbacks_.end() across loop iterations. |
| const auto next_valid = [this](const auto it) { |
| return std::find_if_not(it, callbacks_.end(), [](const auto& callback) { |
| return callback.is_null(); |
| }); |
| }; |
| for (auto it = next_valid(callbacks_.begin()); it != callbacks_.end(); |
| it = next_valid(it)) |
| // NOTE: Intentionally does not call std::forward<RunArgs>(args)..., |
| // since that would allow move-only arguments. |
| static_cast<CallbackListImpl*>(this)->RunCallback(it++, args...); |
| } |
| |
| // Re-entrant invocations shouldn't prune anything from the list. This can |
| // invalidate iterators from underneath higher call frames. It's safe to |
| // simply do nothing, since the outermost frame will continue through here |
| // and prune all null callbacks below. |
| if (iterating_) |
| return; |
| |
| // Any null callbacks remaining in the list were canceled due to |
| // Subscription destruction during iteration, and can safely be erased now. |
| const size_t erased_callbacks = |
| EraseIf(callbacks_, [](const auto& cb) { return cb.is_null(); }); |
| |
| // Run |removal_callback_| if any callbacks were canceled. Note that we |
| // cannot simply compare list sizes before and after iterating, since |
| // notification may result in Add()ing new callbacks as well as canceling |
| // them. Also note that if this is a OnceCallbackList, the OnceCallbacks |
| // that were executed above have all been removed regardless of whether |
| // they're counted in |erased_callbacks_|. |
| if (removal_callback_ && |
| (erased_callbacks || IsOnceCallback<CallbackType>::value)) |
| removal_callback_.Run(); // May delete |this|! |
| } |
| |
| protected: |
| using Callbacks = typename CallbackListTraits<CallbackListImpl>::Callbacks; |
| |
| // Holds non-null callbacks, which will be called during Notify(). |
| Callbacks callbacks_; |
| |
| private: |
| // Cancels the callback pointed to by |it|, which is guaranteed to be valid. |
| void CancelCallback(const typename Callbacks::iterator& it) { |
| if (static_cast<CallbackListImpl*>(this)->CancelNullCallback(it)) |
| return; |
| |
| if (iterating_) { |
| // Calling erase() here is unsafe, since the loop in Notify() may be |
| // referencing this same iterator, e.g. if adjacent callbacks' |
| // Subscriptions are both destroyed when the first one is Run(). Just |
| // reset the callback and let Notify() clean it up at the end. |
| it->Reset(); |
| } else { |
| callbacks_.erase(it); |
| if (removal_callback_) |
| removal_callback_.Run(); // May delete |this|! |
| } |
| } |
| |
| // Set while Notify() is traversing |callbacks_|. Used primarily to avoid |
| // invalidating iterators that may be in use. |
| bool iterating_ = false; |
| |
| // Called after elements are removed from |callbacks_|. |
| RepeatingClosure removal_callback_; |
| |
| WeakPtrFactory<CallbackListBase> weak_ptr_factory_{this}; |
| }; |
| |
| } // namespace internal |
| |
| template <typename Signature> |
| class OnceCallbackList |
| : public internal::CallbackListBase<OnceCallbackList<Signature>> { |
| private: |
| friend internal::CallbackListBase<OnceCallbackList>; |
| using Traits = internal::CallbackListTraits<OnceCallbackList>; |
| |
| // Runs the current callback, which may cancel it or any other callbacks. |
| template <typename... RunArgs> |
| void RunCallback(typename Traits::Callbacks::iterator it, RunArgs&&... args) { |
| // OnceCallbacks still have Subscriptions with outstanding iterators; |
| // splice() removes them from |callbacks_| without invalidating those. |
| null_callbacks_.splice(null_callbacks_.end(), this->callbacks_, it); |
| |
| // NOTE: Intentionally does not call std::forward<RunArgs>(args)...; see |
| // comments in Notify(). |
| std::move(*it).Run(args...); |
| } |
| |
| // If |it| refers to an already-canceled callback, does any necessary cleanup |
| // and returns true. Otherwise returns false. |
| bool CancelNullCallback(const typename Traits::Callbacks::iterator& it) { |
| if (it->is_null()) { |
| null_callbacks_.erase(it); |
| return true; |
| } |
| return false; |
| } |
| |
| // Holds null callbacks whose Subscriptions are still alive, so the |
| // Subscriptions will still contain valid iterators. Only needed for |
| // OnceCallbacks, since RepeatingCallbacks are not canceled except by |
| // Subscription destruction. |
| typename Traits::Callbacks null_callbacks_; |
| }; |
| |
| template <typename Signature> |
| class RepeatingCallbackList |
| : public internal::CallbackListBase<RepeatingCallbackList<Signature>> { |
| private: |
| friend internal::CallbackListBase<RepeatingCallbackList>; |
| using Traits = internal::CallbackListTraits<RepeatingCallbackList>; |
| // Runs the current callback, which may cancel it or any other callbacks. |
| template <typename... RunArgs> |
| void RunCallback(typename Traits::Callbacks::iterator it, RunArgs&&... args) { |
| // NOTE: Intentionally does not call std::forward<RunArgs>(args)...; see |
| // comments in Notify(). |
| it->Run(args...); |
| } |
| |
| // If |it| refers to an already-canceled callback, does any necessary cleanup |
| // and returns true. Otherwise returns false. |
| bool CancelNullCallback(const typename Traits::Callbacks::iterator& it) { |
| // Because at most one Subscription can point to a given callback, and |
| // RepeatingCallbacks are only reset by CancelCallback(), no one should be |
| // able to request cancellation of a canceled RepeatingCallback. |
| DCHECK(!it->is_null()); |
| return false; |
| } |
| }; |
| |
| // Syntactic sugar to parallel that used for Callbacks. |
| // ClosureList explicitly not provided since it is not used, and CallbackList |
| // is deprecated. {Once,Repeating}ClosureList should instead be used. |
| using OnceClosureList = OnceCallbackList<void()>; |
| using RepeatingClosureList = RepeatingCallbackList<void()>; |
| |
| } // namespace base |
| |
| #endif // BASE_CALLBACK_LIST_H_ |