123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340 |
- // 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 <algorithm>
- #include <list>
- #include <memory>
- #include <utility>
- #include "base/auto_reset.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/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.
- // std::unique_ptr<CallbackList::Subscription>
- // 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 reset().
- // }
- //
- // // Automatically deregisters the callback when deleted (e.g. in
- // // ~MyWidgetListener()). Unretained(this) is safe here since the
- // // Subscription does not outlive |this|.
- // std::unique_ptr<MyWidget::CallbackList::Subscription> 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 {
- template <typename Signature>
- class OnceCallbackList;
- template <typename Signature>
- class RepeatingCallbackList;
- 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, "");
- // A cancellation handle for callers who register callbacks. Subscription
- // destruction cancels the associated callback and is legal any time,
- // including after the destruction of the CallbackList that vends it.
- class Subscription {
- public:
- explicit Subscription(base::OnceClosure destruction_closure)
- : destruction_closure_(std::move(destruction_closure)) {}
- Subscription(Subscription&&) = default;
- Subscription& operator=(Subscription&&) = default;
- ~Subscription() { std::move(destruction_closure_).Run(); }
- private:
- // Run when |this| is destroyed to notify the CallbackList the associated
- // callback should be canceled. Since this is bound using a WeakPtr to the
- // CallbackList, it will automatically no-op if the CallbackList no longer
- // exists.
- base::OnceClosure destruction_closure_;
- };
- 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 Subscription that can be
- // used to cancel |cb|.
- std::unique_ptr<Subscription> Add(CallbackType cb) WARN_UNUSED_RESULT {
- DCHECK(!cb.is_null());
- return std::make_unique<Subscription>(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 std::all_of(callbacks_.cbegin(), callbacks_.cend(),
- [](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;
- }
- };
- template <typename Signature>
- using CallbackList = RepeatingCallbackList<Signature>;
- // Syntactic sugar to parallel that used for Callbacks.
- using OnceClosureList = OnceCallbackList<void()>;
- using RepeatingClosureList = RepeatingCallbackList<void()>;
- using ClosureList = CallbackList<void()>;
- } // namespace base
- #endif // BASE_CALLBACK_LIST_H_
|