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

base / functional / callback_internal.h [blame]

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// This file contains utility functions and classes that help the
// implementation, and management of the Callback objects.

#ifndef BASE_FUNCTIONAL_CALLBACK_INTERNAL_H_
#define BASE_FUNCTIONAL_CALLBACK_INTERNAL_H_

#include <type_traits>
#include <utility>

#include "base/base_export.h"
#include "base/compiler_specific.h"
#include "base/functional/callback_forward.h"
#include "base/memory/ref_counted.h"

namespace base {

struct FakeBindState;

namespace internal {

class BindStateBase;

template <bool is_method,
          bool is_nullable,
          bool is_callback,
          typename Functor,
          typename... BoundArgs>
struct BindState;

struct BASE_EXPORT BindStateBaseRefCountTraits {
  static void Destruct(const BindStateBase*);
};

template <typename T>
using PassingType = std::conditional_t<std::is_scalar_v<T>, T, T&&>;

// BindStateBase is used to provide an opaque handle that the Callback
// class can use to represent a function object with bound arguments.  It
// behaves as an existential type that is used by a corresponding
// DoInvoke function to perform the function execution.  This allows
// us to shield the Callback class from the types of the bound argument via
// "type erasure."
// At the base level, the only task is to add reference counting data. Avoid
// using or inheriting any virtual functions. Creating a vtable for every
// BindState template instantiation results in a lot of bloat. Its only task is
// to call the destructor which can be done with a function pointer.
class BASE_EXPORT BindStateBase
    : public RefCountedThreadSafe<BindStateBase, BindStateBaseRefCountTraits> {
 public:
  REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE();

  // What kind of cancellation query the call to the cancellation traits is
  // making. This enum could be removed, at the cost of storing an extra
  // function pointer.
  enum class CancellationQueryMode : bool {
    kIsCancelled = false,
    kMaybeValid = true,
  };

  using InvokeFuncStorage = void (*)();

  BindStateBase(const BindStateBase&) = delete;
  BindStateBase& operator=(const BindStateBase&) = delete;

 private:
  using DestructorPtr = void (*)(const BindStateBase*);
  using QueryCancellationTraitsPtr = bool (*)(const BindStateBase*,
                                              CancellationQueryMode mode);

  BindStateBase(InvokeFuncStorage polymorphic_invoke, DestructorPtr destructor);
  BindStateBase(InvokeFuncStorage polymorphic_invoke,
                DestructorPtr destructor,
                QueryCancellationTraitsPtr query_cancellation_traits);
  ~BindStateBase() = default;

  friend struct BindStateBaseRefCountTraits;
  friend class RefCountedThreadSafe<BindStateBase, BindStateBaseRefCountTraits>;

  friend class BindStateHolder;

  // Allowlist subclasses that access the destructor of BindStateBase.
  template <bool is_method,
            bool is_nullable,
            bool is_callback,
            typename Functor,
            typename... BoundArgs>
  friend struct BindState;
  friend struct ::base::FakeBindState;

  bool IsCancelled() const {
    return query_cancellation_traits_(this,
                                      CancellationQueryMode::kIsCancelled);
  }

  bool MaybeValid() const {
    return query_cancellation_traits_(this, CancellationQueryMode::kMaybeValid);
  }

  // In C++, it is safe to cast function pointers to function pointers of
  // another type. It is not okay to use void*. We create a InvokeFuncStorage
  // that that can store our function pointer, and then cast it back to
  // the original type on usage.
  InvokeFuncStorage polymorphic_invoke_;

  // Pointer to a function that will properly destroy |this|.
  DestructorPtr destructor_;
  QueryCancellationTraitsPtr query_cancellation_traits_;
};

// Minimal wrapper around a `scoped_refptr<BindStateBase>`. It allows more
// expensive operations (such as ones that destroy `BindStateBase` or manipulate
// refcounts) to be defined out-of-line to reduce binary size.
class BASE_EXPORT TRIVIAL_ABI BindStateHolder {
 public:
  using InvokeFuncStorage = BindStateBase::InvokeFuncStorage;

  // Used to construct a null callback.
  inline constexpr BindStateHolder() noexcept;

  // Used to construct a callback by `base::BindOnce()`/`base::BindRepeating().
  inline explicit BindStateHolder(BindStateBase* bind_state);

  // BindStateHolder is always copyable so it can be used by `OnceCallback` and
  // `RepeatingCallback`. `OnceCallback` restricts copies so a `BindStateHolder`
  // used with a `OnceCallback will never be copied.
  BindStateHolder(const BindStateHolder&);
  BindStateHolder& operator=(const BindStateHolder&);

