1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190
  191
  192
  193
  194
  195
  196
  197
  198
  199
  200
  201
  202
  203
  204
  205
  206
  207
  208
  209
  210
  211
  212
  213
  214
  215
  216
  217
  218
  219
  220
  221
  222
  223
  224
  225
  226
  227
  228
  229
  230
  231
  232
  233
  234
  235
  236
  237
  238
  239
  240
  241
  242
  243
  244
  245
  246
  247
  248
  249
  250
  251
  252
  253
  254
  255
  256
  257
  258
  259
  260
  261
  262
  263
  264
  265
  266
  267
  268
  269
  270
  271
  272
  273
  274
  275
  276
  277
  278
  279
  280
  281
  282
  283
  284
  285
  286
  287
  288
  289
  290
  291
  292
  293
  294
  295
  296
  297
  298
  299
  300
  301
  302
  303
  304
  305
  306
  307
  308
  309
  310
  311
  312
  313
  314
  315
  316
  317
  318
  319
  320
  321
  322
  323
  324
  325
  326
  327
  328
  329
  330
  331
  332
  333
  334
  335
  336
  337
  338
  339
  340
  341
  342
  343
  344
  345
  346
  347
  348

base / callback_list.h [blame]

// Copyright 2013 The Chromium Authors
// 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/check.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/memory/weak_ptr.h"
#include "base/ranges/algorithm.h"
#include "base/types/is_instantiation.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 [[nodiscard]] 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;

  // TODO(crbug.com/40139093): 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|.
  [[nodiscard]] CallbackListSubscription Add(CallbackType cb) {
    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 =
        std::erase_if(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 || is_instantiation<OnceCallback, CallbackType>)) {
      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 {Once,Repeating}Callbacks.
using OnceClosureList = OnceCallbackList<void()>;
using RepeatingClosureList = RepeatingCallbackList<void()>;

}  // namespace base

#endif  // BASE_CALLBACK_LIST_H_