Avi Drissman | e4622aa | 2022-09-08 20:36:06 | [diff] [blame] | 1 | // Copyright 2012 The Chromium Authors |
[email protected] | b38d357 | 2011-02-15 01:27:38 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | // This file contains utility functions and classes that help the |
| 6 | // implementation, and management of the Callback objects. |
| 7 | |
[email protected] | 59eff91 | 2011-02-18 23:29:31 | [diff] [blame] | 8 | #ifndef BASE_CALLBACK_INTERNAL_H_ |
| 9 | #define BASE_CALLBACK_INTERNAL_H_ |
[email protected] | b38d357 | 2011-02-15 01:27:38 | [diff] [blame] | 10 | |
Sumaid Syed | 22f60eeb | 2021-08-26 05:16:26 | [diff] [blame] | 11 | #include <utility> |
| 12 | |
[email protected] | 0bea725 | 2011-08-05 15:34:00 | [diff] [blame] | 13 | #include "base/base_export.h" |
tzik | 77d41139 | 2016-03-09 09:47:03 | [diff] [blame] | 14 | #include "base/callback_forward.h" |
[email protected] | 3b63f8f4 | 2011-03-28 01:54:15 | [diff] [blame] | 15 | #include "base/memory/ref_counted.h" |
[email protected] | b38d357 | 2011-02-15 01:27:38 | [diff] [blame] | 16 | |
| 17 | namespace base { |
tzik | 65adef8 | 2017-03-30 06:45:21 | [diff] [blame] | 18 | |
| 19 | struct FakeBindState; |
| 20 | |
[email protected] | b38d357 | 2011-02-15 01:27:38 | [diff] [blame] | 21 | namespace internal { |
tzik | 65adef8 | 2017-03-30 06:45:21 | [diff] [blame] | 22 | |
Alex Clarke | 22c21a9c | 2019-07-06 07:31:07 | [diff] [blame] | 23 | class BindStateBase; |
Alex Clarke | f7b49a4 | 2019-05-22 19:07:27 | [diff] [blame] | 24 | class FinallyExecutorCommon; |
Alex Clarke | b6d55d0 | 2019-05-21 20:45:24 | [diff] [blame] | 25 | class ThenAndCatchExecutorCommon; |
Alex Clarke | 22c21a9c | 2019-07-06 07:31:07 | [diff] [blame] | 26 | |
| 27 | template <typename ReturnType> |
| 28 | class PostTaskExecutor; |
tzik | 65adef8 | 2017-03-30 06:45:21 | [diff] [blame] | 29 | |
| 30 | template <typename Functor, typename... BoundArgs> |
| 31 | struct BindState; |
| 32 | |
Alex Clarke | b6d55d0 | 2019-05-21 20:45:24 | [diff] [blame] | 33 | class CallbackBase; |
| 34 | class CallbackBaseCopyable; |
| 35 | |
Sergei Glazunov | cbc7afd0 | 2022-08-31 21:19:09 | [diff] [blame] | 36 | struct BASE_EXPORT BindStateBaseRefCountTraits { |
tzik | 65adef8 | 2017-03-30 06:45:21 | [diff] [blame] | 37 | static void Destruct(const BindStateBase*); |
| 38 | }; |
| 39 | |
Vladislav Kuzkokov | 6d208e1 | 2017-11-08 21:31:08 | [diff] [blame] | 40 | template <typename T> |
Daniel Cheng | 6f510fa | 2022-01-12 19:36:03 | [diff] [blame] | 41 | using PassingType = std::conditional_t<std::is_scalar_v<T>, T, T&&>; |
Vladislav Kuzkokov | 6d208e1 | 2017-11-08 21:31:08 | [diff] [blame] | 42 | |
[email protected] | 7296f276 | 2011-11-21 19:23:44 | [diff] [blame] | 43 | // BindStateBase is used to provide an opaque handle that the Callback |
[email protected] | b38d357 | 2011-02-15 01:27:38 | [diff] [blame] | 44 | // class can use to represent a function object with bound arguments. It |
| 45 | // behaves as an existential type that is used by a corresponding |
| 46 | // DoInvoke function to perform the function execution. This allows |
| 47 | // us to shield the Callback class from the types of the bound argument via |
| 48 | // "type erasure." |
Honglin Yu | a6a4ba2 | 2019-08-09 02:30:18 | [diff] [blame] | 49 | // At the base level, the only task is to add reference counting data. Avoid |
| 50 | // using or inheriting any virtual functions. Creating a vtable for every |
| 51 | // BindState template instantiation results in a lot of bloat. Its only task is |
| 52 | // to call the destructor which can be done with a function pointer. |
tzik | 65adef8 | 2017-03-30 06:45:21 | [diff] [blame] | 53 | class BASE_EXPORT BindStateBase |
| 54 | : public RefCountedThreadSafe<BindStateBase, BindStateBaseRefCountTraits> { |
tzik | 1886c27 | 2016-09-08 05:45:38 | [diff] [blame] | 55 | public: |
tzik | 65f39693 | 2017-04-03 05:27:34 | [diff] [blame] | 56 | REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE(); |
| 57 | |
tzik | 9697d49e | 2018-08-02 13:35:19 | [diff] [blame] | 58 | enum CancellationQueryMode { |
| 59 | IS_CANCELLED, |
| 60 | MAYBE_VALID, |
| 61 | }; |
| 62 | |
tzik | 1886c27 | 2016-09-08 05:45:38 | [diff] [blame] | 63 | using InvokeFuncStorage = void(*)(); |
| 64 | |
David Bienvenu | b4b441e | 2020-09-23 05:49:57 | [diff] [blame] | 65 | BindStateBase(const BindStateBase&) = delete; |
| 66 | BindStateBase& operator=(const BindStateBase&) = delete; |
| 67 | |
tzik | 65adef8 | 2017-03-30 06:45:21 | [diff] [blame] | 68 | private: |
tzik | 1886c27 | 2016-09-08 05:45:38 | [diff] [blame] | 69 | BindStateBase(InvokeFuncStorage polymorphic_invoke, |
tzik | 30e0c31 | 2016-09-21 08:06:54 | [diff] [blame] | 70 | void (*destructor)(const BindStateBase*)); |
tzik | 1fdcca3 | 2016-09-14 07:15:00 | [diff] [blame] | 71 | BindStateBase(InvokeFuncStorage polymorphic_invoke, |
tzik | 30e0c31 | 2016-09-21 08:06:54 | [diff] [blame] | 72 | void (*destructor)(const BindStateBase*), |
tzik | 9697d49e | 2018-08-02 13:35:19 | [diff] [blame] | 73 | bool (*query_cancellation_traits)(const BindStateBase*, |
| 74 | CancellationQueryMode mode)); |
tzik | 65adef8 | 2017-03-30 06:45:21 | [diff] [blame] | 75 | |
tapted | e7e804c | 2015-05-14 08:03:32 | [diff] [blame] | 76 | ~BindStateBase() = default; |
| 77 | |
tzik | 65adef8 | 2017-03-30 06:45:21 | [diff] [blame] | 78 | friend struct BindStateBaseRefCountTraits; |
| 79 | friend class RefCountedThreadSafe<BindStateBase, BindStateBaseRefCountTraits>; |
| 80 | |
tapted | e7e804c | 2015-05-14 08:03:32 | [diff] [blame] | 81 | friend class CallbackBase; |
tzik | d4bb5b7d | 2017-08-28 19:08:52 | [diff] [blame] | 82 | friend class CallbackBaseCopyable; |
tapted | e7e804c | 2015-05-14 08:03:32 | [diff] [blame] | 83 | |
Nate Fischer | 04068aa | 2021-03-10 22:20:54 | [diff] [blame] | 84 | // Allowlist subclasses that access the destructor of BindStateBase. |
tzik | 65adef8 | 2017-03-30 06:45:21 | [diff] [blame] | 85 | template <typename Functor, typename... BoundArgs> |
| 86 | friend struct BindState; |
| 87 | friend struct ::base::FakeBindState; |
| 88 | |
tzik | 59aa6bb1 | 2016-09-08 10:58:53 | [diff] [blame] | 89 | bool IsCancelled() const { |
tzik | 9697d49e | 2018-08-02 13:35:19 | [diff] [blame] | 90 | return query_cancellation_traits_(this, IS_CANCELLED); |
tzik | 59aa6bb1 | 2016-09-08 10:58:53 | [diff] [blame] | 91 | } |
| 92 | |
tzik | 9697d49e | 2018-08-02 13:35:19 | [diff] [blame] | 93 | bool MaybeValid() const { |
| 94 | return query_cancellation_traits_(this, MAYBE_VALID); |
| 95 | } |
Nicolas Ouellet-payeur | 40f8e9a | 2018-07-30 16:26:48 | [diff] [blame] | 96 | |
tzik | 1886c27 | 2016-09-08 05:45:38 | [diff] [blame] | 97 | // In C++, it is safe to cast function pointers to function pointers of |
| 98 | // another type. It is not okay to use void*. We create a InvokeFuncStorage |
| 99 | // that that can store our function pointer, and then cast it back to |
| 100 | // the original type on usage. |
| 101 | InvokeFuncStorage polymorphic_invoke_; |
| 102 | |
tapted | e7e804c | 2015-05-14 08:03:32 | [diff] [blame] | 103 | // Pointer to a function that will properly destroy |this|. |
tzik | 30e0c31 | 2016-09-21 08:06:54 | [diff] [blame] | 104 | void (*destructor_)(const BindStateBase*); |
tzik | 9697d49e | 2018-08-02 13:35:19 | [diff] [blame] | 105 | bool (*query_cancellation_traits_)(const BindStateBase*, |
| 106 | CancellationQueryMode mode); |
[email protected] | b38d357 | 2011-02-15 01:27:38 | [diff] [blame] | 107 | }; |
| 108 | |
[email protected] | 59eff91 | 2011-02-18 23:29:31 | [diff] [blame] | 109 | // Holds the Callback methods that don't require specialization to reduce |
| 110 | // template bloat. |
tzik | 77d41139 | 2016-03-09 09:47:03 | [diff] [blame] | 111 | // CallbackBase<MoveOnly> is a direct base class of MoveOnly callbacks, and |
| 112 | // CallbackBase<Copyable> uses CallbackBase<MoveOnly> for its implementation. |
tzik | d4bb5b7d | 2017-08-28 19:08:52 | [diff] [blame] | 113 | class BASE_EXPORT CallbackBase { |
[email protected] | 59eff91 | 2011-02-18 23:29:31 | [diff] [blame] | 114 | public: |
tzik | 787d420b | 2018-06-25 16:12:02 | [diff] [blame] | 115 | inline CallbackBase(CallbackBase&& c) noexcept; |
Jüri Valdmann | f841ac2 | 2018-05-18 22:36:28 | [diff] [blame] | 116 | CallbackBase& operator=(CallbackBase&& c) noexcept; |
dcheng | f883604 | 2014-11-26 05:04:55 | [diff] [blame] | 117 | |
tzik | d4bb5b7d | 2017-08-28 19:08:52 | [diff] [blame] | 118 | explicit CallbackBase(const CallbackBaseCopyable& c); |
| 119 | CallbackBase& operator=(const CallbackBaseCopyable& c); |
tzik | 27d1e31 | 2016-09-13 05:28:59 | [diff] [blame] | 120 | |
Jüri Valdmann | f841ac2 | 2018-05-18 22:36:28 | [diff] [blame] | 121 | explicit CallbackBase(CallbackBaseCopyable&& c) noexcept; |
| 122 | CallbackBase& operator=(CallbackBaseCopyable&& c) noexcept; |
tzik | f44c2f8d | 2017-03-08 08:41:15 | [diff] [blame] | 123 | |
[email protected] | 59eff91 | 2011-02-18 23:29:31 | [diff] [blame] | 124 | // Returns true if Callback is null (doesn't refer to anything). |
tzik | 65adef8 | 2017-03-30 06:45:21 | [diff] [blame] | 125 | bool is_null() const { return !bind_state_; } |
tzik | 99de02b | 2016-07-01 05:54:12 | [diff] [blame] | 126 | explicit operator bool() const { return !is_null(); } |
[email protected] | 59eff91 | 2011-02-18 23:29:31 | [diff] [blame] | 127 | |
tzik | 59aa6bb1 | 2016-09-08 10:58:53 | [diff] [blame] | 128 | // Returns true if the callback invocation will be nop due to an cancellation. |
| 129 | // It's invalid to call this on uninitialized callback. |
Nicolas Ouellet-payeur | 40f8e9a | 2018-07-30 16:26:48 | [diff] [blame] | 130 | // |
| 131 | // Must be called on the Callback's destination sequence. |
tzik | 59aa6bb1 | 2016-09-08 10:58:53 | [diff] [blame] | 132 | bool IsCancelled() const; |
| 133 | |
Nicolas Ouellet-payeur | 40f8e9a | 2018-07-30 16:26:48 | [diff] [blame] | 134 | // If this returns false, the callback invocation will be a nop due to a |
| 135 | // cancellation. This may(!) still return true, even on a cancelled callback. |
| 136 | // |
| 137 | // This function is thread-safe. |
| 138 | bool MaybeValid() const; |
| 139 | |
[email protected] | 1c35a48 | 2011-10-25 23:19:51 | [diff] [blame] | 140 | // Returns the Callback into an uninitialized state. |
[email protected] | 59eff91 | 2011-02-18 23:29:31 | [diff] [blame] | 141 | void Reset(); |
| 142 | |
[email protected] | 481915a77 | 2011-09-10 03:14:35 | [diff] [blame] | 143 | protected: |
Alex Clarke | f7b49a4 | 2019-05-22 19:07:27 | [diff] [blame] | 144 | friend class FinallyExecutorCommon; |
Alex Clarke | b6d55d0 | 2019-05-21 20:45:24 | [diff] [blame] | 145 | friend class ThenAndCatchExecutorCommon; |
| 146 | |
Alex Clarke | 22c21a9c | 2019-07-06 07:31:07 | [diff] [blame] | 147 | template <typename ReturnType> |
| 148 | friend class PostTaskExecutor; |
| 149 | |
tzik | 1886c27 | 2016-09-08 05:45:38 | [diff] [blame] | 150 | using InvokeFuncStorage = BindStateBase::InvokeFuncStorage; |
[email protected] | 59eff91 | 2011-02-18 23:29:31 | [diff] [blame] | 151 | |
[email protected] | 1c35a48 | 2011-10-25 23:19:51 | [diff] [blame] | 152 | // Returns true if this callback equals |other|. |other| may be null. |
tzik | 77d41139 | 2016-03-09 09:47:03 | [diff] [blame] | 153 | bool EqualsInternal(const CallbackBase& other) const; |
[email protected] | 1c35a48 | 2011-10-25 23:19:51 | [diff] [blame] | 154 | |
Victor Hugo Vianna Silva | 5539a10 | 2022-02-08 10:33:11 | [diff] [blame] | 155 | inline constexpr CallbackBase(); |
tzik | 10e783d6 | 2018-03-22 08:37:45 | [diff] [blame] | 156 | |
[email protected] | e24f876 | 2011-12-20 00:10:04 | [diff] [blame] | 157 | // Allow initializing of |bind_state_| via the constructor to avoid default |
tzik | 1886c27 | 2016-09-08 05:45:38 | [diff] [blame] | 158 | // initialization of the scoped_refptr. |
tzik | 787d420b | 2018-06-25 16:12:02 | [diff] [blame] | 159 | explicit inline CallbackBase(BindStateBase* bind_state); |
[email protected] | 59eff91 | 2011-02-18 23:29:31 | [diff] [blame] | 160 | |
tzik | 1886c27 | 2016-09-08 05:45:38 | [diff] [blame] | 161 | InvokeFuncStorage polymorphic_invoke() const { |
| 162 | return bind_state_->polymorphic_invoke_; |
| 163 | } |
| 164 | |
[email protected] | 1c35a48 | 2011-10-25 23:19:51 | [diff] [blame] | 165 | // Force the destructor to be instantiated inside this translation unit so |
[email protected] | 59eff91 | 2011-02-18 23:29:31 | [diff] [blame] | 166 | // that our subclasses will not get inlined versions. Avoids more template |
| 167 | // bloat. |
| 168 | ~CallbackBase(); |
| 169 | |
[email protected] | 7296f276 | 2011-11-21 19:23:44 | [diff] [blame] | 170 | scoped_refptr<BindStateBase> bind_state_; |
[email protected] | 59eff91 | 2011-02-18 23:29:31 | [diff] [blame] | 171 | }; |
| 172 | |
tzik | 10e783d6 | 2018-03-22 08:37:45 | [diff] [blame] | 173 | constexpr CallbackBase::CallbackBase() = default; |
tzik | 787d420b | 2018-06-25 16:12:02 | [diff] [blame] | 174 | CallbackBase::CallbackBase(CallbackBase&&) noexcept = default; |
| 175 | CallbackBase::CallbackBase(BindStateBase* bind_state) |
| 176 | : bind_state_(AdoptRef(bind_state)) {} |
tzik | 10e783d6 | 2018-03-22 08:37:45 | [diff] [blame] | 177 | |
tzik | 77d41139 | 2016-03-09 09:47:03 | [diff] [blame] | 178 | // CallbackBase<Copyable> is a direct base class of Copyable Callbacks. |
tzik | d4bb5b7d | 2017-08-28 19:08:52 | [diff] [blame] | 179 | class BASE_EXPORT CallbackBaseCopyable : public CallbackBase { |
tzik | 77d41139 | 2016-03-09 09:47:03 | [diff] [blame] | 180 | public: |
tzik | d4bb5b7d | 2017-08-28 19:08:52 | [diff] [blame] | 181 | CallbackBaseCopyable(const CallbackBaseCopyable& c); |
tzik | 787d420b | 2018-06-25 16:12:02 | [diff] [blame] | 182 | CallbackBaseCopyable(CallbackBaseCopyable&& c) noexcept = default; |
tzik | d4bb5b7d | 2017-08-28 19:08:52 | [diff] [blame] | 183 | CallbackBaseCopyable& operator=(const CallbackBaseCopyable& c); |
Jüri Valdmann | f841ac2 | 2018-05-18 22:36:28 | [diff] [blame] | 184 | CallbackBaseCopyable& operator=(CallbackBaseCopyable&& c) noexcept; |
tzik | 77d41139 | 2016-03-09 09:47:03 | [diff] [blame] | 185 | |
tzik | d4bb5b7d | 2017-08-28 19:08:52 | [diff] [blame] | 186 | protected: |
tzik | 10e783d6 | 2018-03-22 08:37:45 | [diff] [blame] | 187 | constexpr CallbackBaseCopyable() = default; |
tzik | d4bb5b7d | 2017-08-28 19:08:52 | [diff] [blame] | 188 | explicit CallbackBaseCopyable(BindStateBase* bind_state) |
| 189 | : CallbackBase(bind_state) {} |
Chris Watkins | 091d629 | 2017-12-13 04:25:58 | [diff] [blame] | 190 | ~CallbackBaseCopyable() = default; |
tzik | d4bb5b7d | 2017-08-28 19:08:52 | [diff] [blame] | 191 | }; |
tzik | 77d41139 | 2016-03-09 09:47:03 | [diff] [blame] | 192 | |
Daniel Cheng | c0ac78c | 2021-03-03 18:22:24 | [diff] [blame] | 193 | // Helpers for the `Then()` implementation. |
| 194 | template <typename OriginalCallback, typename ThenCallback> |
| 195 | struct ThenHelper; |
| 196 | |
| 197 | // Specialization when original callback returns `void`. |
| 198 | template <template <typename> class OriginalCallback, |
| 199 | template <typename> |
| 200 | class ThenCallback, |
| 201 | typename... OriginalArgs, |
| 202 | typename ThenR, |
| 203 | typename... ThenArgs> |
| 204 | struct ThenHelper<OriginalCallback<void(OriginalArgs...)>, |
| 205 | ThenCallback<ThenR(ThenArgs...)>> { |
| 206 | static_assert(sizeof...(ThenArgs) == 0, |
| 207 | "|then| callback cannot accept parameters if |this| has a " |
| 208 | "void return type."); |
| 209 | |
| 210 | static auto CreateTrampoline() { |
| 211 | return [](OriginalCallback<void(OriginalArgs...)> c1, |
| 212 | ThenCallback<ThenR(ThenArgs...)> c2, OriginalArgs... c1_args) { |
| 213 | std::move(c1).Run(std::forward<OriginalArgs>(c1_args)...); |
| 214 | return std::move(c2).Run(); |
| 215 | }; |
| 216 | } |
| 217 | }; |
| 218 | |
| 219 | // Specialization when original callback returns a non-void type. |
| 220 | template <template <typename> class OriginalCallback, |
| 221 | template <typename> |
| 222 | class ThenCallback, |
| 223 | typename OriginalR, |
| 224 | typename... OriginalArgs, |
| 225 | typename ThenR, |
| 226 | typename... ThenArgs> |
| 227 | struct ThenHelper<OriginalCallback<OriginalR(OriginalArgs...)>, |
| 228 | ThenCallback<ThenR(ThenArgs...)>> { |
| 229 | static_assert(sizeof...(ThenArgs) == 1, |
| 230 | "|then| callback must accept exactly one parameter if |this| " |
| 231 | "has a non-void return type."); |
| 232 | // TODO(dcheng): This should probably check is_convertible as well (same with |
| 233 | // `AssertBindArgsValidity`). |
Daniel Cheng | 6f510fa | 2022-01-12 19:36:03 | [diff] [blame] | 234 | static_assert(std::is_constructible_v<ThenArgs..., OriginalR&&>, |
Daniel Cheng | c0ac78c | 2021-03-03 18:22:24 | [diff] [blame] | 235 | "|then| callback's parameter must be constructible from " |
| 236 | "return type of |this|."); |
| 237 | |
| 238 | static auto CreateTrampoline() { |
| 239 | return [](OriginalCallback<OriginalR(OriginalArgs...)> c1, |
| 240 | ThenCallback<ThenR(ThenArgs...)> c2, OriginalArgs... c1_args) { |
| 241 | return std::move(c2).Run( |
| 242 | std::move(c1).Run(std::forward<OriginalArgs>(c1_args)...)); |
| 243 | }; |
| 244 | } |
| 245 | }; |
danakj | fcc5e7c | 2020-10-23 17:43:27 | [diff] [blame] | 246 | |
[email protected] | b38d357 | 2011-02-15 01:27:38 | [diff] [blame] | 247 | } // namespace internal |
| 248 | } // namespace base |
| 249 | |
[email protected] | 59eff91 | 2011-02-18 23:29:31 | [diff] [blame] | 250 | #endif // BASE_CALLBACK_INTERNAL_H_ |