  // Subtle: since `this` is marked as TRIVIAL_ABI, the move operations must
  // leave a moved-from `BindStateHolder` in a trivially destructible state.
  inline BindStateHolder(BindStateHolder&&) noexcept;
  BindStateHolder& operator=(BindStateHolder&&) noexcept;

  ~BindStateHolder();

  bool is_null() const { return !bind_state_; }
  explicit operator bool() const { return !is_null(); }

  bool IsCancelled() const;
  bool MaybeValid() const;

  void Reset();

  friend bool operator==(const BindStateHolder&,
                         const BindStateHolder&) = default;

  const scoped_refptr<BindStateBase>& bind_state() const { return bind_state_; }

  InvokeFuncStorage polymorphic_invoke() const {
    return bind_state_->polymorphic_invoke_;
  }

 private:
  scoped_refptr<BindStateBase> bind_state_;
};

constexpr BindStateHolder::BindStateHolder() noexcept = default;

// TODO(dcheng): Try plumbing a scoped_refptr all the way through, since
// scoped_refptr is marked as TRIVIAL_ABI.
BindStateHolder::BindStateHolder(BindStateBase* bind_state)
    : bind_state_(AdoptRef(bind_state)) {}

// Unlike the copy constructor, copy assignment operator, and move assignment
// operator, the move constructor is defaulted in the header because it
// generates minimal code: move construction does not change any refcounts, nor
// does it potentially destroy `BindStateBase`.
BindStateHolder::BindStateHolder(BindStateHolder&&) noexcept = default;

// Helpers for the `Then()` implementation.
template <typename OriginalCallback, typename ThenCallback>
struct ThenHelper;

// Specialization when original callback returns `void`.
template <template <typename> class OriginalCallback,
          template <typename>
          class ThenCallback,
          typename... OriginalArgs,
          typename ThenR,
          typename... ThenArgs>
struct ThenHelper<OriginalCallback<void(OriginalArgs...)>,
                  ThenCallback<ThenR(ThenArgs...)>> {
 private:
  // For context on this "templated struct with a lambda that asserts" pattern,
  // see comments in `Invoker<>`.
  template <bool v = sizeof...(ThenArgs) == 0>
  struct CorrectNumberOfArgs {
    static constexpr bool value = [] {
      static_assert(v,
                    "|then| callback cannot accept parameters if |this| has a "
                    "void return type.");
      return v;
    }();
  };

 public:
  static auto CreateTrampoline() {
    return [](OriginalCallback<void(OriginalArgs...)> c1,
              ThenCallback<ThenR(ThenArgs...)> c2,
              OriginalArgs... c1_args) -> ThenR {
      if constexpr (CorrectNumberOfArgs<>::value) {
        std::move(c1).Run(std::forward<OriginalArgs>(c1_args)...);
        return std::move(c2).Run();
      }
    };
  }
};

// Specialization when original callback returns a non-void type.
template <template <typename> class OriginalCallback,
          template <typename>
          class ThenCallback,
          typename OriginalR,
          typename... OriginalArgs,
          typename ThenR,
          typename... ThenArgs>
struct ThenHelper<OriginalCallback<OriginalR(OriginalArgs...)>,
                  ThenCallback<ThenR(ThenArgs...)>> {
 private:
  template <bool v = sizeof...(ThenArgs) == 1>
  struct CorrectNumberOfArgs {
    static constexpr bool value = [] {
      static_assert(
          v,
          "|then| callback must accept exactly one parameter if |this| has a "
          "non-void return type.");
      return v;
    }();
  };

  template <bool v =
                // TODO(dcheng): This should probably check is_convertible as
                // well (same with `AssertBindArgsValidity`).
            std::is_constructible_v<ThenArgs..., OriginalR&&>>
  struct ArgsAreConvertible {
    static constexpr bool value = [] {
      static_assert(v,
                    "|then| callback's parameter must be constructible from "
                    "return type of |this|.");
      return v;
    }();
  };

 public:
  static auto CreateTrampoline() {
    return [](OriginalCallback<OriginalR(OriginalArgs...)> c1,
              ThenCallback<ThenR(ThenArgs...)> c2,
              OriginalArgs... c1_args) -> ThenR {
      if constexpr (std::conjunction_v<CorrectNumberOfArgs<>,
                                       ArgsAreConvertible<>>) {
        return std::move(c2).Run(
            std::move(c1).Run(std::forward<OriginalArgs>(c1_args)...));
      }
    };
  }
};

}  // namespace internal
}  // namespace base

#endif  // BASE_FUNCTIONAL_CALLBACK_INTERNAL_H_